Skip to content

Commit 72b6326

Browse files
committed
refactor(lox-earth): refactor nutation functions
1 parent b9a9286 commit 72b6326

File tree

6 files changed

+146
-269
lines changed

6 files changed

+146
-269
lines changed

crates/lox-earth/src/nutation.rs

Lines changed: 57 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -8,28 +8,20 @@
88
99
use std::ops::Add;
1010

11+
use glam::DMat3;
1112
use lox_test_utils::ApproxEq;
12-
use lox_time::Time;
13-
use lox_time::julian_dates::JulianDate;
14-
use lox_time::time_scales::Tdb;
13+
use lox_time::{
14+
Time,
15+
time_scales::{Tdb, Tt},
16+
};
1517
use lox_units::Angle;
1618

17-
use crate::nutation::iau2000::nutation_iau2000a;
18-
use crate::nutation::iau2000::nutation_iau2000b;
19-
use crate::nutation::iau2006::nutation_iau2006a;
19+
use crate::ecliptic::MeanObliquity;
2020

2121
mod iau1980;
2222
mod iau2000;
2323
mod iau2006;
2424

25-
/// The supported IAU nutation models.
26-
pub enum Model {
27-
IAU1980,
28-
IAU2000A,
29-
IAU2000B,
30-
IAU2006A,
31-
}
32-
3325
/// Nutation components with respect to some ecliptic of date.
3426
#[derive(Debug, Default, Clone, PartialEq, ApproxEq)]
3527
pub struct Nutation {
@@ -39,6 +31,22 @@ pub struct Nutation {
3931
pub obliquity: Angle,
4032
}
4133

34+
impl Nutation {
35+
fn nutation_matrix(&self, mean_obliquity: MeanObliquity) -> DMat3 {
36+
let rot1 = mean_obliquity.0.rotation_x();
37+
let rot2 = (-self.longitude).rotation_z();
38+
let rot3 = (-(self.obliquity + mean_obliquity.0)).rotation_x();
39+
rot3 * rot2 * rot1
40+
}
41+
42+
pub fn nutation_matrix_iau1980(tdb: Time<Tdb>) -> DMat3 {
43+
let tt = tdb.with_scale(Tt);
44+
let nut = Self::iau1980(tdb);
45+
let obl = MeanObliquity::iau1980(tt);
46+
nut.nutation_matrix(obl)
47+
}
48+
}
49+
4250
impl Add for Nutation {
4351
type Output = Self;
4452

@@ -61,67 +69,52 @@ impl Add<&Self> for Nutation {
6169
}
6270
}
6371

64-
/// Calculate nutation coefficients at `time` using the given [Model].
65-
pub fn nutation(model: Model, time: Time<Tdb>) -> Nutation {
66-
let t = time.centuries_since_j2000();
67-
match model {
68-
Model::IAU1980 => Nutation::iau1980(t),
69-
Model::IAU2000A => nutation_iau2000a(t),
70-
Model::IAU2000B => nutation_iau2000b(t),
71-
Model::IAU2006A => nutation_iau2006a(t),
72-
}
73-
}
74-
7572
#[cfg(test)]
7673
mod tests {
7774
use lox_test_utils::assert_approx_eq;
7875
use lox_units::AngleUnits;
7976

8077
use super::*;
8178

82-
const TOLERANCE: f64 = 1e-12;
83-
84-
#[test]
85-
fn test_nutation_iau1980() {
86-
let time = Time::j2000(Tdb);
87-
let expected = Nutation {
88-
longitude: -0.00006750247617532478.rad(),
89-
obliquity: -0.00002799221238377013.rad(),
90-
};
91-
let actual = nutation(Model::IAU1980, time);
92-
assert_approx_eq!(expected, actual, rtol <= TOLERANCE);
93-
}
94-
9579
#[test]
96-
fn test_nutation_iau2000a() {
97-
let time = Time::j2000(Tdb);
98-
let expected = Nutation {
99-
longitude: -0.00006754422426417299.rad(),
100-
obliquity: -0.00002797083119237414.rad(),
80+
fn test_nutation_matrix() {
81+
let nut = Nutation {
82+
longitude: -9.630_909_107_115_582e-6.rad(),
83+
obliquity: 4.063_239_174_001_679e-5.rad(),
10184
};
102-
let actual = nutation(Model::IAU2000A, time);
103-
assert_approx_eq!(expected, actual, rtol <= TOLERANCE);
85+
let obl = MeanObliquity(Angle::new(0.409_078_976_335_651));
86+
let exp = DMat3::from_cols_array(&[
87+
0.999_999_999_953_622_8,
88+
8.836_239_320_236_25e-6,
89+
3.830_833_447_458_252e-6,
90+
-8.836_083_657_016_69e-6,
91+
0.999_999_999_135_465_4,
92+
-4.063_240_865_361_857_4e-5,
93+
-3.831_192_481_833_385_5e-6,
94+
4.063_237_480_216_934e-5,
95+
0.999_999_999_167_166_1,
96+
])
97+
.transpose();
98+
let act = nut.nutation_matrix(obl);
99+
assert_approx_eq!(act, exp, rtol <= 1e-12);
104100
}
105101

106102
#[test]
107-
fn test_nutation_iau2000b() {
108-
let time = Time::j2000(Tdb);
109-
let expected = Nutation {
110-
longitude: -0.00006754261253992235.rad(),
111-
obliquity: -0.00002797092331098565.rad(),
112-
};
113-
let actual = nutation(Model::IAU2000B, time);
114-
assert_approx_eq!(expected, actual, rtol <= TOLERANCE);
115-
}
116-
117-
#[test]
118-
fn test_nutation_iau2006a() {
119-
let time = Time::j2000(Tdb);
120-
let expected = Nutation {
121-
longitude: -0.00006754425598969513.rad(),
122-
obliquity: -0.00002797083119237414.rad(),
123-
};
124-
let actual = nutation(Model::IAU2006A, time);
125-
assert_approx_eq!(expected, actual, rtol <= TOLERANCE);
103+
fn test_nutation_matrix_iau1980() {
104+
let time = Time::from_two_part_julian_date(Tdb, 2400000.5, 53736.0);
105+
let exp = DMat3::from_cols_array(&[
106+
0.9999999999534999268,
107+
0.8847935789636432161e-5,
108+
0.3835906502164019142e-5,
109+
-0.8847780042583435924e-5,
110+
0.9999999991366569963,
111+
-0.4060052702727130809e-4,
112+
-0.3836265729708478796e-5,
113+
0.4060049308612638555e-4,
114+
0.9999999991684415129,
115+
])
116+
.transpose();
117+
let act = Nutation::nutation_matrix_iau1980(time);
118+
assert_approx_eq!(act, exp, rtol <= 1e-12);
126119
}
127120
}

crates/lox-earth/src/nutation/iau1980.rs

Lines changed: 16 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
use std::f64::consts::TAU;
77

88
use lox_core::types::units::{Arcseconds, JulianCenturies};
9+
use lox_time::{Time, julian_dates::JulianDate, time_scales::Tdb};
910
use lox_units::{Angle, AngleUnits};
1011

1112
use crate::nutation::Nutation;
@@ -28,12 +29,13 @@ struct Coefficients {
2829
}
2930

3031
impl Nutation {
31-
pub fn iau1980(centuries_since_j2000_tdb: JulianCenturies) -> Self {
32-
let l = l(centuries_since_j2000_tdb);
33-
let lp = lp(centuries_since_j2000_tdb);
34-
let f = f(centuries_since_j2000_tdb);
35-
let d = d(centuries_since_j2000_tdb);
36-
let om = omega(centuries_since_j2000_tdb);
32+
pub fn iau1980(time: Time<Tdb>) -> Self {
33+
let t = time.centuries_since_j2000();
34+
let l = l(t);
35+
let lp = lp(t);
36+
let f = f(t);
37+
let d = d(t);
38+
let om = omega(t);
3739

3840
let (dpsi, deps) = COEFFICIENTS
3941
.iter()
@@ -45,8 +47,8 @@ impl Nutation {
4547
let arg = coeff.l * l + coeff.lp * lp + coeff.f * f + coeff.d * d + coeff.om * om;
4648

4749
// Accumulate current term.
48-
let sin = coeff.sin_psi + coeff.sin_psi_t * centuries_since_j2000_tdb;
49-
let cos = coeff.cos_eps + coeff.cos_eps_t * centuries_since_j2000_tdb;
50+
let sin = coeff.sin_psi + coeff.sin_psi_t * t;
51+
let cos = coeff.cos_eps + coeff.cos_eps_t * t;
5052
dpsi += sin * arg.sin();
5153
deps += cos * arg.cos();
5254

@@ -227,46 +229,19 @@ const COEFFICIENTS: [Coefficients; 106] = [
227229
// @formatter:on
228230

229231
#[cfg(test)]
230-
/// All fixtures and assertion values were generated using the ERFA C library unless otherwise
231-
/// stated.
232232
mod tests {
233-
use lox_core::types::units::JulianCenturies;
234233
use lox_test_utils::assert_approx_eq;
235234

236235
use super::*;
237236

238-
const TOLERANCE: f64 = 1e-12;
239-
240-
#[test]
241-
fn test_nutation_iau1980_jd0() {
242-
let jd0: JulianCenturies = -67.11964407939767;
243-
let actual = Nutation::iau1980(jd0);
244-
let expected = Nutation {
245-
longitude: 0.00000693404778664026.rad(),
246-
obliquity: 0.00004131255061383108.rad(),
247-
};
248-
assert_approx_eq!(expected, actual, rtol <= TOLERANCE);
249-
}
250-
251-
#[test]
252-
fn test_nutation_iau1980_j2000() {
253-
let j2000: JulianCenturies = 0.0;
254-
let actual = Nutation::iau1980(j2000);
255-
let expected = Nutation {
256-
longitude: -0.00006750247617532478.rad(),
257-
obliquity: -0.00002799221238377013.rad(),
258-
};
259-
assert_approx_eq!(expected, actual, rtol <= TOLERANCE);
260-
}
261-
262237
#[test]
263-
fn test_nutation_iau1980_j2100() {
264-
let j2100: JulianCenturies = 1.0;
265-
let actual = Nutation::iau1980(j2100);
238+
fn test_nutation_iau1980() {
239+
let time = Time::from_two_part_julian_date(Tdb, 2400000.5, 53736.0);
240+
let actual = Nutation::iau1980(time);
266241
let expected = Nutation {
267-
longitude: 0.00001584138015187132.rad(),
268-
obliquity: 0.00004158958379918889.rad(),
242+
longitude: -9.643_658_353_226_563e-6.rad(),
243+
obliquity: 4.060_051_006_879_713e-5.rad(),
269244
};
270-
assert_approx_eq!(expected, actual, rtol <= TOLERANCE);
245+
assert_approx_eq!(expected, actual, rtol <= 1e-13);
271246
}
272247
}

crates/lox-earth/src/nutation/iau2000.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@
33
//
44
// SPDX-License-Identifier: MPL-2.0
55

6-
pub(crate) use iau2000a::nutation_iau2000a;
7-
pub(crate) use iau2000b::nutation_iau2000b;
86
use lox_core::types::units::JulianCenturies;
97
use lox_units::{Angle, AngleUnits};
108

crates/lox-earth/src/nutation/iau2000/iau2000a.rs

Lines changed: 32 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ use lox_bodies::fundamental::mhb2000::{
99
};
1010
use lox_bodies::*;
1111
use lox_core::types::units::JulianCenturies;
12+
use lox_time::Time;
13+
use lox_time::julian_dates::JulianDate;
14+
use lox_time::time_scales::Tdb;
1215
use lox_units::AngleUnits;
1316

1417
use crate::nutation::Nutation;
@@ -46,30 +49,28 @@ struct PlanetaryCoefficients {
4649
cos_eps: f64,
4750
}
4851

49-
pub(crate) fn nutation_iau2000a(centuries_since_j2000_tdb: JulianCenturies) -> Nutation {
50-
let luni_solar_args = DelaunayArguments {
51-
l: Moon.mean_anomaly_iers03(centuries_since_j2000_tdb),
52-
lp: Sun.mean_anomaly_mhb2000(centuries_since_j2000_tdb),
53-
f: Moon
54-
.mean_longitude_minus_ascending_node_mean_longitude_iers03(centuries_since_j2000_tdb),
55-
d: mean_moon_sun_elongation_mhb2000_luni_solar(centuries_since_j2000_tdb),
56-
om: Moon.ascending_node_mean_longitude_iers03(centuries_since_j2000_tdb),
57-
};
58-
59-
let planetary_args = DelaunayArguments {
60-
l: Moon.mean_anomaly_mhb2000(centuries_since_j2000_tdb),
61-
lp: 0.0.rad(), // unused
62-
f: Moon
63-
.mean_longitude_minus_ascending_node_mean_longitude_mhb2000(centuries_since_j2000_tdb),
64-
d: mean_moon_sun_elongation_mhb2000_planetary(centuries_since_j2000_tdb),
65-
om: Moon.ascending_node_mean_longitude_mhb2000(centuries_since_j2000_tdb),
66-
};
67-
68-
luni_solar_nutation(
69-
centuries_since_j2000_tdb,
70-
&luni_solar_args,
71-
&luni_solar::COEFFICIENTS,
72-
) + planetary_nutation(centuries_since_j2000_tdb, planetary_args)
52+
impl Nutation {
53+
pub fn iau2000a(time: Time<Tdb>) -> Nutation {
54+
let t = time.centuries_since_j2000();
55+
let luni_solar_args = DelaunayArguments {
56+
l: Moon.mean_anomaly_iers03(t),
57+
lp: Sun.mean_anomaly_mhb2000(t),
58+
f: Moon.mean_longitude_minus_ascending_node_mean_longitude_iers03(t),
59+
d: mean_moon_sun_elongation_mhb2000_luni_solar(t),
60+
om: Moon.ascending_node_mean_longitude_iers03(t),
61+
};
62+
63+
let planetary_args = DelaunayArguments {
64+
l: Moon.mean_anomaly_mhb2000(t),
65+
lp: 0.0.rad(), // unused
66+
f: Moon.mean_longitude_minus_ascending_node_mean_longitude_mhb2000(t),
67+
d: mean_moon_sun_elongation_mhb2000_planetary(t),
68+
om: Moon.ascending_node_mean_longitude_mhb2000(t),
69+
};
70+
71+
luni_solar_nutation(t, &luni_solar_args, &luni_solar::COEFFICIENTS)
72+
+ planetary_nutation(t, planetary_args)
73+
}
7374
}
7475

7576
fn planetary_nutation(
@@ -118,46 +119,20 @@ fn planetary_nutation(
118119
/// All fixtures and assertion values were generated using the ERFA C library unless otherwise
119120
/// stated.
120121
mod tests {
121-
use lox_core::types::units::JulianCenturies;
122122
use lox_test_utils::assert_approx_eq;
123+
use lox_time::{Time, time_scales::Tdb};
123124
use lox_units::AngleUnits;
124125

125126
use crate::nutation::Nutation;
126127

127-
use super::nutation_iau2000a;
128-
129-
const TOLERANCE: f64 = 1e-11;
130-
131-
#[test]
132-
fn test_nutation_iau2000a_jd0() {
133-
let jd0: JulianCenturies = -67.11964407939767;
134-
let expected = Nutation {
135-
longitude: 0.00000737147877835653.rad(),
136-
obliquity: 0.00004132135467915123.rad(),
137-
};
138-
let actual = nutation_iau2000a(jd0);
139-
assert_approx_eq!(expected, actual, rtol <= TOLERANCE);
140-
}
141-
142-
#[test]
143-
fn test_nutation_iau2000a_j2000() {
144-
let j2000: JulianCenturies = 0.0;
145-
let expected = Nutation {
146-
longitude: -0.00006754422426417299.rad(),
147-
obliquity: -0.00002797083119237414.rad(),
148-
};
149-
let actual = nutation_iau2000a(j2000);
150-
assert_approx_eq!(expected, actual, rtol <= TOLERANCE);
151-
}
152-
153128
#[test]
154-
fn test_nutation_iau2000a_j2100() {
155-
let j2100: JulianCenturies = 1.0;
129+
fn test_nutation_iau2000a() {
130+
let time = Time::from_two_part_julian_date(Tdb, 2400000.5, 53736.0);
156131
let expected = Nutation {
157-
longitude: 0.00001585987390484147.rad(),
158-
obliquity: 0.00004162326779426948.rad(),
132+
longitude: -9.630_909_107_115_518e-6.rad(),
133+
obliquity: 4.063_239_174_001_679e-5.rad(),
159134
};
160-
let actual = nutation_iau2000a(j2100);
161-
assert_approx_eq!(expected, actual, rtol <= TOLERANCE);
135+
let actual = Nutation::iau2000a(time);
136+
assert_approx_eq!(expected, actual, rtol <= 1e-13);
162137
}
163138
}

0 commit comments

Comments
 (0)