Skip to main content

lox_frames/
frames.rs

1// SPDX-FileCopyrightText: 2025 Helge Eichhorn <git@helgeeichhorn.de>
2//
3// SPDX-License-Identifier: MPL-2.0
4
5use lox_bodies::{Origin, RotationalElements, TryRotationalElements, UndefinedOriginPropertyError};
6
7use crate::{
8    iers::IersSystem,
9    traits::{BodyFixed, QuasiInertial, ReferenceFrame},
10};
11
12const ICRF_ID: usize = 0;
13const CIRF_ID: usize = 1;
14const TIRF_ID: usize = 2;
15const ITRF_ID: usize = 3;
16const J2000_ID: usize = 4;
17
18const MOD_ID: usize = 11;
19const TOD_ID: usize = 12;
20const PEF_ID: usize = 13;
21
22/// International Celestial Reference Frame.
23#[derive(Clone, Copy, Debug, PartialEq, Eq)]
24#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
25pub struct Icrf;
26
27impl ReferenceFrame for Icrf {
28    fn name(&self) -> String {
29        "International Celestial Reference Frame".to_string()
30    }
31
32    fn abbreviation(&self) -> String {
33        "ICRF".to_string()
34    }
35
36    fn frame_id(&self, _: crate::traits::private::Internal) -> Option<usize> {
37        Some(ICRF_ID)
38    }
39}
40
41impl QuasiInertial for Icrf {}
42
43/// J2000 Mean Equator and Equinox frame.
44#[derive(Clone, Copy, Debug, PartialEq, Eq)]
45#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
46pub struct J2000;
47
48impl ReferenceFrame for J2000 {
49    fn name(&self) -> String {
50        "J2000 Mean Equator and Equinox".to_string()
51    }
52
53    fn abbreviation(&self) -> String {
54        "J2000".to_string()
55    }
56
57    fn frame_id(&self, _: crate::traits::private::Internal) -> Option<usize> {
58        Some(J2000_ID)
59    }
60}
61
62impl QuasiInertial for J2000 {}
63
64/// Celestial Intermediate Reference Frame.
65#[derive(Clone, Copy, Debug, PartialEq, Eq)]
66#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
67pub struct Cirf;
68
69impl ReferenceFrame for Cirf {
70    fn name(&self) -> String {
71        "Celestial Intermediate Reference Frame".to_string()
72    }
73
74    fn abbreviation(&self) -> String {
75        "CIRF".to_string()
76    }
77
78    fn frame_id(&self, _: crate::traits::private::Internal) -> Option<usize> {
79        Some(CIRF_ID)
80    }
81}
82
83/// Terrestrial Intermediate Reference Frame.
84#[derive(Clone, Copy, Debug, PartialEq, Eq)]
85#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
86pub struct Tirf;
87
88impl ReferenceFrame for Tirf {
89    fn name(&self) -> String {
90        "Terrestrial Intermediate Reference Frame".to_string()
91    }
92
93    fn abbreviation(&self) -> String {
94        "TIRF".to_string()
95    }
96
97    fn frame_id(&self, _: crate::traits::private::Internal) -> Option<usize> {
98        Some(TIRF_ID)
99    }
100}
101
102/// International Terrestrial Reference Frame.
103#[derive(Clone, Copy, Debug, PartialEq, Eq)]
104#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
105pub struct Itrf;
106
107impl ReferenceFrame for Itrf {
108    fn name(&self) -> String {
109        "International Terrestrial Reference Frame".to_string()
110    }
111
112    fn abbreviation(&self) -> String {
113        "ITRF".to_string()
114    }
115
116    fn frame_id(&self, _: crate::traits::private::Internal) -> Option<usize> {
117        Some(ITRF_ID)
118    }
119}
120
121/// Mean of Date frame, parameterised by IERS convention.
122#[derive(Debug, Clone, Copy, PartialEq, Eq)]
123#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
124pub struct Mod<T: IersSystem>(pub T);
125
126impl<T> ReferenceFrame for Mod<T>
127where
128    T: IersSystem,
129{
130    fn name(&self) -> String {
131        format!("{} Mean of Date Frame", self.0.name())
132    }
133
134    fn abbreviation(&self) -> String {
135        format!("MOD({})", self.0.name())
136    }
137
138    fn frame_id(&self, _: crate::traits::private::Internal) -> Option<usize> {
139        Some(MOD_ID * 10 + self.0.id())
140    }
141}
142
143/// True of Date frame, parameterised by IERS convention.
144#[derive(Debug, Clone, Copy, PartialEq, Eq)]
145#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
146pub struct Tod<T: IersSystem>(pub T);
147
148impl<T> ReferenceFrame for Tod<T>
149where
150    T: IersSystem,
151{
152    fn name(&self) -> String {
153        format!("{} True of Date Frame", self.0.name())
154    }
155
156    fn abbreviation(&self) -> String {
157        format!("TOD({})", self.0.name())
158    }
159
160    fn frame_id(&self, _: crate::traits::private::Internal) -> Option<usize> {
161        Some(TOD_ID * 10 + self.0.id())
162    }
163}
164
165/// Pseudo-Earth Fixed frame, parameterised by IERS convention.
166#[derive(Debug, Clone, Copy, PartialEq, Eq)]
167#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
168pub struct Pef<T: IersSystem>(pub T);
169
170impl<T> ReferenceFrame for Pef<T>
171where
172    T: IersSystem,
173{
174    fn name(&self) -> String {
175        format!("{} Pseudo-Earth Fixed Frame", self.0.name())
176    }
177
178    fn abbreviation(&self) -> String {
179        format!("PEF({})", self.0.name())
180    }
181
182    fn frame_id(&self, _: crate::traits::private::Internal) -> Option<usize> {
183        Some(PEF_ID * 10 + self.0.id())
184    }
185}
186
187/// True Equator Mean Equinox frame.
188#[derive(Debug, Clone, Copy, PartialEq, Eq)]
189#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
190pub struct Teme;
191
192impl ReferenceFrame for Teme {
193    fn name(&self) -> String {
194        "True Equator Mean Equinox".to_owned()
195    }
196
197    fn abbreviation(&self) -> String {
198        "TEME".to_owned()
199    }
200
201    fn frame_id(&self, _: crate::traits::private::Internal) -> Option<usize> {
202        Some(7)
203    }
204}
205
206impl BodyFixed for Itrf {}
207
208/// IAU body-fixed reference frame derived from rotational elements.
209#[derive(Clone, Copy, Debug, PartialEq, Eq)]
210#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
211pub struct Iau<T: TryRotationalElements>(T);
212
213impl<T> Iau<T>
214where
215    T: RotationalElements,
216{
217    /// Creates an IAU frame for a body with known rotational elements.
218    pub fn new(body: T) -> Self {
219        Self(body)
220    }
221}
222
223impl<T> Iau<T>
224where
225    T: TryRotationalElements,
226{
227    /// Creates an IAU frame, returning an error if rotational elements are undefined.
228    pub fn try_new(body: T) -> Result<Self, UndefinedOriginPropertyError> {
229        let _ = body.try_right_ascension(0.0)?;
230        Ok(Self(body))
231    }
232
233    /// Returns the underlying body.
234    pub fn body(&self) -> T
235    where
236        T: Copy,
237    {
238        self.0
239    }
240
241    /// Returns the rotational elements (right ascension, declination, prime meridian) at
242    /// the given Julian centuries since J2000.
243    pub fn rotational_elements(&self, j2000: f64) -> (f64, f64, f64) {
244        self.0.try_rotational_elements(j2000).unwrap()
245    }
246
247    /// Returns the time derivatives of the rotational elements.
248    pub fn rotational_element_rates(&self, j2000: f64) -> (f64, f64, f64) {
249        self.0.try_rotational_element_rates(j2000).unwrap()
250    }
251}
252
253impl<T: TryRotationalElements> BodyFixed for Iau<T> {}
254
255impl<T> ReferenceFrame for Iau<T>
256where
257    T: TryRotationalElements + Origin,
258{
259    fn name(&self) -> String {
260        let body = self.0.name();
261        match body {
262            "Sun" | "Moon" => format!("IAU Body-Fixed Reference Frame for the {body}"),
263            _ => format!("IAU Body-Fixed Reference Frame for {body}"),
264        }
265    }
266
267    fn abbreviation(&self) -> String {
268        let body = self.0.name().replace([' ', '-'], "_").to_uppercase();
269        format!("IAU_{body}")
270    }
271
272    fn frame_id(&self, _: crate::traits::private::Internal) -> Option<usize> {
273        Some(1000 + self.0.id().0 as usize)
274    }
275}