use crate::types::BrightDateValue;
pub const LIGHT_SECONDS_PER_AU: f64 = 499.004_784;
pub const LIGHT_DAYS_PER_AU: f64 = LIGHT_SECONDS_PER_AU / 86_400.0;
pub const MARS_SOL_IN_EARTH_DAYS: f64 = 1.027_491_25;
const MSD_JD_REF: f64 = 2_405_522.002_877_9;
const J2000_JD: f64 = 2_451_545.0;
#[derive(Debug, Clone, Copy)]
pub struct SolarSystemBody {
pub name: &'static str,
pub semi_major_axis_au: f64,
pub orbital_period_days: f64,
}
pub const SOLAR_SYSTEM_BODIES: &[SolarSystemBody] = &[
SolarSystemBody { name: "Mercury", semi_major_axis_au: 0.387, orbital_period_days: 87.97 },
SolarSystemBody { name: "Venus", semi_major_axis_au: 0.723, orbital_period_days: 224.7 },
SolarSystemBody { name: "Earth", semi_major_axis_au: 1.0, orbital_period_days: 365.25 },
SolarSystemBody { name: "Mars", semi_major_axis_au: 1.524, orbital_period_days: 687.0 },
SolarSystemBody { name: "Jupiter", semi_major_axis_au: 5.203, orbital_period_days: 4_332.59 },
SolarSystemBody { name: "Saturn", semi_major_axis_au: 9.537, orbital_period_days: 10_759.22 },
SolarSystemBody { name: "Uranus", semi_major_axis_au: 19.191, orbital_period_days: 30_688.5 },
SolarSystemBody { name: "Neptune", semi_major_axis_au: 30.069, orbital_period_days: 60_182.0 },
SolarSystemBody { name: "Moon", semi_major_axis_au: 0.002_57, orbital_period_days: 27.32 },
];
pub fn find_body(name: &str) -> Option<&'static SolarSystemBody> {
let lower = name.to_lowercase();
SOLAR_SYSTEM_BODIES
.iter()
.find(|b| b.name.to_lowercase() == lower)
}
pub fn light_travel_time(distance_au: f64) -> f64 {
distance_au * LIGHT_DAYS_PER_AU
}
pub fn light_delay_to(body: &SolarSystemBody) -> f64 {
light_travel_time(body.semi_major_axis_au)
}
pub fn round_trip_delay(body: &SolarSystemBody) -> f64 {
light_delay_to(body) * 2.0
}
pub fn signal_arrival_time(body: &SolarSystemBody, send_time: BrightDateValue) -> BrightDateValue {
send_time + light_delay_to(body)
}
pub fn signal_send_time(body: &SolarSystemBody, receive_time: BrightDateValue) -> BrightDateValue {
receive_time - light_delay_to(body)
}
pub fn earth_days_to_mars_sols(earth_days: f64) -> f64 {
earth_days / MARS_SOL_IN_EARTH_DAYS
}
pub fn mars_sols_to_earth_days(sols: f64) -> f64 {
sols * MARS_SOL_IN_EARTH_DAYS
}
pub fn to_mars_sol_date(bright_date: BrightDateValue) -> f64 {
let jd = bright_date + J2000_JD;
(jd - MSD_JD_REF) / MARS_SOL_IN_EARTH_DAYS
}
pub fn from_mars_sol_date(msd: f64) -> BrightDateValue {
let jd = msd * MARS_SOL_IN_EARTH_DAYS + MSD_JD_REF;
jd - J2000_JD
}
pub fn coordinated_mars_time(bright_date: BrightDateValue) -> f64 {
let msd = to_mars_sol_date(bright_date);
((msd % 1.0) + 1.0) % 1.0
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn light_travel_earth_au() {
let t = light_travel_time(1.0);
assert!((t - LIGHT_SECONDS_PER_AU / 86_400.0).abs() < 1e-10);
}
#[test]
fn mars_sol_roundtrip() {
let bd = 9000.0_f64;
let msd = to_mars_sol_date(bd);
let back = from_mars_sol_date(msd);
assert!((back - bd).abs() < 1e-8);
}
#[test]
fn mtc_in_range() {
let mtc = coordinated_mars_time(9622.5);
assert!((0.0..1.0).contains(&mtc));
}
#[test]
fn find_body_case_insensitive() {
let body = find_body("mars").unwrap();
assert_eq!(body.name, "Mars");
}
#[test]
fn earth_mars_sols_roundtrip() {
let d = 100.0_f64;
assert!((mars_sols_to_earth_days(earth_days_to_mars_sols(d)) - d).abs() < 1e-10);
}
}