1use 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}