use anise::almanac::Almanac;
use anise::analysis::prelude::Event;
use anise::astro::Occultation;
use anise::constants::frames::{EARTH_J2000, MOON_J2000, SUN_J2000};
use anise::errors::AlmanacResult;
use serde::{Deserialize, Serialize};
use serde_dhall::StaticType;
pub use super::{Frame, Orbit, Spacecraft};
use std::fmt;
#[cfg(feature = "python")]
use pyo3::prelude::*;
#[cfg_attr(feature = "python", pyclass(from_py_object, get_all, set_all))]
#[derive(Clone, Debug, Serialize, Deserialize, StaticType)]
pub struct ShadowModel {
pub light_source: Frame,
pub shadow_bodies: Vec<Frame>,
}
impl fmt::Display for ShadowModel {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let shadow_bodies: Vec<String> = self
.shadow_bodies
.iter()
.map(|b| format!("{b:x}"))
.collect();
write!(
f,
"light-source: {:x}, shadows casted by: {}",
self.light_source,
shadow_bodies.join(", ")
)
}
}
impl ShadowModel {
pub fn cislunar(almanac: &Almanac) -> Self {
let eme2k = almanac.frame_info(EARTH_J2000).unwrap();
let moon_j2k = almanac.frame_info(MOON_J2000).unwrap();
Self {
light_source: almanac.frame_info(SUN_J2000).unwrap(),
shadow_bodies: vec![eme2k, moon_j2k],
}
}
pub fn compute(&self, observer: Orbit, almanac: &Almanac) -> AlmanacResult<Occultation> {
let mut state = Occultation {
epoch: observer.epoch,
back_frame: SUN_J2000,
front_frame: observer.frame,
percentage: 0.0,
};
for eclipsing_body in &self.shadow_bodies {
let this_state = almanac.solar_eclipsing(*eclipsing_body, observer, None)?;
if this_state.percentage > state.percentage {
state = this_state;
}
}
Ok(state)
}
pub fn to_umbra_events(&self) -> Vec<Event> {
self.shadow_bodies
.iter()
.copied()
.map(Event::total_eclipse)
.collect()
}
pub fn to_penumbra_events(&self) -> Vec<Event> {
self.shadow_bodies
.iter()
.copied()
.map(Event::eclipse)
.collect()
}
}