Instruments

All sensor classes live under the hyplan.instruments subpackage and are re-exported from the top-level hyplan namespace for convenience.

Base class

class Sensor[source]

Bases: object

Base class to represent a generic sensor.

Parameters:

name (str) – Human-readable name identifying the sensor.

__init__(name)[source]
Parameters:

name (str)

Line scanners

class LineScanner[source]

Bases: Sensor

A pushbroom or whiskbroom line scanning imager.

Line scanners capture one cross-track line of pixels per frame, building up an image as the aircraft moves along-track.

Parameters:
  • name (str) – Sensor name.

  • fov (float) – Total cross-track field of view in degrees.

  • across_track_pixels (int) – Number of pixels across the swath.

  • frame_rate (Quantity) – Frame acquisition rate in Hz.

  • cross_track_tilt (float) – Cross-track tilt angle in degrees (rotation about the along-track axis). Positive = starboard (right of track), negative = port (left of track). Default 0.0 (nadir-looking).

__init__(name, fov, across_track_pixels, frame_rate, cross_track_tilt=0.0)[source]
Parameters:
property ifov: float

Calculate the cross-track Instantaneous Field of View (IFOV) in degrees.

property half_angle: float

Calculate and return the half angle in degrees.

property frame_period: Quantity

Calculate and return the frame period in seconds.

swath_offset_angles()[source]

Cross-track viewing angles for each swath edge, measured from nadir.

Accounts for cross_track_tilt (rotation about the along-track axis). Negative = port (left of track), positive = starboard (right of track).

Return type:

tuple[float, float]

Returns:

Tuple of (port_edge_angle, starboard_edge_angle) in degrees.

Examples

Nadir sensor, 30° half-angle: (-30.0, 30.0) Same sensor with 10° starboard tilt: (-20.0, 40.0)

swath_width(altitude_agl)[source]

Calculate swath width for a given altitude above ground level (AGL).

Accounts for cross_track_tilt — when the sensor is tilted off-nadir the swath is asymmetric and its total width changes.

Parameters:

altitude_agl (Quantity) – Altitude above ground level.

Returns:

Swath width in meters.

Return type:

Quantity

ground_sample_distance(altitude_agl, mode='nadir')[source]

Calculate the ground sample distance (GSD) for a given altitude above ground level (AGL).

Parameters:
  • altitude_agl (Quantity) – Altitude above ground level.

  • mode (str) – One of "nadir" (GSD directly below the aircraft), "average" (swath width / pixel count), or "edge" (cross-track GSD of the outermost pixel, exact form h · (tan(θ_edge) tan(θ_edge ifov)) with θ_edge = fov / 2).

Raises:

HyPlanValueError – when mode is not recognized.

Return type:

Quantity

altitude_agl_for_ground_sample_distance(gsd, mode='nadir')[source]

Calculate the required altitude AGL (Above Ground Level) for a given ground sample distance (GSD).

Inverts ground_sample_distance() for the same mode, so the two methods round-trip exactly.

Raises:

HyPlanValueError – when mode is not recognized.

Return type:

Quantity

Parameters:
critical_ground_speed(altitude_agl, along_track_sampling=1.0)[source]

Calculate the maximum allowable aircraft ground speed (m/s) to maintain proper along-track sampling.

Parameters:
  • altitude_agl (Quantity) – Altitude above ground level in meters.

  • along_track_sampling (float) – The oversampling factor (default = 1.0).

Returns:

Maximum allowable ground speed in meters per second.

Return type:

Quantity

along_track_pixel_size(aircraft_speed, along_track_sampling=1.0)[source]

Calculate the along-track pixel size for a given aircraft speed and along-track sampling factor.

Parameters:
  • aircraft_speed (Quantity) – Speed of the aircraft in m/s.

  • along_track_sampling (float) – Along-track sampling (oversampling) factor (default = 1.0).

Returns:

Along-track pixel size in meters.

Return type:

Quantity

ground_pixel_dimensions(altitude_agl, ground_speed)[source]

Cross-track and along-track ground pixel size + aspect ratio.

For a pushbroom line scanner, the cross-track GSD is locked by sensor optics (lens + pixel pitch), while the along-track “pixel” is just ground_speed × frame_period. The ratio of the two tells you whether your data product has square or rectangular ground pixels at the planned operating point.

Parameters:
  • altitude_agl (Quantity) – Altitude above ground level.

  • ground_speed (Quantity) – Platform ground speed.

Return type:

dict[str, Quantity | float]

Returns:

A dict with keys "cross_track" (Quantity, meters), "along_track" (Quantity, meters), and "aspect_ratio" (float). aspect_ratio is along_track / cross_track (1.0 = square pixels; >1 = elongated along the flight direction).

class AVIRISClassic

Bases: LineScanner

AVIRIS Classic (34.0° FOV, 677 pixels, 12.0 Hz).

__init__()
Parameters:

self (Any)

Return type:

None

class AVIRISNextGen

Bases: LineScanner

AVIRIS Next Gen (36.0° FOV, 600 pixels, 100.0 Hz).

__init__()
Parameters:

self (Any)

Return type:

None

class AVIRIS3

Bases: LineScanner

AVIRIS 3 (39.6° FOV, 1234 pixels, 216.0 Hz).

__init__()
Parameters:

self (Any)

Return type:

None

class AVIRIS5

Bases: LineScanner

AVIRIS 5 (40.2° FOV, 1239 pixels, 148.0 Hz).

__init__()
Parameters:

self (Any)

Return type:

None

class HyTES

Bases: LineScanner

HyTES (50.0° FOV, 512 pixels, 36.0 Hz).

__init__()
Parameters:

self (Any)

Return type:

None

class PRISM

Bases: LineScanner

PRISM (30.7° FOV, 608 pixels, 176.0 Hz).

__init__()
Parameters:

self (Any)

Return type:

None

class MASTER

Bases: LineScanner

MASTER (85.92° FOV, 716 pixels, 25.0 Hz).

__init__()
Parameters:

self (Any)

Return type:

None

class GLiHT_VNIR

Bases: LineScanner

G-LiHT VNIR (Headwall Microhyperspec E) (55.3° FOV, 645 pixels, 75.0 Hz).

__init__()
Parameters:

self (Any)

Return type:

None

class GLiHT_SIF

Bases: LineScanner

G-LiHT SIF (Headwall FIREFLY) (23.5° FOV, 1600 pixels, 37.5 Hz).

__init__()
Parameters:

self (Any)

Return type:

None

class GCAS_UV_Vis

Bases: LineScanner

GCAS UV-Vis Spectrometer (45.0° FOV, 1024 pixels, 12.0 Hz).

__init__()
Parameters:

self (Any)

Return type:

None

class GCAS_VNIR

Bases: LineScanner

GCAS Visible Near-Infrared (VNIR) Spectrometer (70.0° FOV, 1024 pixels, 12.0 Hz).

__init__()
Parameters:

self (Any)

Return type:

None

class eMAS

Bases: LineScanner

eMAS (85.92° FOV, 716 pixels, 6.25 Hz).

__init__()
Parameters:

self (Any)

Return type:

None

class PICARD

Bases: LineScanner

PICARD (50.0° FOV, 412 pixels, 100.0 Hz).

__init__()
Parameters:

self (Any)

Return type:

None

LVIS lidar

class LVISLens[source]

Bases: object

LVIS lens option defined by its beam divergence.

divergence_mrad is the full-angle beam divergence (the full cone angle, not the half-angle), consistent with the ALS-lidar convention. The footprint diameter is therefore tan(divergence) * altitude.

name: str
divergence_mrad: float
footprint_diameter(altitude_agl)[source]

Footprint diameter on the ground for a given altitude AGL.

Uses the full-angle divergence_mrad: footprint = tan(divergence) * altitude_agl.

Parameters:

altitude_agl (Quantity) – Flight altitude above ground level.

Return type:

Quantity

Returns:

Footprint diameter in meters.

__init__(name, divergence_mrad)
Parameters:
Return type:

None

class LVIS[source]

Bases: Sensor

LVIS full-waveform airborne scanning lidar.

Provides the standard Sensor interface (half_angle, swath_width) so that LVIS works with generate_swath_polygon, generate_flight_lines, and other HyPlan tools. The swath_width returned is the geometric maximum; use effective_swath_width to account for contiguous-coverage constraints.

All altitude parameters expect AGL (above ground level).

