Sensor

class missiontools.AbstractSensor(*, condition=None)[source]

Bases: ABC

Abstract base class for instruments attached to a spacecraft.

Subclasses must implement pointing_eci(), fov_spec(), and __repr__(). The concrete pointing_lvlh() and pointing_ecef() methods are provided here and delegate to pointing_eci() plus a frame transform.

property spacecraft

Host spacecraft, or None if not yet attached.

property condition

Optional AbstractCondition controlling when this sensor is active, or None (always active).

abstractmethod pointing_eci(r_eci, v_eci, t)[source]

Boresight unit vector(s) in the ECI frame.

Parameters:
  • r_eci (ArrayLike)

  • v_eci (ArrayLike)

  • t (ArrayLike)

Return type:

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

abstractmethod fov_spec(r_eci, v_eci, t)[source]

Return FOV parameters frozen at the given orbital state.

Returns a dict with at minimum:

  • 'fov_type': 'conic' or 'rectangular'

  • 'pointing_lvlh': (3,) boresight unit vector in LVLH

Conic specs also include 'cos_half_angle'. Rectangular specs also include 'u1_lvlh', 'u2_lvlh', 'tan_theta1', and 'tan_theta2'.

Parameters:
  • r_eci (ArrayLike)

  • v_eci (ArrayLike)

  • t (ArrayLike)

Return type:

dict[str, Any]

pointing_lvlh(r_eci, v_eci, t)[source]

Boresight unit vector(s) in the LVLH frame.

Parameters:
  • r_eci (array_like, shape ``(N, 3)`` or (3,)) – Host spacecraft ECI position(s) (m).

  • v_eci (array_like, shape ``(N, 3)`` or (3,)) – Host spacecraft ECI velocity(s) (m s⁻¹).

  • t (array_like of datetime64, shape ``(N,)`` or scalar) – Observation epoch(s).

Returns:

npt.NDArray[np.floating] – Unit boresight vector(s) in LVLH, shape (N, 3) for array inputs or (3,) for scalar inputs.

Parameters:
  • r_eci (ArrayLike)

  • v_eci (ArrayLike)

  • t (ArrayLike)

Return type:

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

pointing_ecef(r_eci, v_eci, t)[source]

Boresight unit vector(s) in the ECEF frame.

Parameters:
  • r_eci (array_like, shape ``(N, 3)`` or (3,)) – Host spacecraft ECI position(s) (m).

  • v_eci (array_like, shape ``(N, 3)`` or (3,)) – Host spacecraft ECI velocity(s) (m s⁻¹).

  • t (array_like of datetime64, shape ``(N,)`` or scalar) – Observation epoch(s).

Returns:

npt.NDArray[np.floating] – Unit boresight vector(s) in ECEF, shape (N, 3) for array inputs or (3,) for scalar inputs.

Parameters:
  • r_eci (ArrayLike)

  • v_eci (ArrayLike)

  • t (ArrayLike)

Return type:

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

class missiontools.ConicSensor(half_angle_deg, *, attitude_law=None, body_vector=None, body_euler_deg=None, condition=None)[source]

Bases: AbstractSensor

An instrument attached to a spacecraft with a cone-shaped field of view.

Prefer the keyword arguments to select the pointing mode (see below). The constructor is public and may be called directly when needed.

Parameters:
  • half_angle_deg (float) – Half-angle of the sensor’s conical field of view (degrees). Must satisfy 0 < half_angle_deg <= 90.

  • attitude_law (AbstractAttitudeLaw, optional) – Independent AbstractAttitudeLaw for this sensor, decoupled from the host spacecraft’s attitude. Mutually exclusive with body_vector and body_euler_deg.

  • body_vector (array_like, shape (3,), optional) – Boresight direction expressed in the spacecraft body frame. Normalised on input. Mutually exclusive with attitude_law and body_euler_deg.

  • body_euler_deg ((yaw, pitch, roll) tuple of float, optional) – ZYX intrinsic Euler angles (degrees) defining the sensor frame relative to the spacecraft body frame. The boresight is the sensor frame’s z-axis expressed in body-frame coordinates. Mutually exclusive with attitude_law and body_vector.

Parameters:

Notes

Body-mounted sensors (body_vector or body_euler_deg) require the sensor to be attached to a spacecraft via add_sensor() before their pointing methods can be called.

Examples

Nadir-pointing sensor, 10° half-angle:

from missiontools import ConicSensor, FixedAttitudeLaw
sensor = ConicSensor(10.0, attitude_law=FixedAttitudeLaw.nadir())

Sensor body-mounted along spacecraft body-z (boresight = nadir for a nadir spacecraft), 5° half-angle:

sensor = ConicSensor(5.0, body_vector=[0, 0, 1])

Sensor tilted 30° in pitch from body-z:

sensor = ConicSensor(15.0, body_euler_deg=(0, 30, 0))
property half_angle_rad: float

FOV cone half-angle in radians.

property half_angle_deg: float

FOV cone half-angle in degrees.

pointing_eci(r_eci, v_eci, t)[source]

Boresight unit vector(s) in the ECI frame.

For body_vector / body_euler_deg sensors the host spacecraft’s attitude_law is used to transform the body-frame boresight to ECI.

