use crate::constants::{AU_M, C, C_AUDAY, GS};
use nalgebra::Vector3;
const AVOID_DIVIDE_BY_ZERO: f64 = f64::MIN_POSITIVE;
pub const RMASSES: &[(&str, f64)] = &[
("sun", 1.0),
("jupiter", 1047.3486),
("saturn", 3497.898),
("uranus", 22902.98),
("neptune", 19412.24),
("venus", 408523.71),
("earth", 332946.050895),
("mars", 3098708.0),
("mercury", 6023600.0),
("moon", 27068700.387534),
("pluto", 135200000.0),
];
pub const DEFLECTORS: &[&str] = &[
"sun", "jupiter", "saturn", "moon", "venus", "uranus", "neptune",
];
pub fn rmass(name: &str) -> Option<f64> {
RMASSES.iter().find(|(n, _)| *n == name).map(|(_, m)| *m)
}
pub fn light_time_difference(position: &Vector3<f64>, observer_pos: &Vector3<f64>) -> f64 {
let dis = position.norm();
let u1 = position / (dis + AVOID_DIVIDE_BY_ZERO);
u1.dot(observer_pos) / C_AUDAY
}
pub fn add_deflection(
position: &mut Vector3<f64>,
observer: &Vector3<f64>,
deflector: &Vector3<f64>,
rmass: f64,
) {
let pq = observer + *position - deflector; let pe = observer - deflector;
let pmag = position.norm();
let qmag = pq.norm();
let emag = pe.norm();
let phat = if pmag > 0.0 {
*position / pmag
} else {
Vector3::zeros()
};
let qhat = if qmag > 0.0 {
pq / qmag
} else {
Vector3::zeros()
};
let ehat = if emag > 0.0 {
pe / emag
} else {
Vector3::zeros()
};
let pdotq = phat.dot(&qhat);
let qdote = qhat.dot(&ehat);
let edotp = ehat.dot(&phat);
if edotp.abs() > 0.99999999999 {
return;
}
let fac1 = 2.0 * GS / (C * C * emag * AU_M * rmass);
let fac2 = 1.0 + qdote;
let correction = fac1 * (pdotq * ehat - edotp * qhat) / fac2 * pmag;
*position += correction;
}
pub fn add_aberration(position: &mut Vector3<f64>, velocity: &Vector3<f64>, light_time: f64) {
let p1mag = light_time * C_AUDAY;
let vemag = velocity.norm();
let beta = vemag / C_AUDAY;
let dot = position.dot(velocity);
let cosd = dot / (p1mag * vemag + AVOID_DIVIDE_BY_ZERO);
let gammai = (1.0 - beta * beta).sqrt();
let p = beta * cosd;
let q = (1.0 + p / (1.0 + gammai)) * light_time;
let r = 1.0 + p;
*position *= gammai;
*position += q * velocity;
*position /= r;
}
#[cfg(test)]
mod tests {
use super::*;
use approx::assert_relative_eq;
#[test]
fn test_light_time_difference_along_axis() {
let position = Vector3::new(10.0, 0.0, 0.0);
let observer = Vector3::new(1.0, 0.0, 0.0);
let ltt = light_time_difference(&position, &observer);
assert_relative_eq!(ltt, 1.0 / C_AUDAY, epsilon = 1e-15);
}
#[test]
fn test_light_time_difference_perpendicular() {
let position = Vector3::new(10.0, 0.0, 0.0);
let observer = Vector3::new(0.0, 1.0, 0.0);
let ltt = light_time_difference(&position, &observer);
assert_relative_eq!(ltt, 0.0, epsilon = 1e-15);
}
#[test]
fn test_add_deflection_sun() {
let mut position = Vector3::new(10.0, 0.0, 0.0);
let observer = Vector3::new(0.0, 1.0, 0.0);
let deflector = Vector3::new(0.0, 0.0, 0.0);
let pos_before = position;
add_deflection(&mut position, &observer, &deflector, 1.0);
let shift = (position - pos_before).norm();
assert!(shift > 0.0, "Deflection should be nonzero");
assert!(shift < 1e-6, "Deflection should be tiny in AU");
}
#[test]
fn test_add_deflection_aligned_skipped() {
let mut position = Vector3::new(10.0, 0.0, 0.0);
let observer = Vector3::new(1.0, 0.0, 0.0);
let deflector = Vector3::new(0.0, 0.0, 0.0);
let pos_before = position;
add_deflection(&mut position, &observer, &deflector, 1.0);
assert_relative_eq!(position.x, pos_before.x, epsilon = 1e-15);
assert_relative_eq!(position.y, pos_before.y, epsilon = 1e-15);
assert_relative_eq!(position.z, pos_before.z, epsilon = 1e-15);
}
#[test]
fn test_add_aberration_zero_velocity() {
let mut position = Vector3::new(1.0, 0.0, 0.0);
let velocity = Vector3::new(0.0, 0.0, 0.0);
let pos_before = position;
add_aberration(&mut position, &velocity, 0.01);
assert_relative_eq!(position.x, pos_before.x, epsilon = 1e-10);
assert_relative_eq!(position.y, pos_before.y, epsilon = 1e-10);
}
#[test]
fn test_add_aberration_earth_velocity() {
let mut position = Vector3::new(1.0, 0.0, 0.0);
let earth_v_auday = 29.78e3 / AU_M * 86400.0; let velocity = Vector3::new(0.0, earth_v_auday, 0.0);
let light_time = 1.0 / C_AUDAY;
let pos_before = position;
add_aberration(&mut position, &velocity, light_time);
let shift_y = position.y - pos_before.y;
assert!(
shift_y.abs() > 1e-6,
"Aberration should shift position in y"
);
let angle = (position - pos_before).norm() / position.norm();
assert!(
angle > 1e-5 && angle < 1e-3,
"Aberration angle {} should be ~1e-4 rad",
angle
);
}
#[test]
fn test_rmass_lookup() {
assert_relative_eq!(rmass("sun").unwrap(), 1.0);
assert_relative_eq!(rmass("jupiter").unwrap(), 1047.3486);
assert!(rmass("nonexistent").is_none());
}
}