Parameters:
  • rep_rate (Quantity) – Laser pulse repetition rate (Hz). Default 4000 Hz.

  • lens (object) – Lens option — LVISLens instance, key from LVIS_LENSES (“narrow”, “medium”, “wide”), or None for wide (default).

  • scan_half_angle_deg (float) – Half-scan angle in degrees. Controls the geometric maximum swath via 2 * altitude * tan(angle). Default ≈5.71° corresponds to max swath = 0.2 * altitude.

__init__(rep_rate=<Quantity(4000, 'hertz')>, lens=None, scan_half_angle_deg=np.float64(5.710593137499643))[source]
Parameters:
property half_angle: float

Half-scan angle in degrees (default ≈5.71 deg).

swath_offset_angles()[source]

Cross-track viewing angles for each swath edge (nadir-looking).

Return type:

tuple[float, float]

Returns:

Tuple of (port_edge_angle, starboard_edge_angle) in degrees.

swath_width(altitude_agl)[source]

Maximum swath width set by the scanner geometry.

This is the geometric limit: 2 * altitude_agl * tan(half_angle). Equivalent to 0.2 * altitude_agl.

Used by generate_swath_polygon and generate_flight_lines for swath polygon generation and line spacing.

Parameters:

altitude_agl (Quantity) – Flight altitude above ground level.

Return type:

Quantity

Returns:

Maximum swath width in meters.

equivalent_fov(altitude_agl, speed)[source]

Equivalent field of view in degrees after contiguous-coverage limits.

When the laser footprint can fill the full scanner swath, this equals the geometric max FOV. When coverage is sampling-limited, the equivalent FOV narrows: 2 * atan(effective_swath / (2 * altitude)).

Parameters:
  • altitude_agl (Quantity) – Flight altitude above ground level.

  • speed (Quantity) – Aircraft ground speed.

Return type:

float

Returns:

Equivalent FOV in degrees.

footprint_diameter(altitude_agl)[source]

Laser footprint diameter on the ground for the configured lens.

footprint = tan(divergence_mrad / 1000) * altitude_agl

Parameters:

altitude_agl (Quantity) – Flight altitude above ground level.

Return type:

Quantity

Returns:

Footprint diameter in meters.

coverage_rate(altitude_agl, speed)[source]

Area coverage rate at maximum swath.

coverage_rate = speed * max_swath

Parameters:
  • altitude_agl (Quantity) – Flight altitude above ground level.

  • speed (Quantity) – Aircraft ground speed.

Return type:

Quantity

Returns:

Coverage rate in m^2/s.

footprint_for_max_swath(altitude_agl, speed)[source]

Minimum footprint diameter needed to fill the max swath contiguously.

footprint = sqrt(speed * max_swath / rep_rate)

Parameters:
  • altitude_agl (Quantity) – Flight altitude above ground level.

  • speed (Quantity) – Aircraft ground speed.

Return type:

Quantity

Returns:

Required footprint diameter in meters.

effective_swath_width(altitude_agl, speed)[source]

Achievable swath width accounting for contiguous coverage.

The effective swath is the minimum of: - the scanner’s max swath (geometric limit), and - footprint^2 * rep_rate / speed (contiguous-coverage limit)

When the footprint is too small relative to the flight speed, shots cannot tile the full max swath without gaps, and the effective swath narrows.

Parameters:
  • altitude_agl (Quantity) – Flight altitude above ground level.

  • speed (Quantity) – Aircraft ground speed.

Return type:

Quantity

Returns:

Effective swath width in meters.

is_contiguous(altitude_agl, speed)[source]

Check whether the current configuration fills the max swath.

Returns True if the footprint is large enough to tile the full scanner swath at the given speed.

Return type:

bool

Parameters:
along_track_spacing(speed)[source]

Along-track distance between consecutive laser shots.

along_track_spacing = speed / rep_rate

Parameters:

speed (Quantity) – Aircraft ground speed.

Return type:

Quantity

Returns:

Shot spacing in meters.

is_along_track_contiguous(altitude_agl, speed)[source]

Check whether consecutive shots overlap along-track.

Returns True if the along-track spacing is less than or equal to the footprint diameter.

Return type:

bool

Parameters:
point_density(altitude_agl, speed)[source]

Laser shot density within the effective swath.

point_density = rep_rate / (speed * effective_swath)

This is the primary planning metric for LVIS survey design.

Parameters:
  • altitude_agl (Quantity) – Flight altitude above ground level.

  • speed (Quantity) – Aircraft ground speed.

Return type:

Quantity

Returns:

Point density in shots per square meter (1/m^2).

solve_for_speed(target_density, altitude_agl)[source]

Compute the maximum aircraft speed for a target point density.

Inverts point_density = rep_rate / (speed * effective_swath) accounting for the coupling between speed and effective swath.

Two regimes apply:

  • Geometry-limited (target_density >= 1 / fp^2): slowing down raises the density without bound, so the target is met at speed = rep_rate / (density * max_swath) and at every slower speed. That closed form is the maximum speed at which point_density >= target_density.

  • Below the sampling-limited floor (target_density < 1 / fp^2): at high speed the effective swath narrows while the density within it plateaus at 1 / fp^2, so the density never drops below the floor at any speed. Every speed meets the target and no maximum speed exists — raises HyPlanValueError.

Parameters:
  • target_density (Quantity) – Desired point density (1/m^2).

  • altitude_agl (Quantity) – Flight altitude above ground level.

Return type:

Quantity

Returns:

Maximum speed that achieves at least the target density.

Raises:

HyPlanValueError – If the target density is below the sampling-limited floor 1/fp^2 (achieved at any speed, so no maximum speed exists).

solve_for_altitude(target_density, speed)[source]

Compute the maximum altitude for a target point density.

Inverts the density equation, accounting for the dependence of both footprint and max swath on altitude.

In the geometry-limited regime (where effective swath = max swath):

density = rep_rate / (speed * 2 * altitude * tan(half_angle))

So:

altitude = rep_rate / (speed * density * 2 * tan(half_angle))

Parameters:
  • target_density (Quantity) – Desired point density (1/m^2).

  • speed (Quantity) – Aircraft ground speed.

Return type:

Quantity

Returns:

Maximum altitude AGL in meters.

summary(altitude_agl, speed)[source]

Compute all LVIS coverage parameters for a given flight configuration.

Parameters:
  • altitude_agl (Quantity) – Flight altitude above ground level.

  • speed (Quantity) – Aircraft ground speed.

Return type:

dict[str, Any]

Returns:

Dictionary with all computed parameters.

print_summary(altitude_agl, speed)[source]

Print a formatted summary of LVIS coverage parameters.

Return type:

None

Parameters:
compare_lenses(altitude_agl, speed)[source]

Print a comparison table across all standard lenses.

Return type:

None

Parameters:
footprint_on_terrain(lat, lon, altitude_msl, heading, scan_angle_deg=0.0, dem_file=None)[source]

Compute laser footprint on terrain at a single scan position.

Uses ray_terrain_intersection() to find the ground point, then computes slant range, surface incidence angle, and the resulting footprint ellipse.

Parameters:
  • lat (float) – Aircraft latitude (degrees).

  • lon (float) – Aircraft longitude (degrees).

  • altitude_msl (float) – Aircraft altitude MSL (meters).

  • heading (float) – Aircraft heading (degrees true, clockwise from north).

  • scan_angle_deg (float) – Scan angle from nadir (degrees). Positive = starboard, negative = port. Default 0 (nadir).

  • dem_file (str | None) – Path to DEM file. Auto-downloaded if None.

Return type:

dict[str, Any]

Returns:

Dict with ground position, slant range, incidence angle, footprint ellipse dimensions, and flat-earth comparison.

effective_swath_on_terrain(lat, lon, altitude_msl, heading, speed, dem_file=None, n_scan_positions=21)[source]

Compute effective swath across the scan, accounting for terrain.

Discretises the scan into n_scan_positions angles from port to starboard and evaluates the terrain-aware footprint at each. Returns per-position metrics and the overall effective swath. Each scan position’s share of the rep rate is proportional to its strip width, so local densities and contiguity are independent of the discretization n_scan_positions.

Parameters:
  • lat (float) – Aircraft latitude (degrees).

  • lon (float) – Aircraft longitude (degrees).

  • altitude_msl (float) – Aircraft altitude MSL (meters).

  • heading (float) – Aircraft heading (degrees true).

  • speed (Quantity) – Aircraft ground speed.

  • dem_file (str | None) – Path to DEM file. Auto-downloaded if None.

  • n_scan_positions (int) – Number of scan positions across the swath.

