vista.sensors.sampled_sensor.SampledSensor

class vista.sensors.sampled_sensor.SampledSensor(name, bias_images=None, bias_image_frames=None, uniformity_gain_images=None, uniformity_gain_image_frames=None, bad_pixel_masks=None, bad_pixel_mask_frames=None, _instance_count=0, positions=None, times=None, frames=None, radiometric_gain=None, pointing=None, poly_pixel_to_arf_azimuth=None, poly_pixel_to_arf_elevation=None, poly_arf_to_row=None, poly_arf_to_col=None)[source]

Sensor implementation using sampled position data with interpolation/extrapolation.

SampledSensor stores discrete position samples at known times and provides position estimates at arbitrary times through interpolation (within the time range) or extrapolation (outside the time range). For single-position sensors, the same position is returned for all query times.

positions

Sensor positions as (3, N) array where N is the number of samples. Each column contains [x, y, z] ECEF coordinates in kilometers. Required - will raise ValueError in __post_init__ if not provided.

Type:

NDArray[np.float64]

times

Times corresponding to each position sample. Must have length N. Required - will raise ValueError in __post_init__ if not provided.

Type:

NDArray[np.datetime64]

frames

Sensor frames numbers corresponding to each time sample. Must have length N. Required - will raise ValueError in __post_init__ if not provided.

Type:

NDArray[np.int64]

radiometric_gain

1D array of multiplicative factors for each frame to convert from counts to irradiance in units of kW/km²/sr.

Type:

NDArray, optional

pointing

Sensor pointing unit vectors in ECEF coordinates. Shape: (3, num_frames). Each column is the direction the sensor is pointing for that frame.

Type:

NDArray[np.float64], optional

poly_pixel_to_arf_azimuth

Polynomial coefficients for converting (column, row) to ARF azimuth (radians). Shape: (num_frames, num_coeffs) where num_coeffs depends on polynomial order.

Type:

NDArray[np.float64], optional

poly_pixel_to_arf_elevation

Polynomial coefficients for converting (column, row) to ARF elevation (radians). Shape: (num_frames, num_coeffs) where num_coeffs depends on polynomial order.

Type:

NDArray[np.float64], optional

poly_arf_to_row

Polynomial coefficients for converting (azimuth, elevation) to row. Shape: (num_frames, num_coeffs) where num_coeffs depends on polynomial order.

Type:

NDArray[np.float64], optional

poly_arf_to_col

Polynomial coefficients for converting (azimuth, elevation) to column. Shape: (num_frames, num_coeffs) where num_coeffs depends on polynomial order.

Type:

NDArray[np.float64], optional

get_positions(times)[source]

Return interpolated/extrapolated sensor positions for given times

Notes

  • Duplicate times in the input are automatically removed during initialization

  • For 2+ unique samples: uses linear interpolation within range, linear extrapolation outside

  • For 1 sample: returns the same position for all query times (stationary sensor)

  • Positions must be (3, N) arrays with x, y, z in each column

  • All coordinates are in ECEF Cartesian frame with units of kilometers

  • ARF (Attitude Reference Frame) is a local coordinate system where the X-axis points along the sensor pointing direction

Examples

>>> import numpy as np
>>> # Create sensor with multiple position samples
>>> positions = np.array([[1000, 1100, 1200],
...                       [2000, 2100, 2200],
...                       [3000, 3100, 3200]])  # (3, 3) array
>>> times = np.array(['2024-01-01T00:00:00',
...                   '2024-01-01T00:01:00',
...                   '2024-01-01T00:02:00'], dtype='datetime64')
>>> sensor = SampledSensor(positions=positions, times=times)
>>> # Get interpolated position
>>> query_times = np.array(['2024-01-01T00:00:30'], dtype='datetime64')
>>> pos = sensor.get_positions(query_times)
>>> pos.shape
(3, 1)
>>> # Create stationary sensor with single position
>>> positions_static = np.array([[1000], [2000], [3000]])  # (3, 1) array
>>> times_static = np.array(['2024-01-01T00:00:00'], dtype='datetime64')
>>> sensor_static = SampledSensor(positions=positions_static, times=times_static)
>>> # Returns same position for any query time
>>> pos = sensor_static.get_positions(query_times)
__init__(name, bias_images=None, bias_image_frames=None, uniformity_gain_images=None, uniformity_gain_image_frames=None, bad_pixel_masks=None, bad_pixel_mask_frames=None, _instance_count=0, positions=None, times=None, frames=None, radiometric_gain=None, pointing=None, poly_pixel_to_arf_azimuth=None, poly_pixel_to_arf_elevation=None, poly_arf_to_row=None, poly_arf_to_col=None)

Methods

__init__(name[, bias_images, ...])

add_imagery(imagery)

Register imagery with this sensor and update frame/time tracking.

can_correct_bad_pixel()

Check if sensor has radiometric bad pixel masks.

can_correct_bias()

Check if sensor has bias images

can_correct_non_uniformity()

Check if sensor has uniformity gain images

can_geolocate()

Check if sensor can convert pixels to geodetic coordinates and vice versa.

geodetic_to_pixel(frame, loc)

Convert geodetic coordinates to pixel coordinates using ARF polynomials.

get_imagery_frames_and_times()

Get all unique imagery frames and corresponding times in increasing order.

get_positions(times)

