|
lob 0.11.0
Exterior ballistics library — API + technical reference
|
This page is the worked case study for a recent numerical feature: allowing multiple BC + velocity pairs to tailor the standard drag curve. It follows the implementation in source/splines.hpp, source/lob_builder.cpp and is validated by test/source/lob_builder_test.cpp, test/source/splines_test.cpp.
A single ballistic coefficient assumes the bullets form factor relative to the standard projectile is constant with Mach. Real bullets violate this — the shock structure and base flow change with Mach — so a single BC is a compromise. A manufacturer might quote:
Using any one of those numbers for the whole flight either over- or under- predicts drag outside its band. The transformation lets the caller supply all of them and have lob produce a single smooth Cd(M) that is Cd_std(M)/BC(M) at the band Machs and PCHIP-interpolated between them.
The reference is a standard curve, e.g. G7, sampled at 87 Mach points and projected onto 16 PCHIP knots (source/splines.hpp, source/tables.hpp). Call Cd_std(M) the spline evaluation of that curve (Cubic Hermite Splines). For the figure below the reference is G1 (the most familiar shape) with BC=1 so Cd_ref = Cd_std; the same machinery applies to any G* curve.
The caller supplies N pairs (fps_i, BC_i) via LobBuilderBCVelocityBands (include/lob/lob.h) / Builder::BCVelocityBands (include/lob/lob.hpp):
fps_i is converted to Mach M_i = fps_i / c where c = ctx.speed_of_sound (~1116 fps at ISA sea level). BC_i is multiplied by the atmosphere factor:
so the curve is always ICAO-normalized (source/lob_builder.cpp).
The bands example used in tests and in the figure satisfies all constraints; the maximum band Mach 3000/1116 ≈ 2.69 < 5.0 and N=3.
Two splines are built:
Scaling (retardation) spline S(M) = 1/BC(M) via MakeRetardationCoefs (source/splines.hpp):
Flat padding implements “constant extrapolation outside the user Mach range” — below the slowest band and above the fastest band BC is held constant at the edge value.
Cd_std(M) — the selected G* curve resampled onto the same knots.They are merged via Merge (source/splines.hpp):
The merged coefficients are the Hermite data (Cd, dCd/dM) at each knot, so the solver's CurveView::Eval returns the product directly with correct derivatives.
For a constant BC the inputs are collinear, S(M)=1/BC is flat, and Merge degenerates to Cd_std/BC — numerically equivalent to the single-BC path within 1e-4. The constant-BC tests verify 1e-4 agreement.
Math summary:
S(M) is a true PCHIP (Fritsch–Carlson) interpolant, not linear:
S(M_i)=1/BC_i.0 — this preserves monotonicity and prevents the overshoot that a natural cubic spline would produce.S is constant (the padding), so drag does not diverge.Between bands the scaled drag varies smoothly; the test BCVelocityBandsNonConstantClampingAndInterpolation (test/source/lob_builder_test.cpp) checks the three regimes:
M < smallest band → clamped to first BC (Eval(1500/c) matches /0.20);M between bands → strictly between the two bracketing scaled drags and matches the explicit retardation-curve product to 2e-3;M > largest band → clamped to last BC (Eval(3500/c) matches /0.40).For a visual and a copy-pasteable trajectory see BC Bands Worked Example. That page reuses the same 0.20@2000, 0.30@2500, 0.40@3000 fps bands and the same c=1116 fps figure (static docs/figures/bc_transformation.svg from source/tables.hpp via the same PCHIP as the library).
N < 2 → kLobErrorBcBandsTooShort; N > 16 → kLobErrorBcBandsInvalid.fps not strictly increasing → kLobErrorBcBandsNotMonotonic.fps ≤ 0 or any BC ≤ 0 or non-finite → kLobErrorBcBandsInvalid.fps/c ≥ 5.0 → kLobErrorBcBandsInvalid (would require Mach ≥5 evaluation where the reference tables end).MachVsDragTable followed by BCVelocityBands (or vice versa) discards the earlier one (include/lob/lob.h).MachVsDragTable and BCVelocityBands are mutually exclusive for the same build.BCAtmosphere applies to all bands uniformly.