Skip to main content

deep_time/dt/
to_gps.rs

1use crate::{ATTOS_PER_SEC_I128, ATTOS_PER_WEEK, Dt, Real, SEC_PER_DAYI64, SEC_PER_WEEK, TSpan};
2
3impl Dt {
4    /// Returns the GPS week number and exact Time of Week (TOW) for this instant
5    /// when expressed in GPS Time (GPS).
6    ///
7    /// This is the format used by virtually all GNSS receivers, RINEX observation
8    /// files, NMEA sentences, and raw satellite navigation messages.
9    ///
10    /// - **GPS week number**: Full (untruncated) count of 7-day weeks since the
11    ///   traditional GPS reference epoch **1980-01-06 00:00:00 GPS**.
12    ///   Returned as `i64` (effectively unlimited range).
13    /// - **Time of Week (TOW)**: Exact elapsed time since the start of that GPS
14    ///   week, returned as a [`TSpan`] in the range `[0, 604800)` seconds.
15    ///
16    /// GPS weeks always begin on **Sunday 00:00:00 GPS**.
17    ///
18    /// # Correctness notes
19    ///
20    /// - The calculation is performed entirely on the **GPS** scale.
21    /// - GPS has **no leap seconds** (it is a continuous time scale).
22    /// - Leap seconds are automatically handled when converting from UTC or
23    ///   other scales via `to_type(Scale::GPS)`.
24    /// - The result is **exact** (attosecond precision) and independent of any
25    ///   calendar or timezone rules.
26    pub const fn to_gps_wk_and_tow(&self) -> (i64, TSpan) {
27        let elapsed = self.to_tai_since(Self::GPS_EPOCH);
28        let total_attos = elapsed.to_attos();
29        let wk = (total_attos / ATTOS_PER_WEEK) as i64;
30        let tow_attos = total_attos % ATTOS_PER_WEEK;
31        (wk, TSpan::from_attos(tow_attos))
32    }
33
34    /// Returns the day of the GPS week (0 = Sunday, 1 = Monday, …, 6 = Saturday).
35    ///
36    /// This is computed directly from GPS Time and is independent of the
37    /// Gregorian calendar.
38    pub const fn to_gps_day_of_wk(&self) -> u8 {
39        let elapsed = self.to_tai_since(Self::GPS_EPOCH);
40        let total_sec = elapsed.to_attos() / ATTOS_PER_SEC_I128;
41        let secs_into_wk = total_sec.rem_euclid(SEC_PER_WEEK as i128);
42        (secs_into_wk / SEC_PER_DAYI64 as i128) as u8
43    }
44
45    /// Returns the Time of Week (TOW) as a floating-point value in seconds.
46    ///
47    /// This is a convenience method for code that prefers `f64` / `Real`.
48    /// For full attosecond precision use [`Self::to_gps_wk_and_tow`].
49    #[inline]
50    pub const fn to_gps_tow_f(&self) -> Real {
51        let (_, tow) = self.to_gps_wk_and_tow();
52        tow.to_sec_f()
53    }
54
55    /// Returns only the GPS week number (full, untruncated).
56    #[inline]
57    pub const fn to_gps_week_number(&self) -> i64 {
58        self.to_gps_wk_and_tow().0
59    }
60}