astrix package

AsTrIX — Aerospace Trajectory Imaging & Diagnostics toolbox.

This package provides tools for aerospace trajectory analysis and visualization.

class Acceleration(vec, time, _xp)[source]

Bases: object

Acceleration vector(s) in ECEF coordinates (ax, ay, az) in m/s². Associated with a Time object for the time instances of the accelerations. Internal use only, typically created from Path objects. No data validation is performed.

Parameters:
  • vec (Array) – Acceleration vectors in ECEF coordinates (ax, ay, az) in m/s². Shape (n, 3).

  • time (Time) – Time object associated with the accelerations. Length must match number of acceleration vectors.

  • backend (BackendArg, optional) – Array backend to use (numpy, jax, etc.). Defaults to numpy.

  • _xp (Any)

classmethod from_data(vec, time, backend=None)[source]

Create an Acceleration object from acceleration vector array and Time object.

Return type:

Acceleration

Parameters:
  • vec (Buffer | _SupportsArray[dtype[Any]] | _NestedSequence[_SupportsArray[dtype[Any]]] | complex | bytes | str | _NestedSequence[complex | bytes | str])

  • time (Time)

  • backend (str | Any | None)

convert_to(backend)[source]

Convert the Acceleration object to a different backend.

Return type:

Acceleration

Parameters:

backend (str | Any | None)

interp(time, method='linear', check_bounds=True)[source]

Interpolate the Acceleration to the given times using the specified method. Currently only ‘linear’ interpolation is supported.

Return type:

Acceleration

Parameters:
  • time (Time)

  • method (str)

  • check_bounds (bool)

rotate(rot)[source]
Return type:

Acceleration

Parameters:

rot (Rotation)

property backend: str
property magnitude: Any

Get the acceleration magnitude in m/s².

time: Time
property unit: Any

Get the unit acceleration vector.

vec: Any
class FixedZoomCamera(_res, _sensor_size, _focal_length, _mat, _rad_coef, _xp)[source]

Bases: CameraLike

A simple pinhole camera model with fixed zoom.

Notes

The camera intrinsic matrix is given by:

[ fx 0 cx ] [ 0 fy cy ] [ 0 0 1 ]

where:

fx = focal_length * res[0] / sensor_size[0] fy = focal_length * res[1] / sensor_size[1] cx = res[0] / 2 cy = res[1] / 2

The field of view is given by:

fov_x = 2 * arctan(sensor_size[0] / (2 * focal_length)) fov_y = 2 * arctan(sensor_size[1] / (2 * focal_length))

Parameters:
  • _res (tuple[int, int])

  • _sensor_size (tuple[float, float])

  • _focal_length (float)

  • _mat (Any)

  • _rad_coef (Any | None)

  • _xp (Any)

classmethod from_foc_len(res, sensor_size, focal_length, rad_coef=None, backend=None)[source]

Create a FixedZoomCamera from focal length.

Parameters:
  • res (tuple[int, int]) – Image resolution (width, height) in pixels.

  • sensor_size (tuple[float, float]) – Physical sensor size (width, height) in mm.

  • focal_length (float) – Focal length in mm.

  • rad_coef (optional) – Radial distortion coefficients.

  • backend (optional) – Backend to use. Either “numpy” or “jax”.

Return type:

FixedZoomCamera

classmethod from_hoz_fov(res, hoz_fov, sensor_size, rad_coef=None, backend=None)[source]

Create a FixedZoomCamera from horizontal field of view.

Parameters:
  • res (tuple[int, int]) – Image resolution (width, height) in pixels.

  • hoz_fov (float) – Horizontal field of view in degrees.

  • sensor_size (tuple[float, float]) – Physical sensor size (width, height) in mm.

  • rad_coef (optional) – Radial distortion coefficients.

  • backend (optional) – Backend to use. Either “numpy” or “jax”.

Return type:

FixedZoomCamera

convert_to(backend)[source]

Convert the camera to a different backend.

Return type:

FixedZoomCamera

Parameters:

backend (str | Any | None)

fov(zoom=None)[source]

Field of view in degrees (horizontal, vertical).

Return type:

tuple[float, float]

Parameters:

zoom (float | None)

mat(time=Time invariant (n=1))[source]

Camera intrinsic matrix. No zoom parameter needed as this is a fixed zoom camera.

Return type:

Any

Parameters:

time (Time)

rad_coef(time=Time invariant (n=1))[source]

Radial distortion coefficients.

Return type:

Any

Parameters:

time (Time)

property backend: str

Backend used by the camera.

property has_dist

Whether the camera has distortion coefficients.

class Frame(rot, loc=None, ref_frame=None, backend=None, name='unnamed-frame')[source]

Bases: object

