Both line-of-sight crossings a single fixed bore angle can produce (Tier 2 whole-branch
review, C2): a rifle whose sight sits above its bore generally crosses a level line of
sight TWICE on a rising shot — an ascending “near zero” close to the muzzle, and a
descending “far zero” past the apex. The classic 25/300-yard battle-zero pairing is one
bore angle producing both. TrajectorySolver’s internal find_zero_range and the public
calculate_zero_range_from_angle_* functions return both rather than silently choosing
one, because find_zero_angle’s own choice of root depends on the target distance it was
asked to solve for — the forward and inverse solvers are NOT a single-valued pair.
Shared text for the MBA-1365 “--bc-reference army-standard-metro is inert” warning —
single source of truth for every surface that can trigger it (native CLI, WASM,
BallisticInputs::bc_reference_standard_inert_warning) so the wording can’t drift.
Default hit-zone radius (meters) around the point of aim at the target plane — a 30 cm
circle. Shared by the CLI, FFI, and WASM so “hit probability” means the same thing everywhere.
Generous solve envelope for calculate_zero_range_from_angle_with_conditions /
calculate_zero_range_from_angle_with_resolved_conditions (MBA-1402): a “zero” bore angle
is a near-horizontal shot, so its far line-of-sight crossing sits well inside this even at
extended small-arms ranges. Double the engine’s own default 1000 m solve envelope
(TrajectorySolver::new) rather than something scaled to an (unknown, being solved for)
target distance; solve() still stops at ground impact well before this in the typical
case, so the larger envelope costs effectively nothing.
Solve the zero RANGE(S) that a fixed bore angle produces. Runs the trajectory at
zero_angle_rad and returns BOTH line-of-sight crossings it finds, as
ZeroCrossings — a rifle sighted above the bore generally crosses a level line of
sight twice on a rising shot (near, ascending, close to the muzzle; far, descending past
the apex).
Estimate a BC by fitting a simulated trajectory to measured data, for a chosen drag
model (G1, G7, …) and fit basis (drop or velocity). Uses a coarse 0.01 sweep over
plausible BCs followed by a 0.001 local refine around the coarse best.
Interpolate a muzzle velocity (m/s) from a measured powder-temperature curve at
temp_c (Celsius). curve is (temperature_celsius, velocity_m_s) points; it is
sorted ascending by temperature before use. Values below the first point or above
the last are CLAMPED to the endpoint velocity (no extrapolation beyond measured
data), and segments are linearly interpolated. A single point yields a constant.
Parse a --sweep START:END:STEP temperature range (display units) for the
powder command — ONE parser shared by the native CLI and WASM so their
validation cannot drift. Returns the
inclusive row temperatures. Guarded: STEP must be positive, END >= START, and the
row count is capped so a typo can’t emit an unbounded table.
Resolve the powder-temperature-adjusted muzzle velocity (m/s) — the velocity the
solver actually flies. A non-empty measured powder_temp_curve takes precedence
(interpolated at powder_curve_temp_c, falling back to the ambient
ambient_temperature_c; clamped at the endpoints) and REPLACES the nominal
velocity; otherwise, when use_powder_sensitivity is set, the linear model adds
sensitivity x (ambient - reference) to it. All inputs canonical SI (Celsius,
m/s). This is the single shared implementation behind the trajectory solve, the
powder CLI/WASM command (MBA-737), and anything else that must agree with them.