Coverage Analysis

Coverage class

class missiontools.Coverage(aoi, sensors, *, el_min_deg=0.0, sza_max_deg=None, sza_min_deg=None)[source]

Bases: object

Coverage analysis for an area of interest observed by one or more sensors.

Each sensor must be attached to a Spacecraft via add_sensor() before constructing a Coverage object. The spacecraft orbit and propagator are derived automatically from each sensor’s back-reference.

Combined visibility at each timestep is the union of all sensor FOVs. The SZA constraint (if any) is applied globally after the union — it represents an illumination or link condition on the ground, not on any individual sensor.

Parameters:
  • aoi (AoI) – Area of interest containing ground sample points.

  • sensors (sequence of AbstractSensor) – One or more AbstractSensor instances (list, tuple, or any iterable). All sensors must be attached to a spacecraft. Sensors may belong to the same spacecraft (multi-FOV) or to different spacecraft (constellation).

  • el_min_deg (float, optional) – Minimum ground elevation angle (degrees) for a ground point to be considered in view. Default 0 (horizon).

  • sza_max_deg (float | None, optional) – Maximum solar zenith angle (degrees) for access (daytime constraint). None disables the constraint.

  • sza_min_deg (float | None, optional) – Minimum solar zenith angle (degrees) for access (nighttime constraint). None disables the constraint.

Parameters:

Notes

All five coverage methods evaluate each sensor’s LVLH boresight at t_start and assume it is constant in the LVLH frame for the full analysis window. This is exact for sensors with fixed-LVLH pointing (nadir, fixed tilt, body-mounted on a nadir spacecraft) and an approximation for time-varying pointing modes.

Examples

Single sensor:

import numpy as np
from missiontools import Spacecraft, ConicSensor, AoI, Coverage

sc     = Spacecraft(a=6_771_000., e=0., i=np.radians(51.6),
                    raan=0., arg_p=0., ma=0.,
                    epoch=np.datetime64('2025-01-01', 'us'))
sensor = ConicSensor(30.0, body_vector=[0, 0, 1])
sc.add_sensor(sensor)

aoi = AoI.from_region(-60, 60, -180, 180)
cov = Coverage(aoi, [sensor], el_min_deg=5.0)

result = cov.coverage_fraction(
    np.datetime64('2025-01-01', 'us'),
    np.datetime64('2025-01-02', 'us'),
)
print(result['final_cumulative'])

Constellation (two spacecraft at different RAANs):

sc2    = Spacecraft(a=6_771_000., e=0., i=np.radians(51.6),
                    raan=np.radians(90.), arg_p=0., ma=0.,
                    epoch=np.datetime64('2025-01-01', 'us'))
s2     = ConicSensor(30.0, body_vector=[0, 0, 1])
sc2.add_sensor(s2)

cov2 = Coverage(aoi, [sensor, s2], el_min_deg=5.0)
result2 = cov2.coverage_fraction(
    np.datetime64('2025-01-01', 'us'),
    np.datetime64('2025-01-02', 'us'),
)
print(result2['final_cumulative'])   # ≥ single-satellite result
property aoi: AoI

Area of interest.

property sensors: list

Attached sensors (read-only copy).

coverage_fraction(t_start, t_end, *, alt=0.0, max_step=np.timedelta64(30, 's'), batch_size=1000)[source]

Instantaneous and cumulative coverage fraction over time.

Parameters:
  • t_start (np.datetime64) – Start of the analysis window.

  • t_end (np.datetime64) – End of the analysis window (inclusive).

  • alt (float, optional) – Altitude of ground targets above WGS84 (m). Default 0.

  • max_step (np.timedelta64, optional) – Maximum propagation step (default 30 s).

  • batch_size (int, optional) – Propagation batch size (default 1000).

Returns:

dict't', 'fraction', 'cumulative', 'mean_fraction', 'final_cumulative'. See coverage_fraction().

Parameters:
Return type:

dict

revisit_time(t_start, t_end, *, alt=0.0, max_step=np.timedelta64(30, 's'), batch_size=1000)[source]

Per-point revisit statistics.

Parameters:
  • t_start (np.datetime64) – Start of the analysis window.

  • t_end (np.datetime64) – End of the analysis window (inclusive).

  • alt (float, optional) – Altitude of ground targets above WGS84 (m). Default 0.

  • max_step (np.timedelta64, optional) – Maximum propagation step (default 30 s).

  • batch_size (int, optional) – Propagation batch size (default 1000).

