nyx_space/cosmic/eclipse.rs
1/*
2 Nyx, blazing fast astrodynamics
3 Copyright (C) 2018-onwards Christopher Rabotin <christopher.rabotin@gmail.com>
4
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <https://www.gnu.org/licenses/>.
17*/
18
19use anise::almanac::Almanac;
20use anise::analysis::prelude::Event;
21use anise::astro::Occultation;
22use anise::constants::frames::{EARTH_J2000, MOON_J2000, SUN_J2000};
23use anise::errors::AlmanacResult;
24
25pub use super::{Frame, Orbit, Spacecraft};
26use std::fmt;
27use std::sync::Arc;
28
29#[derive(Clone)]
30pub struct EclipseLocator {
31 pub light_source: Frame,
32 pub shadow_bodies: Vec<Frame>,
33}
34
35impl fmt::Display for EclipseLocator {
36 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
37 let shadow_bodies: Vec<String> = self
38 .shadow_bodies
39 .iter()
40 .map(|b| format!("{b:x}"))
41 .collect();
42 write!(
43 f,
44 "light-source: {:x}, shadows casted by: {}",
45 self.light_source,
46 shadow_bodies.join(", ")
47 )
48 }
49}
50
51impl EclipseLocator {
52 /// Creates a new typical eclipse locator.
53 /// The light source is the Sun, and the shadow bodies are the Earth and the Moon.
54 pub fn cislunar(almanac: Arc<Almanac>) -> Self {
55 let eme2k = almanac.frame_info(EARTH_J2000).unwrap();
56 let moon_j2k = almanac.frame_info(MOON_J2000).unwrap();
57 Self {
58 light_source: almanac.frame_info(SUN_J2000).unwrap(),
59 shadow_bodies: vec![eme2k, moon_j2k],
60 }
61 }
62
63 /// Compute the visibility/eclipse between an observer and an observed state
64 pub fn compute(&self, observer: Orbit, almanac: Arc<Almanac>) -> AlmanacResult<Occultation> {
65 let mut state = Occultation {
66 epoch: observer.epoch,
67 back_frame: SUN_J2000,
68 front_frame: observer.frame,
69 percentage: 0.0,
70 };
71 for eclipsing_body in &self.shadow_bodies {
72 let this_state = almanac.solar_eclipsing(*eclipsing_body, observer, None)?;
73 if this_state.percentage > state.percentage {
74 state = this_state;
75 }
76 }
77 Ok(state)
78 }
79
80 /// Creates an umbra event from this eclipse locator.
81 /// Evaluation of the event, returns 0.0 for umbra, 1.0 for visibility (no shadow) and some value in between for penumbra
82 pub fn to_umbra_events(&self) -> Vec<Event> {
83 self.shadow_bodies
84 .iter()
85 .copied()
86 .map(Event::total_eclipse)
87 .collect()
88 }
89
90 /// Creates a penumbra event from this eclipse locator
91 // Evaluation of the event, returns 0.0 for umbra, 1.0 for visibility (no shadow) and some value in between for penumbra
92 pub fn to_penumbra_events(&self) -> Vec<Event> {
93 self.shadow_bodies
94 .iter()
95 .copied()
96 .map(Event::eclipse)
97 .collect()
98 }
99}