use crate::{ATTOS_PER_SEC, Dt, Real, Scale, sin};
pub const TL_NUM: i128 = 6_798_355_240;
pub const TL_DEN: i128 = 10_000_000_000_000_000_000; pub const LM_NUM: i128 = 648_378;
pub const LM_DEN: i128 = 1_000_000_000_000_000;
#[derive(Copy, Clone)]
pub struct LunarPeriodicTerm {
period_days: Real, amplitude_us: Real, phase_rad: Real, }
pub const LUNAR_PERIODIC_TERMS: [LunarPeriodicTerm; 13] = [
LunarPeriodicTerm {
period_days: 365.26590909,
amplitude_us: 1651.36355077,
phase_rad: 3.10895165,
},
LunarPeriodicTerm {
period_days: 29.53053800,
amplitude_us: 126.30813184,
phase_rad: 5.18472464,
},
LunarPeriodicTerm {
period_days: 398.99950348,
amplitude_us: 19.37467715,
phase_rad: 1.33855843,
},
LunarPeriodicTerm {
period_days: 182.63295455,
amplitude_us: 13.70088760,
phase_rad: 3.07602294,
},
LunarPeriodicTerm {
period_days: 411.67264344,
amplitude_us: 7.47520418,
phase_rad: 3.32446352,
},
LunarPeriodicTerm {
period_days: 4320.34946237,
amplitude_us: 4.24397312,
phase_rad: 3.43186281,
},
LunarPeriodicTerm {
period_days: 377.97977422,
amplitude_us: 3.76051430,
phase_rad: 0.92358639,
},
LunarPeriodicTerm {
period_days: 14.25402654,
amplitude_us: 2.93368121,
phase_rad: 1.09317212,
},
LunarPeriodicTerm {
period_days: 369.63431463,
amplitude_us: 2.67752983,
phase_rad: 1.51225314,
},
LunarPeriodicTerm {
period_days: 32.12797857,
amplitude_us: 2.36687890,
phase_rad: 5.21748801,
},
LunarPeriodicTerm {
period_days: 10859.25675676,
amplitude_us: 1.85820098,
phase_rad: 2.56843762,
},
LunarPeriodicTerm {
period_days: 584.00072674,
amplitude_us: 1.09742615,
phase_rad: 4.67635157,
},
LunarPeriodicTerm {
period_days: 292.00036337,
amplitude_us: 1.08850698,
phase_rad: 2.99248981,
},
];
impl Dt {
#[inline]
pub(crate) const fn mul_lm(attos: i128) -> i128 {
Self::mul_rate(attos, LM_NUM, LM_DEN)
}
pub(crate) const fn tt_to_ltc(tt: Self) -> Self {
let elapsed = Self::to_attos_since_tcg_tcb_epoch(tt);
let secular_attos = Self::mul_lm(elapsed);
let periodic = Self::ltc_periodic_correction(tt);
tt.add(Dt::from_attos(secular_attos, Scale::TAI))
.add(periodic)
}
pub(crate) const fn ltc_to_tt(ltc: Self) -> Self {
let mut tt = ltc; let mut i = 0u32;
while i < 6 {
let elapsed = Self::to_attos_since_tcg_tcb_epoch(tt);
let secular_attos = Self::mul_rate(elapsed, LM_NUM, LM_DEN + LM_NUM);
let periodic = Self::ltc_periodic_correction(tt);
tt = ltc
.sub(Dt::from_attos(secular_attos, Scale::TAI))
.sub(periodic);
i += 1;
}
tt
}
#[inline]
pub(crate) const fn mul_tl(attos: i128) -> i128 {
Self::mul_rate(attos, TL_NUM, TL_DEN)
}
const fn ltc_periodic_correction(tt: Self) -> Dt {
let seconds_since_j2000_tt = f!(tt.sec) + f!(tt.attos) / f!(ATTOS_PER_SEC);
let t_days = seconds_since_j2000_tt / f!(86400.0);
let mut delta_us = f!(0.0);
let two_pi = f!(2.0) * f!(core::f64::consts::PI);
let mut i = 0usize;
while i < LUNAR_PERIODIC_TERMS.len() {
let term = LUNAR_PERIODIC_TERMS[i];
let arg = two_pi * (t_days / term.period_days) + term.phase_rad;
delta_us += term.amplitude_us * sin(arg);
i += 1;
}
Dt::from_sec_f(delta_us * 1e-6)
}
pub(crate) const TCL_TDB_BIAS_SPAN: Dt = Dt::from_sec_f(0.49334260839797583);
#[inline]
pub(crate) const fn to_attos_since_j2000_tdb_epoch(numerical_tdb: Self) -> i128 {
numerical_tdb.to_attos()
}
pub(crate) const fn tai_to_tcl(tai: Self) -> Self {
let tdb = Self::tai_to_tdb(tai);
let elapsed = Self::to_attos_since_j2000_tdb_epoch(tdb);
let secular_attos = Self::mul_tl(elapsed);
let periodic = Self::ltc_periodic_correction(tdb);
tdb.add(Dt::from_attos(secular_attos, Scale::TAI))
.add(periodic)
.add(Self::TCL_TDB_BIAS_SPAN)
}
pub(crate) const fn tcl_to_tai(tcl: Self) -> Self {
let mut tdb = tcl;
let mut i = 0u32;
while i < 6 {
let elapsed = Self::to_attos_since_j2000_tdb_epoch(tdb);
let secular_attos = Self::mul_rate(elapsed, TL_NUM, TL_DEN + TL_NUM);
let periodic = Self::ltc_periodic_correction(tdb);
tdb = tcl
.sub(Dt::from_attos(secular_attos, Scale::TAI))
.sub(periodic)
.sub(Self::TCL_TDB_BIAS_SPAN);
i += 1;
}
Self::tdb_to_tai(tdb)
}
}