Return type:

dict[str, Any]

Returns:

Dict with per-position arrays and aggregate metrics.

terrain_summary(lat, lon, altitude_msl, heading, speed, dem_file=None)[source]

Coverage summary with terrain correction at a specific position.

Combines the flat-earth summary() output with terrain-aware metrics from effective_swath_on_terrain().

Parameters:
  • lat (float) – Aircraft latitude (degrees).

  • lon (float) – Aircraft longitude (degrees).

  • altitude_msl (float) – Aircraft altitude MSL (meters).

  • heading (float) – Aircraft heading (degrees true).

  • speed (Quantity) – Aircraft ground speed.

  • dem_file (str | None) – Path to DEM file. Auto-downloaded if None.

Return type:

dict[str, Any]

Returns:

Dict with all keys from summary() plus terrain-specific keys prefixed with terrain_.

LVIS_LENS_NARROW

LVIS lens option defined by its beam divergence.

divergence_mrad is the full-angle beam divergence (the full cone angle, not the half-angle), consistent with the ALS-lidar convention. The footprint diameter is therefore tan(divergence) * altitude.

LVIS_LENS_MEDIUM

LVIS lens option defined by its beam divergence.

divergence_mrad is the full-angle beam divergence (the full cone angle, not the half-angle), consistent with the ALS-lidar convention. The footprint diameter is therefore tan(divergence) * altitude.

LVIS_LENS_WIDE

LVIS lens option defined by its beam divergence.

divergence_mrad is the full-angle beam divergence (the full cone angle, not the half-angle), consistent with the ALS-lidar convention. The footprint diameter is therefore tan(divergence) * altitude.

The three pre-configured lens instances are also exposed via the LVIS_LENSES mapping (keys "narrow", "medium", "wide") for parameterising tests or campaigns by lens name.

Airborne laser scanners (topographic ALS)

Generic ALSLidar class for rotating-mirror discrete-return topographic lidar (RIEGL VQ-series, Leica TerrainMapper, Optech Galaxy, Phoenix LiDAR Ranger). Pre-configured reference instance: RIEGL_VQ_480II (RIEGL VQ-480 II at the 1200 kHz operating point). Worked planning example: notebooks/als_lidar_planning.ipynb.

class ALSLidar[source]

Bases: Sensor

Generic Airborne Laser Scanner — scanning-mirror discrete-return topographic lidar.

Models rotating-polygon scanners with a fixed-rate pulsed laser sweeping linear scan lines perpendicular to the aircraft track. Conforms to the hyplan.instruments.ScanningSensor Protocol so instances plug directly into hyplan.swath.generate_swath_polygon() and hyplan.flight_box.box_around_polygon().

Parameters

namestr

Instrument display name (e.g. "RIEGL VQ-480 II").

prfQuantity

Laser pulse repetition rate (Hz).

scan_rateQuantity

Scan-line rate (Hz) — number of cross-track scan lines per second.

scan_half_angleQuantity

Half scan angle from nadir (degrees). Total scan FOV = 2 * scan_half_angle.

beam_divergenceQuantity

Full-angle laser beam divergence (radians or mrad).

wavelengthQuantity

Laser wavelength (nm — informational only).

max_rangeQuantity

Maximum usable slant range (m) at the reference target reflectivity. This is the radiometric envelope.

max_range_reflectivityfloat, default 0.6

Target reflectivity (0–1) at which max_range is quoted.

mta_zonesint, default 1

Number of multiple-time-of-arrival processing zones. Defines the timing envelope: mta_max_unambiguous_range = mta_zones * c / (2 * prf). HyPlan reports the envelope; the actual disambiguation algorithm is the vendor’s job.

scan_geometryScanGeometry, default “rotating_polygon_active_arc”

Scanner kinematic convention — see ScanGeometry. Determines the angular pulse spacing formula used by cross_track_spacing_at_nadir() and cross_track_spacing_at_angle(). Constructing with "oscillating_mirror" raises HyPlanValueError.

sourcestr, default “”

Provenance: datasheet citation including URL and retrieval date.

__init__(name, *, prf, scan_rate, scan_half_angle, beam_divergence, wavelength, max_range, max_range_reflectivity=0.6, mta_zones=1, scan_geometry='rotating_polygon_active_arc', source='')[source]
Parameters:
property half_angle: float

Half scan angle in degrees — satisfies ScanningSensor.

swath_offset_angles()[source]

Cross-track edge angles (port, starboard) in degrees.

Negative = port (left of track), positive = starboard.

Return type:

tuple[float, float]

swath_width(altitude_agl)[source]

Total cross-track swath width on flat ground at altitude_agl.

swath = 2 * altitude_agl * tan(scan_half_angle)

Return type:

Quantity

Parameters:

altitude_agl (Quantity)

footprint_diameter(altitude_agl, scan_angle=None)[source]

Laser spot diameter on flat ground.

At nadir: footprint = altitude_agl * beam_divergence (small- angle approximation). At off-nadir scan angle θ the ground spot is elliptical. This method returns the along-track (minor) axis = footprint_nadir / cos(θ) — stretched only by the slant-range growth. The along-scan-line (cross-track, major) axis grows faster, as footprint_nadir / cos²(θ), because the beam also sweeps across the tilted line of sight; that is the convention used by effective_swath_on_terrain().

Return type:

Quantity

Parameters:
point_density(altitude_agl, groundspeed, *, effective_prf=None)[source]

Nominal areal point density over the full swath rectangle.

density_nominal = effective_prf / (groundspeed * swath_width)

Semantics (locked): this is the nominal density — total pulses per unit time divided by the nominal swath-rectangle area per unit time. It does not model along-track gaps; if scan lines do not overlap, the actual instantaneously-illuminated ground points concentrate in narrower strips. We deliberately report the nominal value so the formula stays a single closed-form expression suitable for both forward calculation and inverse solving.

For "rotating_polygon_full_circle" scan geometry only the scan_half_angle / π fraction of pulses reaches the ground swath, so the pulse rate is derated accordingly (consistent with cross_track_spacing_at_nadir()).

Pass effective_prf to override self.prf (e.g. for derated MTA operation).

Survey planners MUST call is_along_track_contiguous() separately to verify the configuration produces contiguous ground coverage.

Return type:

Quantity

Parameters:
along_track_spacing(groundspeed)[source]

Ground distance between adjacent scan lines.

along_track = groundspeed / scan_rate

Purely kinematic — cannot be closed by altering altitude or scan angle. See is_along_track_contiguous().

Return type:

Quantity

Parameters:

groundspeed (Quantity)

cross_track_spacing_at_nadir(altitude_agl)[source]

Distance between adjacent pulses within one scan line at nadir.

For "rotating_polygon_active_arc": = 2 * scan_half_angle * scan_rate / prf. For "rotating_polygon_full_circle": = * scan_rate / prf.

Ground spacing at nadir is then altitude_agl * .

Return type:

Quantity

Parameters:

altitude_agl (Quantity)

cross_track_spacing_at_angle(altitude_agl, scan_angle)[source]

Ground pulse spacing at off-nadir scan angle θ.

Two effects combine for a constant-angular-velocity scanner:

  1. Slant range grows by 1 / cos(θ), stretching altitude * by 1 / cos(θ).

  2. Projection of the slant-range arc onto the ground adds another 1 / cos(θ).

Net: spacing(θ) = (altitude_agl * dθ) / cos²(θ).

Return type:

Quantity

Parameters:
coverage_rate(altitude_agl, groundspeed)[source]

Nominal swath area covered per unit time (m²/s).

coverage_rate = groundspeed * swath_width(altitude_agl)

Geometric coverage rate. Does not derate for along-track gaps — check is_along_track_contiguous() separately.

Return type:

Quantity

Parameters:
is_along_track_contiguous(altitude_agl, groundspeed)[source]

True iff consecutive scan lines on the ground overlap.

along_track_gap = groundspeed / scan_rate nadir_footprint = altitude_agl * beam_divergence contiguous iff along_track_gap <= nadir_footprint.

Strict planning constraint: if False, the flight line leaves un-imaged strips and no amount of cross-track line overlap will fill them.

Return type:

bool

Parameters:
is_cross_track_contiguous(altitude_agl, line_spacing)[source]

