use super::utc_tai::time_data_tai_minus_utc_mjd_extrapolated;
use crate::earth::delta_t::delta_t_seconds_from_modern_points;
use crate::earth::eop::EopValues;
use crate::foundation::error::ConversionError;
use qtty::Day as DayQuantity;
use qtty::Second;
use tempoch_time_data::{EopPoint, TimeDataBundle};
pub(crate) fn time_data_delta_t(
data: &TimeDataBundle,
jd_ut: DayQuantity,
) -> Result<Second, ConversionError> {
delta_t_seconds_from_modern_points(jd_ut, data.modern_delta_t_points())
}
pub(crate) fn time_data_eop_at(data: &TimeDataBundle, mjd_utc: DayQuantity) -> Option<EopValues> {
let points = data.eop_points();
let first = points.first()?.mjd;
let last = points.last()?.mjd;
let mjd_f = mjd_utc.value();
let lo_i = mjd_f.floor() as i32;
let hi_i = lo_i + 1;
if lo_i < first || lo_i > last {
return None;
}
let lo = find_eop_point(points, lo_i)?;
let hi = if hi_i > last {
lo
} else {
find_eop_point(points, hi_i)?
};
let frac = if lo.mjd == hi.mjd {
0.0
} else {
mjd_f - lo_i as f64
};
let lerp = |a: f64, b: f64| a + frac * (b - a);
let lerp_opt = |a: Option<f64>, b: Option<f64>| match (a, b) {
(Some(a), Some(b)) => Some(lerp(a, b)),
_ => None,
};
let lod_milliseconds = match (lo.lod_milliseconds, hi.lod_milliseconds) {
(Some(a), Some(b)) => Some(lerp(a, b)),
_ => None,
};
let ut1_minus_utc = {
let lo_offset =
time_data_tai_minus_utc_mjd_extrapolated(data, DayQuantity::new(lo_i as f64));
let hi_offset =
time_data_tai_minus_utc_mjd_extrapolated(data, DayQuantity::new(hi_i as f64));
let query_offset = time_data_tai_minus_utc_mjd_extrapolated(data, mjd_utc);
match (lo_offset, hi_offset, query_offset) {
(Some(lo_tmu), Some(hi_tmu), Some(query_tmu)) => {
let lo_cont = lo.ut1_minus_utc_seconds - lo_tmu.value();
let hi_cont = hi.ut1_minus_utc_seconds - hi_tmu.value();
Second::new(lerp(lo_cont, hi_cont) + query_tmu.value())
}
_ => Second::new(lerp(lo.ut1_minus_utc_seconds, hi.ut1_minus_utc_seconds)),
}
};
Some(EopValues {
mjd_utc,
pm_xp_arcsec: lerp_opt(lo.pm_xp_arcsec, hi.pm_xp_arcsec),
pm_yp_arcsec: lerp_opt(lo.pm_yp_arcsec, hi.pm_yp_arcsec),
ut1_minus_utc,
lod_milliseconds,
dx_milliarcsec: lerp_opt(lo.dx_milliarcsec, hi.dx_milliarcsec),
dy_milliarcsec: lerp_opt(lo.dy_milliarcsec, hi.dy_milliarcsec),
ut1_observed: lo.ut1_observed && hi.ut1_observed,
})
}
fn find_eop_point(points: &[EopPoint], mjd: i32) -> Option<EopPoint> {
let idx = points.partition_point(|point| point.mjd < mjd);
let point = *points.get(idx)?;
(point.mjd == mjd).then_some(point)
}