Skip to main content

deep_time/dt/
conversions_mars.rs

1use crate::{
2    Dt, MARS_REF_TT, MARS_REF_TT_ATTOS, MARS_SOL_ATTOS, MARS_SOL_LENGTH_SEC, Real, Scale, floor_f,
3    to_sec_f,
4};
5
6impl Dt {
7    /// Exact helper: elapsed attoseconds since the Mars MSD reference epoch (JD 2405522.0028779 TT).
8    #[inline]
9    pub(crate) const fn to_attos_since_mars_msd_epoch(numerical_tt: Dt) -> i128 {
10        numerical_tt.to_attos() - MARS_REF_TT_ATTOS
11    }
12
13    /// Returns the exact Mars Sol Date (MSD) as a tuple of integer sols and the fractional part of a sol.
14    ///
15    /// The computation follows the canonical NASA GISS / AM2000 formulation and works for any input
16    /// [`Scale`]. Leap seconds are automatically accounted for when converting from UTC.
17    pub const fn to_msd_exact(&self, current: Scale) -> (i64, u128) {
18        let tt = self.to(current, Scale::TT);
19        let elapsed = Self::to_attos_since_mars_msd_epoch(tt);
20        let whole_sols = elapsed.div_euclid(MARS_SOL_ATTOS);
21        let frac_attos = elapsed.rem_euclid(MARS_SOL_ATTOS) as u128;
22
23        (Dt::clamp_i128_to_i64(whole_sols), frac_attos)
24    }
25
26    /// Returns Mars Coordinated Time (MTC) as a [`Dt`] representing
27    /// seconds into the current sol (range `[0, one Martian sol)`).
28    #[inline]
29    pub const fn to_mtc(&self, current: Scale) -> Dt {
30        let (_, frac_attos) = self.to_msd_exact(current);
31        Dt::from_attos(frac_attos as i128, Scale::TAI)
32    }
33
34    /// Creates a `Dt` (in TT) from an exact Mars Sol Date using full library precision.
35    pub const fn from_msd_exact(whole_sols: i64, frac_attos: u128) -> Self {
36        let elapsed_attos = (whole_sols as i128) * MARS_SOL_ATTOS + frac_attos as i128;
37        let tt = MARS_REF_TT.add(Dt::from_attos(elapsed_attos, Scale::TAI));
38        Self::from(tt.sec, tt.attos, Scale::TT)
39    }
40
41    /// Creates a `Dt` (in TT) from a floating-point Mars Sol Date.
42    /// Non-exact Real.
43    pub const fn from_msd(msd: Real) -> Self {
44        let whole = floor_f(msd) as i64;
45        let frac = msd - f!(whole);
46        let frac_span = Dt::from_sec_f(frac * MARS_SOL_LENGTH_SEC);
47        Self::from_msd_exact(whole, frac_span.to_attos() as u128)
48    }
49
50    /// Returns the Mars Sol Date (MSD) as a floating-point value (matches NASA Mars24 output).
51    /// Non-exact Real.
52    #[inline]
53    pub const fn to_msd(&self, current: Scale) -> Real {
54        let (whole, frac) = self.to_msd_exact(current);
55        f!(whole) + to_sec_f(frac) / MARS_SOL_LENGTH_SEC
56    }
57}