///|
pub enum EclipseKind {
None
Penumbra
Umbra
} derive(Debug, Eq)
///|
pub struct EclipseResult {
kind : EclipseKind
occultation_fraction : Double
sun_angle_rad : Double
earth_angle_rad : Double
duration_estimate_s : Double
} derive(Debug, Eq)
///|
pub const SunRadiusKm : Double = 695700.0
///|
pub const SunEarthDistanceKm : Double = 149597870.7
///|
pub fn angular_radius(radius_km : Double, distance_km : Double) -> Double {
if radius_km <= 0.0 || distance_km <= 0.0 {
0.0
} else {
@math.asin(clamp(radius_km / distance_km, -1.0, 1.0))
}
}
///|
pub fn eclipse_geometry(
satellite_to_sun : Vec3,
satellite_to_earth : Vec3,
earth_radius_km : Double,
sun_radius_km : Double,
) -> EclipseResult {
let sun_distance = satellite_to_sun.norm()
let earth_distance = satellite_to_earth.norm()
if sun_distance == 0.0 ||
earth_distance == 0.0 ||
earth_radius_km <= 0.0 ||
sun_radius_km <= 0.0 {
return {
kind: None,
occultation_fraction: 0.0,
sun_angle_rad: 0.0,
earth_angle_rad: 0.0,
duration_estimate_s: 0.0,
}
}
let sun_angle = angular_radius(sun_radius_km, sun_distance)
let earth_angle = angular_radius(earth_radius_km, earth_distance)
let separation = satellite_to_sun.angle_between(satellite_to_earth)
let overlap = (sun_angle + earth_angle - separation).max(0.0)
let fraction = clamp(overlap / (2.0 * sun_angle.max(1.0e-15)), 0.0, 1.0)
let kind = if separation >= sun_angle + earth_angle {
None
} else if earth_angle > sun_angle && separation + sun_angle <= earth_angle {
Umbra
} else {
Penumbra
}
let chord = earth_distance * @math.sin(earth_angle.min(half_pi)) * 2.0
{
kind,
occultation_fraction: fraction,
sun_angle_rad: sun_angle,
earth_angle_rad: earth_angle,
duration_estimate_s: if kind is None {
0.0
} else {
chord / 7.5
},
}
}
///|
pub fn cylindrical_eclipse(
satellite_position : Vec3,
sun_direction : Vec3,
earth_radius_km : Double,
) -> EclipseResult {
let axis = sun_direction.unit()
let along = satellite_position.dot(axis)
let perpendicular = satellite_position.sub(axis.scale(along)).norm()
let in_shadow = along < 0.0 && perpendicular <= earth_radius_km
let fraction = if in_shadow { 1.0 } else { 0.0 }
{
kind: if in_shadow {
Umbra
} else {
None
},
occultation_fraction: fraction,
sun_angle_rad: 0.0,
earth_angle_rad: 0.0,
duration_estimate_s: 0.0,
}
}
///|
pub fn beta_angle(orbit_normal : Vec3, sun_direction : Vec3) -> Double {
half_pi - orbit_normal.unit().angle_between(sun_direction.unit())
}
///|
pub fn daylight_fraction(result : EclipseResult) -> Double {
1.0 - result.occultation_fraction
}
///|
pub fn eclipse_is_total(result : EclipseResult) -> Bool {
result.kind is Umbra
}
///|
pub fn eclipse_is_partial(result : EclipseResult) -> Bool {
result.kind is Penumbra
}
///|
pub fn sunlight_power_fraction(
result : EclipseResult,
albedo? : Double = 0.0,
) -> Double {
let direct = daylight_fraction(result)
(direct + albedo * (1.0 - direct)).min(1.0).max(0.0)
}
///|
pub fn solar_flux_at_distance(distance_au : Double) -> Double {
if distance_au <= 0.0 {
0.0
} else {
1361.0 / (distance_au * distance_au)
}
}
///|
pub fn solar_pressure_n_m2(distance_au : Double) -> Double {
solar_flux_at_distance(distance_au) / 299792458.0
}
///|
pub fn eclipse_margin_rad(result : EclipseResult) -> Double {
result.earth_angle_rad - result.sun_angle_rad - result.sun_angle_rad
}
///|
pub fn estimate_eclipse_duration(
orbit_radius_km : Double,
earth_radius_km : Double,
orbital_speed_km_s : Double,
) -> Double {
if orbit_radius_km <= earth_radius_km || orbital_speed_km_s <= 0.0 {
0.0
} else {
let half_chord = (orbit_radius_km * orbit_radius_km -
earth_radius_km * earth_radius_km).sqrt()
2.0 * half_chord / orbital_speed_km_s
}
}