use crate::time::JulianDate;
use qtty::*;
use crate::astro::iers_data;
#[derive(Debug, Clone, Copy)]
pub struct EopValues {
pub dut1: Seconds,
pub xp: Arcseconds,
pub yp: Arcseconds,
pub dx: MilliArcseconds,
pub dy: MilliArcseconds,
}
impl Default for EopValues {
fn default() -> Self {
Self {
dut1: Seconds::new(0.0),
xp: Arcseconds::new(0.0),
yp: Arcseconds::new(0.0),
dx: MilliArcseconds::new(0.0),
dy: MilliArcseconds::new(0.0),
}
}
}
impl EopValues {
#[inline]
pub fn jd_ut1(&self, jd_utc: JulianDate) -> JulianDate {
JulianDate::new(jd_utc.value() + self.dut1.to::<Day>().value())
}
}
pub trait EopProvider {
fn eop_at(&self, jd_utc: JulianDate) -> EopValues;
}
#[derive(Debug, Clone, Copy, Default)]
pub struct NullEop;
impl EopProvider for NullEop {
#[inline]
fn eop_at(&self, _jd_utc: JulianDate) -> EopValues {
EopValues::default()
}
}
#[derive(Debug, Clone)]
pub struct IersEop {
table: &'static [iers_data::EopEntry],
}
impl Default for IersEop {
fn default() -> Self {
Self {
table: iers_data::EOP_TABLE,
}
}
}
impl IersEop {
#[inline]
pub fn new() -> Self {
Self::default()
}
pub fn mjd_range(&self) -> Option<(f64, f64)> {
let first = self.table.first()?.mjd;
let last = self.table.last()?.mjd;
Some((first, last))
}
#[inline]
pub fn len(&self) -> usize {
self.table.len()
}
#[inline]
pub fn is_empty(&self) -> bool {
self.table.is_empty()
}
}
impl EopProvider for IersEop {
fn eop_at(&self, jd_utc: JulianDate) -> EopValues {
let mjd = jd_utc.value() - 2_400_000.5;
match iers_data::lookup(self.table, mjd) {
Some(e) => EopValues {
dut1: Seconds::new(e.dut1),
xp: Arcseconds::new(e.xp),
yp: Arcseconds::new(e.yp),
dx: MilliArcseconds::new(e.dx),
dy: MilliArcseconds::new(e.dy),
},
None => EopValues::default(),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn null_eop_returns_zeros() {
let eop = NullEop;
let vals = eop.eop_at(JulianDate::J2000);
assert_eq!(vals.dut1.value(), 0.0);
assert_eq!(vals.xp.value(), 0.0);
assert_eq!(vals.yp.value(), 0.0);
assert_eq!(vals.dx.value(), 0.0);
assert_eq!(vals.dy.value(), 0.0);
}
#[test]
fn eop_jd_ut1_conversion() {
let vals = EopValues {
dut1: Seconds::new(0.35),
..Default::default()
};
let jd_utc = JulianDate::J2000;
let jd_ut1 = vals.jd_ut1(jd_utc);
let diff_days = (jd_ut1 - jd_utc).value();
let diff_s = diff_days * 86400.0;
assert!(
(diff_s - 0.35).abs() < 1e-3,
"UT1-UTC = {}s, expected 0.35s",
diff_s
);
}
#[test]
fn eop_unit_conversions() {
let as2rad = std::f64::consts::PI / (180.0 * 3600.0);
let vals = EopValues {
xp: Arcseconds::new(0.1),
yp: Arcseconds::new(0.2),
dx: MilliArcseconds::new(0.3),
dy: MilliArcseconds::new(0.4),
..Default::default()
};
assert!((vals.xp.to::<Radian>().value() - 0.1 * as2rad).abs() < 1e-20);
assert!((vals.yp.to::<Radian>().value() - 0.2 * as2rad).abs() < 1e-20);
assert!((vals.dx.to::<Radian>().value() - 0.3e-3 * as2rad).abs() < 1e-20);
assert!((vals.dy.to::<Radian>().value() - 0.4e-3 * as2rad).abs() < 1e-20);
}
}