Parameters:
  • r_eci (array_like, shape ``(N, 3)`` or (3,)) – Host spacecraft ECI position(s) (m).

  • v_eci (array_like, shape ``(N, 3)`` or (3,)) – Host spacecraft ECI velocity(s) (m s⁻¹).

  • t (array_like of datetime64, shape ``(N,)`` or scalar) – Observation epoch(s).

Returns:

npt.NDArray[np.floating] – Unit boresight vector(s) in ECI, shape (N, 3) for array inputs or (3,) for scalar inputs.

Raises:

RuntimeError – If the sensor is in body mode and has not been attached to a spacecraft via add_sensor().

Parameters:
  • r_eci (ArrayLike)

  • v_eci (ArrayLike)

  • t (ArrayLike)

Return type:

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

fov_spec(r_eci, v_eci, t)[source]

Return FOV parameters frozen at the given orbital state.

Returns a dict with at minimum:

  • 'fov_type': 'conic' or 'rectangular'

  • 'pointing_lvlh': (3,) boresight unit vector in LVLH

Conic specs also include 'cos_half_angle'. Rectangular specs also include 'u1_lvlh', 'u2_lvlh', 'tan_theta1', and 'tan_theta2'.

Parameters:
  • r_eci (ArrayLike)

  • v_eci (ArrayLike)

  • t (ArrayLike)

Return type:

dict[str, Any]

class missiontools.RectangularSensor(theta1_deg, theta2_deg, *, attitude_law=None, body_vector=None, body_euler_deg=None, condition=None)[source]

Bases: AbstractSensor

An instrument with a rectangular (pyramidal) field of view.

The FOV is defined by two half-angles, theta1 and theta2, measured in two orthogonal planes of the sensor frame. Together they describe a rectangular pyramid whose apex is at the sensor.

Parameters:
  • theta1_deg (float) – Half-angle in the sensor x-z plane (degrees). Must satisfy 0 < theta1_deg <= 90.

  • theta2_deg (float) – Half-angle in the sensor y-z plane (degrees). Must satisfy 0 < theta2_deg <= 90.

  • attitude_law (AbstractAttitudeLaw, optional) – Independent AbstractAttitudeLaw for this sensor, decoupled from the host spacecraft’s attitude. Mutually exclusive with body_vector and body_euler_deg.

  • body_vector (array_like, shape (3,), optional) – Boresight direction expressed in the spacecraft body frame. Normalised on input. Mutually exclusive with attitude_law and body_euler_deg.

  • body_euler_deg ((yaw, pitch, roll) tuple of float, optional) – ZYX intrinsic Euler angles (degrees) defining the sensor frame relative to the spacecraft body frame. The boresight is the sensor frame’s z-axis; the x/y axes define the FOV orientation about the boresight (roll matters for rectangular sensors). Mutually exclusive with attitude_law and body_vector.

Parameters:

Notes

Body-mounted sensors require attachment to a spacecraft via add_sensor() before their pointing methods can be called.

For body_vector mode the perpendicular axes are derived via Gram-Schmidt orthogonalisation against the body-z direction (falling back to body-x if the boresight is nearly parallel to body-z).

Examples

Nadir-pointing rectangular sensor, 10° × 20°:

from missiontools import RectangularSensor, FixedAttitudeLaw
sensor = RectangularSensor(10.0, 20.0,
                           attitude_law=FixedAttitudeLaw.nadir())

Body-mounted with Euler angles (roll rotates the FOV about boresight):

sensor = RectangularSensor(5.0, 10.0, body_euler_deg=(0, 30, 45))
property theta1_rad: float

FOV half-angle in the sensor x-z plane (radians).

property theta1_deg: float

FOV half-angle in the sensor x-z plane (degrees).

property theta2_rad: float

FOV half-angle in the sensor y-z plane (radians).

property theta2_deg: float

FOV half-angle in the sensor y-z plane (degrees).

sensor_frame_eci(r_eci, v_eci, t)[source]

Sensor frame axes in ECI as columns of a (3, 3) matrix.

Returns [u1 | u2 | boresight] where each column is a unit vector in ECI.

Parameters:
  • r_eci (ArrayLike)

  • v_eci (ArrayLike)

  • t (ArrayLike)

Return type:

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

sensor_frame_lvlh(r_eci, v_eci, t)[source]

Sensor frame axes in LVLH as columns of a (3, 3) matrix.

Parameters:
  • r_eci (ArrayLike)

  • v_eci (ArrayLike)

  • t (ArrayLike)

Return type:

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

pointing_eci(r_eci, v_eci, t)[source]

Boresight unit vector(s) in the ECI frame.

Parameters:
  • r_eci (ArrayLike)

  • v_eci (ArrayLike)

  • t (ArrayLike)

Return type:

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

fov_spec(r_eci, v_eci, t)[source]

Return FOV parameters frozen at the given orbital state.

Returns a dict with at minimum:

  • 'fov_type': 'conic' or 'rectangular'

  • 'pointing_lvlh': (3,) boresight unit vector in LVLH

Conic specs also include 'cos_half_angle'. Rectangular specs also include 'u1_lvlh', 'u2_lvlh', 'tan_theta1', and 'tan_theta2'.

Parameters:
  • r_eci (ArrayLike)

  • v_eci (ArrayLike)

  • t (ArrayLike)

Return type:

dict[str, Any]