Skip to main content

celestial_coords/frames/
ecliptic_cartesian.rs

1use crate::transforms::CartesianFrame;
2use celestial_core::constants::{FRAME_BIAS_PHI_RAD, J2000_OBLIQUITY_RAD};
3use celestial_core::Vector3;
4
5#[derive(Debug, Clone, Copy, PartialEq)]
6pub struct EclipticCartesian {
7    pub x: f64,
8    pub y: f64,
9    pub z: f64,
10}
11
12impl EclipticCartesian {
13    pub fn new(x: f64, y: f64, z: f64) -> Self {
14        Self { x, y, z }
15    }
16
17    pub fn from_vector3(v: &Vector3) -> Self {
18        Self {
19            x: v.x,
20            y: v.y,
21            z: v.z,
22        }
23    }
24}
25
26impl CartesianFrame for EclipticCartesian {
27    fn to_icrs(&self) -> Vector3 {
28        let eps = J2000_OBLIQUITY_RAD;
29        let phi = FRAME_BIAS_PHI_RAD;
30        let (sin_eps, cos_eps) = libm::sincos(eps);
31        let (sin_phi, cos_phi) = libm::sincos(phi);
32
33        let y1 = self.y * cos_eps - self.z * sin_eps;
34        let z1 = self.y * sin_eps + self.z * cos_eps;
35
36        Vector3::new(
37            self.x * cos_phi + y1 * sin_phi,
38            -self.x * sin_phi + y1 * cos_phi,
39            z1,
40        )
41    }
42
43    fn from_icrs(icrs: &Vector3) -> Self {
44        let eps = J2000_OBLIQUITY_RAD;
45        let phi = FRAME_BIAS_PHI_RAD;
46        let (sin_eps, cos_eps) = libm::sincos(eps);
47        let (sin_phi, cos_phi) = libm::sincos(phi);
48
49        let x1 = icrs.x * cos_phi - icrs.y * sin_phi;
50        let y1 = icrs.x * sin_phi + icrs.y * cos_phi;
51
52        Self {
53            x: x1,
54            y: y1 * cos_eps + icrs.z * sin_eps,
55            z: -y1 * sin_eps + icrs.z * cos_eps,
56        }
57    }
58}
59
60#[cfg(test)]
61mod tests {
62    use super::*;
63
64    #[test]
65    fn test_roundtrip() {
66        let ecl = EclipticCartesian::new(-9.8753625435, -27.9588613710, 5.8504463318);
67        let icrs = ecl.to_icrs();
68        let back = EclipticCartesian::from_icrs(&icrs);
69
70        let tol = 1e-14;
71        assert!((ecl.x - back.x).abs() < tol, "X roundtrip error");
72        assert!((ecl.y - back.y).abs() < tol, "Y roundtrip error");
73        assert!((ecl.z - back.z).abs() < tol, "Z roundtrip error");
74    }
75
76    #[test]
77    fn test_pluto_vector_signs() {
78        let ecl = EclipticCartesian::new(-9.8753625435, -27.9588613710, 5.8504463318);
79        let icrs = ecl.to_icrs();
80
81        assert!(icrs.x < 0.0, "X should be negative");
82        assert!(icrs.y < 0.0, "Y should be negative");
83        assert!(icrs.z < 0.0, "Z should be negative in ICRS");
84    }
85
86    #[test]
87    fn test_ecliptic_x_axis() {
88        let ecl = EclipticCartesian::new(1.0, 0.0, 0.0);
89        let icrs = ecl.to_icrs();
90
91        assert!(
92            (icrs.x - 1.0).abs() < 1e-10,
93            "X-axis X component should be ~1"
94        );
95        assert!(icrs.y.abs() < 1e-6, "X-axis Y component should be ~0");
96        assert!(icrs.z.abs() < 1e-10, "X-axis Z component should be ~0");
97    }
98}