Skip to content

20 - Satellite pushbroom imaging

Pushbroom imaging links the 3-D scene, the atmosphere and the instrument into one chain. Besides radiance, the results include the electrons collected by the detector, the digital numbers (DN) after analogue-to-digital conversion, and the observation position and time of every pixel. Typical uses are comparing sensor designs, analysing how images form and producing test data for algorithms.

Pushbroom imaging describes a stretch of flight track in a local planar coordinate system, which suits local scenes. If you need full orbits, Earth curvature or rigorous geometric correction, generate the observation geometry with an orbit tool first.

Three parts

Part Class Content
Observation geometry Pushbroom Altitude, heading, swath width, ground speed, line rate, scan time
Spectrum and atmosphere OpticalImager Bands, samples, observation level, adjacency effect
Instrument Instrument Aperture, integration time, optical transmission, quantum efficiency, modulation transfer function, noise, digitisation

Example

import less

scene = less.Scene()
scene.size = 1000.0
scene.terrain = less.Terrain(property=less.Lambertian(reflectance=0.20))
scene.illumination = less.Illumination(
    source=less.Sun(zenith=30, azimuth=150),
    atmosphere=less.Atmosphere.standard(
        "midlatitude_summer", aerosol="continental", aot550=0.10, sky_mode="anisotropic"),
)

projection = less.Pushbroom(
    resolution=64,        # pixels per scan line
    num_lines=64,         # scan lines along track
    altitude=700000,      # altitude (m)
    swath_width=1000,     # swath width (m)
    heading=180,          # heading, clockwise from north
    ground_speed=7000,    # ground speed (m/s)
    line_rate=441,        # lines per second
    start_time=0.0,
)

instrument = less.Instrument(
    aperture_diameter=0.20,     # m
    integration_time=0.001,     # s
    optical_transmission=0.70,
    quantum_efficiency=0.75,
    read_noise=4.0,             # electrons
    dark_current=20.0,          # electrons per second
    full_well=80000,            # electrons
    gain=5.0,                   # electrons per DN
    bit_depth=14,
    mtf_nyquist=0.30,
    smear=0.2,                  # along-track smear (pixels)
    seed=42,
)

sensor = less.OpticalImager(
    projection=projection,
    bands=less.Sentinel2A(),
    spectral_resolution=20,
    quality=16,
    observation_level="toa",
    adjacency_sigma=1.0,
    instrument=instrument,
)
product = scene.simulate(sensor)

observation_level="toa" uses the scene's atmosphere model to carry the radiance leaving the surface up to the top of the atmosphere before it enters the instrument.

Scan time and coverage

When both ground_speed and line_rate are given, the along-track coverage is

\[ L = v\,\frac{N_{\mathrm{line}}-1}{f_{\mathrm{line}}}, \]

where v is the ground speed, N the number of scan lines and f the line rate. You can also give the coverage directly with ground_track_length, which then takes precedence. Scan line k is recorded at start_time + k / line_rate.

altitude only sets the geometric start of the rays; where atmospheric transmission ends is set by observation_level or observation_altitude_km.

The instrument model

For each band, the radiance is converted to the number of photons reaching the detector using the effective bandwidth, entrance-pupil area, pixel solid angle, integration time and optical transmission, and multiplied by the quantum efficiency to obtain electrons. Then, in order:

  1. point spread function, modulation transfer function, jitter and along-track smear;
  2. photon shot noise, dark current and read noise;
  3. full-well clipping, gain and quantisation by the analogue-to-digital converter.

With spectral response functions the effective bandwidth is computed automatically. A fixed seed gives reproducible noise; noise=False shows the result without noise.

Results

product.source_radiance   # radiance at the entrance pupil
product.radiance          # radiance after the spatial response
product.electrons         # electrons
product.dn                # digital numbers, the same as product.data
product.geolocation       # x, y, view zenith, view azimuth and time of every pixel

product.save("satellite_product.npz")                    # all intermediate quantities
product.save("satellite_dn.tif")                         # digital numbers and geolocation
product.save("satellite_preview.png", bands=[3, 2, 1])

When the scene has map coordinates, each pixel's position also includes easting, northing and coordinate system, and the GeoTIFF records the pushbroom geometry with ground control points:

scene.georeference = {"crs": "EPSG:32650", "origin": (500000.0, 4001000.0)}   # north-west corner of the scene

The scene's y axis points south, so northing decreases southwards from the origin.

Adjacency effect

With adjacency_sigma (pixels) greater than 0, neighbouring pixel radiances are mixed according to the molecular and aerosol scattering optical thickness along the viewing path. This approximation leaves the radiance of uniform scenes unchanged and gives a quick estimate of the adjacency effect near bright–dark edges.

Built-in example

python -m less.examples satellite_pushbroom

Scope

Pushbroom imaging includes platform motion along a local track, cross-track scanning, per-pixel viewing angles, a 1-D layered atmosphere at any observation level, surface–atmosphere coupling, an adjacency approximation, spectral responses and the detector chain. It does not include orbit dynamics, Earth curvature, attitude time series, scenes that change over time, 3-D cloud radiative transfer or polarisation, so the results are not equivalent to geometrically processed operational satellite products.

Next