Dropsondes

Warning

Experimental module (v1.9). The public API of hyplan.instruments.dropsondes is provisional and may change in subsequent releases as it is validated against operational use. Pin the HyPlan version if you depend on the current surface; track release notes for breaking changes.

Event-based sampling — each release is a single (lat, lon, alt, time) event, not a swath. The science footprint is the slant column from release to splash, drifting through the ambient wind. HyPlan’s dropsondes are first-class objects: DropsondeRelease (event), DropsondeTrajectory (simulated descent), and DropsondePlan (collection). All three are frozen with identity equality; DropsondePlan.simulate() is functional — it returns a new plan, leaving the original pre-sim plan intact.

The DropsondeSystem class extends Sensor for naming / registry uniformity but does not implement the ScanningSensor protocol — dropsondes have no swath. The pre-configured AVAPS_NRD41 reference is the standard NCAR-EOL Vaisala NRD41 sonde used in hurricane and airborne campaigns. Reference instances are shared singletons — to customise a parameter, build a new DropsondeSystem(...) rather than mutating the reference.

Worked planning example: notebooks/dropsonde_objects.ipynb.

Core objects

class DropsondeRelease[source]

Bases: object

A planned single-sonde release event.

All gating QC flags are tri-state (True/False/None); qc_release_ok aggregates them. qc_splash_in_target_polygon is not a gate — it is a post-simulation diagnostic populated by DropsondePlan.simulate().

Equality/hashing is identity-based (eq=False): two releases with structurally identical fields are still distinct objects. Use release_id as the explicit join key when set-like semantics are needed.

waypoint: Waypoint
sensor: DropsondeSystem = <hyplan.instruments.dropsondes.sensor.DropsondeSystem object>
aircraft: Aircraft | None = None
release_time: datetime | None = None
aircraft_velocity_mps: tuple[float, float] | None = None
source: FlightLine | Pattern | Hashable | None = None
source_id: Hashable | None = None
source_pattern_id: str | None = None
source_segment_type: str | None = None
qc_min_alt_ok: bool | None = None
qc_aircraft_envelope_ok: bool | None = None
qc_segment_allowed: bool | None = None
qc_splash_in_target_polygon: bool | None = None
release_id: int = 0
property qc_release_ok: bool | None

Tri-state aggregate over the gating flags only.

Rule (locked):

  • any known gate is FalseFalse

  • all gates are TrueTrue

  • otherwise (at least one None, none False) → None

qc_splash_in_target_polygon is intentionally not part of this aggregate.

to_waypoint()[source]

Return a defensive copy of the release waypoint.

Return type:

Waypoint

classmethod from_waypoint(wp, *, sensor=<hyplan.instruments.dropsondes.sensor.DropsondeSystem object>, release_time=None, release_id=0, **kwargs)[source]

Build a release from a Waypoint (defensively copied).

Return type:

DropsondeRelease

Parameters:
to_record()[source]

One-row dict for manifest export.

Columns are stable (used by DropsondePlan.to_manifest_gdf()).

Return type:

dict[str, Any]

with_qc(*, qc_min_alt_ok=<dataclasses._MISSING_TYPE object>, qc_aircraft_envelope_ok=<dataclasses._MISSING_TYPE object>, qc_segment_allowed=<dataclasses._MISSING_TYPE object>, qc_splash_in_target_polygon=<dataclasses._MISSING_TYPE object>)[source]

Return a copy of self with one or more QC flags replaced.

Return type:

DropsondeRelease

Parameters:
__init__(waypoint, sensor=<hyplan.instruments.dropsondes.sensor.DropsondeSystem object>, aircraft=None, release_time=None, aircraft_velocity_mps=None, source=None, source_id=None, source_pattern_id=None, source_segment_type=None, qc_min_alt_ok=None, qc_aircraft_envelope_ok=None, qc_segment_allowed=None, qc_splash_in_target_polygon=None, release_id=0)
Parameters:
Return type:

None

class DropsondeTrajectory[source]

Bases: object

A single simulated descent — the kernel output plus splash diagnostics.

release: DropsondeRelease
track: GeoDataFrame
splash_waypoint: Waypoint
time_to_surface: Quantity
drift_distance: Quantity
drift_bearing_deg: float
ensemble_member: int = 0
qc_terminated_at_ground: bool = True
qc_max_steps_exceeded: bool = False
qc_dem_gap_count: int = 0
to_geodataframe()[source]

Return the per-step trajectory GeoDataFrame.

Return type:

GeoDataFrame

summary_dict()[source]
Return type:

dict[str, Any]

