winds-engine
Deterministic wind profiles and radiosonde validation for trajectory
simulation — the wind data pipeline for rocket modeling, consumed by
rockets-engine. Kernel-side wind modeling (the WindField trait, profile
types, analytic profiles) is staged here pending promotion to
ballistics-engine's wind module; see
docs/winds-engine-plan.md.
use ;
// A measured sounding in the line-based interchange format. Heights are
// metres above sea level; this station's surface report is at 287 m.
let sounding = parse?;
// The profile keeps those heights, so it is read with y = height above sea
// level, the frame rockets-engine uses.
let profile = sounding.to_profile_field?;
let wind = profile.wind; // 500 m above sea level
// Or score a candidate profile against the measurement.
let report = validate;
WindFieldtrait: stateless, deterministicwind(time, position)— the same sampling pattern trajectory integrators already rely on.ProfileField(piecewise-linear, circular direction interpolation),ConstantField,LogProfile,EkmanProfile.Sounding: radiosonde interchange format (tested against a real 115-level KMPX sounding from the IEM RAOB archive). Heights are above sea level and stay that way into_profile_field(), so the profile reads directly in a frame whose y is altitude above sea level, as in rockets-engine (withBallisticForcesand in its flat-frame CLI, whose ground sits at--ground-altitude-m, sea level by default).- Datum conversion:
LogProfileandEkmanProfiletake y as height above the ground, where they are calm. This crate's staged types have no offset wrapper; ballistics-engine'sHeightOffset(from its next release, which rockets-engine's=0.48.0pin does not have) wraps only ballistics-engine's ownWindField, not this crate's. So a caller in a sea-level frame either reads them aty - ground elevationat its single call site, or builds ballistics-engine'sProfileField/LogProfile/EkmanProfilefrom the same values and wraps those inHeightOffset(offset = the height of the caller's y = 0 in the field's datum: minus the ground elevation for a log or Ekman profile, none for a sea-level sounding). Convert in one place only: sounding heights stay above sea level, and shifting them as well would move every wind by the site elevation. validate: speed/direction RMSE and per-layer errors vs a sounding.- Relationship to ballistics-engine: these types are this crate's own.
ballistics-engine's wind fields (unreleased) were adapted from them and
differ:
WindField::windtakes and returnsnalgebra::Vector3<f64>and there is noWindtype,ConstantFieldis a tuple struct over that vector,LogProfile/EkmanProfilekeep their fields private behind getters, and constructors returnWindFieldError. Moving winds-engine onto them changes this crate's public API (the example above,validate()'s signature), so it is a deliberate breaking release, not a drop-in re-export.
Zero dependencies, no network, no wall clock, no unseeded randomness. GRIB2/HRRR/GFS acquisition is the next milestone (risk item in the plan).
MIT licensed (see LICENSE).