use std::f64::consts::PI;
pub fn deg_to_rad(deg: f64) -> f64 {
deg * PI / 180.0
}
pub fn rad_to_deg(rad: f64) -> f64 {
rad * 180.0 / PI
}
pub fn normalize_degrees(deg: f64) -> f64 {
let r = deg % 360.0;
if r < 0.0 { r + 360.0 } else { r }
}
pub fn normalize_radians(rad: f64) -> f64 {
let r = rad % (2.0 * PI);
if r < 0.0 { r + 2.0 * PI } else { r }
}
pub fn mean_obliquity(t: f64) -> f64 {
((5.036_11e-7 * t - 1.638_89e-7) * t - 0.013_004_167) * t + 23.439_291_11
}
pub fn ecliptic_to_equatorial(lon_deg: f64, lat_deg: f64, obliquity_deg: f64) -> (f64, f64) {
let lon = deg_to_rad(lon_deg);
let lat = deg_to_rad(lat_deg);
let eps = deg_to_rad(obliquity_deg);
let sin_lon = lon.sin();
let cos_lon = lon.cos();
let sin_lat = lat.sin();
let cos_lat = lat.cos();
let sin_eps = eps.sin();
let cos_eps = eps.cos();
let ra = (sin_lon * cos_eps * cos_lat - sin_lat * sin_eps).atan2(cos_lon * cos_lat);
let dec = (sin_lat * cos_eps + cos_lat * sin_eps * sin_lon).asin();
(normalize_degrees(rad_to_deg(ra)), rad_to_deg(dec))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn deg_rad_roundtrip() {
let deg = 123.456;
assert!((rad_to_deg(deg_to_rad(deg)) - deg).abs() < 1e-12);
}
#[test]
fn normalize_degrees_cases() {
assert!((normalize_degrees(0.0)).abs() < 1e-10);
assert!((normalize_degrees(360.0)).abs() < 1e-10);
assert!((normalize_degrees(-90.0) - 270.0).abs() < 1e-10);
assert!((normalize_degrees(720.5) - 0.5).abs() < 1e-10);
}
#[test]
fn mean_obliquity_j2000() {
let eps = mean_obliquity(0.0);
assert!((eps - 23.4393).abs() < 0.001, "got {eps}");
}
#[test]
fn ecliptic_to_equatorial_vernal_equinox() {
let (ra, dec) = ecliptic_to_equatorial(0.0, 0.0, 23.4393);
assert!(ra.abs() < 1e-10 || (ra - 360.0).abs() < 1e-10);
assert!(dec.abs() < 1e-10);
}
}