Skip to content

Commit 2d92e61

Browse files
committed
feat(lox-earth): implement celestial to terrestrial rotations
1 parent 3126f25 commit 2d92e61

35 files changed

+994
-425
lines changed

crates/lox-core/src/f64/consts.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,3 +54,9 @@ pub const SECONDS_BETWEEN_MJD_AND_J2000: f64 = 4453444800.0;
5454

5555
/// The number of seconds between the J1950 Epoch and the J2000 Epoch.
5656
pub const SECONDS_BETWEEN_J1950_AND_J2000: f64 = 1577880000.0;
57+
58+
/*
59+
* Physical constants
60+
*/
61+
62+
pub const ROTATION_RATE_EARTH: f64 = 7.2921150e-5;

crates/lox-earth/src/cio.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,10 @@
55

66
//! Module cio exposes functions for calculating the Celestial Intermediate Origin (CIO) locator, s.
77
8-
pub mod s06;
8+
use lox_core::units::Angle;
9+
use lox_test_utils::ApproxEq;
10+
11+
pub mod iau2006;
12+
13+
#[derive(Debug, Clone, Copy, Default, PartialEq, ApproxEq)]
14+
pub struct CioLocator(pub Angle);

crates/lox-earth/src/cio/s06.rs renamed to crates/lox-earth/src/cio/iau2006.rs

Lines changed: 28 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,14 @@
66
//! Module s06 exposes a function for calculating the Celestial Intermediate Origin (CIO) locator,
77
//! s, using IAU 2006 precession and IAU 2000A nutation.
88
9+
use fast_polynomial::poly_array;
910
use lox_core::types::units::JulianCenturies;
1011
use lox_core::units::{Angle, AngleUnits};
12+
use lox_time::Time;
13+
use lox_time::julian_dates::JulianDate;
14+
use lox_time::time_scales::Tdb;
1115

