1use crate::{Dt, Real, Scale, sin};
4
5pub const TL_NUM: i128 = 6_798_355_240;
7pub const TL_DEN: i128 = 10_000_000_000_000_000_000; pub const LM_NUM: i128 = 648_378;
11pub const LM_DEN: i128 = 1_000_000_000_000_000; #[derive(Copy, Clone)]
18pub struct LunarPeriodicTerm {
19 period_days: Real, amplitude_us: Real, phase_rad: Real, }
23
24pub const LUNAR_PERIODIC_TERMS: [LunarPeriodicTerm; 13] = [
25 LunarPeriodicTerm {
26 period_days: 365.26590909,
27 amplitude_us: 1651.36355077,
28 phase_rad: 3.10895165,
29 },
30 LunarPeriodicTerm {
31 period_days: 29.53053800,
32 amplitude_us: 126.30813184,
33 phase_rad: 5.18472464,
34 },
35 LunarPeriodicTerm {
36 period_days: 398.99950348,
37 amplitude_us: 19.37467715,
38 phase_rad: 1.33855843,
39 },
40 LunarPeriodicTerm {
41 period_days: 182.63295455,
42 amplitude_us: 13.70088760,
43 phase_rad: 3.07602294,
44 },
45 LunarPeriodicTerm {
46 period_days: 411.67264344,
47 amplitude_us: 7.47520418,
48 phase_rad: 3.32446352,
49 },
50 LunarPeriodicTerm {
51 period_days: 4320.34946237,
52 amplitude_us: 4.24397312,
53 phase_rad: 3.43186281,
54 },
55 LunarPeriodicTerm {
56 period_days: 377.97977422,
57 amplitude_us: 3.76051430,
58 phase_rad: 0.92358639,
59 },
60 LunarPeriodicTerm {
61 period_days: 14.25402654,
62 amplitude_us: 2.93368121,
63 phase_rad: 1.09317212,
64 },
65 LunarPeriodicTerm {
66 period_days: 369.63431463,
67 amplitude_us: 2.67752983,
68 phase_rad: 1.51225314,
69 },
70 LunarPeriodicTerm {
71 period_days: 32.12797857,
72 amplitude_us: 2.36687890,
73 phase_rad: 5.21748801,
74 },
75 LunarPeriodicTerm {
76 period_days: 10859.25675676,
77 amplitude_us: 1.85820098,
78 phase_rad: 2.56843762,
79 },
80 LunarPeriodicTerm {
81 period_days: 584.00072674,
82 amplitude_us: 1.09742615,
83 phase_rad: 4.67635157,
84 },
85 LunarPeriodicTerm {
86 period_days: 292.00036337,
87 amplitude_us: 1.08850698,
88 phase_rad: 2.99248981,
89 },
90];
91
92impl Dt {
93 #[inline(always)]
94 pub(crate) const fn mul_lm(attos: i128) -> i128 {
95 Self::mul_rate(attos, LM_NUM, LM_DEN)
96 }
97
98 pub(crate) const fn tt_to_ltc(tt: Self) -> Dt {
99 let elapsed = Self::to_attos_since_tcg_tcb_epoch(tt);
100 let secular_attos = Self::mul_lm(elapsed);
101 let periodic = Self::ltc_periodic_correction(tt);
102
103 tt.add(Dt::span(secular_attos)).add(periodic)
104 }
105
106 pub(crate) const fn ltc_to_tt(ltc: Self) -> Dt {
114 let mut tt = ltc; let mut i = 0u32;
116 while i < 6 {
117 let periodic = Self::ltc_periodic_correction(tt);
118
119 let eff = ltc.sub(periodic);
121
122 let elapsed_eff = Self::to_attos_since_tcg_tcb_epoch(eff);
125 let sec_inv_attos = Self::mul_rate(elapsed_eff, LM_NUM, LM_DEN + LM_NUM);
126
127 tt = eff.sub(Dt::span(sec_inv_attos));
128 i += 1;
129 }
130 tt
131 }
132
133 #[inline(always)]
134 pub(crate) const fn mul_tl(attos: i128) -> i128 {
135 Self::mul_rate(attos, TL_NUM, TL_DEN)
136 }
137
138 const fn ltc_periodic_correction(tt: Self) -> Dt {
140 let seconds_since_j2000_tt = tt.to_sec_f();
141 let t_days = seconds_since_j2000_tt / f!(86400.0); let mut delta_us = f!(0.0);
144 let two_pi = f!(2.0) * f!(core::f64::consts::PI);
145
146 let mut i = 0usize;
147 while i < LUNAR_PERIODIC_TERMS.len() {
148 let term = LUNAR_PERIODIC_TERMS[i];
149 let arg = two_pi * (t_days / term.period_days) + term.phase_rad;
150 delta_us += term.amplitude_us * sin(arg);
151 i += 1;
152 }
153
154 Dt::from_sec_f(delta_us * 1e-6, Scale::TAI)
156 }
157
158 pub(crate) const TCL_TDB_BIAS_SPAN: Dt = Dt::from_sec_f(0.49334260839797583, Scale::TAI);
188
189 #[inline(always)]
193 pub(crate) const fn to_attos_since_j2000_tdb_epoch(numerical_tdb: Self) -> i128 {
194 numerical_tdb.to_attos()
195 }
196
197 pub(crate) const fn tai_to_tcl(tai: Self) -> Dt {
198 let tdb = Self::tai_to_tdb(tai);
199
200 let elapsed = Self::to_attos_since_j2000_tdb_epoch(tdb);
201 let secular_attos = Self::mul_tl(elapsed);
202 let periodic = Self::ltc_periodic_correction(tdb);
203
204 tdb.add(Dt::span(secular_attos))
205 .add(periodic)
206 .add(Self::TCL_TDB_BIAS_SPAN)
207 }
208
209 pub(crate) const fn tcl_to_tai(tcl: Self) -> Dt {
216 let mut tdb = tcl;
217 let mut i = 0u32;
218 while i < 6 {
219 let periodic = Self::ltc_periodic_correction(tdb);
220
221 let eff = tcl.sub(periodic).sub(Self::TCL_TDB_BIAS_SPAN);
223
224 let elapsed_eff = Self::to_attos_since_j2000_tdb_epoch(eff);
226 let sec_inv_attos = Self::mul_rate(elapsed_eff, TL_NUM, TL_DEN + TL_NUM);
227
228 tdb = eff.sub(Dt::span(sec_inv_attos));
229 i += 1;
230 }
231 Self::tdb_to_tai(tdb)
232 }
233}