True iff adjacent flight lines spaced line_spacing apart produce overlapping swaths on the ground.

contiguous := line_spacing <= swath_width(altitude_agl)

Return type:

bool

Parameters:
coverage_diagnostic(altitude_agl, groundspeed)[source]

Per-dimension coverage breakdown for one flight line.

Returns a dict with keys (all values float or bool):

  • along_track_gap_m — scan-line ground gap

  • along_track_footprint_m — nadir laser footprint

  • along_track_contiguous — bool

  • swath_width_m — geometric swath

  • nadir_density_pts_m2 — peak density at nadir

  • edge_density_pts_m2 — density at ±scan_half_angle

  • swath_mean_density_pts_m2point_density() value

This is the diagnostic surface for survey planners. Along- and cross-track coverage are independent and reported separately.

Return type:

dict[str, float | bool]

Parameters:
solve_for_altitude(target_density, groundspeed, *, strict_contiguity=True)[source]

Altitude AGL (m) at which nominal density equals target_density at the given groundspeed.

Solves the closed-form altitude = ground_prf / (target_density * groundspeed * 2 * tan(scan_half_angle)) where ground_prf is the pulse rate reaching the ground swath (prf derated by scan_half_angle / π for full-circle scanners, matching point_density()).

If strict_contiguity (default), raises ContiguityError when the solved altitude would leave un-imaged strips between scan lines.

Return type:

Quantity

Parameters:
solve_for_groundspeed(target_density, altitude_agl, *, strict_contiguity=True)[source]

Groundspeed (m/s) at which nominal density equals target_density at the given altitude_agl. Same contiguity guard as solve_for_altitude().

Return type:

Quantity

Parameters:
required_overlap_percent(altitude_agl, target_density=None, groundspeed=None, *, default_overlap_percent=20.0)[source]

Adjacent-line overlap required for contiguous / target-density coverage, expressed as a percent in [0, 100).

Matches HyPlan’s existing overlap convention used by hyplan.flight_box.box_around_polygon() (0–100 percent).

Returns default_overlap_percent (default 20%) when target_density is not supplied — a small non-zero pad absorbs crab-angle / lateral track-keeping error and matches typical operational practice.

When target_density and groundspeed are both supplied, returns the overlap that makes the area-mean combined density equal target_density. The formula 1 mean/target equates the multiplicity-weighted (overlap-counted) density averaged over the swath to the target. Note this is an area-average guarantee, not a worst-case one: single-covered strips between overlap zones remain at the single-pass density (mean), which stays below target — only the doubly-covered overlap zones exceed it. Plan to a minimum-density requirement separately if uniform target coverage is needed.

Return type:

float

Parameters:
mta_max_unambiguous_range()[source]

Maximum unambiguous slant range from MTA timing alone (m).

max_unambiguous = mta_zones * c / (2 * prf)

Out of scope: vendor-proprietary disambiguation algorithms. HyPlan reports the envelope.

Return type:

Quantity

mta_practical_max_altitude()[source]

Practical AGL planning ceiling (m).

= min(mta_max_unambiguous_range, max_range)

The MTA formula gives the timing envelope; max_range gives the radiometric envelope. Both must hold. Assumes nadir geometry — off-nadir operation reduces the ceiling further by cos(scan_half_angle); the v1 module leaves that adjustment to the caller.

Return type:

Quantity

summary(altitude_agl, groundspeed)[source]

Full configuration + coverage summary for an operating point.

Return type:

dict[str, object]

Parameters:
footprint_on_terrain(lat, lon, altitude_msl, heading, scan_angle_deg=0.0, dem_file=None)[source]

Laser footprint on terrain at a single scan position.

Uses hyplan.terrain.ray_terrain_intersection() to find the ground point, then computes slant range, surface incidence angle, and the elliptical footprint on the sloped surface.

Footprint ellipse axes:

  • minor (along the laser axis, perpendicular to the LOS) = tan(beam_divergence) * slant_range.

  • major (along the surface tangent in the laser plane) = minor / cos(incidence) — stretched by the surface incidence angle.

Parameters:
  • lat (float) – Aircraft latitude (degrees).

  • lon (float) – Aircraft longitude (degrees).

  • altitude_msl (float) – Aircraft altitude MSL (meters).

  • heading (float) – Aircraft heading (degrees true, clockwise from north). For the boresight-along-track case, pass the ground track azimuth; for crab-aware planning, pass the aircraft heading (which differs from the track).

  • scan_angle_deg (float) – Scan angle from nadir (degrees). Positive = starboard, negative = port. Default 0 (nadir).

  • dem_file (str | None) – Path to DEM file. Auto-downloaded if None.

Return type:

dict[str, Any]

Returns:

Dict with ground position, slant range, incidence angle, footprint ellipse dimensions, and flat-earth comparison.

effective_swath_on_terrain(lat, lon, altitude_msl, heading, groundspeed, dem_file=None, n_scan_positions=51)[source]

Cross-track scan over terrain — per-position metrics.

Discretises the scan into n_scan_positions angles from port to starboard, ray-traces each to the terrain, and reports per- position slant range, surface incidence, footprint ellipse, cross-track ground spacing, and contiguity.

For ALS the contiguity model is two-axis:

  • along-track: groundspeed / scan_rate vs nadir footprint diameter (kinematic — same across the scan).

  • cross-track: ground pulse spacing between neighbours vs the along-scan-line footprint axis (which stretches by 1 / cos(incidence) on slopes).

Effective swath = length of the longest contiguous block where BOTH conditions hold.

Return type:

dict[str, Any]

Returns:

Dict with per-position arrays (NumPy) plus aggregate metrics (effective_swath_m, density stats, contiguous fraction).

Parameters:
terrain_summary(lat, lon, altitude_msl, heading, groundspeed, dem_file=None)[source]

Coverage summary at a specific position with terrain correction.

Combines the flat-earth summary() output with terrain- aware metrics from effective_swath_on_terrain() and a nadir footprint from footprint_on_terrain(). Keys prefixed with terrain_ are the terrain-aware quantities.

Return type:

dict[str, Any]

Parameters:
exception ContiguityError[source]

Bases: HyPlanValueError

Raised by ALS solvers when the requested target density would require a flight configuration that leaves un-imaged strips between consecutive scan lines.

The nominal density formula density = prf / (groundspeed * swath_width) always has a solution, but the solution may be physically degenerate when groundspeed / scan_rate exceeds the nadir laser footprint. Solvers raise this error by default; pass strict_contiguity=False to receive the nominal-density solution anyway.

RIEGL_VQ_480II

Generic Airborne Laser Scanner — scanning-mirror discrete-return topographic lidar.

Models rotating-polygon scanners with a fixed-rate pulsed laser sweeping linear scan lines perpendicular to the aircraft track. Conforms to the hyplan.instruments.ScanningSensor Protocol so instances plug directly into hyplan.swath.generate_swath_polygon() and hyplan.flight_box.box_around_polygon().

Parameters

namestr

Instrument display name (e.g. "RIEGL VQ-480 II").

prfQuantity

Laser pulse repetition rate (Hz).

scan_rateQuantity

Scan-line rate (Hz) — number of cross-track scan lines per second.

scan_half_angleQuantity

Half scan angle from nadir (degrees). Total scan FOV = 2 * scan_half_angle.

beam_divergenceQuantity

Full-angle laser beam divergence (radians or mrad).

wavelengthQuantity

Laser wavelength (nm — informational only).

max_rangeQuantity

Maximum usable slant range (m) at the reference target reflectivity. This is the radiometric envelope.

max_range_reflectivityfloat, default 0.6

Target reflectivity (0–1) at which max_range is quoted.

mta_zonesint, default 1

Number of multiple-time-of-arrival processing zones. Defines the timing envelope: mta_max_unambiguous_range = mta_zones * c / (2 * prf). HyPlan reports the envelope; the actual disambiguation algorithm is the vendor’s job.

scan_geometryScanGeometry, default “rotating_polygon_active_arc”

Scanner kinematic convention — see ScanGeometry. Determines the angular pulse spacing formula used by cross_track_spacing_at_nadir() and cross_track_spacing_at_angle(). Constructing with "oscillating_mirror" raises HyPlanValueError.

sourcestr, default “”

Provenance: datasheet citation including URL and retrieval date.

