Module atmosphere_service

Module atmosphere_service 

Source
Expand description

Describe an atmosphere the way a solve resolves it: the service behind the atmosphere.density_altitude bridge command.

§Why this exists

Density altitude was reachable only as one field inside a DOPE card, so an embedding app wanting to show it had to reimplement the formula — and an external Android app did, then hand-maintained the copy with its own regression tests (2026-09). A copy of a formula is a copy that drifts. This service answers the question directly, from the same code the solver and the card use.

§One atmosphere schema, decoded and resolved one way

The request carries the SAME atmosphere object a solve-json v1 request does (AtmosphereV1). It is decoded by the solve’s own shape validator and resolved by the solve’s own resolver (solve_v1::resolve_atmosphere), so every error the solve would give for that object comes back identically — same code, same $.atmosphere.* path — as do the ICAO-standard defaults for whatever is omitted (each announced in assumptions) and the QNH-to-station reduction. An app can send the atmosphere it is about to solve with and get the density altitude of precisely that air.

§Then checks the solve does not make (yet — MBA-1594)

The solve’s resolver only requires temperature and pressure to be positive, which admits air that cannot exist: 5 K, a humidity whose vapor pressure exceeds the air’s total pressure, a pressure altitude of 30 km. Those are also exactly what the two likeliest unit mistakes produce — °C sent as temperature_k, hPa sent as pressure_pa — so a figure computed from them is a wrong answer delivered confidently. This service refuses them instead, with the same invalid_value envelope and the path of the input to blame:

  • temperature_k outside 173.15–373.15 K (−100 °C to 100 °C), where the engine’s saturation-vapor and compressibility formulas are defined;
  • station pressure whose pressure altitude is outside −5 km to 11 km — the standard atmosphere’s troposphere, which is all the closed-form arithmetic here covers;
  • a relative humidity that would need more vapor pressure than the total pressure;
  • either density altitude outside −5 km to 11 km.

None of these rejects a condition anyone has fired a rifle in.

§Two density altitudes, by name

“Density altitude” means two different things in the field, and one number under one name is how the confusion this module answers arose. The response therefore never offers a bare density_altitude; it offers both, and a caller has to choose:

  • faa_rule — crate::atmosphere::faa_rule_density_altitude_ft: NWS pressure altitude plus the FAA’s “120 ft per °C” rule of thumb. Humidity-free. This is the formula the engine’s DOPE card header uses, and the exact inverse of the density-altitude ENTRY mode.
  • density_matched — crate::atmosphere::density_matched_altitude_m: the ISA altitude whose standard density equals this air’s actual density, humidity included. The textbook definition, and what humidity-aware tools report — the National Weather Service’s own density-altitude calculator included.

They coincide at ICAO standard sea-level dry air; anywhere else a match is coincidence. They differ by 33.1 m at 15 °C, 1013.25 hPa and 50% RH (humidity), and by about 22 m at 30 °C in perfectly DRY air (the FAA rule’s straight-line approximation). The difference between them is therefore NOT a humidity correction, and must not be presented as one.

air_density_kg_m3 is reported as well: the CIPM-2007 density the solver itself uses at the muzzle, which answers “how thin is the air” with no definition to argue about.

Structs§

AltitudeV1
One altitude in both units a shooter meets it in. Density altitude is conventionally quoted in feet even by shooters who range in meters, so both travel together rather than asking every caller to convert.
DensityAltitudeRequestV1
atmosphere.density_altitude request.
DensityAltitudeResponseV1
atmosphere.density_altitude result.
DensityAltitudesV1
The two density altitudes, each under its own name. See the module docs for why there is no single density_altitude value.

Functions§

decode_density_altitude_request_v1
Decode an atmosphere.density_altitude request from JSON the way solve decodes its atmosphere: the solve’s own shape validator runs first, so an unknown or unit-suffixed field, an explicit null, a string where a number belongs or an unrecognized pressure_reference is refused with the solve’s code at the solve’s $.atmosphere.* path.
density_altitude_v1
Resolve request.atmosphere exactly as a solve would, check that the air it describes can exist and lies where these formulas hold, then report its pressure altitude, both density altitudes and its air density.