Attitude

class missiontools.AbstractAttitudeLaw[source]

Bases: ABC

Base class for spacecraft/sensor pointing laws.

Stores full 3-DOF body orientation as unit quaternions [w, x, y, z]. The boresight is always body-z.

Subclasses must implement pointing_eci(), rotate_from_body(), and __repr__(). The frame-conversion methods pointing_lvlh() and pointing_ecef() are provided by the base class.

Optional yaw steering can be enabled via yaw_steering() on subclasses that support it (FixedAttitudeLaw and TrackAttitudeLaw).

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

Boresight unit vector(s) in the ECI 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 pointing vector(s) in ECI, 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]]

abstractmethod rotate_from_body(v_body, r_eci, v_eci, t)[source]

Express a spacecraft body-frame vector in the ECI frame.

Parameters:
  • v_body (array_like, shape (3,)) – Unit direction in the spacecraft body frame (need not be pre-normalised).

  • 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 vector(s) in ECI, shape (N, 3) or (3,).

Parameters:
  • v_body (ArrayLike)

  • r_eci (ArrayLike)

  • v_eci (ArrayLike)

  • t (ArrayLike)

Return type:

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

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 pointing 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 pointing 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]]

yaw_steering(solar_config)[source]

Enable or disable solar yaw steering.

When active, the roll DOF is controlled dynamically at each timestep to maximise solar power generation. The boresight direction is unchanged; only the rotation about the boresight is affected.

Supported by FixedAttitudeLaw and TrackAttitudeLaw.

Parameters:

solar_config (AbstractSolarConfig or None) – Solar config whose optimal_angle() defines the preferred sun-facing direction in the body frame. Pass None to deactivate yaw steering and revert to the static roll angle.

Raises:
  • NotImplementedError – If this attitude law type does not support yaw steering.

  • TypeError – If solar_config is not an AbstractSolarConfig or None.

Return type:

None

class missiontools.FixedAttitudeLaw(vector, frame, roll=0.0)[source]

Bases: AbstractAttitudeLaw

Fixed attitude law: constant body orientation in a reference frame.

Parameters:
  • vector (array_like, shape (3,)) – Boresight direction (body-z) expressed in frame. Need not be a unit vector — it is normalised on input.

  • frame ({'lvlh', 'eci', 'ecef'}) – Reference frame in which vector is expressed.

  • roll (float, optional) – Roll angle (rad) about the boresight axis. Default 0 gives the minimum rotation from identity that aligns body-z with vector.

Raises:

ValueError – If frame is not recognised or vector is the zero vector.

Parameters:

Examples

Nadir pointing:

law = FixedAttitudeLaw.nadir()

Body-z along-track in LVLH:

law = FixedAttitudeLaw([0, 1, 0], 'lvlh')
classmethod nadir(roll=0.0)[source]

Earth-nadir pointing law (body-z = −R̂ in LVLH).

Full 3-DOF convention at roll=0: body-z = nadir (−R̂), body-x = along-track (Ŝ), body-y = −orbit-normal (−Ŵ). This is a right-handed body frame.

Parameters:

roll (float, optional) – Roll angle (rad) about the boresight (nadir) axis. Default 0 gives body-x = along-track.

Parameters:

roll (float)

Return type:

FixedAttitudeLaw

pointing_eci(r_eci, v_eci, t)[source]

Boresight unit vector(s) in the ECI 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 pointing vector(s) in ECI, shape (N, 3) for array inputs or (3,) for scalar inputs.

rotate_from_body(v_body, r_eci, v_eci, t)[source]

Express a spacecraft body-frame vector in the ECI frame.

Parameters:
  • v_body (array_like, shape (3,)) – Unit direction in the spacecraft body frame (need not be pre-normalised).

  • 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 vector(s) in ECI, shape (N, 3) or (3,).

yaw_steering(solar_config)[source]

Enable or disable solar yaw steering.

When active, the roll DOF is controlled dynamically at each timestep to maximise solar power generation. The boresight direction is unchanged; only the rotation about the boresight is affected.

Supported by FixedAttitudeLaw and TrackAttitudeLaw.

Parameters:

solar_config (AbstractSolarConfig or None) – Solar config whose optimal_angle() defines the preferred sun-facing direction in the body frame. Pass None to deactivate yaw steering and revert to the static roll angle.

Raises:
  • NotImplementedError – If this attitude law type does not support yaw steering.

  • TypeError – If solar_config is not an AbstractSolarConfig or None.

Return type:

None

class missiontools.TrackAttitudeLaw(target, roll=0.0)[source]