__init__(release, track, splash_waypoint, time_to_surface, drift_distance, drift_bearing_deg, ensemble_member=0, qc_terminated_at_ground=True, qc_max_steps_exceeded=False, qc_dem_gap_count=0)
Parameters:
Return type:

None

class DropsondePlan[source]

Bases: object

A collection of planned dropsonde releases (and optionally their simulations).

Frozen; simulate() is functional and returns a new plan. Equality/hashing are identity-based (eq=False) — embedded Waypoint / Aircraft / FlightPlanTrack / GeoDataFrame objects make structural equality either expensive or wrong.

releases: tuple[DropsondeRelease, ...] = ()
trajectories: tuple[DropsondeTrajectory, ...] = ()
flight_track: FlightPlanTrack | None = None
target_polygon: Polygon | None = None
classmethod from_flight_plan(plan, *, sensor=<hyplan.instruments.dropsondes.sensor.DropsondeSystem object>, aircraft=None, takeoff_time, spacing=None, spacing_time=None, segment_types=('flight_line', 'transit'), min_release_altitude=None, surface_elevation_msl=None, target_polygon=None, dem_file=None, terrain_aware=False)[source]

Build a plan from a compute_flight_plan GeoDataFrame.

Set flight_track on the returned plan so the inverse targeting solver can iterate the aircraft trajectory later.

Return type:

DropsondePlan

Parameters:
classmethod from_pattern(pattern, *, sensor=<hyplan.instruments.dropsondes.sensor.DropsondeSystem object>, aircraft=None, takeoff_time=None, spacing=None, spacing_time=None, start_elapsed=<Quantity(0, 'second')>)[source]

Build a plan from a line-based Pattern.

Spacing resets per line; line-to-line transit is NOT modelled in this release. For authoritative multi-line timing use from_flight_plan(compute_flight_plan(...)).

Emits a UserWarning when the pattern has more than one line and takeoff_time is supplied, because the first release on every line carries the same timestamp (takeoff_time + start_elapsed) — physically impossible, just a hazard of geometry-only timing.

For is_waypoint_based patterns returns an empty plan and emits a UserWarning.

Return type:

DropsondePlan

Parameters:
simulate(*, wind_field, n_ensemble=0, wind_perturbation_sigma=<Quantity(1.5, 'meter / second')>, fall_rate_perturbation_pct=5.0, dem_file=None, terrain_aware=False, surface_elevation_msl=None, dt=<Quantity(1, 'second')>, rng_seed=None)[source]

Forward-simulate all non-skipped releases. Returns a new plan.

Return type:

DropsondePlan

Parameters:
to_manifest_gdf()[source]

Manifest GeoDataFrame — one row per planned release.

Return type:

GeoDataFrame

trajectories_gdf()[source]

Concatenated per-step trajectory GeoDataFrame for all releases × members.

Return type:

GeoDataFrame

summary()[source]

Per-release splash diagnostics.

Return type:

DataFrame

plot(*, ax=None, show_traces=True, show_ellipses=True, sigma_scale=2.0)[source]

Simple map overlay: track, releases, traces, splash means.

Return type:

Axes

Parameters:
  • ax (Axes | None)

  • show_traces (bool)

  • show_ellipses (bool)

  • sigma_scale (float)

__init__(releases=(), trajectories=(), flight_track=None, target_polygon=None)
Parameters:
Return type:

None

Sensor

class DropsondeSystem[source]

Bases: Sensor

Generic ballistic-parachute dropsonde with configurable descent model.

Parameters

namestr

Display name (e.g. "Vaisala NRD41").

descent_rate_modelCallable[[Quantity], Quantity]

Function mapping altitude MSL → terminal fall speed (positive downward). Default: terminal_velocity_nrd41().

min_release_altitudeQuantity

Minimum height AGL at which the chute can deploy reliably (default 300 m).

deployment_timeQuantity

Time from canister exit to stable terminal-velocity descent (default 5 s).

nominal_massQuantity

Sonde mass; informational only (default 0.39 kg for NRD41).

sourcestr

Provenance string (URL + retrieval date for the datasheet).

__init__(name='Generic Dropsonde', *, descent_rate_model=<function terminal_velocity_nrd41>, min_release_altitude=<Quantity(300, 'meter')>, deployment_time=<Quantity(5, 'second')>, nominal_mass=<Quantity(0.39, 'kilogram')>, source='')[source]
Parameters:
Return type:

None

fall_time(release_altitude_agl, *, dt=<Quantity(1, 'second')>)[source]

Still-air time from release to surface (no wind).

Return type:

Quantity

Parameters:
AVAPS_NRD41

Generic ballistic-parachute dropsonde with configurable descent model.

Parameters

namestr

Display name (e.g. "Vaisala NRD41").