Multi-lidar rig (analog to MultiCameraRig) for systems that fly two or more identical scanning lidars at known mount orientations. Supports both forward/backward pitch tilt (multi-angle returns, same swath — NASA G-LiHT pattern) and left/right roll tilt (wider combined swath). The pre-configured GLIHT_DUAL_VQ_480I reference instance models the G-LiHT 2017+ dual VQ-480i configuration.

class LidarMount[source]

Bases: object

One unit in a MultiALSLidarRig with its mount orientation.

Attributes

lidarALSLidar

The sensor instance.

labelstr

Human-readable name for this unit (e.g. “forward”, “aft”).

pitch_tilt_degfloat

Pitch tilt about the aircraft’s lateral axis (degrees). Positive = nose-up looking forward along flight direction; negative = looking backward. Pure pitch tilts do not change the cross-track swath — they shift the ground footprint forward/backward by altitude_agl × tan(pitch_tilt) and create multi-angle returns over the same cross-track ground swath.

roll_tilt_degfloat

Roll tilt about the aircraft’s longitudinal axis (degrees). Positive = starboard, negative = port. Pure roll tilts shift the cross-track scan edges by the same angle, so a port-tilted unit plus a starboard-tilted unit produces a wider combined swath.

dx, dyQuantity

Lateral and longitudinal physical offsets of the unit’s mount point from the aircraft reference point. Typically small (≤ 1 m) for rigid integrated systems and below the planning precision used by MultiALSLidarRig.swath_width().

lidar: ALSLidar
label: str
pitch_tilt_deg: float = 0.0
roll_tilt_deg: float = 0.0
dx: Quantity
dy: Quantity
__init__(lidar, label, pitch_tilt_deg=0.0, roll_tilt_deg=0.0, dx=<factory>, dy=<factory>)
Parameters:
Return type:

None

class MultiALSLidarRig[source]

Bases: Sensor

Rig of multiple ALSLidar instances with known mount orientations.

Models the two common multi-scanning-lidar integrations:

  • Pitch-tilted forward/backward (e.g. NASA G-LiHT’s dual VQ-480i): both units image the same cross-track swath from forward and backward oblique angles. Combined cross-track swath = single-unit swath; combined nominal density ≈ sum of unit densities; provides multi-angle returns useful for canopy 3D structure.

  • Roll-tilted left/right: each unit images a different cross-track band. Combined cross-track swath is the union of the per-unit FOVs (wider than a single unit); density at any given ground point is the single-unit value, except in the overlap zone where the rolls are small enough that adjacent unit FOVs intersect.

Conforms to the hyplan.instruments.ScanningSensor Protocol so rig instances plug directly into hyplan.swath.generate_swath_polygon() and hyplan.flight_box.box_around_polygon(). The rig’s cross-track edges are the outermost edges across all units (each unit contributes [roll_tilt - half_angle, roll_tilt + half_angle]).

__init__(name, units)[source]
Parameters:
Return type:

None

units: list[LidarMount]
property half_angle: float

Magnitude of the larger combined edge angle (degrees).

Used by the ScanningSensor Protocol to size the cross-track ray cast. Equals max(|port|, |starboard|) of the combined edges.

swath_offset_angles()[source]

Combined cross-track edge angles (port, starboard) in degrees, from nadir. Negative = port, positive = starboard. Asymmetric roll-tilted rigs are supported (port ≠ -starboard).

Return type:

tuple[float, float]

swath_width(altitude_agl)[source]

Total combined cross-track swath width on flat ground.

swath = altitude × (tan(starboard_edge) - tan(port_edge))

For pure pitch-tilted rigs (all roll_tilt_deg == 0): equals the single-unit swath. For roll-tilted rigs: extended union.

Return type:

Quantity

Parameters:

altitude_agl (Quantity)

along_track_offsets(altitude_agl)[source]

Along-track ground offset of each unit’s nadir line at altitude.

For unit with pitch tilt θ, nadir ray hits the ground at altitude_agl × tan(θ) ahead of (or behind) the aircraft position. Returns {label: offset_distance} (positive = forward of aircraft).

Return type:

dict[str, Quantity]

Parameters:

altitude_agl (Quantity)

combined_point_density(altitude_agl, groundspeed)[source]

Sum of per-unit nominal point densities (pts/m²).

combined = sum(unit.point_density(altitude, speed) for unit in rig)

For pitch-only rigs whose units cover the same ground swath, this is the true combined density on every ground point.

For roll-tilted rigs, this is the mean density: total pulses per unit time over the combined swath area. Points in the overlap zone get higher density than this mean, points outside the overlap get lower density — call unit_point_densities() for the per-unit breakdown.

Return type:

Quantity

Parameters:
unit_point_densities(altitude_agl, groundspeed)[source]

Per-unit nominal density at this altitude and groundspeed.

Return type:

dict[str, Quantity]

Parameters:
solve_for_groundspeed(target_density, altitude_agl, *, strict_contiguity=True)[source]

Groundspeed (m/s) at which the rig’s combined point density equals target_density at the given altitude_agl.

Inverts combined_point_density(): each unit contributes prf / (speed × swath), so the combined density across N units is (sum prf_i) / (speed × swath). Solving for speed:

speed = (sum prf_i) / (target_density × swath)

For an N-unit pitch-only rig at identical PRF this is N× the per-unit speed returned by ALSLidar.solve_for_groundspeed() — the dual VQ-480i rig therefore tolerates roughly double the per-unit density-limited speed.

Same per-unit contiguity guard as ALSLidar.solve_for_groundspeed(): any unit whose nadir footprint is smaller than the along-track scan spacing at the solved speed raises ContiguityError (unless strict_contiguity=False).

Parameters:
  • target_density (Quantity) – Target combined point density (pts/m²).

  • altitude_agl (Quantity) – Altitude above ground level.

  • strict_contiguity (bool) – When True (default), raise ContiguityError if any unit’s scan lines would not be contiguous at the solved speed.

Return type:

Quantity

Returns:

Ground speed (m/s) achieving the target combined density.

multi_angle_pairs(min_dir_diff_deg=5.0)[source]

Find unit pairs with opposing pitch tilts (forward/backward).

Two units are considered a multi-angle pair if their pitch tilts differ in sign and |θ_fwd - θ_aft| >= min_dir_diff_deg. Default threshold is 5° — pitch tilts of ±7° (typical for canopy multi-angle configurations) clear it; the tighter QUAKES-I-style ±10°-and-up pairs are obviously detected too. Returns ordered (forward, backward) pairs — forward is the unit with positive pitch_tilt_deg.

Analogous to MultiCameraRig.stereo_pairs().

Return type:

list[tuple[LidarMount, LidarMount]]

Parameters:

min_dir_diff_deg (float)

coverage_diagnostic(altitude_agl, groundspeed)[source]

Per-unit + combined coverage breakdown for the rig.

Returns a dict with:

  • units — list of per-unit ALSLidar.coverage_diagnostic() results, each tagged with label and pitch_tilt_deg / roll_tilt_deg.

  • combined_swath_width_m — rig swath width.

  • combined_density_pts_m2 — sum of per-unit nominal densities.

  • along_track_offsets_m — per-unit ground offset for pitch tilts.

Return type:

dict[str, Any]

Parameters:
GLIHT_DUAL_VQ_480I

NASA G-LiHT 2017+ dual VQ-480i scanning lidar rig.

Two identical RIEGL VQ-480i units pitch-tilted ±7° (forward and backward) viewing the same cross-track 60° FOV swath from two oblique along-track angles, providing multi-angle returns for canopy 3D structure and approximately doubled point density.

Source: G-LiHT V2.0/V4.0 User Guide (Wirt 2021, LP DAAC), sec. 2.8 describes the upgrade as ‘Riegl VQ 480i Dual Scanning LiDAR’. The user guide does not publish the tilt angle; ±7° is a representative value used here, parametrically exposed for override. Combined swath and density numbers in the v.2 specs page (387 m at 335 m AGL, ~12 pts/m²) are consistent with forward/backward pitch tilt of the same cross-track scan rather than cross-track FOV extension.

Sampling systems

Event-based sampling (single release per drop, drifts to splash through wind), not a swath geometry. The DropsondeSystem class and helpers are documented on the dedicated Dropsondes page.

Profiling lidars

Nadir-pointing single-beam atmospheric profilers (no cross-track swath): ProfilingLidar base class plus three pre-configured instruments — HSRL2, HALO, and CPL. Detailed signatures and references are documented on the dedicated Profiling Lidars page.

