celestial_core/cio/
mod.rs1pub mod coordinates;
24pub mod locator;
25pub mod origins;
26
27pub use coordinates::CipCoordinates;
28pub use locator::CioLocator;
29pub use origins::EquationOfOrigins;
30
31use crate::errors::AstroResult;
32use crate::matrix::RotationMatrix3;
33
34pub fn gcrs_to_cirs_matrix(x: f64, y: f64, s: f64) -> RotationMatrix3 {
39 let r2 = x * x + y * y;
40 let e = if r2 > 0.0 { libm::atan2(y, x) } else { 0.0 };
41 let d = libm::atan(libm::sqrt(r2 / (1.0 - r2)));
42
43 let mut matrix = RotationMatrix3::identity();
44 matrix.rotate_z(e);
45 matrix.rotate_y(d);
46 matrix.rotate_z(-(e + s));
47
48 matrix
49}
50
51#[derive(Debug, Clone, PartialEq)]
56pub struct CioSolution {
57 pub cip: CipCoordinates,
59 pub s: f64,
61 pub equation_of_origins: f64,
63}
64
65impl CioSolution {
66 pub fn calculate(
68 npb_matrix: &crate::matrix::RotationMatrix3,
69 tt_centuries: f64,
70 ) -> AstroResult<Self> {
71 let cip = CipCoordinates::from_npb_matrix(npb_matrix)?;
72
73 let locator = CioLocator::iau2006a(tt_centuries);
74 let s = locator.calculate(cip.x, cip.y)?;
75
76 let equation_of_origins = EquationOfOrigins::from_npb_and_locator(npb_matrix, s)?;
77
78 Ok(Self {
79 cip,
80 s,
81 equation_of_origins,
82 })
83 }
84}
85
86#[cfg(test)]
87mod tests {
88 use super::*;
89
90 #[test]
91 fn cio_identity_matrix_returns_zero_components() {
92 let identity = crate::matrix::RotationMatrix3::identity();
93 let solution = CioSolution::calculate(&identity, 0.0).unwrap();
94
95 assert!(solution.cip.x.abs() < 1e-15);
96 assert!(solution.cip.y.abs() < 1e-15);
97 assert!(solution.s.abs() < 1e-8);
98 assert!(solution.equation_of_origins.abs() < 1e-8);
99 }
100
101 #[test]
102 fn gcrs_to_cirs_matrix_with_zero_inputs_returns_identity() {
103 let matrix = gcrs_to_cirs_matrix(0.0, 0.0, 0.0);
104 let identity = RotationMatrix3::identity();
105 assert!(matrix.max_difference(&identity) < 1e-15);
106 }
107
108 #[test]
109 fn gcrs_to_cirs_matrix_is_rotation_matrix() {
110 let matrix = gcrs_to_cirs_matrix(1e-6, 2e-6, 5e-9);
111 assert!(matrix.is_rotation_matrix(1e-14));
112 }
113
114 #[test]
115 fn gcrs_to_cirs_matrix_small_cip_produces_near_identity() {
116 let x = 1e-6;
117 let y = 1e-6;
118 let s = 1e-9;
119 let matrix = gcrs_to_cirs_matrix(x, y, s);
120 let identity = RotationMatrix3::identity();
121 assert!(matrix.max_difference(&identity) < 1e-5);
122 }
123}