winds-engine 0.9.1

Deterministic wind profiles and radiosonde validation for trajectory simulation
Documentation

winds-engine

Deterministic wind profiles and radiosonde validation for trajectory simulation — the wind data pipeline for rocket modeling, consumed by rockets-engine. The wind-field types themselves (the WindField trait, the profile types, the analytic profiles, HeightOffset) are ballistics-engine's wind module, re-exported here so the pipelines and their consumers share one definition; see docs/winds-engine-plan.md.

use nalgebra::Vector3;
use winds_engine::{Sounding, WindField, validate};

// 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 = Sounding::parse("287 0.0 0\n305 3.09 125\n610 3.60 145\n")?;
// 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(0.0, Vector3::new(0.0, 500.0, 0.0)); // 500 m above sea level
// Or score a candidate profile against the measurement.
let report = validate::validate(&profile, &sounding);
  • WindField trait: stateless, deterministic wind(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 in to_profile_field(), so the profile reads directly in a frame whose y is altitude above sea level, as in rockets-engine (with BallisticForces and in its flat-frame CLI, whose ground sits at --ground-altitude-m, sea level by default).

  • Datum conversion: LogProfile and EkmanProfile take y as height above the ground, where they are calm. Wrap them in HeightOffset (re-exported) to read them in another frame: the offset is the height of the caller's y = 0 in the field's datum — minus the ground elevation in a sea-level frame, nothing 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: winds-engine depends on it and re-exports its wind-field core (since 0.6.0, against ballistics-engine 0.49.0). The kernel owns gravity, drag, the ICAO atmosphere and the integrator; winds-engine owns the wind data: sounding/profile parsing, forecast ingestion and terrain flow. They meet in rockets-engine, which depends on both and samples a WindField through ballistics-engine's force kernel.

  • grib (feature): decode GRIB2 forecast files (GFS isobaric HGT/UGRD/VGRD) into a ProfileField at a point — bilinear-interpolated, offline and deterministic.

  • download (feature): fetch live forecasts — GFS via the NOAA NOMADS filter script (server-side subsetting) or HRRR's 3 km CONUS grids via byte-range requests against the AWS mirror's index files — the only networked code in the crate.

  • terrain: a diagnostic mass-consistent terrain wind. HeightGrid loads terrain from raw arrays, ASCII .asc rasters, or SRTM .hgt tiles (hgt_grid, projected into local metres); behind the tiff feature, geotiff_grid decodes a GeoTIFF DEM and reads its georeferencing (GeoTransform from the pixel-scale/tiepoint tags) so it lands with its real origin and spacing. TerrainFlowField adjusts a base field to satisfy 2-D horizontal mass conservation over terrain, constrained by surface wind Monitors (from_geo and monitors_from_geo_csv project GPS lat/lon and compass directions into the field's frame).

The default build depends on ballistics-engine (no default features) and nalgebra; grib adds the grib crate, download adds ureq. No wall clock or unseeded randomness anywhere — cycle date and forecast hour are explicit parameters.

MIT licensed (see LICENSE).