nyx_space/md/objective.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
/*
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 super::StateParameter;
use crate::{errors::StateError, Spacecraft, State};
use std::fmt;
/// Defines a state parameter event finder
#[derive(Copy, Clone, Debug)]
pub struct Objective {
/// The state parameter to target
pub parameter: StateParameter,
/// The desired self.desired_value, must be in the same units as the state parameter
pub desired_value: f64,
/// The precision on the desired value
pub tolerance: f64,
/// A multiplicative factor this parameter's error in the targeting (defaults to 1.0)
pub multiplicative_factor: f64,
/// An additive factor to this parameters's error in the targeting (defaults to 0.0)
pub additive_factor: f64,
}
impl Objective {
/// Match a specific value for the parameter.
/// By default, the tolerance on the parameter is 0.1 times whatever unit is the default for that parameter.
/// For example, a radius event will seek the requested value at the decimeter level, and an angle event will seek it at the tenth of a degree.
pub fn new(parameter: StateParameter, desired_value: f64) -> Self {
Self::within_tolerance(
parameter,
desired_value,
parameter.default_event_precision(),
)
}
/// Match a specific value for the parameter to hit the specified value with the provided tolerance on the value
pub fn within_tolerance(parameter: StateParameter, desired_value: f64, tolerance: f64) -> Self {
Self {
parameter,
desired_value,
tolerance,
multiplicative_factor: 1.0,
additive_factor: 0.0,
}
}
/// Returns whether this objective has been achieved, and the associated parameter error.
pub fn assess(&self, achieved: &Spacecraft) -> Result<(bool, f64), StateError> {
Ok(self.assess_value(achieved.value(self.parameter)?))
}
/// Returns whether this objective has been achieved, and the associated parameter error.
/// Warning: the parameter `achieved` must be in the same unit as the objective.
pub fn assess_value(&self, achieved: f64) -> (bool, f64) {
let param_err =
self.multiplicative_factor * (self.desired_value - achieved) + self.additive_factor;
(param_err.abs() <= self.tolerance, param_err)
}
}
impl fmt::Display for Objective {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
write!(f, "\t{self:x}")
}
}
impl fmt::LowerHex for Objective {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
let max_obj_tol = self.tolerance.log10().abs().ceil() as usize;
write!(
f,
"{:?} → {:.prec$} {}",
self.parameter,
self.desired_value,
self.parameter.unit(),
prec = max_obj_tol,
)?;
if self.tolerance.abs() < 1e-1 {
write!(f, " (± {:.1e})", self.tolerance)
} else {
write!(f, " (± {:.2})", self.tolerance)
}
}
}