use crate::constants::{astro::SOLAR_RADIUS_KM, earth::MEAN_EARTH_RADIUS_KM};
use crate::math::vec3;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum EclipseStatus {
Sunlit,
Penumbra,
Umbra,
}
pub fn shadow_fraction(sat_pos: [f64; 3], sun_pos: [f64; 3]) -> f64 {
let d_sun = vec3::norm3(sun_pos);
let sun_u = [sun_pos[0] / d_sun, sun_pos[1] / d_sun, sun_pos[2] / d_sun];
let proj = vec3::dot3(sat_pos, sun_u);
if proj >= 0.0 {
return 0.0;
}
let perp = vec3::sub3(sat_pos, vec3::scale3(sun_u, proj));
let rho = vec3::norm3(perp);
let dist_behind = -proj;
let alpha_umbra = ((SOLAR_RADIUS_KM - MEAN_EARTH_RADIUS_KM) / d_sun).asin();
let alpha_penumbra = ((SOLAR_RADIUS_KM + MEAN_EARTH_RADIUS_KM) / d_sun).asin();
let r_umbra = MEAN_EARTH_RADIUS_KM - dist_behind * alpha_umbra.tan();
let r_penumbra = MEAN_EARTH_RADIUS_KM + dist_behind * alpha_penumbra.tan();
if rho >= r_penumbra {
0.0
} else if r_umbra > 0.0 && rho <= r_umbra {
1.0
} else if r_umbra > 0.0 {
(r_penumbra - rho) / (r_penumbra - r_umbra)
} else {
0.0_f64.max((r_penumbra - rho) / r_penumbra)
}
}
pub fn status(sat_pos: [f64; 3], sun_pos: [f64; 3]) -> EclipseStatus {
let fraction = shadow_fraction(sat_pos, sun_pos);
if fraction >= 1.0 {
EclipseStatus::Umbra
} else if fraction > 0.0 {
EclipseStatus::Penumbra
} else {
EclipseStatus::Sunlit
}
}
#[cfg(test)]
mod tests {
use super::*;
const AU_KM: f64 = 149_597_870.7;
#[test]
fn shadow_fraction_matches_reference_bits() {
let sun = [AU_KM, 0.0, 0.0];
assert_eq!(
shadow_fraction([7000.0, 0.0, 0.0], sun).to_bits(),
0.0_f64.to_bits()
);
assert_eq!(
shadow_fraction([0.0, 7000.0, 0.0], sun).to_bits(),
0.0_f64.to_bits()
);
assert_eq!(
shadow_fraction([-7000.0, 0.0, 0.0], sun).to_bits(),
1.0_f64.to_bits()
);
assert_eq!(
shadow_fraction([-7000.0, 6370.0, 0.0], sun).to_bits(),
0x3fe0_a32f_08e7_fb1f
);
assert_eq!(
shadow_fraction([-7000.0, 6410.0, 0.0], sun).to_bits(),
0.0_f64.to_bits()
);
assert_eq!(
shadow_fraction([-7000.0, 6330.0, 0.0], sun).to_bits(),
1.0_f64.to_bits()
);
}
#[test]
fn status_classifies_cones() {
let sun = [AU_KM, 0.0, 0.0];
assert_eq!(status([7000.0, 0.0, 0.0], sun), EclipseStatus::Sunlit);
assert_eq!(status([0.0, 7000.0, 0.0], sun), EclipseStatus::Sunlit);
assert_eq!(status([-7000.0, 0.0, 0.0], sun), EclipseStatus::Umbra);
assert_eq!(status([-7000.0, 6370.0, 0.0], sun), EclipseStatus::Penumbra);
}
#[test]
fn shadow_fraction_increases_toward_axis() {
let sun = [AU_KM, 0.0, 0.0];
let outside = shadow_fraction([-7000.0, 6410.0, 0.0], sun);
let penumbra = shadow_fraction([-7000.0, 6370.0, 0.0], sun);
let umbra = shadow_fraction([-7000.0, 6330.0, 0.0], sun);
let center = shadow_fraction([-7000.0, 0.0, 0.0], sun);
assert_eq!(outside, 0.0);
assert!(penumbra > 0.0 && penumbra < 1.0);
assert_eq!(umbra, 1.0);
assert_eq!(center, 1.0);
}
}