Bases: AbstractAttitudeLaw

Target-tracking attitude law: boresight always points toward a target.

Parameters:
  • target (Spacecraft | GroundStation) – The object to track. A Spacecraft target is propagated analytically; a GroundStation target is converted from its fixed geodetic position to ECI at each timestep.

  • roll (float, optional) – Roll angle (rad) about the boresight axis. Default 0 uses the minimum-rotation convention from _q_from_vec() to pin the remaining degree of freedom.

Raises:

TypeError – If target is not a Spacecraft or GroundStation.

Parameters:

roll (float)

Examples

Track another spacecraft:

target = Spacecraft(...)
law = TrackAttitudeLaw(target)

Track a ground station:

from missiontools import GroundStation
gs = GroundStation(lat=51.5, lon=-0.1)
law = TrackAttitudeLaw(gs)
pointing_eci(r_eci, v_eci, t)[source]

Boresight unit vector(s) in the ECI 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 pointing vector(s) in ECI, shape (N, 3) for array inputs or (3,) for scalar inputs.

rotate_from_body(v_body, r_eci, v_eci, t)[source]

Express a spacecraft body-frame vector in the ECI frame.

Parameters:
  • v_body (array_like, shape (3,)) – Unit direction in the spacecraft body frame (need not be pre-normalised).

  • 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 vector(s) in ECI, shape (N, 3) or (3,).

yaw_steering(solar_config)[source]

Enable or disable solar yaw steering.

When active, the roll DOF is controlled dynamically at each timestep to maximise solar power generation. The boresight direction is unchanged; only the rotation about the boresight is affected.

Supported by FixedAttitudeLaw and TrackAttitudeLaw.

Parameters:

solar_config (AbstractSolarConfig or None) – Solar config whose optimal_angle() defines the preferred sun-facing direction in the body frame. Pass None to deactivate yaw steering and revert to the static roll angle.

Raises:
  • NotImplementedError – If this attitude law type does not support yaw steering.

  • TypeError – If solar_config is not an AbstractSolarConfig or None.

Return type:

None

class missiontools.CustomAttitudeLaw(callback)[source]

Bases: AbstractAttitudeLaw

Custom attitude law defined by a user-supplied callback.

The callback receives the spacecraft state at each timestep and returns full 3-DOF body attitude quaternions in ECI, giving complete control over pointing.

Parameters:

callback (callable) –

A function with signature:

callback(t, r_eci, v_eci) -> quaternions

where:

  • t(N,) array of datetime64[us] time instants

  • r_eci(N, 3) ECI position array (m)

  • v_eci(N, 3) ECI velocity array (m s⁻¹)

  • returns — (N, 4) array of unit quaternions [w, x, y, z] defining body attitude in ECI

Raises:

TypeError – If callback is not callable.

Examples

def my_attitude(t, r_eci, v_eci):
    N = len(t)
    q = np.zeros((N, 4))
    q[:, 0] = 1.0  # identity: body frame aligned with ECI
    return q

law = CustomAttitudeLaw(my_attitude)
pointing_eci(r_eci, v_eci, t)[source]

Boresight unit vector(s) in the ECI 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 pointing vector(s) in ECI, shape (N, 3) for array inputs or (3,) for scalar inputs.

rotate_from_body(v_body, r_eci, v_eci, t)[source]

Express a spacecraft body-frame vector in the ECI frame.

Parameters:
  • v_body (array_like, shape (3,)) – Unit direction in the spacecraft body frame (need not be pre-normalised).

  • 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 vector(s) in ECI, shape (N, 3) or (3,).

class missiontools.LimbAttitudeLaw(body_vector, altitude_km, *, yaw_deg=0.0, roll_deg=0.0, body_semi_major_axis=6378137.0, body_flattening=0.0033528106647474805)[source]

Bases: AbstractAttitudeLaw

Limb-pointing attitude law: body-frame vector grazes the central body.

The given body-frame vector is continuously aligned with the ray from the spacecraft that grazes an offset ellipsoid at altitude altitude_km above the central body.

Parameters:
  • body_vector (array_like, shape (3,)) – Body-frame axis to be aligned with the limb direction. Need not be pre-normalised.

  • altitude_km (float) – Grazing altitude above the central body (km, ≥ 0).

  • yaw_deg (float, optional) – Azimuth of the pointing direction in the LVLH horizontal plane, measured right-handed about zenith (+R̂) from the +Ŝ (along-track) axis. Default 0 (forward limb).

  • roll_deg (float, optional) – Right-hand rotation about the outward pointing vector. Default 0.

  • body_semi_major_axis (float, optional) – Equatorial radius of the central body (m). Defaults to Earth WGS84 (EARTH_SEMI_MAJOR_AXIS).

  • body_flattening (float, optional) – Flattening of the central body (dimensionless, in [0, 1); 0 gives a sphere). Defaults to Earth WGS84.

