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