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