Module truing_dsf

Module truing_dsf 

Source
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.
DsfSolveInputs
Full input set for solve_for_dsf — the scalar-BC model (mirroring TruingModelInputsV1’s fields) plus every profile field the CLI’s historical solve_profile_for_dsf fed into the physics that TruingModelInputsV1 alone has no slot for (MBA-1357 Task 8 review, Finding 1). None on any Option field 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 converted TruingModelInputsV1, and one that does gets ALL of it honored, not silently dropped.
DsfTable
A validated, Mach-sorted table of up to DSF_MAX_POINTS DsfPoints.

Enums§

UpsertOutcome
What DsfTable::upsert did 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 DsfPoint may 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)) in DsfTable::factor_at.
DSF_MAX
Exclusive upper bound a point’s dsf must clear.
DSF_MAX_POINTS
Maximum number of distinct points a DsfTable may hold.
DSF_MIN
Exclusive lower bound a point’s dsf must clear.
DSF_SUPERSEDE_TOLERANCE_MACH
A new point within this many Mach units of an existing one supersedes it in DsfTable::upsert instead 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.points and, 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 dsf verb’s “solution reliability degrades” warning should fire.
interpolate_position_and_velocity
Linearly interpolate (position.y, velocity_magnitude) at horizontal distance target_dist_m from a solved trajectory’s points (position.x = downrange). Mirrors cli_api::fit_value_at’s interpolation (private to that module), but resolves both quantities from the same bracketing pair in one pass since the dsf verb needs drop AND Mach at the identical range. None if the trajectory never reaches target_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 the dsf command’s derivation step (MBA-1357 Task 8), given plain values directly rather than a saved Profile — the JSON bridge cannot construct a Profile, and must not read one from disk.