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}