use crate::data::runtime_data::{active_time_data, time_data_eop_at};
use qtty::{Day, Second};
pub fn eop_start() -> Option<Day> {
active_time_data()
.eop_start_mjd()
.map(|v| Day::new(v as f64))
}
pub fn eop_observed_end() -> Option<Day> {
active_time_data()
.eop_observed_end_mjd()
.map(|v| Day::new(v as f64))
}
pub fn eop_end() -> Option<Day> {
active_time_data().eop_end_mjd().map(|v| Day::new(v as f64))
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct EopValues {
pub mjd_utc: Day,
pub pm_xp: Option<qtty::f64::Arcsecond>,
pub pm_yp: Option<qtty::f64::Arcsecond>,
pub ut1_minus_utc: Second,
pub lod: Option<qtty::f64::Millisecond>,
pub dx: Option<qtty::f64::MilliArcsecond>,
pub dy: Option<qtty::f64::MilliArcsecond>,
pub ut1_observed: bool,
}
#[inline]
pub fn builtin_eop_covers(mjd_utc: Day) -> bool {
let data = active_time_data();
time_data_eop_at(data.as_ref(), mjd_utc).is_some()
}
pub fn builtin_eop_at(mjd_utc: Day) -> Option<EopValues> {
let data = active_time_data();
time_data_eop_at(data.as_ref(), mjd_utc)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::archive::time::{EopPoint, TimeDataBundle, TimeDataProvenance, UtcTaiSegment};
use crate::data::runtime_data::with_test_time_data;
use qtty::{Arcsecond, Day, MilliArcsecond, Millisecond, Second};
fn make_test_eop_bundle(points: Vec<EopPoint>) -> TimeDataBundle {
TimeDataBundle::new(
vec![UtcTaiSegment {
start_mjd: 41317,
end_mjd: None,
base: Second::new(37.0),
reference_mjd: 41317.0,
slope_seconds_per_day: 0.0,
}],
vec![(41714.0, 42.184), (42369.0, 45.0)],
41714.0,
points,
TimeDataProvenance::new("test", "x", "x", "x", "x"),
)
}
fn three_point_fixture() -> Vec<EopPoint> {
vec![
EopPoint {
mjd: 50000,
pm_observed: true,
ut1_observed: true,
nutation_observed: true,
pm_xp: Some(Arcsecond::new(0.1)),
pm_yp: Some(Arcsecond::new(0.2)),
ut1_minus_utc: Second::new(0.3),
lod: Some(Millisecond::new(1.0)),
dx: Some(MilliArcsecond::new(0.01)),
dy: Some(MilliArcsecond::new(0.02)),
},
EopPoint {
mjd: 50001,
pm_observed: true,
ut1_observed: true,
nutation_observed: true,
pm_xp: Some(Arcsecond::new(0.2)),
pm_yp: Some(Arcsecond::new(0.4)),
ut1_minus_utc: Second::new(0.5),
lod: Some(Millisecond::new(2.0)),
dx: None,
dy: None,
},
EopPoint {
mjd: 50002,
pm_observed: false,
ut1_observed: false,
nutation_observed: false,
pm_xp: Some(Arcsecond::new(0.3)),
pm_yp: Some(Arcsecond::new(0.6)),
ut1_minus_utc: Second::new(0.7),
lod: None,
dx: None,
dy: None,
},
]
}
#[test]
fn covers_start_and_end() {
let bundle = make_test_eop_bundle(three_point_fixture());
with_test_time_data(bundle, || {
assert!(builtin_eop_covers(Day::new(50000.0)));
assert!(builtin_eop_covers(Day::new(50002.0)));
assert!(!builtin_eop_covers(Day::new(49999.0)));
assert!(!builtin_eop_covers(Day::new(50003.0)));
});
}
#[test]
fn exact_point_matches_source() {
let points = three_point_fixture();
let bundle = make_test_eop_bundle(points.clone());
with_test_time_data(bundle, || {
let mid = &points[1];
let got = builtin_eop_at(Day::new(mid.mjd as f64)).unwrap();
assert_eq!(got.pm_xp.map(|v| v.value()), mid.pm_xp.map(|v| v.value()));
assert_eq!(got.pm_yp.map(|v| v.value()), mid.pm_yp.map(|v| v.value()));
assert!(
(got.ut1_minus_utc.value() - mid.ut1_minus_utc.value()).abs() < 1e-12,
"ut1: {} vs {}",
got.ut1_minus_utc.value(),
mid.ut1_minus_utc.value()
);
assert_eq!(got.dx.map(|v| v.value()), mid.dx.map(|v| v.value()));
assert_eq!(got.dy.map(|v| v.value()), mid.dy.map(|v| v.value()));
});
}
#[test]
fn midpoint_is_halfway() {
let points = three_point_fixture();
let bundle = make_test_eop_bundle(points.clone());
with_test_time_data(bundle, || {
let got = builtin_eop_at(Day::new(50000.5)).unwrap();
let expected =
0.5 * (points[0].ut1_minus_utc.value() + points[1].ut1_minus_utc.value());
assert!((got.ut1_minus_utc.value() - expected).abs() < 1e-12);
});
}
#[test]
fn missing_optional_fields_remain_missing() {
let bundle = make_test_eop_bundle(three_point_fixture());
with_test_time_data(bundle, || {
let got = builtin_eop_at(Day::new(50001.5)).unwrap();
assert_eq!(got.dx, None);
assert_eq!(got.dy, None);
});
}
#[test]
fn out_of_range_returns_none() {
let bundle = make_test_eop_bundle(three_point_fixture());
with_test_time_data(bundle, || {
assert!(builtin_eop_at(Day::new(49990.0)).is_none());
assert!(builtin_eop_at(Day::new(50010.0)).is_none());
});
}
#[test]
fn no_eop_data_returns_none() {
let bundle = make_test_eop_bundle(Vec::new());
with_test_time_data(bundle, || {
assert!(builtin_eop_at(Day::new(50000.0)).is_none());
assert!(!builtin_eop_covers(Day::new(50000.0)));
});
}
}