descent_rate_modelCallable[[Quantity], Quantity]

Function mapping altitude MSL → terminal fall speed (positive downward). Default: terminal_velocity_nrd41().

min_release_altitudeQuantity

Minimum height AGL at which the chute can deploy reliably (default 300 m).

deployment_timeQuantity

Time from canister exit to stable terminal-velocity descent (default 5 s).

nominal_massQuantity

Sonde mass; informational only (default 0.39 kg for NRD41).

sourcestr

Provenance string (URL + retrieval date for the datasheet).

RD94

Generic ballistic-parachute dropsonde with configurable descent model.

Parameters

namestr

Display name (e.g. "Vaisala NRD41").

descent_rate_modelCallable[[Quantity], Quantity]

Function mapping altitude MSL → terminal fall speed (positive downward). Default: terminal_velocity_nrd41().

min_release_altitudeQuantity

Minimum height AGL at which the chute can deploy reliably (default 300 m).

deployment_timeQuantity

Time from canister exit to stable terminal-velocity descent (default 5 s).

nominal_massQuantity

Sonde mass; informational only (default 0.39 kg for NRD41).

sourcestr

Provenance string (URL + retrieval date for the datasheet).

AXCTD

Generic ballistic-parachute dropsonde with configurable descent model.

Parameters

namestr

Display name (e.g. "Vaisala NRD41").

descent_rate_modelCallable[[Quantity], Quantity]

Function mapping altitude MSL → terminal fall speed (positive downward). Default: terminal_velocity_nrd41().

min_release_altitudeQuantity

Minimum height AGL at which the chute can deploy reliably (default 300 m).

deployment_timeQuantity

Time from canister exit to stable terminal-velocity descent (default 5 s).

nominal_massQuantity

Sonde mass; informational only (default 0.39 kg for NRD41).

sourcestr

Provenance string (URL + retrieval date for the datasheet).

terminal_velocity_nrd41(altitude_msl)[source]

Vaisala NRD41 / RD94 terminal fall velocity, positive downward.

Linearly interpolates between published Vaisala values (~11 m/s at sea level, rising to ~22 m/s at 13 km MSL). Outside the tabulated range the curve clamps to the nearest endpoint.

Return type:

Quantity

Parameters:

altitude_msl (Quantity)

terminal_velocity_sippican_axctd(altitude_msl)[source]

Air-phase terminal fall velocity for a Lockheed-Martin Sippican AXCTD.

Approximate planning model from public product literature: ~10 m/s at the surface rising to ~13-14 m/s at the upper end of operational release altitudes. The water-phase descent through the ocean column is not modelled — DropsondeSystem terminates the trajectory at splash.

Return type:

Quantity

Parameters:

altitude_msl (Quantity)

Simulation

simulate_release(release, *, wind_field, dem_file=None, terrain_aware=False, surface_elevation_msl=None, dt=<Quantity(1, 'second')>, u_bias_mps=0.0, v_bias_mps=0.0, fall_rate_scale=1.0, ensemble_member=0)[source]

Forward-simulate a DropsondeRelease through wind_field.

Wraps simulate_descent_trajectory(). All inputs except the integration kwargs come from release. Returns a DropsondeTrajectory whose release field is the same object passed in.

Return type:

DropsondeTrajectory

Parameters:
simulate_descent_trajectory(release_lat, release_lon, release_altitude_msl, release_time_utc, *, sensor, wind_field, dem_file=None, terrain_aware=False, surface_elevation_msl=None, dt=<Quantity(1, 'second')>, method='rk4', max_steps=3600, release_id=0, ensemble_member=0, u_bias_mps=0.0, v_bias_mps=0.0, fall_rate_scale=1.0, aircraft_velocity_mps=None)[source]

RK4 integrator for a single dropsonde descent.

Integrates:

dlat/dt = v_n / R_earth
dlon/dt = v_e / (R_earth * cos(lat))
dz/dt   = -w_f(z)

with per-step wind sampling at the current trajectory point, a deployment-transient that decays linearly from the aircraft velocity to the local wind over sensor.deployment_time, and per-ensemble (u_bias, v_bias) additive offsets.

Termination, in order: terrain (terrain_aware=True), surface_elevation_msl, sea level, or max_steps.

Returns the per-step GeoDataFrame with terminated_at_ground / max_steps_exceeded / dem_gap_count attached as attrs.

Return type:

GeoDataFrame

Parameters:

Planning helpers

releases_along_flight_line(flight_line, *, sensor=<hyplan.instruments.dropsondes.sensor.DropsondeSystem object>, aircraft=None, takeoff_time=None, start_elapsed=<Quantity(0, 'second')>, groundspeed=None, spacing=None, spacing_time=None, min_release_altitude=None, surface_elevation_msl=None, first_release_id=0)[source]