Return sensor positions for given times via interpolation/extrapolation.

model_psf([sigma, size])

Model the sensor's point spread function (PSF).

pixel_to_geodetic(frame, rows, columns)

Convert pixel coordinates to geodetic coordinates using ARF polynomials.

to_hdf5(group)

Save sampled sensor data to an HDF5 group.

Attributes

bad_pixel_mask_frames

bad_pixel_masks

bias_image_frames

bias_images

frames

pointing

poly_arf_to_col

poly_arf_to_row

poly_pixel_to_arf_azimuth

poly_pixel_to_arf_elevation

positions

radiometric_gain

times

uniformity_gain_image_frames

uniformity_gain_images

uuid

name

positions: ndarray[tuple[Any, ...], dtype[float64]] | None = None
times: ndarray[tuple[Any, ...], dtype[datetime64]] | None = None
frames: ndarray[tuple[Any, ...], dtype[int64]] | None = None
radiometric_gain: ndarray[tuple[Any, ...], dtype[_ScalarT]] | None = None
pointing: ndarray[tuple[Any, ...], dtype[float64]] | None = None
poly_pixel_to_arf_azimuth: ndarray[tuple[Any, ...], dtype[float64]] | None = None
poly_pixel_to_arf_elevation: ndarray[tuple[Any, ...], dtype[float64]] | None = None
poly_arf_to_row: ndarray[tuple[Any, ...], dtype[float64]] | None = None
poly_arf_to_col: ndarray[tuple[Any, ...], dtype[float64]] | None = None
__post_init__()[source]

Validate inputs and remove duplicate times.

Ensures positions and times have compatible shapes and removes any duplicate time entries along with their corresponding positions.

Raises:

ValueError – If positions or times are not provided, or if they have incompatible shapes.

can_geolocate()[source]

Check if sensor can convert pixels to geodetic coordinates and vice versa.

Returns:

True if sensor has all required ARF geolocation data: pointing vectors and both forward (pixel→ARF) and reverse (ARF→pixel) polynomials.

Return type:

bool

get_positions(times)[source]

Return sensor positions for given times via interpolation/extrapolation.

Parameters:

times (NDArray[np.datetime64]) – Array of times for which to retrieve sensor positions

Returns:

Sensor positions as (3, N) array where N is the number of query times. Each column contains [x, y, z] coordinates in ECEF frame (km).

Return type:

NDArray[np.float64]

Notes

  • For sensors with 1 sample: returns the single position for all times

  • For sensors with 2+ samples: uses linear interpolation within the time range and linear extrapolation outside the range

pixel_to_geodetic(frame, rows, columns)[source]

Convert pixel coordinates to geodetic coordinates using ARF polynomials.

Uses ARF (Attitude Reference Frame) polynomials to map (row, column) pixel coordinates to geodetic coordinates by ray-casting to the Earth’s surface. Pixels that do not intersect Earth will have NaN coordinates.

Parameters:
  • frame (int) – Frame number for which to perform the conversion

  • rows (np.ndarray) – Array of row pixel coordinates

  • columns (np.ndarray) – Array of column pixel coordinates

Returns:

Astropy EarthLocation object(s) with geodetic coordinates. Returns NaN coordinates for pixels that do not intersect Earth. Returns zero coordinates if polynomials are not available or frame not found.

Return type:

EarthLocation

Notes

  • Requires ARF polynomials and pointing vectors to be defined

  • Frame must exist in self.frames array

  • Off-Earth pixels will have NaN lat/lon/height values

geodetic_to_pixel(frame, loc)[source]

Convert geodetic coordinates to pixel coordinates using ARF polynomials.

Uses ARF (Attitude Reference Frame) polynomials to map geodetic coordinates (latitude, longitude, altitude) to (row, column) pixel coordinates. This method properly handles targets at any altitude, not just ground level.

Parameters:
  • frame (int) – Frame number for which to perform the conversion

  • loc (EarthLocation) – Astropy EarthLocation object(s) containing geodetic coordinates

Returns:

  • rows (np.ndarray) – Array of row pixel coordinates (zeros if polynomials unavailable)

  • columns (np.ndarray) – Array of column pixel coordinates (zeros if polynomials unavailable)

Return type:

Tuple[ndarray, ndarray]

Notes

  • Requires ARF polynomials and pointing vectors to be defined

  • Frame must exist in self.frames array

  • Returns zero coordinates if polynomials are not available or frame not found

  • Properly handles targets at any altitude (not limited to ground level)

to_hdf5(group)[source]

Save sampled sensor data to an HDF5 group.

Parameters:

group (h5py.Group) – HDF5 group to write sensor data to (typically sensors/<sensor_name>/)

Notes

This method extends the base Sensor.to_hdf5() by adding: - Position data (positions, times) in position/ subgroup - Geolocation polynomials in geolocation/ subgroup - Radiometric gain values in radiometric/ subgroup

__init__(name, bias_images=None, bias_image_frames=None, uniformity_gain_images=None, uniformity_gain_image_frames=None, bad_pixel_masks=None, bad_pixel_mask_frames=None, _instance_count=0, positions=None, times=None, frames=None, radiometric_gain=None, pointing=None, poly_pixel_to_arf_azimuth=None, poly_pixel_to_arf_elevation=None, poly_arf_to_row=None, poly_arf_to_col=None)