Skip to content

Optical imaging access

Lox models passive (electro-optical) payloads via OpticalPayload and OpticalAccessAnalysis. The payload declares a nadir-centred swath width and an optional off-nadir pointing capability; the analysis computes per-AOI access windows by scanning the circular ground footprint under the satellite.

Sensor models

Nadir-only

For sensors that image straight down (e.g. Sentinel-2), the accessible ground range equals half the swath width:

import lox_space as lox

payload = lox.OpticalPayload.nadir_only(290.0 * lox.km)

Off-nadir pointing

For satellites that can slew away from nadir to image targets, the accessible range is the sum of the off-nadir ground range and half the swath width:

payload = lox.OpticalPayload.off_nadir(
    swath_width=20.0 * lox.km,
    max_off_nadir=30.0 * lox.deg,
)

The off-nadir ground range is computed geometrically from the spacecraft altitude, the maximum off-nadir angle, and the body's mean radius.

Attaching payloads to spacecraft

Pass the payload via the optical_payload keyword argument when constructing a Spacecraft. Spacecraft without a payload are silently skipped during analysis:

# With payload — included in optical access analysis
sc1 = lox.Spacecraft("imager", orbit, optical_payload=payload)

# Without payload — skipped
sc2 = lox.Spacecraft("relay", orbit)

scenario = lox.Scenario(t0, t1, spacecraft=[sc1, sc2])
# Only sc1 will produce imaging windows

Example: Sentinel-2 over Europe

import lox_space as lox

sentinel2a = lox.SGP4("""\
SENTINEL-2A
1 40697U 15028A   26079.19377485 -.00000072  00000+0 -11026-4 0  9994
2 40697  98.5642 155.3327 0001269  98.1407 261.9920 14.30816376561005""")

payload = lox.OpticalPayload.nadir_only(290.0 * lox.km)
sc = lox.Spacecraft("S2A", sentinel2a, optical_payload=payload)

t0 = sentinel2a.time()
t1 = t0 + 6 * lox.hours
scenario = lox.Scenario(t0, t1, spacecraft=[sc])

europe = lox.Aoi(
    [(-10.0, 35.0), (20.0, 35.0), (20.0, 60.0), (-10.0, 60.0), (-10.0, 35.0)]
)

analysis = lox.OpticalAccessAnalysis(
    scenario,
    aois=[("europe", europe)],
    step=30 * lox.seconds,
)
results = analysis.compute()

for window in results.windows("S2A", "europe"):
    iv = window.interval()
    print(f"{iv.start()}{iv.end()}  ({float(iv.duration()):.0f}s)")
    print(window.direction())

OpticalPayload

Optical sensor payload describing a spacecraft's ground coverage capability.

Defines the sensor's swath width and optional off-nadir pointing capability. Assign to a spacecraft via the optical_payload parameter.

Examples:

>>> payload = lox.OpticalPayload.nadir_only(20.0 * lox.km)
>>> payload = lox.OpticalPayload.off_nadir(20.0 * lox.km, 30.0 * lox.deg)
>>> sc = lox.Spacecraft("sat1", orbit, optical_payload=payload)

Methods:

  • nadir_only

    Create a payload for a nadir-only sensor.

  • off_nadir

    Create a payload for a sensor with off-nadir pointing capability.

nadir_only classmethod

nadir_only(swath_width: Distance) -> Self

Create a payload for a nadir-only sensor.

Parameters:

  • swath_width

    (Distance) –

    Full swath width as Distance.

off_nadir classmethod

Create a payload for a sensor with off-nadir pointing capability.

Parameters:

  • swath_width

    (Distance) –

    Full swath width as Distance.

  • max_off_nadir

    (Angle) –

    Maximum off-nadir angle as Angle.


OpticalAccessAnalysis

AOI optical access analysis: computes imaging windows for spacecraft over AOIs.

Optical payloads are read from each spacecraft; spacecraft without a payload are silently skipped.

Parameters:

  • scenario

    Scenario containing spacecraft with optical_payload.

  • aois

    List of (id, Aoi) tuples defining the areas of interest.

  • ensemble

    Optional pre-computed Ensemble.

  • step

    Optional time step for event detection (default: 60s).

  • body_fixed_frame

    Optional body-fixed frame override.

Examples:

>>> payload = lox.OpticalPayload.off_nadir(20.0 * lox.km, 30.0 * lox.deg)
>>> sc = lox.Spacecraft("sat1", orbit, optical_payload=payload)
>>> scenario = lox.Scenario(t0, t1, spacecraft=[sc])
>>> aoi = lox.Aoi.from_geojson('{"type":"Polygon","coordinates":[[[10,45],[11,45],[11,46],[10,46],[10,45]]]}')
>>> analysis = lox.OpticalAccessAnalysis(scenario, aois=[("rome", aoi)])
>>> results = analysis.compute()

Methods:

  • compute

    Compute access intervals for all (spacecraft, AOI) pairs.

compute

compute() -> AccessResults

Compute access intervals for all (spacecraft, AOI) pairs.

If no ensemble was provided, the scenario is propagated automatically. Spacecraft without an optical payload are skipped.