A reference frame defined by a rotation and location. Can be static or time-varying, and can have rotation defined relative to another Frame. Combines RotationLike and Location objects, and manages time associations.

Parameters:
  • rot (Rotation | RotationSequence) – A scipy Rotation object (single rotation) or RotationSequence (time-tagged rotations). If a single Rotation is provided, the frame rotation is static.

  • loc (Location, optional) – A Location object (Point or Path) defining the frame origin in ECEF coordinates. If not provided, the frame origin is assumed to be at the origin of the reference frame. If loc is provided, it must be a singular Point (1x3) for static frames. Use Path objects for time-varying locations.

  • ref_frame (Frame, optional) – A reference Frame object to define the rotation relative to. If not provided, the rotation is assumed to be absolute (e.g., from ECEF frame).

  • backend (BackendArg, optional) – Array backend to use (numpy, jax, etc.). Defaults to numpy.

  • name (str)

Examples

Static frame with static rotation and location:

>>> from astrix import Frame, Point
>>> from scipy.spatial.transform import Rotation
>>> rot = Rotation.from_euler("xyz", [90, 0, 0], degrees=True)
>>> loc = Point.from_geodet([-27.47, 153.03, 0])
>>> frame_static = Frame(rot, loc)
>>> frame_static.interp_rot().as_euler("xyz", degrees=True)
array([[90.,  0.,  0.]])

Time-varying frame with rotation sequence and static location:

>>> from astrix import Time, RotationSequence
>>> from datetime import datetime, timezone
>>> times = Time.from_datetime([
...     datetime(2021, 1, 1, 12, 0, 0, tzinfo=timezone.utc),
...     datetime(2021, 1, 1, 13, 0, 0, tzinfo=timezone.utc),
... ])
>>> rots = Rotation.from_euler("xyz", [[0, 0, 0], [90, 0, 0]], degrees=True)
>>> rot_seq = RotationSequence(rots, times)
>>> frame_dynamic = Frame(rot_seq, loc)
>>> t_interp = Time.from_datetime(
...     datetime(2021, 1, 1, 12, 30, 0, tzinfo=timezone.utc)
... )
>>> frame_dynamic.interp_rot(t_interp).as_euler("xyz", degrees=True)
array([[45.,  0.,  0.]])

Frame defined relative to another frame:

>>> rot_ref = Rotation.from_euler("xyz", [0, 30, 0], degrees=True)
>>> frame_ref = Frame(rot_ref, loc)
>>> rot_rel = Rotation.from_euler("xyz", [0, 40, 0], degrees=True)
>>> frame = Frame(rot_rel, ref_frame=frame_ref)
>>> frame.interp_rot().as_euler("xyz", degrees=True)
array([[ 0., 70.,  0.]])

Notes

  • If both loc and ref_frame are provided, the new frame location is used and the reference frame location is disregarded.

  • A TimeGroup object is created internally to manage time associations between rotation, location, and reference frame.

  • If the frame is static (single rotation and singular Point), the time properties return TIME_INVARIANT.

  • Use Path objects for time-varying locations.

convert_to(backend)[source]

Convert the Frame object to a different backend.

Return type:

Frame

Parameters:

backend (str | Any | None)

index_loc(index)[source]

Get the location of the frame at the given index.

Warning: This should only be used after downsampling so that rotation indices are time-aligned. Use with caution. Prefer interp_loc for general use.

Return type:

Point

Parameters:

index (int)

index_rot(index)[source]

Get the absolute rotation of the frame at the given index.

Warning: This should only be used after downsampling so that location and rotation indeces align. Prefer interp_rot for general use.

Return type:

Rotation

Parameters:

index (int)

interp_loc(time=Time invariant (n=1), check_bounds=True)[source]

Get the interpolated location of the frame at the given times. If the location is static, time can be None.

Return type:

Point

Parameters:
  • time (Time)

  • check_bounds (bool)

interp_rot(time=Time invariant (n=1), check_bounds=True)[source]

Get the interpolated absolute rotation of the frame at the given times. If all rotations are time invariant, time can be None.

Return type:

Rotation

Parameters:
  • time (Time)

  • check_bounds (bool)

replace_rot(frame_name, new_rot)[source]

Replace a rotation in the rotation chain with a new rotation.

This is an advanced feature and currently only applicable for static rotations. Should primarily be used for optimisation purposes in autograd frameworks, such as correcting misalignment.

Parameters:
  • frame_name (str) – Name of the frame whose rotation is to be replaced.

  • new_rot (Rotation) – New scipy Rotation object to replace the existing rotation.

Return type:

Frame

sample_at_time(time)[source]

Sample the Frame object at specific times, returning a new Frame with time-varying components sampled at those times. The new frame can then be indexed at these times directly to avoid interpolation