Raises:

ValueError – If body_vector is the zero vector or not shape (3,), altitude_km < 0, body_semi_major_axis <= 0, or body_flattening is outside [0, 1).

Parameters:

Examples

Boresight (body-z) at the forward limb 20 km above the surface:

law = LimbAttitudeLaw([0, 0, 1], altitude_km=20)

Cross-track limb view with body-x aligned to the limb:

law = LimbAttitudeLaw([1, 0, 0], altitude_km=50, yaw_deg=90)
pointing_eci(r_eci, v_eci, t)[source]

Boresight unit vector(s) in the ECI 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 pointing vector(s) in ECI, shape (N, 3) for array inputs or (3,) for scalar inputs.

rotate_from_body(v_body, r_eci, v_eci, t)[source]

Express a spacecraft body-frame vector in the ECI frame.

Parameters:
  • v_body (array_like, shape (3,)) – Unit direction in the spacecraft body frame (need not be pre-normalised).

  • 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 vector(s) in ECI, shape (N, 3) or (3,).

class missiontools.ConditionAttitudeLaw(default_attitude, condition_attitudes)[source]

Bases: AbstractAttitudeLaw

Conditional attitude law: route between attitude laws by condition.

Each timestep is dispatched to one of a chain of child attitude laws, selected by evaluating a list of AbstractCondition predicates in priority order. When no condition holds, the timestep falls through to default_attitude.

Parameters:
  • default_attitude (AbstractAttitudeLaw) – The fallback law used at any timestep where no condition holds.

  • condition_attitudes (list of (AbstractCondition, AbstractAttitudeLaw)) – Ordered chain of (condition, law) pairs. At each timestep, the first condition that evaluates to True selects its paired law; earlier pairs take precedence over later ones. An empty list is permitted and produces a degenerate wrapper around default_attitude.

Raises:

TypeError – If default_attitude is not an AbstractAttitudeLaw, or any element of condition_attitudes is not a 2-tuple of (AbstractCondition, AbstractAttitudeLaw).

Notes

Switching between child laws produces an instantaneous pointing discontinuity at the boundary; no slew dynamics are modelled.

Yaw steering, when enabled via yaw_steering(), is propagated to every child law and to the default; if any child does not support yaw steering the call raises NotImplementedError.

Examples

Switch from nadir pointing to ground-station tracking when the spacecraft is in view of a downlink site:

from missiontools import (FixedAttitudeLaw, TrackAttitudeLaw,
                          GroundStation, Spacecraft)
from missiontools.attitude import ConditionAttitudeLaw
from missiontools.condition import SpaceGroundAccessCondition

sc       = Spacecraft(...)
gs       = GroundStation(lat=51.5, lon=-0.1)
link_law = TrackAttitudeLaw(...)
nadir    = FixedAttitudeLaw.nadir()
cond     = SpaceGroundAccessCondition(sc, gs, el_min_deg=5.0)

law = ConditionAttitudeLaw(
    default_attitude    = nadir,
    condition_attitudes = [(cond, link_law)],
)
pointing_eci(r_eci, v_eci, t)[source]

Boresight unit vector(s) in the ECI 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 pointing vector(s) in ECI, shape (N, 3) for array inputs or (3,) for scalar inputs.

rotate_from_body(v_body, r_eci, v_eci, t)[source]

Express a spacecraft body-frame vector in the ECI frame.

Parameters:
  • v_body (array_like, shape (3,)) – Unit direction in the spacecraft body frame (need not be pre-normalised).

  • 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 vector(s) in ECI, shape (N, 3) or (3,).

yaw_steering(solar_config)[source]

Enable or disable solar yaw steering.

When active, the roll DOF is controlled dynamically at each timestep to maximise solar power generation. The boresight direction is unchanged; only the rotation about the boresight is affected.

Supported by FixedAttitudeLaw and TrackAttitudeLaw.

Parameters:

solar_config (AbstractSolarConfig or None) – Solar config whose optimal_angle() defines the preferred sun-facing direction in the body frame. Pass None to deactivate yaw steering and revert to the static roll angle.

Raises:
  • NotImplementedError – If this attitude law type does not support yaw steering.

  • TypeError – If solar_config is not an AbstractSolarConfig or None.

Return type:

None