1use crate::historical_sofa::historical_sofa_offset_for_non_adjusted;
2use crate::{
3 ClockModel, Drift, Dt, LB_DEN, LB_NUM, LG_DEN, LG_NUM, Scale, TAI_SEC_AT_1972,
4 TCG_TCB_REF_ATTOS_SINCE_J2000, TDB0_ATTOS, TT_TAI_OFFSET, tdb_minus_tt,
5};
6
7impl Dt {
8 #[inline]
9 pub const fn from_dt(dt: Dt, scale: Scale) -> Dt {
10 Self::from(dt.sec, dt.attos, scale)
11 }
12
13 #[inline]
14 pub const fn from_attos_since(attos: i128, reference: Dt) -> Self {
15 reference.add(Dt::from_attos(attos, Scale::TAI))
16 }
17
18 #[inline]
19 pub const fn to_scale_and_then_diff(&self, scale: Scale, epoch: Dt) -> Dt {
20 self.to_internal(scale).to_diff_raw(epoch)
21 }
22
23 #[inline]
24 pub const fn from_diff_and_scale(diff: Dt, epoch: Dt, current: Scale) -> Self {
25 Dt::from_dt(epoch.add(diff), current)
26 }
27
28 pub const fn from(sec: i64, attos: u64, scale: Scale) -> Dt {
30 let raw = Dt::new(sec, attos);
31 match scale {
32 Scale::TAI | Scale::Custom | Scale::UT1 => raw,
33 Scale::TT => raw.sub(TT_TAI_OFFSET),
34 Scale::UTC => raw.add(Dt {
35 sec: raw.leap_seconds(true).offset,
36 attos: 0,
37 }),
38 Scale::UTCSpice => {
39 let tai = raw.add(Dt {
40 sec: raw.leap_seconds(true).offset,
41 attos: 0,
42 });
43 if sec < TAI_SEC_AT_1972 - 10 {
44 tai.add(Dt::from_sec(9, Scale::TAI))
45 } else {
46 tai
47 }
48 }
49 Scale::UTCSofa => {
50 let tai = raw.add(Dt {
51 sec: raw.leap_seconds(true).offset,
52 attos: 0,
53 });
54 if let Some(offset) = historical_sofa_offset_for_non_adjusted(&raw) {
55 tai.add(Dt::from_sec_f(offset))
56 } else {
57 tai
58 }
59 }
60 Scale::GPS | Scale::QZSS | Scale::GST => raw.add(Dt::SEC_19),
61 Scale::BDT => raw.add(Dt::SEC_33),
62 Scale::TDB | Scale::ET => Self::tdb_to_tai(raw),
63 Scale::TCG => {
64 let tt = Self::tcg_to_tt(raw);
65 tt.sub(TT_TAI_OFFSET)
66 }
67 Scale::TCB => {
68 let tdb = Self::tcb_to_tdb(raw);
69 Self::tdb_to_tai(tdb)
70 }
71 Scale::LTC => {
72 let tt = Self::ltc_to_tt(raw);
73 tt.sub(TT_TAI_OFFSET)
74 }
75 Scale::TCL => Self::tcl_to_tai(raw),
76 }
77 }
78
79 pub(crate) const fn to_internal(&self, scale: Scale) -> Dt {
80 match scale {
81 Scale::TAI | Scale::Custom | Scale::UT1 => *self,
82 Scale::TT => self.add(TT_TAI_OFFSET),
83 Scale::UTC => self.sub(Dt {
84 sec: self.leap_seconds(false).offset,
85 attos: 0,
86 }),
87 Scale::UTCSpice => {
88 let spice = self.sub(Dt {
89 sec: self.leap_seconds(false).offset,
90 attos: 0,
91 });
92 if self.sec < TAI_SEC_AT_1972 {
93 spice.sub(Dt::from_sec_f(f!(9.0)))
94 } else {
95 spice
96 }
97 }
98 Scale::UTCSofa => {
99 let sofa = self.sub(Dt {
100 sec: self.leap_seconds(false).offset,
101 attos: 0,
102 });
103 if let Some(offset) = historical_sofa_offset_for_non_adjusted(&self) {
104 sofa.sub(Dt::from_sec_f(offset))
105 } else {
106 sofa
107 }
108 }
109 Scale::GPS | Scale::QZSS | Scale::GST => self.sub(Dt::SEC_19),
110 Scale::BDT => self.sub(Dt::SEC_33),
111 Scale::TDB | Scale::ET => Self::tai_to_tdb(*self),
112 Scale::TCG => Self::tai_to_tcg(*self),
113 Scale::TCB => Self::tai_to_tcb(*self),
114 Scale::LTC => {
115 let tt = self.add(TT_TAI_OFFSET);
116 Self::tt_to_ltc(tt)
117 }
118 Scale::TCL => Self::tai_to_tcl(*self),
119 }
120 }
121
122 #[inline]
123 pub const fn to_tai(&self, current: Scale) -> Dt {
124 Self::from(self.sec, self.attos, current)
125 }
126
127 #[inline]
128 pub const fn to(&self, current: Scale, target: Scale) -> Dt {
129 if !current.eq(target) {
130 Self::from(self.sec, self.attos, current).to_internal(target)
131 } else {
132 *self
133 }
134 }
135
136 #[inline]
139 pub const fn convert_using_drift(self, reference: Self, drift: Drift) -> Self {
140 let span = self.to_diff_raw(reference);
141 let correction = drift.time_diff_after(&span);
142 self.add(correction)
143 }
144
145 pub const fn convert_back_using_drift(self, reference: Self, drift: Drift) -> Self {
151 if drift.rate().is_zero() && drift.accel().is_zero() {
152 return self.sub(*drift.constant());
153 }
154 let mut guess = self;
155 let mut i = 0u32;
156 while i < 16 {
157 let span = guess.to_diff_raw(reference);
158 let correction = drift.time_diff_after(&span);
159 guess = self.sub(correction);
160 i += 1;
161 }
162 guess
163 }
164
165 #[inline]
167 pub const fn convert_using_model(self, model: ClockModel) -> Self {
168 self.convert_using_drift(model.reference, model.drift)
169 }
170
171 #[inline]
173 pub const fn convert_back_using_model(self, model: ClockModel) -> Self {
174 self.convert_back_using_drift(model.reference, model.drift)
175 }
176
177 pub const fn tai_to_tdb(tai: Self) -> Self {
178 let tt = tai.add(TT_TAI_OFFSET);
179 let correction = tdb_minus_tt(tt.to_sec_f());
180 tt.add(Dt::from_sec_f(correction))
181 }
182
183 pub const fn tdb_to_tai(tdb: Self) -> Self {
184 let elapsed = Self::to_attos_since_tcg_tcb_epoch(tdb);
186 let linear_span = Self::mul_lb(elapsed); let mut tt = tdb
188 .sub(Dt::from_attos(linear_span, Scale::TAI))
189 .sub(Dt::from_attos(TDB0_ATTOS, Scale::TAI));
190
191 let mut i = 0u32;
193 while i < 8 {
194 let p = tdb_minus_tt(tt.to_sec_f());
195 let new_tt = tdb.sub(Dt::from_sec_f(p));
196
197 let delta = new_tt.to_diff_raw(tt);
199 if delta.sec == 0 && delta.attos < 1 {
200 tt = new_tt;
201 break;
202 }
203
204 tt = new_tt;
205 i += 1;
206 }
207
208 tt.sub(TT_TAI_OFFSET)
209 }
210
211 pub(crate) const fn tai_to_tcg(tai: Self) -> Self {
212 let tt = tai.add(TT_TAI_OFFSET);
213 Self::tt_to_tcg(tt)
214 }
215
216 pub(crate) const fn tai_to_tcb(tai: Self) -> Self {
217 let tdb = Self::tai_to_tdb(tai);
218 Self::tdb_to_tcb(tdb)
219 }
220
221 #[inline]
224 pub(crate) const fn to_attos_since_tcg_tcb_epoch(numerical: Self) -> i128 {
225 numerical.to_attos() - TCG_TCB_REF_ATTOS_SINCE_J2000
226 }
227
228 pub(crate) const fn mul_rate(attos: i128, num: i128, den: i128) -> i128 {
230 if attos == 0 {
231 return 0;
232 }
233 let sign = if attos < 0 { -1i128 } else { 1i128 };
234 let a = if attos < 0 { -attos } else { attos };
235 let q = a / den;
236 let r = a % den;
237 sign * (q * num + (r * num) / den)
238 }
239
240 #[inline]
241 pub(crate) const fn mul_lg(attos: i128) -> i128 {
242 Self::mul_rate(attos, LG_NUM, LG_DEN)
243 }
244
245 #[inline]
246 pub(crate) const fn mul_lb(attos: i128) -> i128 {
247 Self::mul_rate(attos, LB_NUM, LB_DEN)
248 }
249
250 pub(crate) const fn tt_to_tcg(tt: Self) -> Self {
251 let elapsed = Self::to_attos_since_tcg_tcb_epoch(tt);
252 let span_attos = Self::mul_lg(elapsed);
253 tt.add(Dt::from_attos(span_attos, Scale::TAI))
254 }
255
256 pub(crate) const fn tcg_to_tt(tcg: Self) -> Self {
257 let elapsed_cg = Self::to_attos_since_tcg_tcb_epoch(tcg);
258 let span_attos = Self::mul_rate(elapsed_cg, LG_NUM, LG_DEN + LG_NUM);
259 tcg.sub(Dt::from_attos(span_attos, Scale::TAI))
260 }
261
262 pub(crate) const fn tcb_to_tdb(tcb: Self) -> Self {
263 let elapsed_cg = Self::to_attos_since_tcg_tcb_epoch(tcb);
264 let span_attos = Self::mul_rate(elapsed_cg, LB_NUM, LB_DEN + LB_NUM);
265 tcb.sub(Dt::from_attos(span_attos, Scale::TAI))
266 .sub(Dt::from_attos(TDB0_ATTOS, Scale::TAI))
267 }
268
269 pub(crate) const fn tdb_to_tcb(tdb: Self) -> Self {
270 let elapsed = Self::to_attos_since_tcg_tcb_epoch(tdb);
271 let span_attos = Self::mul_lb(elapsed);
272 tdb.add(Dt::from_attos(span_attos, Scale::TAI))
273 .add(Dt::from_attos(TDB0_ATTOS, Scale::TAI))
274 }
275}