nyx_space/cosmic/
eclipse.rs1use anise::almanac::Almanac;
20use anise::analysis::prelude::Event;
21use anise::astro::Aberration;
22use anise::astro::Occultation;
23use anise::constants::frames::{EARTH_J2000, MOON_J2000, SUN_J2000};
24use anise::errors::AlmanacResult;
25use serde::{Deserialize, Serialize};
26use serde_dhall::StaticType;
27
28pub use super::{Frame, Orbit, Spacecraft};
29use std::fmt;
30
31#[cfg(feature = "python")]
32use pyo3::prelude::*;
33
34#[cfg_attr(feature = "python", pyclass(from_py_object, get_all, set_all))]
35#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, StaticType)]
36pub struct ShadowModel {
37 pub light_source: Frame,
38 pub shadow_bodies: Vec<Frame>,
39 pub correction: Option<Aberration>,
41}
42
43impl fmt::Display for ShadowModel {
44 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
45 let shadow_bodies: Vec<String> = self
46 .shadow_bodies
47 .iter()
48 .map(|b| format!("{b:x}"))
49 .collect();
50
51 write!(
52 f,
53 "light-source: {:x}, shadows casted by: {} (correction: {:?})",
54 self.light_source,
55 shadow_bodies.join(", "),
56 self.correction
57 )
58 }
59}
60
61impl ShadowModel {
62 pub fn cislunar(almanac: &Almanac) -> Self {
65 let eme2k = almanac.frame_info(EARTH_J2000).unwrap();
66 let moon_j2k = almanac.frame_info(MOON_J2000).unwrap();
67 Self {
68 light_source: almanac.frame_info(SUN_J2000).unwrap(),
69 shadow_bodies: vec![eme2k, moon_j2k],
70 correction: None,
71 }
72 }
73
74 pub fn compute(&self, observer: Orbit, almanac: &Almanac) -> AlmanacResult<Occultation> {
76 let mut state = Occultation {
77 epoch: observer.epoch,
78 back_frame: SUN_J2000,
79 front_frame: observer.frame,
80 percentage: 0.0,
81 };
82 for eclipsing_body in &self.shadow_bodies {
83 let this_state = almanac.solar_eclipsing(*eclipsing_body, observer, self.correction)?;
84 if this_state.percentage > state.percentage {
85 state = this_state;
86 }
87 }
88 Ok(state)
89 }
90
91 pub fn to_umbra_events(&self) -> Vec<Event> {
94 self.shadow_bodies
95 .iter()
96 .copied()
97 .map(Event::total_eclipse)
98 .collect()
99 }
100
101 pub fn to_penumbra_events(&self) -> Vec<Event> {
104 self.shadow_bodies
105 .iter()
106 .copied()
107 .map(Event::eclipse)
108 .collect()
109 }
110}
111
112#[cfg(test)]
113mod ut_shadow_mdl {
114 use super::{Aberration, EARTH_J2000, MOON_J2000, SUN_J2000, ShadowModel};
115 #[test]
116 fn ut_shadow_mdl_dhall_no_corr() {
117 let mdl_no_corr = ShadowModel {
118 light_source: SUN_J2000,
119 shadow_bodies: vec![EARTH_J2000, MOON_J2000],
120 correction: None,
121 };
122 let as_dhall = serde_dhall::serialize(&mdl_no_corr)
123 .static_type_annotation()
124 .to_string()
125 .unwrap();
126 println!("{as_dhall}");
127
128 let from_dhall: ShadowModel = serde_dhall::from_str(&as_dhall).parse().unwrap();
129 assert_eq!(from_dhall, mdl_no_corr);
130 }
131
132 #[test]
133 fn ut_shadow_mdl_dhall() {
134 let mdl_no_corr = ShadowModel {
135 light_source: SUN_J2000,
136 shadow_bodies: vec![EARTH_J2000, MOON_J2000],
137 correction: Aberration::LT,
138 };
139 let as_dhall = serde_dhall::serialize(&mdl_no_corr)
140 .static_type_annotation()
141 .to_string()
142 .unwrap();
143 println!("{as_dhall}");
144
145 let from_dhall: ShadowModel = serde_dhall::from_str(&as_dhall).parse().unwrap();
146 assert_eq!(from_dhall, mdl_no_corr);
147 }
148}