Doppler wind lidar

AerosolWindProfiler is a dual-line-of-sight profiler for vector wind retrieval. Detailed signature and planning helpers are documented on the dedicated AWP Profiling page.

Radar

class SidelookingRadar[source]

Bases: Sensor

Represents a side-looking Synthetic Aperture Radar (SAR).

Models the slant-range geometry where the swath is offset from nadir, defined by near-range and far-range incidence angles. Supports stripmap SAR instruments like UAVSAR.

The swath lies entirely on one side of the flight track (typically left).

__init__(name, frequency, bandwidth, near_range_angle, far_range_angle, azimuth_resolution, polarization, look_direction='left', peak_power=None, antenna_length=None)[source]
Parameters:
property wavelength: Quantity

Radar wavelength derived from frequency.

property range_resolution: Quantity

c / (2 * B).

Type:

Slant-range resolution from bandwidth

property half_angle: float

Effective half-angle for swath calculations.

For a side-looking radar this is the angular extent from the swath center to either edge, i.e. half the angular swath width.

property swath_center_angle: float

Incidence angle at swath center (degrees from nadir).

swath_width(altitude_agl)[source]

Ground swath width for a given altitude AGL.

Computed from the difference in ground range at near and far incidence angles.

Parameters:

altitude_agl (Quantity) – Flight altitude above ground level.

Return type:

Quantity

Returns:

Swath width in meters.

near_range_ground_distance(altitude_agl)[source]

Ground distance from nadir to near edge of swath.

Return type:

Quantity

Parameters:

altitude_agl (Quantity)

far_range_ground_distance(altitude_agl)[source]

Ground distance from nadir to far edge of swath.

Return type:

Quantity

Parameters:

altitude_agl (Quantity)

ground_range_resolution(altitude_agl, incidence_angle=None)[source]

Ground-range resolution at a given incidence angle.

ground_range_res = slant_range_res / sin(incidence_angle)

Parameters:
  • altitude_agl (Quantity) – Flight altitude above ground level.

  • incidence_angle (float | None) – Incidence angle in degrees. Defaults to swath center.

Return type:

Quantity

Returns:

Ground-range resolution in meters.

ground_sample_distance(altitude_agl)[source]

Ground sample distance at near range, center, and far range.

Parameters:

altitude_agl (Quantity) – Flight altitude above ground level.

Return type:

dict[Any, Any]

Returns:

Dict with ‘near_range’, ‘center’, ‘far_range’ ground-range resolutions and ‘azimuth’ resolution.

slant_range(altitude_agl, incidence_angle=None)[source]

Slant range distance to target at given incidence angle.

Parameters:
  • altitude_agl (Quantity) – Flight altitude above ground level.

  • incidence_angle (float | None) – Incidence angle in degrees. Defaults to swath center.

Return type:

Quantity

Returns:

Slant range in meters.

swath_offset_angles()[source]

Return the (port_angle, starboard_angle) for swath polygon generation.

For a left-looking radar, the swath is on the left (port) side. For a right-looking radar, the swath is on the right (starboard) side.

Return type:

tuple[Any, ...]

Returns:

Tuple of (near_side_angle, far_side_angle) where the sign convention matches swath.py: port = left of track, starboard = right of track.

interferometric_line_spacing(altitude_agl, overlap_fraction=0.0)[source]

Compute the required spacing between parallel flight lines for interferometric or mosaicking coverage.

Parameters:
  • altitude_agl (Quantity) – Flight altitude above ground level.

  • overlap_fraction (float) – Fraction of swath overlap (0.0 = edge-to-edge, 0.5 = 50% overlap). For InSAR mosaics, typically 0.1-0.2.

Return type:

Quantity

Returns:

Line spacing in meters (center-to-center).

class UAVSAR_Lband[source]

Bases: SidelookingRadar

NASA/JPL UAVSAR L-band fully polarimetric SAR.

Platform: Gulfstream III (C-20A) Typical altitude: ~12,500 m (41,000 ft)

__init__()[source]
Return type:

None

class UAVSAR_Pband[source]

Bases: SidelookingRadar

NASA/JPL UAVSAR P-band SAR (AirMOSS configuration).

Platform: Gulfstream III (C-20A) Typical altitude: ~12,500 m (41,000 ft)

__init__()[source]
Return type:

None

class UAVSAR_Kaband[source]

Bases: SidelookingRadar

NASA/JPL GLISTIN-A Ka-band single-pass cross-track interferometric SAR.

Platform: Gulfstream III (C-20A) Typical altitude: ~12,500 m (41,000 ft)

__init__()[source]
Return type:

None

class RadarExclusionConflict[source]

Bases: object

A detected conflict between a UAVSAR swath and an FAA L-Band radar exclusion zone.

Variables:
  • radar_name – Name of the FAA radar site.

  • swath_index – Index of the conflicting swath in the input list.

  • intersection – Shapely geometry of the overlap between the swath and exclusion zone.

  • exclusion_zone – Shapely Polygon of the full exclusion zone boundary.

radar_name: str
swath_index: int
intersection: object
exclusion_zone: Polygon
__init__(radar_name, swath_index, intersection, exclusion_zone)
Parameters:
Return type:

None

check_lband_radar_exclusions(swath_polygons, geojson=None)[source]

Check UAVSAR swath polygons against FAA L-Band radar exclusion zones.

UAVSAR L-Band swaths must remain outside a 10 nautical mile radius of each FAA long-range L-Band radar site. The exclusion zone polygons are pre-computed 10 NMI circles stored in a GeoJSON FeatureCollection.

Parameters:
  • swath_polygons (Polygon | list[Polygon]) – A single Shapely Polygon or a list of Shapely Polygons representing UAVSAR swath footprints (e.g. from generate_swath_polygon()).

  • geojson (str | dict[Any, Any] | None) –

    Exclusion zone data. One of:

    • None — load the bundled hyplan/data/faa_radar_exclusion_zones.geojson file.

    • str — path to a GeoJSON FeatureCollection file on disk.

    • dict — an already-parsed GeoJSON FeatureCollection.

Return type:

list[RadarExclusionConflict]

Returns:

List of RadarExclusionConflict, one for each swath/zone pair that intersects. An empty list means no conflicts.

Raises:
  • FileNotFoundError – If geojson is None and the bundled data file does not exist, or if a path string is given that does not exist.

  • HyPlanValueError – If the GeoJSON is not a valid FeatureCollection.

Frame camera

For nadir survey planning the relevant frame-rate limit is max_ground_speed_for_overlap() — the maximum ground speed that maintains a requested forward overlap at the configured frame rate. critical_ground_speed() reports the (typically much tighter, rarely-binding) one-pixel-motion-per-frame ground speed and is retained for backward compatibility.

class FrameCamera[source]

Bases: Sensor

A frame (area-array) camera sensor.

Unlike line scanners, frame cameras capture a full 2D image per frame. The footprint is determined by both horizontal and vertical fields of view.

Parameters:
  • name (str) – Sensor name.

  • sensor_width (Quantity) – Physical sensor width in mm.

  • sensor_height (Quantity) – Physical sensor height in mm.

  • focal_length (Quantity) – Lens focal length in mm.

  • resolution_x (int) – Number of pixels across-track (horizontal).

  • resolution_y (int) – Number of pixels along-track (vertical).

  • frame_rate (Quantity) – Frame acquisition rate in Hz.

  • f_speed (float) – Lens f-number (focal length / aperture diameter).

__init__(name, sensor_width, sensor_height, focal_length, resolution_x, resolution_y, frame_rate, f_speed, tilt_angle=0.0, tilt_direction=0.0, integration_time=None)[source]
Parameters:
property ifov_x: float

Instantaneous field of view per pixel across-track (microradians).

property ifov_y: float

Instantaneous field of view per pixel along-track (microradians).

property fov_x: float

Calculate horizontal Field of View (FoV) in degrees.

property fov_y: float

Calculate vertical Field of View (FoV) in degrees.

ground_sample_distance(altitude_agl)[source]

Calculate the ground sample distance (GSD) for a given altitude AGL.

For nadir cameras, returns x (across-track) and y (along-track) GSD. For tilted cameras, additionally returns y_near and y_far showing GSD variation across the frame.

Parameters:

altitude_agl (Quantity) – Altitude above ground level in meters.

Returns:

Ground sample distances in meters.

Return type:

Dict[str, Quantity]

