const V_ORB_KM_S: f64 = 29.7859;
const OBLIQUITY_RAD: f64 = 23.4393 * (std::f64::consts::PI / 180.0);
const L0_DEG: f64 = 280.460;
const L_RATE_DEG: f64 = 0.9856474;
pub fn earth_barycentric_velocity(days_since_j2000: f64) -> [f64; 3] {
let lambda_deg = L0_DEG + L_RATE_DEG * days_since_j2000;
let lambda = lambda_deg.to_radians();
let sin_l = lambda.sin();
let cos_l = lambda.cos();
let cos_e = OBLIQUITY_RAD.cos();
let sin_e = OBLIQUITY_RAD.sin();
[
V_ORB_KM_S * sin_l,
-V_ORB_KM_S * cos_l * cos_e,
-V_ORB_KM_S * cos_l * sin_e,
]
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_speed_is_approximately_30_km_s() {
for d in [0.0, 91.0, 182.0, 273.0, 365.25] {
let v = earth_barycentric_velocity(d);
let speed = (v[0] * v[0] + v[1] * v[1] + v[2] * v[2]).sqrt();
assert!(
(speed - V_ORB_KM_S).abs() < 0.01,
"day {d}: speed {speed:.4} km/s, expected ~{V_ORB_KM_S}"
);
}
}
#[test]
fn test_vernal_equinox_direction() {
let d = (360.0 - L0_DEG) / L_RATE_DEG;
let v = earth_barycentric_velocity(d);
assert!(v[0].abs() < 1.0, "vx at equinox: {}", v[0]);
assert!(v[1] < -25.0, "vy at equinox should be << 0: {}", v[1]);
}
#[test]
fn test_half_year_reversal() {
let v1 = earth_barycentric_velocity(0.0);
let v2 = earth_barycentric_velocity(365.25 / 2.0);
let dot = v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2];
assert!(
dot < -800.0, "half-year dot product should be strongly negative: {dot}"
);
}
}