Skip to main content

lox_frames/
traits.rs

1// SPDX-FileCopyrightText: 2025 Helge Eichhorn <git@helgeeichhorn.de>
2//
3// SPDX-License-Identifier: MPL-2.0
4
5use lox_bodies::{CoordinateOrigin, NaifId, UndefinedOriginPropertyError};
6use lox_core::coords::Ellipsoid;
7use thiserror::Error;
8
9use crate::iers::ReferenceSystem;
10
11pub(crate) mod private {
12    /// Internal token to seal `frame_key`.
13    pub struct Internal;
14}
15
16/// Structural identity of a reference frame, used to detect when two frames —
17/// whether expressed concretely or as a [`Frame`](crate::Frame) — are the
18/// same, without rotating.
19#[derive(Clone, Copy, Debug, PartialEq, Eq)]
20pub enum FrameKey {
21    /// International Celestial Reference Frame.
22    Icrf,
23    /// J2000 Mean Equator and Equinox.
24    J2000,
25    /// Celestial Intermediate Reference Frame.
26    Cirf,
27    /// Terrestrial Intermediate Reference Frame.
28    Tirf,
29    /// International Terrestrial Reference Frame.
30    Itrf,
31    /// True Equator Mean Equinox.
32    Teme,
33    /// Mean of Date for the given IERS convention.
34    Mod(ReferenceSystem),
35    /// True of Date for the given IERS convention.
36    Tod(ReferenceSystem),
37    /// Pseudo-Earth Fixed for the given IERS convention.
38    Pef(ReferenceSystem),
39    /// IAU body-fixed frame for the given body.
40    Iau(NaifId),
41}
42
43/// A reference frame with a human-readable name and abbreviation.
44pub trait ReferenceFrame {
45    /// Returns the full name of the frame (e.g. "International Celestial Reference Frame").
46    fn name(&self) -> String;
47    /// Returns the abbreviated name (e.g. "ICRF").
48    fn abbreviation(&self) -> String;
49    #[doc(hidden)]
50    fn frame_key(&self, _: private::Internal) -> Option<FrameKey> {
51        None
52    }
53}
54
55/// Returns the frame's identity key, if it has one.
56pub fn frame_key(frame: &impl ReferenceFrame) -> Option<FrameKey> {
57    frame.frame_key(private::Internal)
58}
59
60/// Marker trait for quasi-inertial reference frames.
61pub trait QuasiInertial: ReferenceFrame {}
62
63/// The frame is not quasi-inertial.
64#[derive(Clone, Debug, Error, Eq, PartialEq)]
65#[error("{0} is not a quasi-inertial frame")]
66pub struct NonQuasiInertialFrameError(pub String);
67
68/// Fallible check for quasi-inertial frames (used by dynamic dispatch).
69pub trait TryQuasiInertial: ReferenceFrame {
70    /// Returns `Ok(())` if the frame is quasi-inertial.
71    fn try_quasi_inertial(&self) -> Result<(), NonQuasiInertialFrameError>;
72}
73
74impl<T: QuasiInertial> TryQuasiInertial for T {
75    fn try_quasi_inertial(&self) -> Result<(), NonQuasiInertialFrameError> {
76        Ok(())
77    }
78}
79
80/// Marker trait for body-fixed reference frames.
81pub trait BodyFixed: ReferenceFrame {
82    /// The coordinate origin (central body) of the body-fixed frame.
83    type Origin: CoordinateOrigin + Copy;
84
85    /// Returns the coordinate origin (central body) of the body-fixed frame.
86    fn origin(&self) -> Self::Origin;
87}
88
89/// The frame is not body-fixed.
90#[derive(Clone, Debug, Error)]
91#[error("{0} is not a body-fixed frame")]
92pub struct NonBodyFixedFrameError(pub String);
93
94/// Fallible check for body-fixed frames (used by dynamic dispatch).
95pub trait TryBodyFixed: ReferenceFrame {
96    /// The coordinate origin (central body) of the body-fixed frame.
97    type Origin: CoordinateOrigin + Copy;
98
99    /// Returns `Ok(())` if the frame is body-fixed.
100    fn try_body_fixed(&self) -> Result<(), NonBodyFixedFrameError>;
101
102    /// Returns the coordinate origin (central body) of the body-fixed frame.
103    fn try_origin(&self) -> Result<Self::Origin, NonBodyFixedFrameError>;
104}
105
106impl<T: BodyFixed> TryBodyFixed for T {
107    type Origin = T::Origin;
108
109    fn try_body_fixed(&self) -> Result<(), NonBodyFixedFrameError> {
110        Ok(())
111    }
112
113    fn try_origin(&self) -> Result<Self::Origin, NonBodyFixedFrameError> {
114        Ok(self.origin())
115    }
116}
117
118/// A body-fixed frame with a conventional reference ellipsoid.
119///
120/// The ellipsoid is the one conventionally paired with the frame rather than an
121/// intrinsic property of its origin: the terrestrial frames
122/// ([`Itrf`](crate::Itrf), [`Tirf`](crate::Tirf) and [`Pef`](crate::Pef)) are
123/// paired with GRS80, while [`Iau`](crate::Iau) frames use their body's own
124/// spheroid. Callers needing a different datum supply the ellipsoid explicitly.
125pub trait ReferenceEllipsoid: BodyFixed {
126    /// Returns the reference ellipsoid conventionally paired with this frame.
127    fn reference_ellipsoid(&self) -> Ellipsoid;
128}
129
130/// Fallible accessor for a frame's reference ellipsoid (used by dynamic dispatch).
131pub trait TryReferenceEllipsoid: TryBodyFixed {
132    /// Returns the reference ellipsoid conventionally paired with this frame,
133    /// or an error if the frame is not body-fixed or its origin is not a spheroid.
134    fn try_reference_ellipsoid(&self) -> Result<Ellipsoid, UndefinedReferenceEllipsoidError>;
135}
136
137impl<T> TryReferenceEllipsoid for T
138where
139    T: ReferenceEllipsoid,
140{
141    fn try_reference_ellipsoid(&self) -> Result<Ellipsoid, UndefinedReferenceEllipsoidError> {
142        Ok(self.reference_ellipsoid())
143    }
144}
145
146/// Error returned when a frame has no reference ellipsoid.
147#[derive(Debug, thiserror::Error)]
148pub enum UndefinedReferenceEllipsoidError {
149    /// The frame is not body-fixed, so no datum is associated with it.
150    #[error(transparent)]
151    NotBodyFixed(#[from] NonBodyFixedFrameError),
152    /// The frame's origin has no spheroid, as for a triaxial body.
153    #[error(transparent)]
154    UndefinedSpheroid(#[from] UndefinedOriginPropertyError),
155}