Skip to main content

nyx_space/md/
objective.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 super::StateParameter;
20use crate::{errors::StateError, Spacecraft, State};
21use serde::{Deserialize, Serialize};
22use serde_dhall::StaticType;
23use std::fmt;
24
25/// Defines a state parameter event finder
26#[derive(Copy, Clone, Debug, Serialize, Deserialize, StaticType)]
27pub struct Objective {
28    /// The state parameter to target
29    pub parameter: StateParameter,
30    /// The desired self.desired_value, must be in the same units as the state parameter
31    pub desired_value: f64,
32    /// The precision on the desired value
33    pub tolerance: f64,
34    /// A multiplicative factor this parameter's error in the targeting (defaults to 1.0)
35    pub multiplicative_factor: f64,
36    /// An additive factor to this parameters's error in the targeting (defaults to 0.0)
37    pub additive_factor: f64,
38}
39
40impl Objective {
41    /// Match a specific value for the parameter.
42    /// By default, the tolerance on the parameter is 0.1 times whatever unit is the default for that parameter.
43    /// 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.
44    pub fn new(parameter: StateParameter, desired_value: f64) -> Self {
45        Self::within_tolerance(
46            parameter,
47            desired_value,
48            parameter.default_event_precision(),
49        )
50    }
51
52    /// Match a specific value for the parameter to hit the specified value with the provided tolerance on the value
53    pub fn within_tolerance(parameter: StateParameter, desired_value: f64, tolerance: f64) -> Self {
54        Self {
55            parameter,
56            desired_value,
57            tolerance,
58            multiplicative_factor: 1.0,
59            additive_factor: 0.0,
60        }
61    }
62
63    /// Returns whether this objective has been achieved, and the associated parameter error.
64    pub fn assess(&self, achieved: &Spacecraft) -> Result<(bool, f64), StateError> {
65        Ok(self.assess_value(achieved.value(self.parameter)?))
66    }
67
68    /// Returns whether this objective has been achieved, and the associated parameter error.
69    /// Warning: the parameter `achieved` must be in the same unit as the objective.
70    pub fn assess_value(&self, achieved: f64) -> (bool, f64) {
71        let param_err =
72            self.multiplicative_factor * (self.desired_value - achieved) + self.additive_factor;
73
74        (param_err.abs() <= self.tolerance, param_err)
75    }
76}
77
78impl fmt::Display for Objective {
79    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
80        write!(f, "\t{self:x}")
81    }
82}
83
84impl fmt::LowerHex for Objective {
85    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
86        let max_obj_tol = self.tolerance.log10().abs().ceil() as usize;
87
88        write!(
89            f,
90            "{:?} → {:.prec$} {}",
91            self.parameter,
92            self.desired_value,
93            self.parameter.unit(),
94            prec = max_obj_tol,
95        )?;
96
97        if self.tolerance.abs() < 1e-1 {
98            write!(f, " (± {:.1e})", self.tolerance)
99        } else {
100            write!(f, " (± {:.2})", self.tolerance)
101        }
102    }
103}