use crate::epoch::{J2000_TAI_TJT, TAI_TT_OFFSET};
use crate::time_converter_tai_tt::tai_to_tt;
use crate::{SecondsSince, TAI, TDB};
use std::f64::consts::PI;
pub fn tdb_tt_offset(tai_tjt: f64) -> f64 {
let dt_days = tai_tjt - J2000_TAI_TJT;
let g = (PI / 180.0) * (357.53 + 0.9856003 * dt_days);
0.001658 * g.sin() + 0.000014 * (2.0 * g).sin()
}
pub fn tai_to_tdb(tai_seconds: f64, tai_tjt: f64) -> f64 {
let tt = tai_to_tt(tai_seconds);
let offset = tdb_tt_offset(tai_tjt);
tt + offset
}
pub fn tdb_to_tai(tdb_seconds: f64, tai_tjt_initial: f64) -> f64 {
let mut tai = tdb_seconds - TAI_TT_OFFSET;
let mut tai_tjt = tai_tjt_initial;
for _ in 0..5 {
let offset = tdb_tt_offset(tai_tjt);
let new_tai = tdb_seconds - TAI_TT_OFFSET - offset;
let dtai = new_tai - tai;
tai = new_tai;
tai_tjt = tai_tjt_initial + dtai / 86400.0;
if dtai.abs() < 1e-15 || (tai.abs() > 0.0 && (dtai / tai).abs() < 1.0e-15) {
break;
}
}
tai
}
pub fn tai_to_tdb_typed(t: SecondsSince<TAI>, tai_tjt: f64) -> SecondsSince<TDB> {
SecondsSince::from_seconds(tai_to_tdb(t.as_seconds(), tai_tjt))
}
pub fn tdb_to_tai_typed(t: SecondsSince<TDB>, tai_tjt_initial: f64) -> SecondsSince<TAI> {
SecondsSince::from_seconds(tdb_to_tai(t.as_seconds(), tai_tjt_initial))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn tdb_offset_at_j2000() {
let offset = tdb_tt_offset(J2000_TAI_TJT);
assert!(
offset.abs() < 0.002,
"TDB-TT offset at J2000: {} (should be < 2ms)",
offset
);
}
#[test]
fn tai_tdb_round_trip() {
let tai = 1_000_000.0;
let tai_tjt = J2000_TAI_TJT + tai / 86400.0;
let tdb = tai_to_tdb(tai, tai_tjt);
let back = tdb_to_tai(tdb, tai_tjt);
assert!(
(back - tai).abs() < 1e-10,
"TAI-TDB round trip: {} -> {} -> {}, err={}",
tai,
tdb,
back,
(back - tai).abs()
);
}
#[test]
fn tai_tdb_round_trip_near_zero() {
let tai = 0.0;
let tai_tjt = J2000_TAI_TJT;
let tdb = tai_to_tdb(tai, tai_tjt);
let back = tdb_to_tai(tdb, tai_tjt);
assert!(
(back - tai).abs() < 1e-10,
"TAI-TDB round trip near zero: {} -> {} -> {}, err={}",
tai,
tdb,
back,
(back - tai).abs()
);
}
#[test]
fn tai_tdb_typed_matches_f64() {
let tai = 1_000_000.0_f64;
let tai_tjt = J2000_TAI_TJT + tai / 86400.0;
let tdb_typed = tai_to_tdb_typed(SecondsSince::<TAI>::from_seconds(tai), tai_tjt);
assert_eq!(tdb_typed.as_seconds(), tai_to_tdb(tai, tai_tjt));
}
#[test]
fn tai_tdb_typed_round_trip() {
let tai_s = 1_000_000.0_f64;
let tai_tjt = J2000_TAI_TJT + tai_s / 86400.0;
let tai = SecondsSince::<TAI>::from_seconds(tai_s);
let tdb = tai_to_tdb_typed(tai, tai_tjt);
let back = tdb_to_tai_typed(tdb, tai_tjt);
let err = (back.as_seconds() - tai.as_seconds()).abs();
assert!(err < 1e-10, "TAI->TDB->TAI typed round-trip err={}", err);
}
}