Parameters:

time (Time) – Time object specifying the times to sample the Frame at.

Returns:

New Frame object with components sampled at the specified times.

Return type:

Frame

property backend: str

Get the name of the array backend in use (e.g., ‘numpy’, ‘jax’).

property has_ref: bool

Check if the frame has a reference frame.

property is_static: bool

Check if the frame is static (single rotation and singular Point location).

property loc: Location

Get the location of the frame in ECEF coordinates.

property name: str

Get the name of the frame.

property name_chain: list[str]

Get the names of all frames in the rotation chain, from base to current.

property path: Path

Get the time-varying Path of the frame location, if applicable. If the frame location is static, raises AttributeError.

property point: Point

Get the singular Point of the frame location, if applicable. If the frame location is time-varying, raises AttributeError.

property rel_rot: RotationLike

Get the last rotation of the frame relative to the reference frame.

property time_bounds: tuple[Time, Time]

Get the time bounds of the frame as a tuple (start_time, end_time). If the frame is static, returns TIME_INVARIANT.

property time_group: TimeGroup

Get the Time object associated with the frame, if any.

class Path(point, backend=None)[source]

Bases: Location

Path of multiple Point objects with associated Time. Enables interpolation between points over time and calculation of velocity. Must have at least 2 points with associated Time.

Parameters:
  • point (Point | list of Point) – Point object or list of Point objects with associated Time. If a list is provided, all Points must have the same backend and associated Time.

  • backend (BackendArg, optional) – Array backend to use (numpy, jax, etc.). Defaults to numpy.

Examples

Instantiating a Path from a list of Points:

>>> from astrix import Point, Time, Path
>>> from datetime import datetime, timezone
>>> times = Time.from_datetime([
...     datetime(2025, 1, 1, 12, 0, 0, tzinfo=timezone.utc),
...     datetime(2025, 1, 1, 12, 0, 1, tzinfo=timezone.utc),
...     datetime(2025, 1, 1, 12, 0, 2, tzinfo=timezone.utc),
... ])
>>> path = Path([
...     Point([1, 2, 0], time=times[0]),
...     Point([2, 3.8, 0.4], time=times[1]),
...     Point([3, 6.0, 1], time=times[2]),
... ])

Interpolate Path and get velocity:

>>> t_interp = Time.from_datetime(
...     datetime(2025, 1, 1, 12, 0, 1, 500000, tzinfo=timezone.utc)
... )
>>> path.interp(t_interp).ecef
array([[2.5, 4.9, 0.7]])
>>> vel = path.vel.interp(t_interp)
>>> vel.magnitude
array([2.48997992])
convert_to(backend)[source]

Convert the Path object to a different backend.

Return type:

Path

Parameters:

backend (str | Any | None)

downsample(dt_max)[source]

Downsample the Path to a maximum time step of dt_max seconds. Note: This function is not JIT-compatible due to data validation checks.

Return type:

Path

Parameters:

dt_max (float)

interp(time, method='linear', check_bounds=True)[source]

Interpolate the Path to the given times using the specified method.

Parameters:
  • time (Time) – Times to interpolate to.

  • method (str, optional) – Interpolation method. Currently only ‘linear’ is supported. Defaults to ‘linear’.

  • check_bounds (bool, optional) – Whether to check if the interpolation times are within the path time bounds. Defaults to True.

Returns:

Interpolated Point object at the given times.

Return type:

Point

time_at_alt(alt)[source]

Find the times when the Path crosses the given altitude (in metres). Uses linear interpolation between points to find the crossing times. Note: This function is not JIT-compatible due to data validation checks.

Return type:

Time

Parameters:

alt (float)

truncate(start_time=None, end_time=None)[source]

Truncate the Path to the given start and end times. If start_time or end_time is None, the Path is truncated to the start or end of the Path respectively.

Note: This function is not JIT-compatible due to data validation checks.

Return type:

Path

Parameters:
  • start_time (Time | None)

  • end_time (Time | None)

property acc: Acceleration

Calculate the acceleration from the Path using central differences.

property end_time: Time

Get the end time of the Path.

property is_singular: bool

Check if the Path object represents a single point. Always False for Path objects.

property points: Point

Get the list of Point objects that make up the Path.

property start_time: Time

Get the start time of the Path.

property vel: Velocity

Calculate the velocity from the Path using central differences.

class Pixel(uv, time=Time invariant (n=1), backend=None)[source]

Bases: object

A pixel in an image.

Notes

The pixel coordinates are given in the image coordinate system, where the origin is at the top-left corner of the image, and the u-axis points to the right and the v-axis points down.

