Expand description
Mach-keyed drop-scale-factor (DSF) truing table (MBA-1357).
Applied Ballistics’ published two-stage truing workflow calibrates muzzle velocity (MV) first — that fixes the supersonic drag curve against a chronograph/observed-drop comparison at Mach >= 1.2. Below that, as the bullet moves through the transonic region and into the subsonic regime, no single MV correction can fix drop discrepancies that grow with range: the residual is a slowly-varying function of Mach, not a constant offset. AB’s second stage records a handful of observed drop / predicted drop ratios at specific (subsonic-or-transonic) Mach numbers and uses them to scale predicted drop at nearby Mach numbers on later solves.
This module is a cleanroom reimplementation of that workflow’s shape, not a
bit-for-bit copy of AB’s unpublished interpolation — Kestrel/AB do not publish their
exact curve. The design decision unique to this implementation is the anchor:
the table’s Mach domain is (0, 1.2) (points at/above Mach 1.2 belong to MV truing,
not here — DsfTable::from_points rejects them), and every table implicitly
continues with a DSF of 1.0 at Mach 1.2 — the exact boundary where MV calibration
takes over. That implicit anchor point (1.2, 1.0) is never stored in
DsfTable::points; it exists only inside DsfTable::factor_at’s interpolation so
the transition from “supersonic, MV-trued, unscaled” to “transonic/subsonic,
DSF-scaled” is continuous — a shot solved at Mach 1.1999 and one solved at Mach 1.2001
get (to floating-point precision) the same drop. This is a functional-equivalence
choice made for this engine, not a replication of AB’s internal method.
Below the lowest recorded point, DsfTable::factor_at flat-clamps to that point’s
DSF — there is no data past it, and AB’s guidance is that further subsonic drop
continues to track the last-calibrated regime rather than drift back toward identity.
apply_dsf is a drop-only post-processing step over an already-solved
crate::TrajectoryResult: it rescales each point’s vertical position relative to
the line of sight by the DSF at that point’s Mach, and touches nothing else —
velocity, kinetic energy, time, and downrange/windage position are byte-identical
before and after. Per-point Mach is computed the same way the solver’s own
diagnostics compute it (see apply_dsf’s doc comment for the exact fields), NOT
from a re-derived per-altitude local speed of sound the engine does not store per
point.
No feature gate: this module must compile for wasm32-unknown-unknown. It is
fs-free (profile persistence of a table’s points is the caller’s job, e.g.
main.rs’s saved-profile handling in a later task).
Structs§
- DsfPoint
- One observed drop-scale-factor keyed to the Mach number it was recorded at.
- DsfSolve
Inputs - Full input set for
solve_for_dsf— the scalar-BC model (mirroringTruingModelInputsV1’s fields) plus every profile field the CLI’s historicalsolve_profile_for_dsffed into the physics thatTruingModelInputsV1alone has no slot for (MBA-1357 Task 8 review, Finding 1).Noneon anyOptionfield means exactly what it meant to the historical code when a profile didn’t carry that field — the same physically neutral default, documented per field below — so a profile that sets none of them solves byte-identically to a bare convertedTruingModelInputsV1, and one that does gets ALL of it honored, not silently dropped. - DsfTable
- A validated, Mach-sorted table of up to
DSF_MAX_POINTSDsfPoints.
Enums§
- Upsert
Outcome - What
DsfTable::upsertdid with the incoming point.
Constants§
- DSF_
ANCHOR_ VALUE - DSF value of the implicit anchor at
DSF_MACH_CEILING— identity, matching the MV-trued supersonic regime this table hands off from. - DSF_
MACH_ CEILING - Upper bound (exclusive) of the Mach domain a
DsfPointmay describe. Observations at or above this Mach belong to muzzle-velocity truing, not the DSF table; it doubles as the implicit anchor’s Mach coordinate ((DSF_MACH_CEILING, 1.0)) inDsfTable::factor_at. - DSF_MAX
- Exclusive upper bound a point’s
dsfmust clear. - DSF_
MAX_ POINTS - Maximum number of distinct points a
DsfTablemay hold. - DSF_MIN
- Exclusive lower bound a point’s
dsfmust clear. - DSF_
SUPERSEDE_ TOLERANCE_ MACH - A new point within this many Mach units of an existing one supersedes it in
DsfTable::upsertinstead of being appended.
Functions§
- apply_
dsf - Apply a DSF table to an already-solved trajectory, IN PLACE, scaling only each
point’s drop below the line of sight — in BOTH
result.pointsand, when present,result.sampled_points. - dsf_
observation_ beyond_ 90pct - Whether an observation range is beyond 90% of the trajectory’s solved max range — past this point the solution’s reliability degrades (short-range extrapolation of a trajectory that terminated, e.g., at ground impact just past the observation).
- dsf_
observation_ warrants_ 90pct_ warning - Whether the
dsfverb’s “solution reliability degrades” warning should fire. - interpolate_
position_ and_ velocity - Linearly interpolate
(position.y, velocity_magnitude)at horizontal distancetarget_dist_mfrom a solved trajectory’s points (position.x= downrange). Mirrorscli_api::fit_value_at’s interpolation (private to that module), but resolves both quantities from the same bracketing pair in one pass since thedsfverb needs drop AND Mach at the identical range.Noneif the trajectory never reachestarget_dist_m. - mach_
1_ crossing_ range_ m - The downrange distance (meters) where the trajectory’s station Mach first drops below 1.0 (the “crossed_subsonic” transition), linearly interpolated between the bracketing solved points.
- solve_
for_ dsf - Solve a
DsfSolveInputs’s own trajectory for thedsfcommand’s derivation step (MBA-1357 Task 8), given plain values directly rather than a savedProfile— the JSON bridge cannot construct aProfile, and must not read one from disk.