Skip to content

Commit db36cb5

Browse files
committed
WIP
1 parent 3126f25 commit db36cb5

File tree

20 files changed

+461
-70
lines changed

20 files changed

+461
-70
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/frames.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// SPDX-FileCopyrightText: 2025 Helge Eichhorn <[email protected]>
2+
//
3+
// SPDX-License-Identifier: MPL-2.0
4+
5+
use lox_frames::transformations::Rotation;
6+
use lox_time::{Time, time_scales::Tt};
7+
8+
use crate::precession::BiasPrecessionIau2000;
9+
10+
pub mod iers1996;
11+
pub mod iers2003;
12+
pub mod iers2010;
13+
14+
pub fn icrf_to_j2000(time: Time<Tt>) -> Rotation {
15+
let bp = BiasPrecessionIau2000::new(time);
16+
Rotation::new(bp.rb)
17+
}
18+
19+
pub fn j2000_to_icrf(time: Time<Tt>) -> Rotation {
20+
icrf_to_j2000(time).transpose()
21+
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
// SPDX-FileCopyrightText: 2025 Helge Eichhorn <[email protected]>
2+
//
3+
// SPDX-License-Identifier: MPL-2.0
4+
5+
use glam::DVec3;
6+
use lox_bodies::{Earth, RotationalElements};
7+
use lox_frames::transformations::Rotation;
8+
use lox_time::{
9+
Time,
10+
julian_dates::JulianDate,
11+
time_scales::{Tdb, Tt, Ut1},
12+
};
13+
14+
use crate::{
15+
ecliptic::MeanObliquity, nutation::Nutation, precession::PrecessionIau1976,
16+
rotation::GreenwichApparentSiderealTime,
17+
};
18+
19+
pub fn j2000_to_mod(time: Time<Tdb>) -> Rotation {
20+
Rotation::new(PrecessionIau1976::matrix(time))
21+
}
22+
23+
pub fn mod_to_j2000(time: Time<Tdb>) -> Rotation {
24+
j2000_to_mod(time).transpose()
25+
}
26+
27+
pub fn mod_to_tod(time: Time<Tdb>, corrections: Option<Nutation>) -> Rotation {
28+
let nut = Nutation::iau1980(time) + corrections.unwrap_or_default();
29+
let epsa = MeanObliquity::iau1980(time.with_scale(Tt));
30+
Rotation::new(nut.nutation_matrix(epsa))
31+
}
32+
33+
pub fn tod_to_mod(time: Time<Tdb>, corrections: Option<Nutation>) -> Rotation {
34+
mod_to_tod(time, corrections).transpose()
35+
}
36+
37+
pub fn tod_to_pef(tdb: Time<Tdb>, ut1: Time<Ut1>) -> Rotation {
38+
let gst = GreenwichApparentSiderealTime::iau1994(ut1);
39+
let t = tdb.seconds_since_j2000();
40+
Rotation::new(gst.0.rotation_z()).with_angular_velocity(DVec3::new(
41+
0.0,
42+
0.0,
43+
Earth.rotation_rate(t),
44+
))
45+
}
46+
47+
pub fn pef_to_tod(tdb: Time<Tdb>, ut1: Time<Ut1>) -> Rotation {
48+
tod_to_pef(tdb, ut1).transpose()
49+
}
50+
51+
pub fn pef_to_itrf() -> Rotation {
52+
todo!()
53+
}
54+
55+
pub fn itrf_to_pef() -> Rotation {
56+
pef_to_itrf().transpose()
57+
}
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
// SPDX-FileCopyrightText: 2025 Helge Eichhorn <[email protected]>
2+
//
3+
// SPDX-License-Identifier: MPL-2.0
4+
5+
use glam::DVec3;
6+
use lox_core::{f64::consts::ROTATION_RATE_EARTH, units::Angle};
7+
use lox_frames::transformations::Rotation;
8+
use lox_time::{
9+
Time,
10+
time_scales::{Tdb, Tt, Ut1},
11+
};
12+
13+
use crate::{
14+
cip::CipCoords, ecliptic::MeanObliquity, nutation::Nutation, precession::BiasPrecessionIau2000,
15+
rotation::GreenwichApparentSiderealTime,
16+
};
17+
18+
pub fn icrf_to_mod(time: Time<Tt>) -> Rotation {
19+
let bp = BiasPrecessionIau2000::new(time);
20+
Rotation::new(bp.rpb)
21+
}
22+
23+
pub fn mod_to_icrf(time: Time<Tt>) -> Rotation {
24+
icrf_to_mod(time).transpose()
25+
}
26+
27+
pub fn mod_to_tod_iau2000a(time: Time<Tt>, corrections: Option<CipCoords>) -> Rotation {
28+
let CipCoords { x: dx, y: dy } = corrections.unwrap_or_default();
29+
let nut = Nutation::iau2000a(time.with_scale(Tdb));
30+
let epsa = MeanObliquity::iau1980(time);
31+
let numat = nut.nutation_matrix(epsa);
32+
33+
let BiasPrecessionIau2000 { rpb, .. } = BiasPrecessionIau2000::new(time);
34+
let rnpb = numat * rpb;
35+
let v1 = DVec3::new(dx.as_f64(), dy.as_f64(), 0.0);
36+
let v2 = rnpb * v1;
37+
let corrections = Nutation {
38+
longitude: Angle::new(v2.x / epsa.0.sin()),
39+
obliquity: Angle::new(v2.y),
40+
};
41+
42+
Rotation::new((nut + corrections).nutation_matrix(epsa))
43+
}
44+
45+
pub fn tod_to_mod_iau2000a(time: Time<Tt>, corrections: Option<CipCoords>) -> Rotation {
46+
mod_to_tod_iau2000a(time, corrections).transpose()
47+
}
48+
49+
pub fn mod_to_tod_iau2000b(time: Time<Tt>, corrections: Option<CipCoords>) -> Rotation {
50+
let CipCoords { x: dx, y: dy } = corrections.unwrap_or_default();
51+
let nut = Nutation::iau2000b(time.with_scale(Tdb));
52+
let epsa = MeanObliquity::iau1980(time);
53+
let numat = nut.nutation_matrix(epsa);
54+
55+
let BiasPrecessionIau2000 { rpb, .. } = BiasPrecessionIau2000::new(time);
56+
let rnpb = numat * rpb;
57+
let v1 = DVec3::new(dx.as_f64(), dy.as_f64(), 0.0);
58+
let v2 = rnpb * v1;
59+
let corrections = Nutation {
60+
longitude: Angle::new(v2.x / epsa.0.sin()),
61+
obliquity: Angle::new(v2.y),
62+
};
63+
64+
Rotation::new((nut + corrections).nutation_matrix(epsa))
65+
}
66+
67+
pub fn tod_to_mod_iau2000b(time: Time<Tt>, corrections: Option<CipCoords>) -> Rotation {
68+
mod_to_tod_iau2000b(time, corrections).transpose()
69+
}
70+
71+
pub fn tod_to_pef_iau2000a(tt: Time<Tt>, ut1: Time<Ut1>) -> Rotation {
72+
let gst = GreenwichApparentSiderealTime::iau2000a(tt, ut1);
73+
Rotation::new(gst.0.rotation_z()).with_angular_velocity(DVec3::new(
74+
0.0,
75+
0.0,
76+
ROTATION_RATE_EARTH,
77+
))
78+
}
79+
80+
pub fn pef_to_tod_iau2000a(tt: Time<Tt>, ut1: Time<Ut1>) -> Rotation {
81+
tod_to_pef_iau2000a(tt, ut1).transpose()
82+
}
83+
84+
pub fn tod_to_pef_iau2000b(tt: Time<Tt>, ut1: Time<Ut1>) -> Rotation {
85+
let gst = GreenwichApparentSiderealTime::iau2000b(tt, ut1);
86+
Rotation::new(gst.0.rotation_z()).with_angular_velocity(DVec3::new(
87+
0.0,
88+
0.0,
89+
ROTATION_RATE_EARTH,
90+
))
91+
}
92+
93+
pub fn pef_to_tod_iau2000b(tt: Time<Tt>, ut1: Time<Ut1>) -> Rotation {
94+
tod_to_pef_iau2000b(tt, ut1).transpose()
95+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
// SPDX-FileCopyrightText: 2025 Helge Eichhorn <[email protected]>
2+
//
3+
// SPDX-License-Identifier: MPL-2.0

crates/lox-earth/src/iers.rs

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
// SPDX-FileCopyrightText: 2025 Helge Eichhorn <[email protected]>
2+
//
3+
// SPDX-License-Identifier: MPL-2.0
4+
5+
mod sealed {
6+
pub trait Sealed {}
7+
impl Sealed for super::Iers1996 {}
8+
impl Sealed for super::Iers2003 {}
9+
impl Sealed for super::Iers2010 {}
10+
impl Sealed for super::IersConvention {}
11+
}
12+
13+
pub trait IersConventionId: sealed::Sealed {
14+
fn id(&self) -> usize;
15+
}
16+
17+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
18+
pub struct Iers1996;
19+
20+
impl IersConventionId for Iers1996 {
21+
fn id(&self) -> usize {
22+
0
23+
}
24+
}
25+
26+
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
27+
pub enum Iau2000 {
28+
#[default]
29+
A = 1,
30+
B = 2,
31+
}
32+
33+
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
34+
pub struct Iers2003(pub Iau2000);
35+
36+
impl IersConventionId for Iers2003 {
37+
fn id(&self) -> usize {
38+
self.0 as usize
39+
}
40+
}
41+
42+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
43+
pub struct Iers2010;
44+
45+
impl IersConventionId for Iers2010 {
46+
fn id(&self) -> usize {
47+
3
48+
}
49+
}
50+
51+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
52+
pub enum IersConvention {
53+
Iers1996,
54+
Iers2003(Iau2000),
55+
Iers2010,
56+
}
57+
58+
impl IersConventionId for IersConvention {
59+
fn id(&self) -> usize {
60+
match self {
61+
IersConvention::Iers1996 => Iers1996.id(),
62+
IersConvention::Iers2003(iau2000) => Iers2003(*iau2000).id(),
63+
IersConvention::Iers2010 => Iers2010.id(),
64+
}
65+
}
66+
}
67+
68+
#[cfg(test)]
69+
mod tests {
70+
use rstest::rstest;
71+
72+
use super::*;
73+
74+
#[rstest]
75+
#[case(Iers1996, 0)]
76+
#[case(Iers2003(Iau2000::A), 1)]
77+
#[case(Iers2003(Iau2000::B), 2)]
78+
#[case(Iers2010, 3)]
79+
#[case(IersConvention::Iers1996, 0)]
80+
#[case(IersConvention::Iers2003(Iau2000::A), 1)]
81+
#[case(IersConvention::Iers2003(Iau2000::B), 2)]
82+
#[case(IersConvention::Iers2010, 3)]
83+
fn test_iers_convention_id<T: IersConventionId>(#[case] iers: T, #[case] exp: usize) {
84+
let act = iers.id();
85+
assert_eq!(act, exp);
86+
}
87+
}

crates/lox-earth/src/itrf.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use glam::{DMat3, DVec3};
1313
use lox_bodies::{Earth, RotationalElements};
1414
use lox_core::f64::consts::SECONDS_PER_DAY;
1515
use lox_frames::dynamic::DynTransformError;
16-
use lox_frames::transformations::TryTransform;
16+
use lox_frames::transformations::TryRotation;
1717
use lox_frames::transformations::rotations::Rotation;
1818
use lox_frames::{Cirf, DynFrame, Icrf, transform_provider};
1919
use lox_time::Time;
@@ -32,14 +32,14 @@ pub fn icrf_to_cirf(centuries: f64) -> Rotation {
3232
Rotation::new(m)
3333
}
3434

35-
impl<T> TryTransform<Icrf, Cirf, T> for EopProvider
35+
impl<T> TryRotation<Icrf, Cirf, T> for EopProvider
3636
where
3737
T: TimeScale + Copy,
3838
Self: TryOffset<T, Tdb, Error = EopProviderError>,
3939
{
4040
type Error = EopProviderError;
4141

42-
fn try_transform(
42+
fn try_rotation(
4343
&self,
4444
_origin: Icrf,
4545
_target: Cirf,
@@ -75,10 +75,10 @@ pub enum DynTransformEopError {
7575
Eop(#[from] EopProviderError),
7676
}
7777

78-
impl TryTransform<DynFrame, DynFrame, DynTimeScale> for EopProvider {
78+
impl TryRotation<DynFrame, DynFrame, DynTimeScale> for EopProvider {
7979
type Error = DynTransformEopError;
8080

81-
fn try_transform(
81+
fn try_rotation(
8282
&self,
8383
_origin: DynFrame,
8484
_target: DynFrame,

crates/lox-earth/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@ pub mod coordinate_transformations;
1010
pub mod ecliptic;
1111
pub mod eop;
1212
pub mod ephemeris;
13+
pub mod frames;
1314
pub mod fundamental;
15+
pub mod iers;
1416
pub mod itrf;
1517
pub mod nutation;
1618
pub mod precession;

crates/lox-earth/src/nutation.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ pub struct Nutation {
3232
}
3333

3434
impl Nutation {
35-
fn nutation_matrix(&self, mean_obliquity: MeanObliquity) -> DMat3 {
35+
pub fn nutation_matrix(&self, mean_obliquity: MeanObliquity) -> DMat3 {
3636
let rot1 = mean_obliquity.0.rotation_x();
3737
let rot2 = (-self.longitude).rotation_z();
3838
let rot3 = (-(self.obliquity + mean_obliquity.0)).rotation_x();

crates/lox-earth/src/precession.rs

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,11 @@ impl PrecessionIau1976 {
4545
* tas2r;
4646
Self { zeta, z, theta }
4747
}
48+
49+
pub fn matrix(time: Time<Tdb>) -> DMat3 {
50+
let prec = Self::new(Time::default(), time);
51+
(-prec.z).rotation_z() * prec.theta.rotation_y() * (-prec.zeta).rotation_z()
52+
}
4853
}
4954

5055
pub struct BiasPrecessionIau2000 {
@@ -53,7 +58,7 @@ pub struct BiasPrecessionIau2000 {
5358
/// Precession matrix.
5459
pub rp: DMat3,
5560
/// Bias-precession matrix.
56-
pub rbp: DMat3,
61+
pub rpb: DMat3,
5762
}
5863

5964
impl BiasPrecessionIau2000 {
@@ -81,7 +86,7 @@ impl BiasPrecessionIau2000 {
8186

8287
// Bias-precession matrix: GCRS to mean of date
8388
let rbp = rp * rb;
84-
Self { rb, rp, rbp }
89+
Self { rb, rp, rpb: rbp }
8590
}
8691
}
8792

@@ -225,7 +230,7 @@ mod tests {
225230
0.999_999_928_598_468_3,
226231
])
227232
.transpose();
228-
let rbp_exp = DMat3::from_cols_array(&[
233+
let rpb_exp = DMat3::from_cols_array(&[
229234
0.999_999_550_517_508_8,
230235
8.695_405_883_617_885e-4,
231236
3.779_734_722_239_007e-4,
@@ -239,7 +244,7 @@ mod tests {
239244
.transpose();
240245
assert_approx_eq!(bp.rb, rb_exp, atol <= 1e-12);
241246
assert_approx_eq!(bp.rp, rp_exp, atol <= 1e-12);
242-
assert_approx_eq!(bp.rbp, rbp_exp, atol <= 1e-12);
247+
assert_approx_eq!(bp.rpb, rpb_exp, atol <= 1e-12);
243248
}
244249

245250
#[test]

0 commit comments

Comments
 (0)