deep_time/dt/
conversions.rs1use crate::historical_sofa::historical_sofa_offset_for_non_adjusted;
2use crate::leap_seconds::get_leap_seconds;
3use crate::{
4 ATTOS_PER_SEC, ATTOS_PER_SEC_I128, ClockDrift, ClockModel, Dt, J2000_JD_TT,
5 J2000_SEC_PER_CENTURY, LB_DEN, LB_NUM, LG_DEN, LG_NUM, Real, SEC_PER_DAYI64, SEC_PER_DAYI128,
6 Scale, TAI_SEC_AT_1972, TCG_TCB_REF_JD_INT, TCG_TCB_REF_TOD_SEC, TCG_TCB_REF_TOD_SUBSEC,
7 TDB0_ATTOS, TSpan, TT_TAI_OFFSET_SPAN, sin_approx,
8};
9
10impl Dt {
11 #[inline(always)]
12 pub const fn to_span(&self) -> TSpan {
13 TSpan {
14 sec: self.sec,
15 attos: self.attos,
16 }
17 }
18
19 pub const fn from(sec: i64, attos: u64, scale: Scale) -> Dt {
20 let raw = Dt::new(sec, attos);
22
23 match scale {
24 Scale::TAI | Scale::Custom | Scale::UT1 => raw,
25
26 Scale::TT => raw.sub(TT_TAI_OFFSET_SPAN),
27
28 Scale::UTC => raw.add(TSpan::from_sec(get_leap_seconds(&raw, true).offset)),
29
30 Scale::UTCSpice => {
31 let tai = raw.add(TSpan::from_sec(get_leap_seconds(&raw, true).offset));
32 if sec < TAI_SEC_AT_1972 - 10 {
33 tai.add(TSpan::from_sec_f(f!(9.0)))
34 } else {
35 tai
36 }
37 }
38 Scale::UTCSofa => {
39 let tai = raw.add(TSpan::from_sec(get_leap_seconds(&raw, true).offset));
40 if let Some(offset) = historical_sofa_offset_for_non_adjusted(&raw) {
41 tai.add(TSpan::from_sec_f(offset))
42 } else {
43 tai
44 }
45 }
46
47 Scale::GPS | Scale::QZSS | Scale::GST => raw.add(TSpan::SEC_19),
48
49 Scale::BDT => raw.add(TSpan::SEC_33),
50
51 Scale::TDB | Scale::ET => Self::tdb_to_tai(raw),
52
53 Scale::TCG => {
54 let tt = Self::tcg_to_tt(raw);
55 tt.sub(TT_TAI_OFFSET_SPAN)
56 }
57
58 Scale::TCB => {
59 let tdb = Self::tcb_to_tdb(raw);
60 Self::tdb_to_tai(tdb)
61 }
62
63 Scale::LTC => {
64 let tt = Self::ltc_to_tt(raw);
65 tt.sub(TT_TAI_OFFSET_SPAN)
66 }
67 }
68 }
69
70 pub const fn to(&self, scale: Scale) -> TSpan {
76 match scale {
77 Scale::TAI | Scale::Custom | Scale::UT1 => self.to_span(),
78
79 Scale::TT => self.add(TT_TAI_OFFSET_SPAN).to_span(),
80
81 Scale::UTC => self
82 .sub(TSpan::from_sec(get_leap_seconds(&self, false).offset))
83 .to_span(),
84
85 Scale::UTCSpice => {
86 if self.sec < TAI_SEC_AT_1972 {
87 self.sub(TSpan::from_sec(get_leap_seconds(&self, false).offset))
88 .sub(TSpan::from_sec_f(f!(9.0)))
89 .to_span()
90 } else {
91 self.sub(TSpan::from_sec(get_leap_seconds(&self, false).offset))
92 .to_span()
93 }
94 }
95 Scale::UTCSofa => {
96 if let Some(offset) = historical_sofa_offset_for_non_adjusted(&self) {
97 self.sub(TSpan::from_sec(get_leap_seconds(&self, false).offset))
98 .sub(TSpan::from_sec_f(offset))
99 .to_span()
100 } else {
101 self.sub(TSpan::from_sec(get_leap_seconds(&self, false).offset))
102 .to_span()
103 }
104 }
105
106 Scale::GPS | Scale::QZSS | Scale::GST => self.sub(TSpan::SEC_19).to_span(),
107
108 Scale::BDT => self.sub(TSpan::SEC_33).to_span(),
109
110 Scale::TDB | Scale::ET => Self::tai_to_tdb(*self).to_span(),
111
112 Scale::TCG => Self::tai_to_tcg(*self).to_span(),
113
114 Scale::TCB => Self::tai_to_tcb(*self).to_span(),
115
116 Scale::LTC => {
117 let tt = self.add(TT_TAI_OFFSET_SPAN);
118 Self::tt_to_ltc(tt).to_span()
119 }
120 }
121 }
122
123 #[inline]
126 pub const fn convert_using_drift(self, reference: Self, drift: ClockDrift) -> Self {
127 let span = self.to_tai_since(reference);
128 let correction = drift.time_diff_after(&span);
129 self.add(correction)
130 }
131
132 pub const fn convert_back_using_drift(self, reference: Self, drift: ClockDrift) -> Self {
138 if drift.rate().is_zero() && drift.accel().is_zero() {
139 return self.sub(*drift.constant());
140 }
141 let mut guess = self;
142 let mut i = 0u32;
143 while i < 16 {
144 let span = guess.to_tai_since(reference);
145 let correction = drift.time_diff_after(&span);
146 guess = self.sub(correction);
147 i += 1;
148 }
149 guess
150 }
151
152 #[inline]
157 pub const fn convert_using_model(self, model: ClockModel) -> Self {
158 self.convert_using_drift(model.reference, model.drift)
159 }
160
161 #[inline]
163 pub const fn convert_back_using_model(self, model: ClockModel) -> Self {
164 self.convert_back_using_drift(model.reference, model.drift)
165 }
166
167 const fn tai_to_tdb(tai: Self) -> Self {
168 let tt = tai.add(TT_TAI_OFFSET_SPAN);
169 let span = Self::tdb_minus_tt(tt.sec, tt.attos);
170 tt.add(span)
171 }
172
173 const fn tdb_to_tai(tdb: Self) -> Self {
174 let mut tt = tdb;
175 let mut i = 0u32;
176 while i < 8 {
177 tt = tdb.sub(Self::tdb_minus_tt(tt.sec, tt.attos));
178 i += 1;
179 }
180 tt.sub(TT_TAI_OFFSET_SPAN)
181 }
182
183 const fn tai_to_tcg(tai: Self) -> Self {
184 let tt = tai.add(TT_TAI_OFFSET_SPAN);
185 Self::tt_to_tcg(tt)
186 }
187
188 const fn tai_to_tcb(tai: Self) -> Self {
189 let tdb = Self::tai_to_tdb(tai);
190 Self::tdb_to_tcb(tdb)
191 }
192
193 pub(crate) const fn elapsed_to_attos_since_ref(numerical: Self) -> i128 {
196 let days_since_j2000 = numerical.sec.div_euclid(SEC_PER_DAYI64);
197 let tod_sec = numerical.sec.rem_euclid(SEC_PER_DAYI64);
198
199 let jd_days = J2000_JD_TT + days_since_j2000;
200 let days_diff = jd_days - TCG_TCB_REF_JD_INT;
201
202 let mut sec_diff =
203 (days_diff as i128) * SEC_PER_DAYI128 + (tod_sec as i128 - TCG_TCB_REF_TOD_SEC as i128);
204 let mut attos_diff = (numerical.attos as i128) - (TCG_TCB_REF_TOD_SUBSEC as i128);
205
206 if attos_diff < 0 {
207 attos_diff += ATTOS_PER_SEC_I128;
208 sec_diff -= 1;
209 }
210
211 sec_diff * ATTOS_PER_SEC_I128 + attos_diff
212 }
213
214 pub(crate) const fn mul_rate(attos: i128, num: i128, den: i128) -> i128 {
216 if attos == 0 {
217 return 0;
218 }
219 let sign = if attos < 0 { -1i128 } else { 1i128 };
220 let a = if attos < 0 { -attos } else { attos };
221 let q = a / den;
222 let r = a % den;
223 sign * (q * num + (r * num) / den)
224 }
225
226 #[inline]
227 pub(crate) const fn mul_lg(attos: i128) -> i128 {
228 Self::mul_rate(attos, LG_NUM, LG_DEN)
229 }
230
231 #[inline]
232 pub(crate) const fn mul_lb(attos: i128) -> i128 {
233 Self::mul_rate(attos, LB_NUM, LB_DEN)
234 }
235
236 pub(crate) const fn tt_to_tcg(tt: Self) -> Self {
237 let elapsed = Self::elapsed_to_attos_since_ref(tt);
238 let span_attos = Self::mul_lg(elapsed);
239 tt.add(TSpan::from_attos(span_attos))
240 }
241
242 pub(crate) const fn tcg_to_tt(tcg: Self) -> Self {
243 let elapsed_cg = Self::elapsed_to_attos_since_ref(tcg);
244 let span_attos = Self::mul_rate(elapsed_cg, LG_NUM, LG_DEN + LG_NUM);
245 tcg.sub(TSpan::from_attos(span_attos))
246 }
247
248 pub(crate) const fn tcb_to_tdb(tcb: Self) -> Self {
249 let elapsed_cg = Self::elapsed_to_attos_since_ref(tcb);
250 let span_attos = Self::mul_rate(elapsed_cg, LB_NUM, LB_DEN + LB_NUM);
251 tcb.sub(TSpan::from_attos(span_attos))
252 .sub(TSpan::from_attos(TDB0_ATTOS))
253 }
254
255 pub(crate) const fn tdb_to_tcb(tdb: Self) -> Self {
256 let elapsed = Self::elapsed_to_attos_since_ref(tdb);
257 let span_attos = Self::mul_lb(elapsed);
258 tdb.add(TSpan::from_attos(span_attos))
259 .add(TSpan::from_attos(TDB0_ATTOS))
260 }
261
262 const fn tdb_minus_tt(sec: i64, attos: u64) -> TSpan {
309 let seconds_since_j2000_tt = f!(sec) + f!(attos) / f!(ATTOS_PER_SEC);
310 let t = seconds_since_j2000_tt / J2000_SEC_PER_CENTURY;
311
312 let g = f!(2.0) * f!(core::f64::consts::PI) * (f!(357.52910918) + f!(35999.050290) * t)
314 / f!(360.0);
315
316 let sin_g = sin_approx(g + f!(0.01671) * sin_approx(g));
318
319 let sin_2g = sin_approx(f!(2.0) * g);
321
322 let lunar = sin_approx(f!(52.9690965) * t + f!(0.444401603));
324
325 let venus = sin_approx(f!(606.977675) * t + f!(4.021195093));
327
328 let correction = f!(0.001656674564) * sin_g
329 + f!(0.000022417471) * sin_2g
330 + f!(0.000004770086) * lunar
331 + f!(0.000004676740) * venus;
332
333 TSpan::from_sec_f(correction)
334 }
335}