ballistics-engine 0.25.1 → 0.26.0

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/README.md CHANGED
@@ -1,131 +1,912 @@
1
- # ballistics-engine (WASM)
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 and G7 reference curves (with automatic transonic corrections) plus user-supplied custom Cd(Mach) drag tables (`--drag-table`, used as-is with endpoint hold outside their measured domain, no transonic correction applied — see [CLI_USAGE.md](CLI_USAGE.md#custom-drag-tables); `bc_value` is ignored while a custom table is active)
11
+ - **Automatic Zeroing** - Calculate sight adjustments and apply zero angles automatically
12
+ - **Canted-Rifle Modeling** - Model a rifle zeroed level but fired canted (`--cant <DEGREES>`, alias `--cant-angle`, on `trajectory`/`monte-carlo`); clockwise cant shifts point of impact right and low downrange for a rifle with an upward zero correction — see [CLI_USAGE.md](CLI_USAGE.md#canted-shooting)
13
+ - **Moving-Target Lead** - Wind-aware hold tables for targets moving at a constant speed/angle, with iterative intercept-range correction for non-perpendicular motion (`lead` subcommand; public `ballistics_engine::calculate_lead` API) — see [CLI_USAGE.md](CLI_USAGE.md#moving-target-lead)
14
+ - **Mover Ring** - Field-tested alternative for engaging movers: a per-point ring radius (`target_speed × time-of-flight`) falls out of an already-solved trajectory with no second command or re-entered ballistic data (`trajectory --target-speed`, additive across table/JSON/CSV output); `lead` also gained `trajectory`'s powder-temperature flags for muzzle-velocity parity between the two — see [CLI_USAGE.md](CLI_USAGE.md#mover-ring---target-speed)
15
+ - **Unit Conversion** - Seamless switching between Imperial (default) and Metric units
16
+ - **BC Segmentation** - Velocity-dependent ballistic coefficient modeling with automatic estimation
17
+ - **Atmospheric Modeling** - Temperature, pressure, humidity, and altitude effects with ICAO standard atmosphere
18
+ - **Wind Effects** - 3D wind calculations with altitude-dependent wind shear modeling, **downrange-segmented wind** (`--wind-segment SPEED:ANGLE:DIST[:VERTICAL]`, repeatable — model wind that varies along the path, e.g. muzzle plus downrange sensor readings), and **vertical wind** (`--wind-vertical <SPEED>` on `trajectory`/`monte-carlo`, or the segment's optional 4th field; positive = updraft, raises point of impact) — see [CLI_USAGE.md](CLI_USAGE.md#vertical-wind)
19
+ - **Oblique Wind-Drift Cards** - Wind dope cards at any wind-FROM angle, not just full-value 90° crosswind (`wind-card --wind-angle <DEG>` or `--wind-angles <CSV>` for one card per angle); each cell is a real trajectory solve, default (no flags) unchanged from the classic full-value 90° card — see [CLI_USAGE.md](CLI_USAGE.md#wind-card)
20
+ - **Monte Carlo Simulations** - Statistical analysis with parameter uncertainties
21
+ - **BC Estimation** - Estimate ballistic coefficients from trajectory data
22
+ - **Advanced Physics**:
23
+ - **Spin Effects**: Magnus effect and empirical Litz spin drift
24
+ - **Earth Effects**: Coriolis effect with latitude-dependent calculations
25
+ - **Angular Motion**: Gyroscopic precession and nutation physics
26
+ - **Transonic Analysis**: Pitch damping coefficients and stability warnings
27
+ - **Trajectory Sampling**: Regular interval data collection for analysis
28
+ - **Form Factor Corrections**: Bullet-specific drag adjustments
29
+ - **Multiple Output Formats** - JSON, CSV, formatted tables, and printable PDF dope cards
30
+ - **Terminal Chart** - Inline drop-vs-range and drift-vs-range charts right in the terminal (`trajectory --plot`, Unicode braille-dot canvas by default, `--plot ascii` fallback); pure Rust, zero new dependencies, no ANSI colors — see [CLI_USAGE.md](CLI_USAGE.md#terminal-chart---plot)
31
+ - **Profile import**: `ballistics profile import file.a7p` — imports ArcherBC2 `.a7p` profiles (rifle, bullet, atmosphere, zero) with a full mapping report; `--dry-run` previews without saving
32
+
33
+ ## Installation
34
+
35
+ ### From crates.io
2
36
 
3
- WebAssembly build of [`ballistics-engine`](https://github.com/ajokela/ballistics-engine), a
4
- high-performance ballistics trajectory engine (RK4 integration, wind/Coriolis/spin-drift/Magnus
5
- effects, custom drag tables, Monte Carlo, and more). This package exposes the same CLI-style
6
- command surface as the native Rust binary, plus a small object-oriented `Calculator` class, to
7
- JavaScript/TypeScript.
37
+ ```bash
38
+ cargo install ballistics-engine
39
+ ```
40
+
41
+ ### From Source
42
+
43
+ ```bash
44
+ git clone https://github.com/ajokela/ballistics-engine.git
45
+ cd ballistics-engine
46
+ cargo build --release
47
+ ```
48
+
49
+ The binary will be at: `target/release/ballistics`
50
+
51
+ ### Feature Flags
52
+
53
+ | Feature | Default | Description |
54
+ |---------|---------|-------------|
55
+ | `online` | ✅ Yes | HTTP client for API integration (`--online` flag) |
56
+
57
+ To build without network capabilities:
58
+ ```bash
59
+ cargo build --release --no-default-features
60
+ ```
61
+
62
+ ## Quick Start
63
+
64
+ ### Basic Trajectory (Imperial Units - Default)
65
+
66
+ ```bash
67
+ # .308 Winchester, 168gr bullet at 2700 fps
68
+ ./ballistics trajectory -v 2700 -b 0.475 -m 168 -d 0.308 --max-range 1000
69
+
70
+ # With automatic zeroing at 200 yards
71
+ ./ballistics trajectory -v 2700 -b 0.475 -m 168 -d 0.308 --auto-zero 200 --max-range 500
72
+ ```
73
+
74
+ ### Metric Units
75
+
76
+ ```bash
77
+ # Same bullet in metric units
78
+ ./ballistics trajectory --units metric -v 823 -b 0.475 -m 10.9 -d 7.82 --max-range 1000
79
+ ```
80
+
81
+ ## Unit Systems
82
+
83
+ The engine supports two unit systems, selectable with the `--units` flag:
84
+
85
+ ### Imperial (Default)
86
+ - **Velocity**: feet per second (fps)
87
+ - **Mass**: grains
88
+ - **Distance**: yards
89
+ - **Diameter**: inches
90
+ - **Temperature**: Fahrenheit
91
+ - **Pressure**: inHg
92
+ - **Wind**: mph
93
+
94
+ ### Metric
95
+ - **Velocity**: meters per second (m/s)
96
+ - **Mass**: grams
97
+ - **Distance**: meters
98
+ - **Diameter**: millimeters
99
+ - **Temperature**: Celsius
100
+ - **Pressure**: hPa (millibars)
101
+ - **Wind**: m/s
102
+
103
+ ## Commands
104
+
105
+ ### Trajectory Calculation
106
+
107
+ Calculate ballistic trajectory with environmental conditions:
108
+
109
+ ```bash
110
+ # Imperial units (default)
111
+ ./ballistics trajectory \
112
+ -v 2700 # Velocity (fps)
113
+ -b 0.475 # Ballistic coefficient
114
+ -m 168 # Mass (grains)
115
+ -d 0.308 # Diameter (inches)
116
+ --drag-model g7 # G7 drag model
117
+ --angle 0 # Launch angle (degrees)
118
+ --max-range 1000 # Maximum range (yards)
119
+ --wind-speed 10 # Wind speed (mph)
120
+ --wind-direction 90 # Wind from right (degrees)
121
+ --temperature 59 # Temperature (Fahrenheit)
122
+ --pressure 29.92 # Pressure (inHg)
123
+ --humidity 50 # Relative humidity (%)
124
+ --altitude 0 # Altitude (feet)
125
+ --full # Show all trajectory points
126
+ ```
127
+
128
+ #### Auto-Zero Feature
129
+
130
+ Automatically calculate and apply the zero angle for a specific distance:
131
+
132
+ ```bash
133
+ # Zero at 200 yards and show trajectory to 500 yards
134
+ ./ballistics trajectory \
135
+ -v 2700 -b 0.475 -m 168 -d 0.308 \
136
+ --auto-zero 200 \ # Automatically zero at 200 yards
137
+ --max-range 500 \
138
+ --full
139
+
140
+ # Custom sight height for auto-zero
141
+ ./ballistics trajectory \
142
+ -v 2700 -b 0.475 -m 168 -d 0.308 \
143
+ --auto-zero 100 \
144
+ --sight-height 0.055 # 2.2 inches in yards
145
+ ```
146
+
147
+ #### Zero-Day Conditions (zero shift)
148
+
149
+ A rifle's zero is a fixed barrel angle set on the day you sighted in. If you later shoot
150
+ in different weather — or with a different muzzle velocity (e.g. a cold vs. warm powder
151
+ temperature) — the point of impact shifts. By default `--auto-zero` solves the zero angle
152
+ using the same conditions you pass for the shot, which assumes you zeroed in today's
153
+ conditions. The `--zero-*` flags let you decouple the two: the zero **angle** is solved
154
+ under the conditions the rifle was actually zeroed in, while the trajectory itself runs
155
+ under the current shot-day conditions.
156
+
157
+ ```bash
158
+ # Zeroed on a cold morning (28 F) at 2600 fps; shooting this afternoon at 85 F / 2700 fps.
159
+ # The zero angle is solved for the cold/slow load, then the warm/fast trajectory is
160
+ # computed against it — so the dope correctly shows the point of impact drifting high.
161
+ ./ballistics trajectory \
162
+ -v 2700 -b 0.19 -m 77 -d 0.224 --drag-model g7 \
163
+ --temperature 85 --pressure 29.92 \
164
+ --auto-zero 100 --max-range 1000 --full \
165
+ --zero-velocity 2600 \
166
+ --zero-temperature 28
167
+ ```
168
+
169
+ Available overrides (each independently optional; any omitted flag falls back to the
170
+ shot-day value, so leaving them all off reproduces the previous behavior exactly):
171
+
172
+ | Flag | Meaning | Units (imperial / metric) |
173
+ |------|---------|---------------------------|
174
+ | `--zero-velocity` | Muzzle velocity on the zeroing day | fps / m·s⁻¹ |
175
+ | `--zero-temperature` | Air temperature on the zeroing day | °F / °C |
176
+ | `--zero-pressure` | Barometric pressure on the zeroing day | inHg / hPa |
177
+ | `--zero-humidity` | Relative humidity on the zeroing day | percent |
178
+ | `--zero-altitude` | Altitude on the zeroing day | feet / meters |
179
+
180
+ #### Powder Temperature
181
+
182
+ Propellant temperature changes muzzle velocity. Two models are available:
183
+
184
+ **Linear** — a constant sensitivity (fps or m/s per degree) applied relative to the
185
+ temperature the load was chronographed at:
186
+
187
+ ```bash
188
+ ./ballistics trajectory -v 2700 -b 0.19 -m 77 -d 0.224 --drag-model g7 \
189
+ --temperature 85 --use-powder-sensitivity \
190
+ --powder-temp-sensitivity 1.2 --powder-temp 70 # +1.2 fps per F above 70 F
191
+ ```
192
+
193
+ **Measured curve (non-linear)** — real powders aren't perfectly linear (temperature-
194
+ stable powders flatten; others steepen when hot). If you've chronographed the load at
195
+ several temperatures, pass the points directly and the muzzle velocity is interpolated
196
+ at the powder temperature (clamped at the endpoints — no extrapolation). This
197
+ **overrides** `--powder-temp-sensitivity` when supplied:
198
+
199
+ ```bash
200
+ ./ballistics trajectory -v 2700 -b 0.19 -m 77 -d 0.224 --drag-model g7 \
201
+ --temperature 85 \
202
+ --powder-temp-curve "40:2620,70:2700,100:2760" # TEMP:VELOCITY points
203
+ ```
204
+
205
+ **Powder temperature vs air temperature.** The curve maps *powder* temperature to
206
+ velocity, while `--temperature` drives air *density*. These are decoupled: the curve is
207
+ looked up at `--powder-temp` when given, otherwise at `--temperature` (powder assumed at
208
+ air temperature). So a load left in a hot chamber or a cold pocket:
209
+
210
+ ```bash
211
+ # 85 F air (density), but the powder is at 60 F (velocity from the curve at 60 F)
212
+ ./ballistics trajectory ... --temperature 85 --powder-temp 60 \
213
+ --powder-temp-curve "40:2620,70:2700,100:2760"
214
+ ```
215
+
216
+ Both powder models compose with `--auto-zero`, symmetrically. For the linear model,
217
+ `--zero-temperature` resolves zero-day velocity relative to the reference `--powder-temp`.
218
+ For a curve, `--zero-powder-temp` overrides the powder lookup; otherwise an explicit
219
+ `--zero-temperature` is used, or the shot-day `--powder-temp` is inherited when no zero-day
220
+ temperature was supplied. Zero-day atmosphere flags still drive air density independently.
221
+ An explicit `--zero-velocity` takes precedence over either powder model.
222
+
223
+ #### Bore Height and Ground Impact
224
+
225
+ Control bore height above ground and ground impact detection:
226
+
227
+ ```bash
228
+ # Set bore height for prone shooting position (2 feet)
229
+ ./ballistics trajectory \
230
+ -v 2700 -b 0.475 -m 168 -d 0.308 \
231
+ --auto-zero 100 \
232
+ --bore-height 2 # 2 feet (imperial) or meters (metric)
233
+
234
+ # Disable ground impact detection for full trajectory to max range
235
+ ./ballistics trajectory \
236
+ -v 2700 -b 0.475 -m 168 -d 0.308 \
237
+ --auto-zero 100 \
238
+ --max-range 1000 \
239
+ --ignore-ground-impact
240
+ ```
241
+
242
+ Bore height defaults: 5 feet (imperial) / 1.5 meters (metric) - standing position.
243
+
244
+ #### Advanced BC Modeling
245
+
246
+ Enable velocity-dependent BC modeling for more accurate long-range predictions:
247
+
248
+ ```bash
249
+ # Enable BC segmentation (velocity-based BC changes)
250
+ ./ballistics trajectory \
251
+ -v 2700 -b 0.475 -m 168 -d 0.308 \
252
+ --use-bc-segments \
253
+ --auto-zero 600 \
254
+ --max-range 1000
255
+ ```
256
+
257
+ #### Advanced Physics - Magnus and Spin Drift
258
+
259
+ Enable advanced gyroscopic and aerodynamic effects:
260
+
261
+ ```bash
262
+ # Magnus effect and spin drift calculation
263
+ ./ballistics trajectory \
264
+ -v 2700 -b 0.475 -m 168 -d 0.308 \
265
+ --twist-rate 10 # 1:10" barrel twist
266
+ --twist-right # Right-hand twist
267
+ --enable-magnus # Enable Magnus effect
268
+ --enable-spin-drift # Enable empirical Litz spin drift
269
+ --wind-speed 10 \
270
+ --wind-direction 90 \
271
+ --max-range 1000
272
+
273
+ # Coriolis effect for extreme long range
274
+ ./ballistics trajectory \
275
+ -v 3000 -b 0.750 -m 250 -d 0.338 \
276
+ --enable-coriolis \
277
+ --latitude 45 # Shooting latitude
278
+ --shooting-angle 90 # Azimuth (0=N, 90=E)
279
+ --max-range 2000
280
+ ```
281
+
282
+ ### Zero Calculation
283
+
284
+ Calculate the sight adjustment needed to zero at a specific distance:
285
+
286
+ ```bash
287
+ # Calculate zero for 200 yards
288
+ ./ballistics zero \
289
+ -v 2700 -b 0.475 -m 168 -d 0.308 \
290
+ --target-distance 200
291
+
292
+ # With custom sight height (default is 0.05 yards / 1.8 inches)
293
+ ./ballistics zero \
294
+ -v 2700 -b 0.475 -m 168 -d 0.308 \
295
+ --target-distance 300 \
296
+ --sight-height 0.055 # 2.2 inches
297
+
298
+ # Metric example
299
+ ./ballistics zero --units metric \
300
+ -v 823 -b 0.475 -m 10.9 -d 7.82 \
301
+ --target-distance 200 # 200 meters
302
+ ```
303
+
304
+ Output includes:
305
+ - Zero angle in degrees
306
+ - Adjustment in MOA (Minutes of Angle)
307
+ - Adjustment in mrad (milliradians)
308
+ - Maximum ordinate (highest point of trajectory)
309
+
310
+ ### Monte Carlo Simulation
311
+
312
+ Run statistical analysis with parameter variations:
313
+
314
+ ```bash
315
+ ./ballistics monte-carlo \
316
+ -v 2700 # Base velocity (fps)
317
+ -b 0.475 # Base BC
318
+ -m 168 # Mass (grains)
319
+ -d 0.308 # Diameter (inches)
320
+ -n 1000 # Number of simulations
321
+ --velocity-std 10 # Velocity std dev (fps)
322
+ --angle-std 0.5 # Angle std dev (degrees)
323
+ --bc-std 0.01 # BC std dev
324
+ --wind-std 2 # Wind speed std dev (mph)
325
+ --wind-direction-std 5 # Wind direction std dev (degrees)
326
+ --target-distance 300 # Target distance for hit probability
327
+ ```
328
+
329
+ ### BC Estimation
330
+
331
+ Estimate ballistic coefficient from observed trajectory data:
332
+
333
+ ```bash
334
+ ./ballistics estimate-bc \
335
+ -v 2700 -m 168 -d 0.308 \
336
+ --distance1 100 --drop1 0.0 # First data point
337
+ --distance2 200 --drop2 0.023 # Second data point
338
+ ```
339
+
340
+ ### True Velocity (Velocity Truing)
341
+
342
+ Calculate the effective muzzle velocity that produces a measured drop at a known range. This helps "true" your ballistic system by identifying discrepancies between chronograph readings and real-world performance.
8
343
 
9
- Built with `wasm-pack` and `--no-default-features` — the native crate's default `pdf`/`online`
10
- features pull in dependencies that don't compile for `wasm32-unknown-unknown`, so the PDF dope-card
11
- export and the online BC-estimation API are not available from WASM.
344
+ ```bash
345
+ # Basic offline calculation
346
+ ./ballistics true-velocity \
347
+ --measured-drop 5.1 --range 600 \
348
+ --bc 0.27 --drag-model g7 \
349
+ --mass 140 --diameter 0.264 \
350
+ --offline
351
+
352
+ # With chronograph comparison
353
+ ./ballistics true-velocity \
354
+ --measured-drop 5.1 --range 600 \
355
+ --bc 0.27 --drag-model g7 \
356
+ --mass 140 --diameter 0.264 \
357
+ --chrono-velocity 2822 \
358
+ --offline
359
+
360
+ # With BC5D tables for improved accuracy
361
+ ./ballistics true-velocity \
362
+ --measured-drop 5.1 --range 600 \
363
+ --bc 0.27 --drag-model g7 \
364
+ --mass 140 --diameter 0.264 \
365
+ --bc-table-auto --offline
366
+
367
+ # Joint MV + BC calibration from multiple observed impacts
368
+ ./ballistics true-velocity \
369
+ --range 300 --measured-drop 1.30 \
370
+ --observed 600:4.40 --observed 900:9.00 \
371
+ --bc 0.45 --drag-model g1 \
372
+ --mass 168 --diameter 0.308
373
+ ```
374
+
375
+ Use case: A shooter measures 5.1 MIL of drop at 600 yards. Their chronograph showed 2822 fps. The command calculates the effective velocity is actually ~2740 fps, suggesting a -82 fps adjustment for accurate ballistic predictions.
376
+
377
+ **Joint velocity + BC truing.** With two or more `--observed RANGE:DROP` impacts spanning supersonic to transonic ranges, `true-velocity` fits *both* muzzle velocity and ballistic coefficient against the real trajectory solver. When the observation set is too short/closely-spaced to separate the two, it refuses the joint fit, trues velocity only, and says so — no false-precision BC. See [CLI_USAGE.md](CLI_USAGE.md#joint-mv--bc-calibration-multiple-observed-impacts) for details.
378
+
379
+ ## Advanced Features
380
+
381
+ ### Online Mode (API Integration)
382
+
383
+ The CLI can query a remote ballistics API server instead of calculating locally. This enables access to enhanced BC data, ML-augmented predictions, and doppler-derived drag curves.
384
+
385
+ > **Important:** The `--online` feature connects to a **proprietary cloud service** that is not covered by the MIT license. When using `--online`, trajectory parameters and your IP address are transmitted to our servers. See [ONLINE_SERVICE.md](ONLINE_SERVICE.md) for full terms, privacy policy, and data handling practices.
386
+
387
+ ```bash
388
+ # Use online mode to query the API
389
+ ./ballistics trajectory \
390
+ -v 2700 -b 0.475 -m 168 -d 0.308 \
391
+ --online \
392
+ --max-range 1000
393
+
394
+ # Custom API endpoint
395
+ ./ballistics trajectory \
396
+ -v 2700 -b 0.475 -m 168 -d 0.308 \
397
+ --online \
398
+ --api-url https://your-api.example.com/v1/calculate \
399
+ --max-range 1000
400
+ ```
401
+
402
+ **Default API**: `https://api.ballistics.7.62x51mm.sh/v1/calculate`
12
403
 
13
- > This package is built from `pkg/` (the `wasm-pack --target bundler` output) via
14
- > `scripts/build-npm.sh` in the source repo. The same script also produces a `pkg-web/` build
15
- > (`--target web`) for use without a bundler — see "Browser without a bundler" below.
404
+ Online mode benefits:
405
+ - **Enhanced BC data** - Access to doppler-derived ballistic coefficients
406
+ - **ML predictions** - Machine learning augmented trajectory calculations
407
+ - **BC segments** - Velocity-dependent BC modeling from measured data
408
+ - **Form factor corrections** - Bullet-specific drag adjustments
16
409
 
17
- ## Install
410
+ **Data transmitted when using --online:**
411
+ - All trajectory parameters (BC, mass, velocity, wind, atmospheric conditions, etc.)
412
+ - Your IP address and client version
413
+ - Request logs retained for 30 days, then deleted
18
414
 
415
+ To use only local calculations (no network, no data transmission):
19
416
  ```bash
20
- npm install @SCOPE/ballistics-engine
417
+ cargo install ballistics-engine --no-default-features
21
418
  ```
22
419
 
23
- `@SCOPE` is a placeholder — see the source repo's `README.md` ("WASM / npm Package" section) for
24
- the real published name once one exists.
420
+ ### Integration Methods
25
421
 
26
- ## Quick start (bundler: webpack, Vite, Rollup, Parcel)
422
+ The engine supports two numerical integration methods:
27
423
 
28
- This package's `main` entry imports its `.wasm` file as a native ES module, which is how
29
- `wasm-pack --target bundler` output is meant to be consumed. It works out of the box with Vite and
30
- Rollup (`@rollup/plugin-wasm`), and with webpack once `experiments.asyncWebAssembly` (or
31
- `experiments.syncWebAssembly`) is enabled — check your bundler's WASM docs if the import fails.
424
+ - **RK45 (Dormand-Prince Adaptive)** - Default method, provides best accuracy with adaptive step sizing
425
+ - **RK4 (Runge-Kutta 4th Order Fixed-Step)** - Available with `--use-rk4-fixed` flag for faster computation
32
426
 
33
- ```js
34
- import { WasmBallistics } from '@SCOPE/ballistics-engine';
427
+ ### Wind Shear Modeling
35
428
 
36
- const calc = new WasmBallistics();
429
+ Model altitude-dependent wind variations:
37
430
 
38
- // The command surface mirrors the native CLI (see CLI_USAGE.md in the source repo for the full
39
- // flag reference): .308 Winchester, 168gr @ 2700 fps, zeroed at 200 yd, table out to 500 yd.
40
- const table = calc.runCommand(
41
- 'trajectory -v 2700 -b 0.475 -m 168 -d 0.308 --max-range 500 --auto-zero 200',
42
- );
43
- console.log(table);
431
+ ```bash
432
+ ./ballistics trajectory -v 2700 -b 0.475 -m 168 -d 0.308 \
433
+ --wind-speed 10 --wind-direction 90 \
434
+ --enable-wind-shear \
435
+ --max-range 1000
436
+ ```
437
+
438
+ ### Transonic Stability Analysis
439
+
440
+ Analyze projectile stability through the transonic regime:
441
+
442
+ ```bash
443
+ ./ballistics trajectory -v 3000 -b 0.475 -m 168 -d 0.308 \
444
+ --enable-pitch-damping \
445
+ --max-range 2000
44
446
  ```
45
447
 
46
- ### Custom drag tables (`loadDragTable`)
448
+ Provides warnings about transonic instability and minimum pitch damping coefficients.
47
449
 
48
- Supply a measured or manufacturer-published Mach:Cd drag curve (Hornady CDM data, a Lapua/Doppler
49
- deck, or your own) instead of a G1/G7 model + BC. Once loaded it's applied automatically to every
50
- `trajectory`, `zero`, `lead`, and `monte-carlo` run — no extra flag needed.
450
+ ### Trajectory Sampling
51
451
 
52
- ```js
53
- const csv = 'mach,cd\n0.5,0.220\n0.8,0.230\n1.0,0.520\n1.2,0.480\n1.5,0.400\n2.0,0.330\n2.5,0.300\n';
54
- calc.loadDragTable(new TextEncoder().encode(csv));
55
- calc.hasDragTable(); // true
452
+ Collect trajectory data at regular intervals for detailed analysis:
56
453
 
57
- calc.runCommand('trajectory -v 2700 -b 0.475 -m 168 -d 0.308 --max-range 500');
454
+ ```bash
455
+ ./ballistics trajectory -v 2700 -b 0.475 -m 168 -d 0.308 \
456
+ --sample-trajectory \
457
+ --sample-interval 25 # Sample every 25 meters
458
+ --max-range 1000 -o json
58
459
  ```
59
460
 
60
- `loadDragTable` takes raw bytes because WASM has no filesystem access — fetch the CSV yourself
61
- (`fetch()` in the browser, `fs.readFileSync` in Node) and pass the bytes in. The CSV format is
62
- documented in the source repo's `CLI_USAGE.md` ("Custom Drag Tables"); a matching
63
- `loadBc5dTable(bytes)` / `hasBc5dTable()` pair exists for BC5D correction tables.
461
+ ### Angular Motion Physics
64
462
 
65
- ## Browser without a bundler
463
+ Model precession and nutation of spinning projectiles:
66
464
 
67
- For a plain `<script type="module">` page (no build step), use the `pkg-web/` build instead —
68
- produced by the same `scripts/build-npm.sh`, and the same build already deployed at
69
- [ballistics.sh](https://ballistics.sh). It ships an explicit async `init()` you call once before
70
- constructing `WasmBallistics`:
465
+ ```bash
466
+ ./ballistics trajectory -v 2700 -b 0.475 -m 168 -d 0.308 \
467
+ --twist-rate 10 \
468
+ --enable-precession \
469
+ --max-range 1000
470
+ ```
71
471
 
72
- ```html
73
- <script type="module">
74
- import init, { WasmBallistics } from './ballistics_engine.js';
472
+ ### Complete Advanced Physics Example
75
473
 
76
- await init(); // fetches and instantiates ballistics_engine_bg.wasm relative to this file
77
- const calc = new WasmBallistics();
78
- console.log(calc.runCommand('trajectory -v 2700 -b 0.475 -m 168 -d 0.308 --max-range 500'));
79
- </script>
474
+ ```bash
475
+ ./ballistics trajectory \
476
+ -v 2850 -b 0.690 -m 230 -d 0.338 \
477
+ --drag-model g7 \
478
+ --twist-rate 8.5 --twist-right \
479
+ --enable-magnus \
480
+ --enable-coriolis \
481
+ --enable-spin-drift \
482
+ --enable-wind-shear \
483
+ --enable-pitch-damping \
484
+ --enable-precession \
485
+ --sample-trajectory \
486
+ --latitude 38.5 \
487
+ --shooting-angle 45 \
488
+ --wind-speed 15 --wind-direction 270 \
489
+ --altitude 6000 \
490
+ --max-range 2000
80
491
  ```
81
492
 
82
- Serve `ballistics_engine.js` and `ballistics_engine_bg.wasm` from the same directory, and make sure
83
- your host serves `.wasm` with `Content-Type: application/wasm` (all major static hosts and CDNs do
84
- this by default).
493
+ ## Physics Modeling
494
+
495
+ The ballistics engine implements comprehensive physics modeling for accurate trajectory prediction:
496
+
497
+ ### Aerodynamic Effects
498
+ - **Drag Modeling** - Multiple drag functions (G1-G8, JBM, custom curves) with transonic flow corrections
499
+ - **Form Factor** - Projectile efficiency corrections based on shape and design
500
+ - **Reynolds Number Effects** - Reynolds diagnostics and an opt-in helper for genuinely low-Re flow; standard drag tables are not multiplied by an extra correction
85
501
 
86
- This `pkg-web/` build is not published to npm as part of this package in the current release —
87
- it's built locally alongside `pkg/` for direct use or self-hosting. If you need it from npm, either
88
- vendor the files from `pkg-web/` yourself or publish it as a second package.
502
+ ### Gyroscopic Effects
503
+ - **Spin Drift** - Lateral deviation due to gyroscopic and Magnus effects
504
+ - **Precession** - Gyroscopic precession of spinning projectile
505
+ - **Nutation** - Oscillatory motion superimposed on precession
506
+ - **Spin Decay** - Reduction in spin rate over time due to aerodynamic damping
507
+ - **Pitch Damping** - Aerodynamic moments opposing angular motion
89
508
 
90
- ### Node.js without a bundler
509
+ ### Environmental Effects
510
+ - **Coriolis Effect** - Earth's rotation influence on long-range trajectories
511
+ - **Magnus Effect** - Force from spinning projectile in crossflow
512
+ - **Wind Shear** - Altitude-dependent wind variations
513
+ - **Atmospheric Stratification** - Density and sound speed variations with altitude
91
514
 
92
- Plain Node `import`/`require` cannot load this package's bundler-target `.wasm` import directly.
93
- Use the `pkg-web/` build instead, passing the file bytes explicitly (Node's `fetch()` does not
94
- support `file://` URLs):
515
+ ### Stability Modeling
516
+ - **Dynamic Stability** - Gyroscopic and aerodynamic stability calculations
517
+ - **Yaw of Repose** - Gravity/gyroscopic equilibrium yaw; crosswind yaw is a transient handled by aerodynamic jump
518
+ - **Limit Cycle Yaw** - Bounded oscillatory motion analysis
95
519
 
96
- ```js
97
- import { readFileSync } from 'node:fs';
98
- import init, { WasmBallistics } from './pkg-web/ballistics_engine.js';
520
+ ## Language Bindings
99
521
 
100
- const wasmBytes = readFileSync(new URL('./pkg-web/ballistics_engine_bg.wasm', import.meta.url));
101
- await init({ module_or_path: wasmBytes });
522
+ Official language bindings are maintained as separate projects:
102
523
 
103
- const calc = new WasmBallistics();
104
- console.log(calc.runCommand('trajectory -v 2700 -b 0.475 -m 168 -d 0.308 --max-range 500'));
524
+ - **Python**: [ballistics-engine-py](https://github.com/ajokela/ballistics-engine-py) - PyO3 bindings via maturin
525
+ - **Ruby**: [ballistics-engine-rb](https://github.com/ajokela/ballistics-engine-rb) - Magnus bindings via rb_sys
526
+
527
+ These bindings depend on the `ballistics-engine` crate published on [crates.io](https://crates.io/crates/ballistics-engine).
528
+
529
+ ### WASM / npm Package
530
+
531
+ The engine also compiles to WebAssembly (`src/wasm.rs`, `wasm-bindgen`) and already powers
532
+ [ballistics.sh](https://ballistics.sh) and [ballistics.rs](https://ballistics.rs) in the browser.
533
+ It is not yet published to npm for third-party use — `scripts/build-npm.sh` builds and prepares a
534
+ publish-ready package; publishing itself is a manual step (see below).
535
+
536
+ ```bash
537
+ scripts/build-npm.sh
538
+ ```
539
+
540
+ This builds two `wasm-bindgen` targets, both with `--no-default-features` (the default
541
+ `pdf`/`online` features pull in `printpdf`/`ureq`+`ring`, which do not compile for
542
+ `wasm32-unknown-unknown` — see "Updating the WASM Module" in `CLAUDE.md`):
543
+
544
+ - **`pkg/`** — `--target bundler`, the package meant for `npm publish`. Consumed via a native
545
+ `.wasm` ES import by bundlers that understand it (webpack with `experiments.asyncWebAssembly`,
546
+ Vite, Rollup + `@rollup/plugin-wasm`, Parcel).
547
+ - **`pkg-web/`** — `--target web`, a no-bundler build for direct `<script type="module">` browser
548
+ use or manual Node usage without a bundler — the same `--target` already used to build
549
+ ballistics.sh/ballistics.rs's WASM. Documented and built for completeness; not published under
550
+ the primary package name in this initial pass.
551
+
552
+ `wasm-pack` has no built-in dual-target/"publish both" mode, and stitching bundler- and web-target
553
+ output into one package.json via manual `exports` conditions isn't something `wasm-pack` generates
554
+ or tests for you — see the comment header of `scripts/build-npm.sh` for the full reasoning. A
555
+ single bundler-target package as the published npm artifact, with the web build documented
556
+ separately, is the ecosystem-standard shape for `wasm-bindgen` crates on npm.
557
+
558
+ The script also post-processes each `package.json` (name, description, license, repository,
559
+ keywords, and the `files` list — including an `LICENSE-APACHE` entry `wasm-pack` itself omits even
560
+ though it copies the file) and installs `README-npm.md` as the package's `README.md`.
561
+
562
+ **Before publishing**, edit `pkg/package.json`'s `"name"` — it ships as the placeholder
563
+ `"@SCOPE/ballistics-engine"`. Replace `SCOPE` with the maintainer's real npm org/user scope (a
564
+ scope decision, plus an npm account with publish rights to it, are both needed and don't exist yet
565
+ as of this writing). Then:
566
+
567
+ ```bash
568
+ scripts/build-npm.sh
569
+ cd pkg
570
+ npm pack --dry-run # sanity-check the tarball contents first
571
+ npm publish --access public
105
572
  ```
106
573
 
107
- If you need a CommonJS (`require()`) Node build, generate one yourself:
108
- `wasm-pack build --target nodejs --no-default-features`.
574
+ (`--access public` is required the first time a scoped package is published, since scoped packages
575
+ default to private on free npm accounts; `pkg/package.json` also sets `publishConfig.access` to
576
+ `public` so a plain `npm publish` works too.)
577
+
578
+ ## FFI Layer
579
+
580
+ 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.
581
+
582
+ <img src="ios.png" alt="iOS Integration Example" width="35%">
583
+
584
+ ### FFI Features
585
+ - **C-Compatible Structures** - All data structures use C-compatible layouts
586
+ - **Safe Memory Management** - Proper handling of memory across language boundaries
587
+ - **iOS/Swift Integration** - Ready for use with Swift through bridging headers
588
+ - **Android/JNI Support** - Compatible with Java Native Interface
589
+ - **Monte Carlo Simulation** - Statistical analysis with parameter variations
590
+ - **Error Handling** - Graceful error propagation across FFI boundary
591
+
592
+ ### Example FFI Usage (C/Swift)
593
+ ```c
594
+ // Create input parameters
595
+ FFIBallisticInputs inputs = {
596
+ .muzzle_velocity = 823.0, // m/s
597
+ .ballistic_coefficient = 0.475,
598
+ .mass = 0.0109, // kg
599
+ .diameter = 0.00782, // meters
600
+ .drag_model = 0, // G1
601
+ .sight_height = 0.05, // meters
602
+ .temperature = 15.0, // Celsius
603
+ .altitude = 0.0
604
+ };
605
+
606
+ // Calculate trajectory. The final argument is the integration step in milliseconds
607
+ // (minimum 0.1 ms; smaller or non-finite values return NULL).
608
+ FFITrajectoryResult* result = ballistics_calculate_trajectory(&inputs, NULL, NULL, 1000.0, 0.1);
609
+
610
+ // NULL also reports invalid inputs or the 250,000-point resource ceiling.
611
+ // Increase the step, reduce the range, or use adaptive RK45 for an over-budget solve.
612
+ if (result != NULL) {
613
+ printf("Max range: %.2f meters\n", result->max_range);
614
+ ballistics_free_trajectory_result(result);
615
+ }
616
+ ```
617
+
618
+ ### Monte Carlo Simulation via FFI
619
+ ```c
620
+ // Set up Monte Carlo parameters
621
+ FFIMonteCarloParams params = {
622
+ .num_simulations = 1000,
623
+ .velocity_std_dev = 10.0, // m/s variation
624
+ .angle_std_dev = 0.001, // radian variation (elevation)
625
+ .bc_std_dev = 0.01, // BC variation
626
+ .wind_speed_std_dev = 2.0, // m/s wind variation
627
+ .target_distance = 600.0, // Target at 600m
628
+ .azimuth_std_dev = 0.001 // radian variation (horizontal)
629
+ };
630
+
631
+ // Run simulation with an independent 0.1-radian wind-direction sigma.
632
+ // Use ballistics_monte_carlo(...) when no direction variation is desired.
633
+ FFIMonteCarloResults* results =
634
+ ballistics_monte_carlo_with_direction_std_dev(&inputs, NULL, &params, 0.1);
635
+
636
+ // Use statistical results
637
+ printf("Mean range: %.2f m (σ=%.2f)\n", results->mean_range, results->std_dev_range);
638
+ printf("Hit probability at 600m: %.1f%%\n", results->hit_probability * 100);
639
+
640
+ // Access individual shots
641
+ for (int i = 0; i < results->num_results; i++) {
642
+ printf("Shot %d: Range %.2f m, Impact velocity %.2f m/s\n",
643
+ i, results->ranges[i], results->impact_velocities[i]);
644
+ }
645
+
646
+ // Clean up
647
+ ballistics_free_monte_carlo_results(results);
648
+ ```
109
649
 
110
- ## Caveats
650
+ ## Output Formats
111
651
 
112
- - **Size**: the `.wasm` binary is roughly 430 KB (about 175 KB gzipped). It is not code-split or
113
- lazily loaded — the whole engine loads up front.
114
- - **Single-threaded**: no SIMD/threads assumptions; no `SharedArrayBuffer` or
115
- cross-origin-isolation (COOP/COEP) headers required.
116
- - **No filesystem/network**: table loaders (`loadDragTable`, `loadBc5dTable`) and any file-based
117
- CLI flags (e.g. native `--drag-table <FILE>`) need the host to fetch bytes and hand them in; see
118
- `loadDragTable` above.
119
- - **`pdf`/`online` features are unavailable**: this build excludes them (see above), so
120
- PDF dope-card export and the online BC-estimation API are not part of the WASM surface.
121
- - Full API surface (including the `Calculator` builder class) is documented in the bundled
122
- `.d.ts`; the full CLI flag reference `runCommand` accepts is documented in the source repo's
123
- `CLI_USAGE.md`.
652
+ All commands support three output formats via the `-o` flag:
653
+
654
+ - **table** (default) - Formatted ASCII table for terminal display
655
+ - **json** - Complete data in JSON format for programmatic use
656
+ - **csv** - Comma-separated values for spreadsheet analysis
657
+
658
+ ## Practical Examples
659
+
660
+ ### Hunting Zero
661
+
662
+ Zero a hunting rifle at 200 yards with environmental conditions:
663
+
664
+ ```bash
665
+ # Calculate zero angle
666
+ ./ballistics zero \
667
+ -v 2650 -b 0.460 -m 180 -d 0.308 \
668
+ --target-distance 200
669
+
670
+ # Verify trajectory with auto-zero
671
+ ./ballistics trajectory \
672
+ -v 2650 -b 0.460 -m 180 -d 0.308 \
673
+ --auto-zero 200 \
674
+ --max-range 400 \
675
+ --wind-speed 15 \
676
+ --wind-direction 270 \
677
+ --temperature 32 \
678
+ --humidity 30 \
679
+ --altitude 5000 \
680
+ --full
681
+ ```
682
+
683
+ ### Long Range Shooting
684
+
685
+ Analyze trajectory for 1000-yard shot:
686
+
687
+ ```bash
688
+ ./ballistics trajectory \
689
+ -v 2850 -b 0.690 -m 230 -d 0.338 \
690
+ --drag-model g7 \
691
+ --auto-zero 100 \
692
+ --max-range 1100 \
693
+ --wind-speed 10 \
694
+ --wind-direction 45 \
695
+ --full \
696
+ -o json > trajectory.json
697
+ ```
698
+
699
+ ### Load Development
700
+
701
+ Compare different loads using Monte Carlo:
702
+
703
+ ```bash
704
+ # Load 1: Higher velocity, more variation
705
+ ./ballistics monte-carlo \
706
+ -v 2750 -b 0.475 -m 168 -d 0.308 \
707
+ -n 1000 \
708
+ --velocity-std 15 \
709
+ --target-distance 600
710
+
711
+ # Load 2: Lower velocity, more consistent
712
+ ./ballistics monte-carlo \
713
+ -v 2680 -b 0.475 -m 168 -d 0.308 \
714
+ -n 1000 \
715
+ --velocity-std 8 \
716
+ --target-distance 600
717
+ ```
718
+
719
+ ## Advanced Features
720
+
721
+ ### BC Segmentation
722
+
723
+ Velocity-dependent BC modeling accounts for how ballistic coefficient changes as the bullet slows down. Enable with `--use-bc-segments`:
724
+
725
+ - Automatically estimates BC segments based on bullet characteristics
726
+ - No external data required - uses caliber, weight, and BC
727
+ - Identifies bullet type (Match, Hunting, VLD, etc.) from parameters
728
+ - Applies physics-based BC degradation curves
729
+
730
+ Example:
731
+ ```bash
732
+ ./ballistics trajectory -v 2700 -b 0.475 -m 168 -d 0.308 --use-bc-segments --max-range 1000
733
+ ```
734
+
735
+ **Manual velocity-keyed BC segments** — supply your own `VMIN:VMAX:BC` pairs (repeatable,
736
+ velocities in `--units`) instead of the auto-estimated/table ones. Keyed to velocity, so it
737
+ composes with distance-keyed `--wind-segment`; implies `--use-bc-segments` and overrides
738
+ `--bc-table` and `--bc-table-dir`:
739
+ ```bash
740
+ ./ballistics trajectory -v 2600 -b 0.243 -m 175 -d 0.308 --drag-model g7 --max-range 1000 \
741
+ --bc-segment 1800:4000:0.243 --bc-segment 1500:1800:0.228 --bc-segment 1200:1500:0.205
742
+ ```
743
+
744
+ ### BC5D Correction Tables
745
+
746
+ BC5D tables provide ML-derived, 5-dimensional BC corrections indexed by weight, BC, muzzle velocity, current velocity, and drag model. Tables are caliber-specific and capture the complete velocity-dependent behavior.
747
+
748
+ **Auto-Download Mode** (requires `online` feature):
749
+ ```bash
750
+ # Downloads tables automatically on first use
751
+ ./ballistics trajectory -v 2700 -b 0.475 -m 168 -d 0.308 --bc-table-auto
752
+
753
+ # Force refresh cached tables
754
+ ./ballistics trajectory -v 2700 -b 0.475 -m 168 -d 0.308 --bc-table-auto --bc-table-refresh
755
+ ```
756
+
757
+ **Offline Mode** with pre-downloaded tables:
758
+ ```bash
759
+ ./ballistics trajectory -v 2700 -b 0.475 -m 168 -d 0.308 --bc-table-dir ./bc_tables/
760
+ ```
761
+
762
+ **Available calibers:** .224, .243, .264, .277, .284, .308, .338
763
+
764
+ **Cache locations:**
765
+ - macOS: `~/Library/Caches/ballistics-engine/bc5d/`
766
+ - Linux: `~/.cache/ballistics-engine/bc5d/`
767
+ - Windows: `%LOCALAPPDATA%\ballistics-engine\cache\bc5d\`
768
+
769
+ Tables are approximately 1-1.5 MB each and include CRC32 validation to ensure data integrity.
770
+
771
+ ### Advanced Physics Modeling
772
+
773
+ When enabled, the engine calculates:
774
+ - **Magnus Effect** - Side force from spinning projectiles
775
+ - **Spin Drift** - Lateral drift due to gyroscopic effects
776
+ - **Coriolis Effect** - Earth rotation effects (with latitude input)
777
+ - **Transonic Drag** - Enhanced drag modeling in transonic regime
778
+ - **Low-Reynolds Helper** - Opt-in viscous correction below the standard projectile-table regime
779
+
780
+ ## Building from Source
781
+
782
+ ### Requirements
783
+
784
+ - Rust 1.70 or later
785
+ - Cargo build system
786
+
787
+ ### Build Commands
788
+
789
+ ```bash
790
+ # Debug build
791
+ cargo build
792
+
793
+ # Release build (optimized)
794
+ cargo build --release
795
+
796
+ # Run tests
797
+ cargo test
798
+
799
+ # Build documentation
800
+ cargo doc --open
801
+ ```
802
+
803
+ ## Library Usage
804
+
805
+ Use as a Rust library in your own projects:
806
+
807
+ ```rust
808
+ use ballistics_engine::{
809
+ BallisticInputs, TrajectorySolver,
810
+ WindConditions, AtmosphericConditions
811
+ };
812
+
813
+ fn main() -> Result<(), Box<dyn std::error::Error>> {
814
+ let inputs = BallisticInputs {
815
+ muzzle_velocity: 823.0, // m/s
816
+ launch_angle: 0.0, // radians
817
+ ballistic_coefficient: 0.475,
818
+ mass: 0.0109, // kg
819
+ diameter: 0.00782, // meters
820
+ sight_height: 0.05, // meters
821
+ ..Default::default()
822
+ };
823
+
824
+ let wind = WindConditions {
825
+ speed: 5.0, // m/s
826
+ direction: 1.5708, // 90 degrees in radians
827
+ ..Default::default()
828
+ };
829
+
830
+ let atmosphere = AtmosphericConditions {
831
+ temperature: 15.0, // Celsius
832
+ pressure: 1013.25, // hPa
833
+ humidity: 50.0, // %
834
+ altitude: 0.0, // meters
835
+ ..Default::default()
836
+ };
837
+
838
+ let solver = TrajectorySolver::new(inputs, wind, atmosphere);
839
+ let result = solver.solve()?;
840
+
841
+ println!("Max range: {:.2} m", result.max_range);
842
+ println!("Max height: {:.2} m", result.max_height);
843
+ println!("Time of flight: {:.3} s", result.time_of_flight);
844
+
845
+ Ok(())
846
+ }
847
+ ```
848
+
849
+ ## Performance
850
+
851
+ Optimized Rust implementation provides:
852
+ - Single trajectory (1000m): ~5ms
853
+ - Monte Carlo (1000 runs): ~500ms
854
+ - BC estimation: ~50ms
855
+ - Zero calculation: ~10ms
856
+
857
+ ## Common Ballistic Coefficients
858
+
859
+ | Caliber | Weight | BC (G1) | BC (G7) | Description |
860
+ |---------|--------|---------|---------|-------------|
861
+ | .223 | 55gr | 0.250 | - | FMJ |
862
+ | .223 | 77gr | 0.362 | 0.182 | Match |
863
+ | .308 | 168gr | 0.475 | 0.224 | Match |
864
+ | .308 | 175gr | 0.505 | 0.253 | Match |
865
+ | .308 | 180gr | 0.480 | - | Hunting |
866
+ | .338 | 300gr | 0.768 | 0.383 | Match |
867
+ | 6.5mm | 140gr | 0.620 | 0.310 | Match |
868
+ | .50 | 750gr | 1.050 | 0.520 | Match |
869
+
870
+ ## Troubleshooting
871
+
872
+ ### Trajectory hits ground early
873
+ - Check if you're using `--auto-zero` or setting `--angle` manually
874
+ - Default angle is 0° (horizontal), which will hit ground quickly
875
+ - Use `--auto-zero <distance>` to automatically calculate proper angle
876
+
877
+ ### Units confusion
878
+ - Default is Imperial (fps, grains, yards)
879
+ - Use `--units metric` for metric system
880
+ - All inputs must match the selected unit system
881
+
882
+ ### Unexpected BC behavior
883
+ - G1 and G7 models have different BC values for same bullet
884
+ - G7 typically better for boat-tail bullets
885
+ - BC segmentation automatically applied based on bullet type
886
+
887
+ ## Contributing
888
+
889
+ Contributions are welcome! Please:
890
+ 1. Fork the repository
891
+ 2. Create a feature branch
892
+ 3. Add tests for new features
893
+ 4. Run `cargo test` and `cargo fmt`
894
+ 5. Submit a pull request
124
895
 
125
896
  ## License
126
897
 
127
- MIT OR Apache-2.0 see `LICENSE` and `LICENSE-APACHE` in this package.
898
+ This project is licensed under the MIT License - see LICENSE file for details.
899
+
900
+ **Note:** The MIT license applies to the open source ballistics-engine library, CLI, and FFI bindings. The `--online` feature connects to a proprietary cloud service with separate terms. See [ONLINE_SERVICE.md](ONLINE_SERVICE.md) for details.
901
+
902
+ ## Acknowledgments
903
+
904
+ - Ballistics physics based on Robert McCoy's "Modern Exterior Ballistics"
905
+ - Drag tables from military ballistics research
906
+ - BC segmentation algorithms from Bryan Litz's research
907
+ - Community contributions and testing
128
908
 
129
- ## Source
909
+ ## Support
130
910
 
131
- https://github.com/ajokela/ballistics-engine
911
+ For issues, questions, or contributions:
912
+ - GitHub Issues: [github.com/ajokela/ballistics-engine/issues](https://github.com/ajokela/ballistics-engine/issues)