nyx_space/cosmic/eclipse.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189
/*
Nyx, blazing fast astrodynamics
Copyright (C) 2018-onwards Christopher Rabotin <christopher.rabotin@gmail.com>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
use anise::almanac::Almanac;
use anise::astro::Occultation;
use anise::constants::frames::{EARTH_J2000, MOON_J2000, SUN_J2000};
use anise::errors::AlmanacResult;
use snafu::ResultExt;
pub use super::{Frame, Orbit, Spacecraft};
use crate::errors::{EventAlmanacSnafu, EventError};
use crate::md::EventEvaluator;
use crate::time::{Duration, Unit};
use std::fmt;
use std::sync::Arc;
#[derive(Clone)]
pub struct EclipseLocator {
pub light_source: Frame,
pub shadow_bodies: Vec<Frame>,
}
impl fmt::Display for EclipseLocator {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let shadow_bodies: Vec<String> = self
.shadow_bodies
.iter()
.map(|b| format!("{b:x}"))
.collect();
write!(
f,
"light-source: {:x}, shadows casted by: {}",
self.light_source,
shadow_bodies.join(", ")
)
}
}
impl EclipseLocator {
/// Creates a new typical eclipse locator.
/// The light source is the Sun, and the shadow bodies are the Earth and the Moon.
pub fn cislunar(almanac: Arc<Almanac>) -> Self {
let eme2k = almanac.frame_from_uid(EARTH_J2000).unwrap();
let moon_j2k = almanac.frame_from_uid(MOON_J2000).unwrap();
Self {
light_source: almanac.frame_from_uid(SUN_J2000).unwrap(),
shadow_bodies: vec![eme2k, moon_j2k],
}
}
/// Compute the visibility/eclipse between an observer and an observed state
pub fn compute(&self, observer: Orbit, almanac: Arc<Almanac>) -> AlmanacResult<Occultation> {
let mut state = Occultation {
epoch: observer.epoch,
back_frame: SUN_J2000,
front_frame: observer.frame,
percentage: 0.0,
};
for eclipsing_body in &self.shadow_bodies {
let this_state = almanac.solar_eclipsing(*eclipsing_body, observer, None)?;
if this_state.percentage > state.percentage {
state = this_state;
}
}
Ok(state)
}
/// Creates an umbra event from this eclipse locator.
/// Evaluation of the event, returns 0.0 for umbra, 1.0 for visibility (no shadow) and some value in between for penumbra
pub fn to_umbra_event(&self) -> UmbraEvent {
UmbraEvent {
e_loc: self.clone(),
}
}
/// Creates a penumbra event from this eclipse locator
// Evaluation of the event, returns 0.0 for umbra, 1.0 for visibility (no shadow) and some value in between for penumbra
pub fn to_penumbra_event(&self) -> PenumbraEvent {
PenumbraEvent {
e_loc: self.clone(),
}
}
}
/// An event to find the darkest eclipse state (more than 98% shadow)
pub struct UmbraEvent {
e_loc: EclipseLocator,
}
impl fmt::Display for UmbraEvent {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "umbra event {}", self.e_loc)
}
}
impl EventEvaluator<Spacecraft> for UmbraEvent {
// Evaluation of the event
fn eval(&self, sc: &Spacecraft, almanac: Arc<Almanac>) -> Result<f64, EventError> {
let occult = self
.e_loc
.compute(sc.orbit, almanac)
.context(EventAlmanacSnafu)?
.factor();
Ok((occult - 1.0).abs())
// match self
// .e_loc
// .compute(sc.orbit, almanac)
// .context(EventAlmanacSnafu)?
// {
// EclipseState::Umbra => Ok(0.0),
// EclipseState::Visibilis => Ok(1.0),
// EclipseState::Penumbra(val) => Ok(val),
// }
}
/// Stop searching when the time has converged to less than 0.1 seconds
fn epoch_precision(&self) -> Duration {
0.1 * Unit::Second
}
/// Finds the darkest part of an eclipse within 2% of penumbra (i.e. 98% in shadow)
fn value_precision(&self) -> f64 {
0.02
}
fn eval_string(&self, state: &Spacecraft, almanac: Arc<Almanac>) -> Result<String, EventError> {
Ok(format!(
"{}",
self.e_loc
.compute(state.orbit, almanac)
.context(EventAlmanacSnafu)?
))
}
}
/// An event to find the start of a penumbra
pub struct PenumbraEvent {
e_loc: EclipseLocator,
}
impl fmt::Display for PenumbraEvent {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "penumbra event {}", self.e_loc)
}
}
impl EventEvaluator<Spacecraft> for PenumbraEvent {
fn eval(&self, sc: &Spacecraft, almanac: Arc<Almanac>) -> Result<f64, EventError> {
let occult = self
.e_loc
.compute(sc.orbit, almanac)
.context(EventAlmanacSnafu)?
.factor();
Ok((occult - 1.0).abs())
}
/// Stop searching when the time has converged to less than 0.1 seconds
fn epoch_precision(&self) -> Duration {
0.1 * Unit::Second
}
/// Finds the slightest penumbra within 2% (i.e. 98% in visibility)
fn value_precision(&self) -> f64 {
0.02
}
fn eval_string(&self, state: &Spacecraft, almanac: Arc<Almanac>) -> Result<String, EventError> {
Ok(format!(
"{}",
self.e_loc
.compute(state.orbit, almanac)
.context(EventAlmanacSnafu)?
))
}
}