altitude_agl_for_ground_sample_distance(gsd_x, gsd_y)[source]

Calculate the required altitude AGL for a given ground sample distance (GSD) at nadir.

Each GSD is treated as a not-to-exceed requirement: the returned altitude is the highest at which both axes satisfy their requested GSD (the binding axis is met exactly; the other is finer than requested).

Parameters:
  • gsd_x (Quantity) – Desired ground sample distance in meters along the x-axis (across-track).

  • gsd_y (Quantity) – Desired ground sample distance in meters along the y-axis (along-track).

Returns:

The required altitude AGL in meters.

Return type:

Quantity

footprint_at(altitude_agl)[source]

Calculate the footprint dimensions (m) for a given altitude AGL.

For nadir cameras (tilt_angle == 0), returns width and height. For tilted cameras, additionally returns height_near and height_far (distances from the principal point to the near/far edges along the tilt axis).

Return type:

dict[str, Quantity]

Parameters:

altitude_agl (Quantity)

swath_width(altitude_agl)[source]

Across-track swath width at a given altitude AGL.

This is the width component of footprint_at(), provided for API compatibility with LineScanner and flight_box.box_around_center_line().

Return type:

Quantity

Parameters:

altitude_agl (Quantity)

image_scale(altitude_agl)[source]

Image scale denominator (1:N) at a given altitude AGL.

Parameters:

altitude_agl (Quantity) – Altitude above ground level.

Returns:

Return type:

Scale denominator N such that the image scale is 1

altitude_for_scale(scale_denominator)[source]

Altitude AGL required for a given image scale (1:N).

Parameters:

scale_denominator (float) – The denominator N of the desired image scale.

Return type:

Quantity

Returns:

Required altitude AGL in meters.

focal_length_for_gsd(altitude_agl, target_gsd)[source]

Required focal length for a target GSD at a given altitude.

Useful for zoom lenses where the focal length can be adjusted.

Parameters:
  • altitude_agl (Quantity) – Altitude above ground level.

  • target_gsd (Quantity) – Desired ground sample distance.

Return type:

Quantity

Returns:

Required focal length in mm.

line_spacing(altitude_agl, sidelap_pct=60.0)[source]

Flight line spacing from sidelap percentage.

Parameters:
  • altitude_agl (Quantity) – Altitude above ground level.

  • sidelap_pct (float) – Desired sidelap between adjacent flight lines (0-100).

Return type:

Quantity

Returns:

Center-to-center distance between parallel flight lines in meters.

trigger_distance(altitude_agl, overlap_pct=80.0)[source]

Along-track distance between camera exposures from overlap percentage.

Parameters:
  • altitude_agl (Quantity) – Altitude above ground level.

  • overlap_pct (float) – Desired forward overlap between successive images, in percent. Must satisfy 0 <= overlap_pct < 100.

Return type:

Quantity

Returns:

Distance between exposure centers in meters.

Raises:

HyPlanValueError – when overlap_pct is outside [0, 100).

trigger_interval(altitude_agl, ground_speed, overlap_pct=80.0)[source]

Time between camera triggers for a given speed and overlap.

Parameters:
  • altitude_agl (Quantity) – Altitude above ground level.

  • ground_speed (Quantity) – Aircraft ground speed.

  • overlap_pct (float) – Desired forward overlap between successive images (0-100).

Return type:

Quantity

Returns:

Time interval between triggers in seconds.

coverage_buffer(altitude_agl, overlap_pct=80.0, n_frames=4)[source]

Extra distance beyond AOI boundary to ensure full edge coverage.

Camera triggering should begin this distance before the AOI entry and continue this distance past the AOI exit.

Parameters:
  • altitude_agl (Quantity) – Altitude above ground level.

  • overlap_pct (float) – Desired forward overlap between successive images (0-100).

  • n_frames (int) – Number of extra frames beyond the boundary (default 4).

Return type:

Quantity

Returns:

Buffer distance in meters.

critical_ground_speed(altitude_agl)[source]

Ground speed corresponding to one along-track pixel of motion per frame.

This is a pixel-rate limit (one GSD_y of ground motion per exposure interval), not a survey-overlap planning limit. For most operational frame-camera surveys the binding constraint is the requested forward overlap at the configured frame rate — use max_ground_speed_for_overlap() for that.

Parameters:

altitude_agl (Quantity) – Altitude above ground level.

Return type:

Quantity

Returns:

Ground speed in m/s at which one along-track GSD is traversed per frame period.

max_ground_speed_for_overlap(altitude_agl, overlap_pct=80.0)[source]

Maximum ground speed that maintains the requested forward overlap.

Uses the along-track footprint and frame rate:

speed_max = trigger_distance(altitude_agl, overlap_pct) * frame_rate

This is the operational frame-rate limit for nadir frame-camera survey planning — distinct from critical_ground_speed(), which reports one-pixel-per-frame motion (a much tighter, rarely-binding limit).

Parameters:
  • altitude_agl (Quantity) – Altitude above ground level.

  • overlap_pct (float) – Desired forward overlap between successive images, in percent. Must satisfy 0 <= overlap_pct < 100.

Return type:

Quantity

Returns:

Maximum allowable ground speed in m/s.

Raises:

HyPlanValueError – when overlap_pct is outside [0, 100).

motion_blur_at(altitude_agl, ground_speed)[source]

Along-track motion blur during a single exposure.

Reports the ground distance the platform traverses while the shutter is open (ground_speed × integration_time) and normalises that length to the along-track GSD so the answer is interpretable in units of “pixels of smear per exposure”.

Parameters:
  • altitude_agl (Quantity) – Altitude above ground level.

  • ground_speed (Quantity) – Platform ground speed.

Return type:

dict[str, Quantity | float]

Returns:

{"length": Quantity (m), "pixels": float}length is the along-track ground motion during integration; pixels is length / GSD_y.

Raises:

HyPlanValueError – when integration_time was not set on the camera.

max_ground_speed_for_motion_blur(altitude_agl, max_blur_pixels=1.0)[source]

Maximum ground speed at which motion blur stays under a threshold.

The threshold is expressed in along-track GSD units — max_blur_pixels=1.0 means the blur length equals one along-track GSD; 0.5 is the half-pixel-blur photometry rule of thumb; 3.0 is a generous bound for feature-detection workflows.

Parameters:
  • altitude_agl (Quantity) – Altitude above ground level.

  • max_blur_pixels (float) – Allowed motion-blur length expressed in along-track GSDs (default 1.0 — one-pixel smear). Must be positive.

Return type:

Quantity

Returns:

Maximum ground speed in m/s.

Raises:

HyPlanValueError – when integration_time was not set, or max_blur_pixels is non-positive.

base_height_ratio(altitude_agl, overlap_pct=80.0)[source]

Base-to-height ratio for stereo photogrammetry.

B/H is the ratio of the baseline (distance between successive exposure centres) to the flying height. Larger values give better vertical accuracy but more occlusion.

Parameters:
  • altitude_agl (Quantity) – Altitude above ground level.

  • overlap_pct (float) – Forward overlap between successive images (0-100).

Return type:

float

Returns:

Dimensionless B/H ratio.

vertical_accuracy(altitude_agl, overlap_pct=80.0, sigma_parallax=0.5)[source]

Estimated vertical accuracy from stereo overlap.

Uses the photogrammetric relation σ_z = (H / B) × σ_p × GSD_y where σ_p is the parallax measurement error in pixels.

Parameters:
  • altitude_agl (Quantity) – Altitude above ground level.

  • overlap_pct (float) – Forward overlap (0-100).

  • sigma_parallax (float) – Parallax measurement error in pixels (default 0.5).

Return type:

Quantity

Returns:

Vertical accuracy (σ_z) in meters.

range_accuracy(altitude_agl, baseline, sigma_q=None)[source]

Stereo range accuracy using the range-error formula.

σ_R = × σ_q / B where R is the slant range from the camera to the ground, B is the baseline, and σ_q is the angular measurement uncertainty in radians. This is the model used in Donnellan et al. (2025) for QUAKES-I.

Parameters:
  • altitude_agl (Quantity) – Altitude above ground level.

  • baseline (Quantity) – Distance between stereo exposure centres.

  • sigma_q (float | None) – Angular uncertainty in radians. If None, derived from ifov_y / 3 (sub-pixel matching at ⅓ pixel).

Return type:

Quantity

Returns:

Range accuracy (σ_R) in meters.

