use qtty::{Day, Second};
use crate::format::{J2000s, JD, MJD};
use crate::model::scale::{TAI, TT, UTC};
use crate::model::time::Time;
use crate::InfallibleFormatForScale;
pub const J2000_JD_TT_DAY: Day = Day::new(2_451_545.0);
pub(crate) const JD_MINUS_MJD: Day = Day::new(2_400_000.5);
pub const TT_MINUS_TAI: Second = Second::new(32.184);
pub const UNIX_EPOCH_JD_DAY: Day = Day::new(2_440_587.5);
pub const GPS_EPOCH_JD_UTC_DAY: Day = Day::new(2_444_244.5);
pub const GPS_EPOCH_TAI_MINUS_UTC: Second = Second::new(19.0);
pub const IAU_TIME_EPOCH_T0_JD_DAY: Day = Day::new(2_443_144.500_372_5);
pub const TDB_TT_MODEL_HIGH_ACCURACY_START_JD_DAY: Day = Day::new(2_305_447.5);
pub const TDB_TT_MODEL_HIGH_ACCURACY_END_JD_DAY: Day = Day::new(2_524_598.5);
pub const UTC_DEFINED_FROM_MJD_DAY: Day = Day::new(37_300.0);
pub(crate) const UTC_INTERVAL_EPS: Day = Day::new(1e-15);
pub(crate) const L_G: f64 = 6.969_290_134e-10;
pub(crate) const L_B: f64 = 1.550_519_768e-8;
pub(crate) const TDB0: Second = Second::new(-6.55e-5);
#[inline]
pub(crate) fn unix_epoch_mjd_day() -> Day {
UNIX_EPOCH_JD_DAY - JD_MINUS_MJD
}
#[inline]
pub(crate) fn gps_epoch_jd_tai_day() -> Day {
GPS_EPOCH_JD_UTC_DAY + GPS_EPOCH_TAI_MINUS_UTC.to::<qtty::unit::Day>()
}
#[inline]
pub(crate) fn gps_epoch_tai_seconds() -> Second {
(GPS_EPOCH_JD_UTC_DAY - J2000_JD_TT_DAY).to::<qtty::unit::Second>() + GPS_EPOCH_TAI_MINUS_UTC
}
#[inline]
pub fn j2000_jd_tt() -> Time<TT, JD> {
<JD as InfallibleFormatForScale<TT>>::into_time(J2000_JD_TT_DAY)
}
#[inline]
pub fn unix_epoch_jd() -> Time<UTC, JD> {
<JD as InfallibleFormatForScale<UTC>>::into_time(UNIX_EPOCH_JD_DAY)
}
#[inline]
pub fn unix_epoch_mjd() -> Time<UTC, MJD> {
<MJD as InfallibleFormatForScale<UTC>>::into_time(unix_epoch_mjd_day())
}
#[inline]
pub fn gps_epoch_jd_utc() -> Time<UTC, JD> {
<JD as InfallibleFormatForScale<UTC>>::into_time(GPS_EPOCH_JD_UTC_DAY)
}
#[inline]
pub fn gps_epoch_jd_tai() -> Time<TAI, JD> {
<JD as InfallibleFormatForScale<TAI>>::into_time(gps_epoch_jd_tai_day())
}
#[inline]
pub fn iau_time_epoch_t0_jd() -> Time<TT, JD> {
<JD as InfallibleFormatForScale<TT>>::into_time(IAU_TIME_EPOCH_T0_JD_DAY)
}
#[inline]
pub fn tdb_tt_model_high_accuracy_start_jd() -> Time<TT, JD> {
<JD as InfallibleFormatForScale<TT>>::into_time(TDB_TT_MODEL_HIGH_ACCURACY_START_JD_DAY)
}
#[inline]
pub fn tdb_tt_model_high_accuracy_end_jd() -> Time<TT, JD> {
<JD as InfallibleFormatForScale<TT>>::into_time(TDB_TT_MODEL_HIGH_ACCURACY_END_JD_DAY)
}
#[inline]
pub fn gps_epoch_tai() -> Time<TAI, J2000s> {
<J2000s as InfallibleFormatForScale<TAI>>::into_time(gps_epoch_tai_seconds())
}
#[inline]
pub fn utc_defined_from_mjd() -> Time<UTC, MJD> {
<MJD as InfallibleFormatForScale<UTC>>::into_time(UTC_DEFINED_FROM_MJD_DAY)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::earth::delta_t::DELTA_T_PREDICTION_HORIZON_MJD;
#[test]
fn unix_epoch_mjd_is_derived_from_jd() {
assert!((UNIX_EPOCH_JD_DAY - JD_MINUS_MJD - unix_epoch_mjd_day()).abs() < Day::new(1e-15));
}
#[test]
fn j2000_reference_values_match_known_offsets() {
assert!((J2000_JD_TT_DAY - JD_MINUS_MJD - Day::new(51_544.5)).abs() < Day::new(1e-12));
assert!((TT_MINUS_TAI - Second::new(32.184)).abs() < Second::new(1e-12));
assert!((UTC_DEFINED_FROM_MJD_DAY - Day::new(37_300.0)).abs() < Day::new(1e-12));
assert!((GPS_EPOCH_JD_UTC_DAY - Day::new(2_444_244.5)).abs() < Day::new(1e-12));
assert!((GPS_EPOCH_TAI_MINUS_UTC - Second::new(19.0)).abs() < Second::new(1e-12));
assert!(
(gps_epoch_jd_tai_day()
- GPS_EPOCH_JD_UTC_DAY
- GPS_EPOCH_TAI_MINUS_UTC.to::<qtty::unit::Day>())
.abs()
< Day::new(1e-9)
);
assert!((gps_epoch_tai_seconds() - Second::new(-630_763_181.0)).abs() < Second::new(1e-9));
}
#[test]
fn high_accuracy_model_interval_is_ordered() {
assert!(tdb_tt_model_high_accuracy_end_jd() > tdb_tt_model_high_accuracy_start_jd());
assert!(gps_epoch_tai().raw().is_finite());
assert!(DELTA_T_PREDICTION_HORIZON_MJD.value().is_finite());
}
#[test]
fn helper_constructors_match_exported_scalar_constants() {
assert_eq!(j2000_jd_tt().raw(), J2000_JD_TT_DAY);
assert_eq!(unix_epoch_jd().raw(), UNIX_EPOCH_JD_DAY);
assert_eq!(unix_epoch_mjd().raw(), unix_epoch_mjd_day());
assert_eq!(gps_epoch_jd_utc().raw(), GPS_EPOCH_JD_UTC_DAY);
assert_eq!(gps_epoch_jd_tai().raw(), gps_epoch_jd_tai_day());
assert_eq!(iau_time_epoch_t0_jd().raw(), IAU_TIME_EPOCH_T0_JD_DAY);
assert_eq!(
tdb_tt_model_high_accuracy_start_jd().raw(),
TDB_TT_MODEL_HIGH_ACCURACY_START_JD_DAY
);
assert_eq!(
tdb_tt_model_high_accuracy_end_jd().raw(),
TDB_TT_MODEL_HIGH_ACCURACY_END_JD_DAY
);
assert_eq!(gps_epoch_tai().raw(), gps_epoch_tai_seconds());
assert_eq!(utc_defined_from_mjd().raw(), UTC_DEFINED_FROM_MJD_DAY);
}
}