Skip to main content

lox_frames/
iers.rs

1// SPDX-FileCopyrightText: 2025 Helge Eichhorn <git@helgeeichhorn.de>
2//
3// SPDX-License-Identifier: MPL-2.0
4
5use std::{
6    fmt::Display,
7    ops::{Add, AddAssign},
8};
9
10use glam::{DMat3, DVec3};
11use lox_units::Angle;
12
13use crate::iers::{cip::CipCoords, ecliptic::MeanObliquity, nutation::Nutation};
14
15pub mod cio;
16pub mod cip;
17pub mod earth_rotation;
18pub mod ecliptic;
19pub mod fundamental;
20pub mod nutation;
21pub mod polar_motion;
22pub mod precession;
23pub mod tio;
24
25mod sealed {
26    pub trait Sealed {}
27    impl Sealed for super::Iers1996 {}
28    impl Sealed for super::Iers2003 {}
29    impl Sealed for super::Iers2010 {}
30    impl Sealed for super::ReferenceSystem {}
31}
32
33pub trait IersSystem: sealed::Sealed {
34    fn id(&self) -> usize;
35    fn name(&self) -> String;
36}
37
38#[derive(Debug, Clone, Copy, PartialEq, Eq)]
39#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
40pub struct Iers1996;
41
42impl IersSystem for Iers1996 {
43    fn id(&self) -> usize {
44        0
45    }
46
47    fn name(&self) -> String {
48        "IERS1996".to_owned()
49    }
50}
51
52impl Display for Iers1996 {
53    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
54        self.name().fmt(f)
55    }
56}
57
58#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
59#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
60pub enum Iau2000Model {
61    #[default]
62    A = 1,
63    B = 2,
64}
65
66impl Display for Iau2000Model {
67    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
68        match self {
69            Iau2000Model::A => "IAU2000A".fmt(f),
70            Iau2000Model::B => "IAU2000B".fmt(f),
71        }
72    }
73}
74
75#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
76#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
77pub struct Iers2003(pub Iau2000Model);
78
79impl IersSystem for Iers2003 {
80    fn id(&self) -> usize {
81        self.0 as usize
82    }
83
84    fn name(&self) -> String {
85        "IERS2003".to_owned()
86    }
87}
88
89impl Display for Iers2003 {
90    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
91        self.name().fmt(f)
92    }
93}
94
95#[derive(Debug, Clone, Copy, PartialEq, Eq)]
96#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
97pub struct Iers2010;
98
99impl IersSystem for Iers2010 {
100    fn id(&self) -> usize {
101        3
102    }
103
104    fn name(&self) -> String {
105        "IERS2010".to_owned()
106    }
107}
108
109impl Display for Iers2010 {
110    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
111        self.name().fmt(f)
112    }
113}
114
115#[derive(Debug, Clone, Copy, PartialEq, Eq)]
116#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
117pub enum ReferenceSystem {
118    Iers1996,
119    Iers2003(Iau2000Model),
120    Iers2010,
121}
122
123impl IersSystem for ReferenceSystem {
124    fn id(&self) -> usize {
125        match self {
126            ReferenceSystem::Iers1996 => Iers1996.id(),
127            ReferenceSystem::Iers2003(iau2000) => Iers2003(*iau2000).id(),
128            ReferenceSystem::Iers2010 => Iers2010.id(),
129        }
130    }
131
132    fn name(&self) -> String {
133        match self {
134            ReferenceSystem::Iers1996 => Iers1996.to_string(),
135            ReferenceSystem::Iers2003(model) => Iers2003(*model).to_string(),
136            ReferenceSystem::Iers2010 => Iers2010.to_string(),
137        }
138    }
139}
140
141impl Display for ReferenceSystem {
142    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
143        self.name().fmt(f)
144    }
145}
146
147impl From<Iers1996> for ReferenceSystem {
148    fn from(_: Iers1996) -> Self {
149        ReferenceSystem::Iers1996
150    }
151}
152
153impl From<Iers2003> for ReferenceSystem {
154    fn from(sys: Iers2003) -> Self {
155        ReferenceSystem::Iers2003(sys.0)
156    }
157}
158
159impl From<Iers2010> for ReferenceSystem {
160    fn from(_: Iers2010) -> Self {
161        ReferenceSystem::Iers2010
162    }
163}
164
165#[derive(Debug, Clone, Copy, Default)]
166#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
167pub struct Corrections(pub Angle, pub Angle);
168
169impl Corrections {
170    pub fn is_zero(&self) -> bool {
171        self.0.is_zero() && self.1.is_zero()
172    }
173}
174
175impl Add<Corrections> for Nutation {
176    type Output = Self;
177
178    fn add(self, rhs: Corrections) -> Self::Output {
179        Self {
180            dpsi: self.dpsi + rhs.0,
181            deps: self.deps + rhs.1,
182        }
183    }
184}
185
186impl AddAssign<Corrections> for Nutation {
187    fn add_assign(&mut self, rhs: Corrections) {
188        self.dpsi += rhs.0;
189        self.deps += rhs.1;
190    }
191}
192
193impl Add<Corrections> for CipCoords {
194    type Output = Self;
195
196    fn add(self, rhs: Corrections) -> Self::Output {
197        Self {
198            x: self.x + rhs.0,
199            y: self.y + rhs.1,
200        }
201    }
202}
203
204impl AddAssign<Corrections> for CipCoords {
205    fn add_assign(&mut self, rhs: Corrections) {
206        self.x += rhs.0;
207        self.y += rhs.1;
208    }
209}
210
211impl ReferenceSystem {
212    pub fn ecliptic_corrections(
213        &self,
214        corr: Corrections,
215        nut: Nutation,
216        epsa: MeanObliquity,
217        rpb: DMat3,
218    ) -> Corrections {
219        match self {
220            ReferenceSystem::Iers1996 => corr,
221            ReferenceSystem::Iers2003(_) | ReferenceSystem::Iers2010 => {
222                let Corrections(dx, dy) = corr;
223                let rbpn = nut.nutation_matrix(epsa) * rpb;
224                let v1 = DVec3::new(dx.as_f64(), dy.as_f64(), 0.0);
225                let v2 = rbpn * v1;
226                Corrections(Angle::new(v2.x / epsa.0.sin()), Angle::new(v2.y))
227            }
228        }
229    }
230}
231
232#[cfg(test)]
233mod tests {
234    use rstest::rstest;
235
236    use super::*;
237
238    #[rstest]
239    #[case(Iers1996, 0)]
240    #[case(Iers2003(Iau2000Model::A), 1)]
241    #[case(Iers2003(Iau2000Model::B), 2)]
242    #[case(Iers2010, 3)]
243    #[case(ReferenceSystem::Iers1996, 0)]
244    #[case(ReferenceSystem::Iers2003(Iau2000Model::A), 1)]
245    #[case(ReferenceSystem::Iers2003(Iau2000Model::B), 2)]
246    #[case(ReferenceSystem::Iers2010, 3)]
247    fn test_iers_convention_id<T: IersSystem>(#[case] iers: T, #[case] exp: usize) {
248        let act = iers.id();
249        assert_eq!(act, exp);
250    }
251}