nyx_space/cosmic/
eclipse.rs1use anise::almanac::Almanac;
20use anise::astro::Occultation;
21use anise::constants::frames::{EARTH_J2000, MOON_J2000, SUN_J2000};
22use anise::errors::AlmanacResult;
23use snafu::ResultExt;
24
25pub use super::{Frame, Orbit, Spacecraft};
26use crate::errors::{EventAlmanacSnafu, EventError};
27use crate::md::EventEvaluator;
28use crate::time::{Duration, Unit};
29use std::fmt;
30use std::sync::Arc;
31
32#[derive(Clone)]
33pub struct EclipseLocator {
34 pub light_source: Frame,
35 pub shadow_bodies: Vec<Frame>,
36}
37
38impl fmt::Display for EclipseLocator {
39 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
40 let shadow_bodies: Vec<String> = self
41 .shadow_bodies
42 .iter()
43 .map(|b| format!("{b:x}"))
44 .collect();
45 write!(
46 f,
47 "light-source: {:x}, shadows casted by: {}",
48 self.light_source,
49 shadow_bodies.join(", ")
50 )
51 }
52}
53
54impl EclipseLocator {
55 pub fn cislunar(almanac: Arc<Almanac>) -> Self {
58 let eme2k = almanac.frame_from_uid(EARTH_J2000).unwrap();
59 let moon_j2k = almanac.frame_from_uid(MOON_J2000).unwrap();
60 Self {
61 light_source: almanac.frame_from_uid(SUN_J2000).unwrap(),
62 shadow_bodies: vec![eme2k, moon_j2k],
63 }
64 }
65
66 pub fn compute(&self, observer: Orbit, almanac: Arc<Almanac>) -> AlmanacResult<Occultation> {
68 let mut state = Occultation {
69 epoch: observer.epoch,
70 back_frame: SUN_J2000,
71 front_frame: observer.frame,
72 percentage: 0.0,
73 };
74 for eclipsing_body in &self.shadow_bodies {
75 let this_state = almanac.solar_eclipsing(*eclipsing_body, observer, None)?;
76 if this_state.percentage > state.percentage {
77 state = this_state;
78 }
79 }
80 Ok(state)
81 }
82
83 pub fn to_umbra_event(&self) -> UmbraEvent {
86 UmbraEvent {
87 e_loc: self.clone(),
88 }
89 }
90
91 pub fn to_penumbra_event(&self) -> PenumbraEvent {
94 PenumbraEvent {
95 e_loc: self.clone(),
96 }
97 }
98}
99
100pub struct UmbraEvent {
102 e_loc: EclipseLocator,
103}
104
105impl fmt::Display for UmbraEvent {
106 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
107 write!(f, "umbra event {}", self.e_loc)
108 }
109}
110
111impl EventEvaluator<Spacecraft> for UmbraEvent {
112 fn eval(&self, sc: &Spacecraft, almanac: Arc<Almanac>) -> Result<f64, EventError> {
114 let occult = self
115 .e_loc
116 .compute(sc.orbit, almanac)
117 .context(EventAlmanacSnafu)?
118 .factor();
119
120 Ok((occult - 1.0).abs())
121 }
131
132 fn epoch_precision(&self) -> Duration {
134 0.1 * Unit::Second
135 }
136 fn value_precision(&self) -> f64 {
138 0.02
139 }
140 fn eval_string(&self, state: &Spacecraft, almanac: Arc<Almanac>) -> Result<String, EventError> {
141 Ok(format!(
142 "{}",
143 self.e_loc
144 .compute(state.orbit, almanac)
145 .context(EventAlmanacSnafu)?
146 ))
147 }
148}
149
150pub struct PenumbraEvent {
152 e_loc: EclipseLocator,
153}
154
155impl fmt::Display for PenumbraEvent {
156 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
157 write!(f, "penumbra event {}", self.e_loc)
158 }
159}
160
161impl EventEvaluator<Spacecraft> for PenumbraEvent {
162 fn eval(&self, sc: &Spacecraft, almanac: Arc<Almanac>) -> Result<f64, EventError> {
163 let occult = self
164 .e_loc
165 .compute(sc.orbit, almanac)
166 .context(EventAlmanacSnafu)?
167 .factor();
168
169 Ok((occult - 1.0).abs())
170 }
171
172 fn epoch_precision(&self) -> Duration {
174 0.1 * Unit::Second
175 }
176 fn value_precision(&self) -> f64 {
178 0.02
179 }
180
181 fn eval_string(&self, state: &Spacecraft, almanac: Arc<Almanac>) -> Result<String, EventError> {
182 Ok(format!(
183 "{}",
184 self.e_loc
185 .compute(state.orbit, almanac)
186 .context(EventAlmanacSnafu)?
187 ))
188 }
189}