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;
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}
49
50#[derive(Debug, Clone, Copy, PartialEq, Eq)]
52#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
53pub struct Iers1996;
54
55impl IersSystem for Iers1996 {
56 fn id(&self) -> usize {
57 0
58 }
59
60 fn name(&self) -> String {
61 "IERS1996".to_owned()
62 }
63}
64
65impl Display for Iers1996 {
66 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
67 self.name().fmt(f)
68 }
69}
70
71#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
73#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
74pub enum Iau2000Model {
75 #[default]
77 A = 1,
78 B = 2,
80}
81
82impl Display for Iau2000Model {
83 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
84 match self {
85 Iau2000Model::A => "IAU2000A".fmt(f),
86 Iau2000Model::B => "IAU2000B".fmt(f),
87 }
88 }
89}
90
91#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
93#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
94pub struct Iers2003(pub Iau2000Model);
95
96impl IersSystem for Iers2003 {
97 fn id(&self) -> usize {
98 self.0 as usize
99 }
100
101 fn name(&self) -> String {
102 "IERS2003".to_owned()
103 }
104}
105
106impl Display for Iers2003 {
107 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
108 self.name().fmt(f)
109 }
110}
111
112#[derive(Debug, Clone, Copy, PartialEq, Eq)]
114#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
115pub struct Iers2010;
116
117impl IersSystem for Iers2010 {
118 fn id(&self) -> usize {
119 3
120 }
121
122 fn name(&self) -> String {
123 "IERS2010".to_owned()
124 }
125}
126
127impl Display for Iers2010 {
128 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
129 self.name().fmt(f)
130 }
131}
132
133#[derive(Debug, Clone, Copy, PartialEq, Eq)]
135#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
136pub enum ReferenceSystem {
137 Iers1996,
139 Iers2003(Iau2000Model),
141 Iers2010,
143}
144
145impl IersSystem for ReferenceSystem {
146 fn id(&self) -> usize {
147 match self {
148 ReferenceSystem::Iers1996 => Iers1996.id(),
149 ReferenceSystem::Iers2003(iau2000) => Iers2003(*iau2000).id(),
150 ReferenceSystem::Iers2010 => Iers2010.id(),
151 }
152 }
153
154 fn name(&self) -> String {
155 match self {
156 ReferenceSystem::Iers1996 => Iers1996.to_string(),
157 ReferenceSystem::Iers2003(model) => Iers2003(*model).to_string(),
158 ReferenceSystem::Iers2010 => Iers2010.to_string(),
159 }
160 }
161}
162
163impl Display for ReferenceSystem {
164 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
165 self.name().fmt(f)
166 }
167}
168
169impl From<Iers1996> for ReferenceSystem {
170 fn from(_: Iers1996) -> Self {
171 ReferenceSystem::Iers1996
172 }
173}
174
175impl From<Iers2003> for ReferenceSystem {
176 fn from(sys: Iers2003) -> Self {
177 ReferenceSystem::Iers2003(sys.0)
178 }
179}
180
181impl From<Iers2010> for ReferenceSystem {
182 fn from(_: Iers2010) -> Self {
183 ReferenceSystem::Iers2010
184 }
185}
186
187#[derive(Debug, Clone, Copy, Default)]
189#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
190pub struct Corrections(pub Angle, pub Angle);
191
192impl Corrections {
193 pub fn is_zero(&self) -> bool {
195 self.0.is_zero() && self.1.is_zero()
196 }
197}
198
199impl Add<Corrections> for Nutation {
200 type Output = Self;
201
202 fn add(self, rhs: Corrections) -> Self::Output {
203 Self {
204 dpsi: self.dpsi + rhs.0,
205 deps: self.deps + rhs.1,
206 }
207 }
208}
209
210impl AddAssign<Corrections> for Nutation {
211 fn add_assign(&mut self, rhs: Corrections) {
212 self.dpsi += rhs.0;
213 self.deps += rhs.1;
214 }
215}
216
217impl Add<Corrections> for CipCoords {
218 type Output = Self;
219
220 fn add(self, rhs: Corrections) -> Self::Output {
221 Self {
222 x: self.x + rhs.0,
223 y: self.y + rhs.1,
224 }
225 }
226}
227
228impl AddAssign<Corrections> for CipCoords {
229 fn add_assign(&mut self, rhs: Corrections) {
230 self.x += rhs.0;
231 self.y += rhs.1;
232 }
233}
234
235impl ReferenceSystem {
236 pub fn ecliptic_corrections(
238 &self,
239 corr: Corrections,
240 nut: Nutation,
241 epsa: MeanObliquity,
242 rpb: DMat3,
243 ) -> Corrections {
244 match self {
245 ReferenceSystem::Iers1996 => corr,
246 ReferenceSystem::Iers2003(_) | ReferenceSystem::Iers2010 => {
247 let Corrections(dx, dy) = corr;
248 let rbpn = nut.nutation_matrix(epsa) * rpb;
249 let v1 = DVec3::new(dx.as_f64(), dy.as_f64(), 0.0);
250 let v2 = rbpn * v1;
251 Corrections(Angle::new(v2.x / epsa.0.sin()), Angle::new(v2.y))
252 }
253 }
254 }
255}
256
257#[cfg(test)]
258mod tests {
259 use rstest::rstest;
260
261 use super::*;
262
263 #[rstest]
264 #[case(Iers1996, 0)]
265 #[case(Iers2003(Iau2000Model::A), 1)]
266 #[case(Iers2003(Iau2000Model::B), 2)]
267 #[case(Iers2010, 3)]
268 #[case(ReferenceSystem::Iers1996, 0)]
269 #[case(ReferenceSystem::Iers2003(Iau2000Model::A), 1)]
270 #[case(ReferenceSystem::Iers2003(Iau2000Model::B), 2)]
271 #[case(ReferenceSystem::Iers2010, 3)]
272 fn test_iers_convention_id<T: IersSystem>(#[case] iers: T, #[case] exp: usize) {
273 let act = iers.id();
274 assert_eq!(act, exp);
275 }
276}