Returns:

dict'max_revisit' : (M,) float — maximum gap between consecutive accesses for each ground point (s). nan for points with fewer than two accesses.

'mean_revisit' : (M,) float — mean gap (s). nan for points with fewer than two accesses.

'global_max' : float — maximum value across all points (s). nan if no point has two or more accesses.

'global_mean' : float — mean of per-point mean revisit times (s). nan if no point has two or more accesses.

Parameters:
Return type:

dict

pointwise_coverage(t_start, t_end, *, alt=0.0, max_step=np.timedelta64(30, 's'), batch_size=1000)[source]

Raw per-timestep visibility matrix.

Parameters:
  • t_start (np.datetime64) – Start of the analysis window.

  • t_end (np.datetime64) – End of the analysis window (inclusive).

  • alt (float, optional) – Altitude of ground targets above WGS84 (m). Default 0.

  • max_step (np.timedelta64, optional) – Maximum propagation step (default 30 s).

  • batch_size (int, optional) – Propagation batch size (default 1000).

Returns:

dict't', 'lat', 'lon', 'alt', 'visible'. See pointwise_coverage().

Parameters:
Return type:

dict

access_pointwise(t_start, t_end, *, alt=0.0, max_step=np.timedelta64(30, 's'), batch_size=1000)[source]

Per-point access intervals (AOS / LOS pairs).

Parameters:
  • t_start (np.datetime64) – Start of the analysis window.

  • t_end (np.datetime64) – End of the analysis window (inclusive).

  • alt (float, optional) – Altitude of ground targets above WGS84 (m). Default 0.

  • max_step (np.timedelta64, optional) – Maximum propagation step (default 30 s).

  • batch_size (int, optional) – Propagation batch size (default 1000).

Returns:

list[list[tuple[np.datetime64, np.datetime64]]]result[m] is a list of (AOS, LOS) pairs for ground point m. See access_pointwise().

Parameters:
Return type:

list

revisit_pointwise(t_start, t_end, *, alt=0.0, max_step=np.timedelta64(30, 's'), batch_size=1000)[source]

Per-point revisit gap arrays.

Parameters:
  • t_start (np.datetime64) – Start of the analysis window.

  • t_end (np.datetime64) – End of the analysis window (inclusive).

  • alt (float, optional) – Altitude of ground targets above WGS84 (m). Default 0.

  • max_step (np.timedelta64, optional) – Maximum propagation step (default 30 s).

  • batch_size (int, optional) – Propagation batch size (default 1000).

Returns:

list[np.ndarray of timedelta64]result[m] is an array of LOS-to-AOS gap durations for ground point m. See revisit_pointwise().

Parameters:
Return type:

list

Functional API

missiontools.coverage

Coverage and access analysis, and geographic area sampling.

missiontools.coverage.sample_aoi(polygon, n)[source]
Parameters:
Return type:

tuple[ndarray[tuple[Any, …], dtype[floating]], ndarray[tuple[Any, …], dtype[floating]]]

missiontools.coverage.sample_region(lat_min=None, lat_max=None, lon_min=None, lon_max=None, point_density=100000.0)[source]
Parameters:
Return type:

tuple[ndarray[tuple[Any, …], dtype[floating]], ndarray[tuple[Any, …], dtype[floating]]]

missiontools.coverage.sample_shapefile(path, *, feature_index=None, point_density=100000.0)[source]
Parameters:
  • path (str)

  • feature_index (int | None)

  • point_density (float)

Return type:

tuple[ndarray[tuple[Any, …], dtype[floating]], ndarray[tuple[Any, …], dtype[floating]]]

missiontools.coverage.sample_geography(geography, *, point_density=100000.0)[source]
Parameters:
Return type:

tuple[ndarray[tuple[Any, …], dtype[floating]], ndarray[tuple[Any, …], dtype[floating]], object]

missiontools.coverage.coverage_fraction(lat, lon, keplerian_params, t_start, t_end, alt=0.0, el_min=0.0, propagator_type='twobody', max_step=np.timedelta64(30, 's'), batch_size=1000, fov_pointing_lvlh=None, fov_half_angle=None, sza_max=None, sza_min=None)[source]
Parameters:
Return type:

dict

missiontools.coverage.revisit_time(lat, lon, keplerian_params, t_start, t_end, alt=0.0, el_min=0.0, propagator_type='twobody', max_step=np.timedelta64(30, 's'), batch_size=1000, fov_pointing_lvlh=None, fov_half_angle=None, sza_max=None, sza_min=None)[source]
Parameters:
Return type:

