use crate::error::{Result, validate_ra, validate_dec};
pub fn equatorial_to_galactic(ra: f64, dec: f64) -> Result<(f64, f64)> {
validate_ra(ra)?;
validate_dec(dec)?;
let ra_rad = ra.to_radians();
let dec_rad = dec.to_radians();
let (l_rad, b_rad) = erfars::galacticcoordinates::Icrs2g(ra_rad, dec_rad);
let mut l_deg = l_rad.to_degrees();
let b_deg = b_rad.to_degrees();
if l_deg < 0.0 {
l_deg += 360.0;
} else if l_deg >= 360.0 {
l_deg -= 360.0;
}
Ok((l_deg, b_deg))
}
pub fn galactic_to_equatorial(l: f64, b: f64) -> Result<(f64, f64)> {
if !(-90.0..=90.0).contains(&b) {
return Err(crate::error::AstroError::InvalidCoordinate {
coord_type: "Galactic latitude",
value: b,
valid_range: "[-90, 90]",
});
}
let l_rad = l.to_radians();
let b_rad = b.to_radians();
let (ra_rad, dec_rad) = erfars::galacticcoordinates::G2icrs(l_rad, b_rad);
let mut ra_deg = ra_rad.to_degrees();
let dec_deg = dec_rad.to_degrees();
if ra_deg < 0.0 {
ra_deg += 360.0;
} else if ra_deg >= 360.0 {
ra_deg -= 360.0;
}
Ok((ra_deg, dec_deg))
}
pub const NGP_RA: f64 = 192.85948; pub const NGP_DEC: f64 = 27.12825;
pub const GC_RA: f64 = 266.405; pub const GC_DEC: f64 = -28.936;
pub fn galactic_landmarks() -> Vec<(&'static str, f64, f64)> {
vec![
("Galactic Center", 0.0, 0.0),
("Galactic North Pole", 0.0, 90.0), ("Galactic South Pole", 0.0, -90.0), ("Galactic Anticenter", 180.0, 0.0),
("Cygnus X-1", 71.3, 3.1),
("Large Magellanic Cloud", 280.5, -32.9),
("Small Magellanic Cloud", 302.8, -44.3),
("M31 (Andromeda)", 121.2, -21.6),
]
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_galactic_center() {
let (l, b) = equatorial_to_galactic(GC_RA, GC_DEC).unwrap();
assert!((l - 0.0).abs() < 0.01 || (l - 360.0).abs() < 0.01);
assert!((b - 0.0).abs() < 0.01);
}
#[test]
fn test_galactic_poles() {
let (_l, b) = equatorial_to_galactic(NGP_RA, NGP_DEC).unwrap();
assert!((b - 90.0).abs() < 0.01);
let sgp_ra = NGP_RA + 180.0;
let sgp_dec = -NGP_DEC;
let (_l2, b2) = equatorial_to_galactic(sgp_ra % 360.0, sgp_dec).unwrap();
assert!((b2 - (-90.0)).abs() < 0.01);
}
#[test]
fn test_galactic_round_trip() {
let test_coords = [
(83.633, 22.0145), (279.234, 38.784), (201.298, -43.019), (0.0, 0.0), ];
for (ra, dec) in test_coords {
let (l, b) = equatorial_to_galactic(ra, dec).unwrap();
let (ra2, dec2) = galactic_to_equatorial(l, b).unwrap();
let ra_diff = (ra2 - ra).abs();
let ra_diff_wrapped = (360.0 - ra_diff).abs();
let min_ra_diff = ra_diff.min(ra_diff_wrapped);
assert!(min_ra_diff < 0.01,
"RA mismatch for ({}, {}): {} -> {}", ra, dec, ra, ra2);
assert!((dec2 - dec).abs() < 0.01,
"Dec mismatch for ({}, {}): {} -> {}", ra, dec, dec, dec2);
}
}
#[test]
fn test_known_objects() {
let (l, b) = equatorial_to_galactic(266.417, -29.008).unwrap();
assert!(!(0.5..=359.5).contains(&l));
assert!(b.abs() < 0.1);
let (l, b) = equatorial_to_galactic(299.590, 35.202).unwrap();
assert!((l - 71.3).abs() < 0.5);
assert!((b - 3.1).abs() < 0.5);
}
}