Parameters:
  • uv (Array)

  • time (Time)

  • backend (BackendArg)

property backend: str

Backend used by the pixel.

property has_time: bool

Whether the pixel has a time associated with it.

property time: Time
property uv: Any

Pixel coordinates (u, v) in pixels.

class Point(ecef, time=Time invariant (n=1), backend=None)[source]

Bases: Location

Point(s) in ECEF coordinates, stored as (x, y, z) in metres. Can represent a single point or multiple points, and can be associated with a Time object for time instances of the points.

Parameters:
  • ecef (Array) – ECEF coordinates as (x, y, z) in metres. Shape (3,) or (1,3) for single points, (n, 3) for multiple points.

  • time (Time, optional) – Time object associated with the points. If provided, the length of time must match the number of points. Defaults to TIME_INVARIANT for static points.

  • backend (BackendArg, optional) – Array backend to use (numpy, jax, etc.). Defaults to numpy.

Examples

Single static point:

>>> from astrix import Point
>>> p1 = Point([-5047162.4, 2568329.79, -2924521.17])  # Brisbane ECEF (m)
>>> p1.geodet  # Convert to geodetic (lat, lon, alt)
array([[-27.47, 153.03, 0.0]])
>>> p2 = Point.from_geodet([-27.47, 153.03, 0])
>>> p2.ecef
array([[-5047162.4, 2568329.79, -2924521.17]])

Multiple static points:

>>> pts = Point([
...     [-5047162.4, 2568329.79, -2924521.17],  # Brisbane
...     [-2694045.0, -4293642.0, 3857878.0],  # San Francisco
... ])
>>> pt_bris = pts[0]
>>> len(pts)
2

Dynamic point with time:

>>> from datetime import datetime, timezone
>>> from astrix import Time
>>> times = Time.from_datetime([
...     datetime(2021, 1, 1, 12, 0, 0, tzinfo=timezone.utc),
...     datetime(2021, 1, 1, 13, 0, 0, tzinfo=timezone.utc),
... ])
>>> pts_time = Point([
...     [-5047162.4, 2568329.79, -2924521.17],
...     [-2694045.0, -4293642.0, 3857878.0],
... ], time=times)
>>> pts_time.has_time
True
>>> pts_time.is_singular
False

Notes

  • When associating a Time object, the length of the Time must match the number of points.

  • Use Path objects for interpolating between multiple points over time.

classmethod from_geodet(geodet, time=Time invariant (n=1), backend=None)[source]

Create a Point object from geodetic coordinates (lat, lon, alt). Latitude and longitude are in degrees, altitude is in meters.

Return type:

Point

Parameters:
  • geodet (Buffer | _SupportsArray[dtype[Any]] | _NestedSequence[_SupportsArray[dtype[Any]]] | complex | bytes | str | _NestedSequence[complex | bytes | str])

  • time (Time)

  • backend (str | Any | None)

classmethod from_list(points)[source]

Create a Point object from a list of Point objects.

Return type:

Point

Parameters:

points (list[Point])

convert_to(backend)[source]

Convert the Point object to a different backend.

Return type:

Point

Parameters:

backend (str | Any | None)

property has_time: bool

Check if the Point has associated Time.

property is_singular: bool

Check if the Point object represents a single point.

class Ray(dir_rel, origin_rel=array([[0., 0., 0.]]), frame=Frame(name=ECEF, static_rot=True, static_loc=True, has_ref=False, time_bounds=(Time invariant (n=1), Time invariant (n=1)), backend=numpy), time=Time invariant (n=1), check=True, backend=None)[source]

Bases: object

A ray defined by an origin point, direction vector, reference frame, and optional time.

Parameters:
  • dir_rel (Array) – Nx3 array of ray direction vectors in local frame. Need not be normalised. E.g., (1, 0, 0) is a ray pointing along axis 1 of reference frame.

  • origin_rel (Array) – 1x3 or Nx3 array defining the ray origin(s) in local frame (meters). Typically (0,0,0) for camera reference frames, or ECEF coordinates for ECEF frame rays.

  • frame (Frame, optional) – Reference frame for the ray origin and direction.

  • time (Time, optional) – Time object associated with the rays. Must be same length as origin if provided. Defaults to TIME_INVARIANT.

  • check (bool, optional) – If True (default), validate non-zero direction vectors; disable for JIT paths.

  • backend (BackendArg, optional) – Array backend to use (numpy, jax, etc.). Defaults to numpy.

Notes

  • For calculating metrics (e.g. az/el), the axis are assumed (1) forward, (2) right, (3) down (FRD frame).

  • Although stored in local coordiantes, rays are globally defined by their reference frame.

  • Monotonically increasing time is required for interpolation. But to prevent data-dependent control flow,

    this is not checked on initialization. Use Time.is_increasing to check if needed.

