use glam::DVec3;
const C_SQUARED: f64 = 89_875_517_873_681_764.0;
#[derive(Debug, Clone)]
pub struct RelativisticSource {
pub mu: f64,
pub position: DVec3,
}
pub fn compute_relativistic_correction(
primary_mu: f64,
primary_pos: DVec3,
vehicle_pos: DVec3,
vehicle_vel: DVec3,
primary_vel: DVec3,
other_sources: &[RelativisticSource],
) -> DVec3 {
let rel_pos = vehicle_pos - primary_pos; let rel_vel = vehicle_vel - primary_vel;
let r_sq = rel_pos.length_squared();
let r_mag = r_sq.sqrt();
if r_mag < 1.0 {
return DVec3::ZERO; }
let mut body_potential = 0.0;
let mut point_potential = primary_mu / r_mag;
let mut body_accel = DVec3::ZERO;
for src in other_sources {
let r_vehicle_src = (vehicle_pos - src.position).length();
if r_vehicle_src > 1.0 {
point_potential += src.mu / r_vehicle_src;
}
let r_primary_src = (primary_pos - src.position).length();
if r_primary_src > 1.0 {
body_potential += src.mu / r_primary_src;
let dir = src.position - primary_pos;
body_accel += src.mu / (r_primary_src * r_primary_src * r_primary_src) * dir;
}
}
let v_a_sq = vehicle_vel.length_squared();
let v_rel_sq = rel_vel.length_squared();
let r_dot_vb = rel_pos.dot(primary_vel);
let r_dot_vb_over_r = r_dot_vb / r_mag;
let s1 = -4.0 * point_potential - body_potential + 2.0 * v_rel_sq
- v_a_sq
- 1.5 * (r_dot_vb_over_r * r_dot_vb_over_r)
- 0.5 * (-rel_pos).dot(body_accel);
let accel1 = rel_pos * (-s1 / r_sq);
let term2_vel = 4.0 * vehicle_vel - 3.0 * primary_vel;
let scale2 = rel_pos.dot(term2_vel) / r_sq;
let accel2 = rel_vel * scale2;
let accel3 = body_accel * 3.5;
let common_factor = primary_mu / r_mag / C_SQUARED;
(accel1 + accel2 + accel3) * common_factor
}
#[cfg(test)]
mod tests {
use super::*;
const MU_SUN: f64 = 1.327_124_400_18e20;
const MU_EARTH: f64 = 3.986_004_415e14;
#[test]
fn relativistic_correction_is_small() {
let sun_pos = DVec3::ZERO;
let mercury_pos = DVec3::new(4.6e10, 0.0, 0.0);
let mercury_vel = DVec3::new(0.0, 5.9e4, 0.0);
let sun_vel = DVec3::ZERO;
let correction = compute_relativistic_correction(
MU_SUN,
sun_pos,
mercury_pos,
mercury_vel,
sun_vel,
&[], );
let newtonian = MU_SUN / (4.6e10 * 4.6e10);
let ratio = correction.length() / newtonian;
assert!(
ratio > 1e-9 && ratio < 1e-5,
"relativistic/newtonian ratio {ratio:.2e} should be ~1e-7"
);
}
#[test]
fn correction_increases_near_sun() {
let sun_pos = DVec3::ZERO;
let sun_vel = DVec3::ZERO;
let vel = DVec3::new(0.0, 5.0e4, 0.0);
let c1 = compute_relativistic_correction(
MU_SUN,
sun_pos,
DVec3::new(4.6e10, 0.0, 0.0),
vel,
sun_vel,
&[],
);
let c2 = compute_relativistic_correction(
MU_SUN,
sun_pos,
DVec3::new(2.3e10, 0.0, 0.0),
vel,
sun_vel,
&[],
);
assert!(
c2.length() > c1.length(),
"closer to Sun should give larger correction"
);
}
#[test]
fn zero_for_zero_mu() {
let correction = compute_relativistic_correction(
0.0,
DVec3::ZERO,
DVec3::new(1e10, 0.0, 0.0),
DVec3::new(0.0, 3e4, 0.0),
DVec3::ZERO,
&[],
);
assert_eq!(correction, DVec3::ZERO);
}
#[test]
fn with_perturbing_body() {
let sun_pos = DVec3::ZERO;
let mercury_pos = DVec3::new(4.6e10, 0.0, 0.0);
let mercury_vel = DVec3::new(0.0, 5.9e4, 0.0);
let sun_vel = DVec3::ZERO;
let without = compute_relativistic_correction(
MU_SUN,
sun_pos,
mercury_pos,
mercury_vel,
sun_vel,
&[],
);
let earth = RelativisticSource {
mu: MU_EARTH,
position: DVec3::new(1.496e11, 0.0, 0.0),
};
let with = compute_relativistic_correction(
MU_SUN,
sun_pos,
mercury_pos,
mercury_vel,
sun_vel,
&[earth],
);
let diff = (with - without).length();
assert!(
diff > 0.0,
"Earth should perturb the relativistic correction"
);
assert!(
diff < without.length() * 0.1,
"Earth's perturbation should be small compared to total correction"
);
}
}