use crate::errors::{AstroError, AstroResult};
use crate::matrix::RotationMatrix3;
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct CipCoordinates {
pub x: f64,
pub y: f64,
}
impl CipCoordinates {
pub fn new(x: f64, y: f64) -> Self {
Self { x, y }
}
pub fn from_npb_matrix(npb_matrix: &RotationMatrix3) -> AstroResult<Self> {
let matrix = npb_matrix.elements();
let x = matrix[2][0];
let y = matrix[2][1];
if x.abs() > 0.2 || y.abs() > 0.2 {
return Err(AstroError::math_error(
"CIP coordinate extraction",
crate::errors::MathErrorKind::InvalidInput,
&format!(
"CIP coordinates out of reasonable range: X={:.6}, Y={:.6}",
x, y
),
));
}
Ok(Self { x, y })
}
pub fn magnitude(&self) -> f64 {
libm::sqrt(self.x * self.x + self.y * self.y)
}
pub fn to_degrees(&self) -> (f64, f64) {
(
self.x * crate::constants::RAD_TO_DEG,
self.y * crate::constants::RAD_TO_DEG,
)
}
pub fn to_arcseconds(&self) -> (f64, f64) {
(
self.x * crate::constants::RAD_TO_DEG * 3600.0,
self.y * crate::constants::RAD_TO_DEG * 3600.0,
)
}
}
impl std::fmt::Display for CipCoordinates {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let (x_as, y_as) = self.to_arcseconds();
write!(f, "CIP(X={:.3}\", Y={:.3}\")", x_as, y_as)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::matrix::RotationMatrix3;
#[test]
fn test_cip_coordinates_creation() {
let cip = CipCoordinates::new(1e-6, -2e-6);
assert_eq!(cip.x, 1e-6);
assert_eq!(cip.y, -2e-6);
}
#[test]
fn test_cip_magnitude() {
let cip = CipCoordinates::new(3e-6, 4e-6);
assert!((cip.magnitude() - 5e-6).abs() < 1e-12);
}
#[test]
fn test_cip_display() {
let cip = CipCoordinates::new(1e-6, -1e-6);
let display = format!("{}", cip);
assert!(display.contains("CIP"));
assert!(display.contains("0.206")); }
#[test]
fn test_cip_from_identity_matrix() {
let identity = RotationMatrix3::identity();
let cip = CipCoordinates::from_npb_matrix(&identity).unwrap();
assert_eq!(cip.x, 0.0);
assert_eq!(cip.y, 0.0);
}
#[test]
fn test_cip_validation_error() {
let invalid_matrix = RotationMatrix3::from_array([
[1.0, 0.0, 0.0],
[0.0, 1.0, 0.0],
[0.25, 0.05, 1.0], ]);
let result = CipCoordinates::from_npb_matrix(&invalid_matrix);
assert!(result.is_err());
let error_message = format!("{}", result.unwrap_err());
assert!(error_message.contains("CIP coordinates out of reasonable range"));
}
#[test]
fn test_cip_reasonable_values() {
let reasonable_cip = CipCoordinates::new(1e-6, 1e-6);
assert_eq!(reasonable_cip.x, 1e-6);
assert_eq!(reasonable_cip.y, 1e-6);
assert_eq!(reasonable_cip.magnitude(), libm::sqrt(2e-12_f64)); }
}