Examples

>>> from astrix import Point, Ray
>>> origin = Point([0.0, 0.0, 0.0])
>>> target = Point([1.0, 0.0, 0.0])
>>> ray = Ray.from_points(target, origin)
>>> ray.unit_rel
array([[1., 0., 0.]])
classmethod from_az_el(az_el, frame=Frame(name=ECEF, static_rot=True, static_loc=True, has_ref=False, time_bounds=(Time invariant (n=1), Time invariant (n=1)), backend=numpy), time=Time invariant (n=1), origin_rel=array([[0., 0., 0.]]), check=True, backend=None)[source]

Create a Ray object from origin points and heading/elevation angles.

Parameters:
  • az_el (Array) – Nx2 array of azimuth and elevation angles in degrees, relative to the reference frame.

  • frame (Frame, optional) – Reference frame for the ray origin and direction. Defaults to ECEF frame.

  • time (Time, optional) – Time object associated with the rays.

  • origin_rel (Array, optional) – Nx3 array of ray origin points in local frame coordinates. Defaults to (0,0,0), which is the reference frame origin.

  • check (bool, optional) – Whether to check input arrays for validity (not JIT compatible). Defaults to True.

  • backend (BackendArg, optional) – Array backend to use (numpy, jax, etc.). Defaults to numpy.

Return type:

Ray

classmethod from_camera(pixel, camera, frame, backend=None)[source]

Create a Ray object from pixel coordinates and a camera model.

Parameters:
  • pixel (Pixel) – Pixel object defining the pixel coordinates and optional time.

  • camera (CameraLike) – Camera model defining the camera parameters and orientation.

  • frame (Frame) – Reference frame for the ray origin and direction.

  • backend (BackendArg, optional) – Array backend to use (numpy, jax, etc.). Defaults to numpy.

Returns:

Ray object defined by the pixel coordinates and camera model.

Return type:

Ray

classmethod from_points(endpoint, origin, time=Time invariant (n=1), check=True, backend=None)[source]

Create a Ray object from origin and endpoint arrays in ECEF frame.

Parameters:
  • endpoint (Point) – End points (ECEF coordinates). Must be length N.

  • origin (Point) – Origin points (ECEF coordinates). Must be length N or 1.

  • time (Time, optional) – Time object associated with the rays. If TIME_INVARIANT, time is inferred from endpoint or origin when available.

  • check (bool, optional) – If True, validate geometry (non-zero directions). Disable for JIT compatibility. Defaults to True.

  • backend (BackendArg, optional) – Array backend to use (numpy, jax, etc.). Defaults to numpy.

Returns:

Ray object defined by the origin and direction from origin to endpoint.

Return type:

Ray

classmethod from_target_frame(target, frame, check_bounds=True, backend=None)[source]

Create a Ray object from a reference frame and target point(s).

Parameters:
  • target (Point) – Target point(s) in ECEF coordinates. Must be length N or 1.

  • frame (Frame) – Reference frame for the ray origin and direction.

  • check_bounds (bool, optional) – If True, ensure target times fall within frame time ranges.

  • backend (BackendArg, optional) – Array backend to use (numpy, jax, etc.). Defaults to numpy.

Returns:

Ray object defined by the frame origin and direction to the target point(s).

Return type:

Ray

convert_to(backend)[source]

Convert the Ray object to a different backend.

Return type:

Ray

Parameters:

backend (str | Any | None)

correct_refraction(alt=100000.0)[source]

Apply atmospheric refraction correction to the Ray object using Bennett’s formula. Altitude sets the exponential scale height (metres) for the correction.

Returns:

New Ray object with refraction-corrected direction vectors.

Return type:

Ray

Parameters:

alt (float)

Notes

  • Assumes standard atmospheric conditions.

interp(time, check_bounds=True)[source]

Interpolate the Ray origin and direction to the given times.

Parameters:
  • time (Time) – Times to interpolate to.

  • check_bounds (bool, optional) – Whether to check if the interpolation times are within the ray time bounds. Defaults to True.

Returns:

Interpolated Ray object at the given times.

Return type:

Ray

project_to_cam(camera)[source]

Project the Ray object to pixel coordinates using a camera model.

Parameters:

camera (CameraLike) – Camera model defining the camera parameters and orientation.

Returns:

Pixel object defining the pixel coordinates and associated time.

Return type:

Pixel

Notes

  • The Ray must be defined in the same reference frame as the camera.

  • Rays that do not intersect the image plane will result in NaN pixel coordinates.

replace_frame(frame)[source]

Replace the reference frame of the Ray without changing origin or direction. Not a transformation, but direct replacement. Use with caution.

