ballistics-engine 0.13.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +614 -0
- package/ballistics_engine.d.ts +49 -0
- package/ballistics_engine.js +544 -0
- package/ballistics_engine_bg.wasm +0 -0
- package/package.json +27 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Alex Jokela
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,614 @@
|
|
|
1
|
+
# Ballistics Engine
|
|
2
|
+
|
|
3
|
+
A high-performance ballistics trajectory calculation engine with comprehensive physics modeling, automatic zeroing, and statistical analysis capabilities.
|
|
4
|
+
|
|
5
|
+
**Project Website:** [https://ballistics.rs/](https://ballistics.rs/)
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
- **Full 3D Trajectory Integration** - Six-state ballistic modeling with adaptive RK45 and fixed-step RK4 integration methods
|
|
10
|
+
- **Advanced Drag Models** - Support for G1, G7, and custom drag curves with automatic transonic corrections
|
|
11
|
+
- **Automatic Zeroing** - Calculate sight adjustments and apply zero angles automatically
|
|
12
|
+
- **Unit Conversion** - Seamless switching between Imperial (default) and Metric units
|
|
13
|
+
- **BC Segmentation** - Velocity-dependent ballistic coefficient modeling with automatic estimation
|
|
14
|
+
- **Atmospheric Modeling** - Temperature, pressure, humidity, and altitude effects with ICAO standard atmosphere
|
|
15
|
+
- **Wind Effects** - 3D wind calculations with altitude-dependent wind shear modeling
|
|
16
|
+
- **Monte Carlo Simulations** - Statistical analysis with parameter uncertainties
|
|
17
|
+
- **BC Estimation** - Estimate ballistic coefficients from trajectory data
|
|
18
|
+
- **Advanced Physics**:
|
|
19
|
+
- **Spin Effects**: Magnus effect, enhanced spin drift with decay modeling
|
|
20
|
+
- **Earth Effects**: Coriolis effect with latitude-dependent calculations
|
|
21
|
+
- **Angular Motion**: Gyroscopic precession and nutation physics
|
|
22
|
+
- **Transonic Analysis**: Pitch damping coefficients and stability warnings
|
|
23
|
+
- **Trajectory Sampling**: Regular interval data collection for analysis
|
|
24
|
+
- **Form Factor Corrections**: Bullet-specific drag adjustments
|
|
25
|
+
- **Multiple Output Formats** - JSON, CSV, and formatted tables
|
|
26
|
+
|
|
27
|
+
## Installation
|
|
28
|
+
|
|
29
|
+
### From Source
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
git clone https://github.com/ajokela/ballistics-engine.git
|
|
33
|
+
cd ballistics-engine
|
|
34
|
+
cargo build --release
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
The binary will be at: `target/release/ballistics`
|
|
38
|
+
|
|
39
|
+
## Quick Start
|
|
40
|
+
|
|
41
|
+
### Basic Trajectory (Imperial Units - Default)
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
# .308 Winchester, 168gr bullet at 2700 fps
|
|
45
|
+
./ballistics trajectory -v 2700 -b 0.475 -m 168 -d 0.308 --max-range 1000
|
|
46
|
+
|
|
47
|
+
# With automatic zeroing at 200 yards
|
|
48
|
+
./ballistics trajectory -v 2700 -b 0.475 -m 168 -d 0.308 --auto-zero 200 --max-range 500
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### Metric Units
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
# Same bullet in metric units
|
|
55
|
+
./ballistics trajectory --units metric -v 823 -b 0.475 -m 10.9 -d 7.82 --max-range 1000
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Unit Systems
|
|
59
|
+
|
|
60
|
+
The engine supports two unit systems, selectable with the `--units` flag:
|
|
61
|
+
|
|
62
|
+
### Imperial (Default)
|
|
63
|
+
- **Velocity**: feet per second (fps)
|
|
64
|
+
- **Mass**: grains
|
|
65
|
+
- **Distance**: yards
|
|
66
|
+
- **Diameter**: inches
|
|
67
|
+
- **Temperature**: Fahrenheit
|
|
68
|
+
- **Pressure**: inHg
|
|
69
|
+
- **Wind**: mph
|
|
70
|
+
|
|
71
|
+
### Metric
|
|
72
|
+
- **Velocity**: meters per second (m/s)
|
|
73
|
+
- **Mass**: grams
|
|
74
|
+
- **Distance**: meters
|
|
75
|
+
- **Diameter**: millimeters
|
|
76
|
+
- **Temperature**: Celsius
|
|
77
|
+
- **Pressure**: hPa (millibars)
|
|
78
|
+
- **Wind**: m/s
|
|
79
|
+
|
|
80
|
+
## Commands
|
|
81
|
+
|
|
82
|
+
### Trajectory Calculation
|
|
83
|
+
|
|
84
|
+
Calculate ballistic trajectory with environmental conditions:
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
# Imperial units (default)
|
|
88
|
+
./ballistics trajectory \
|
|
89
|
+
-v 2700 # Velocity (fps)
|
|
90
|
+
-b 0.475 # Ballistic coefficient
|
|
91
|
+
-m 168 # Mass (grains)
|
|
92
|
+
-d 0.308 # Diameter (inches)
|
|
93
|
+
--drag-model g7 # G7 drag model
|
|
94
|
+
--angle 0 # Launch angle (degrees)
|
|
95
|
+
--max-range 1000 # Maximum range (yards)
|
|
96
|
+
--wind-speed 10 # Wind speed (mph)
|
|
97
|
+
--wind-direction 90 # Wind from right (degrees)
|
|
98
|
+
--temperature 59 # Temperature (Fahrenheit)
|
|
99
|
+
--pressure 29.92 # Pressure (inHg)
|
|
100
|
+
--humidity 50 # Relative humidity (%)
|
|
101
|
+
--altitude 0 # Altitude (feet)
|
|
102
|
+
--full # Show all trajectory points
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
#### Auto-Zero Feature
|
|
106
|
+
|
|
107
|
+
Automatically calculate and apply the zero angle for a specific distance:
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
# Zero at 200 yards and show trajectory to 500 yards
|
|
111
|
+
./ballistics trajectory \
|
|
112
|
+
-v 2700 -b 0.475 -m 168 -d 0.308 \
|
|
113
|
+
--auto-zero 200 \ # Automatically zero at 200 yards
|
|
114
|
+
--max-range 500 \
|
|
115
|
+
--full
|
|
116
|
+
|
|
117
|
+
# Custom sight height for auto-zero
|
|
118
|
+
./ballistics trajectory \
|
|
119
|
+
-v 2700 -b 0.475 -m 168 -d 0.308 \
|
|
120
|
+
--auto-zero 100 \
|
|
121
|
+
--sight-height 0.055 # 2.2 inches in yards
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
#### Advanced BC Modeling
|
|
125
|
+
|
|
126
|
+
Enable velocity-dependent BC modeling for more accurate long-range predictions:
|
|
127
|
+
|
|
128
|
+
```bash
|
|
129
|
+
# Enable BC segmentation (velocity-based BC changes)
|
|
130
|
+
./ballistics trajectory \
|
|
131
|
+
-v 2700 -b 0.475 -m 168 -d 0.308 \
|
|
132
|
+
--use-bc-segments \
|
|
133
|
+
--auto-zero 600 \
|
|
134
|
+
--max-range 1000
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
#### Advanced Physics - Magnus and Spin Drift
|
|
138
|
+
|
|
139
|
+
Enable advanced gyroscopic and aerodynamic effects:
|
|
140
|
+
|
|
141
|
+
```bash
|
|
142
|
+
# Magnus effect and spin drift calculation
|
|
143
|
+
./ballistics trajectory \
|
|
144
|
+
-v 2700 -b 0.475 -m 168 -d 0.308 \
|
|
145
|
+
--twist-rate 10 # 1:10" barrel twist
|
|
146
|
+
--twist-right # Right-hand twist
|
|
147
|
+
--enable-magnus # Enable Magnus effect
|
|
148
|
+
--enable-spin-drift # Enable enhanced spin drift
|
|
149
|
+
--wind-speed 10 \
|
|
150
|
+
--wind-direction 90 \
|
|
151
|
+
--max-range 1000
|
|
152
|
+
|
|
153
|
+
# Coriolis effect for extreme long range
|
|
154
|
+
./ballistics trajectory \
|
|
155
|
+
-v 3000 -b 0.750 -m 250 -d 0.338 \
|
|
156
|
+
--enable-coriolis \
|
|
157
|
+
--latitude 45 # Shooting latitude
|
|
158
|
+
--shooting-angle 90 # Azimuth (0=N, 90=E)
|
|
159
|
+
--max-range 2000
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
### Zero Calculation
|
|
163
|
+
|
|
164
|
+
Calculate the sight adjustment needed to zero at a specific distance:
|
|
165
|
+
|
|
166
|
+
```bash
|
|
167
|
+
# Calculate zero for 200 yards
|
|
168
|
+
./ballistics zero \
|
|
169
|
+
-v 2700 -b 0.475 -m 168 -d 0.308 \
|
|
170
|
+
--target-distance 200
|
|
171
|
+
|
|
172
|
+
# With custom sight height (default is 0.05 yards / 1.8 inches)
|
|
173
|
+
./ballistics zero \
|
|
174
|
+
-v 2700 -b 0.475 -m 168 -d 0.308 \
|
|
175
|
+
--target-distance 300 \
|
|
176
|
+
--sight-height 0.055 # 2.2 inches
|
|
177
|
+
|
|
178
|
+
# Metric example
|
|
179
|
+
./ballistics zero --units metric \
|
|
180
|
+
-v 823 -b 0.475 -m 10.9 -d 7.82 \
|
|
181
|
+
--target-distance 200 # 200 meters
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
Output includes:
|
|
185
|
+
- Zero angle in degrees
|
|
186
|
+
- Adjustment in MOA (Minutes of Angle)
|
|
187
|
+
- Adjustment in mrad (milliradians)
|
|
188
|
+
- Maximum ordinate (highest point of trajectory)
|
|
189
|
+
|
|
190
|
+
### Monte Carlo Simulation
|
|
191
|
+
|
|
192
|
+
Run statistical analysis with parameter variations:
|
|
193
|
+
|
|
194
|
+
```bash
|
|
195
|
+
./ballistics monte-carlo \
|
|
196
|
+
-v 2700 # Base velocity (fps)
|
|
197
|
+
-b 0.475 # Base BC
|
|
198
|
+
-m 168 # Mass (grains)
|
|
199
|
+
-d 0.308 # Diameter (inches)
|
|
200
|
+
-n 1000 # Number of simulations
|
|
201
|
+
--velocity-std 10 # Velocity std dev (fps)
|
|
202
|
+
--angle-std 0.5 # Angle std dev (degrees)
|
|
203
|
+
--bc-std 0.01 # BC std dev
|
|
204
|
+
--wind-std 2 # Wind speed std dev (mph)
|
|
205
|
+
--target-distance 300 # Target distance for hit probability
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
### BC Estimation
|
|
209
|
+
|
|
210
|
+
Estimate ballistic coefficient from observed trajectory data:
|
|
211
|
+
|
|
212
|
+
```bash
|
|
213
|
+
./ballistics estimate-bc \
|
|
214
|
+
-v 2700 -m 168 -d 0.308 \
|
|
215
|
+
--distance1 100 --drop1 0.0 # First data point
|
|
216
|
+
--distance2 200 --drop2 0.023 # Second data point
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
## Advanced Features
|
|
220
|
+
|
|
221
|
+
### Integration Methods
|
|
222
|
+
|
|
223
|
+
The engine supports two numerical integration methods:
|
|
224
|
+
|
|
225
|
+
- **RK45 (Dormand-Prince Adaptive)** - Default method, provides best accuracy with adaptive step sizing
|
|
226
|
+
- **RK4 (Runge-Kutta 4th Order Fixed-Step)** - Available with `--use-rk4-fixed` flag for faster computation
|
|
227
|
+
|
|
228
|
+
### Wind Shear Modeling
|
|
229
|
+
|
|
230
|
+
Model altitude-dependent wind variations:
|
|
231
|
+
|
|
232
|
+
```bash
|
|
233
|
+
./ballistics trajectory -v 2700 -b 0.475 -m 168 -d 0.308 \
|
|
234
|
+
--wind-speed 10 --wind-direction 90 \
|
|
235
|
+
--enable-wind-shear \
|
|
236
|
+
--max-range 1000
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
### Transonic Stability Analysis
|
|
240
|
+
|
|
241
|
+
Analyze projectile stability through the transonic regime:
|
|
242
|
+
|
|
243
|
+
```bash
|
|
244
|
+
./ballistics trajectory -v 3000 -b 0.475 -m 168 -d 0.308 \
|
|
245
|
+
--enable-pitch-damping \
|
|
246
|
+
--max-range 2000
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
Provides warnings about transonic instability and minimum pitch damping coefficients.
|
|
250
|
+
|
|
251
|
+
### Trajectory Sampling
|
|
252
|
+
|
|
253
|
+
Collect trajectory data at regular intervals for detailed analysis:
|
|
254
|
+
|
|
255
|
+
```bash
|
|
256
|
+
./ballistics trajectory -v 2700 -b 0.475 -m 168 -d 0.308 \
|
|
257
|
+
--sample-trajectory \
|
|
258
|
+
--sample-interval 25 # Sample every 25 yards
|
|
259
|
+
--max-range 1000 -o json
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
### Angular Motion Physics
|
|
263
|
+
|
|
264
|
+
Model precession and nutation of spinning projectiles:
|
|
265
|
+
|
|
266
|
+
```bash
|
|
267
|
+
./ballistics trajectory -v 2700 -b 0.475 -m 168 -d 0.308 \
|
|
268
|
+
--twist-rate 10 \
|
|
269
|
+
--enable-precession \
|
|
270
|
+
--max-range 1000
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
### Complete Advanced Physics Example
|
|
274
|
+
|
|
275
|
+
```bash
|
|
276
|
+
./ballistics trajectory \
|
|
277
|
+
-v 2850 -b 0.690 -m 230 -d 0.338 \
|
|
278
|
+
--drag-model g7 \
|
|
279
|
+
--twist-rate 8.5 --twist-right \
|
|
280
|
+
--enable-magnus \
|
|
281
|
+
--enable-coriolis \
|
|
282
|
+
--enable-spin-drift \
|
|
283
|
+
--enable-wind-shear \
|
|
284
|
+
--enable-pitch-damping \
|
|
285
|
+
--enable-precession \
|
|
286
|
+
--sample-trajectory \
|
|
287
|
+
--latitude 38.5 \
|
|
288
|
+
--shooting-angle 45 \
|
|
289
|
+
--wind-speed 15 --wind-direction 270 \
|
|
290
|
+
--altitude 6000 \
|
|
291
|
+
--max-range 2000
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
## Physics Modeling
|
|
295
|
+
|
|
296
|
+
The ballistics engine implements comprehensive physics modeling for accurate trajectory prediction:
|
|
297
|
+
|
|
298
|
+
### Aerodynamic Effects
|
|
299
|
+
- **Drag Modeling** - Multiple drag functions (G1-G8, JBM, custom curves) with transonic flow corrections
|
|
300
|
+
- **Form Factor** - Projectile efficiency corrections based on shape and design
|
|
301
|
+
- **Reynolds Number Effects** - Viscosity and flow regime corrections
|
|
302
|
+
|
|
303
|
+
### Gyroscopic Effects
|
|
304
|
+
- **Spin Drift** - Lateral deviation due to gyroscopic and Magnus effects
|
|
305
|
+
- **Precession** - Gyroscopic precession of spinning projectile
|
|
306
|
+
- **Nutation** - Oscillatory motion superimposed on precession
|
|
307
|
+
- **Spin Decay** - Reduction in spin rate over time due to aerodynamic damping
|
|
308
|
+
- **Pitch Damping** - Aerodynamic moments opposing angular motion
|
|
309
|
+
|
|
310
|
+
### Environmental Effects
|
|
311
|
+
- **Coriolis Effect** - Earth's rotation influence on long-range trajectories
|
|
312
|
+
- **Magnus Effect** - Force from spinning projectile in crossflow
|
|
313
|
+
- **Wind Shear** - Altitude-dependent wind variations
|
|
314
|
+
- **Atmospheric Stratification** - Density and sound speed variations with altitude
|
|
315
|
+
|
|
316
|
+
### Stability Modeling
|
|
317
|
+
- **Dynamic Stability** - Gyroscopic and aerodynamic stability calculations
|
|
318
|
+
- **Yaw of Repose** - Equilibrium yaw angle in crosswind
|
|
319
|
+
- **Limit Cycle Yaw** - Bounded oscillatory motion analysis
|
|
320
|
+
|
|
321
|
+
## FFI Layer
|
|
322
|
+
|
|
323
|
+
The library includes a Foreign Function Interface (FFI) layer for integration with iOS, Android, and other platforms. The FFI provides C-compatible bindings for all major functionality.
|
|
324
|
+
|
|
325
|
+
<img src="ios.png" alt="iOS Integration Example" width="35%">
|
|
326
|
+
|
|
327
|
+
### FFI Features
|
|
328
|
+
- **C-Compatible Structures** - All data structures use C-compatible layouts
|
|
329
|
+
- **Safe Memory Management** - Proper handling of memory across language boundaries
|
|
330
|
+
- **iOS/Swift Integration** - Ready for use with Swift through bridging headers
|
|
331
|
+
- **Android/JNI Support** - Compatible with Java Native Interface
|
|
332
|
+
- **Monte Carlo Simulation** - Statistical analysis with parameter variations
|
|
333
|
+
- **Error Handling** - Graceful error propagation across FFI boundary
|
|
334
|
+
|
|
335
|
+
### Example FFI Usage (C/Swift)
|
|
336
|
+
```c
|
|
337
|
+
// Create input parameters
|
|
338
|
+
FFIBallisticInputs inputs = {
|
|
339
|
+
.muzzle_velocity = 823.0, // m/s
|
|
340
|
+
.ballistic_coefficient = 0.475,
|
|
341
|
+
.mass = 0.0109, // kg
|
|
342
|
+
.diameter = 0.00782, // meters
|
|
343
|
+
.drag_model = 0, // G1
|
|
344
|
+
.sight_height = 0.05, // meters
|
|
345
|
+
.temperature = 15.0, // Celsius
|
|
346
|
+
.altitude = 0.0
|
|
347
|
+
};
|
|
348
|
+
|
|
349
|
+
// Calculate trajectory
|
|
350
|
+
FFITrajectoryResult* result = ballistics_calculate_trajectory(&inputs, NULL, NULL, 1000.0, 0.1);
|
|
351
|
+
|
|
352
|
+
// Use results
|
|
353
|
+
printf("Max range: %.2f meters\n", result->max_range);
|
|
354
|
+
|
|
355
|
+
// Clean up
|
|
356
|
+
ballistics_free_trajectory_result(result);
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
### Monte Carlo Simulation via FFI
|
|
360
|
+
```c
|
|
361
|
+
// Set up Monte Carlo parameters
|
|
362
|
+
FFIMonteCarloParams params = {
|
|
363
|
+
.num_simulations = 1000,
|
|
364
|
+
.velocity_std_dev = 10.0, // m/s variation
|
|
365
|
+
.angle_std_dev = 0.001, // radian variation (elevation)
|
|
366
|
+
.bc_std_dev = 0.01, // BC variation
|
|
367
|
+
.wind_speed_std_dev = 2.0, // m/s wind variation
|
|
368
|
+
.target_distance = 600.0, // Target at 600m
|
|
369
|
+
.azimuth_std_dev = 0.001 // radian variation (horizontal)
|
|
370
|
+
};
|
|
371
|
+
|
|
372
|
+
// Run simulation
|
|
373
|
+
FFIMonteCarloResults* results = ballistics_monte_carlo(&inputs, NULL, ¶ms);
|
|
374
|
+
|
|
375
|
+
// Use statistical results
|
|
376
|
+
printf("Mean range: %.2f m (σ=%.2f)\n", results->mean_range, results->std_dev_range);
|
|
377
|
+
printf("Hit probability at 600m: %.1f%%\n", results->hit_probability * 100);
|
|
378
|
+
|
|
379
|
+
// Access individual shots
|
|
380
|
+
for (int i = 0; i < results->num_results; i++) {
|
|
381
|
+
printf("Shot %d: Range %.2f m, Impact velocity %.2f m/s\n",
|
|
382
|
+
i, results->ranges[i], results->impact_velocities[i]);
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
// Clean up
|
|
386
|
+
ballistics_free_monte_carlo_results(results);
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
## Output Formats
|
|
390
|
+
|
|
391
|
+
All commands support three output formats via the `-o` flag:
|
|
392
|
+
|
|
393
|
+
- **table** (default) - Formatted ASCII table for terminal display
|
|
394
|
+
- **json** - Complete data in JSON format for programmatic use
|
|
395
|
+
- **csv** - Comma-separated values for spreadsheet analysis
|
|
396
|
+
|
|
397
|
+
## Practical Examples
|
|
398
|
+
|
|
399
|
+
### Hunting Zero
|
|
400
|
+
|
|
401
|
+
Zero a hunting rifle at 200 yards with environmental conditions:
|
|
402
|
+
|
|
403
|
+
```bash
|
|
404
|
+
# Calculate zero angle
|
|
405
|
+
./ballistics zero \
|
|
406
|
+
-v 2650 -b 0.460 -m 180 -d 0.308 \
|
|
407
|
+
--target-distance 200
|
|
408
|
+
|
|
409
|
+
# Verify trajectory with auto-zero
|
|
410
|
+
./ballistics trajectory \
|
|
411
|
+
-v 2650 -b 0.460 -m 180 -d 0.308 \
|
|
412
|
+
--auto-zero 200 \
|
|
413
|
+
--max-range 400 \
|
|
414
|
+
--wind-speed 15 \
|
|
415
|
+
--wind-direction 270 \
|
|
416
|
+
--temperature 32 \
|
|
417
|
+
--humidity 30 \
|
|
418
|
+
--altitude 5000 \
|
|
419
|
+
--full
|
|
420
|
+
```
|
|
421
|
+
|
|
422
|
+
### Long Range Shooting
|
|
423
|
+
|
|
424
|
+
Analyze trajectory for 1000-yard shot:
|
|
425
|
+
|
|
426
|
+
```bash
|
|
427
|
+
./ballistics trajectory \
|
|
428
|
+
-v 2850 -b 0.690 -m 230 -d 0.338 \
|
|
429
|
+
--drag-model g7 \
|
|
430
|
+
--auto-zero 100 \
|
|
431
|
+
--max-range 1100 \
|
|
432
|
+
--wind-speed 10 \
|
|
433
|
+
--wind-direction 45 \
|
|
434
|
+
--full \
|
|
435
|
+
-o json > trajectory.json
|
|
436
|
+
```
|
|
437
|
+
|
|
438
|
+
### Load Development
|
|
439
|
+
|
|
440
|
+
Compare different loads using Monte Carlo:
|
|
441
|
+
|
|
442
|
+
```bash
|
|
443
|
+
# Load 1: Higher velocity, more variation
|
|
444
|
+
./ballistics monte-carlo \
|
|
445
|
+
-v 2750 -b 0.475 -m 168 -d 0.308 \
|
|
446
|
+
-n 1000 \
|
|
447
|
+
--velocity-std 15 \
|
|
448
|
+
--target-distance 600
|
|
449
|
+
|
|
450
|
+
# Load 2: Lower velocity, more consistent
|
|
451
|
+
./ballistics monte-carlo \
|
|
452
|
+
-v 2680 -b 0.475 -m 168 -d 0.308 \
|
|
453
|
+
-n 1000 \
|
|
454
|
+
--velocity-std 8 \
|
|
455
|
+
--target-distance 600
|
|
456
|
+
```
|
|
457
|
+
|
|
458
|
+
## Advanced Features
|
|
459
|
+
|
|
460
|
+
### BC Segmentation
|
|
461
|
+
|
|
462
|
+
Velocity-dependent BC modeling accounts for how ballistic coefficient changes as the bullet slows down. Enable with `--use-bc-segments`:
|
|
463
|
+
|
|
464
|
+
- Automatically estimates BC segments based on bullet characteristics
|
|
465
|
+
- No external data required - uses caliber, weight, and BC
|
|
466
|
+
- Identifies bullet type (Match, Hunting, VLD, etc.) from parameters
|
|
467
|
+
- Applies physics-based BC degradation curves
|
|
468
|
+
|
|
469
|
+
Example:
|
|
470
|
+
```bash
|
|
471
|
+
./ballistics trajectory -v 2700 -b 0.475 -m 168 -d 0.308 --use-bc-segments --max-range 1000
|
|
472
|
+
```
|
|
473
|
+
|
|
474
|
+
|
|
475
|
+
### Advanced Physics Modeling
|
|
476
|
+
|
|
477
|
+
When enabled, the engine calculates:
|
|
478
|
+
- **Magnus Effect** - Side force from spinning projectiles
|
|
479
|
+
- **Spin Drift** - Lateral drift due to gyroscopic effects
|
|
480
|
+
- **Coriolis Effect** - Earth rotation effects (with latitude input)
|
|
481
|
+
- **Transonic Drag** - Enhanced drag modeling in transonic regime
|
|
482
|
+
- **Reynolds Number Corrections** - Viscous effects at low velocities
|
|
483
|
+
|
|
484
|
+
## Building from Source
|
|
485
|
+
|
|
486
|
+
### Requirements
|
|
487
|
+
|
|
488
|
+
- Rust 1.70 or later
|
|
489
|
+
- Cargo build system
|
|
490
|
+
|
|
491
|
+
### Build Commands
|
|
492
|
+
|
|
493
|
+
```bash
|
|
494
|
+
# Debug build
|
|
495
|
+
cargo build
|
|
496
|
+
|
|
497
|
+
# Release build (optimized)
|
|
498
|
+
cargo build --release
|
|
499
|
+
|
|
500
|
+
# Run tests
|
|
501
|
+
cargo test
|
|
502
|
+
|
|
503
|
+
# Build documentation
|
|
504
|
+
cargo doc --open
|
|
505
|
+
```
|
|
506
|
+
|
|
507
|
+
## Library Usage
|
|
508
|
+
|
|
509
|
+
Use as a Rust library in your own projects:
|
|
510
|
+
|
|
511
|
+
```rust
|
|
512
|
+
use ballistics_engine::{
|
|
513
|
+
BallisticInputs, TrajectorySolver,
|
|
514
|
+
WindConditions, AtmosphericConditions
|
|
515
|
+
};
|
|
516
|
+
|
|
517
|
+
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|
518
|
+
let inputs = BallisticInputs {
|
|
519
|
+
muzzle_velocity: 823.0, // m/s
|
|
520
|
+
launch_angle: 0.0, // radians
|
|
521
|
+
ballistic_coefficient: 0.475,
|
|
522
|
+
mass: 0.0109, // kg
|
|
523
|
+
diameter: 0.00782, // meters
|
|
524
|
+
sight_height: 0.05, // meters
|
|
525
|
+
..Default::default()
|
|
526
|
+
};
|
|
527
|
+
|
|
528
|
+
let wind = WindConditions {
|
|
529
|
+
speed: 5.0, // m/s
|
|
530
|
+
direction: 1.5708, // 90 degrees in radians
|
|
531
|
+
..Default::default()
|
|
532
|
+
};
|
|
533
|
+
|
|
534
|
+
let atmosphere = AtmosphericConditions {
|
|
535
|
+
temperature: 15.0, // Celsius
|
|
536
|
+
pressure: 1013.25, // hPa
|
|
537
|
+
humidity: 50.0, // %
|
|
538
|
+
altitude: 0.0, // meters
|
|
539
|
+
..Default::default()
|
|
540
|
+
};
|
|
541
|
+
|
|
542
|
+
let solver = TrajectorySolver::new(inputs, wind, atmosphere);
|
|
543
|
+
let result = solver.solve()?;
|
|
544
|
+
|
|
545
|
+
println!("Max range: {:.2} m", result.max_range);
|
|
546
|
+
println!("Max height: {:.2} m", result.max_height);
|
|
547
|
+
println!("Time of flight: {:.3} s", result.time_of_flight);
|
|
548
|
+
|
|
549
|
+
Ok(())
|
|
550
|
+
}
|
|
551
|
+
```
|
|
552
|
+
|
|
553
|
+
## Performance
|
|
554
|
+
|
|
555
|
+
Optimized Rust implementation provides:
|
|
556
|
+
- Single trajectory (1000m): ~5ms
|
|
557
|
+
- Monte Carlo (1000 runs): ~500ms
|
|
558
|
+
- BC estimation: ~50ms
|
|
559
|
+
- Zero calculation: ~10ms
|
|
560
|
+
|
|
561
|
+
## Common Ballistic Coefficients
|
|
562
|
+
|
|
563
|
+
| Caliber | Weight | BC (G1) | BC (G7) | Description |
|
|
564
|
+
|---------|--------|---------|---------|-------------|
|
|
565
|
+
| .223 | 55gr | 0.250 | - | FMJ |
|
|
566
|
+
| .223 | 77gr | 0.362 | 0.182 | Match |
|
|
567
|
+
| .308 | 168gr | 0.475 | 0.224 | Match |
|
|
568
|
+
| .308 | 175gr | 0.505 | 0.253 | Match |
|
|
569
|
+
| .308 | 180gr | 0.480 | - | Hunting |
|
|
570
|
+
| .338 | 300gr | 0.768 | 0.383 | Match |
|
|
571
|
+
| 6.5mm | 140gr | 0.620 | 0.310 | Match |
|
|
572
|
+
| .50 | 750gr | 1.050 | 0.520 | Match |
|
|
573
|
+
|
|
574
|
+
## Troubleshooting
|
|
575
|
+
|
|
576
|
+
### Trajectory hits ground early
|
|
577
|
+
- Check if you're using `--auto-zero` or setting `--angle` manually
|
|
578
|
+
- Default angle is 0° (horizontal), which will hit ground quickly
|
|
579
|
+
- Use `--auto-zero <distance>` to automatically calculate proper angle
|
|
580
|
+
|
|
581
|
+
### Units confusion
|
|
582
|
+
- Default is Imperial (fps, grains, yards)
|
|
583
|
+
- Use `--units metric` for metric system
|
|
584
|
+
- All inputs must match the selected unit system
|
|
585
|
+
|
|
586
|
+
### Unexpected BC behavior
|
|
587
|
+
- G1 and G7 models have different BC values for same bullet
|
|
588
|
+
- G7 typically better for boat-tail bullets
|
|
589
|
+
- BC segmentation automatically applied based on bullet type
|
|
590
|
+
|
|
591
|
+
## Contributing
|
|
592
|
+
|
|
593
|
+
Contributions are welcome! Please:
|
|
594
|
+
1. Fork the repository
|
|
595
|
+
2. Create a feature branch
|
|
596
|
+
3. Add tests for new features
|
|
597
|
+
4. Run `cargo test` and `cargo fmt`
|
|
598
|
+
5. Submit a pull request
|
|
599
|
+
|
|
600
|
+
## License
|
|
601
|
+
|
|
602
|
+
This project is licensed under the MIT License - see LICENSE file for details.
|
|
603
|
+
|
|
604
|
+
## Acknowledgments
|
|
605
|
+
|
|
606
|
+
- Ballistics physics based on Robert McCoy's "Modern Exterior Ballistics"
|
|
607
|
+
- Drag tables from military ballistics research
|
|
608
|
+
- BC segmentation algorithms from Bryan Litz's research
|
|
609
|
+
- Community contributions and testing
|
|
610
|
+
|
|
611
|
+
## Support
|
|
612
|
+
|
|
613
|
+
For issues, questions, or contributions:
|
|
614
|
+
- GitHub Issues: [github.com/ajokela/ballistics-engine/issues](https://github.com/ajokela/ballistics-engine/issues)
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/* tslint:disable */
|
|
2
|
+
/* eslint-disable */
|
|
3
|
+
/**
|
|
4
|
+
* Object-oriented calculator for programmatic use
|
|
5
|
+
* Provides a type-safe, fluent API alternative to the CLI interface
|
|
6
|
+
*/
|
|
7
|
+
export class Calculator {
|
|
8
|
+
free(): void;
|
|
9
|
+
[Symbol.dispose](): void;
|
|
10
|
+
setAltitude(altitude_ft: number): Calculator;
|
|
11
|
+
setDiameter(diameter_inches: number): Calculator;
|
|
12
|
+
setHumidity(humidity: number): Calculator;
|
|
13
|
+
setPressure(pressure_inhg: number): Calculator;
|
|
14
|
+
setVelocity(velocity_fps: number): Calculator;
|
|
15
|
+
setMaxRange(range_yards: number): Calculator;
|
|
16
|
+
setDragModel(model: string): Calculator;
|
|
17
|
+
setZeroRange(range_yards: number): Calculator;
|
|
18
|
+
setTemperature(temp_f: number): Calculator;
|
|
19
|
+
setSightHeight(height_inches: number): Calculator;
|
|
20
|
+
enableCoriolis(enabled: boolean, latitude?: number | null): Calculator;
|
|
21
|
+
/**
|
|
22
|
+
* Get full trajectory table as array of points
|
|
23
|
+
* Returns array of: [{ range_yards, drop_inches, windage_inches, velocity_fps, energy_ftlb, time_sec }, ...]
|
|
24
|
+
*/
|
|
25
|
+
getFullTrajectory(): any;
|
|
26
|
+
/**
|
|
27
|
+
* Calculate trajectory and return result as JavaScript object
|
|
28
|
+
* Returns: { range_yards, drop_inches, windage_inches, velocity_fps, energy_ftlb, time_sec }
|
|
29
|
+
*/
|
|
30
|
+
calculateTrajectory(range_yards: number): any;
|
|
31
|
+
enableSpinDrift(enabled: boolean, twist_rate?: number | null): Calculator;
|
|
32
|
+
/**
|
|
33
|
+
* Create a new calculator with default values
|
|
34
|
+
* Defaults: .308 Winchester 168gr at 2700 fps, standard atmosphere
|
|
35
|
+
*/
|
|
36
|
+
constructor();
|
|
37
|
+
setBC(bc: number): Calculator;
|
|
38
|
+
setMass(mass_grains: number): Calculator;
|
|
39
|
+
setWind(speed_mph: number, direction_deg: number): Calculator;
|
|
40
|
+
}
|
|
41
|
+
export class WasmBallistics {
|
|
42
|
+
free(): void;
|
|
43
|
+
[Symbol.dispose](): void;
|
|
44
|
+
/**
|
|
45
|
+
* Run a command and return the output
|
|
46
|
+
*/
|
|
47
|
+
runCommand(command: string): string;
|
|
48
|
+
constructor();
|
|
49
|
+
}
|
|
@@ -0,0 +1,544 @@
|
|
|
1
|
+
|
|
2
|
+
let imports = {};
|
|
3
|
+
imports['__wbindgen_placeholder__'] = module.exports;
|
|
4
|
+
|
|
5
|
+
let cachedUint8ArrayMemory0 = null;
|
|
6
|
+
|
|
7
|
+
function getUint8ArrayMemory0() {
|
|
8
|
+
if (cachedUint8ArrayMemory0 === null || cachedUint8ArrayMemory0.byteLength === 0) {
|
|
9
|
+
cachedUint8ArrayMemory0 = new Uint8Array(wasm.memory.buffer);
|
|
10
|
+
}
|
|
11
|
+
return cachedUint8ArrayMemory0;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
let cachedTextDecoder = new TextDecoder('utf-8', { ignoreBOM: true, fatal: true });
|
|
15
|
+
|
|
16
|
+
cachedTextDecoder.decode();
|
|
17
|
+
|
|
18
|
+
function decodeText(ptr, len) {
|
|
19
|
+
return cachedTextDecoder.decode(getUint8ArrayMemory0().subarray(ptr, ptr + len));
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function getStringFromWasm0(ptr, len) {
|
|
23
|
+
ptr = ptr >>> 0;
|
|
24
|
+
return decodeText(ptr, len);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function addToExternrefTable0(obj) {
|
|
28
|
+
const idx = wasm.__externref_table_alloc();
|
|
29
|
+
wasm.__wbindgen_externrefs.set(idx, obj);
|
|
30
|
+
return idx;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function handleError(f, args) {
|
|
34
|
+
try {
|
|
35
|
+
return f.apply(this, args);
|
|
36
|
+
} catch (e) {
|
|
37
|
+
const idx = addToExternrefTable0(e);
|
|
38
|
+
wasm.__wbindgen_exn_store(idx);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function getArrayU8FromWasm0(ptr, len) {
|
|
43
|
+
ptr = ptr >>> 0;
|
|
44
|
+
return getUint8ArrayMemory0().subarray(ptr / 1, ptr / 1 + len);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function isLikeNone(x) {
|
|
48
|
+
return x === undefined || x === null;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
let WASM_VECTOR_LEN = 0;
|
|
52
|
+
|
|
53
|
+
const cachedTextEncoder = new TextEncoder();
|
|
54
|
+
|
|
55
|
+
if (!('encodeInto' in cachedTextEncoder)) {
|
|
56
|
+
cachedTextEncoder.encodeInto = function (arg, view) {
|
|
57
|
+
const buf = cachedTextEncoder.encode(arg);
|
|
58
|
+
view.set(buf);
|
|
59
|
+
return {
|
|
60
|
+
read: arg.length,
|
|
61
|
+
written: buf.length
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function passStringToWasm0(arg, malloc, realloc) {
|
|
67
|
+
|
|
68
|
+
if (realloc === undefined) {
|
|
69
|
+
const buf = cachedTextEncoder.encode(arg);
|
|
70
|
+
const ptr = malloc(buf.length, 1) >>> 0;
|
|
71
|
+
getUint8ArrayMemory0().subarray(ptr, ptr + buf.length).set(buf);
|
|
72
|
+
WASM_VECTOR_LEN = buf.length;
|
|
73
|
+
return ptr;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
let len = arg.length;
|
|
77
|
+
let ptr = malloc(len, 1) >>> 0;
|
|
78
|
+
|
|
79
|
+
const mem = getUint8ArrayMemory0();
|
|
80
|
+
|
|
81
|
+
let offset = 0;
|
|
82
|
+
|
|
83
|
+
for (; offset < len; offset++) {
|
|
84
|
+
const code = arg.charCodeAt(offset);
|
|
85
|
+
if (code > 0x7F) break;
|
|
86
|
+
mem[ptr + offset] = code;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (offset !== len) {
|
|
90
|
+
if (offset !== 0) {
|
|
91
|
+
arg = arg.slice(offset);
|
|
92
|
+
}
|
|
93
|
+
ptr = realloc(ptr, len, len = offset + arg.length * 3, 1) >>> 0;
|
|
94
|
+
const view = getUint8ArrayMemory0().subarray(ptr + offset, ptr + len);
|
|
95
|
+
const ret = cachedTextEncoder.encodeInto(arg, view);
|
|
96
|
+
|
|
97
|
+
offset += ret.written;
|
|
98
|
+
ptr = realloc(ptr, len, offset, 1) >>> 0;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
WASM_VECTOR_LEN = offset;
|
|
102
|
+
return ptr;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function takeFromExternrefTable0(idx) {
|
|
106
|
+
const value = wasm.__wbindgen_externrefs.get(idx);
|
|
107
|
+
wasm.__externref_table_dealloc(idx);
|
|
108
|
+
return value;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const CalculatorFinalization = (typeof FinalizationRegistry === 'undefined')
|
|
112
|
+
? { register: () => {}, unregister: () => {} }
|
|
113
|
+
: new FinalizationRegistry(ptr => wasm.__wbg_calculator_free(ptr >>> 0, 1));
|
|
114
|
+
/**
|
|
115
|
+
* Object-oriented calculator for programmatic use
|
|
116
|
+
* Provides a type-safe, fluent API alternative to the CLI interface
|
|
117
|
+
*/
|
|
118
|
+
class Calculator {
|
|
119
|
+
|
|
120
|
+
static __wrap(ptr) {
|
|
121
|
+
ptr = ptr >>> 0;
|
|
122
|
+
const obj = Object.create(Calculator.prototype);
|
|
123
|
+
obj.__wbg_ptr = ptr;
|
|
124
|
+
CalculatorFinalization.register(obj, obj.__wbg_ptr, obj);
|
|
125
|
+
return obj;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
__destroy_into_raw() {
|
|
129
|
+
const ptr = this.__wbg_ptr;
|
|
130
|
+
this.__wbg_ptr = 0;
|
|
131
|
+
CalculatorFinalization.unregister(this);
|
|
132
|
+
return ptr;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
free() {
|
|
136
|
+
const ptr = this.__destroy_into_raw();
|
|
137
|
+
wasm.__wbg_calculator_free(ptr, 0);
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* @param {number} altitude_ft
|
|
141
|
+
* @returns {Calculator}
|
|
142
|
+
*/
|
|
143
|
+
setAltitude(altitude_ft) {
|
|
144
|
+
const ptr = this.__destroy_into_raw();
|
|
145
|
+
const ret = wasm.calculator_setAltitude(ptr, altitude_ft);
|
|
146
|
+
return Calculator.__wrap(ret);
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* @param {number} diameter_inches
|
|
150
|
+
* @returns {Calculator}
|
|
151
|
+
*/
|
|
152
|
+
setDiameter(diameter_inches) {
|
|
153
|
+
const ptr = this.__destroy_into_raw();
|
|
154
|
+
const ret = wasm.calculator_setDiameter(ptr, diameter_inches);
|
|
155
|
+
return Calculator.__wrap(ret);
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* @param {number} humidity
|
|
159
|
+
* @returns {Calculator}
|
|
160
|
+
*/
|
|
161
|
+
setHumidity(humidity) {
|
|
162
|
+
const ptr = this.__destroy_into_raw();
|
|
163
|
+
const ret = wasm.calculator_setHumidity(ptr, humidity);
|
|
164
|
+
return Calculator.__wrap(ret);
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* @param {number} pressure_inhg
|
|
168
|
+
* @returns {Calculator}
|
|
169
|
+
*/
|
|
170
|
+
setPressure(pressure_inhg) {
|
|
171
|
+
const ptr = this.__destroy_into_raw();
|
|
172
|
+
const ret = wasm.calculator_setPressure(ptr, pressure_inhg);
|
|
173
|
+
return Calculator.__wrap(ret);
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* @param {number} velocity_fps
|
|
177
|
+
* @returns {Calculator}
|
|
178
|
+
*/
|
|
179
|
+
setVelocity(velocity_fps) {
|
|
180
|
+
const ptr = this.__destroy_into_raw();
|
|
181
|
+
const ret = wasm.calculator_setVelocity(ptr, velocity_fps);
|
|
182
|
+
return Calculator.__wrap(ret);
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* @param {number} range_yards
|
|
186
|
+
* @returns {Calculator}
|
|
187
|
+
*/
|
|
188
|
+
setMaxRange(range_yards) {
|
|
189
|
+
const ptr = this.__destroy_into_raw();
|
|
190
|
+
const ret = wasm.calculator_setMaxRange(ptr, range_yards);
|
|
191
|
+
return Calculator.__wrap(ret);
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* @param {string} model
|
|
195
|
+
* @returns {Calculator}
|
|
196
|
+
*/
|
|
197
|
+
setDragModel(model) {
|
|
198
|
+
const ptr = this.__destroy_into_raw();
|
|
199
|
+
const ptr0 = passStringToWasm0(model, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
|
200
|
+
const len0 = WASM_VECTOR_LEN;
|
|
201
|
+
const ret = wasm.calculator_setDragModel(ptr, ptr0, len0);
|
|
202
|
+
return Calculator.__wrap(ret);
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* @param {number} range_yards
|
|
206
|
+
* @returns {Calculator}
|
|
207
|
+
*/
|
|
208
|
+
setZeroRange(range_yards) {
|
|
209
|
+
const ptr = this.__destroy_into_raw();
|
|
210
|
+
const ret = wasm.calculator_setZeroRange(ptr, range_yards);
|
|
211
|
+
return Calculator.__wrap(ret);
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* @param {number} temp_f
|
|
215
|
+
* @returns {Calculator}
|
|
216
|
+
*/
|
|
217
|
+
setTemperature(temp_f) {
|
|
218
|
+
const ptr = this.__destroy_into_raw();
|
|
219
|
+
const ret = wasm.calculator_setTemperature(ptr, temp_f);
|
|
220
|
+
return Calculator.__wrap(ret);
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* @param {number} height_inches
|
|
224
|
+
* @returns {Calculator}
|
|
225
|
+
*/
|
|
226
|
+
setSightHeight(height_inches) {
|
|
227
|
+
const ptr = this.__destroy_into_raw();
|
|
228
|
+
const ret = wasm.calculator_setSightHeight(ptr, height_inches);
|
|
229
|
+
return Calculator.__wrap(ret);
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* @param {boolean} enabled
|
|
233
|
+
* @param {number | null} [latitude]
|
|
234
|
+
* @returns {Calculator}
|
|
235
|
+
*/
|
|
236
|
+
enableCoriolis(enabled, latitude) {
|
|
237
|
+
const ptr = this.__destroy_into_raw();
|
|
238
|
+
const ret = wasm.calculator_enableCoriolis(ptr, enabled, !isLikeNone(latitude), isLikeNone(latitude) ? 0 : latitude);
|
|
239
|
+
return Calculator.__wrap(ret);
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* Get full trajectory table as array of points
|
|
243
|
+
* Returns array of: [{ range_yards, drop_inches, windage_inches, velocity_fps, energy_ftlb, time_sec }, ...]
|
|
244
|
+
* @returns {any}
|
|
245
|
+
*/
|
|
246
|
+
getFullTrajectory() {
|
|
247
|
+
const ret = wasm.calculator_getFullTrajectory(this.__wbg_ptr);
|
|
248
|
+
if (ret[2]) {
|
|
249
|
+
throw takeFromExternrefTable0(ret[1]);
|
|
250
|
+
}
|
|
251
|
+
return takeFromExternrefTable0(ret[0]);
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* Calculate trajectory and return result as JavaScript object
|
|
255
|
+
* Returns: { range_yards, drop_inches, windage_inches, velocity_fps, energy_ftlb, time_sec }
|
|
256
|
+
* @param {number} range_yards
|
|
257
|
+
* @returns {any}
|
|
258
|
+
*/
|
|
259
|
+
calculateTrajectory(range_yards) {
|
|
260
|
+
const ret = wasm.calculator_calculateTrajectory(this.__wbg_ptr, range_yards);
|
|
261
|
+
if (ret[2]) {
|
|
262
|
+
throw takeFromExternrefTable0(ret[1]);
|
|
263
|
+
}
|
|
264
|
+
return takeFromExternrefTable0(ret[0]);
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* @param {boolean} enabled
|
|
268
|
+
* @param {number | null} [twist_rate]
|
|
269
|
+
* @returns {Calculator}
|
|
270
|
+
*/
|
|
271
|
+
enableSpinDrift(enabled, twist_rate) {
|
|
272
|
+
const ptr = this.__destroy_into_raw();
|
|
273
|
+
const ret = wasm.calculator_enableSpinDrift(ptr, enabled, !isLikeNone(twist_rate), isLikeNone(twist_rate) ? 0 : twist_rate);
|
|
274
|
+
return Calculator.__wrap(ret);
|
|
275
|
+
}
|
|
276
|
+
/**
|
|
277
|
+
* Create a new calculator with default values
|
|
278
|
+
* Defaults: .308 Winchester 168gr at 2700 fps, standard atmosphere
|
|
279
|
+
*/
|
|
280
|
+
constructor() {
|
|
281
|
+
const ret = wasm.calculator_new();
|
|
282
|
+
this.__wbg_ptr = ret >>> 0;
|
|
283
|
+
CalculatorFinalization.register(this, this.__wbg_ptr, this);
|
|
284
|
+
return this;
|
|
285
|
+
}
|
|
286
|
+
/**
|
|
287
|
+
* @param {number} bc
|
|
288
|
+
* @returns {Calculator}
|
|
289
|
+
*/
|
|
290
|
+
setBC(bc) {
|
|
291
|
+
const ptr = this.__destroy_into_raw();
|
|
292
|
+
const ret = wasm.calculator_setBC(ptr, bc);
|
|
293
|
+
return Calculator.__wrap(ret);
|
|
294
|
+
}
|
|
295
|
+
/**
|
|
296
|
+
* @param {number} mass_grains
|
|
297
|
+
* @returns {Calculator}
|
|
298
|
+
*/
|
|
299
|
+
setMass(mass_grains) {
|
|
300
|
+
const ptr = this.__destroy_into_raw();
|
|
301
|
+
const ret = wasm.calculator_setMass(ptr, mass_grains);
|
|
302
|
+
return Calculator.__wrap(ret);
|
|
303
|
+
}
|
|
304
|
+
/**
|
|
305
|
+
* @param {number} speed_mph
|
|
306
|
+
* @param {number} direction_deg
|
|
307
|
+
* @returns {Calculator}
|
|
308
|
+
*/
|
|
309
|
+
setWind(speed_mph, direction_deg) {
|
|
310
|
+
const ptr = this.__destroy_into_raw();
|
|
311
|
+
const ret = wasm.calculator_setWind(ptr, speed_mph, direction_deg);
|
|
312
|
+
return Calculator.__wrap(ret);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
if (Symbol.dispose) Calculator.prototype[Symbol.dispose] = Calculator.prototype.free;
|
|
316
|
+
|
|
317
|
+
exports.Calculator = Calculator;
|
|
318
|
+
|
|
319
|
+
const WasmBallisticsFinalization = (typeof FinalizationRegistry === 'undefined')
|
|
320
|
+
? { register: () => {}, unregister: () => {} }
|
|
321
|
+
: new FinalizationRegistry(ptr => wasm.__wbg_wasmballistics_free(ptr >>> 0, 1));
|
|
322
|
+
|
|
323
|
+
class WasmBallistics {
|
|
324
|
+
|
|
325
|
+
__destroy_into_raw() {
|
|
326
|
+
const ptr = this.__wbg_ptr;
|
|
327
|
+
this.__wbg_ptr = 0;
|
|
328
|
+
WasmBallisticsFinalization.unregister(this);
|
|
329
|
+
return ptr;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
free() {
|
|
333
|
+
const ptr = this.__destroy_into_raw();
|
|
334
|
+
wasm.__wbg_wasmballistics_free(ptr, 0);
|
|
335
|
+
}
|
|
336
|
+
/**
|
|
337
|
+
* Run a command and return the output
|
|
338
|
+
* @param {string} command
|
|
339
|
+
* @returns {string}
|
|
340
|
+
*/
|
|
341
|
+
runCommand(command) {
|
|
342
|
+
let deferred3_0;
|
|
343
|
+
let deferred3_1;
|
|
344
|
+
try {
|
|
345
|
+
const ptr0 = passStringToWasm0(command, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
|
346
|
+
const len0 = WASM_VECTOR_LEN;
|
|
347
|
+
const ret = wasm.wasmballistics_runCommand(this.__wbg_ptr, ptr0, len0);
|
|
348
|
+
var ptr2 = ret[0];
|
|
349
|
+
var len2 = ret[1];
|
|
350
|
+
if (ret[3]) {
|
|
351
|
+
ptr2 = 0; len2 = 0;
|
|
352
|
+
throw takeFromExternrefTable0(ret[2]);
|
|
353
|
+
}
|
|
354
|
+
deferred3_0 = ptr2;
|
|
355
|
+
deferred3_1 = len2;
|
|
356
|
+
return getStringFromWasm0(ptr2, len2);
|
|
357
|
+
} finally {
|
|
358
|
+
wasm.__wbindgen_free(deferred3_0, deferred3_1, 1);
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
constructor() {
|
|
362
|
+
const ret = wasm.wasmballistics_new();
|
|
363
|
+
this.__wbg_ptr = ret >>> 0;
|
|
364
|
+
WasmBallisticsFinalization.register(this, this.__wbg_ptr, this);
|
|
365
|
+
return this;
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
if (Symbol.dispose) WasmBallistics.prototype[Symbol.dispose] = WasmBallistics.prototype.free;
|
|
369
|
+
|
|
370
|
+
exports.WasmBallistics = WasmBallistics;
|
|
371
|
+
|
|
372
|
+
exports.__wbg___wbindgen_is_function_ee8a6c5833c90377 = function(arg0) {
|
|
373
|
+
const ret = typeof(arg0) === 'function';
|
|
374
|
+
return ret;
|
|
375
|
+
};
|
|
376
|
+
|
|
377
|
+
exports.__wbg___wbindgen_is_object_c818261d21f283a4 = function(arg0) {
|
|
378
|
+
const val = arg0;
|
|
379
|
+
const ret = typeof(val) === 'object' && val !== null;
|
|
380
|
+
return ret;
|
|
381
|
+
};
|
|
382
|
+
|
|
383
|
+
exports.__wbg___wbindgen_is_string_fbb76cb2940daafd = function(arg0) {
|
|
384
|
+
const ret = typeof(arg0) === 'string';
|
|
385
|
+
return ret;
|
|
386
|
+
};
|
|
387
|
+
|
|
388
|
+
exports.__wbg___wbindgen_is_undefined_2d472862bd29a478 = function(arg0) {
|
|
389
|
+
const ret = arg0 === undefined;
|
|
390
|
+
return ret;
|
|
391
|
+
};
|
|
392
|
+
|
|
393
|
+
exports.__wbg___wbindgen_throw_b855445ff6a94295 = function(arg0, arg1) {
|
|
394
|
+
throw new Error(getStringFromWasm0(arg0, arg1));
|
|
395
|
+
};
|
|
396
|
+
|
|
397
|
+
exports.__wbg_call_525440f72fbfc0ea = function() { return handleError(function (arg0, arg1, arg2) {
|
|
398
|
+
const ret = arg0.call(arg1, arg2);
|
|
399
|
+
return ret;
|
|
400
|
+
}, arguments) };
|
|
401
|
+
|
|
402
|
+
exports.__wbg_call_e762c39fa8ea36bf = function() { return handleError(function (arg0, arg1) {
|
|
403
|
+
const ret = arg0.call(arg1);
|
|
404
|
+
return ret;
|
|
405
|
+
}, arguments) };
|
|
406
|
+
|
|
407
|
+
exports.__wbg_crypto_574e78ad8b13b65f = function(arg0) {
|
|
408
|
+
const ret = arg0.crypto;
|
|
409
|
+
return ret;
|
|
410
|
+
};
|
|
411
|
+
|
|
412
|
+
exports.__wbg_getRandomValues_b8f5dbd5f3995a9e = function() { return handleError(function (arg0, arg1) {
|
|
413
|
+
arg0.getRandomValues(arg1);
|
|
414
|
+
}, arguments) };
|
|
415
|
+
|
|
416
|
+
exports.__wbg_length_69bca3cb64fc8748 = function(arg0) {
|
|
417
|
+
const ret = arg0.length;
|
|
418
|
+
return ret;
|
|
419
|
+
};
|
|
420
|
+
|
|
421
|
+
exports.__wbg_msCrypto_a61aeb35a24c1329 = function(arg0) {
|
|
422
|
+
const ret = arg0.msCrypto;
|
|
423
|
+
return ret;
|
|
424
|
+
};
|
|
425
|
+
|
|
426
|
+
exports.__wbg_new_1acc0b6eea89d040 = function() {
|
|
427
|
+
const ret = new Object();
|
|
428
|
+
return ret;
|
|
429
|
+
};
|
|
430
|
+
|
|
431
|
+
exports.__wbg_new_e17d9f43105b08be = function() {
|
|
432
|
+
const ret = new Array();
|
|
433
|
+
return ret;
|
|
434
|
+
};
|
|
435
|
+
|
|
436
|
+
exports.__wbg_new_no_args_ee98eee5275000a4 = function(arg0, arg1) {
|
|
437
|
+
const ret = new Function(getStringFromWasm0(arg0, arg1));
|
|
438
|
+
return ret;
|
|
439
|
+
};
|
|
440
|
+
|
|
441
|
+
exports.__wbg_new_with_length_01aa0dc35aa13543 = function(arg0) {
|
|
442
|
+
const ret = new Uint8Array(arg0 >>> 0);
|
|
443
|
+
return ret;
|
|
444
|
+
};
|
|
445
|
+
|
|
446
|
+
exports.__wbg_node_905d3e251edff8a2 = function(arg0) {
|
|
447
|
+
const ret = arg0.node;
|
|
448
|
+
return ret;
|
|
449
|
+
};
|
|
450
|
+
|
|
451
|
+
exports.__wbg_process_dc0fbacc7c1c06f7 = function(arg0) {
|
|
452
|
+
const ret = arg0.process;
|
|
453
|
+
return ret;
|
|
454
|
+
};
|
|
455
|
+
|
|
456
|
+
exports.__wbg_prototypesetcall_2a6620b6922694b2 = function(arg0, arg1, arg2) {
|
|
457
|
+
Uint8Array.prototype.set.call(getArrayU8FromWasm0(arg0, arg1), arg2);
|
|
458
|
+
};
|
|
459
|
+
|
|
460
|
+
exports.__wbg_push_df81a39d04db858c = function(arg0, arg1) {
|
|
461
|
+
const ret = arg0.push(arg1);
|
|
462
|
+
return ret;
|
|
463
|
+
};
|
|
464
|
+
|
|
465
|
+
exports.__wbg_randomFillSync_ac0988aba3254290 = function() { return handleError(function (arg0, arg1) {
|
|
466
|
+
arg0.randomFillSync(arg1);
|
|
467
|
+
}, arguments) };
|
|
468
|
+
|
|
469
|
+
exports.__wbg_require_60cc747a6bc5215a = function() { return handleError(function () {
|
|
470
|
+
const ret = module.require;
|
|
471
|
+
return ret;
|
|
472
|
+
}, arguments) };
|
|
473
|
+
|
|
474
|
+
exports.__wbg_set_c2abbebe8b9ebee1 = function() { return handleError(function (arg0, arg1, arg2) {
|
|
475
|
+
const ret = Reflect.set(arg0, arg1, arg2);
|
|
476
|
+
return ret;
|
|
477
|
+
}, arguments) };
|
|
478
|
+
|
|
479
|
+
exports.__wbg_static_accessor_GLOBAL_89e1d9ac6a1b250e = function() {
|
|
480
|
+
const ret = typeof global === 'undefined' ? null : global;
|
|
481
|
+
return isLikeNone(ret) ? 0 : addToExternrefTable0(ret);
|
|
482
|
+
};
|
|
483
|
+
|
|
484
|
+
exports.__wbg_static_accessor_GLOBAL_THIS_8b530f326a9e48ac = function() {
|
|
485
|
+
const ret = typeof globalThis === 'undefined' ? null : globalThis;
|
|
486
|
+
return isLikeNone(ret) ? 0 : addToExternrefTable0(ret);
|
|
487
|
+
};
|
|
488
|
+
|
|
489
|
+
exports.__wbg_static_accessor_SELF_6fdf4b64710cc91b = function() {
|
|
490
|
+
const ret = typeof self === 'undefined' ? null : self;
|
|
491
|
+
return isLikeNone(ret) ? 0 : addToExternrefTable0(ret);
|
|
492
|
+
};
|
|
493
|
+
|
|
494
|
+
exports.__wbg_static_accessor_WINDOW_b45bfc5a37f6cfa2 = function() {
|
|
495
|
+
const ret = typeof window === 'undefined' ? null : window;
|
|
496
|
+
return isLikeNone(ret) ? 0 : addToExternrefTable0(ret);
|
|
497
|
+
};
|
|
498
|
+
|
|
499
|
+
exports.__wbg_subarray_480600f3d6a9f26c = function(arg0, arg1, arg2) {
|
|
500
|
+
const ret = arg0.subarray(arg1 >>> 0, arg2 >>> 0);
|
|
501
|
+
return ret;
|
|
502
|
+
};
|
|
503
|
+
|
|
504
|
+
exports.__wbg_versions_c01dfd4722a88165 = function(arg0) {
|
|
505
|
+
const ret = arg0.versions;
|
|
506
|
+
return ret;
|
|
507
|
+
};
|
|
508
|
+
|
|
509
|
+
exports.__wbindgen_cast_2241b6af4c4b2941 = function(arg0, arg1) {
|
|
510
|
+
// Cast intrinsic for `Ref(String) -> Externref`.
|
|
511
|
+
const ret = getStringFromWasm0(arg0, arg1);
|
|
512
|
+
return ret;
|
|
513
|
+
};
|
|
514
|
+
|
|
515
|
+
exports.__wbindgen_cast_cb9088102bce6b30 = function(arg0, arg1) {
|
|
516
|
+
// Cast intrinsic for `Ref(Slice(U8)) -> NamedExternref("Uint8Array")`.
|
|
517
|
+
const ret = getArrayU8FromWasm0(arg0, arg1);
|
|
518
|
+
return ret;
|
|
519
|
+
};
|
|
520
|
+
|
|
521
|
+
exports.__wbindgen_cast_d6cd19b81560fd6e = function(arg0) {
|
|
522
|
+
// Cast intrinsic for `F64 -> Externref`.
|
|
523
|
+
const ret = arg0;
|
|
524
|
+
return ret;
|
|
525
|
+
};
|
|
526
|
+
|
|
527
|
+
exports.__wbindgen_init_externref_table = function() {
|
|
528
|
+
const table = wasm.__wbindgen_externrefs;
|
|
529
|
+
const offset = table.grow(4);
|
|
530
|
+
table.set(0, undefined);
|
|
531
|
+
table.set(offset + 0, undefined);
|
|
532
|
+
table.set(offset + 1, null);
|
|
533
|
+
table.set(offset + 2, true);
|
|
534
|
+
table.set(offset + 3, false);
|
|
535
|
+
;
|
|
536
|
+
};
|
|
537
|
+
|
|
538
|
+
const wasmPath = `${__dirname}/ballistics_engine_bg.wasm`;
|
|
539
|
+
const wasmBytes = require('fs').readFileSync(wasmPath);
|
|
540
|
+
const wasmModule = new WebAssembly.Module(wasmBytes);
|
|
541
|
+
const wasm = exports.__wasm = new WebAssembly.Instance(wasmModule, imports).exports;
|
|
542
|
+
|
|
543
|
+
wasm.__wbindgen_start();
|
|
544
|
+
|
|
Binary file
|
package/package.json
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "ballistics-engine",
|
|
3
|
+
"collaborators": [
|
|
4
|
+
"Alex Jokela <email@tinycomputers.io>"
|
|
5
|
+
],
|
|
6
|
+
"description": "High-performance ballistics trajectory engine with professional physics",
|
|
7
|
+
"version": "0.13.3",
|
|
8
|
+
"license": "MIT OR Apache-2.0",
|
|
9
|
+
"repository": {
|
|
10
|
+
"type": "git",
|
|
11
|
+
"url": "https://github.com/ajokela/ballistics-engine"
|
|
12
|
+
},
|
|
13
|
+
"files": [
|
|
14
|
+
"ballistics_engine_bg.wasm",
|
|
15
|
+
"ballistics_engine.js",
|
|
16
|
+
"ballistics_engine.d.ts"
|
|
17
|
+
],
|
|
18
|
+
"main": "ballistics_engine.js",
|
|
19
|
+
"homepage": "https://ballistics.rs/",
|
|
20
|
+
"types": "ballistics_engine.d.ts",
|
|
21
|
+
"keywords": [
|
|
22
|
+
"ballistics",
|
|
23
|
+
"trajectory",
|
|
24
|
+
"physics",
|
|
25
|
+
"simulation"
|
|
26
|
+
]
|
|
27
|
+
}
|