Spaced DropsondeRelease events along a FlightLine.

Heading comes from flight_line.az12; altitude is the line’s single altitude. Spacing follows the same stricter-wins rule as DropsondePlan.from_flight_plan().

Return type:

list[DropsondeRelease]

Parameters:
  • flight_line (FlightLine)

  • sensor (DropsondeSystem)

  • aircraft (Aircraft | None)

  • takeoff_time (_dt.datetime | None)

  • start_elapsed (Quantity)

  • groundspeed (Quantity | None)

  • spacing (Quantity | None)

  • spacing_time (Quantity | None)

  • min_release_altitude (Quantity | None)

  • surface_elevation_msl (Quantity | None)

  • first_release_id (int)

Inverse targeting

class DropsondeReleaseSolution[source]

Bases: object

Result of an inverse targeting solve.

target: Waypoint
release: DropsondeRelease
trajectory: DropsondeTrajectory
miss_distance: Quantity
feasible: bool
reason: str | None = None
__init__(target, release, trajectory, miss_distance, feasible, reason=None)
Parameters:
Return type:

None

solve_release_for_target(target, flight_plan, *, takeoff_time, sensor=<hyplan.instruments.dropsondes.sensor.DropsondeSystem object>, wind_field, aircraft=None, search_window=None, segment_types=('flight_line', 'transit'), coarse_step=<Quantity(10, 'second')>, tolerance=<Quantity(100, 'meter')>, max_iter=30)[source]

Find the release time along flight_plan whose splash hits target.

Coarse scan + golden-section refine. Returns a solution whose feasible flag is True only when the miss distance is within tolerance.

Return type:

DropsondeReleaseSolution

Parameters:

Flight-plan adapter

class FlightPlanTrack[source]

Bases: object

Typed view of a computed flight plan, with a trajectory sampler.

__init__(segments)[source]
Parameters:

segments (list[PlannedSegment])

Return type:

None

classmethod from_compute_flight_plan(plan)[source]

Validate + normalise a compute_flight_plan GeoDataFrame.

Return type:

FlightPlanTrack

Parameters:

plan (GeoDataFrame)

filter(segment_types)[source]

Return a track with only the given segment types, preserving cumulative timing.

Return type:

FlightPlanTrack

Parameters:

segment_types (tuple[str, ...])

total_duration_s()[source]
Return type:

float

elapsed_range()[source]
Return type:

tuple[float, float]

sample_at_elapsed(elapsed)[source]

Aircraft state at the given elapsed time after takeoff.

Return type:

AircraftTrackSample

Parameters:

elapsed (Quantity | float)

iter_samples(*, step=<Quantity(1, 'second')>, segment_types=None)[source]

Iterate samples at uniform elapsed-time spacing.

Samples that fall inside a segment whose type is not in segment_types (when provided) are skipped. This is what the inverse-targeting solver uses to enumerate feasible release times.

Return type:

Iterator[AircraftTrackSample]

Parameters:
class PlannedSegment[source]

Bases: object

One row of a computed flight plan, normalised to SI units.

index: Hashable
geometry: LineString
segment_type: str
start_altitude_m: float
end_altitude_m: float
duration_s: float
start_elapsed_s: float
end_elapsed_s: float
planned_track_deg: float | None
wind_corrected_heading_deg: float | None
groundspeed_mps: float | None
segment_name: str | None
__init__(index, geometry, segment_type, start_altitude_m, end_altitude_m, duration_s, start_elapsed_s, end_elapsed_s, planned_track_deg, wind_corrected_heading_deg, groundspeed_mps, segment_name, _length_m=0.0)
Parameters:
Return type:

None

class AircraftTrackSample[source]

Bases: object

Aircraft state at a specific elapsed time after takeoff.

elapsed_s: float
latitude: float
longitude: float
altitude_m: float
heading_deg: float | None
groundspeed_mps: float | None
segment_index: Hashable
segment_type: str
segment_name: str | None
__init__(elapsed_s, latitude, longitude, altitude_m, heading_deg, groundspeed_mps, segment_index, segment_type, segment_name)
Parameters:
Return type:

None

GeoDataFrame exports

DropsondePlan exposes three GeoDataFrame views — manifest (one row per planned release), trajectories (one row per integration step), and summary (per-release diagnostics with splash ellipse) — via to_manifest_gdf(), trajectories_gdf(), and summary(). See the worked notebook for the column schemas in context.

summarize_trajectories(trajectories, *, target_polygon=None)[source]

Per-release splash diagnostics + (when supplied) polygon-hit fraction.

Return type:

DataFrame

Parameters: