use crate::{SecondsSince, GMST};
use std::f64::consts::PI;
use uom::si::angle::radian;
use uom::si::f64::Angle;
pub fn ut1_to_gmst_days(du: f64) -> f64 {
let du2 = du * du;
let du3 = du2 * du;
0.7790572733 + 1.002737909350795 * du + 8.0775e-16 * du2 - 1.5e-24 * du3
}
pub fn ut1_to_gmst_seconds(du: f64) -> f64 {
ut1_to_gmst_days(du) * 86400.0
}
pub fn ut1_to_gmst_radians(du: f64) -> f64 {
let gmst_days = ut1_to_gmst_days(du);
let fractional = gmst_days - gmst_days.floor();
fractional * 2.0 * PI
}
pub fn ut1_to_gmst_seconds_typed(du: f64) -> SecondsSince<GMST> {
SecondsSince::from_seconds(ut1_to_gmst_seconds(du))
}
pub fn ut1_to_gmst_angle(du: f64) -> Angle {
Angle::new::<radian>(ut1_to_gmst_radians(du))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn gmst_at_j2000() {
let gmst_days = ut1_to_gmst_days(0.0);
let gmst_deg = (gmst_days - gmst_days.floor()) * 360.0;
assert!(
(gmst_deg - 280.46).abs() < 0.01,
"GMST at noon J2000: {:.4} degrees, expected ~280.46",
gmst_deg
);
}
#[test]
fn gmst_accumulates() {
let gmst_0 = ut1_to_gmst_days(0.0);
let gmst_365 = ut1_to_gmst_days(365.25);
assert!(
gmst_365 > gmst_0 + 365.0,
"GMST should accumulate: gmst_0={}, gmst_365={}",
gmst_0,
gmst_365
);
}
#[test]
fn gmst_seconds_matches_days() {
let days = 100.0;
let gmst_days = ut1_to_gmst_days(days);
let gmst_secs = ut1_to_gmst_seconds(days);
assert!(
(gmst_secs - gmst_days * 86400.0).abs() < 1e-10,
"seconds should be days * 86400"
);
}
#[test]
fn gmst_radians_is_bounded() {
for days in [-1000.0, 0.0, 365.25, 3652.5] {
let gmst_rad = ut1_to_gmst_radians(days);
assert!(
(0.0..std::f64::consts::TAU).contains(&gmst_rad),
"GMST radians out of range: {} at days={}",
gmst_rad,
days
);
}
}
#[test]
fn ut1_gmst_typed_matches_f64() {
let du = 365.25_f64;
let typed_secs = ut1_to_gmst_seconds_typed(du);
assert_eq!(typed_secs.as_seconds(), ut1_to_gmst_seconds(du));
let typed_angle = ut1_to_gmst_angle(du);
assert_eq!(typed_angle.get::<radian>(), ut1_to_gmst_radians(du));
}
#[test]
fn ut1_gmst_typed_angle_is_bounded() {
for days in [-1000.0, 0.0, 365.25, 3652.5] {
let angle_rad = ut1_to_gmst_angle(days).get::<radian>();
assert!(
(0.0..std::f64::consts::TAU).contains(&angle_rad),
"typed GMST angle out of range: {} at days={}",
angle_rad,
days
);
}
}
}