Skip to main content

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;
24use serde::{Deserialize, Serialize};
25use serde_dhall::StaticType;
26
27pub use super::{Frame, Orbit, Spacecraft};
28use std::fmt;
29use std::sync::Arc;
30
31#[derive(Clone, Debug, Serialize, Deserialize, StaticType)]
32pub struct ShadowModel {
33    pub light_source: Frame,
34    pub shadow_bodies: Vec<Frame>,
35}
36
37impl fmt::Display for ShadowModel {
38    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
39        let shadow_bodies: Vec<String> = self
40            .shadow_bodies
41            .iter()
42            .map(|b| format!("{b:x}"))
43            .collect();
44        write!(
45            f,
46            "light-source: {:x}, shadows casted by: {}",
47            self.light_source,
48            shadow_bodies.join(", ")
49        )
50    }
51}
52
53impl ShadowModel {
54    /// Creates a new typical eclipse locator.
55    /// The light source is the Sun, and the shadow bodies are the Earth and the Moon.
56    pub fn cislunar(almanac: Arc<Almanac>) -> Self {
57        let eme2k = almanac.frame_info(EARTH_J2000).unwrap();
58        let moon_j2k = almanac.frame_info(MOON_J2000).unwrap();
59        Self {
60            light_source: almanac.frame_info(SUN_J2000).unwrap(),
61            shadow_bodies: vec![eme2k, moon_j2k],
62        }
63    }
64
65    /// Compute the visibility/eclipse between an observer and an observed state
66    pub fn compute(&self, observer: Orbit, almanac: Arc<Almanac>) -> AlmanacResult<Occultation> {
67        let mut state = Occultation {
68            epoch: observer.epoch,
69            back_frame: SUN_J2000,
70            front_frame: observer.frame,
71            percentage: 0.0,
72        };
73        for eclipsing_body in &self.shadow_bodies {
74            let this_state = almanac.solar_eclipsing(*eclipsing_body, observer, None)?;
75            if this_state.percentage > state.percentage {
76                state = this_state;
77            }
78        }
79        Ok(state)
80    }
81
82    /// Creates an umbra event from this eclipse locator.
83    /// Evaluation of the event, returns 0.0 for umbra, 1.0 for visibility (no shadow) and some value in between for penumbra
84    pub fn to_umbra_events(&self) -> Vec<Event> {
85        self.shadow_bodies
86            .iter()
87            .copied()
88            .map(Event::total_eclipse)
89            .collect()
90    }
91
92    /// Creates a penumbra event from this eclipse locator
93    // Evaluation of the event, returns 0.0 for umbra, 1.0 for visibility (no shadow) and some value in between for penumbra
94    pub fn to_penumbra_events(&self) -> Vec<Event> {
95        self.shadow_bodies
96            .iter()
97            .copied()
98            .map(Event::eclipse)
99            .collect()
100    }
101}