/rust/registry/src/index.crates.io-1949cf8c6b5b557f/hifitime-4.3.0/src/timescale/mod.rs
Line | Count | Source |
1 | | /* |
2 | | * Hifitime |
3 | | * Copyright (C) 2017-onward Christopher Rabotin <christopher.rabotin@gmail.com> et al. (cf. https://github.com/nyx-space/hifitime/graphs/contributors) |
4 | | * This Source Code Form is subject to the terms of the Mozilla Public |
5 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
6 | | * file, You can obtain one at https://mozilla.org/MPL/2.0/. |
7 | | * |
8 | | * Documentation: https://nyxspace.com/ |
9 | | */ |
10 | | |
11 | | #[cfg(feature = "python")] |
12 | | use pyo3::prelude::*; |
13 | | |
14 | | #[cfg(kani)] |
15 | | mod kani; |
16 | | |
17 | | #[cfg(feature = "serde")] |
18 | | use serde_derive::{Deserialize, Serialize}; |
19 | | |
20 | | mod fmt; |
21 | | |
22 | | /// EXPERIMENTAL Temps Lunaire Coordonnee / Lunar Coordinated Time |
23 | | pub(crate) mod tcl; |
24 | | |
25 | | use crate::{Duration, Epoch, Unit, SECONDS_PER_DAY}; |
26 | | |
27 | | /// The J1900 reference epoch (1900-01-01 at noon) TAI. |
28 | | pub const J1900_REF_EPOCH: Epoch = Epoch { |
29 | | duration: Duration { |
30 | | centuries: 0, |
31 | | nanoseconds: 43200000000000, |
32 | | }, |
33 | | time_scale: TimeScale::TAI, |
34 | | }; |
35 | | |
36 | | /// The J2000 reference epoch (2000-01-01 at midnight) TAI. |
37 | | /// |UTC - TAI| = XX Leap Seconds on that day. |
38 | | pub const J2000_REF_EPOCH: Epoch = Epoch { |
39 | | duration: Duration { |
40 | | centuries: 1, |
41 | | nanoseconds: 43200000000000, |
42 | | }, |
43 | | time_scale: TimeScale::TAI, |
44 | | }; |
45 | | |
46 | | pub const GPST_REF_EPOCH: Epoch = Epoch::from_tai_duration(Duration { |
47 | | centuries: 0, |
48 | | nanoseconds: 2_524_953_619_000_000_000, // XXX |
49 | | }); |
50 | | pub const SECONDS_GPS_TAI_OFFSET: f64 = 2_524_953_619.0; |
51 | | pub const SECONDS_GPS_TAI_OFFSET_I64: i64 = 2_524_953_619; |
52 | | pub const DAYS_GPS_TAI_OFFSET: f64 = SECONDS_GPS_TAI_OFFSET / SECONDS_PER_DAY; |
53 | | |
54 | | /// QZSS and GPS share the same reference epoch. |
55 | | pub const QZSST_REF_EPOCH: Epoch = GPST_REF_EPOCH; |
56 | | |
57 | | /// GST (Galileo) reference epoch is 13 seconds before 1999 August 21 UTC at midnight. |
58 | | /// |UTC - TAI| = XX Leap Seconds on that day. |
59 | | pub const GST_REF_EPOCH: Epoch = Epoch::from_tai_duration(Duration { |
60 | | centuries: 0, |
61 | | nanoseconds: 3_144_268_819_000_000_000, // 3_144_268_800_000_000_000, |
62 | | }); |
63 | | pub const SECONDS_GST_TAI_OFFSET: f64 = 3_144_268_819.0; |
64 | | pub const SECONDS_GST_TAI_OFFSET_I64: i64 = 3_144_268_819; |
65 | | |
66 | | /// BDT(BeiDou): 2005 Dec 31st Midnight |
67 | | /// BDT (BeiDou) reference epoch is 2005 December 31st UTC at midnight. **This time scale is synchronized with UTC.** |
68 | | /// |UTC - TAI| = XX Leap Seconds on that day. |
69 | | pub const BDT_REF_EPOCH: Epoch = Epoch::from_tai_duration(Duration { |
70 | | centuries: 1, |
71 | | nanoseconds: 189_302_433_000_000_000, //189_302_400_000_000_000, |
72 | | }); |
73 | | pub const SECONDS_BDT_TAI_OFFSET: f64 = 3_345_062_433.0; |
74 | | pub const SECONDS_BDT_TAI_OFFSET_I64: i64 = 3_345_062_433; |
75 | | |
76 | | /// The UNIX reference epoch of 1970-01-01 in TAI duration, accounting only for IERS leap seconds. |
77 | | pub const UNIX_REF_EPOCH: Epoch = Epoch::from_tai_duration(Duration { |
78 | | centuries: 0, |
79 | | nanoseconds: 2_208_988_800_000_000_000, |
80 | | }); |
81 | | |
82 | | /// Reference year of the Hifitime prime epoch. |
83 | | pub(crate) const HIFITIME_REF_YEAR: i32 = 1900; |
84 | | |
85 | | /// Enum of the different time systems available |
86 | | #[non_exhaustive] |
87 | | #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] |
88 | | #[cfg_attr(feature = "python", pyclass(eq, eq_int))] |
89 | | #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] |
90 | | pub enum TimeScale { |
91 | | /// TAI is the representation of an Epoch internally |
92 | | TAI, |
93 | | /// Terrestrial Time (TT) (previously called Terrestrial Dynamical Time (TDT)) |
94 | | TT, |
95 | | /// Ephemeris Time as defined by SPICE (slightly different from true TDB) |
96 | | ET, |
97 | | /// Dynamic Barycentric Time (TDB) (higher fidelity SPICE ephemeris time) |
98 | | TDB, |
99 | | /// Universal Coordinated Time |
100 | | UTC, |
101 | | /// GPS Time scale whose reference epoch is UTC midnight between 05 January and 06 January 1980; |
102 | | /// cf. <https://gssc.esa.int/navipedia/index.php/Time_References_in_GNSS#GPS_Time_.28GPST.29>. |UTC - TAI| = 19 Leap Seconds on that day. |
103 | | GPST, |
104 | | /// Galileo Time scale |
105 | | GST, |
106 | | /// BeiDou Time scale |
107 | | BDT, |
108 | | /// QZSS Time scale has the same properties as GPST but with dedicated clocks |
109 | | QZSST, |
110 | | /// Geocentric Coordinate Time |
111 | | TCG, |
112 | | /// Barycentric Coordinate Time |
113 | | TCB, |
114 | | /// Experimental Lunar Time, option (iii) from the Lunar Reference Timescale paper, A Bourgoin*, P Defraigne and F Meynadier |
115 | | /// |
116 | | /// TL is defined as a linear scaling of TCL such that TL has no secular drift |
117 | | /// with respect to TT. Since this implementation omits the bounded periodic |
118 | | /// TCL-TT terms, TL is equivalent to TT after the common 1977 reference epoch. |
119 | | TL, |
120 | | /// Experimental mean Lunar Coordinate Time of Lunar reference timescale, A Bourgoin*, P Defraigne and F Meynadier |
121 | | /// |
122 | | /// This is not a full IAU-quality TCL realization. It models only the |
123 | | /// conventional secular mean rate between TCL and TT: |
124 | | /// ```text |
125 | | /// d(TCL - TT) / dTT ≈ 6.8e-10 |
126 | | /// ``` |
127 | | /// The bounded periodic TCL-TT terms and ephemeris-dependent relativistic |
128 | | /// integral are intentionally omitted |
129 | | TCL, |
130 | | } |
131 | | |
132 | | impl Default for TimeScale { |
133 | | /// Builds default TAI time scale |
134 | 0 | fn default() -> Self { |
135 | 0 | Self::TAI |
136 | 0 | } |
137 | | } |
138 | | |
139 | | impl TimeScale { |
140 | 4.09k | pub(crate) const fn formatted_len(&self) -> usize { |
141 | 4.09k | match &self { |
142 | 0 | Self::QZSST => 5, |
143 | 257 | Self::GPST => 4, |
144 | | Self::TAI |
145 | | | Self::TDB |
146 | | | Self::UTC |
147 | | | Self::GST |
148 | | | Self::BDT |
149 | | | Self::TCG |
150 | | | Self::TCB |
151 | 3.70k | | Self::TCL => 3, |
152 | 141 | Self::ET | Self::TT | Self::TL => 2, |
153 | | } |
154 | 4.09k | } |
155 | | |
156 | | /// Returns true if Self is based off a GNSS constellation |
157 | 0 | pub const fn is_gnss(&self) -> bool { |
158 | 0 | matches!(self, Self::GPST | Self::GST | Self::BDT | Self::QZSST) |
159 | 0 | } |
160 | | |
161 | | /// Returns this time scale's reference epoch: Time Scale initialization date, |
162 | | /// expressed as an Epoch in TAI |
163 | 0 | pub const fn reference_epoch(self) -> Epoch { |
164 | 0 | Epoch { |
165 | 0 | duration: Duration::ZERO, |
166 | 0 | time_scale: self, |
167 | 0 | } |
168 | 0 | } |
169 | | |
170 | | /// Returns the duration between this time scale's reference epoch and the hifitime "prime epoch" of 1900-01-01 00:00:00 TAI (the NTP prime epoch). |
171 | | /// This is used to compute the Gregorian date representations in any time scale. |
172 | 24.6k | pub(crate) const fn prime_epoch_offset(self) -> Duration { |
173 | 24.6k | match self { |
174 | | TimeScale::ET | TimeScale::TDB => { |
175 | | // Both ET and TDB are defined at J2000, which is 2000-01-01 12:00:00 and there were only 36524 days in the 20th century. |
176 | | // Hence, this math is the output of (Unit.Century*1 + Unit.Hour*12 - Unit.Day*1).to_parts() via Hifitime in Python. |
177 | 20.2k | Duration { |
178 | 20.2k | centuries: 0, |
179 | 20.2k | nanoseconds: 3155716800000000000, |
180 | 20.2k | } |
181 | | } |
182 | 8 | TimeScale::GPST | TimeScale::QZSST => Duration { |
183 | 8 | centuries: 0, |
184 | 8 | nanoseconds: 2_524_953_619_000_000_000, |
185 | 8 | }, |
186 | 6 | TimeScale::GST => Duration { |
187 | 6 | centuries: 0, |
188 | 6 | nanoseconds: 3_144_268_819_000_000_000, |
189 | 6 | }, |
190 | 5 | TimeScale::BDT => Duration { |
191 | 5 | centuries: 1, |
192 | 5 | nanoseconds: 189_302_433_000_000_000, |
193 | 5 | }, |
194 | | TimeScale::TCG | TimeScale::TL | TimeScale::TCL => { |
195 | | // TCG reference epoch is 1977-01-01 00:00:32.184 TT. |
196 | 5 | Duration { |
197 | 5 | centuries: 0, |
198 | 5 | nanoseconds: 2429913632184000000, |
199 | 5 | } |
200 | | } |
201 | | TimeScale::TCB => { |
202 | | // TCB reference epoch is 1977-01-01 00:00:32.184 TT + 65.5 µs. |
203 | 1 | Duration { |
204 | 1 | centuries: 0, |
205 | 1 | nanoseconds: 2429913632184065500, |
206 | 1 | } |
207 | | } |
208 | 4.33k | _ => Duration::ZERO, |
209 | | } |
210 | 24.6k | } |
211 | | |
212 | 3.70k | pub(crate) fn gregorian_epoch_offset(self) -> Duration { |
213 | 3.70k | let prime_offset = self.prime_epoch_offset(); |
214 | | |
215 | 3.70k | prime_offset - prime_offset.subdivision(Unit::Second).unwrap() |
216 | 3.70k | } |
217 | | } |
218 | | |
219 | | #[cfg_attr(feature = "python", pymethods)] |
220 | | impl TimeScale { |
221 | | /// Returns true if self takes leap seconds into account |
222 | | /// :rtype: bool |
223 | 0 | pub const fn uses_leap_seconds(&self) -> bool { |
224 | 0 | matches!(self, Self::UTC) |
225 | 0 | } |
226 | | } |
227 | | |
228 | | /// Allows conversion of a TimeSystem into a u8 |
229 | | /// Mapping: TAI: 0; TT: 1; ET: 2; TDB: 3; UTC: 4; GPST: 5; GST: 6; BDT: 7; QZSST: 8; TCG: 9; TCB: 10; |
230 | | impl From<TimeScale> for u8 { |
231 | 0 | fn from(ts: TimeScale) -> Self { |
232 | 0 | match ts { |
233 | 0 | TimeScale::TAI => 0, |
234 | 0 | TimeScale::TT => 1, |
235 | 0 | TimeScale::ET => 2, |
236 | 0 | TimeScale::TDB => 3, |
237 | 0 | TimeScale::UTC => 4, |
238 | 0 | TimeScale::GPST => 5, |
239 | 0 | TimeScale::GST => 6, |
240 | 0 | TimeScale::BDT => 7, |
241 | 0 | TimeScale::QZSST => 8, |
242 | 0 | TimeScale::TCG => 9, |
243 | 0 | TimeScale::TCB => 10, |
244 | 0 | TimeScale::TL => 11, |
245 | 0 | TimeScale::TCL => 12, |
246 | | } |
247 | 0 | } |
248 | | } |
249 | | |
250 | | /// Allows conversion of a u8 into a TimeSystem. |
251 | | /// Mapping: 1: TT; 2: ET; 3: TDB; 4: UTC; 5: GPST; 6: GST; 7: BDT; 8: QZSST; 9: TCG; 10: TCB; anything else: TAI |
252 | | impl From<u8> for TimeScale { |
253 | 0 | fn from(val: u8) -> Self { |
254 | 0 | match val { |
255 | 0 | 1 => Self::TT, |
256 | 0 | 2 => Self::ET, |
257 | 0 | 3 => Self::TDB, |
258 | 0 | 4 => Self::UTC, |
259 | 0 | 5 => Self::GPST, |
260 | 0 | 6 => Self::GST, |
261 | 0 | 7 => Self::BDT, |
262 | 0 | 8 => Self::QZSST, |
263 | 0 | 9 => Self::TCG, |
264 | 0 | 10 => Self::TCB, |
265 | 0 | 11 => Self::TL, |
266 | 0 | 12 => Self::TCL, |
267 | 0 | _ => Self::TAI, |
268 | | } |
269 | 0 | } |
270 | | } |
271 | | |
272 | | #[cfg(test)] |
273 | | mod ut_timescale { |
274 | | use super::TimeScale; |
275 | | |
276 | | #[test] |
277 | | #[cfg(feature = "serde")] |
278 | | fn test_serdes() { |
279 | | let ts = TimeScale::UTC; |
280 | | let content = "\"UTC\""; |
281 | | assert_eq!(content, serde_json::to_string(&ts).unwrap()); |
282 | | let parsed: TimeScale = serde_json::from_str(content).unwrap(); |
283 | | assert_eq!(ts, parsed); |
284 | | } |
285 | | |
286 | | #[test] |
287 | | fn test_ts() { |
288 | | for ts_u8 in 0..u8::MAX { |
289 | | let ts = TimeScale::from(ts_u8); |
290 | | let ts_u8_back: u8 = ts.into(); |
291 | | // If the u8 is greater than 10, it isn't valid and necessarily encoded as TAI. |
292 | | if ts_u8 < 13 { |
293 | | assert_eq!(ts_u8_back, ts_u8, "got {ts_u8_back} want {ts_u8}"); |
294 | | } else { |
295 | | assert_eq!(ts, TimeScale::TAI); |
296 | | } |
297 | | } |
298 | | } |
299 | | |
300 | | #[test] |
301 | | #[cfg(feature = "std")] |
302 | | fn test_ref_epoch() { |
303 | | use crate::{Duration, Epoch, Unit}; |
304 | | let prime_e = Epoch::from_duration(Duration::ZERO, TimeScale::TAI); |
305 | | assert_eq!(prime_e.duration, Duration::ZERO); |
306 | | assert_eq!(format!("{prime_e}"), "1900-01-01T00:00:00 TAI"); |
307 | | // NOTE: There are only 36524 days in the 20th century, but one century is 36425, so we "overflow" the next century by one day! |
308 | | assert_eq!( |
309 | | format!("{}", prime_e + Unit::Century * 1), |
310 | | "2000-01-02T00:00:00 TAI" |
311 | | ); |
312 | | |
313 | | assert_eq!( |
314 | | format!("{}", TimeScale::ET.reference_epoch()), |
315 | | "2000-01-01T12:00:00 ET" |
316 | | ); |
317 | | } |
318 | | } |