celestial-time 0.1.1-alpha.2

Pure Rust astronomical time scales
Documentation
use crate::{JulianDate, TimeResult};
use celestial_core::angle::wrap_0_2pi;
use celestial_core::constants::{J2000_JD, TWOPI};
use celestial_core::math::fmod;

pub fn earth_rotation_angle(ut1_jd: &JulianDate) -> TimeResult<f64> {
    let ut1_jd1 = ut1_jd.jd1();
    let ut1_jd2 = ut1_jd.jd2();

    let (d1, d2) = if ut1_jd1 < ut1_jd2 {
        (ut1_jd1, ut1_jd2)
    } else {
        (ut1_jd2, ut1_jd1)
    };

    let t = d1 + (d2 - J2000_JD);

    let f = fmod(d1, 1.0) + fmod(d2, 1.0);

    let theta = wrap_0_2pi(TWOPI * (f + 0.7790572732640 + 0.00273781191135448 * t));

    Ok(theta)
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::UT1;
    use celestial_core::constants::J2000_JD;

    #[test]
    fn test_era_j2000() {
        let ut1 = UT1::j2000();
        let era = earth_rotation_angle(&ut1.to_julian_date()).unwrap();

        assert!((era - 4.894961212823757).abs() < 1e-12);
    }

    #[test]
    fn test_era_precision() {
        let test_cases = [
            (J2000_JD, 0.0),
            (2451545.5, 0.0),
            (2440587.5, 0.0),
            (J2000_JD, 0.5),
        ];

        for (jd1, jd2) in test_cases {
            let ut1 = UT1::from_julian_date(JulianDate::new(jd1, jd2));
            let era = earth_rotation_angle(&ut1.to_julian_date()).unwrap();

            assert!((0.0..2.0 * celestial_core::constants::PI).contains(&era));
        }
    }
}