use lib '/usr/cluster/modules'; use Fcntl ':mode'; use LWP::Simple; use Math::Trig; $MY_PI = 3.14159265358979323846; sub padtowith { my ( $s, $n, $w ) = @_; return ( ("$w"x($n-length($s))).$s ); } sub padi { my ( $i, $n ) = @_; return ( padtowith ( $i, $n, '0' ) ); } sub pads { my ( $s, $n ) = @_; return ( padtowith ( $s, $n, ' ' ) ); } sub u { return ( units ( $_[0], $_[1] ) ); } sub units { my ( $fr, $to, $x ) = @_; chomp ( $x = `units '$fr' '$to' | head -1` ); if ( $x =~ /^\s+\*\s+(.+)/ ) { $x = $1; } return ( $x ); } sub punits { my ( $fr, $to ) = @_; my ( $x, $z ); $x = ''; shift ( @_ ); shift ( @_ ); foreach $z ( @_ ) { if ( $x ne '' ) { $x .= ' '; } $x .= units ( "$z $fr", $to ); } return ( $x ); } sub pu # same as punits { return ( punits ( @_ ) ); } sub nunits { my ( $fr, $to, $x ) = @_; chomp ( $x = `units '$fr' '$to' | head -1` ); if ( $x =~ /^\s+\*\s+([0-9\.]+)/ ) { $x = $1; } return ( $x ); } sub spell { my ( $s ) = @_; my ( $x, $c ); $c = ""; foreach $x ( split ( /\s+/, $s ) ) { $c .= " | grep '$x'"; } $s = `cat /usr/dict/words $c`; print ("$s"); return ( $CALC_nopush ); } sub rd_f { my ( $fn, $buf, $aline ) = ( $_[0], "", "" ); my ( $fh ); open ( $fh, "<$fn" ); while ( defined ( $aline = <$fh> ) ) { $buf .= $aline; } close ( $fh ); return ( $buf ); } sub wr_f { my ( $fn, $buf ) = ( $_[0], $_[1] ); my ( $fh ); open ( $fh, ">$fn" ); print ( $fh $buf ); close ( $fh ); return ($buf); } sub ap_f { my ( $fn, $buf ) = ( $_[0], $_[1] ); my ( $fh ); open ( $fh, ">>$fn" ); print ( $fh $buf ); close ( $fh ); return ($buf); } sub ke { my ( $v, $w, $n ) = @_; my ( $ret ); $ret = $v * $v * $w / 1000000; # if ( defined ( $n ) ) { $ret .= " \t$n"; } # convert to foot-pounds # $ret *= 4.44; $ret *= 2.22; # round over to nearest thousandth $ret = int ( $ret * 1000 + 0.5 ) / 1000; return ( $ret ); } sub f2c { my ( $f ) = @_; my ( $c ); $c = ( $f - 32 ) * 5 / 9; return ( $c ); } sub c2f { my ( $c ) = @_; my ( $f ); $f = ( $c * 9 / 5 ) + 32; return ( $f ); } sub psi2bhn { # very approximately converts steel uts psi to brinell hardness number # source: http://www.monachos.gr/en/resources/hardness_conversion.asp # qv alt: http://mdmetric.com/tech/hardness.htm # this table provides approx 2% error in conversion my ( $psi ) = @_; if ( $psi =~ /[^0-9\.\s]/ ) { $psi = u ( $psi, 'psi' ); } if ( $psi =~ /[^0-9\.\s]/ ) { return ("error"); } $psi /= 1000; if ( $psi < 127 ) { return ( $psi / .500 ); } if ( $psi < 140 ) { return ( $psi / .497 ); } if ( $psi < 153 ) { return ( $psi / .487 ); } if ( $psi < 166 ) { return ( $psi / .485 ); } if ( $psi < 179 ) { return ( $psi / .486 ); } if ( $psi < 192 ) { return ( $psi / .484 ); } if ( $psi < 205 ) { return ( $psi / .483 ); } if ( $psi < 218 ) { return ( $psi / .484 ); } if ( $psi < 231 ) { return ( $psi / .489 ); } if ( $psi < 257 ) { return ( $psi / .493 ); } if ( $psi < 270 ) { return ( $psi / .494 ); } if ( $psi < 283 ) { return ( $psi / .495 ); } if ( $psi < 296 ) { return ( $psi / .497 ); } if ( $psi < 322 ) { return ( $psi / .498 ); } if ( $psi < 335 ) { return ( $psi / .499 ); } if ( $psi < 348 ) { return ( $psi / .500 ); } if ( $psi < 374 ) { return ( $psi / .501 ); } if ( $psi < 387 ) { return ( $psi / .503 ); } if ( $psi < 400 ) { return ( $psi / .504 ); } if ( $psi < 426 ) { return ( $psi / .506 ); } if ( $psi < 439 ) { return ( $psi / .507 ); } if ( $psi < 452 ) { return ( $psi / .510 ); } if ( $psi < 478 ) { return ( $psi / .509 ); } if ( $psi < 491 ) { return ( $psi / .511 ); } if ( $psi < 504 ) { return ( $psi / .512 ); } if ( $psi < 530 ) { return ( $psi / .513 ); } if ( $psi < 595 ) { return ( $psi / .514 ); } if ( $psi < 621 ) { return ( $psi / .515 ); } if ( $psi < 634 ) { return ( $psi / .516 ); } return ( $psi / .517 ); } sub bhn2psi { # very approximately converts brinell hardness number to steel uts psi # source: http://www.monachos.gr/en/resources/hardness_conversion.asp # qv alt: http://mdmetric.com/tech/hardness.htm # this table provides approx 2% error in conversion my ( $bhn ) = @_; if ( $bhn =~ /[^0-9\.\s]/ ) { return ("error"); } if ( $bhn < 127 ) { return ( $bhn * 500 ); } if ( $bhn < 131 ) { return ( $bhn * 496 ); } if ( $bhn < 138 ) { return ( $bhn * 489 ); } if ( $bhn < 144 ) { return ( $bhn * 497 ); } if ( $bhn < 150 ) { return ( $bhn * 490 ); } if ( $bhn < 157 ) { return ( $bhn * 487 ); } if ( $bhn < 168 ) { return ( $bhn * 485 ); } if ( $bhn < 171 ) { return ( $bhn * 488 ); } if ( $bhn < 175 ) { return ( $bhn * 489 ); } if ( $bhn < 184 ) { return ( $bhn * 486 ); } if ( $bhn < 188 ) { return ( $bhn * 481 ); } if ( $bhn < 193 ) { return ( $bhn * 484 ); } if ( $bhn < 198 ) { return ( $bhn * 482 ); } if ( $bhn < 202 ) { return ( $bhn * 488 ); } if ( $bhn < 208 ) { return ( $bhn * 483 ); } if ( $bhn < 213 ) { return ( $bhn * 481 ); } if ( $bhn < 218 ) { return ( $bhn * 484 ); } if ( $bhn < 230 ) { return ( $bhn * 485 ); } if ( $bhn < 236 ) { return ( $bhn * 489 ); } if ( $bhn < 242 ) { return ( $bhn * 490 ); } if ( $bhn < 249 ) { return ( $bhn * 492 ); } if ( $bhn < 256 ) { return ( $bhn * 494 ); } if ( $bhn < 263 ) { return ( $bhn * 492 ); } if ( $bhn < 270 ) { return ( $bhn * 494 ); } if ( $bhn < 294 ) { return ( $bhn * 495 ); } if ( $bhn < 303 ) { return ( $bhn * 497 ); } if ( $bhn < 322 ) { return ( $bhn * 498 ); } if ( $bhn < 332 ) { return ( $bhn * 501 ); } if ( $bhn < 342 ) { return ( $bhn * 499 ); } if ( $bhn < 353 ) { return ( $bhn * 500 ); } if ( $bhn < 376 ) { return ( $bhn * 501 ); } if ( $bhn < 389 ) { return ( $bhn * 503 ); } if ( $bhn < 402 ) { return ( $bhn * 504 ); } if ( $bhn < 430 ) { return ( $bhn * 506 ); } if ( $bhn < 445 ) { return ( $bhn * 507 ); } if ( $bhn < 462 ) { return ( $bhn * 510 ); } if ( $bhn < 478 ) { return ( $bhn * 509 ); } if ( $bhn < 496 ) { return ( $bhn * 511 ); } if ( $bhn < 515 ) { return ( $bhn * 512 ); } if ( $bhn < 535 ) { return ( $bhn * 513 ); } if ( $bhn < 602 ) { return ( $bhn * 514 ); } if ( $bhn < 628 ) { return ( $bhn * 515 ); } if ( $bhn < 631 ) { return ( $bhn * 514 ); } if ( $bhn < 639 ) { return ( $bhn * 516 ); } return ( $bhn * 517 ); } sub hv2bhn { # very approximately converts hardness vickers to brinell hardness number (10/3000 WC method) my ( $hv ) = @_; if ( $hv =~ /[^0-9\.\s]/ ) { return ("error"); } if ( $hv < 701 ) { return ( $hv * 0.92857 ); } if ( $hv < 826 ) { return ( $hv * 0.70588 + 156 ); } if ( $hv < 851 ) { return ( $hv * 0.56770 + 270 ); } if ( $hv < 1001 ) { return ( $hv * 0.34417 + 460 ); } if ( $hv < 1201 ) { return ( $hv * 0.17210 + 632 ); } return ( $hv * 0.08605 + 735 ); } sub bhn2hv { # very approximately converts brinell hardness number (10/3000 WC method) to hardness vickers my ( $bhn ) = @_; if ( $bhn =~ /[^0-9\.\s]/ ) { return ("error"); } if ( $bhn > 838 ) { return ( ( $bhn - 735 ) / 0.08605 ); } if ( $bhn > 804 ) { return ( ( $bhn - 632 ) / 0.17210 ); } if ( $bhn > 752 ) { return ( ( $bhn - 460 ) / 0.34417 ); } if ( $bhn > 738 ) { return ( ( $bhn - 270 ) / 0.56770 ); } if ( $bhn > 650 ) { return ( ( $bhn - 156 ) / 0.70588 ); } return ( $bhn / 0.92857 ); } sub hrc2bhn { # very approximately converts rockwell hardness C to BHN (10/3000 WC) # FIXME -- can segment into ranges to improve accuracy # actual calculated absolute # HRC BHN BHN error # 20 228 228.833 0.833 # 30 286 288.475 2.475 # 40 371 366.717 -4.283 # 50 482 482.458 0.458 # 60 657 654.6 -2.400 my ( $hrc ) = @_; my ( $bhn ); if ( $hrc > 65 ) { return ( -1 ); } # error condition -- invalid range if ( $hrc < 15 ) { return ( -1 ); } # error condition -- invalid range $bhn = 89.75 + (28.5125*$hrc/3) - (0.1905*($hrc**2)) + (0.00315*($hrc**3)); return ( int ( 0.5 + $bhn ) ); # eliminating illusion of accuracy } sub boxes # params: length, width, height, thick / front, back, side, top, den { my ( $inx, $iny, $inz, $tf, $tb, $ts, $tt, $den ) = @_; my ( $inv, $ouv, $vdiff, $mass, $retval ); $inv = $inx * $iny * $inz; $ouv = ( $inx + $tf + $tb ) * ( $iny + 2 * $ts ) * ( $inz + $tt ); $vdiff = $ouv - $inv; $mass = $vdiff * $den; $lbs = int ( ( $mass * 2.2 / 100 ) + 0.5 ) / 10; $rat = int ( $inv * 100 / $mass ) / 100; $retval = ("inv: $inv ouv: $vdiff m: $mass ($lbs lbs) = $rat"); return ( $retval ); } sub gimme_stock { my ( $tick, $what ) = @_; my ( $txt, $res ); $what = 'now' if ( !defined($what) || ( $what eq '' ) ); $tick = join ( '+', split ( /\s+/s, $tick ) ) if ( $tick =~ /\s/s ); $txt = get ( "http://finance.yahoo.com/q?s=$tick" ); if ( $what eq 'now' ) { $res = $1 if ( $txt =~ /Last Trade:.*?\[^\d]*([\d\.]+)/s ); $res *= -1 if ( defined($res) && ( $1 eq 'Down' ) ); } elsif ( $what eq 'high' ) { $res = $1 if ( $txt =~ /Day.s Range:.*?[\d\.]+\s+\-\s+([\d\.]+)/s ); } elsif ( $what eq 'low' ) { $res = $1 if ( $txt =~ /Day.s Range:.*?([\d\.]+)\s+\-\s+[\d\.]+/s ); } else { # default same as 'now' $res = $1 if ( $txt =~ /Last Trade:.*?\([0-9\.]+)/s ); } $res = 0 if ( !defined($res) ); return ( $res ); } sub me2te # given mass efficiency and density, returns thickness efficiency { my ( $me, $den ) = @_; return ( $me * $den / 7.86 ); } sub te2me # given thickness efficiency and density, returns mass efficiency { my ( $te, $den ) = @_; return ( $te * 7.86 / $den ); } sub pc { # estimate armor penetration capability of a small-arms bullet # input: $grain = bullet mass in grains # $fps = velocity in feet per second # $diam = bullet diameter in inches (default) or mm or cm # $typ = bullet type: hp sp bp sc wc tc du my ( $grain, $fps, $diam, $typ ) = @_; my ( $KED, $RHA ); if ( $diam =~ /([0-9\.]+)mm/ ) { $diam /= 25.4; } if ( $diam =~ /([0-9\.]+)cm/ ) { $diam /= 2.54; } $fps /= 1000; $KED = ($grain ** 1.3) * ($fps ** 1.5) / $diam; # Energy Density if ( $typ eq 'hp' ) { $KED /= 2.0; } # hollowpoint elsif ( $typ eq 'sp' ) { $KED /= 1.7; } # softpoint elsif ( $typ eq 'bp' ) { $KED /= 1.2; } # ballpoint elsif ( $typ eq 'sc' ) { $KED *= 2.0; } # steel-core AP elsif ( $typ eq 'wc' ) { $KED *= 3.0; } # tungsten-core AP (WHA) elsif ( $typ eq 'tc' ) { $KED *= 3.0; } # tungsten-core AP (WHA) elsif ( $typ eq 'du' ) { $KED *= 3.0; } # uranium-core AP $RHA = $KED / 2800; # estimated penetration in mm of RHA $RHA = int ( ($RHA * 100) + 0.5 ) / 100; # more precision than deserved return ( $RHA ); } sub l { my ( $msg ); $msg = join ( "\n*", @_ ); $msg = '%#% LOG BEGIN ['.(localtime()).' '.(time()).'] '."\n".$msg."\n".'%#% LOG END'; return ( $msg ); } sub factor { my ( $x ) = @_; my @primes = ( 71, 67, 61, 59, 53, 47, 43, 41, 37, 31, 29, 23, 19, 17, 13, 11, 7, 5, 3, 2 ); my $ip = $#primes; my $r = ''; while ( ( $ip > -1 ) && ( $x * $x > $primes[ip] ) ) { if ( ($x/$primes[$ip]) == int($x/$primes[$ip]) ) { $x /= $primes[$ip]; $r .= $primes[$ip] . " "; } else { $ip--; } } # END of while $r .= $x if ( $x != 1 ); return ( $r ); } sub bench { my ( $frag, $nsec ) = @_; $nsec = 3 if ( !defined($nsec) || ( $nsec < 1 ) ); my $code = '{ my $t1 = time(); my $t2 = $t1; my $n = 0; my $k = '.$nsec.'; while($t1==$t2){$t1 = time();} $t1 = time() + $k; while ( $t1 > time() ) { $n++; '.$frag.' } $n = int ( ( $n / $k ) + 0.49 ); return $n; }'; eval ( $code ); } sub ponce0 { my ( $cal, $mass, $vel, $c0, $c1 ) = @_; return ('usage: ponce ( cal_mm, mass_grain, vel_fps, targ_shear_psi, targ_density ) = pen_mm') if ( scalar(@_) < 4 ); # c0 = Shearing strength # c1 = Specific density (water = 1 = 1 g/cc) $mass /= 15.432; # converting grains to grams $vel /= 3.280; # converting ft/s to m/s $c0 /= 6894.757; # converting psi to pascal my $depth_cm = ( $mass / (2*$c1*(3.141592*$cal*$cal/4) ) ) * log ( ($c1*$vel*$vel+$c0)/$c0 ); return ( int ( $depth_cm * 100 + 0.5 ) / 10 ); # converting cm to nearest 1/10 mm } sub ponce { my ( $cal, $mass, $vel, $c0, $c1 ) = @_; return ('usage: ponce ( cal_mm, mass_grain, vel_fps, targ_shear_MPa, targ_density ) = pen_cm') if ( scalar(@_) < 4 ); # c0 = Shearing strength # c1 = Specific density (water = 1 = 1 g/cc) $mass /= 15.43; # converting grains to grams $vel /= 3.28; # converting ft/s to m/s $c0 /= 145.04; # converting psi to MPa my $depth_cm = ( $mass / (2*$c1*(3.141592*$cal*$cal/4) ) ) * log ( ($c1*$vel*$vel+$c0)/$c0 ); return ( $depth_cm ); # return ( int ( $depth_cm * 100 + 0.5 ) / 10 ); # converting cm to nearest 1/10 mm } # Copper-lined shaped charge depth of penetration, lifted from Ogorkiewicz's _Design and Development of Fighting Vehicles_, and modified as per _Journal of Battlefield Technology_ Vol 1-1 pp 1, copy of chart from that article can be found at: http://ciar.org/ttk/mbt/news/news.smm.ww2-armor-plate.de5bf54f.0110271532.871cbf@posting.google.com.txt # NOTE: does not take into account any advanced effects from composited targets. # NOTE: removed jet density parameter from argument list because liner ductility effects eclipse liner density in practice, and I don't want to factor liner ductility into this code right now. (eg: according to Ogorkiewicz, a steel liner would increase penetration, but in practice steel liners reduce penetration due to their relatively low ductility.) # NOTE: Ogorkiewicz's curve for nonprecision charges was a little low at optimal standoff and high elsewhere relative to the _JoBT_ article chart, so I split the difference. I suspect his precision charge curve is similarly a bit optimistic in favor of higher penetration, so take it with a grain of salt. sub heat_dop { my ( $diam, $soff, $aden, $prec, $hard ) = @_; my $jden; my $pen = 0; return ("usage: heat_dop ( diameter, standoff[mm], [targ-den], [precision], [hard] )\nDiameter units is mm\nstandoff assumed cd's unless 'mm' is specified\ntarget density default is 7.86 (RHA)\nprecision default is 0 (non-precision), 1 for high precision\nhardness is BHN, default is 300 (ignore if not steel)\nReturns depth of penetration in mm") unless ( defined ( $soff ) ); setundef ( \$aden, 7.86 ); setundef ( \$jden, 8.96 ); setundef ( \$prec, 0 ); setundef ( \$hard, 300 ); print (" aden=$aden jden=$jden prec=$prec hard=$hard\n"); $aden = 0.00000000001 if ( $aden < 0.00000000001 ); # avoid divide-by-zero error if ( $soff =~ /(\d+)mm/ ) { $soff = $1 / $diam; } # normalize to factor of cone diameters # $soff *= $hard / 300; # I'm guessing here -- target hardness does appear to shift optimal standoff, but effects of nonoptimal standoff not quite proportional to this relation. if ( $prec != 0 ) { # precision shaped charge DoP curve looks something like this: if ( $soff <= 1 ) { $pen = 3.0 + 1.500 * ( $soff - 0 ); } elsif ( $soff <= 3 ) { $pen = 4.5 + 0.350 * ( $soff - 1 ); } elsif ( $soff <= 6 ) { $pen = 5.2 + 0.100 * ( $soff - 3 ); } elsif ( $soff <= 9 ) { $pen = 5.5 - 0.033 * ( $soff - 6 ); } else { $pen = 5.4 - ( ( $soff - 9 ) / 4.117 ); } } else { # nonprecision shaped charge DoP curve looks something like this: if ( $soff <= 1 ) { $pen = 3.00 + 1.200 * ( $soff - 0 ); } elsif ( $soff <= 2 ) { $pen = 4.20 + 0.150 * ( $soff - 1 ); } elsif ( $soff <= 3 ) { $pen = 4.35 - 0.150 * ( $soff - 2 ); } elsif ( $soff <= 4 ) { $pen = 4.20 - 0.400 * ( $soff - 3 ); } elsif ( $soff <= 7 ) { $pen = 4.00 - 0.550 * ( $soff - 4 ); } elsif ( $soff <= 10 ) { $pen = 2.35 - 0.170 * ( $soff - 7 ); } else { $pen = 1.84 - ( ( $soff - 10 ) / 7.95 ); } } $pen *= ( ( $jden / $aden ) / ( 8.96 / 7.86 ) ) ** 0.5; $pen *= $diam; # $pen /= 1.75 * ( $hard / 300 ); # again, I'm guessing here, isn't really right # round off, this is *not* any kind of precise estimate! $pen = int ( $pen + 0.5 ); return ( $pen ); } # Main gun recoil forces, lifted from Ogorkiewicz's _Design and Development of Fighting Vehicles_, page 58 # This should not exceed twice the vehicle's mass, or it might roll over when firing. # Propellant mass and velocity will be estimated relative to projectile mass if not actually specified (TODO: factor in projectile velocity), but this will likely be somewhat inaccurate. sub recoil { my ( $w_g, $w_p, $v_p, $w_e, $v_e, $rl, $engmet ) = @_; my ( $gees, $force ); return ("usage: recoil ( gun_mass, proj_mass, proj_vel, [gas_mass], [gas_vel], [recoil|{1 inch}], [units:{e,m}]") unless ( defined ( $v_p ) ); setundef ( \$gees, 9.80665 ); setundef ( \$rl, '1 inch' ); setundef ( \$engmet, 'm' ); $gees = u_cvt ( $gees, 'meters / sec / sec', 'feet / sec / sec' ); $rl = u_cvt ( $rl, 'cm', 'feet' ); $w_p = u_cvt ( $w_p, 'kg', 'pound' ); $v_p = u_cvt ( $v_p, 'meter / sec', 'feet / sec' ); $w_g = u_cvt ( $w_g, 'kg', 'pound' ); setundef ( \$w_e, ($w_p / 2.0) ); my ( $v_p0, $v_p1, $v_p2 ) = ( 0, $v_p*1.5, u_cvt(1000,'meter/sec','feet/sec') ); $v_p0 = $v_p1; $v_p0 = $v_p2 if ( $v_p2 > $v_p1 ); setundef ( \$v_e, $v_p0 ); # print (" ((($w_p*$v_p)+($w_e*$v_e))**2) / ( 2*$gees*$w_g*$rl );\n"); $force = ((($w_p*$v_p)+($w_e*$v_e))**2) / ( 2*$gees*$w_g*$rl ); if ( $engmet eq 'e' ) { $engmet = 'tons'; } elsif ( $engmet eq 'm' ) { $engmet = 'tonnes'; } $force = u_cvt ( $force, 'pounds', $engmet ); if ( $force < 10 ) { $force = int ( $force * 100 + 0.5 ) / 100; } elsif ( $force < 100 ) { $force = int ( $force * 10 + 0.5 ) / 10; } else { $force = int ( $force * 1 + 0.5 ) / 1; } return ( $force ); } sub u_cvt # convert value (assumed of type default_unit if no units given) to equivalent value in different units { my ( $v, $def_u, $dest_u ) = @_; $v = "$v $def_u" if ( $v =~ /^\s*[\.\-\d]+\s*$/ ); return ( units ( $v, $dest_u ) ); } sub setundef { ${$_[0]} = $_[1] unless ( defined ( ${$_[0]} ) ); } sub absolute { $_[0] = 0 - $_[0] if ( $_[0] < 0 ); return $_[0]; } sub sigma { my $n = scalar(@_); my $tot = 0; my $sqs = 0; my ( $avg, $sig ); foreach my $term ( @_ ) { $tot += $term; } $avg = $tot / $n; foreach my $term ( @_ ) { my $dif = absolute ( $term - $avg ); $sqs += $dif * $dif; } $sig = ( $sqs / $n ) ** 0.5; return ( $sig ); } sub average { my ( @ar ) = @_; my $n = scalar(@ar); my $sum = 0; return ( 0 ) if ( $n < 1 ); foreach my $x ( @ar ) { $sum += $x; } return ( $sum / $n ); } sub hash_to_string { my ( $hr ) = @_; my $ret = ''; foreach my $k ( sort ( keys ( %{$hr} ) ) ) { my $v = $hr->{$k}; my $t = 's'; $t = 'i' if ( $v =~ /^\d+$/ ); if ( ref($v) ne '' ) { $v = substr ( ref($v), 0, 2 ); $t = 'r'; } $v = "'$v'" if ( $t eq 's' ); $ret .= "$k=$v "; } chop ( $ret ); return ( $ret ); } # BEGIN following functions used to implement gunspec(): # spec2force ( psi, diam ): returns force generated when a cylinder # of the given diameter is charged to the given pressure. Diameter # can be given in inches (eg, "4.5 in") or millimeters ("88 mm"), # and untyped measures will be assumed to be inches. # Returns a float representing force in Newtons (ie, (kg*m)/(s**2)). sub spec2force { my ( $psi, $diam ) = @_; my ( $area, $ret ); # convert PSI to N/mm**2 $psi /= 145; # convert diam to mm**2 if ( $diam =~ /([0-9\.]+)\s*i/i ) { $diam = $1 * 25.4; } elsif ( $diam =~ /([0-9\.]+)/ ) { $diam = $1; } else { return ("ERROR: bad diameter"); } # calculate surface area from diameter $area = $MY_PI * ( ( $diam / 2 )**2 ); # calculate force $ret = $psi * $area; return ( $ret ); } # spec2throw ( force, distance, mass ): given a mass thrown the given # distance when applied to by a constant force over that distance, returns # a whitespace-delimited list of values: the time taken to cross that # distance in seconds, its velocity at that point in m/s, that velocity # in ft/s, and its kinetic energy in (kg*m**2)/(s**2). # The force parameter must be in unit (kg*m)/(s**2) (eg, the return value # of spec2force), distance can be in inches, cm, centimeters, feet, ft, or # meters (when untyped, assumed meters) eg: "76 in", and mass can be in # ounces, oz, pounds, lbs, grams, g, kg, or kilograms. sub spec2throw { my ( $force, $distance, $mass ) = @_; my ( $accel, $t, $vms, $vfs, $ke, $ret ); # convert distance to meters if ( $distance =~ /([0-9\.]+)\s*i/i ) { $distance = $1 / 39.370079; } elsif ( $distance =~ /([0-9\.]+)\s*f/i ) { $distance = $1 / 3.2808399; } elsif ( $distance =~ /([0-9\.]+)\s*c/i ) { $distance = $1 / 100; } elsif ( $distance =~ /([0-9\.]+)\s*mm/i ) { $distance = $1 / 1000; } elsif ( $distance =~ /([0-9\.]+)/ ) { $distance = $1; } else { return ("ERROR: bad distance"); } # convert mass to kg if ( $mass =~ /([0-9\.]+)\s*o/ ) { $mass = $1 / 35.273962; } elsif ( $mass =~ /([0-9\.]+)\s*p/ ) { $mass = $1 / 2.2046226; } elsif ( $mass =~ /([0-9\.]+)\s*l/ ) { $mass = $1 / 2.2046226; } elsif ( $mass =~ /([0-9\.]+)\s*gr/) { $mass = $1 / 15432.358; } elsif ( $mass =~ /([0-9\.]+)\s*g/ ) { $mass = $1 / 1000; } elsif ( $mass =~ /([0-9\.]+)/ ) { $mass = $1; } else { return ("ERROR: bad mass"); } $accel = $force / $mass; # s = 1/2 at**2, so t = sqrt(2s/a) $t = ( 2 * $distance / $accel ) ** (1/2); # v = at $vms = $accel * $t; $vfs = $vms * 3.2808399; # energy = 1/2 mass * velocity**2 $ke = ( $mass * ( $vms**2 ) ) / 2; $ret = "$t $vms $vfs $ke"; return ( $ret ); } sub rndto { my ( $x, $n ) = @_; my ( $ret ); $ret = int(($x*(10**$n))+0.5) / (10**$n); if ( $n == 0 ) { return ( $ret ); } if ( $ret !~ /\./ ) { return ("$ret.".("0"x$n)); } while ( $ret !~ /\.../ ) { $ret .= '0'; } return ( $ret ); } sub specglob { my ( $psi, $diam, $distance, $mass ) = @_; my ( $ret, @alist ); my ( $t, $vms, $vfs, $ke, $dis, $lob ); my ( $xvel, $xt ); $ret = spec2throw ( spec2force ( $psi, $diam ), $distance, $mass ); @alist = split ( /\s+/, $ret ); $vms = $alist[1]; $xvel = ( ($vms**2) / 2 )**(1/2); $xt = $xvel / 4.9; $dis = $vms * $xt; $lob = $dis; $dis = pads ( rndto($dis,2), 9 ); $rng = int ( ( $vms * $vms**.5 / 2.5 ) + 0.5 ); $lob += $rng; $lob /= 2; # $t = pads ( rndto($alist[0],6), 9 ); # $vms = pads ( rndto($alist[1],2), 7 ); # $vft = pads ( rndto($alist[2],2), 7 ); # $ke = pads ( rndto($alist[3],0), 8 ); # $ret = "$t sec $vms m/s $vft f/s $ke N*m $dis m/th $rng m/est"; $t = rndto($alist[0],6); $vms = rndto($alist[1],2); $vft = rndto($alist[2],2); $ke = rndto($alist[3],0); $lob = rndto($lob,0); $ret = {}; $ret->{'tm'} = $t; $ret->{'m/s'} = $vms; $ret->{'f/s'} = $vft; $ret->{'N*m'} = $ke; $ret->{'r,m'} = $lob; return ( $ret ); } sub gunspec { my ( $psi, $diam, $dist, $mass, $minimize ) = @_; return ( "usage: psi, diameter, distance, mass, [minimize]" ) unless ( defined($mass) ); my $res = specglob ( $psi, $diam, $dist, $mass ); if ( defined($minimize) && ( $minimize != 0 ) ) { delete ( $res->{'N*m'} ); delete ( $res->{'tm'} ); } return ( $res ); } # END of gunspec() sub r2d { my ( $r ) = @_; return ( 360 * $r / ( 2*$MY_PI) ); } sub tokenize_list { my ( $raw, $delim ) = @_; $delim = $delim || ' '; my %delh = (); foreach my $c ( split ( //, $delim ) ) { $delh{$c} = 1; } my @d = split ( //, $raw ); push ( @d, ' ' ); my @toklist = (); my $buf = ''; my $state = 0; # 0: tween strings, 1: in ' string, 2: in " string, 3: in non-string for ( my ( $i, $k ) = ( 0, scalar(@d)-1 ); $i < $k; $i++ ) { if ( $state == 0 ) { my $c = $d[$i]; next if ( defined ( $delh{$c} ) ); if ( $c eq "'" ) { $buf .= $c; $state = 1; } elsif ( $c eq '"' ) { $buf .= $c; $state = 2; } else { $buf .= $c; $state = 3; } } elsif ( $state == 1 ) { if ( ( $c eq '\\' ) && ( ($i+1) >= $k ) ) { $buf .= $c; } elsif ( ( $c eq "'" ) && ( $d[$i+1] eq "'" ) ) { $i++; $buf .= $d[$i]; } elsif ( $c eq '\\' ) { $i++; $buf .= $d[$i]; } elsif ( $c eq "'" ) { $buf .= $c; push ( @toklist, $buf ); $buf = ''; $state = 0; } else { $buf .= $c; } } elsif ( $state == 2 ) { if ( ( $c eq '\\' ) && ( ($i+1) >= $k ) ) { $buf .= $c; } elsif ( ( $c eq '"' ) && ( $d[$i+1] eq '"' ) ) { $i++; $buf .= $d[$i]; } elsif ( $c eq '\\' ) { $i++; $buf .= $d[$i]; } elsif ( $c eq '"' ) { $buf .= $c; push ( @toklist, $buf ); $buf = ''; $state = 0; } else { $buf .= $c; } } elsif ( $state == 3 ) { if ( defined ( $delh{$c} ) ) { push ( @toklist, $buf ); $buf = ''; $state = 0; } else { $buf .= $c; } } } return ( @toklist ); } # based on Anderson TN, from _Accuracy of Perforation Equations_, less 11% correction per that paper's conclusions, and including adjustments from Lakowski for scale, material, and backsurface effects, qv: http://63.99.108.76/forums/index.php?showtopic=8332&pid=156211&mode=threaded&show=&st=& and http://63.99.108.76/forums/index.php?showtopic=10482&st=100 sub anderson { my ( $len, $diam, $vel, $material, $deg_angle, $scaling ) = @_; return ( "usage: pen_cm = anderson ( length_cm, diam_cm, vel_kps, material, [ deg_angle ]" ) unless ( defined ( $len ) && defined ($vel) ); $scaling = 1.0 unless ( defined ( $scaling ) ); $material = 1.0 unless ( defined ( $material ) ); # 1.00 = WHA $material = lc ( $material ); $material = 1.00 if ( $material eq 'wha' ); $material = 1.13 if ( $material eq 'du' ); $material = 1.20 if ( $material eq 'duv' ); $material = 0.50 if ( $material eq 'steel' ); $material = 0.50 if ( $material =~ /[^\d\.]/ ); $deg_angle = 0 unless ( defined ( $deg_angle ) ); my $angle = $MY_PI * $def_angle / 180; # convert degrees to radians my $baseline = ( 1.044 * $vel ) - ( 0.194 * log($len/$diam) ) - 0.212; my $scale_effect = 1 + ( $diam / ( 13 * $scaling ) ); my $backsurface = $diam / cos ( $angle ); my $base_pen = $baseline * $scale_effect * $len; my $penetration = $base_pen * $material; $penetration = $penetration + $backsurface; $penetration = $penetration * .89; $penetration = int ( $penetration * 10 + 0.5 ) / 10; return ( $penetration ); } $no = $CALC_nopush; "ok";