ground_footprint(altitude_agl, cross_track_offset=0.0, *, edge_points=10, lat=None, lon=None, altitude_msl=None, heading=0.0, dem_file=None)[source]

Project the sensor perimeter onto the ground as a Shapely Polygon.

Points are distributed along each sensor edge (controlled by edge_points) so the polygon faithfully represents footprint curvature, especially over terrain.

Operates in two modes:

Flat-ground mode (default): When lat, lon, and altitude_msl are all None. Returns a 3-D Shapely Polygon with coordinates (x_cross, y_along, 0) in meters, origin at the nadir point.

Terrain mode: When lat, lon, and altitude_msl are provided. Rays are intersected with the DEM via ray_terrain_intersection(). Returns a 3-D Shapely Polygon with coordinates (lon, lat, elevation). The altitude_agl parameter is ignored in this mode.

Parameters:
  • altitude_agl (Quantity) – Altitude above ground level (used in flat mode).

  • cross_track_offset (float) – Additional cross-track angular offset in degrees (e.g. for cameras in a multi-camera rig).

  • edge_points (int) – Number of points per sensor edge (default 10). Use 2 for a simple 4-corner quadrilateral.

  • lat (float | None) – Camera latitude in degrees (terrain mode).

  • lon (float | None) – Camera longitude in degrees (terrain mode).

  • altitude_msl (float | None) – Camera altitude MSL in meters (terrain mode).

  • heading (float) – Aircraft heading in degrees from north (terrain mode).

  • dem_file (str | None) – Path to DEM file. If None and terrain mode is active, a DEM is downloaded automatically.

Return type:

Polygon

Returns:

A shapely.geometry.Polygon with 3-D coordinates.

footprint_polygon_at(lat, lon, altitude_agl, *, heading=0.0, cross_track_offset=0.0, edge_points=4)[source]

Flat-earth ground footprint Polygon at a specified geodetic centre.

Convenience wrapper that returns a 2-D Shapely Polygon in (lon, lat) coordinates without DEM access — useful for notebooks and survey-plan visualisation where the synthetic flight line shouldn’t depend on terrain data.

Internally builds the flat-mode local-meter polygon via ground_footprint() (which already handles tilt, cross- track offset, and edge densification) and projects each vertex to lat/lon via Vincenty.

Parameters:
  • lat (float) – Camera latitude in degrees.

  • lon (float) – Camera longitude in degrees.

  • altitude_agl (Quantity) – Altitude above ground level.

  • heading (float) – Aircraft heading in degrees from north (default 0 = north).

  • cross_track_offset (float) – Additional cross-track angular offset in degrees (e.g. for cameras in a multi-camera rig).

  • edge_points (int) – Number of points per sensor edge (default 4 = quadrilateral).

Return type:

Polygon

Returns:

A shapely.geometry.Polygon with 2-D (lon, lat) coordinates (no elevation field).

ground_footprint_corners(*args, **kwargs)[source]

Deprecated — use ground_footprint() instead.

Return type:

Polygon

Parameters:
static footprint_corners(lat, lon, altitude_msl, fov_x, fov_y, dem_file, tilt_angle=0.0, tilt_direction=0.0, heading=0.0)[source]

Calculate terrain-intersected footprint corners.

Deprecated since version Use: the instance method ground_footprint() with lat, lon, and altitude_msl keyword arguments instead. It uses the camera’s own FOV and tilt parameters automatically.

Return type:

list[tuple[float, float, float]]

Parameters:
class MultiCameraRig[source]

Bases: Sensor

A rig of multiple FrameCamera instances with known orientations.

Each camera carries its own tilt_angle and tilt_direction. The rig stores an optional angular cross-track offset and a longitudinal mount offset for each camera.

Parameters:
  • name (str) – Rig name.

  • cameras (list[dict[str, Any]]) – List of dicts, each with keys: "camera" (FrameCamera), "label" (str), "cross_track_offset" (Quantity, angular cross-track offset in degrees, default 0°), "dy" (Quantity, longitudinal offset, default 0 m).

__init__(name, cameras)[source]
Parameters:
Return type:

None

swath_width(altitude_agl)[source]

Combined across-track swath width (union of all cameras).

swath = altitude × (tan(starboard_edge) - tan(port_edge)) where the edges are the outermost cross-track view angles across all cameras (mount offset ± half cross-track FOV).

Return type:

Quantity

Parameters:

altitude_agl (Quantity)

ground_sample_distance(altitude_agl)[source]

Finest GSD across all cameras.

Return type:

dict[str, Quantity]

Parameters:

altitude_agl (Quantity)

combined_footprints(altitude_agl)[source]

Per-camera footprint dicts with labels.

Return type:

list[dict[str, Any]]

Parameters:

altitude_agl (Quantity)

ground_footprint(altitude_agl, *, edge_points=10, lat=None, lon=None, altitude_msl=None, heading=0.0, dem_file=None)[source]

Project each camera’s sensor perimeter onto the ground.

Uses each camera’s tilt geometry and the cross_track_offset angular offset stored in the rig layout.

Return type:

list[dict[str, Any]]

Returns:

List of dicts with "label" (str) and "polygon" (shapely.geometry.Polygon).

Parameters:
ground_footprint_corners(*args, **kwargs)[source]

Deprecated — use ground_footprint() instead.

Return type:

list[dict[str, Any]]

Parameters:
stereo_pairs()[source]

Find camera pairs with opposing tilt directions (~180° apart).

Returns a list of (forward_entry, aft_entry) tuples.

Return type:

list[tuple[dict[str, Any], dict[str, Any]]]

composite_base_height_ratio(altitude_agl)[source]

B/H ratio for each stereo pair.

For convergent stereo, B/H = tan(θ_fwd) + tan(θ_aft) where θ is the tilt angle of each camera.

Return type:

list[dict[str, Any]]

Returns:

List of dicts with "pair" (labels) and "bh_ratio".

Parameters:

altitude_agl (Quantity)

line_spacing(altitude_agl, sidelap_pct=60.0)[source]

Flight line spacing from sidelap and combined swath width.

Return type:

Quantity

Parameters:
classmethod quakes_i()[source]

Create a QUAKES-I multi-camera rig.

Based on Donnellan et al. (2025), Earth and Space Science. The camera hardware is the same regardless of aircraft platform (Gulfstream V at 12.5 km / 250 m/s, or King Air at 6 km / 125 m/s). The platform choice only affects operational parameters like the altitude passed to swath_width() etc.

Return type:

MultiCameraRig

Returns:

A MultiCameraRig with 8 cameras (4 forward + 4 aft).

GLIHT_HRAC

A frame (area-array) camera sensor.

Unlike line scanners, frame cameras capture a full 2D image per frame. The footprint is determined by both horizontal and vertical fields of view.

Parameters:
  • name (str) – Sensor name.

  • sensor_width (Quantity) – Physical sensor width in mm.

  • sensor_height (Quantity) – Physical sensor height in mm.

  • focal_length (Quantity) – Lens focal length in mm.

  • resolution_x (int) – Number of pixels across-track (horizontal).

  • resolution_y (int) – Number of pixels along-track (vertical).

  • frame_rate (Quantity) – Frame acquisition rate in Hz.

  • f_speed (float) – Lens f-number (focal length / aperture diameter).

GLIHT_THERMAL

A frame (area-array) camera sensor.

Unlike line scanners, frame cameras capture a full 2D image per frame. The footprint is determined by both horizontal and vertical fields of view.

Parameters:
  • name (str) – Sensor name.

  • sensor_width (Quantity) – Physical sensor width in mm.

  • sensor_height (Quantity) – Physical sensor height in mm.

  • focal_length (Quantity) – Lens focal length in mm.

  • resolution_x (int) – Number of pixels across-track (horizontal).

  • resolution_y (int) – Number of pixels along-track (vertical).

  • frame_rate (Quantity) – Frame acquisition rate in Hz.

  • f_speed (float) – Lens f-number (focal length / aperture diameter).

Factory function

create_sensor(sensor_type)[source]

Construct a sensor by registered name.

Parameters:

sensor_type (str) – Canonical name or alias registered via register_sensor(). Look-up is case- and whitespace-sensitive.

Return type:

Sensor

Returns:

A Sensor instance. Singleton-backed names return the same shared object on every call; class-backed names return a fresh instance each call.

Raises:

HyPlanValueError – If sensor_type is not registered.