use crate::errors::AstroResult;
use crate::matrix::RotationMatrix3;
pub struct EquationOfOrigins;
impl EquationOfOrigins {
pub fn from_npb_and_locator(npb_matrix: &RotationMatrix3, s: f64) -> AstroResult<f64> {
let matrix = npb_matrix.elements();
let x = matrix[2][0];
let ax = x / (1.0 + matrix[2][2]);
let xs = 1.0 - ax * x;
let ys = -ax * matrix[2][1];
let zs = -x;
let p = matrix[0][0] * xs + matrix[0][1] * ys + matrix[0][2] * zs;
let q = matrix[1][0] * xs + matrix[1][1] * ys + matrix[1][2] * zs;
let eo = if p != 0.0 || q != 0.0 {
s - q.atan2(p)
} else {
s
};
Ok(eo)
}
pub fn from_equation_of_equinoxes_approximation(
equation_of_equinoxes: f64,
s: f64,
) -> AstroResult<f64> {
let eo_approx = equation_of_equinoxes - s;
Ok(eo_approx)
}
pub fn to_arcseconds(eo_radians: f64) -> f64 {
eo_radians.to_degrees() * 3600.0
}
pub fn to_milliarcseconds(eo_radians: f64) -> f64 {
eo_radians.to_degrees() * 3600.0 * 1000.0
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::matrix::RotationMatrix3;
#[test]
fn test_equation_of_origins_identity_matrix() {
let identity = RotationMatrix3::identity();
let s = 0.0;
let eo = EquationOfOrigins::from_npb_and_locator(&identity, s).unwrap();
assert!(
eo.abs() < 1e-15,
"EO should be zero for identity matrix: {}",
eo
);
}
#[test]
fn test_equation_of_origins_small_rotation() {
let mut small_rotation = RotationMatrix3::identity();
small_rotation.rotate_z(1e-6);
let s = 1e-7;
let eo = EquationOfOrigins::from_npb_and_locator(&small_rotation, s).unwrap();
assert!(eo.abs() < 1e-3, "EO should be small: {}", eo);
assert!(
eo.abs() > 1e-12,
"EO should be non-zero for rotation: {}",
eo
);
}
#[test]
fn test_equation_of_origins_approximation() {
let equation_of_equinoxes = 1e-6;
let s = 5e-7;
let eo =
EquationOfOrigins::from_equation_of_equinoxes_approximation(equation_of_equinoxes, s)
.unwrap();
let expected = equation_of_equinoxes - s;
assert!(
(eo - expected).abs() < 1e-15,
"Approximation should match expected value"
);
}
#[test]
fn test_unit_conversions() {
let eo_rad = 1e-6;
let eo_arcsec = EquationOfOrigins::to_arcseconds(eo_rad);
let eo_mas = EquationOfOrigins::to_milliarcseconds(eo_rad);
assert!((eo_arcsec * 1000.0 - eo_mas).abs() < 1e-10);
assert!(eo_arcsec > 0.0 && eo_arcsec < 1.0);
}
#[test]
fn test_large_eo_validation() {
let mut normal_matrix = RotationMatrix3::identity();
normal_matrix.rotate_z(1e-6);
let normal_s = 1e-7;
let eo = EquationOfOrigins::from_npb_and_locator(&normal_matrix, normal_s).unwrap();
assert!(eo.abs() < 1e-3);
}
}