dict

missiontools.coverage.pointwise_coverage(lat, lon, keplerian_params, t_start, t_end, alt=0.0, el_min=0.0, propagator_type='twobody', max_step=np.timedelta64(30, 's'), batch_size=1000, fov_pointing_lvlh=None, fov_half_angle=None, sza_max=None, sza_min=None)[source]
Parameters:
Return type:

dict

missiontools.coverage.access_pointwise(lat, lon, keplerian_params, t_start, t_end, alt=0.0, el_min=0.0, propagator_type='twobody', max_step=np.timedelta64(30, 's'), batch_size=1000, fov_pointing_lvlh=None, fov_half_angle=None, sza_max=None, sza_min=None)[source]
Parameters:
Return type:

list[list[tuple[datetime64, datetime64]]]

missiontools.coverage.revisit_pointwise(lat, lon, keplerian_params, t_start, t_end, alt=0.0, el_min=0.0, propagator_type='twobody', max_step=np.timedelta64(30, 's'), batch_size=1000, fov_pointing_lvlh=None, fov_half_angle=None, sza_max=None, sza_min=None)[source]
Parameters:
Return type:

list[ndarray[tuple[Any, …], dtype[timedelta64]]]

class missiontools.coverage.SensorSpec(keplerian_params, propagator_type, fov_type, pointing_lvlh, cos_fov, u1_lvlh, u2_lvlh, tan_theta1, tan_theta2, active_intervals)[source]

Bases: NamedTuple

Parameters:
keplerian_params: dict

Alias for field number 0

propagator_type: str

Alias for field number 1

fov_type: str

Alias for field number 2

pointing_lvlh: ndarray[tuple[Any, ...], dtype[_ScalarT]] | None

Alias for field number 3

cos_fov: float | None

Alias for field number 4

u1_lvlh: ndarray[tuple[Any, ...], dtype[_ScalarT]] | None

Alias for field number 5

u2_lvlh: ndarray[tuple[Any, ...], dtype[_ScalarT]] | None

Alias for field number 6

tan_theta1: float | None

Alias for field number 7

tan_theta2: float | None

Alias for field number 8

active_intervals: list[tuple[datetime64, datetime64]] | None

Alias for field number 9

missiontools.coverage.make_sensor_spec(keplerian_params, propagator_type, fov_pointing_lvlh, fov_half_angle)[source]
Parameters:
Return type:

SensorSpec

missiontools.coverage.make_sensor_spec_from_fov(keplerian_params, propagator_type, fov)[source]
Parameters:
  • keplerian_params (dict)

  • propagator_type (str)

  • fov (dict)

Return type:

SensorSpec

class missiontools.coverage.CoverageConstraints(el_min: 'float | None', sza_max: 'float | None', sza_min: 'float | None', sin_el_min: 'float', cos_sza_max: 'float | None', cos_sza_min: 'float | None')[source]

Bases: object

Parameters:
el_min: float | None
sza_max: float | None
sza_min: float | None
sin_el_min: float
cos_sza_max: float | None
cos_sza_min: float | None
property use_sza: bool
classmethod from_angles(el_min, sza_max=None, sza_min=None)[source]
Parameters:
Return type:

CoverageConstraints

missiontools.coverage.coverage_fraction_multi(lat, lon, sensor_specs, t_start, t_end, alt, constraints, max_step, batch_size)[source]
Parameters:
Return type:

dict

missiontools.coverage.pointwise_coverage_multi(lat, lon, sensor_specs, t_start, t_end, alt, constraints, max_step, batch_size)[source]
Parameters:
Return type:

dict

missiontools.coverage.collect_access_intervals_multi(lat, lon, sensor_specs, t_start, t_end, alt, constraints, max_step, batch_size, *, close_at_end)[source]
Parameters:
Return type:

list[list[tuple[datetime64, datetime64]]]

missiontools.coverage.load_shapefile_geometry(path, feature_index)[source]
missiontools.coverage.sample_from_geometry(geom, crosses_am, point_density)[source]
Parameters:
Return type:

tuple[ndarray[tuple[Any, …], dtype[floating]], ndarray[tuple[Any, …], dtype[floating]]]

missiontools.coverage.geography_geometry(geography)[source]
Parameters:

geography (str)

Return type:

tuple[object, bool]