Expand description
Weather-balloon soundings from the University of Wyoming’s radiosonde archive, turned into a
SoundingProfile.
A radiosonde is an instrument package carried up by a weather balloon, usually at 00 and 12 UTC, from hundreds of stations worldwide. It measures pressure, temperature and humidity, and its drift gives the wind. The University of Wyoming serves the archive of these soundings. It is a measurement, not a forecast, but only at the station and the time of the flight, which can be a hundred kilometers and some hours from a launch.
A WyomingRequest names a station (its WMO number, such as 72364 for Santa Teresa, New
Mexico) and the sounding’s nominal hour, and asks for the comma-separated text, one row per
level, with these columns (among others) in these units:
| column | unit | read as |
|---|---|---|
time | YYYY-MM-DD HH:MM:SS UTC | the release time, from the first row |
latitude, longitude | degrees | the release point, from the first row |
pressure_hPa | hPa | pressure |
geopotential height_m | geopotential m | height above sea level |
temperature_C | °C | temperature |
relative humidity_% | % | over liquid water (the file has humidity wrt ice_% too) |
wind direction_degree, wind speed_m/s | °, m/s | the wind, the direction it blows from |
A column in any other unit is refused, not converted. Two versions of most soundings are
served (WyomingVersion): the coded message stations send (WMO FM 35, “TEMP”), with the
standard pressure levels and the significant levels between them, about 200 rows; and the
BUFR file (WMO’s binary format, as the archive decodes it), a row a second, about 6,000.
WyomingSounding::parse first finds the rows that fit. A row with a pressure, height and
temperature fits the row before it when its height above that row is the layer’s thickness
by the hypsometric equation (from the two pressures and virtual temperatures, WMO-No. 8 eqs.
12.17 and 12.18), within 5% (THICKNESS_SHARE) plus what rounding the pressures can move it
plus 30 m (THICKNESS_SLACK_M). Rows are kept from the longest chain from the ground in
which each row fits the one before it, skipping at most MAX_MISFITS rows at a time (rows
with no temperature, or one out of bounds, can’t be checked, and aren’t counted); of
equally long chains, the one whose layers miss by the smallest total share of their
allowances. A row missing only its wind or humidity can be in the chain, so a gap in the wind
doesn’t widen the layers checked. A row not in the chain is left out
(DropReason::Thickness); in the recordings every row fits the row before it, to within 1 m
beyond rounding. Then it keeps:
- The ground, the first row: the pressure, temperature, humidity and wind at the station
when the balloon was released. Nothing before it checks it, so a chain without it that beats
every chain with it, starting among the rows it could reach, refuses the answer
(
WyomingError::GroundMisfit). Bad rows right after a good ground that fit the rows above them can beat it the same way, and refuse the answer: two BUFR rows 31 m high do it. - One row of each run of rows in the chain with the same pressure, the middle one. BUFR’s pressures are rounded to 0.1 hPa, and high up the balloon climbs tens of meters while the pressure falls that much, so runs of rows share a pressure; the rounded value is the pressure at about the middle of its run.
- Each such row that lies above the last row kept, higher and at a lower pressure.
A row missing a value (the last row often has no wind) is dropped, and so is one with a value
no air on Earth has (DropReason::OutOfRange lists the bounds). WyomingSounding::dropped
lists each row left out, with its reason.
So a row with a bad pressure or height, a pressure missing a digit, say, is left out, not kept
to hide the good rows after it; rows that fall or stay at one height, a balloon coming down, fit
and are left out however many. More than MAX_MISFITS rows after the chain’s end refuse the
answer (WyomingError::Misfit): the end, or all of them, are wrong (a block of heights 1 km
off), or a long run of rows with no temperature leaves a layer too thick for its two ends’
temperatures to give. Not caught: a wrong wind, humidity or temperature (the check doesn’t use
the wind, humidity moves it by a few percent, and on layers under about 100 m any temperature
within the bounds fits), a height error within the allowance, a row whose pressure and height
are both wrong yet fit each other, and a bad ground that fits the row after it or has only one
row after it. A block of bad rows that fit their neighbours can be kept, and good rows beside
them left out instead, no more than the block holds. A row, or a short block, kept a little too
high leaves out the good rows just above it, which now lie below it: in the tests, at most 2
other levels of a coded message and 10 other rows of a BUFR file (8 for one row).
Heights are geopotential meters (the column says so), converted to geometric heights at the
first row’s latitude with WMO-No. 8 (2023) eqs. 12.15 and 12.16
(hpr_atmos::profile::geometric_from_wmo_geopotential_m); the atmosphere page
explains why. That is the latitude the profile uses for its hydrostatics. The balloon drifts;
converting at the latitude it reached instead would move a height by about 0.8 m per degree
of drift at 10 km, 2.5 m at 30 km. A relative humidity above 100%, which radiosondes report in
cloud, is kept as recorded and taken as 100% in WyomingSounding::sounding, as the
atmosphere’s decision record asks.
fetch asks a Client for the URL, so the answer comes from the cache when it can, and
offline from the cache only; an answer that doesn’t parse is never cached. Show
ATTRIBUTION (it is on every Fetched) wherever the sounding is shown.
How far to trust it: the profile gives back every row it keeps as recorded (the tests). A radiosonde’s own errors are small next to how far the air can change between the station and the launch, and nothing here measures that. The guide page says more.
use hpr_atmos::WindInterpolation;
use hpr_net::wyoming::WyomingSounding;
// Santa Teresa, New Mexico, 21 June 2025, 12 UTC: the coded message's 228 rows.
let body = include_bytes!("../tests/fixtures/replay/wyoming-72364-fm35.csv");
let sounding = WyomingSounding::parse(body)?;
assert_eq!(sounding.levels.len(), 227); // the last row has no wind
let air = sounding.sounding(WindInterpolation::SpeedDirection)?;
let at_5_km = air.sample(5_000.0)?.air;
// Between the rows at 570 hPa (4,852 m) and 557 hPa (5,035 m) of geopotential height.
assert!((at_5_km.pressure_pa - 55_950.0).abs() < 100.0);Structs§
- Dropped
Level - A row left out of the profile, and why.
- Wyoming
Level - One level of a sounding, as recorded, in SI units.
- Wyoming
Request - What to ask the archive for: a station, the sounding’s nominal hour and its version.
- Wyoming
Sounding - A sounding read from the archive’s answer: the ground and the levels above it.
Enums§
- Drop
Reason - Why a row was left out of the profile.
- Wyoming
Error - Why a Wyoming request or answer was refused.
- Wyoming
Version - Which version of a sounding to ask for.
Constants§
- ATTRIBUTION
- The credit shown wherever a sounding is shown. The archive states no license; the soundings are the stations’ observations, which weather services exchange freely (WMO Resolution 40).
- ENDPOINT
- The archive’s address.
- MAX_
MISFITS - The most rows with a pressure, height and temperature within the bounds the chain of rows that fit may skip at a time, and the most that may follow its end. More after its end means the end, or all of them, are wrong (a block of heights 1 km off), and the answer is refused. In a BUFR file 10 rows are about 10 s of the balloon’s climb, some 50 m; in a coded message they can span kilometers.
- MAX_
ROWS - The most rows an answer may have. The archive’s BUFR files have about 6,000.
- SETTLED_
TTL_ S - How long an answer fetched after its sounding settled stays fresh, s: 30 days.
- SETTLE_
S - How long after its nominal hour a sounding may still be filling in, s: a day. The archive’s copy can grow for some hours after the flight, as a station’s later messages arrive.
- THICKNESS_
SHARE - The share of a layer’s hypsometric thickness a row’s height may miss it by, beyond rounding
and
THICKNESS_SLACK_M: 5%, for a layer whose inner rows have no temperature, which the mean of its two ends’ temperatures gives less well. A judgment, not a measurement: no row in the recordings needs any share. - THICKNESS_
SLACK_ M - The meters a row’s height may miss its layer’s thickness by, beyond the share and the pressures’ rounding: 30 m. A coded message’s heights from 500 hPa up are rounded to 10 m; a height 30 m off misplaces its level by as much as a pressure error of 0.33% to 0.55% (the air’s scale height, the climb over which pressure falls by a factor of e, is 5.5 to 9 km), so the check is for gross errors, not for these.
- YOUNG_
TTL_ S - How long an answer stays fresh while its sounding may still be filling in, s: an hour.
Functions§
- fetch
- Fetches
requestthroughclientand reads it.