line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Physics::Ballistics::External; |
2
|
1
|
|
|
1
|
|
118861
|
use strict; |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
29
|
|
3
|
1
|
|
|
1
|
|
6
|
use warnings; |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
82
|
|
4
|
|
|
|
|
|
|
require Exporter; |
5
|
|
|
|
|
|
|
our @ISA = qw(Exporter); |
6
|
|
|
|
|
|
|
our @EXPORT = qw(ebc flight_simulator g1_drag muzzle_energy muzzle_velocity_from_energy); |
7
|
|
|
|
|
|
|
our $VERSION = '1.03'; |
8
|
|
|
|
|
|
|
|
9
|
1
|
|
|
1
|
|
537
|
use Math::Trig qw(tan asin acos atan pi); |
|
1
|
|
|
|
|
12582
|
|
|
1
|
|
|
|
|
105
|
|
10
|
1
|
|
|
1
|
|
422
|
use Physics::Ballistics; |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
3120
|
|
11
|
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
=head1 NAME |
13
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
Physics::Ballistics::External -- External ballistics formulae. |
15
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
=head1 ABSTRACT |
17
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
External ballistics is the study of projectiles in flight, from the time they |
19
|
|
|
|
|
|
|
leave the barrel (or hand, or trebuchet, or whatever), to the moment before |
20
|
|
|
|
|
|
|
they strike their target. This module implements mathematical formulae and |
21
|
|
|
|
|
|
|
functions useful in the analysis and prediction of external ballistic behavior. |
22
|
|
|
|
|
|
|
|
23
|
|
|
|
|
|
|
=head1 ANNOTATIONS OF SOURCES |
24
|
|
|
|
|
|
|
|
25
|
|
|
|
|
|
|
Regarding their source, these functions fall into three categories: Some are |
26
|
|
|
|
|
|
|
simple encodings of basic physics (like energy = 1/2 * mass * velocity**2), |
27
|
|
|
|
|
|
|
and these will not be cited. Others are from published works, such as books |
28
|
|
|
|
|
|
|
or trade journals, and these will be cited when possible. A few are products |
29
|
|
|
|
|
|
|
of my own efforts, and will be thus annotated. |
30
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
=head1 OOP INTERFACE |
32
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
A more integrated, object-oriented interface for these functions is under |
34
|
|
|
|
|
|
|
development. |
35
|
|
|
|
|
|
|
|
36
|
|
|
|
|
|
|
=head1 FUNCTIONS |
37
|
|
|
|
|
|
|
|
38
|
|
|
|
|
|
|
=head2 ebc (mass_grains, diameter_inches, [shape,] [form_factor]) |
39
|
|
|
|
|
|
|
|
40
|
|
|
|
|
|
|
Attempts to predict the G1 ballistic coefficient of a projectile, based on its |
41
|
|
|
|
|
|
|
mass, width, and shape. Useful for predicting the ballistic behavior of |
42
|
|
|
|
|
|
|
hypothetical or new, untested projectiles. When compared against the known G1 |
43
|
|
|
|
|
|
|
BC's of well-understood projectiles, its predictions usually come within 5% of |
44
|
|
|
|
|
|
|
actual. |
45
|
|
|
|
|
|
|
|
46
|
|
|
|
|
|
|
The "shape" parameter indicates that the hypothetical projectile is closest in |
47
|
|
|
|
|
|
|
its shape, composition, and quality of manufacture to the named entity. Some |
48
|
|
|
|
|
|
|
shapes are very general ("hollowpoint", "fmj") while others are very specific |
49
|
|
|
|
|
|
|
to product families manufactured by particular companies ("scenar", "amax"). |
50
|
|
|
|
|
|
|
|
51
|
|
|
|
|
|
|
The "shape" parameter maps to a numerical form base (see table below), which |
52
|
|
|
|
|
|
|
gets tweaked a little by other factors to derive a form factor. The form |
53
|
|
|
|
|
|
|
factor has an inverse impact on ballistic coefficient (higher form factor |
54
|
|
|
|
|
|
|
means a lower coefficient). In lieu of depending on the "shape" parameter, a |
55
|
|
|
|
|
|
|
form factor may be specified as a parameter (mostly useful for debugging or |
56
|
|
|
|
|
|
|
deriving new entries in the shape table). |
57
|
|
|
|
|
|
|
|
58
|
|
|
|
|
|
|
When no shape or form factor are provided, the "default" shape is used, which |
59
|
|
|
|
|
|
|
has a form base chosen to minimize the error produced by this function when |
60
|
|
|
|
|
|
|
its output is compared to entries in http://www.frfrogspad.com/g1bclist.xls |
61
|
|
|
|
|
|
|
not already covered in the shape table. |
62
|
|
|
|
|
|
|
|
63
|
|
|
|
|
|
|
A hashfile including all of the information from g1bclist.xls with "shape" |
64
|
|
|
|
|
|
|
entries matching the names from the shape table below may be found here: |
65
|
|
|
|
|
|
|
http://ciar.org/ttk/mbt/guns/table.bc.hash |
66
|
|
|
|
|
|
|
|
67
|
|
|
|
|
|
|
This function is the original work of the module's author. |
68
|
|
|
|
|
|
|
|
69
|
|
|
|
|
|
|
=over 4 |
70
|
|
|
|
|
|
|
|
71
|
|
|
|
|
|
|
parameter: (float) mass of the projectile (in grains) |
72
|
|
|
|
|
|
|
|
73
|
|
|
|
|
|
|
parameter: (float) diameter of the projectile (in inches) |
74
|
|
|
|
|
|
|
|
75
|
|
|
|
|
|
|
parameter: (str) OPTIONAL: shape/composition of the projectile (see table below, default is "default") |
76
|
|
|
|
|
|
|
|
77
|
|
|
|
|
|
|
parameter: (float) OPTIONAL: custom form-factor of the projectile, unnecessary if "shape" is provided. |
78
|
|
|
|
|
|
|
|
79
|
|
|
|
|
|
|
returns: a list, containing the following values: |
80
|
|
|
|
|
|
|
|
81
|
|
|
|
|
|
|
* The estimated G1 ballistic coefficient, |
82
|
|
|
|
|
|
|
* The form factor (suitable for use as as form_factor parameter) |
83
|
|
|
|
|
|
|
* The "very short factor" (1.0 for most well-proportioned bullets) |
84
|
|
|
|
|
|
|
* The shape parameter used |
85
|
|
|
|
|
|
|
|
86
|
|
|
|
|
|
|
=back |
87
|
|
|
|
|
|
|
|
88
|
|
|
|
|
|
|
The shape table currently contains the following entities, with the given form bases: |
89
|
|
|
|
|
|
|
|
90
|
|
|
|
|
|
|
'7n14' => 111, # Very deep ogival shape and boat-tail with tight tolerances, used in military 7.62x54R specifically for sniping. |
91
|
|
|
|
|
|
|
'scenar' => 124, # Scenar, by Lapua |
92
|
|
|
|
|
|
|
'scenar_s' => 124, # Scenar Silver, by Lapua, appears ballistically indistinguishable from Scenar |
93
|
|
|
|
|
|
|
'7n1' => 125, # Very deep ogival shape and boat-tail, used in military 7.62x54R. qv http://7.62x54r.net/MosinID/MosinAmmo007.htm |
94
|
|
|
|
|
|
|
'7n6' => 125, # Very deep ogival shape and boat-tail, used in military 5.45x39mm qv http://7.62x54r.net/MosinID/MosinAmmo007.htm |
95
|
|
|
|
|
|
|
'7n6m' => 125, # Synonym for 7N6 |
96
|
|
|
|
|
|
|
'amax' => 127, # A-Max, by Hornady |
97
|
|
|
|
|
|
|
'boat_tail_og' => 128, # Catch-all for many boat-tailed projectiles with long, pointed ogival nose shapes |
98
|
|
|
|
|
|
|
'hollowpoint_ct' => 131, # CT variation of hollowpoint "match" projectiles, by Nosler |
99
|
|
|
|
|
|
|
'bst' => 136, # A type of flat-bottomed ogival, by Nosler |
100
|
|
|
|
|
|
|
'spire_point' => 138, # A type of flat-bottomed ogival, by Speer |
101
|
|
|
|
|
|
|
'boat_tail_nosler' => 138, # Nosler's line of boat-tails perform more poorly than others for some reason |
102
|
|
|
|
|
|
|
'boat_tail_ct' => 139, # Nosler's CT variation of boat-tail |
103
|
|
|
|
|
|
|
'spitzer' => 139, # Another flat-bottomed ogival, by Speer. Good fit to many military bullets. |
104
|
|
|
|
|
|
|
'hollowpoint_match' => 140, # Catch-all for many hollowpoint "match" projectiles |
105
|
|
|
|
|
|
|
'accubond' => 142, # An expansive line by Nosler offering controlled terminal expansion |
106
|
|
|
|
|
|
|
'interbond' => 142, # A line of polymer-tipped bullets by Hornady optimized for terminal cohesion |
107
|
|
|
|
|
|
|
'vmax' => 145, # A line of polymer-tipped bullets by Hornady optimized for terminal expansion / fragmentation |
108
|
|
|
|
|
|
|
'gold_match' => 147, # A type of wadcutter, by Speer |
109
|
|
|
|
|
|
|
'grand_slam' => 159, # Another wadcutter, by Speer, with less streamlined nose shape for higher overall mass |
110
|
|
|
|
|
|
|
'hollowpoint' => 176, # Catch-all for many large-game hollowpoint projectiles |
111
|
|
|
|
|
|
|
'default' => 179, # Catch-all for unknown/unspecified shapes; derived via best-fit to ballistic table |
112
|
|
|
|
|
|
|
'fmj' => 187, # Catch-all for military full metal jacket with boat-tail and short nose |
113
|
|
|
|
|
|
|
'mag_tip' => 190, # Another wadcutter, by Speer |
114
|
|
|
|
|
|
|
'afss' => 202, |
115
|
|
|
|
|
|
|
'tsx_fb' => 205, |
116
|
|
|
|
|
|
|
'plinker' => 205, # Typical of short, underweight, round-nosed, non-streamlined projectiles, |
117
|
|
|
|
|
|
|
'semispitzer' => 224, # A foreshortened, flat-bottomed projectile, by Speer |
118
|
|
|
|
|
|
|
'round_nose' => 228, # Catch-all for many flat-bottomed, hemispherical-nosed projectiles |
119
|
|
|
|
|
|
|
'varminter' => 232, # Catch-all for many light hollowpoints with very large expanding cavities, for varminting |
120
|
|
|
|
|
|
|
'fmj_2' => 268 # Woodleigh's FMJ projectiles, which are shape-optimized for travel in big game meat and bone, instead of air. |
121
|
|
|
|
|
|
|
|
122
|
|
|
|
|
|
|
This hash table is exported as %Physics::Ballistics::External::Bullet_Form_Factors_H, |
123
|
|
|
|
|
|
|
so that users and modules may easily modify/add its content without resorting |
124
|
|
|
|
|
|
|
to editing sources. |
125
|
|
|
|
|
|
|
|
126
|
|
|
|
|
|
|
=cut |
127
|
|
|
|
|
|
|
|
128
|
|
|
|
|
|
|
our %Bullet_Form_Factors_H = ( |
129
|
|
|
|
|
|
|
'7n14' => 111, # Very, very deep ogival shape and boat-tail with tight tolerances, used in military 7.62x54R specifically for sniping. |
130
|
|
|
|
|
|
|
'7n10' => 118, # 7n6 with enhanced penetration and different weight distribution |
131
|
|
|
|
|
|
|
'scenar' => 124, # Scenar, by Lapua |
132
|
|
|
|
|
|
|
'scenar_s' => 124, # Scenar Silver, by Lapua, appears ballistically indistinguishable from Scenar |
133
|
|
|
|
|
|
|
'7n1' => 125, # Russian boat-tail military bullet with very, very deep ogival shape |
134
|
|
|
|
|
|
|
'7n6' => 125, # Russian boat-tail military bullet with very, very deep ogival shape |
135
|
|
|
|
|
|
|
'7n6m' => 125, # Synonym for 7N6 |
136
|
|
|
|
|
|
|
'amax' => 127, # A-Max, by Hornady |
137
|
|
|
|
|
|
|
'boat_tail_og' => 128, # Catch-all for many boat-tailed projectiles with long, pointed ogival nose shapes |
138
|
|
|
|
|
|
|
'hollowpoint_ct' => 131, # CT variation of hollowpoint "match" projectiles, by Nosler |
139
|
|
|
|
|
|
|
'mk318' => 133, # USMC 5.56x45mm Mk318 Mod 0 SOST open-tip/boattail 62gr half-lead/half-copper |
140
|
|
|
|
|
|
|
'bst' => 135, # A type of flat-bottomed ogival, by Nosler |
141
|
|
|
|
|
|
|
'btsp' => 137, # Boat-tailed spire-point from Hornady |
142
|
|
|
|
|
|
|
'spire_point' => 138, # A type of flat-bottomed ogival, by Speer |
143
|
|
|
|
|
|
|
'boat_tail_nosler' => 138, # Nosler's line of boat-tails perform more poorly than others for some reason |
144
|
|
|
|
|
|
|
'boat_tail_ct' => 139, # Nosler's CT variation of boat-tail |
145
|
|
|
|
|
|
|
'spitzer' => 139, # Another flat-bottomed ogival, by Speer |
146
|
|
|
|
|
|
|
'hollowpoint_match' => 140, # Catch-all for many hollowpoint "match" projectiles |
147
|
|
|
|
|
|
|
'accubond' => 142, # An expansive line by Nosler |
148
|
|
|
|
|
|
|
'interbond' => 142, # An expansive line by Hornady |
149
|
|
|
|
|
|
|
'tts' => 143, # Tipped Triple Shock, another line of all-copper bullets from Barnes |
150
|
|
|
|
|
|
|
'vmax' => 145, # A line of polymer-tipped bullets by Hornady optimized for terminal expansion / fragmentation |
151
|
|
|
|
|
|
|
'gold_match' => 147, # A type of wadcutter, by Speer |
152
|
|
|
|
|
|
|
'tsx' => 151, # Another line of all-copper hunting bullets from Barnes |
153
|
|
|
|
|
|
|
'partition' => 152, # A crappy hunting bullet by Nosler |
154
|
|
|
|
|
|
|
'spbt' => 152.5, # Soft-Point Boat-Tail |
155
|
|
|
|
|
|
|
'grand_slam' => 159, # Another wadcutter, by Speer |
156
|
|
|
|
|
|
|
'barnes_xxx' => 161, # Flat-bottomed all-copper hunting round, by Barnes |
157
|
|
|
|
|
|
|
'hollowpoint' => 176, # Catch-all for many large-game hollowpoint projectiles |
158
|
|
|
|
|
|
|
'sp' => 177, # Catch-all for many softpoint hunting projectiles without sharp points |
159
|
|
|
|
|
|
|
'default' => 179, # Catch-all for unknown/unspecified shapes; derived via best-fit to ballistic table |
160
|
|
|
|
|
|
|
'fmj' => 187, # Catch-all for military full metal jacket with boat-tail and short nose |
161
|
|
|
|
|
|
|
'mag_tip' => 190, # Another wadcutter, by Speer |
162
|
|
|
|
|
|
|
'afss' => 202, |
163
|
|
|
|
|
|
|
'tsx_fb' => 205, |
164
|
|
|
|
|
|
|
'plinker' => 205, # Typical of short, underweight, round-nosed, non-streamlined projectiles, |
165
|
|
|
|
|
|
|
'rws_ks' => 210, |
166
|
|
|
|
|
|
|
'semispitzer' => 224, # A foreshortened, flat-bottomed projectile, by Speer |
167
|
|
|
|
|
|
|
'round_nose' => 228, # Catch-all for many flat-bottomed, hemispherical-nosed projectiles |
168
|
|
|
|
|
|
|
'varminter' => 232, # Catch-all for many light hollowpoints with very large expanding cavities, for varminting |
169
|
|
|
|
|
|
|
'fmj_2' => 268, # Woodleigh's FMJ projectiles, which are shape-optimized for travel in big game meat and bone, instead of air. |
170
|
|
|
|
|
|
|
'flat_nose' => 299 # Flat-nosed bullets for tubular magazines |
171
|
|
|
|
|
|
|
); |
172
|
|
|
|
|
|
|
|
173
|
|
|
|
|
|
|
# TODO: Contact Geoffrey Kolbe (Inventor at Border Barrels Limited) and ask for permission to publish his splendid implementations: |
174
|
|
|
|
|
|
|
# * Bullet drag calculator - http://www.border-barrels.com/cgi-bin/drag_working.cgi |
175
|
|
|
|
|
|
|
# * Barrel weight calculator - http://www.border-barrels.com/cgi-bin/swamped_barrel_weight.cgi |
176
|
|
|
|
|
|
|
# |
177
|
|
|
|
|
|
|
# His drag calculator appears far superior to my own ebc function. |
178
|
|
|
|
|
|
|
|
179
|
|
|
|
|
|
|
sub ebc { # estimate ballistic coefficient (G1) from mass, diameter, and shape or form factor |
180
|
|
|
|
|
|
|
# Form factor is based on shape (nose, tail, distance between them), and has inverse impact on BC (higher form factor == lower BC). |
181
|
|
|
|
|
|
|
# Form factor is subject to some modification: |
182
|
|
|
|
|
|
|
# * adjusted for diameter (normalizing on .308 inch diameters) |
183
|
|
|
|
|
|
|
# * adjusted upward at very small L/D (very short shapes) |
184
|
3
|
|
|
3
|
1
|
1701
|
my ($mass_gr, $diam_in, $shape, $ff) = @_; |
185
|
3
|
50
|
|
|
|
14
|
die("bc, form-factor, very-small-factor, shape = ebc(mass_gr, diam_in[, shape[, form-factor]])") unless (defined($diam_in)); |
186
|
3
|
|
|
|
|
7
|
my $vsf = 1; # "very small" factor -- a penalty incurred when length/diameter is too small. |
187
|
3
|
50
|
|
|
|
9
|
$shape = "default" unless (defined($shape)); |
188
|
3
|
50
|
|
|
|
10
|
if (!defined($ff)) { |
189
|
|
|
|
|
|
|
# Form factors of various commercial shapes. Higher ff implies more drag. |
190
|
3
|
|
|
|
|
6
|
my $vsthreshold = 4480; # determined empirically, looking at 45gr 0.224" spitzer, which looks to be just beyond the threshold, and 40gr 0.224" spire_point, which is significantly beyond it. |
191
|
3
|
|
|
|
|
27
|
my $LD = $mass_gr / $diam_in**3; |
192
|
3
|
50
|
|
|
|
12
|
$vsf = ($vsthreshold / $LD) if ($LD < $vsthreshold); |
193
|
3
|
|
|
|
|
7
|
my $ff_base = undef; |
194
|
3
|
50
|
|
|
|
15
|
if (defined($Physics::Ballistics::External::Bullet_Form_Factors_H{$shape})) { |
195
|
3
|
|
|
|
|
8
|
$ff_base = $Physics::Ballistics::External::Bullet_Form_Factors_H{$shape}; |
196
|
|
|
|
|
|
|
} else { |
197
|
0
|
|
|
|
|
0
|
$ff_base = $Physics::Ballistics::External::Bullet_Form_Factors_H{'default'}; |
198
|
0
|
0
|
|
|
|
0
|
$ff_base = 179 unless (defined($ff_base)); |
199
|
0
|
|
|
|
|
0
|
print STDERR "ebc: warning: unknown shape '$shape' and no form factor explicitly provided; assuming default (ff=$ff_base)\n"; |
200
|
|
|
|
|
|
|
} |
201
|
3
|
|
|
|
|
19
|
my $diameter_factor = ($diam_in / .308)**0.541; |
202
|
3
|
|
|
|
|
8
|
$ff = $vsf * $ff_base * $diameter_factor; |
203
|
|
|
|
|
|
|
} # END of if !$ff |
204
|
3
|
|
|
|
|
12
|
my $bc = ($mass_gr**1.25 / (10*$diam_in)**2) / $ff; |
205
|
3
|
|
|
|
|
28
|
return {bc => $bc, ff => $ff, vsf => $vsf, shape => $shape}; |
206
|
|
|
|
|
|
|
} |
207
|
|
|
|
|
|
|
|
208
|
|
|
|
|
|
|
############## BEGIN port of GNU-Ballistics gebc-1.07 lib/ballistics/ballistics.cpp |
209
|
|
|
|
|
|
|
# TODO: Incorporate wobble estimation function (used by improved penetration estimator). |
210
|
|
|
|
|
|
|
# TODO: Port bugfixes and enhancements to Inline::C. The pure-perl performance is horrible. |
211
|
|
|
|
|
|
|
|
212
|
|
|
|
|
|
|
my $GRAVITY = -32.194; |
213
|
|
|
|
|
|
|
my $BCOMP_MAXRANGE = 2000; |
214
|
|
|
|
|
|
|
|
215
|
|
|
|
|
|
|
# Specialty angular conversion functions, straightforward PP port of GNU-Ballistics gebc-1.07 lib/ballistics/ballistics.cpp |
216
|
|
|
|
|
|
|
|
217
|
|
|
|
|
|
|
sub DegtoMOA { |
218
|
0
|
|
|
0
|
0
|
0
|
my ($deg) = @_; |
219
|
0
|
|
|
|
|
0
|
return $deg*60; |
220
|
|
|
|
|
|
|
} |
221
|
|
|
|
|
|
|
sub DegtoRad { |
222
|
33
|
|
|
33
|
0
|
120
|
my ($deg) = @_; |
223
|
33
|
|
|
|
|
235
|
return $deg*pi/180; |
224
|
|
|
|
|
|
|
} |
225
|
|
|
|
|
|
|
sub MOAtoDeg { |
226
|
0
|
|
|
0
|
0
|
0
|
my ($moa) = @_; |
227
|
0
|
|
|
|
|
0
|
return $moa/60; |
228
|
|
|
|
|
|
|
} |
229
|
|
|
|
|
|
|
sub MOAtoRad { |
230
|
30
|
|
|
30
|
0
|
102
|
my ($moa) = @_; |
231
|
30
|
|
|
|
|
178
|
return $moa/60*pi/180; |
232
|
|
|
|
|
|
|
} |
233
|
|
|
|
|
|
|
sub RadtoDeg { |
234
|
1
|
|
|
1
|
0
|
2
|
my ($rad) = @_; |
235
|
1
|
|
|
|
|
5
|
return $rad*180/pi; |
236
|
|
|
|
|
|
|
} |
237
|
|
|
|
|
|
|
sub RadtoMOA { |
238
|
601
|
|
|
601
|
0
|
4262
|
my ($rad) = @_; |
239
|
601
|
|
|
|
|
1397
|
return $rad*60*180/pi; |
240
|
|
|
|
|
|
|
} |
241
|
|
|
|
|
|
|
|
242
|
|
|
|
|
|
|
# Functions for correcting for atmosphere, straightforward PP port of GNU-Ballistics gebc-1.07 lib/ballistics/ballistics.cpp |
243
|
|
|
|
|
|
|
|
244
|
|
|
|
|
|
|
sub calcFR { |
245
|
0
|
|
|
0
|
0
|
0
|
my ($Temperature, $Pressure, $RelativeHumidity) = @_; |
246
|
0
|
|
|
|
|
0
|
my $VPw = (4e-6) * $Temperature**3 - 0.0004 * $Temperature**2 + 0.0234 * $Temperature - 0.2517; |
247
|
0
|
|
|
|
|
0
|
my $FRH = 0.995 * $Pressure / ($Pressure - 0.3783 * $RelativeHumidity * $VPw); |
248
|
0
|
|
|
|
|
0
|
return $FRH; |
249
|
|
|
|
|
|
|
} |
250
|
|
|
|
|
|
|
|
251
|
|
|
|
|
|
|
sub calcFP { |
252
|
0
|
|
|
0
|
0
|
0
|
my ($Pressure) = @_; |
253
|
0
|
|
|
|
|
0
|
my $Pstd = 29.53; # inches-hg |
254
|
0
|
|
|
|
|
0
|
my $FP = ($Pressure - $Pstd) / $Pstd; |
255
|
0
|
|
|
|
|
0
|
return $FP; |
256
|
|
|
|
|
|
|
} |
257
|
|
|
|
|
|
|
|
258
|
|
|
|
|
|
|
sub calcFT { |
259
|
0
|
|
|
0
|
0
|
0
|
my ($Temperature, $Altitude) = @_; |
260
|
0
|
|
|
|
|
0
|
my $Tstd = -0.0036 * $Altitude + 59; |
261
|
0
|
|
|
|
|
0
|
my $FT = ($Temperature - $Tstd) / (459.6 + $Tstd); |
262
|
0
|
|
|
|
|
0
|
return $FT; |
263
|
|
|
|
|
|
|
} |
264
|
|
|
|
|
|
|
|
265
|
|
|
|
|
|
|
sub calcFA { |
266
|
0
|
|
|
0
|
0
|
0
|
my ($Altitude) = @_; |
267
|
0
|
|
|
|
|
0
|
my $fa = (-4e-15) * $Altitude**3 + (4e-10) * $Altitude**2 - (3e-5) * $Altitude + 1; |
268
|
0
|
|
|
|
|
0
|
return 1/$fa; |
269
|
|
|
|
|
|
|
} |
270
|
|
|
|
|
|
|
|
271
|
|
|
|
|
|
|
sub AtmCorrect { |
272
|
0
|
|
|
0
|
0
|
0
|
my ($DragCoefficient, $Altitude, $Barometer, $Temperature, $RelativeHumidity) = @_; |
273
|
0
|
|
|
|
|
0
|
my $FA = calcFA($Altitude); |
274
|
0
|
|
|
|
|
0
|
my $FT = calcFT($Temperature, $Altitude); |
275
|
0
|
|
|
|
|
0
|
my $FR = calcFR($Temperature, $Barometer, $RelativeHumidity); |
276
|
0
|
|
|
|
|
0
|
my $FP = calcFP($Barometer); |
277
|
|
|
|
|
|
|
|
278
|
|
|
|
|
|
|
# Calculate the atmospheric correction factor |
279
|
0
|
|
|
|
|
0
|
my $CD = $FA * (1 + $FT - $FP) * $FR; |
280
|
0
|
|
|
|
|
0
|
return $DragCoefficient * $CD; |
281
|
|
|
|
|
|
|
} |
282
|
|
|
|
|
|
|
|
283
|
|
|
|
|
|
|
# Function for correcting for ballistic drag, straightforward PP port of GNU-Ballistics gebc-1.07 lib/ballistics/ballistics.cpp |
284
|
|
|
|
|
|
|
sub velocity_loss { |
285
|
24717
|
|
|
24717
|
0
|
47572
|
my ($DragFunction, $DragCoefficient, $Velocity) = @_; |
286
|
24717
|
|
|
|
|
36108
|
my $vp = $Velocity; |
287
|
24717
|
|
|
|
|
33708
|
my $val = -1; |
288
|
24717
|
|
|
|
|
31942
|
my $A = -1; |
289
|
24717
|
|
|
|
|
32891
|
my $M = -1; |
290
|
24717
|
50
|
|
|
|
45543
|
if ($DragFunction eq 'G1') { |
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
291
|
24717
|
50
|
|
|
|
118115
|
if ($vp > 4230) { $A = 1.477404177730177e-04; $M = 1.9565; } |
|
0
|
50
|
|
|
|
0
|
|
|
0
|
50
|
|
|
|
0
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
292
|
0
|
|
|
|
|
0
|
elsif ($vp > 3680) { $A = 1.920339268755614e-04; $M = 1.925 ; } |
|
0
|
|
|
|
|
0
|
|
293
|
0
|
|
|
|
|
0
|
elsif ($vp > 3450) { $A = 2.894751026819746e-04; $M = 1.875 ; } |
|
0
|
|
|
|
|
0
|
|
294
|
0
|
|
|
|
|
0
|
elsif ($vp > 3295) { $A = 4.349905111115636e-04; $M = 1.825 ; } |
|
0
|
|
|
|
|
0
|
|
295
|
0
|
|
|
|
|
0
|
elsif ($vp > 3130) { $A = 6.520421871892662e-04; $M = 1.775 ; } |
|
0
|
|
|
|
|
0
|
|
296
|
0
|
|
|
|
|
0
|
elsif ($vp > 2960) { $A = 9.748073694078696e-04; $M = 1.725 ; } |
|
0
|
|
|
|
|
0
|
|
297
|
0
|
|
|
|
|
0
|
elsif ($vp > 2830) { $A = 1.453721560187286e-03; $M = 1.675 ; } |
|
0
|
|
|
|
|
0
|
|
298
|
0
|
|
|
|
|
0
|
elsif ($vp > 2680) { $A = 2.162887202930376e-03; $M = 1.625 ; } |
|
0
|
|
|
|
|
0
|
|
299
|
0
|
|
|
|
|
0
|
elsif ($vp > 2460) { $A = 3.209559783129881e-03; $M = 1.575 ; } |
|
0
|
|
|
|
|
0
|
|
300
|
6704
|
|
|
|
|
10115
|
elsif ($vp > 2225) { $A = 3.904368218691249e-03; $M = 1.55 ; } |
|
6704
|
|
|
|
|
9715
|
|
301
|
8358
|
|
|
|
|
11924
|
elsif ($vp > 2015) { $A = 3.222942271262336e-03; $M = 1.575 ; } |
|
8358
|
|
|
|
|
11547
|
|
302
|
5040
|
|
|
|
|
7707
|
elsif ($vp > 1890) { $A = 2.203329542297809e-03; $M = 1.625 ; } |
|
5040
|
|
|
|
|
6131
|
|
303
|
3289
|
|
|
|
|
4805
|
elsif ($vp > 1810) { $A = 1.511001028891904e-03; $M = 1.675 ; } |
|
3289
|
|
|
|
|
4597
|
|
304
|
1326
|
|
|
|
|
1914
|
elsif ($vp > 1730) { $A = 8.609957592468259e-04; $M = 1.75 ; } |
|
1326
|
|
|
|
|
1869
|
|
305
|
0
|
|
|
|
|
0
|
elsif ($vp > 1595) { $A = 4.086146797305117e-04; $M = 1.85 ; } |
|
0
|
|
|
|
|
0
|
|
306
|
0
|
|
|
|
|
0
|
elsif ($vp > 1520) { $A = 1.954473210037398e-04; $M = 1.95 ; } |
|
0
|
|
|
|
|
0
|
|
307
|
0
|
|
|
|
|
0
|
elsif ($vp > 1420) { $A = 5.431896266462351e-05; $M = 2.125 ; } |
|
0
|
|
|
|
|
0
|
|
308
|
0
|
|
|
|
|
0
|
elsif ($vp > 1360) { $A = 8.847742581674416e-06; $M = 2.375 ; } |
|
0
|
|
|
|
|
0
|
|
309
|
0
|
|
|
|
|
0
|
elsif ($vp > 1315) { $A = 1.456922328720298e-06; $M = 2.625 ; } |
|
0
|
|
|
|
|
0
|
|
310
|
0
|
|
|
|
|
0
|
elsif ($vp > 1280) { $A = 2.419485191895565e-07; $M = 2.875 ; } |
|
0
|
|
|
|
|
0
|
|
311
|
0
|
|
|
|
|
0
|
elsif ($vp > 1220) { $A = 1.657956321067612e-08; $M = 3.25 ; } |
|
0
|
|
|
|
|
0
|
|
312
|
0
|
|
|
|
|
0
|
elsif ($vp > 1185) { $A = 4.745469537157371e-10; $M = 3.75 ; } |
|
0
|
|
|
|
|
0
|
|
313
|
0
|
|
|
|
|
0
|
elsif ($vp > 1150) { $A = 1.379746590025088e-11; $M = 4.25 ; } |
|
0
|
|
|
|
|
0
|
|
314
|
0
|
|
|
|
|
0
|
elsif ($vp > 1100) { $A = 4.070157961147882e-13; $M = 4.75 ; } |
|
0
|
|
|
|
|
0
|
|
315
|
0
|
|
|
|
|
0
|
elsif ($vp > 1060) { $A = 2.938236954847331e-14; $M = 5.125 ; } |
|
0
|
|
|
|
|
0
|
|
316
|
0
|
|
|
|
|
0
|
elsif ($vp > 1025) { $A = 1.228597370774746e-14; $M = 5.25 ; } |
|
0
|
|
|
|
|
0
|
|
317
|
0
|
|
|
|
|
0
|
elsif ($vp > 980) { $A = 2.916938264100495e-14; $M = 5.125 ; } |
|
0
|
|
|
|
|
0
|
|
318
|
0
|
|
|
|
|
0
|
elsif ($vp > 945) { $A = 3.855099424807451e-13; $M = 4.75 ; } |
|
0
|
|
|
|
|
0
|
|
319
|
0
|
|
|
|
|
0
|
elsif ($vp > 905) { $A = 1.185097045689854e-11; $M = 4.25 ; } |
|
0
|
|
|
|
|
0
|
|
320
|
0
|
|
|
|
|
0
|
elsif ($vp > 860) { $A = 3.566129470974951e-10; $M = 3.75 ; } |
|
0
|
|
|
|
|
0
|
|
321
|
0
|
|
|
|
|
0
|
elsif ($vp > 810) { $A = 1.045513263966272e-08; $M = 3.25 ; } |
|
0
|
|
|
|
|
0
|
|
322
|
0
|
|
|
|
|
0
|
elsif ($vp > 780) { $A = 1.291159200846216e-07; $M = 2.875 ; } |
|
0
|
|
|
|
|
0
|
|
323
|
0
|
|
|
|
|
0
|
elsif ($vp > 750) { $A = 6.824429329105383e-07; $M = 2.625 ; } |
|
0
|
|
|
|
|
0
|
|
324
|
0
|
|
|
|
|
0
|
elsif ($vp > 700) { $A = 3.569169672385163e-06; $M = 2.375 ; } |
|
0
|
|
|
|
|
0
|
|
325
|
0
|
|
|
|
|
0
|
elsif ($vp > 640) { $A = 1.839015095899579e-05; $M = 2.125 ; } |
|
0
|
|
|
|
|
0
|
|
326
|
0
|
|
|
|
|
0
|
elsif ($vp > 600) { $A = 5.71117468873424e-05 ; $M = 1.950 ; } |
|
0
|
|
|
|
|
0
|
|
327
|
0
|
|
|
|
|
0
|
elsif ($vp > 550) { $A = 9.226557091973427e-05; $M = 1.875 ; } |
|
0
|
|
|
|
|
0
|
|
328
|
0
|
|
|
|
|
0
|
elsif ($vp > 250) { $A = 9.337991957131389e-05; $M = 1.875 ; } |
|
0
|
|
|
|
|
0
|
|
329
|
0
|
|
|
|
|
0
|
elsif ($vp > 100) { $A = 7.225247327590413e-05; $M = 1.925 ; } |
|
0
|
|
|
|
|
0
|
|
330
|
0
|
|
|
|
|
0
|
elsif ($vp > 65) { $A = 5.792684957074546e-05; $M = 1.975 ; } |
|
0
|
|
|
|
|
0
|
|
331
|
0
|
|
|
|
|
0
|
elsif ($vp > 0) { $A = 5.206214107320588e-05; $M = 2.000 ; } |
|
0
|
|
|
|
|
0
|
|
332
|
|
|
|
|
|
|
} |
333
|
|
|
|
|
|
|
|
334
|
|
|
|
|
|
|
elsif ($DragFunction eq 'G2') { |
335
|
0
|
0
|
|
|
|
0
|
if ($vp > 1674 ) { $A = .0079470052136733 ; $M = 1.36999902851493; } |
|
0
|
0
|
|
|
|
0
|
|
|
0
|
0
|
|
|
|
0
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
336
|
0
|
|
|
|
|
0
|
elsif ($vp > 1172 ) { $A = 1.00419763721974e-03; $M = 1.65392237010294; } |
|
0
|
|
|
|
|
0
|
|
337
|
0
|
|
|
|
|
0
|
elsif ($vp > 1060 ) { $A = 7.15571228255369e-23; $M = 7.91913562392361; } |
|
0
|
|
|
|
|
0
|
|
338
|
0
|
|
|
|
|
0
|
elsif ($vp > 949 ) { $A = 1.39589807205091e-10; $M = 3.81439537623717; } |
|
0
|
|
|
|
|
0
|
|
339
|
0
|
|
|
|
|
0
|
elsif ($vp > 670 ) { $A = 2.34364342818625e-04; $M = 1.71869536324748; } |
|
0
|
|
|
|
|
0
|
|
340
|
0
|
|
|
|
|
0
|
elsif ($vp > 335 ) { $A = 1.77962438921838e-04; $M = 1.76877550388679; } |
|
0
|
|
|
|
|
0
|
|
341
|
0
|
|
|
|
|
0
|
elsif ($vp > 0 ) { $A = 5.18033561289704e-05; $M = 1.98160270524632; } |
|
0
|
|
|
|
|
0
|
|
342
|
|
|
|
|
|
|
} |
343
|
|
|
|
|
|
|
|
344
|
|
|
|
|
|
|
elsif ($DragFunction eq 'G5') { |
345
|
0
|
0
|
|
|
|
0
|
if ($vp > 1730 ){ $A = 7.24854775171929e-03; $M = 1.41538574492812; } |
|
0
|
0
|
|
|
|
0
|
|
|
0
|
0
|
|
|
|
0
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
346
|
0
|
|
|
|
|
0
|
elsif ($vp > 1228 ){ $A = 3.50563361516117e-05; $M = 2.13077307854948; } |
|
0
|
|
|
|
|
0
|
|
347
|
0
|
|
|
|
|
0
|
elsif ($vp > 1116 ){ $A = 1.84029481181151e-13; $M = 4.81927320350395; } |
|
0
|
|
|
|
|
0
|
|
348
|
0
|
|
|
|
|
0
|
elsif ($vp > 1004 ){ $A = 1.34713064017409e-22; $M = 7.8100555281422 ; } |
|
0
|
|
|
|
|
0
|
|
349
|
0
|
|
|
|
|
0
|
elsif ($vp > 837 ){ $A = 1.03965974081168e-07; $M = 2.84204791809926; } |
|
0
|
|
|
|
|
0
|
|
350
|
0
|
|
|
|
|
0
|
elsif ($vp > 335 ){ $A = 1.09301593869823e-04; $M = 1.81096361579504; } |
|
0
|
|
|
|
|
0
|
|
351
|
0
|
|
|
|
|
0
|
elsif ($vp > 0 ){ $A = 3.51963178524273e-05; $M = 2.00477856801111; } |
|
0
|
|
|
|
|
0
|
|
352
|
|
|
|
|
|
|
} |
353
|
|
|
|
|
|
|
|
354
|
|
|
|
|
|
|
elsif ($DragFunction eq 'G6') { |
355
|
0
|
0
|
|
|
|
0
|
if ($vp > 3236 ) { $A = 0.0455384883480781 ; $M = 1.15997674041274; } |
|
0
|
0
|
|
|
|
0
|
|
|
0
|
0
|
|
|
|
0
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
356
|
0
|
|
|
|
|
0
|
elsif ($vp > 2065 ) { $A = 7.167261849653769e-02; $M = 1.10704436538885; } |
|
0
|
|
|
|
|
0
|
|
357
|
0
|
|
|
|
|
0
|
elsif ($vp > 1311 ) { $A = 1.66676386084348e-03 ; $M = 1.60085100195952; } |
|
0
|
|
|
|
|
0
|
|
358
|
0
|
|
|
|
|
0
|
elsif ($vp > 1144 ) { $A = 1.01482730119215e-07 ; $M = 2.9569674731838 ; } |
|
0
|
|
|
|
|
0
|
|
359
|
0
|
|
|
|
|
0
|
elsif ($vp > 1004 ) { $A = 4.31542773103552e-18 ; $M = 6.34106317069757; } |
|
0
|
|
|
|
|
0
|
|
360
|
0
|
|
|
|
|
0
|
elsif ($vp > 670 ) { $A = 2.04835650496866e-05 ; $M = 2.11688446325998; } |
|
0
|
|
|
|
|
0
|
|
361
|
0
|
|
|
|
|
0
|
elsif ($vp > 0 ) { $A = 7.50912466084823e-05 ; $M = 1.92031057847052; } |
|
0
|
|
|
|
|
0
|
|
362
|
|
|
|
|
|
|
} |
363
|
|
|
|
|
|
|
|
364
|
|
|
|
|
|
|
elsif ($DragFunction eq 'G7') { |
365
|
0
|
0
|
|
|
|
0
|
if ($vp > 4200 ) { $A = 1.29081656775919e-09; $M = 3.24121295355962; } |
|
0
|
0
|
|
|
|
0
|
|
|
0
|
0
|
|
|
|
0
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
366
|
0
|
|
|
|
|
0
|
elsif ($vp > 3000 ) { $A = 0.0171422231434847 ; $M = 1.27907168025204; } |
|
0
|
|
|
|
|
0
|
|
367
|
0
|
|
|
|
|
0
|
elsif ($vp > 1470 ) { $A = 2.33355948302505e-03; $M = 1.52693913274526; } |
|
0
|
|
|
|
|
0
|
|
368
|
0
|
|
|
|
|
0
|
elsif ($vp > 1260 ) { $A = 7.97592111627665e-04; $M = 1.67688974440324; } |
|
0
|
|
|
|
|
0
|
|
369
|
0
|
|
|
|
|
0
|
elsif ($vp > 1110 ) { $A = 5.71086414289273e-12; $M = 4.3212826264889 ; } |
|
0
|
|
|
|
|
0
|
|
370
|
0
|
|
|
|
|
0
|
elsif ($vp > 960 ) { $A = 3.02865108244904e-17; $M = 5.99074203776707; } |
|
0
|
|
|
|
|
0
|
|
371
|
0
|
|
|
|
|
0
|
elsif ($vp > 670 ) { $A = 7.52285155782535e-06; $M = 2.1738019851075 ; } |
|
0
|
|
|
|
|
0
|
|
372
|
0
|
|
|
|
|
0
|
elsif ($vp > 540 ) { $A = 1.31766281225189e-05; $M = 2.08774690257991; } |
|
0
|
|
|
|
|
0
|
|
373
|
0
|
|
|
|
|
0
|
elsif ($vp > 0 ) { $A = 1.34504843776525e-05; $M = 2.08702306738884; } |
|
0
|
|
|
|
|
0
|
|
374
|
|
|
|
|
|
|
} |
375
|
|
|
|
|
|
|
|
376
|
|
|
|
|
|
|
elsif ($DragFunction eq 'G8') { |
377
|
0
|
0
|
|
|
|
0
|
if ($vp > 3571 ) { $A = .0112263766252305 ; $M = 1.33207346655961; } |
|
0
|
0
|
|
|
|
0
|
|
|
0
|
0
|
|
|
|
0
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
378
|
0
|
|
|
|
|
0
|
elsif ($vp > 1841 ) { $A = .0167252613732636 ; $M = 1.28662041261785; } |
|
0
|
|
|
|
|
0
|
|
379
|
0
|
|
|
|
|
0
|
elsif ($vp > 1120 ) { $A = 2.20172456619625e-03; $M = 1.55636358091189; } |
|
0
|
|
|
|
|
0
|
|
380
|
0
|
|
|
|
|
0
|
elsif ($vp > 1088 ) { $A = 2.0538037167098e-16 ; $M = 5.80410776994789; } |
|
0
|
|
|
|
|
0
|
|
381
|
0
|
|
|
|
|
0
|
elsif ($vp > 976 ) { $A = 5.92182174254121e-12; $M = 4.29275576134191; } |
|
0
|
|
|
|
|
0
|
|
382
|
0
|
|
|
|
|
0
|
elsif ($vp > 0 ) { $A = 4.3917343795117e-05 ; $M = 1.99978116283334; } |
|
0
|
|
|
|
|
0
|
|
383
|
|
|
|
|
|
|
} |
384
|
|
|
|
|
|
|
|
385
|
24717
|
50
|
33
|
|
|
138524
|
if ($A != -1 && $M != -1 && $vp > 0 && $vp < 10000 ) { |
|
|
|
33
|
|
|
|
|
|
|
|
33
|
|
|
|
|
386
|
24717
|
|
|
|
|
51395
|
$val = $A * $vp**$M / $DragCoefficient; |
387
|
24717
|
|
|
|
|
59950
|
return $val; |
388
|
|
|
|
|
|
|
} |
389
|
0
|
|
|
|
|
0
|
return -1; |
390
|
|
|
|
|
|
|
} |
391
|
|
|
|
|
|
|
|
392
|
|
|
|
|
|
|
sub Windage { |
393
|
300
|
|
|
300
|
0
|
573
|
my ($WindSpeed, $Vi, $xx, $t) = @_; |
394
|
300
|
|
|
|
|
448
|
my $Vw = $WindSpeed * 17.60; # Convert to inches per second. |
395
|
300
|
|
|
|
|
723
|
return $Vw * ($t - $xx / $Vi); |
396
|
|
|
|
|
|
|
} |
397
|
|
|
|
|
|
|
|
398
|
|
|
|
|
|
|
# Headwind is positive at WindAngle=0 |
399
|
|
|
|
|
|
|
sub HeadWind { |
400
|
0
|
|
|
0
|
0
|
0
|
my ($WindSpeed, $WindAngle) = @_; |
401
|
0
|
|
|
|
|
0
|
my $Wangle = DegtoRad($WindAngle); |
402
|
0
|
|
|
|
|
0
|
return cos($Wangle) * $WindSpeed; |
403
|
|
|
|
|
|
|
} |
404
|
|
|
|
|
|
|
|
405
|
|
|
|
|
|
|
# Positive is from Shooter's Right to Left (Wind from 90 degree) |
406
|
|
|
|
|
|
|
sub CrossWind { |
407
|
0
|
|
|
0
|
0
|
0
|
my ($WindSpeed, $WindAngle) = @_; |
408
|
0
|
|
|
|
|
0
|
my $Wangle = DegtoRad($WindAngle); |
409
|
0
|
|
|
|
|
0
|
return sin($Wangle) * $WindSpeed; |
410
|
|
|
|
|
|
|
} |
411
|
|
|
|
|
|
|
|
412
|
|
|
|
|
|
|
# Equivalent to, but slightly faster than, calling HeadWind and CrossWind separately. |
413
|
|
|
|
|
|
|
# Perl suffers from a couple thousand clock cycles of overhead per function call (about |
414
|
|
|
|
|
|
|
# 1 microsecond of wallclock time on typical 2010's hardware), so consolidating similar |
415
|
|
|
|
|
|
|
# functions is a performance win. |
416
|
|
|
|
|
|
|
sub HeadAndCrossWind { |
417
|
1
|
|
|
1
|
0
|
3
|
my ($WindSpeed, $WindAngle) = @_; |
418
|
1
|
|
|
|
|
2
|
my $Wangle = DegtoRad($WindAngle); |
419
|
1
|
|
|
|
|
12
|
return (cos($Wangle) * $WindSpeed, sin($Wangle) * $WindSpeed); |
420
|
|
|
|
|
|
|
} |
421
|
|
|
|
|
|
|
|
422
|
|
|
|
|
|
|
# Straightforward PP port from GNU-Ballistics gebc-1.07 lib/ballistics/ballistics.cpp |
423
|
|
|
|
|
|
|
sub ZeroAngle { |
424
|
1
|
|
|
1
|
0
|
4
|
my ($DragFunction, $DragCoefficient, $Vi, $SightHeight, $ZeroRange, $yIntercept) = @_; |
425
|
|
|
|
|
|
|
|
426
|
|
|
|
|
|
|
# Numerical Integration variables |
427
|
1
|
|
|
|
|
2
|
my $t = 0; |
428
|
1
|
|
|
|
|
3
|
my $dt = 1/$Vi; # The solution accuracy generally doesn't suffer if its within a foot for each second of time. |
429
|
1
|
|
|
|
|
4
|
my $y = -1 * $SightHeight / 12; |
430
|
1
|
|
|
|
|
2
|
my $x = 0; |
431
|
1
|
|
|
|
|
3
|
my $da; # The change in the bore angle used to iterate in on the correct zero angle. |
432
|
|
|
|
|
|
|
|
433
|
|
|
|
|
|
|
# State variables for each integration loop. |
434
|
|
|
|
|
|
|
# velocity: |
435
|
1
|
|
|
|
|
2
|
my $v = 0; |
436
|
1
|
|
|
|
|
3
|
my $vx = 0; |
437
|
1
|
|
|
|
|
2
|
my $vy = 0; |
438
|
|
|
|
|
|
|
# Last frame's velocity, used for computing average velocity: |
439
|
1
|
|
|
|
|
3
|
my $vx1 = 0; |
440
|
1
|
|
|
|
|
2
|
my $vy1 = 0; |
441
|
|
|
|
|
|
|
# acceleration: |
442
|
1
|
|
|
|
|
2
|
my $dv = 0; |
443
|
1
|
|
|
|
|
3
|
my $dvx = 0; |
444
|
1
|
|
|
|
|
2
|
my $dvy = 0; |
445
|
|
|
|
|
|
|
# Gravitational acceleration: |
446
|
1
|
|
|
|
|
2
|
my $Gx = 0; |
447
|
1
|
|
|
|
|
3
|
my $Gy = 0; |
448
|
|
|
|
|
|
|
|
449
|
1
|
|
|
|
|
3
|
my $angle = 0; # The actual angle of the bore. |
450
|
|
|
|
|
|
|
|
451
|
1
|
|
|
|
|
2
|
my $quit = 0; # We know it's time to quit our successive approximation loop when this is 1. |
452
|
|
|
|
|
|
|
|
453
|
|
|
|
|
|
|
# Start with a very coarse angular change, to quickly solve even large launch angle problems. |
454
|
1
|
|
|
|
|
4
|
$da = DegtoRad(14); |
455
|
|
|
|
|
|
|
|
456
|
|
|
|
|
|
|
# The general idea here is to start at 0 degrees elevation, and increase the elevation by 14 degrees |
457
|
|
|
|
|
|
|
# until we are above the correct elevation. Then reduce the angular change by half, and begin reducing |
458
|
|
|
|
|
|
|
# the angle. Once we are again below the correct angle, reduce the angular change by half again, and go |
459
|
|
|
|
|
|
|
# back up. This allows for a fast successive approximation of the correct elevation, usually within less |
460
|
|
|
|
|
|
|
# than 20 iterations. |
461
|
1
|
|
|
|
|
10
|
for ($angle = 0; $quit == 0; $angle = $angle + $da) { |
462
|
30
|
|
|
|
|
126
|
$vy = $Vi * sin($angle); |
463
|
30
|
|
|
|
|
117
|
$vx = $Vi * cos($angle); |
464
|
30
|
|
|
|
|
68
|
$Gx = $GRAVITY * sin($angle); |
465
|
30
|
|
|
|
|
76
|
$Gy = $GRAVITY * cos($angle); |
466
|
|
|
|
|
|
|
|
467
|
30
|
|
|
|
|
121
|
for (($t, $x, $y) = (0, 0, -1 * $SightHeight/12); $x <= $ZeroRange*3; $t += $dt) { |
468
|
22912
|
|
|
|
|
32880
|
$vy1 = $vy; |
469
|
22912
|
|
|
|
|
29503
|
$vx1 = $vx; |
470
|
22912
|
|
|
|
|
51556
|
$v = ($vx**2 + $vy**2)**0.5; |
471
|
22912
|
|
|
|
|
33397
|
$dt = 1/$v; |
472
|
|
|
|
|
|
|
|
473
|
22912
|
|
|
|
|
42813
|
$dv = velocity_loss ($DragFunction, $DragCoefficient, $v); |
474
|
22912
|
|
|
|
|
42487
|
$dvy = -1 * $dv * $vy / $v * $dt; |
475
|
22912
|
|
|
|
|
33448
|
$dvx = -1 * $dv * $vx / $v * $dt; |
476
|
|
|
|
|
|
|
|
477
|
22912
|
|
|
|
|
30570
|
$vx = $vx + $dvx; |
478
|
22912
|
|
|
|
|
29121
|
$vy = $vy + $dvy; |
479
|
22912
|
|
|
|
|
31800
|
$vy = $vy + $dt * $Gy; |
480
|
22912
|
|
|
|
|
36838
|
$vx = $vx + $dt * $Gx; |
481
|
|
|
|
|
|
|
|
482
|
22912
|
|
|
|
|
35708
|
$x += $dt * ($vx + $vx1) / 2; |
483
|
22912
|
|
|
|
|
33201
|
$y += $dt * ($vy + $vy1) / 2; |
484
|
|
|
|
|
|
|
# Break early to save CPU time if we won't find a solution. |
485
|
22912
|
100
|
100
|
|
|
58162
|
last if ($vy < 0 && $y < $yIntercept); |
486
|
22898
|
50
|
|
|
|
65681
|
last if ($vy>3 * $vx); |
487
|
|
|
|
|
|
|
} |
488
|
|
|
|
|
|
|
|
489
|
30
|
100
|
100
|
|
|
155
|
$da = -1 * $da / 2 if ($y > $yIntercept && $da > 0); |
490
|
30
|
100
|
100
|
|
|
159
|
$da = -1 * $da / 2 if ($y < $yIntercept && $da < 0); |
491
|
|
|
|
|
|
|
|
492
|
30
|
100
|
|
|
|
200
|
$quit = 1 if (abs($da) < MOAtoRad(0.01)); # If our accuracy is sufficient, we can stop approximating. |
493
|
30
|
50
|
|
|
|
132
|
$quit = 1 if ($angle > DegtoRad(45)); # If we exceed the 45 degree launch $angle, then the projectile just won't get there, so we stop trying. |
494
|
|
|
|
|
|
|
} |
495
|
1
|
|
|
|
|
6
|
return RadtoDeg($angle); # Convert to degrees for return value. |
496
|
|
|
|
|
|
|
} |
497
|
|
|
|
|
|
|
|
498
|
|
|
|
|
|
|
=head2 flight_simulator (drag_function, ballistic_coefficient, muzzle_velocity_fps, sight_height_inches, shot_angle_deg, [bore_to_sight_angle_deg,] zero_range_yards, wind_speed_fps, wind_angle_deg, [max_range_yards]) |
499
|
|
|
|
|
|
|
|
500
|
|
|
|
|
|
|
Attempts to predict the flight characteristics of a projectile in flight, providing a data point for every yard of progress it makes downrange. |
501
|
|
|
|
|
|
|
|
502
|
|
|
|
|
|
|
This is a pure-perl port of the "SolveAll" function from GNU-Ballistics gebc-1.07 lib/ballistics/ballistics.cpp, and as slow as one might expect from a pure-perl port of an arithmetic-intensive algorithm. |
503
|
|
|
|
|
|
|
|
504
|
|
|
|
|
|
|
On my system it takes about an eighth of a second to simulate a 1200-yard flight. This may be supplemented at some point with an Inline::C equivalent. |
505
|
|
|
|
|
|
|
|
506
|
|
|
|
|
|
|
Note that most manufacturers report G1 ballistic coefficients. Using the wrong drag function for a given ballistic coefficient will produce ludicrously incorrect results. |
507
|
|
|
|
|
|
|
|
508
|
|
|
|
|
|
|
To ascertain the correct bore elevation to hit a target at a specific distance, change the shot_angle_deg parameter on successive calls to flight_simulator(), and converge on drop_inches == 0.0 at the given range via binary search. I should get around to providing a function for that at some point (GNU-Ballistics has such a function, I just didn't port it). |
509
|
|
|
|
|
|
|
|
510
|
|
|
|
|
|
|
=over 4 |
511
|
|
|
|
|
|
|
|
512
|
|
|
|
|
|
|
parameter: (str) drag_function is exactly one of: 'G1', 'G2', 'G5', 'G6', 'G7', 'G8'. |
513
|
|
|
|
|
|
|
|
514
|
|
|
|
|
|
|
parameter: (float) ballistic_coefficient, qv: http://en.wikipedia.org/wiki/Ballistic_coefficient |
515
|
|
|
|
|
|
|
|
516
|
|
|
|
|
|
|
parameter: (float) muzzle_velocity_fps is the velocity of the projectile at time=0 (feet per second) |
517
|
|
|
|
|
|
|
|
518
|
|
|
|
|
|
|
parameter: (float) sight_height_inches is the distance from the center of the sight to the center of the bore (inches) |
519
|
|
|
|
|
|
|
|
520
|
|
|
|
|
|
|
parameter: (float) shot_angle_deg is the bore elevation (degrees, 0 = horizontal, 90 = vertical) |
521
|
|
|
|
|
|
|
|
522
|
|
|
|
|
|
|
parameter: (float) OPTIONAL: bore_to_sight_angle_deg is the difference in angle between the bore elevation and the sight elevation. Set to undef or -1 to have flight_simulator() calculate it for you from the zero_range_yards parameter (degrees) |
523
|
|
|
|
|
|
|
|
524
|
|
|
|
|
|
|
parameter: (float) wind_speed_fps is the velocity of the wind (feet per second) |
525
|
|
|
|
|
|
|
|
526
|
|
|
|
|
|
|
parameter: (float) wind_angle_deg is the direction the wind is blowing (degrees, 0 = shooting directly into wind, 90 = wind is blowing from the right, perpendicular to flight path, -90 = wind is blowing from the left, perpendicular to flight path) |
527
|
|
|
|
|
|
|
|
528
|
|
|
|
|
|
|
parameter: (float) OPTIONAL: max_range_yards is the maximum range to which the flight will be simulated (yards, default is 2000) |
529
|
|
|
|
|
|
|
|
530
|
|
|
|
|
|
|
returns: a reference to an array of hash references, one per yard, denoting the projectile's disposition when it reaches that range. All data fields are floating-point numbers: |
531
|
|
|
|
|
|
|
|
532
|
|
|
|
|
|
|
range_yards How far downrange the projectile has travelled, in yards. |
533
|
|
|
|
|
|
|
drop_inches How far below the horizontal plane intersecting the muzzle the projectile has travelled, in inches. |
534
|
|
|
|
|
|
|
correction_moa The angle from the muzzle to the projectile in the vertical plane, relative to the path from the muzzle to the target, in minutes. |
535
|
|
|
|
|
|
|
time_seconds How much time has elapsed since leaving the muzzle, in seconds. |
536
|
|
|
|
|
|
|
windage_inches How far in the horizontal plane the projectile has moved due to wind, in inches. |
537
|
|
|
|
|
|
|
windage_moa The angle from the muzzle to the projectile in the horzontal plane, relative to the path from the muzzle to the target, in minutes. |
538
|
|
|
|
|
|
|
vel_fps The velocity of the projectile, in feet per second. |
539
|
|
|
|
|
|
|
vel_horiz_fps The horizontal component of the velocity of the projectile, in feet per second. |
540
|
|
|
|
|
|
|
vel_vert_fps The vertical component of the velocity of the projectile, in feet per second. |
541
|
|
|
|
|
|
|
|
542
|
|
|
|
|
|
|
=back |
543
|
|
|
|
|
|
|
|
544
|
|
|
|
|
|
|
=cut |
545
|
|
|
|
|
|
|
|
546
|
|
|
|
|
|
|
# The solve-all solution. |
547
|
|
|
|
|
|
|
# PP port of "SolveAll" from GNU-Ballistics gebc-1.07 lib/ballistics/ballistics.cpp |
548
|
|
|
|
|
|
|
# $DragFunction is one of 'G1', 'G2', 'G5', 'G6', 'G7', 'G8' |
549
|
|
|
|
|
|
|
# $DragCoefficient is the ballistic coefficient |
550
|
|
|
|
|
|
|
# $Vi is the muzzle velocity of the projectile, feet per second |
551
|
|
|
|
|
|
|
# $SightHeight is the height of the scope in inches |
552
|
|
|
|
|
|
|
# $ShootingAngle is the bore elevation (angle) |
553
|
|
|
|
|
|
|
# $ZAngle is the bore-to-sight angle, or -1 to calculate from $ZRange |
554
|
|
|
|
|
|
|
# $ZRange is the range, in yards, to which the rifle is sighted-in. |
555
|
|
|
|
|
|
|
# $WindSpeed is the speed of the wind, in feet per second |
556
|
|
|
|
|
|
|
# $WindAngle is the angle of the wind relative to the shooter -- coming from the right is positive, coming from the left is negative. |
557
|
|
|
|
|
|
|
# $MaxRange is the maximum range, in yards, to simulate (defaults to $BCOMP_MAXRANGE if -1 or undef) |
558
|
|
|
|
|
|
|
sub flight_simulator { |
559
|
1
|
|
|
1
|
1
|
518
|
my ($DragFunction, $DragCoefficient, $Vi, $SightHeight, $ShootingAngle, $ZAngle, $ZRange, $WindSpeed, $WindAngle, $MaxRange) = @_; |
560
|
1
|
|
|
|
|
4
|
my $ptr = {}; |
561
|
|
|
|
|
|
|
|
562
|
1
|
50
|
|
|
|
9
|
die("usage: ar = flight_simulator(DragFunction, DragCoefficient, Vel_fps, SightHeight_inches, ShootingAngle_deg, ZAngle_deg, ZRange_yards, WindSpeed_fps, WindAngle_deg, MaxRange_yards)\n") unless(defined($DragCoefficient)); |
563
|
|
|
|
|
|
|
|
564
|
1
|
|
|
|
|
3
|
my $t = 0; |
565
|
1
|
|
|
|
|
4
|
my $dt = 0.5 / $Vi; |
566
|
1
|
|
|
|
|
3
|
my $v = 0; |
567
|
1
|
|
|
|
|
2
|
my $vx = 0; # horizontal velocity, feet per second, at beginning of quantum |
568
|
1
|
|
|
|
|
3
|
my $vx1 = 0; # horizontal velocity, feet per second, at end of quantum |
569
|
1
|
|
|
|
|
2
|
my $vy = 0; |
570
|
1
|
|
|
|
|
3
|
my $vy1 = 0; |
571
|
1
|
|
|
|
|
2
|
my $dv = 0; |
572
|
1
|
|
|
|
|
3
|
my $dvx = 0; |
573
|
1
|
|
|
|
|
2
|
my $dvy = 0; |
574
|
1
|
|
|
|
|
24
|
my $x = 0; |
575
|
1
|
|
|
|
|
4
|
my $y = 0; |
576
|
|
|
|
|
|
|
|
577
|
|
|
|
|
|
|
# print ("fs: 0010 ShootingAngle=$ShootingAngle ZAngle=$ZAngle\n"); |
578
|
|
|
|
|
|
|
|
579
|
1
|
50
|
33
|
|
|
9
|
$ZAngle = ZeroAngle($DragFunction, $DragCoefficient, $Vi, $SightHeight, $ZRange, 0) if ($ZAngle == -1 || !defined($ZAngle)); |
580
|
1
|
50
|
33
|
|
|
10
|
$MaxRange = $BCOMP_MAXRANGE unless (defined($MaxRange) && $MaxRange >= 0); |
581
|
1
|
|
|
|
|
3
|
$MaxRange *= 3; # Convert yards to feet. |
582
|
|
|
|
|
|
|
|
583
|
|
|
|
|
|
|
# print ("fs: 0020 ShootingAngle=$ShootingAngle ZAngle=$ZAngle MaxRange=$MaxRange\n"); |
584
|
|
|
|
|
|
|
|
585
|
1
|
|
|
|
|
3
|
my ($headwind, $crosswind) = HeadAndCrossWind($WindSpeed, $WindAngle); |
586
|
1
|
|
|
|
|
10
|
my $Radians = DegtoRad($ShootingAngle + $ZAngle); |
587
|
1
|
|
|
|
|
2
|
my $CosRadians = cos($Radians); |
588
|
1
|
|
|
|
|
2
|
my $SinRadians = sin($Radians); |
589
|
1
|
|
|
|
|
2
|
my $Gy = $GRAVITY * $CosRadians; |
590
|
1
|
|
|
|
|
2
|
my $Gx = $GRAVITY * $SinRadians; |
591
|
|
|
|
|
|
|
|
592
|
|
|
|
|
|
|
# $vx = $Vi * cos(DegtoRad($ZAngle)); |
593
|
|
|
|
|
|
|
# $vy = $Vi * sin(DegtoRad($ZAngle)); |
594
|
1
|
|
|
|
|
2
|
$vx = $Vi * $CosRadians; |
595
|
1
|
|
|
|
|
2
|
$vy = $Vi * $SinRadians; |
596
|
|
|
|
|
|
|
|
597
|
|
|
|
|
|
|
# print ("fs: 0030 vx=$vx vy=$vy\n"); |
598
|
|
|
|
|
|
|
|
599
|
1
|
|
|
|
|
8
|
$y = -1 * $SightHeight / 12; |
600
|
|
|
|
|
|
|
|
601
|
1
|
|
|
|
|
2
|
my $yards_downrange = 0; |
602
|
1
|
|
|
|
|
2
|
my $rv = []; |
603
|
1
|
|
|
|
|
6
|
for ($t = 0; 1; $t += $dt) { |
604
|
1805
|
|
|
|
|
2542
|
$vx1 = $vx; |
605
|
1805
|
|
|
|
|
2376
|
$vy1 = $vy; |
606
|
1805
|
|
|
|
|
3877
|
$v = ($vx**2 + $vy**2)**0.5; |
607
|
1805
|
|
|
|
|
2700
|
$dt = 0.5 / $v; |
608
|
|
|
|
|
|
|
|
609
|
|
|
|
|
|
|
# print ("fs: 0040 vx=$vx vy=$vy\n"); |
610
|
|
|
|
|
|
|
|
611
|
|
|
|
|
|
|
# Compute acceleration using the drag function retardation |
612
|
1805
|
|
|
|
|
3742
|
$dv = velocity_loss($DragFunction, $DragCoefficient, $v + $headwind); |
613
|
1805
|
|
|
|
|
3238
|
$dvx = -1 * ($vx / $v) * $dv; |
614
|
1805
|
|
|
|
|
3031
|
$dvy = -1 * ($vy / $v) * $dv; |
615
|
|
|
|
|
|
|
|
616
|
|
|
|
|
|
|
# Compute velocity, including the resolved gravity vectors. |
617
|
1805
|
|
|
|
|
2856
|
$vx = $vx + $dt * $dvx + $dt * $Gx; |
618
|
1805
|
|
|
|
|
2592
|
$vy = $vy + $dt * $dvy + $dt * $Gy; |
619
|
|
|
|
|
|
|
|
620
|
1805
|
100
|
|
|
|
3840
|
if ($x/3 >= $yards_downrange) { |
621
|
301
|
|
|
|
|
525
|
my $hr = {}; |
622
|
301
|
|
|
|
|
761
|
$hr->{range_yards} = $x / 3; |
623
|
301
|
|
|
|
|
567
|
$hr->{drop_inches} = $y * -12; |
624
|
301
|
|
|
|
|
601
|
$hr->{correction_moa} = 0; |
625
|
301
|
100
|
|
|
|
1195
|
$hr->{correction_moa} = -1 * RadtoMOA(atan($y / $x)) if ($x > 0); |
626
|
301
|
|
|
|
|
656
|
$hr->{time_seconds} = $t + $dt; |
627
|
301
|
|
|
|
|
485
|
$hr->{windage_inches} = 0; |
628
|
301
|
100
|
|
|
|
840
|
$hr->{windage_inches} = Windage($crosswind, $Vi, $x, $t + $dt) if ($x > 0); |
629
|
301
|
|
|
|
|
580
|
$hr->{windage_moa} = 0; |
630
|
301
|
50
|
100
|
|
|
1241
|
$hr->{windage_moa} = RadtoMOA(atan(($hr->{windage_inches} / 12) / (($hr->{range_yards} * 3) || 0.1))) if ($crosswind > 0); |
631
|
301
|
|
|
|
|
646
|
$hr->{vel_fps} = $v; |
632
|
301
|
|
|
|
|
983
|
$hr->{vel_horiz_fps} = $vx; |
633
|
301
|
|
|
|
|
898
|
$hr->{vel_vert_fps} = $vy; |
634
|
301
|
|
|
|
|
430
|
push (@{$rv}, $hr); |
|
301
|
|
|
|
|
625
|
|
635
|
301
|
|
|
|
|
585
|
$yards_downrange++; |
636
|
|
|
|
|
|
|
} |
637
|
|
|
|
|
|
|
|
638
|
|
|
|
|
|
|
# Compute position based on average velocity. |
639
|
1805
|
|
|
|
|
3242
|
$x += $dt * ($vx + $vx1) / 2; |
640
|
1805
|
|
|
|
|
2925
|
$y += $dt * ($vy + $vy1) / 2; |
641
|
|
|
|
|
|
|
|
642
|
|
|
|
|
|
|
# last if (abs($vy) > abs(3 * $vx)); |
643
|
1805
|
50
|
|
|
|
3518
|
last if ($t > 3600); |
644
|
1805
|
100
|
|
|
|
4068
|
last if ($x >= $MaxRange+1.0); |
645
|
|
|
|
|
|
|
} |
646
|
1
|
|
|
|
|
7
|
return $rv; |
647
|
|
|
|
|
|
|
} |
648
|
|
|
|
|
|
|
|
649
|
|
|
|
|
|
|
################# no functions ported from GNU-Ballistics gebc-1.07 lib/ballistics/ballistics.cpp after this line ################## |
650
|
|
|
|
|
|
|
|
651
|
|
|
|
|
|
|
=head2 g1_drag (velocity_fps) |
652
|
|
|
|
|
|
|
|
653
|
|
|
|
|
|
|
The canonical function for computing instantaneous velocity drop at a given velocity, per the G1 drag model. |
654
|
|
|
|
|
|
|
|
655
|
|
|
|
|
|
|
=over 4 |
656
|
|
|
|
|
|
|
|
657
|
|
|
|
|
|
|
parameter: (float) velocity_fps is the velocity of the projectile (in feet per second) |
658
|
|
|
|
|
|
|
|
659
|
|
|
|
|
|
|
returns: (float) the deceleration of the projectile from drag (in feet per second per second) |
660
|
|
|
|
|
|
|
|
661
|
|
|
|
|
|
|
=back |
662
|
|
|
|
|
|
|
|
663
|
|
|
|
|
|
|
=cut |
664
|
|
|
|
|
|
|
|
665
|
|
|
|
|
|
|
# PP implementation of G1 drag model. |
666
|
|
|
|
|
|
|
# Multiply by G1 ballistic coefficient to get instantaneous velocity drop. |
667
|
|
|
|
|
|
|
sub g1_drag { |
668
|
5
|
|
|
5
|
1
|
3788
|
my ($fps) = @_; |
669
|
5
|
100
|
|
|
|
140
|
if ($fps > 4230) { return (1.477404177730177E-04) * ($fps**1.9565); } |
|
1
|
50
|
|
|
|
35
|
|
|
|
100
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
670
|
0
|
|
|
|
|
0
|
elsif ($fps > 3680) { return (1.920339268755614E-04) * ($fps**1.925); } |
671
|
1
|
|
|
|
|
10
|
elsif ($fps > 3450) { return (2.894751026819746E-04) * ($fps**1.875); } |
672
|
0
|
|
|
|
|
0
|
elsif ($fps > 3295) { return (4.349905111115636E-04) * ($fps**1.825); } |
673
|
0
|
|
|
|
|
0
|
elsif ($fps > 3130) { return (6.520421871892662E-04) * ($fps**1.775); } |
674
|
0
|
|
|
|
|
0
|
elsif ($fps > 2960) { return (9.748073694078696E-04) * ($fps**1.725); } |
675
|
0
|
|
|
|
|
0
|
elsif ($fps > 2830) { return (1.453721560187286E-03) * ($fps**1.675); } |
676
|
0
|
|
|
|
|
0
|
elsif ($fps > 2680) { return (2.162887202930376E-03) * ($fps**1.625); } |
677
|
1
|
|
|
|
|
7
|
elsif ($fps > 2460) { return (3.209559783129881E-03) * ($fps**1.575); } |
678
|
0
|
|
|
|
|
0
|
elsif ($fps > 2225) { return (3.904368218691249E-03) * ($fps**1.550); } |
679
|
0
|
|
|
|
|
0
|
elsif ($fps > 2015) { return (3.222942271262336E-03) * ($fps**1.575); } |
680
|
0
|
|
|
|
|
0
|
elsif ($fps > 1890) { return (2.203329542297809E-03) * ($fps**1.625); } |
681
|
0
|
|
|
|
|
0
|
elsif ($fps > 1810) { return (1.511001028891904E-03) * ($fps**1.675); } |
682
|
0
|
|
|
|
|
0
|
elsif ($fps > 1730) { return (8.609957592468259E-04) * ($fps**1.750); } |
683
|
0
|
|
|
|
|
0
|
elsif ($fps > 1595) { return (4.086146797305117E-04) * ($fps**1.850); } |
684
|
0
|
|
|
|
|
0
|
elsif ($fps > 1520) { return (1.954473210037398E-04) * ($fps**1.950); } |
685
|
1
|
|
|
|
|
7
|
elsif ($fps > 1420) { return (5.431896266462351E-05) * ($fps**2.125); } |
686
|
0
|
|
|
|
|
0
|
elsif ($fps > 1360) { return (8.847742581674416E-06) * ($fps**2.375); } |
687
|
0
|
|
|
|
|
0
|
elsif ($fps > 1315) { return (1.456922328720298E-06) * ($fps**2.625); } |
688
|
0
|
|
|
|
|
0
|
elsif ($fps > 1280) { return (2.419485191895565E-07) * ($fps**2.875); } |
689
|
0
|
|
|
|
|
0
|
elsif ($fps > 1220) { return (1.657956321067612E-08) * ($fps**3.250); } |
690
|
0
|
|
|
|
|
0
|
elsif ($fps > 1185) { return (4.745469537157371E-10) * ($fps**3.750); } |
691
|
0
|
|
|
|
|
0
|
elsif ($fps > 1150) { return (1.379746590025088E-11) * ($fps**4.250); } |
692
|
0
|
|
|
|
|
0
|
elsif ($fps > 1100) { return (4.070157961147882E-13) * ($fps**4.750); } |
693
|
0
|
|
|
|
|
0
|
elsif ($fps > 1060) { return (2.938236954847331E-14) * ($fps**5.125); } |
694
|
0
|
|
|
|
|
0
|
elsif ($fps > 1025) { return (1.228597370774746E-14) * ($fps**5.250); } |
695
|
0
|
|
|
|
|
0
|
elsif ($fps > 980) { return (2.916938264100495E-14) * ($fps**5.125); } |
696
|
0
|
|
|
|
|
0
|
elsif ($fps > 945) { return (3.855099424807451E-13) * ($fps**4.750); } |
697
|
0
|
|
|
|
|
0
|
elsif ($fps > 905) { return (1.185097045689854E-11) * ($fps**4.250); } |
698
|
0
|
|
|
|
|
0
|
elsif ($fps > 860) { return (3.566129470974951E-10) * ($fps**3.750); } |
699
|
0
|
|
|
|
|
0
|
elsif ($fps > 810) { return (1.045513263966272E-08) * ($fps**3.250); } |
700
|
0
|
|
|
|
|
0
|
elsif ($fps > 780) { return (1.291159200846216E-07) * ($fps**2.875); } |
701
|
0
|
|
|
|
|
0
|
elsif ($fps > 750) { return (6.824429329105383E-07) * ($fps**2.625); } |
702
|
0
|
|
|
|
|
0
|
elsif ($fps > 700) { return (3.569169672385163E-06) * ($fps**2.375); } |
703
|
0
|
|
|
|
|
0
|
elsif ($fps > 640) { return (1.839015095899579E-05) * ($fps**2.125); } |
704
|
0
|
|
|
|
|
0
|
elsif ($fps > 600) { return (5.711174688734240E-05) * ($fps**1.950); } |
705
|
0
|
|
|
|
|
0
|
elsif ($fps > 550) { return (9.226557091973427E-05) * ($fps**1.875); } |
706
|
1
|
|
|
|
|
14
|
elsif ($fps > 250) { return (9.337991957131389E-05) * ($fps**1.875); } |
707
|
0
|
|
|
|
|
0
|
elsif ($fps > 100) { return (7.225247327590413E-05) * ($fps**1.925); } |
708
|
0
|
|
|
|
|
0
|
elsif ($fps > 65) { return (5.792684957074546E-05) * ($fps**1.975); } |
709
|
0
|
|
|
|
|
0
|
elsif ($fps > 0) { return (5.206214107320588E-05) * ($fps**2.000); } |
710
|
0
|
|
|
|
|
0
|
return 0.0; |
711
|
|
|
|
|
|
|
} |
712
|
|
|
|
|
|
|
|
713
|
|
|
|
|
|
|
=head2 muzzle_energy (mass_grains, velocity_fps, [want_joules_bool]) |
714
|
|
|
|
|
|
|
|
715
|
|
|
|
|
|
|
A convenience function for computing kinetic energy from mass and velocity. |
716
|
|
|
|
|
|
|
Despite its name, it is useful for computing the kinetic energy of a projectile |
717
|
|
|
|
|
|
|
at any point during its flight. |
718
|
|
|
|
|
|
|
|
719
|
|
|
|
|
|
|
=over 4 |
720
|
|
|
|
|
|
|
|
721
|
|
|
|
|
|
|
parameter: (float) mass_grains is the mass of the projectile (in grains) |
722
|
|
|
|
|
|
|
|
723
|
|
|
|
|
|
|
parameter: (float) velocity_fps is the velocity of the projectile (in feet per second) |
724
|
|
|
|
|
|
|
|
725
|
|
|
|
|
|
|
parameter: (boolean) OPTIONAL: set want_joules_bool to a True value to get Joules instead of foot-pounds (boolean, default=False) |
726
|
|
|
|
|
|
|
|
727
|
|
|
|
|
|
|
returns: (float) the kinetic energy of the projectile (in foot-pounds or Joules) |
728
|
|
|
|
|
|
|
|
729
|
|
|
|
|
|
|
=back |
730
|
|
|
|
|
|
|
|
731
|
|
|
|
|
|
|
=cut |
732
|
|
|
|
|
|
|
|
733
|
|
|
|
|
|
|
sub muzzle_energy { |
734
|
2
|
|
|
2
|
1
|
836
|
my ($grains, $fps, $wantJ) = @_; |
735
|
2
|
50
|
|
|
|
8
|
die("footpounds = muzzle_energy(grains, fps[, want_Joules])") unless(defined($fps)); |
736
|
2
|
|
|
|
|
7
|
my $nrg = 0.5 * $grains * $fps * $fps / 225312.839; # footpounds |
737
|
2
|
100
|
|
|
|
6
|
$nrg *= 1.3558 if ($wantJ); |
738
|
2
|
|
|
|
|
11
|
return int(100 * $nrg + 0.5) / 100; |
739
|
|
|
|
|
|
|
} |
740
|
|
|
|
|
|
|
|
741
|
|
|
|
|
|
|
=head2 muzzle_velocity_from_energy (mass_grains, energy_ftlbs) |
742
|
|
|
|
|
|
|
|
743
|
|
|
|
|
|
|
A convenience function for computing velocity from mass and kinetic energy. |
744
|
|
|
|
|
|
|
Despite its name, it is useful for computing the velocity of a projectile |
745
|
|
|
|
|
|
|
at any point during its flight. |
746
|
|
|
|
|
|
|
|
747
|
|
|
|
|
|
|
If all you have is Joules, divide by 1.3558179 to get foot-pounds. |
748
|
|
|
|
|
|
|
|
749
|
|
|
|
|
|
|
=over 4 |
750
|
|
|
|
|
|
|
|
751
|
|
|
|
|
|
|
parameter: (float) mass_grains is the mass of the projectile (in grains) |
752
|
|
|
|
|
|
|
|
753
|
|
|
|
|
|
|
parameter: (float) energy_ftlbs is the kinetic energy of the projectile (in foot-pounds) |
754
|
|
|
|
|
|
|
|
755
|
|
|
|
|
|
|
returns: (float) the velocity of the projectile (in feet per second) |
756
|
|
|
|
|
|
|
|
757
|
|
|
|
|
|
|
=back |
758
|
|
|
|
|
|
|
|
759
|
|
|
|
|
|
|
=cut |
760
|
|
|
|
|
|
|
|
761
|
|
|
|
|
|
|
sub muzzle_velocity_from_energy { |
762
|
1
|
|
|
1
|
1
|
423
|
my ($grains, $footpounds) = @_; |
763
|
1
|
50
|
|
|
|
4
|
die("feet/second = muzzle_velocity_from_energy(grains, footpounds)") unless(defined($footpounds)); |
764
|
1
|
|
|
|
|
5
|
my $fps = (450625.678 * $footpounds / $grains) ** 0.5; |
765
|
1
|
|
|
|
|
7
|
return int(100 * $fps + 0.5) / 100; |
766
|
|
|
|
|
|
|
} |
767
|
|
|
|
|
|
|
|
768
|
|
|
|
|
|
|
1; |
769
|
|
|
|
|
|
|
|
770
|
|
|
|
|
|
|
=head1 TODO |
771
|
|
|
|
|
|
|
|
772
|
|
|
|
|
|
|
Contact Geoffrey Kolbe (Inventor at Border Barrels Limited) and ask for permission to publish his splendid implementations: |
773
|
|
|
|
|
|
|
|
774
|
|
|
|
|
|
|
* Bullet drag calculator - http://www.border-barrels.com/cgi-bin/drag_working.cgi |
775
|
|
|
|
|
|
|
|
776
|
|
|
|
|
|
|
* Barrel weight calculator - http://www.border-barrels.com/cgi-bin/swamped_barrel_weight.cgi |
777
|
|
|
|
|
|
|
|
778
|
|
|
|
|
|
|
His drag calculator seems better than my own ebc function. |
779
|
|
|
|
|
|
|
|
780
|
|
|
|
|
|
|
=cut |