16+
use crate::cio::CioLocator;
1217
use crate::cip::CipCoords;
1318
use crate::fundamental::iers03::{
1419
d_iers03, earth_l_iers03, f_iers03, l_iers03, lp_iers03, omega_iers03, pa_iers03,
@@ -17,6 +22,16 @@ use crate::fundamental::iers03::{
1722

1823
mod terms;
1924

25+
impl CioLocator {
26+
pub fn iau2006(time: Time<Tdb>, CipCoords { x, y }: CipCoords) -> Self {
27+
let t = time.centuries_since_j2000();
28+
let fundamental_args = fundamental_args(t);
29+
let evaluated_terms = evaluate_terms(&fundamental_args);
30+
let s = poly_array(t, &evaluated_terms).arcsec();
31+
CioLocator(s - y.to_radians() / 2.0 * x)
32+
}
33+
}
34+
2035
/// l, l', F, D, Ω, LVe, LE and pA.
2136
type FundamentalArgs = [Angle; 8];
2237

@@ -28,7 +43,7 @@ pub fn cio_locator(
2843
) -> Angle {
2944
let fundamental_args = fundamental_args(centuries_since_j2000_tdb);
3045
let evaluated_terms = evaluate_terms(&fundamental_args);
31-
let s = fast_polynomial::poly_array(centuries_since_j2000_tdb, &evaluated_terms).arcsec();
46+
let s = poly_array(centuries_since_j2000_tdb, &evaluated_terms).arcsec();
3247
Angle::radians(s.to_radians() - x.to_radians() * y.to_radians() / 2.0)
3348
}
3449

@@ -76,52 +91,27 @@ fn evaluate_single_order_terms(
7691

7792
#[cfg(test)]
7893
mod tests {
79-
use std::iter::zip;
80-
8194
use lox_test_utils::assert_approx_eq;
8295

8396
use super::*;
8497

85-
const TOLERANCE: f64 = 1e-11;
86-
87-
#[test]
88-
fn test_s_jd0() {
89-
let jd0: JulianCenturies = -67.11964407939767;
90-
let xy = CipCoords::new(jd0);
91-
assert_approx_eq!(
92-
cio_locator(jd0, xy),
93-
-0.0723985415686306.rad(),
94-
rtol <= TOLERANCE
95-
);
96-
}
97-
98-
#[test]
99-
fn test_s_j2000() {
100-
let j2000: JulianCenturies = 0.0;
101-
let xy = CipCoords::new(j2000);
102-
assert_approx_eq!(
103-
cio_locator(j2000, xy),
104-
-0.00000001013396519178.rad(),
105-
rtol <= TOLERANCE
106-
);
107-
}
108-
10998
#[test]
110-
fn test_s_j2100() {
111-
let j2100: JulianCenturies = 1.0;
112-
let xy = CipCoords::new(j2100);
113-
assert_approx_eq!(
114-
cio_locator(j2100, xy),
115-
-0.00000000480511934533.rad(),
116-
rtol <= TOLERANCE
117-
);
99+
fn test_cio_locator_iau2006() {
100+
let cip_coords = CipCoords {
101+
x: 5.791_308_486_706_011e-4.rad(),
102+
y: 4.020_579_816_732_961e-5.rad(),
103+
};
104+
let time = Time::from_two_part_julian_date(Tdb, 2400000.5, 53736.0);
105+
let exp = CioLocator(-1.220_032_213_076_463e-8.rad());
106+
let act = CioLocator::iau2006(time, cip_coords);
107+
assert_approx_eq!(act, exp, atol <= 1e-18);
118108
}
119109

120110
#[test]
121111
fn test_fundamental_args_ordering() {
122112
let j2000: JulianCenturies = 0.0;
123-
let actual = fundamental_args(j2000);
124-
let expected = [
113+
let act = fundamental_args(j2000);
114+
let exp = [
125115
l_iers03(j2000),
126116
lp_iers03(j2000),
127117
f_iers03(j2000),
@@ -132,8 +122,6 @@ mod tests {
132122
pa_iers03(j2000),
133123
];
134124

135-
for (act, exp) in zip(actual, expected) {
136-
assert_approx_eq!(act, exp, rtol <= TOLERANCE)
137-
}
125+
assert_approx_eq!(act, exp, rtol <= 1e-12)
138126
}
139127
}
File renamed without changes.

crates/lox-earth/src/cip.rs

Lines changed: 68 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,15 @@
66
//! Module cip exposes functions for calculating the position of the
77
//! Celestial Intermediate Pole (CIP).
88
9+
use std::ops::{Add, AddAssign};
10+
911
use glam::DMat3;
10-
use lox_core::{
11-
types::units::JulianCenturies,
12-
units::{Angle, AngleUnits},
13-
};
12+
use lox_core::units::{Angle, AngleUnits};
13+
use lox_time::{Time, julian_dates::JulianDate, time_scales::Tdb};
14+
15+
use crate::cio::CioLocator;
1416

15-
mod xy06;
17+
mod iau2006;
1618

1719
#[derive(Copy, Clone, Debug, Default, PartialEq)]
1820
pub struct CipCoords {
@@ -23,8 +25,9 @@ pub struct CipCoords {
2325
impl CipCoords {
2426
/// Calculates the (X, Y) coordinates of the Celestial Intermediate Pole (CIP) using the the IAU
2527
/// 2006 precession and IAU 2000A nutation models.
26-
pub fn new(centuries_since_j2000_tdb: JulianCenturies) -> Self {
27-
let (x, y) = xy06::cip_coords(centuries_since_j2000_tdb);
28+
pub fn iau2006(time: Time<Tdb>) -> Self {
29+
let t = time.centuries_since_j2000();
30+
let (x, y) = iau2006::cip_coords(t);
2831
Self { x, y }
2932
}
3033

@@ -34,10 +37,45 @@ impl CipCoords {
3437
let y = bpn.y_axis.z.rad();
3538
Self { x, y }
3639
}
40+
41+
pub fn celestial_to_intermediate_matrix(&self, s: CioLocator) -> DMat3 {
42+
let x = self.x.to_radians();
43+
let y = self.y.to_radians();
44+
let r2 = x.powi(2) + y.powi(2);
45+
46+
let e = if r2 > 0.0 {
47+
Angle::from_atan2(y, x)
48+
} else {
49+
Angle::default()
50+
};
51+
let d = Angle::from_atan((r2 / (1.0 - r2)).sqrt());
52+
53+
(-(e + s.0)).rotation_z() * d.rotation_y() * e.rotation_z()
54+
}
55+
}
56+
57+
impl Add for CipCoords {
58+
type Output = Self;
59+
60+
fn add(self, rhs: Self) -> Self::Output {
61+
CipCoords {
62+
x: self.x + rhs.x,
63+
y: self.y + rhs.y,
64+
}
65+
}
66+
}
67+
68+
impl AddAssign for CipCoords {
69+
fn add_assign(&mut self, rhs: Self) {
70+
self.x += rhs.x;
71+
self.y += rhs.y;
72+
}
3773
}
3874

3975
#[cfg(test)]
4076
mod tests {
77+
use lox_test_utils::assert_approx_eq;
78+
4179
use super::*;
4280

4381
#[test]
@@ -58,4 +96,27 @@ mod tests {
5896
assert_eq!(cip.x, 1.093465510215479e-3.rad());
5997
assert_eq!(cip.y, -4.281337229063151e-5.rad());
6098
}
99+
100+
#[test]
101+
fn test_cip_celestial_to_intermediate_matrix() {
102+
let xy = CipCoords {
103+
x: 5.791_308_486_706_011e-4.rad(),
104+
y: 4.020_579_816_732_961e-5.rad(),
105+
};
106+
let s = CioLocator(-1.220_040_848_472_272e-8.rad());
107+
let exp = DMat3::from_cols_array(&[
108+
0.999_999_832_303_715_7,
109+
5.581_984_869_168_499e-10,
110+
-5.791_308_491_611_282e-4,
111+
-2.384_261_642_670_440_2e-8,
112+
0.999_999_999_191_746_9,
113+
-4.020_579_110_169_669e-5,
114+
5.791_308_486_706_011e-4,
115+
4.020_579_816_732_961e-5,
116+
0.999_999_831_495_462_8,
117+
])
118+
.transpose();
119+
let act = xy.celestial_to_intermediate_matrix(s);
120+
assert_approx_eq!(act, exp, atol <= 1e-12);
121+
}
61122
}

crates/lox-earth/src/cip/xy06.rs renamed to crates/lox-earth/src/cip/iau2006.rs

Lines changed: 6 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,7 @@ fn calculate_cip_unit_vector(
174174
#[cfg(test)]
175175
mod tests {
176176
use lox_test_utils::assert_approx_eq;
177+
use lox_time::{Time, time_scales::Tdb};
177178

178179
use crate::cip::CipCoords;
179180

@@ -182,27 +183,11 @@ mod tests {
182183
const TOLERANCE: f64 = 1e-12;
183184

184185
#[test]
185-
fn test_cip_xy_jd0() {
186-
let jd0: JulianCenturies = -67.11964407939767;
187-
let CipCoords { x, y } = CipCoords::new(jd0);
188-
assert_approx_eq!(x, -0.4088355637476968.rad(), rtol <= TOLERANCE);
189-
assert_approx_eq!(y, -0.38359667445777073.rad(), rtol <= TOLERANCE);
190-
}
191-
192-
#[test]
193-
fn test_cip_xy_j2000() {
194-
let j2000: JulianCenturies = 0.0;
195-
let CipCoords { x, y } = CipCoords::new(j2000);
196-
assert_approx_eq!(x, -0.0000269463795685740.rad(), rtol <= TOLERANCE);
197-
assert_approx_eq!(y, -0.00002800472282281282.rad(), rtol <= TOLERANCE);
198-
}
199-
200-
#[test]
201-
fn test_cip_xy_j2100() {
202-
let j2100: JulianCenturies = 1.0;
203-
let CipCoords { x, y } = CipCoords::new(j2100);
204-
assert_approx_eq!(x, 0.00972070446172924.rad(), rtol <= TOLERANCE);
205-
assert_approx_eq!(y, -0.0000673058699616719.rad(), rtol <= TOLERANCE);
186+
fn test_cip_coordinates_iau2006() {
187+
let time = Time::from_two_part_julian_date(Tdb, 2400000.5, 53736.0);
188+
let CipCoords { x, y } = CipCoords::iau2006(time);
189+
assert_approx_eq!(x, 5.791_308_486_706_011e-4.rad(), rtol <= TOLERANCE);
190+
assert_approx_eq!(y, 4.020_579_816_732_958e-5.rad(), rtol <= TOLERANCE);
206191
}
207192

208193
#[test]

0 commit comments

Comments
 (0)