1use crate::{ATTOS_PER_SEC, 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]
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) -> Self {
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::from_attos(secular_attos, Scale::TAI))
104 .add(periodic)
105 }
106
107 pub(crate) const fn ltc_to_tt(ltc: Self) -> Self {
117 let mut tt = ltc; let mut i = 0u32;
119 while i < 6 {
120 let elapsed = Self::to_attos_since_tcg_tcb_epoch(tt);
121 let secular_attos = Self::mul_rate(elapsed, LM_NUM, LM_DEN + LM_NUM);
122 let periodic = Self::ltc_periodic_correction(tt);
123
124 tt = ltc
125 .sub(Dt::from_attos(secular_attos, Scale::TAI))
126 .sub(periodic);
127 i += 1;
128 }
129 tt
130 }
131
132 #[inline]
133 pub(crate) const fn mul_tl(attos: i128) -> i128 {
134 Self::mul_rate(attos, TL_NUM, TL_DEN)
135 }
136
137 const fn ltc_periodic_correction(tt: Self) -> Dt {
139 let seconds_since_j2000_tt = f!(tt.sec) + f!(tt.attos) / f!(ATTOS_PER_SEC);
140 let t_days = seconds_since_j2000_tt / f!(86400.0); let mut delta_us = f!(0.0);
143 let two_pi = f!(2.0) * f!(core::f64::consts::PI);
144
145 let mut i = 0usize;
146 while i < LUNAR_PERIODIC_TERMS.len() {
147 let term = LUNAR_PERIODIC_TERMS[i];
148 let arg = two_pi * (t_days / term.period_days) + term.phase_rad;
149 delta_us += term.amplitude_us * sin(arg);
150 i += 1;
151 }
152
153 Dt::from_sec_f(delta_us * 1e-6)
155 }
156
157 pub(crate) const TCL_TDB_BIAS_SPAN: Dt = Dt::from_sec_f(0.49334260839797583);
187
188 #[inline]
192 pub(crate) const fn to_attos_since_j2000_tdb_epoch(numerical_tdb: Self) -> i128 {
193 numerical_tdb.to_attos()
194 }
195
196 pub(crate) const fn tai_to_tcl(tai: Self) -> Self {
197 let tdb = Self::tai_to_tdb(tai);
198
199 let elapsed = Self::to_attos_since_j2000_tdb_epoch(tdb);
200 let secular_attos = Self::mul_tl(elapsed);
201 let periodic = Self::ltc_periodic_correction(tdb);
202
203 tdb.add(Dt::from_attos(secular_attos, Scale::TAI))
204 .add(periodic)
205 .add(Self::TCL_TDB_BIAS_SPAN)
206 }
207
208 pub(crate) const fn tcl_to_tai(tcl: Self) -> Self {
211 let mut tdb = tcl;
212 let mut i = 0u32;
213 while i < 6 {
214 let elapsed = Self::to_attos_since_j2000_tdb_epoch(tdb);
215 let secular_attos = Self::mul_rate(elapsed, TL_NUM, TL_DEN + TL_NUM);
216 let periodic = Self::ltc_periodic_correction(tdb);
217
218 tdb = tcl
219 .sub(Dt::from_attos(secular_attos, Scale::TAI))
220 .sub(periodic)
221 .sub(Self::TCL_TDB_BIAS_SPAN);
222 i += 1;
223 }
224 Self::tdb_to_tai(tdb)
225 }
226}