astrix.spatial.frame module

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.primitives import Frame, Point
>>> from scipy.spatial.transform import Rotation
>>>
>>> rot = Rotation.from_euler(
...     "xyz", [90, 0, 0], degrees=True
... )  # 90 degree rotation about x-axis
>>> loc = Point.from_geodet([27.47, 153.03, 0])  # Brisbane location
>>> frame_static = Frame(rot, loc)  # Frame with static rotation and location
>>> frame_static.interp_rot().as_euler("xyz", degrees=True)  # Get absolute rotation
array([[90.,  0.,  0.]])
>>> frame_static.loc.geodet  # Get frame location in geodetic coordinates
array([[153.03, 27.47, 0.0]])

Time-varying frame with rotation sequence and static location:

>>> from astrix.primitives import Time
>>> 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)
>>> loc = Point.from_geodet([27.47, 153.03, 0])  # Brisbane location
>>> frame_dynamic_rot = Frame(
...     rot_seq, loc
... )  # Frame with time-varying rotation and static location
>>> interp_rot = frame_dynamic_rot.interp_rot(
...     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 absolute rotation as Euler angles
array([[45.,  0.,  0.]])
>>> frame_dynamic_rot.loc.geodet  # Get frame location in geodetic coordinates
array([[153.03, 27.47, 0.0]])

Frame defined relative to another frame:

>>> rot_ref = Rotation.from_euler(
...     "xyz", [0, 30, 0], degrees=True
... )  # Reference frame
>>> frame_ref = Frame(rot_ref, loc)  # Reference frame
>>> 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
... )  # Absolute rotation (rot_ref * rot_rel)
array([[ 0., 70.,  0.]])
>>> frame.loc.geodet  # (Same as reference frame)
array([[153.03, 27.47, 0.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 location and rotation indeces align. Prefer interp_rot 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=TimeInvariant object, 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:
interp_rot(time=TimeInvariant object, 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:
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.

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[TimeLike]

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[TimeLike, TimeLike]

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.

heading_frame(path, downsample=1.0, name='heading frame')[source]

Create a heading-aligned frame at each point along a Path. The frame is defined such that the first aligns with the heading direction (azimuth), the z-axis aligns with the local vertical plane, and the y-axis completes the right-handed system.

Parameters:
  • path (Path) – Path object defining the trajectory.

  • downsample (float, optional) – Downsample interval for Path objects in seconds. If None, no downsampling is performed. Defaults to 10s. If path time resolution is greater than downsample interval, the Path will be downsampled before creating the heading frame to reduce computational load.

  • name (str, optional) – Name of the frame. Defaults to “heading frame”.

Returns:

Heading-aligned frame along the Path.

Return type:

Frame

Notes

  • Adopts backend from Path object.

ned_frame(loc, downsample=10.0, name='NED frame')[source]

Create a local NED (North-East-Down) frame at the given location(s). NED rotations are evaluated at all times in the Point/Path.

Parameters:
  • loc (Location) – Location(s) to define the NED frame origin. Must be single point or time-varying Path.

  • downsample (float, optional) – Downsample interval for Path objects in seconds. If None, no downsampling is performed. Defaults to 10s. If loc is a Path and time resolution is greater than downsample interval, the Path will be downsampled before creating the NED frame to reduce computational load. Note that NED frames likely vary slowly compared to other frames (gimbals, aircraft, etc.), so downsampling is recommended for high-resolution paths.

  • name (str, optional) – Name of the frame. Defaults to “NED frame”.

Returns:

NED frame at the given location(s).

Return type:

Frame

Notes

  • Adopts backend from Location object.

velocity_frame(path, downsample=1.0, name='velocity frame')[source]

Create a velocity-aligned frame at each point along a Path. The frame is defined such that the x-axis aligns with the velocity vector, the z-axis aligns with the local vertical plane, and the y-axis completes the right-handed system.

Parameters:
  • path (Path) – Path object defining the trajectory.

  • downsample (float, optional) – Downsample interval for Path objects in seconds. If None, no downsampling is performed. Defaults to 10s. If path time resolution is greater than downsample interval, the Path will be downsampled before creating the velocity frame to reduce computational load.

  • name (str, optional) – Name of the frame. Defaults to “velocity frame”.

Returns:

Velocity-aligned frame along the Path.

Return type:

Frame

Notes

  • Adopts backend from Path object.