Parameters:

frame (Frame) – New reference frame for the ray.

Returns:

Ray object with the new reference frame.

Return type:

Ray

to_ecef()[source]

Convert the Ray object to ECEF coordinates.

Return type:

Ray

to_frame(frame)[source]

Convert the Ray object to a different reference frame.

Parameters:

frame (Frame) – Reference frame to convert the ray to.

Returns:

Ray object defined in the new reference frame.

Return type:

Ray

to_ned()[source]

Convert the Ray object to a local NED frame at the ray origin.

Return type:

Ray

property az_el

Return the azimuth and elevation angles from the forward axis in degrees.

property backend: str

Get the name of the array backend in use (e.g., ‘numpy’, ‘jax’).

property frame: Frame

Get the reference Frame of the ray.

property has_time: bool

Check if the Ray has associated Time.

property origin_points: Point

Get the ray origin point(s) as ECEF. Note: this involves a frame transformation. For repeated access, recommend converting the Ray to ECEF frame first using to_ecef().

property origin_rel: Any

Get the ray origin point(s) in the local frame coordinates. Typically zero for camera reference frames, or ECEF coordinates for ECEF frame rays.

property time: Time

Get the associated Time object, if any.

property total_angle: Any

Return the total angle from the forward axis in degrees.

property unit_ecef: Any

Get the unit direction vector(s) of the ray in ECEF frame. Note: this involves a frame transformation. For repeated access, recommend converting the Ray to ECEF frame first using to_ecef().

property unit_rel: Any

Get the unit direction vector(s) of the ray in the local frame coordinates.

class RotationSequence(rot, time, backend=None)[source]

Bases: RotationLike

A sequence of time-tagged rotations, enabling interpolation between them. Uses scipy.spatial.transform.Slerp for interpolation.

Parameters:
  • rot (Rotation | list of Rotation) – A scipy Rotation object containing multiple rotations, or a list of such objects. If a list is provided, all elements must be scipy Rotation objects.

  • time (Time) – A Time object with time instances corresponding to each rotation. Must be the same length as the number of rotations and strictly increasing.

  • backend (BackendArg, optional) – Array backend to use (numpy, jax, etc.). Defaults to numpy.

Examples

>>> from astrix import Time, RotationSequence
>>> from scipy.spatial.transform import Rotation
>>> from datetime import datetime, timezone
>>> times = Time.from_datetime(
...     [
...         datetime(2021, 1, 1, 12, 0, 0, tzinfo=timezone.utc),
...         datetime(2021, 1, 1, 13, 0, 0, tzinfo=timezone.utc),
...         datetime(2021, 1, 1, 14, 0, 0, tzinfo=timezone.utc),
...     ]
... )
>>> rots = Rotation.from_euler(
...     "xyz",
...     [
...         [0, 0, 0],
...         [90, 0, 0],
...         [180, 0, 0],
...     ],
...     degrees=True,
... )
>>> rot_seq = RotationSequence(rots, times)
>>> interp_rot = rot_seq.interp(
...     Time.from_datetime(datetime(2021, 1, 1, 12, 30, 0, tzinfo=timezone.utc))
... )  # Interpolate to halfway between first and second rotation
>>> interp_rot.as_euler(
...     "xyz", degrees=True
... )  # Get interpolated rotation as Euler angles
array([[45.,  0.,  0.]])
convert_to(backend)[source]

Convert the RotationSequence object to a different backend.

Return type:

RotationSequence

Parameters:

backend (str | Any | None)

downsample(dt_max)[source]

Downsample the rotation sequence to a coarser time resolution.

Parameters:

dt_max (float) – Desired maximum time step in seconds for downsampling.

Returns:

A new RotationSequence object with downsampled rotations.

Return type:

RotationSequence

interp(time, check_bounds=True)[source]

Interpolate the rotation sequence at the given times to return Rotation(s).

Return type:

Rotation

Parameters:
  • time (Time)

  • check_bounds (bool)

property rots: Rotation

Get the underlying scipy Rotation object containing all rotations.

property time: Time

Get the Time object associated with the rotation sequence.

class Time(unix, backend=None, invariant=False)[source]

Bases: object

One or more time instances.

Represents time using seconds since Unix epoch (1970-01-01 00:00:00 UTC). Can handle single time instances or arrays of times with consistent backend support for JAX/NumPy compatibility.

Parameters:
  • unix (Array | list of float | float) – Time values in seconds since Unix epoch (1970-01-01 UTC)

  • backend (BackendArg, optional) – Array backend to use (numpy, jax, etc.). Defaults to numpy.

  • invariant (bool)

Examples

Single time instance:

>>> t = Time(1609459200.0)  # 2021-01-01 00:00:00 UTC

Multiple times:

>>> times = Time([1609459200.0, 1609545600.0])  # Jan 1-2, 2021

From datetime:

>>> from datetime import datetime, timezone
>>> dt = datetime(2021, 1, 1, tzinfo=timezone.utc)
>>> t = Time.from_datetime(dt)
>>> dt_list = [
...     datetime(2021, 1, 1, tzinfo=timezone.utc),
...     datetime(2021, 1, 2, tzinfo=timezone.utc),
... ]
>>> times = Time.from_datetime(dt_list)

Notes

All datetime objects must be timezone-aware to avoid ambiguity.

classmethod from_datetime(time, backend=None)[source]

Create a Time object from a single or list of datetime objects. Will not accept timezone-unaware datetime obejects due to ambiguity.

Return type:

Time

Parameters:
  • time (datetime | list[datetime])

  • backend (str | Any | None)

classmethod invariant(n=1, backend=None)[source]

Create a time-invariant Time object of length n.

Return type:

Time

Parameters:
  • n (int)

  • backend (str | Any | None)

convert_to(backend)[source]

Convert the Time object to a different backend.

Return type:

Time

Parameters:

backend (str | Any | None)

copy()[source]
Return type:

Time

in_bounds(time)[source]

Check if the given time(s) are within the bounds of this Time object.

Return type:

bool

Parameters:

time (Time)

nearest_idx(time)[source]

Find the index of the nearest time in this Time object for each time in the input Time object.

Parameters:

time (Time) – Time object containing times to find nearest indices for.

Returns:

Array of indices corresponding to the nearest times in this Time object.

Return type:

Array

Examples

>>> from astrix import Time
>>> from datetime import datetime, timezone
>>> t1 = Time.from_datetime(
...     [
...         datetime(2021, 1, 1, tzinfo=timezone.utc),
...         datetime(2021, 1, 2, tzinfo=timezone.utc),
...         datetime(2021, 1, 3, tzinfo=timezone.utc),
...     ]
... )
>>> t2 = Time.from_datetime(
...     [
...         datetime(2021, 1, 1, 12, tzinfo=timezone.utc),
...         datetime(2021, 1, 2, 12, tzinfo=timezone.utc),
...     ]
... )
>>> idx = t1.nearest_idx(t2)
>>> idx.tolist()
[0, 1]
offset(offset)[source]

Return a new Time object with offset (seconds) added to all time values.

Return type:

Time

Parameters:

offset (float)

return_in_bounds(time)[source]

Return a new Time object containing only the times within the bounds of this Time object.

Return type:

Time

Parameters:

time (Time)

property backend: str

Get the name of the array backend in use (e.g., ‘numpy’, ‘jax.numpy’).

property datetime: list[datetime]
property duration: float | Any

Get the duration between the first and last time in seconds.

property end_sec: float | Any

Get the end time in seconds since epoch.

property is_increasing: bool

Check if the time values are strictly increasing.

property is_invariant: bool

Whether the Time object represents an invariant (constant) time.

property is_singular: bool

Check if the Time object represents a single time instance.

property start_sec: float | Any

Get the start time in seconds since epoch.

property unix: Any

Get the time values in seconds since epoch.

class TimeGroup(times, backend=None)[source]

Bases: object

A group of Time objects. Used to manage multiple time instances and determine overlapping time bounds.

Parameters:
  • times (list of Time) – List of Time objects (Time, TimeGroup)

  • backend (BackendArg, optional) – Array backend to use (numpy, jax, etc.). Defaults to numpy.

Examples

>>> t1 = Time.from_datetime(
...     [
...         datetime(2021, 1, 1, 12, 0, 0, tzinfo=timezone.utc),
...         datetime(2021, 1, 1, 13, 0, 0, tzinfo=timezone.utc),
...     ]
... )
>>> t2 = Time.from_datetime(
...     [
...         datetime(2021, 1, 1, 12, 30, 0, tzinfo=timezone.utc),
...         datetime(2021, 1, 1, 14, 0, 0, tzinfo=timezone.utc),
...     ]
... )
>>> tg = TimeGroup([t1, t2])
>>> tg.duration  # Duration of overlap in seconds
1800.0
>>> overlap = tg.overlap_bounds  # Overlapping time range
>>> assert overlap[0].datetime[0] == datetime(
...     2021, 1, 1, 12, 30, 0, tzinfo=timezone.utc
... )
>>> assert overlap[1].datetime[0] == datetime(
...     2021, 1, 1, 13, 0, 0, tzinfo=timezone.utc
... )
>>> tg.in_bounds(
...     Time.from_datetime(datetime(2021, 1, 1, 12, 45, 0, tzinfo=timezone.utc))
... )
True
convert_to(backend)[source]

Convert the TimeGroup object to a different backend.

Return type:

TimeGroup

Parameters:

backend (str | Any | None)

in_bounds(time)[source]

Check if the given time(s) are within the overlap bounds of this TimeGroup.

Return type:

bool

Parameters:

time (Time)

property backend: str
property duration: float | Any

Get the duration of the overlap bounds in seconds.

property extreme_bounds: tuple[Time, Time]

Get the extreme bounds of the TimeGroup as Time objects.

property is_invariant: bool
property overlap_bounds: tuple[Time, Time]

Get the overlap bounds of the TimeGroup as Time objects.

property times: tuple[Time, ...]
class Velocity(vec, time, _xp)[source]

Bases: object

Velocity vector(s) in ECEF coordinates (vx, vy, vz) in m/s. Associated with a Time object for the time instances of the velocities. Internal use only, typically created from Path objects. No data validation is performed.

Parameters:
  • vec (Array) – Velocity vectors in ECEF coordinates (vx, vy, vz) in m/s. Shape (n, 3).

  • time (Time) – Time object associated with the velocities. Length must match number of velocity vectors.

  • backend (BackendArg, optional) – Array backend to use (numpy, jax, etc.). Defaults to numpy.

  • _xp (Any)

Examples

Velocity objects are typically created from Path objects.

>>> from astrix import Point, Time, Path
>>> from datetime import datetime, timezone
>>> times = Time.from_datetime([
...     datetime(2025, 1, 1, 12, 0, 0, tzinfo=timezone.utc),
...     datetime(2025, 1, 1, 12, 0, 1, tzinfo=timezone.utc),
...     datetime(2025, 1, 1, 12, 0, 2, tzinfo=timezone.utc),
... ])
>>> path = Path([
...     Point([1, 2, 0], time=times[0]),
...     Point([2, 3.8, 0.4], time=times[1]),
...     Point([3, 6.0, 1], time=times[2]),
... ])
>>> vel = path.vel
>>> vel.magnitude
array([1.91049732, 2.29128785, 2.6925824])
classmethod from_data(vec, time, backend=None)[source]

Create a Velocity object from velocity vector array and Time object.

Return type:

Velocity

Parameters:
  • vec (Buffer | _SupportsArray[dtype[Any]] | _NestedSequence[_SupportsArray[dtype[Any]]] | complex | bytes | str | _NestedSequence[complex | bytes | str])

  • time (Time)

  • backend (str | Any | None)

convert_to(backend)[source]

Convert the Velocity object to a different backend.

Return type:

Velocity

Parameters:

backend (str | Any | None)

interp(time, method='linear', check_bounds=True)[source]

Interpolate the Velocity to the given times using the specified method. Currently only ‘linear’ interpolation is supported.

Return type:

Velocity

Parameters:
  • time (Time)

  • method (str)

  • check_bounds (bool)

rotate(rot)[source]
Return type:

Velocity

Parameters:

rot (Rotation)

property acc: Acceleration

Calculate the acceleration from the velocity using central differences.

property backend: str
property magnitude: Any

Get the velocity magnitude in m/s.

time: Time
property unit: Any

Get the unit velocity vector.

vec: Any
resolve_backend(name_or_mod=None)[source]

Resolve the backend (array namespace) from a string or module.

Return type:

ModuleType

Parameters:

name_or_mod (str | ModuleType | None)

time_linspace(t1, t2, num)[source]

Create a Time object with linearly spaced times between two Time objects. If t1 and t2 are non-singular, uses t1[0] and t2[-1].

Parameters:
  • t1 (Time) – Start time.

  • t2 (Time) – End time.

  • num (int) – Number of time points to generate.

Returns:

Time object with linearly spaced times.

Return type:

Time

Examples

>>> from astrix import Time
>>> from datetime import datetime, timezone
>>> t_start = Time.from_datetime(datetime(2021, 1, 1, tzinfo=timezone.utc))
>>> t_end = Time.from_datetime(datetime(2021, 1, 2, tzinfo=timezone.utc))
>>> t_lin = time_linspace(t_start, t_end, num=5)
>>> t_lin.datetime
[datetime.datetime(2021, 1, 1, 0, 0, tzinfo=datetime.timezone.utc),
 datetime.datetime(2021, 1, 1, 6, 0, tzinfo=datetime.timezone.utc),
 datetime.datetime(2021, 1, 1, 12, 0, tzinfo=datetime.timezone.utc),
 datetime.datetime(2021, 1, 1, 18, 0, tzinfo=datetime.timezone.utc),
 datetime.datetime(2021, 1, 2, 0, 0, tzinfo=datetime.timezone.utc)]

Subpackages

Submodules