use crate::types::{Ecef, Vec3};
#[derive(Clone, Copy, Debug, Default, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Cpa {
pub time_s: f64,
pub distance_m: f64,
}
pub fn closest_approach(p1: Ecef, v1: Vec3, p2: Ecef, v2: Vec3) -> Cpa {
let prel = p1.vec() - p2.vec();
let vrel = v1 - v2;
let vv = vrel.dot(vrel);
let time_s = if vv == 0.0 { 0.0 } else { -prel.dot(vrel) / vv };
Cpa {
time_s,
distance_m: (prel + vrel * time_s).norm(),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn parallel_non_closing_is_now() {
let c = closest_approach(
Ecef::new(0.0, 0.0, 0.0),
Vec3::new(1.0, 0.0, 0.0),
Ecef::new(0.0, 3.0, 0.0),
Vec3::new(1.0, 0.0, 0.0),
);
assert_eq!(c.time_s, 0.0);
assert!((c.distance_m - 3.0).abs() < 1e-9);
}
#[test]
fn diverging_reports_a_past_time() {
let c = closest_approach(
Ecef::new(0.0, 0.0, 0.0),
Vec3::new(1.0, 0.0, 0.0),
Ecef::new(-10.0, 0.0, 0.0),
Vec3::new(-1.0, 0.0, 0.0),
);
assert!(c.time_s < 0.0 && c.distance_m < 1e-9);
}
}