ballistics 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: afee8804afd6b1d6c21c972cd12e6a56e0b9a90c
4
+ data.tar.gz: 13a04e599ba668fac98d97addf2f6a9d31ba2ee2
5
+ SHA512:
6
+ metadata.gz: 76f6b65bd5991e05270c03cb7b15d89c67aa5264889fba200fc887135942c3427795849dfa89752a00cd0494fe009da06d7aa6b8e9249445a636918e08fef6fe
7
+ data.tar.gz: 611d4d8ea5f81dd93067b10904487bc393464b2388e6bdcfb280d734490d96c79f510b67430541a76f921d461e5d9da6c20337b2800d9445018da1822521b3b8
@@ -0,0 +1,98 @@
1
+ #include <ruby.h>
2
+ #include <gnu_ballistics.h>
3
+ VALUE method_calculate_zero_angle(VALUE self, VALUE drag_function, VALUE drag_coefficient, VALUE velocity, VALUE sight_height, VALUE zero_range, VALUE y_intercept);
4
+ VALUE method_map_trajectory(VALUE self, VALUE drag_function, VALUE drag_coefficient, VALUE velocity, VALUE sight_height, VALUE shooting_angle, VALUE zero_angle, VALUE wind_speed, VALUE wind_angle, VALUE max_range);
5
+
6
+ VALUE cBallistics;
7
+ VALUE cZero;
8
+ VALUE cTrajectory;
9
+ void Init_ballistics() {
10
+ cBallistics = rb_define_module("Ballistics");
11
+ cZero = rb_define_module_under(cBallistics, "Zero");
12
+ cTrajectory = rb_define_module_under(cBallistics, "Trajectory");
13
+ rb_define_singleton_method(cZero, "_calculate_zero_angle", method_calculate_zero_angle, 6);
14
+ rb_define_singleton_method(cTrajectory, "_map_trajectory", method_map_trajectory, 9);
15
+ }
16
+
17
+ VALUE method_calculate_zero_angle(VALUE self, VALUE drag_function, VALUE drag_coefficient, VALUE velocity, VALUE sight_height, VALUE zero_range, VALUE y_intercept) {
18
+ double angle = ZeroAngle(FIX2INT(drag_function), NUM2DBL(drag_coefficient), NUM2DBL(velocity), NUM2DBL(sight_height), NUM2DBL(zero_range), NUM2DBL(y_intercept));
19
+ return rb_float_new(angle);
20
+ }
21
+
22
+ VALUE method_map_trajectory(VALUE self, VALUE drag_function, VALUE drag_coefficient, VALUE velocity, VALUE sight_height, VALUE shooting_angle, VALUE zero_angle, VALUE wind_speed, VALUE wind_angle, VALUE max_range) {
23
+
24
+ /* cast ruby variables */
25
+ int DragFunction = FIX2INT(drag_function);
26
+ double DragCoefficient = NUM2DBL(drag_coefficient);
27
+ double Vi = NUM2DBL(velocity);
28
+ double SightHeight = NUM2DBL(sight_height);
29
+ double ShootingAngle = NUM2DBL(shooting_angle);
30
+ double ZAngle = NUM2DBL(zero_angle);
31
+ double WindSpeed = NUM2DBL(wind_speed);
32
+ double WindAngle = NUM2DBL(wind_angle);
33
+ int MaxRange = FIX2INT(max_range);
34
+
35
+ /* create ruby objects */
36
+ VALUE result_array = rb_ary_new2(MaxRange);
37
+
38
+
39
+ double t=0;
40
+ double dt=0.5/Vi;
41
+ double v=0;
42
+ double vx=0, vx1=0, vy=0, vy1=0;
43
+ double dv=0, dvx=0, dvy=0;
44
+ double x=0, y=0;
45
+
46
+ double headwind=HeadWind(WindSpeed, WindAngle);
47
+ double crosswind=CrossWind(WindSpeed, WindAngle);
48
+
49
+ double Gy=GRAVITY*cos(DegtoRad((ShootingAngle + ZAngle)));
50
+ double Gx=GRAVITY*sin(DegtoRad((ShootingAngle + ZAngle)));
51
+
52
+ vx=Vi*cos(DegtoRad(ZAngle));
53
+ vy=Vi*sin(DegtoRad(ZAngle));
54
+
55
+ y=-SightHeight/12;
56
+
57
+
58
+ int n=0;
59
+ for (t=0;;t=t+dt){
60
+
61
+ vx1=vx, vy1=vy;
62
+ v=pow(pow(vx,2)+pow(vy,2),0.5);
63
+ dt=0.5/v;
64
+
65
+ // Compute acceleration using the drag function retardation
66
+ dv = retard(DragFunction,DragCoefficient,v+headwind);
67
+ dvx = -(vx/v)*dv;
68
+ dvy = -(vy/v)*dv;
69
+
70
+ // Compute velocity, including the resolved gravity vectors.
71
+ vx=vx + dt*dvx + dt*Gx;
72
+ vy=vy + dt*dvy + dt*Gy;
73
+
74
+
75
+
76
+ if (x/3>=n){
77
+ VALUE entry = rb_hash_new();
78
+ rb_hash_aset(entry, rb_str_new2("range"), rb_float_new(x/3));
79
+ rb_hash_aset(entry, rb_str_new2("path"), rb_float_new(y*12));
80
+ rb_hash_aset(entry, rb_str_new2("moa_correction"), rb_float_new(-RadtoMOA(atan(y/x))));
81
+ rb_hash_aset(entry, rb_str_new2("time"), rb_float_new(t+dt));
82
+ rb_hash_aset(entry, rb_str_new2("windage"), rb_float_new(Windage(crosswind,Vi,x,t+dt)));
83
+ rb_hash_aset(entry, rb_str_new2("moa_windage"), rb_float_new(RadtoMOA(atan(rb_hash_aref(entry, rb_str_new2("windage"))))));
84
+ rb_hash_aset(entry, rb_str_new2("velocity"), rb_float_new(v));
85
+ rb_ary_push(result_array, entry);
86
+ n++;
87
+ }
88
+
89
+ // Compute position based on average velocity.
90
+ x=x+dt*(vx+vx1)/2;
91
+ y=y+dt*(vy+vy1)/2;
92
+
93
+ if (fabs(vy)>fabs(3*vx)) break;
94
+ if (n>=MaxRange+1) break;
95
+ }
96
+
97
+ return result_array;
98
+ }
@@ -0,0 +1,3 @@
1
+ require 'mkmf'
2
+
3
+ create_makefile('ballistics/ballistics')
@@ -0,0 +1,577 @@
1
+ // GNU Ballistics Library
2
+ // Originally created by Derek Yates
3
+ // Now available free under the GNU GPL
4
+
5
+
6
+
7
+ #ifndef __lib_ballistics__
8
+ #define __lib_ballistics__
9
+ #define __BCOMP_MAXRANGE__ 50001
10
+ #define GRAVITY (-32.194)
11
+ #include <math.h>
12
+ #include <stdlib.h>
13
+
14
+ enum __DragFunctions {G1=1,G2,G3,G4,G5,G6,G7,G8};
15
+
16
+ // Angular conversion functions to make things a little easier.
17
+ // Source is in _angle.c
18
+ double DegtoMOA(double deg); // Converts degrees to minutes of angle
19
+ double DegtoRad(double deg); // Converts degrees to radians
20
+ double MOAtoDeg(double moa); // Converts minutes of angle to degrees
21
+ double MOAtoRad(double moa); // Converts minutes of angle to radians
22
+ double RadtoDeg(double rad); // Converts radians to degrees
23
+ double RadtoMOA(double rad); // Converts radiants to minutes of angle
24
+
25
+
26
+ // A function to calculate ballistic retardation values based on standard drag functions.
27
+ // Source is in "_retard.c"
28
+ double retard(int DragFunction, double DragCoefficient, double Vi);
29
+ /* Arguments:
30
+ DragFunction: G1, G2, G3, G4, G5, G6, G7, or G8. All are enumerated above.
31
+ DragCoefficient: The coefficient of drag for the projectile for the given drag function.
32
+ Vi: The Velocity of the projectile.
33
+
34
+ Return Value:
35
+ The function returns the projectile drag retardation velocity, in ft/s per second.
36
+
37
+ */
38
+
39
+
40
+ // A function to correct a "standard" Drag Coefficient for differing atmospheric conditions.
41
+ // Returns the corrected drag coefficient for supplied drag coefficient and atmospheric conditions.
42
+ // Source is in "_atmosphere.c"
43
+ double AtmCorrect(double DragCoefficient, double Altitude, double Barometer, double Temperature, double RelativeHumidity);
44
+ /* Arguments:
45
+ DragCoefficient: The coefficient of drag for a given projectile.
46
+ Altitude: The altitude above sea level in feet. Standard altitude is 0 feet above sea level.
47
+ Barometer: The barometric pressure in inches of mercury (in Hg).
48
+ This is not "absolute" pressure, it is the "standardized" pressure reported in the papers and news.
49
+ Standard pressure is 29.53 in Hg.
50
+ Temperature: The temperature in Fahrenheit. Standard temperature is 59 degrees.
51
+ RelativeHumidity: The relative humidity fraction. Ranges from 0.00 to 1.00, with 0.50 being 50% relative humidity.
52
+ Standard humidity is 78%
53
+
54
+ Return Value:
55
+ The function returns a ballistic coefficient, corrected for the supplied atmospheric conditions.
56
+ */
57
+
58
+
59
+ // A function to compute the windage deflection for a given crosswind speed,
60
+ // given flight time in a vacuum, and given flight time in real life.
61
+ // Returns the windage correction needed in inches.
62
+ // Source is in "_windage.c"
63
+ double Windage(double WindSpeed, double Vi, double x, double t);
64
+ /* Arguments:
65
+ WindSpeed: The wind velocity in mi/hr.
66
+ Vi: The initial velocity of the projectile (muzzle velocity).
67
+ x: The range at which you wish to determine windage, in feet.
68
+ t: The time it has taken the projectile to traverse the range x, in seconds.
69
+
70
+ Return Value:
71
+ Returns the amount of windage correction, in inches, required to achieve zero on a target at the given range.
72
+
73
+ */
74
+
75
+ // Functions to resolve any wind / angle combination into headwind and crosswind components.
76
+ // Source is in "_windage.c"
77
+ double HeadWind(double WindSpeed, double WindAngle);
78
+ double CrossWind(double WindSpeed, double WindAngle);
79
+ /* Arguments:
80
+ WindSpeed: The wind velocity, in mi/hr.
81
+ WindAngle: The angle from which the wind is coming, in degrees.
82
+ 0 degrees is from straight ahead
83
+ 90 degrees is from right to left
84
+ 180 degrees is from directly behind
85
+ 270 or -90 degrees is from left to right.
86
+
87
+ Return value:
88
+ Returns the headwind or crosswind velocity component, in mi/hr.
89
+ */
90
+
91
+
92
+ // A function to determine the bore angle needed to achieve a target zero at Range yards
93
+ // (at standard conditions and on level ground.)
94
+ // Source is in "_zero.c"
95
+ double ZeroAngle(int DragFunction, double DragCoefficient, double Vi, double SightHeight, double ZeroRange, double yIntercept);
96
+ /* Arguments:
97
+ DragFunction: The drag function to use (G1, G2, G3, G5, G6, G7, G8)
98
+ DragCoefficient: The coefficient of drag for the projectile, for the supplied drag function.
99
+ Vi: The initial velocity of the projectile, in feet/s
100
+ SightHeight: The height of the sighting system above the bore centerline, in inches.
101
+ Most scopes fall in the 1.6 to 2.0 inch range.
102
+ ZeroRange: The range in yards, at which you wish the projectile to intersect yIntercept.
103
+ yIntercept: The height, in inches, you wish for the projectile to be when it crosses ZeroRange yards.
104
+ This is usually 0 for a target zero, but could be any number. For example if you wish
105
+ to sight your rifle in 1.5" high at 100 yds, then you would set yIntercept to 1.5, and ZeroRange to 100
106
+
107
+ Return Value:
108
+ Returns the angle of the bore relative to the sighting system, in degrees.
109
+ */
110
+
111
+
112
+ // A function to generate a ballistic solution table in 1 yard increments, up to __BCOMP_MAXRANGE__.
113
+ // Source is in "_solve.c"
114
+ int SolveAll(int DragFunction, double DragCoefficient, double Vi, double SightHeight, \
115
+ double ShootingAngle, double ZeroAngle, double WindSpeed, double WindAngle, double** Solution);
116
+ /* Arguments:
117
+ DragFunction: The drag function you wish to use for the solution (G1, G2, G3, G5, G6, G7, or G8)
118
+ DragCoefficient: The coefficient of drag for the projectile you wish to model.
119
+ Vi: The projectile initial velocity.
120
+ SightHeight: The height of the sighting system above the bore centerline.
121
+ Most scopes are in the 1.5"-2.0" range.
122
+ ShootingAngle: The uphill or downhill shooting angle, in degrees. Usually 0, but can be anything from
123
+ 90 (directly up), to -90 (directly down).
124
+ ZeroAngle: The angle of the sighting system relative to the bore, in degrees. This can be easily computed
125
+ using the ZeroAngle() function documented above.
126
+ WindSpeed: The wind velocity, in mi/hr
127
+ WindAngle: The angle at which the wind is approaching from, in degrees.
128
+ 0 degrees is a straight headwind
129
+ 90 degrees is from right to left
130
+ 180 degrees is a straight tailwind
131
+ -90 or 270 degrees is from left to right.
132
+ Solution: A pointer provided for accessing the solution after it has been generated.
133
+ Memory for this pointer is allocated in the function, so the user does not need
134
+ to worry about it. This solution can be passed to the retrieval functions to get
135
+ useful data from the solution.
136
+ Return Value:
137
+ This function returns an integer representing the maximum valid range of the
138
+ solution. This also indicates the maximum number of rows in the solution matrix,
139
+ and should not be exceeded in order to avoid a memory segmentation fault.
140
+
141
+ */
142
+
143
+
144
+ // Functions for retrieving data from a solution generated with SolveAll()
145
+ double GetRange(double* sln, int yardage); // Returns range, in yards.
146
+ double GetPath(double* sln, int yardage); // Returns projectile path, in inches, relative to the line of sight.
147
+ double GetMOA(double* sln, int yardage); // Returns an estimated elevation correction for achieving a zero at this range.
148
+ // this is useful for "click charts" and the like.
149
+ double GetTime(double* sln, int yardage);// Returns the projectile's time of flight to this range.
150
+ double GetWindage(double* sln, int yardage); // Returns the windage correction in inches required to achieve zero at this range.
151
+ double GetWindageMOA(double* sln, int yardage); // Returns an approximate windage correction in MOA to achieve a zero at this range.
152
+ double GetVelocity(double* sln, int yardage); // Returns the projectile's total velocity (Vector product of Vx and Vy)
153
+ double GetVx(double* sln, int yardage); // Returns the velocity of the projectile in the bore direction.
154
+ double GetVy(double* sln, int yardage); // Returns the velocity of the projectile perpendicular to the bore direction.
155
+
156
+ // For very steep shooting angles, Vx can actually become what you would think of as Vy relative to the ground,
157
+ // because Vx is referencing the bore's axis. All computations are carried out relative to the bore's axis, and
158
+ // have very little to do with the ground's orientation.
159
+
160
+
161
+ // I have split the source up into several files to make it easier for me to work on and refine.
162
+ // Some people hate this, but I find it is easier to work on the code in small chunks.
163
+
164
+ // Specialty angular conversion functions
165
+ double DegtoMOA(double deg){
166
+ return deg*60;
167
+ }
168
+ double DegtoRad(double deg){
169
+ return deg*M_PI/180;
170
+ }
171
+ double MOAtoDeg(double moa){
172
+ return moa/60;
173
+ }
174
+ double MOAtoRad(double moa){
175
+ return moa/60*M_PI/180;
176
+ }
177
+ double RadtoDeg(double rad){
178
+ return rad*180/M_PI;
179
+ }
180
+ double RadtoMOA(double rad){
181
+ return rad*60*180/M_PI;
182
+ }
183
+
184
+ double retard(int DragFunction, double DragCoefficient, double Velocity){
185
+
186
+ // printf("DF: %d, CD: %f, V: %f,);
187
+
188
+ double vp=Velocity;
189
+ double val=-1;
190
+ double A=-1;
191
+ double M=-1;
192
+ switch(DragFunction) {
193
+ case G1:
194
+ if (vp > 4230) { A = 1.477404177730177e-04; M = 1.9565; }
195
+ else if (vp> 3680) { A = 1.920339268755614e-04; M = 1.925 ; }
196
+ else if (vp> 3450) { A = 2.894751026819746e-04; M = 1.875 ; }
197
+ else if (vp> 3295) { A = 4.349905111115636e-04; M = 1.825 ; }
198
+ else if (vp> 3130) { A = 6.520421871892662e-04; M = 1.775 ; }
199
+ else if (vp> 2960) { A = 9.748073694078696e-04; M = 1.725 ; }
200
+ else if (vp> 2830) { A = 1.453721560187286e-03; M = 1.675 ; }
201
+ else if (vp> 2680) { A = 2.162887202930376e-03; M = 1.625 ; }
202
+ else if (vp> 2460) { A = 3.209559783129881e-03; M = 1.575 ; }
203
+ else if (vp> 2225) { A = 3.904368218691249e-03; M = 1.55 ; }
204
+ else if (vp> 2015) { A = 3.222942271262336e-03; M = 1.575 ; }
205
+ else if (vp> 1890) { A = 2.203329542297809e-03; M = 1.625 ; }
206
+ else if (vp> 1810) { A = 1.511001028891904e-03; M = 1.675 ; }
207
+ else if (vp> 1730) { A = 8.609957592468259e-04; M = 1.75 ; }
208
+ else if (vp> 1595) { A = 4.086146797305117e-04; M = 1.85 ; }
209
+ else if (vp> 1520) { A = 1.954473210037398e-04; M = 1.95 ; }
210
+ else if (vp> 1420) { A = 5.431896266462351e-05; M = 2.125 ; }
211
+ else if (vp> 1360) { A = 8.847742581674416e-06; M = 2.375 ; }
212
+ else if (vp> 1315) { A = 1.456922328720298e-06; M = 2.625 ; }
213
+ else if (vp> 1280) { A = 2.419485191895565e-07; M = 2.875 ; }
214
+ else if (vp> 1220) { A = 1.657956321067612e-08; M = 3.25 ; }
215
+ else if (vp> 1185) { A = 4.745469537157371e-10; M = 3.75 ; }
216
+ else if (vp> 1150) { A = 1.379746590025088e-11; M = 4.25 ; }
217
+ else if (vp> 1100) { A = 4.070157961147882e-13; M = 4.75 ; }
218
+ else if (vp> 1060) { A = 2.938236954847331e-14; M = 5.125 ; }
219
+ else if (vp> 1025) { A = 1.228597370774746e-14; M = 5.25 ; }
220
+ else if (vp> 980) { A = 2.916938264100495e-14; M = 5.125 ; }
221
+ else if (vp> 945) { A = 3.855099424807451e-13; M = 4.75 ; }
222
+ else if (vp> 905) { A = 1.185097045689854e-11; M = 4.25 ; }
223
+ else if (vp> 860) { A = 3.566129470974951e-10; M = 3.75 ; }
224
+ else if (vp> 810) { A = 1.045513263966272e-08; M = 3.25 ; }
225
+ else if (vp> 780) { A = 1.291159200846216e-07; M = 2.875 ; }
226
+ else if (vp> 750) { A = 6.824429329105383e-07; M = 2.625 ; }
227
+ else if (vp> 700) { A = 3.569169672385163e-06; M = 2.375 ; }
228
+ else if (vp> 640) { A = 1.839015095899579e-05; M = 2.125 ; }
229
+ else if (vp> 600) { A = 5.71117468873424e-05 ; M = 1.950 ; }
230
+ else if (vp> 550) { A = 9.226557091973427e-05; M = 1.875 ; }
231
+ else if (vp> 250) { A = 9.337991957131389e-05; M = 1.875 ; }
232
+ else if (vp> 100) { A = 7.225247327590413e-05; M = 1.925 ; }
233
+ else if (vp> 65) { A = 5.792684957074546e-05; M = 1.975 ; }
234
+ else if (vp> 0) { A = 5.206214107320588e-05; M = 2.000 ; }
235
+ break;
236
+
237
+ case G2:
238
+ if (vp> 1674 ) { A = .0079470052136733 ; M = 1.36999902851493; }
239
+ else if (vp> 1172 ) { A = 1.00419763721974e-03; M = 1.65392237010294; }
240
+ else if (vp> 1060 ) { A = 7.15571228255369e-23; M = 7.91913562392361; }
241
+ else if (vp> 949 ) { A = 1.39589807205091e-10; M = 3.81439537623717; }
242
+ else if (vp> 670 ) { A = 2.34364342818625e-04; M = 1.71869536324748; }
243
+ else if (vp> 335 ) { A = 1.77962438921838e-04; M = 1.76877550388679; }
244
+ else if (vp> 0 ) { A = 5.18033561289704e-05; M = 1.98160270524632; }
245
+ break;
246
+
247
+ case G5:
248
+ if (vp> 1730 ){ A = 7.24854775171929e-03; M = 1.41538574492812; }
249
+ else if (vp> 1228 ){ A = 3.50563361516117e-05; M = 2.13077307854948; }
250
+ else if (vp> 1116 ){ A = 1.84029481181151e-13; M = 4.81927320350395; }
251
+ else if (vp> 1004 ){ A = 1.34713064017409e-22; M = 7.8100555281422 ; }
252
+ else if (vp> 837 ){ A = 1.03965974081168e-07; M = 2.84204791809926; }
253
+ else if (vp> 335 ){ A = 1.09301593869823e-04; M = 1.81096361579504; }
254
+ else if (vp> 0 ){ A = 3.51963178524273e-05; M = 2.00477856801111; }
255
+ break;
256
+
257
+ case G6:
258
+ if (vp> 3236 ) { A = 0.0455384883480781 ; M = 1.15997674041274; }
259
+ else if (vp> 2065 ) { A = 7.167261849653769e-02; M = 1.10704436538885; }
260
+ else if (vp> 1311 ) { A = 1.66676386084348e-03 ; M = 1.60085100195952; }
261
+ else if (vp> 1144 ) { A = 1.01482730119215e-07 ; M = 2.9569674731838 ; }
262
+ else if (vp> 1004 ) { A = 4.31542773103552e-18 ; M = 6.34106317069757; }
263
+ else if (vp> 670 ) { A = 2.04835650496866e-05 ; M = 2.11688446325998; }
264
+ else if (vp> 0 ) { A = 7.50912466084823e-05 ; M = 1.92031057847052; }
265
+ break;
266
+
267
+ case G7:
268
+ if (vp> 4200 ) { A = 1.29081656775919e-09; M = 3.24121295355962; }
269
+ else if (vp> 3000 ) { A = 0.0171422231434847 ; M = 1.27907168025204; }
270
+ else if (vp> 1470 ) { A = 2.33355948302505e-03; M = 1.52693913274526; }
271
+ else if (vp> 1260 ) { A = 7.97592111627665e-04; M = 1.67688974440324; }
272
+ else if (vp> 1110 ) { A = 5.71086414289273e-12; M = 4.3212826264889 ; }
273
+ else if (vp> 960 ) { A = 3.02865108244904e-17; M = 5.99074203776707; }
274
+ else if (vp> 670 ) { A = 7.52285155782535e-06; M = 2.1738019851075 ; }
275
+ else if (vp> 540 ) { A = 1.31766281225189e-05; M = 2.08774690257991; }
276
+ else if (vp> 0 ) { A = 1.34504843776525e-05; M = 2.08702306738884; }
277
+ break;
278
+
279
+ case G8:
280
+ if (vp> 3571 ) { A = .0112263766252305 ; M = 1.33207346655961; }
281
+ else if (vp> 1841 ) { A = .0167252613732636 ; M = 1.28662041261785; }
282
+ else if (vp> 1120 ) { A = 2.20172456619625e-03; M = 1.55636358091189; }
283
+ else if (vp> 1088 ) { A = 2.0538037167098e-16 ; M = 5.80410776994789; }
284
+ else if (vp> 976 ) { A = 5.92182174254121e-12; M = 4.29275576134191; }
285
+ else if (vp> 0 ) { A = 4.3917343795117e-05 ; M = 1.99978116283334; }
286
+ break;
287
+
288
+ default:
289
+ break;
290
+
291
+ }
292
+
293
+ if (A!=-1 && M!=-1 && vp>0 && vp<10000){
294
+ val=A*pow(vp,M)/DragCoefficient;
295
+ return val;
296
+ }
297
+ else return -1;
298
+ }
299
+
300
+ double calcFR(double Temperature, double Pressure, double RelativeHumidity){
301
+ double VPw=4e-6*pow(Temperature,3) - 0.0004*pow(Temperature,2)+0.0234*Temperature-0.2517;
302
+ double FRH=0.995*(Pressure/(Pressure-(0.3783)*(RelativeHumidity)*VPw));
303
+ return FRH;
304
+ }
305
+
306
+ double calcFP(double Pressure){
307
+ double Pstd=29.53; // in-hg
308
+ double FP=0;
309
+ FP = (Pressure-Pstd)/(Pstd);
310
+ return FP;
311
+ }
312
+
313
+ double calcFT(double Temperature,double Altitude){
314
+ double Tstd=-0.0036*Altitude+59;
315
+ double FT = (Temperature-Tstd)/(459.6+Tstd);
316
+ return FT;
317
+ }
318
+
319
+ double calcFA(double Altitude){
320
+ double fa=0;
321
+ fa=-4e-15*pow(Altitude,3)+4e-10*pow(Altitude,2)-3e-5*Altitude+1;
322
+ return (1/fa);
323
+ }
324
+
325
+ double AtmCorrect(double DragCoefficient, double Altitude, double Barometer, double Temperature, double RelativeHumidity){
326
+
327
+ double FA = calcFA(Altitude);
328
+ double FT = calcFT(Temperature, Altitude);
329
+ double FR = calcFR(Temperature, Barometer, RelativeHumidity);
330
+ double FP = calcFP(Barometer);
331
+
332
+ // Calculate the atmospheric correction factor
333
+ double CD = (FA*(1+FT-FP)*FR);
334
+ return DragCoefficient*CD;
335
+
336
+ }
337
+
338
+ double Windage(double WindSpeed, double Vi, double xx, double t){
339
+ double Vw = WindSpeed*17.60; // Convert to inches per second.
340
+ return (Vw*(t-xx/Vi));
341
+ }
342
+
343
+
344
+ // Headwind is positive at WindAngle=0
345
+ double HeadWind(double WindSpeed, double WindAngle){
346
+ double Wangle = DegtoRad(WindAngle);
347
+ return (cos(Wangle)*WindSpeed);
348
+ }
349
+
350
+ // Positive is from Shooter's Right to Left (Wind from 90 degree)
351
+ double CrossWind(double WindSpeed, double WindAngle){
352
+ double Wangle = DegtoRad(WindAngle);
353
+ return (sin(Wangle)*WindSpeed);
354
+ }
355
+
356
+
357
+ double ZeroAngle(int DragFunction, double DragCoefficient, double Vi, double SightHeight, double ZeroRange, double yIntercept){
358
+
359
+ // Numerical Integration variables
360
+ double t=0;
361
+ double dt=1/Vi; // The solution accuracy generally doesn't suffer if its within a foot for each second of time.
362
+ double y=-SightHeight/12;
363
+ double x=0;
364
+ double da; // The change in the bore angle used to iterate in on the correct zero angle.
365
+
366
+ // State variables for each integration loop.
367
+ double v=0, vx=0, vy=0; // velocity
368
+ double vx1=0, vy1=0; // Last frame's velocity, used for computing average velocity.
369
+ double dv=0, dvx=0, dvy=0; // acceleration
370
+ double Gx=0, Gy=0; // Gravitational acceleration
371
+
372
+ double angle=0; // The actual angle of the bore.
373
+
374
+ int quit=0; // We know it's time to quit our successive approximation loop when this is 1.
375
+
376
+ // Start with a very coarse angular change, to quickly solve even large launch angle problems.
377
+ da=DegtoRad(14);
378
+
379
+
380
+ // The general idea here is to start at 0 degrees elevation, and increase the elevation by 14 degrees
381
+ // until we are above the correct elevation. Then reduce the angular change by half, and begin reducing
382
+ // the angle. Once we are again below the correct angle, reduce the angular change by half again, and go
383
+ // back up. This allows for a fast successive approximation of the correct elevation, usually within less
384
+ // than 20 iterations.
385
+ for (angle=0;quit==0;angle=angle+da){
386
+ vy=Vi*sin(angle);
387
+ vx=Vi*cos(angle);
388
+ Gx=GRAVITY*sin(angle);
389
+ Gy=GRAVITY*cos(angle);
390
+
391
+ for (t=0,x=0,y=-SightHeight/12;x<=ZeroRange*3;t=t+dt){
392
+ vy1=vy;
393
+ vx1=vx;
394
+ v=pow((pow(vx,2)+pow(vy,2)),0.5);
395
+ dt=1/v;
396
+
397
+ dv = retard(DragFunction, DragCoefficient, v);
398
+ dvy = -dv*vy/v*dt;
399
+ dvx = -dv*vx/v*dt;
400
+
401
+ vx=vx+dvx;
402
+ vy=vy+dvy;
403
+ vy=vy+dt*Gy;
404
+ vx=vx+dt*Gx;
405
+
406
+ x=x+dt*(vx+vx1)/2;
407
+ y=y+dt*(vy+vy1)/2;
408
+ // Break early to save CPU time if we won't find a solution.
409
+ if (vy<0 && y<yIntercept) {
410
+ break;
411
+ }
412
+ if (vy>3*vx) {
413
+ break;
414
+ }
415
+ }
416
+
417
+ if (y>yIntercept && da>0){
418
+ da=-da/2;
419
+ }
420
+
421
+ if (y<yIntercept && da<0){
422
+ da=-da/2;
423
+ }
424
+
425
+ if (fabs(da) < MOAtoRad(0.01)) quit=1; // If our accuracy is sufficient, we can stop approximating.
426
+ if (angle > DegtoRad(45)) quit=1; // If we exceed the 45 degree launch angle, then the projectile just won't get there, so we stop trying.
427
+
428
+ }
429
+
430
+
431
+ return RadtoDeg(angle); // Convert to degrees for return value.
432
+ }
433
+
434
+
435
+ int SolveAll(int DragFunction, double DragCoefficient, double Vi, double SightHeight, \
436
+ double ShootingAngle, double ZAngle, double WindSpeed, double WindAngle, double** Solution){
437
+
438
+ double* ptr;
439
+ ptr = (double*)malloc(10*__BCOMP_MAXRANGE__*sizeof(double)+2048);
440
+
441
+ double t=0;
442
+ double dt=0.5/Vi;
443
+ double v=0;
444
+ double vx=0, vx1=0, vy=0, vy1=0;
445
+ double dv=0, dvx=0, dvy=0;
446
+ double x=0, y=0;
447
+
448
+ double headwind=HeadWind(WindSpeed, WindAngle);
449
+ double crosswind=CrossWind(WindSpeed, WindAngle);
450
+
451
+ double Gy=GRAVITY*cos(DegtoRad((ShootingAngle + ZAngle)));
452
+ double Gx=GRAVITY*sin(DegtoRad((ShootingAngle + ZAngle)));
453
+
454
+ vx=Vi*cos(DegtoRad(ZAngle));
455
+ vy=Vi*sin(DegtoRad(ZAngle));
456
+
457
+ y=-SightHeight/12;
458
+
459
+ int n=0;
460
+ for (t=0;;t=t+dt){
461
+
462
+ vx1=vx, vy1=vy;
463
+ v=pow(pow(vx,2)+pow(vy,2),0.5);
464
+ dt=0.5/v;
465
+
466
+ // Compute acceleration using the drag function retardation
467
+ dv = retard(DragFunction,DragCoefficient,v+headwind);
468
+ dvx = -(vx/v)*dv;
469
+ dvy = -(vy/v)*dv;
470
+
471
+ // Compute velocity, including the resolved gravity vectors.
472
+ vx=vx + dt*dvx + dt*Gx;
473
+ vy=vy + dt*dvy + dt*Gy;
474
+
475
+
476
+
477
+ if (x/3>=n){
478
+ ptr[10*n+0]=x/3; // Range in yds
479
+ ptr[10*n+1]=y*12; // Path in inches
480
+ ptr[10*n+2]=-RadtoMOA(atan(y/x)); // Correction in MOA
481
+ ptr[10*n+3]=t+dt; // Time in s
482
+ ptr[10*n+4]=Windage(crosswind,Vi,x,t+dt); // Windage in inches
483
+ ptr[10*n+5]=RadtoMOA(atan(ptr[10*n+4])); // Windage in MOA
484
+ ptr[10*n+6]=v; // Velocity (combined)
485
+ ptr[10*n+7]=vx; // Velocity (x)
486
+ ptr[10*n+8]=vy; // Velocity (y)
487
+ ptr[10*n+9]=0; // Reserved
488
+ n++;
489
+ }
490
+
491
+ // Compute position based on average velocity.
492
+ x=x+dt*(vx+vx1)/2;
493
+ y=y+dt*(vy+vy1)/2;
494
+
495
+ if (fabs(vy)>fabs(3*vx)) break;
496
+ if (n>=__BCOMP_MAXRANGE__+1) break;
497
+ }
498
+
499
+ ptr[10*__BCOMP_MAXRANGE__+1]=(double)n;
500
+
501
+ *Solution = ptr;
502
+
503
+ return n;
504
+ }
505
+ double GetRange(double* sln, int yardage){
506
+ double size=sln[__BCOMP_MAXRANGE__*10+1];
507
+ if (yardage<size){
508
+ return sln[10*yardage];
509
+ }
510
+ else return 0;
511
+ }
512
+
513
+ double GetPath(double* sln, int yardage){
514
+ double size=sln[__BCOMP_MAXRANGE__*10+1];
515
+ if (yardage<size){
516
+ return sln[10*yardage+1];
517
+ }
518
+ else return 0;
519
+ }
520
+
521
+ double GetMOA(double* sln, int yardage){
522
+ double size=sln[__BCOMP_MAXRANGE__*10+1];
523
+ if (yardage<size){
524
+ return sln[10*yardage+2];
525
+ }
526
+ else return 0;
527
+ }
528
+
529
+
530
+ double GetTime(double* sln, int yardage){
531
+ double size=sln[__BCOMP_MAXRANGE__*10+1];
532
+ if (yardage<size){
533
+ return sln[10*yardage+3];
534
+ }
535
+ else return 0;
536
+ }
537
+
538
+ double GetWindage(double* sln, int yardage){
539
+ double size=sln[__BCOMP_MAXRANGE__*10+1];
540
+ if (yardage<size){
541
+ return sln[10*yardage+4];
542
+ }
543
+ else return 0;
544
+ }
545
+
546
+ double GetWindageMOA(double* sln, int yardage){
547
+ double size=sln[__BCOMP_MAXRANGE__*10+1];
548
+ if (yardage<size){
549
+ return sln[10*yardage+5];
550
+ }
551
+ else return 0;
552
+ }
553
+
554
+ double GetVelocity(double* sln, int yardage){
555
+ double size=sln[__BCOMP_MAXRANGE__*10+1];
556
+ if (yardage<size){
557
+ return sln[10*yardage+6];
558
+ }
559
+ else return 0;
560
+ }
561
+
562
+ double GetVx(double* sln, int yardage){
563
+ double size=sln[__BCOMP_MAXRANGE__*10+1];
564
+ if (yardage<size){
565
+ return sln[10*yardage+7];
566
+ }
567
+ else return 0;
568
+ }
569
+
570
+ double GetVy(double* sln, int yardage){
571
+ double size=sln[__BCOMP_MAXRANGE__*10+1];
572
+ if (yardage<size){
573
+ return sln[10*yardage+8];
574
+ }
575
+ else return 0;
576
+ }
577
+ #endif
@@ -0,0 +1,51 @@
1
+ require 'bigdecimal'
2
+ require 'bigdecimal/util'
3
+
4
+ module Ballistics
5
+ class Atmosphere
6
+ STANDARD_TEMPERATURE = 59.to_d
7
+ STANDARD_PRESSURE = 29.53.to_d
8
+ STANDARD_HUMIDITY = 0.78.to_d
9
+ STANDARD_ALTITUDE = 0.to_d
10
+ RANKLINE_CORRECTION = 459.4.to_d
11
+
12
+ attr_accessor :altitude, :barometric_pressure, :temperature, :relative_humidity
13
+
14
+ def initialize(options = {})
15
+ @altitude = options[:altitude].to_d
16
+ @barometric_pressure = options[:barometric_pressure].to_d
17
+ @temperature = options[:temperature].to_d
18
+ @relative_humidity = options[:relative_humidity].to_d
19
+ end
20
+
21
+ def correct_ballistic_coefficient(ballistic_coefficient)
22
+ ballistic_coefficient = ballistic_coefficient.to_d
23
+ corrected_ballistic_coefficient = (altitude_factor * (1 + temperature_factor - pressure_factor) * humidity_factor)
24
+ return ballistic_coefficient * corrected_ballistic_coefficient
25
+ end
26
+
27
+ private
28
+
29
+ def humidity_factor
30
+ vpw = 4e-6.to_d * @temperature**3 - 0.0004.to_d * @temperature**2.to_d + 0.0234.to_d * @temperature - 0.2517.to_d
31
+ frh = 0.995.to_d * (@barometric_pressure / (@barometric_pressure - 0.3783.to_d * @relative_humidity * vpw) )
32
+ return frh
33
+ end
34
+
35
+ def pressure_factor
36
+ pressure_correction_factor = (@barometric_pressure - STANDARD_PRESSURE) / STANDARD_PRESSURE
37
+ return pressure_correction_factor
38
+ end
39
+
40
+ def temperature_factor
41
+ standard_temp_for_altitude = -0.0036.to_d * @altitude + STANDARD_TEMPERATURE
42
+ temp_correction_factor = (@temperature - standard_temp_for_altitude) / (RANKLINE_CORRECTION + standard_temp_for_altitude)
43
+ return temp_correction_factor
44
+ end
45
+
46
+ def altitude_factor
47
+ altitude_correction_factor = -4e-15.to_d * @altitude**3 + 4e-10.to_d * @altitude**2 - 3e-5.to_d * @altitude + 1
48
+ return 1 / altitude_correction_factor
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,16 @@
1
+ module Ballistics
2
+ module DFMap
3
+ def self.__convert_df_to_int(drag_function)
4
+ case drag_function
5
+ when 'G1' then 1
6
+ when 'G2' then 2
7
+ when 'G3' then 3
8
+ when 'G4' then 4
9
+ when 'G5' then 5
10
+ when 'G6' then 6
11
+ when 'G7' then 7
12
+ when 'G8' then 8
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,21 @@
1
+ require_relative 'df_map'
2
+
3
+ module Ballistics
4
+ module Trajectory
5
+ def self.map_trajectory(opts = {})
6
+ options = { shooting_angle: 0, wind_speed: 0, wind_angle: 0, max_range: 500 }.merge(opts)
7
+
8
+ drag_function = Ballistics::DFMap.__convert_df_to_int(options[:drag_function])
9
+ drag_coefficient = options[:drag_coefficient]
10
+ vi = options[:velocity]
11
+ sight_height = options[:sight_height]
12
+ shooting_angle = options[:shooting_angle]
13
+ z_angle = options[:zero_angle]
14
+ wind_speed = options[:wind_speed]
15
+ wind_angle = options[:wind_angle]
16
+ max_range = options[:max_range]
17
+
18
+ self._map_trajectory(drag_function, drag_coefficient, vi, sight_height, shooting_angle, z_angle, wind_speed, wind_angle, max_range)
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,36 @@
1
+ module Ballistics
2
+ require 'bigdecimal'
3
+ require 'bigdecimal/util'
4
+
5
+ UNIT_CORRECTION_FACTOR = 225400.to_d # (7000 gr./lb. * 32.2 f.p.s. ** 2)
6
+ ACCELERATION_OF_GRAVITY = 32.2.to_d
7
+
8
+ module Utils
9
+ def self.sectional_density(bullet_weight, bullet_diameter)
10
+ return ((bullet_weight.to_d / 7000.to_d) / (bullet_diameter.to_d ** 2.to_d))
11
+ end
12
+
13
+ def self.kinetic_energy(velocity, bullet_weight)
14
+ return (0.5.to_d * bullet_weight.to_d * velocity.to_d ** 2.to_d / 7000.to_d / 32.175.to_d)
15
+ end
16
+
17
+ def self.taylorko(velocity, bullet_weight, bullet_diameter)
18
+ return ((bullet_weight.to_d * velocity.to_d * bullet_diameter.to_d) / 7000.to_d)
19
+ end
20
+
21
+ def self.recoil_impulse(bullet_weight, propellant_weight, velocity, propellant_velocity=4000)
22
+ return (bullet_weight.to_d * velocity.to_d + propellant_weight.to_d * propellant_velocity.to_d) / UNIT_CORRECTION_FACTOR
23
+ end
24
+
25
+ def self.free_recoil(bullet_weight, propellant_weight, velocity, firearm_weight, propellant_velocity=4000)
26
+ recoil_impulse = recoil_impulse(bullet_weight, propellant_weight, velocity, propellant_velocity)
27
+ return (ACCELERATION_OF_GRAVITY * recoil_impulse.to_d / firearm_weight.to_d)
28
+ end
29
+
30
+ def self.recoil_energy(bullet_weight, propellant_weight, velocity, firearm_weight, propellant_velocity=4000)
31
+ free_recoil = free_recoil(bullet_weight, propellant_weight, velocity, firearm_weight, propellant_velocity)
32
+ return (firearm_weight.to_d * free_recoil.to_d ** 2.to_d / (ACCELERATION_OF_GRAVITY * 2.to_d))
33
+ end
34
+ end
35
+
36
+ end
@@ -0,0 +1,18 @@
1
+ require_relative 'df_map'
2
+
3
+ module Ballistics
4
+ module Zero
5
+ def self.calculate_zero_angle(opts = {})
6
+ options = { y_intercept: 0 }.merge(opts)
7
+
8
+ drag_function = Ballistics::DFMap.__convert_df_to_int(options[:drag_function])
9
+ drag_coefficient = options[:drag_coefficient]
10
+ vi = options[:velocity]
11
+ sight_height = options[:sight_height]
12
+ zero_range = options[:zero_range]
13
+ y_intercept = options[:y_intercept]
14
+
15
+ self._calculate_zero_angle(drag_function, drag_coefficient, vi, sight_height, zero_range, y_intercept)
16
+ end
17
+ end
18
+ end
data/lib/ballistics.rb ADDED
@@ -0,0 +1,25 @@
1
+ require 'ballistics/ballistics'
2
+ require_relative 'ballistics/trajectory'
3
+ require_relative 'ballistics/atmosphere'
4
+ require_relative 'ballistics/zero'
5
+
6
+ module Ballistics
7
+
8
+ def self.build_environment(options = {})
9
+ Ballistics::Atmosphere.new(options)
10
+ end
11
+
12
+ def self.map_trajectory(options = {})
13
+ [:drag_function, :drag_coefficient, :velocity, :sight_height, :zero_range].each do |requirement|
14
+ raise ArgumentError "Failed to specify: #{requirement}" unless options[requirement]
15
+ end
16
+
17
+ # correct ballistic coefficient if an environment was passed
18
+ options[:drag_coefficient] = options[:environment].correct_ballistic_coefficient(options[:drag_coefficient]) if options[:environment]
19
+
20
+ options[:zero_angle] = Zero.calculate_zero_angle(options)
21
+ Ballistics::Trajectory.map_trajectory(options)
22
+ end
23
+
24
+ end
25
+
@@ -0,0 +1,15 @@
1
+ # Created on 2013-05-14
2
+ require_relative "../../lib/ballistics/atmosphere"
3
+
4
+ describe Ballistics::Atmosphere do
5
+ let(:atmosphere) { Ballistics::Atmosphere.new(altitude: 5430, barometric_pressure: 29.93, temperature: 40, relative_humidity: 0.48) }
6
+
7
+ it 'returns a corrected BC' do
8
+ expect(atmosphere.correct_ballistic_coefficient(0.338).round(3).to_f).to eq 0.392
9
+ end
10
+
11
+ it 'has working accessors' do
12
+ expect(atmosphere.temperature).to eq 40
13
+ end
14
+
15
+ end
@@ -0,0 +1,33 @@
1
+ require_relative "../../lib/ballistics/utils"
2
+
3
+ describe Ballistics do
4
+ context 'Bullet Calculations' do
5
+ it 'calculates sectional density' do
6
+ expect(Ballistics::Utils.sectional_density(230, 0.451).round(3)).to eq 0.162
7
+ end
8
+ end
9
+
10
+ context 'Energy Calculations' do
11
+ it 'calculates kinetic energy' do
12
+ expect(Ballistics::Utils.kinetic_energy(800.0, 230).round(2)).to eq 326.78
13
+ end
14
+
15
+ it 'calculates Taylor Knockout values' do
16
+ expect(Ballistics::Utils.taylorko(800.0, 230, 0.452).round(0)).to eq 12
17
+ end
18
+ end
19
+
20
+ context 'Recoil Calculations' do
21
+ it 'calculates recoil impulse' do
22
+ expect(Ballistics::Utils.recoil_impulse(150, 46, 2800).round(2)).to eq 2.68
23
+ end
24
+
25
+ it 'calculates free recoil' do
26
+ expect(Ballistics::Utils.free_recoil(150, 46, 2800, 8).round(1)).to eq 10.8
27
+ end
28
+
29
+ it 'calculates recoil energy' do
30
+ expect(Ballistics::Utils.recoil_energy(150, 46, 2800, 8).round(1)).to eq 14.5
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,19 @@
1
+ # Created on 2013-05-14
2
+ require_relative "../../lib/ballistics/zero"
3
+ require 'ballistics/ballistics'
4
+
5
+ describe Ballistics::Zero do
6
+ let(:options) do
7
+ {
8
+ drag_function: 'G1',
9
+ drag_coefficient: 0.5,
10
+ velocity: 1200,
11
+ sight_height: 1.6,
12
+ zero_range: 100
13
+ }
14
+ end
15
+
16
+ it 'returns the correct value' do
17
+ expect(Ballistics::Zero.calculate_zero_angle(options).round(6)).to eq 0.227188
18
+ end
19
+ end
@@ -0,0 +1,25 @@
1
+ require_relative '../lib/ballistics'
2
+
3
+ describe Ballistics do
4
+ let(:environment) { Ballistics.build_environment(altitude: 5430, barometric_pressure: 29.93, temperature: 40, relative_humidity: 0.48) }
5
+ let(:result_array) do
6
+ Ballistics.map_trajectory(drag_function: 'G1',
7
+ drag_coefficient: 0.5,
8
+ velocity: 2850,
9
+ sight_height: 1.6,
10
+ zero_range: 200,
11
+ max_range: 400,
12
+ environment: environment)
13
+ end
14
+
15
+ it 'returns an environment' do
16
+ expect(environment).to be_an_instance_of Ballistics::Atmosphere
17
+ end
18
+
19
+ it 'returns an array of queryable ballistic objects' do
20
+ expect(result_array[400]['path'].round(1).to_f).to eq -20.1
21
+ expect(result_array[400]['velocity'].to_i).to eq 2246
22
+ expect(result_array[400]['moa_correction'].round(1).to_f).to eq 4.8
23
+ expect(result_array[100]['moa_correction'].round(1).to_f).to eq -1.5
24
+ end
25
+ end
metadata ADDED
@@ -0,0 +1,62 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ballistics
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.0
5
+ platform: ruby
6
+ authors:
7
+ - Travis Staton
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-05-13 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: Ballistic calculations based on the GNU Ballistics library by Derek Yates.
14
+ email: hello@travisstaton.com
15
+ executables: []
16
+ extensions:
17
+ - ext/ballistics/extconf.rb
18
+ extra_rdoc_files: []
19
+ files:
20
+ - lib/ballistics.rb
21
+ - lib/ballistics/atmosphere.rb
22
+ - lib/ballistics/df_map.rb
23
+ - lib/ballistics/trajectory.rb
24
+ - lib/ballistics/utils.rb
25
+ - lib/ballistics/zero.rb
26
+ - ext/ballistics/ballistics.c
27
+ - ext/ballistics/extconf.rb
28
+ - ext/ballistics/gnu_ballistics.h
29
+ - spec/ballistics/atmosphere_spec.rb
30
+ - spec/ballistics/utils_spec.rb
31
+ - spec/ballistics/zero_spec.rb
32
+ - spec/ballistics_spec.rb
33
+ homepage: http://github.com/travisstaton/ballistics
34
+ licenses:
35
+ - GPL version 2
36
+ metadata: {}
37
+ post_install_message:
38
+ rdoc_options: []
39
+ require_paths:
40
+ - lib
41
+ required_ruby_version: !ruby/object:Gem::Requirement
42
+ requirements:
43
+ - - '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ required_rubygems_version: !ruby/object:Gem::Requirement
47
+ requirements:
48
+ - - '>='
49
+ - !ruby/object:Gem::Version
50
+ version: '0'
51
+ requirements: []
52
+ rubyforge_project:
53
+ rubygems_version: 2.0.3
54
+ signing_key:
55
+ specification_version: 4
56
+ summary: Useful calculations related to firearms and ballistics
57
+ test_files:
58
+ - spec/ballistics/atmosphere_spec.rb
59
+ - spec/ballistics/utils_spec.rb
60
+ - spec/ballistics/zero_spec.rb
61
+ - spec/ballistics_spec.rb
62
+ has_rdoc: