tempoch_core/constats.rs
1// SPDX-License-Identifier: AGPL-3.0-only
2// Copyright (C) 2026 Vallés Puig, Ramon
3
4//! Typed epoch and offset constants.
5//!
6//! These values are exposed as raw `qtty` quantities so callers can pass them
7//! directly to `Time::<A>::from_julian_days`, `from_modified_julian_days`, etc.
8
9use qtty::{Day, Second};
10
11pub use crate::delta_t::DELTA_T_PREDICTION_HORIZON_MJD;
12
13/// J2000 epoch as JD(TT) = 2_451_545.0.
14pub const J2000_JD_TT: Day = Day::new(2_451_545.0);
15
16/// Offset between the Julian Day and Modified Julian Day counts.
17///
18/// `MJD = JD - JD_MINUS_MJD`.
19pub const JD_MINUS_MJD: Day = Day::new(2_400_000.5);
20
21/// Exact `TT - TAI` offset (32.184 s).
22pub const TT_MINUS_TAI: Second = Second::new(32.184);
23
24/// Unix epoch as a Julian Day on the UTC axis: 1970-01-01T00:00:00 UTC.
25pub const UNIX_EPOCH_JD: Day = Day::new(2_440_587.5);
26
27/// Unix epoch as a Modified Julian Day on the UTC axis.
28pub const UNIX_EPOCH_MJD: Day = Day::new(40_587.0);
29
30/// GPS epoch as a Julian Day on the UTC axis: 1980-01-06T00:00:00 UTC.
31pub const GPS_EPOCH_JD_UTC: Day = Day::new(2_444_244.5);
32
33/// Exact `TAI - UTC` offset at the GPS epoch.
34pub const GPS_EPOCH_TAI_MINUS_UTC: Second = Second::new(19.0);
35
36/// GPS epoch expressed as a Julian Day on the TAI axis.
37///
38/// At the GPS epoch, `TAI - UTC = 19 s` exactly, so this is
39/// `GPS_EPOCH_JD_UTC + 19 s`, converted to Julian days.
40pub const GPS_EPOCH_JD_TAI: Day =
41 GPS_EPOCH_JD_UTC.const_add(GPS_EPOCH_TAI_MINUS_UTC.to_const::<qtty::unit::Day>());
42
43/// IAU 2000 B1.9 reference epoch `T0` as JD(TT).
44pub const IAU_TIME_EPOCH_T0_JD: Day = Day::new(2_443_144.500_372_5);
45
46/// Start of the interval where the built-in TT↔TDB truncated series achieves
47/// about 10 microseconds accuracy relative to numerical integration.
48///
49/// The seven-term Fairhead-Bretagnon truncation (USNO Circular 179, Eq. 2.27)
50/// has two distinct error budgets:
51///
52/// - **~2 µs** relative to the full Fairhead-Bretagnon (1990) series (series
53/// truncation error only).
54/// - **~10 µs** relative to JPL numerical integration (full series + modeling
55/// error combined).
56///
57/// The **end-to-end** accuracy ceiling is therefore **~10 µs**. These bounds
58/// apply within the 1600-01-01 to 2200-01-01 TT interval. This constant marks
59/// the start of that interval, corresponding approximately to 1600-01-01 TT.
60pub const TDB_TT_MODEL_HIGH_ACCURACY_START_JD: Day = Day::new(2_305_447.5);
61
62/// End of the interval where the built-in TT↔TDB truncated series achieves
63/// about 10 microseconds accuracy relative to numerical integration.
64///
65/// See [`TDB_TT_MODEL_HIGH_ACCURACY_START_JD`] for the full accuracy breakdown.
66/// This constant corresponds approximately to 2200-01-01 TT.
67pub const TDB_TT_MODEL_HIGH_ACCURACY_END_JD: Day = Day::new(2_524_598.5);
68
69/// GPS epoch expressed as TAI seconds since J2000 TT on the TAI axis.
70///
71/// The storage convention is `(JD_TAI(P) − J2000_JD_TT) × 86400`. For the GPS
72/// epoch, `JD_UTC = GPS_EPOCH_JD_UTC` and `TAI − UTC = 19 s` (exact), giving:
73///
74/// `(44_244.0 − 51_544.5) × 86400 + 19 = −630_763_181`.
75pub const GPS_EPOCH_TAI: Second = Second::new(-630_763_181.0);
76
77/// First MJD covered by the compiled UTC-TAI segment table.
78///
79/// This corresponds to 1961-01-01. UTC was defined starting from this date.
80/// For queries before this boundary, `Time<UTC>` conversions return
81/// [`crate::ConversionError::UtcBeforeDefinition`] by default. Back-extrapolation
82/// of the first segment can be enabled by building the conversion context with
83/// [`crate::TimeContext::allow_pre_definition_utc`]. The extrapolated offset is
84/// internally consistent (round-trips close) but is not a historically defined
85/// UTC-TAI value; no standard UTC existed before 1961.
86pub const UTC_DEFINED_FROM_MJD: Day = Day::new(37_300.0);
87
88/// One Julian century in days (36 525 d), used for the Fairhead–Bretagnon
89/// parameter.
90pub(crate) const DAYS_PER_JC: Day = Day::new(36_525.0);
91
92pub(crate) const UTC_INTERVAL_EPS: Day = Day::new(1e-15);
93pub(crate) const L_G: f64 = 6.969_290_134e-10;
94pub(crate) const L_B: f64 = 1.550_519_768e-8;
95pub(crate) const TDB0: Second = Second::new(-6.55e-5);
96
97#[cfg(test)]
98mod tests {
99 use super::*;
100
101 #[test]
102 fn unix_epoch_jd_and_mjd_constants_are_consistent() {
103 assert!((UNIX_EPOCH_JD - JD_MINUS_MJD - UNIX_EPOCH_MJD).abs() < Day::new(1e-15));
104 }
105
106 #[test]
107 fn j2000_reference_values_match_known_offsets() {
108 assert!((J2000_JD_TT - JD_MINUS_MJD - Day::new(51_544.5)).abs() < Day::new(1e-12));
109 assert!((TT_MINUS_TAI - Second::new(32.184)).abs() < Second::new(1e-12));
110 assert!((UTC_DEFINED_FROM_MJD - Day::new(37_300.0)).abs() < Day::new(1e-12));
111 assert!((GPS_EPOCH_JD_UTC - Day::new(2_444_244.5)).abs() < Day::new(1e-12));
112 assert!((GPS_EPOCH_TAI_MINUS_UTC - Second::new(19.0)).abs() < Second::new(1e-12));
113 assert!(
114 (GPS_EPOCH_JD_TAI - GPS_EPOCH_JD_UTC - GPS_EPOCH_TAI_MINUS_UTC.to::<qtty::unit::Day>())
115 .abs()
116 < Day::new(1e-9)
117 );
118 }
119
120 #[test]
121 fn high_accuracy_model_interval_is_ordered() {
122 assert!(TDB_TT_MODEL_HIGH_ACCURACY_END_JD > TDB_TT_MODEL_HIGH_ACCURACY_START_JD);
123 assert!(GPS_EPOCH_TAI.is_finite());
124 assert!(DELTA_T_PREDICTION_HORIZON_MJD.is_finite());
125 }
126}