nyx_space/md/opti/
target_variable.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 crate::cosmic::Frame;
20use crate::errors::TargetingError;
21use std::default::Default;
22use std::f64::consts::{FRAC_PI_2, FRAC_PI_8, PI};
23use std::fmt;
24
25/// Defines the kind of correction to apply in the targeter
26#[derive(Copy, Clone, Debug, PartialEq, Eq)]
27pub enum Vary {
28    /// Vary position component X
29    PositionX,
30    /// Vary position component Y
31    PositionY,
32    /// Vary position component Z
33    PositionZ,
34    /// Vary velocity component X
35    VelocityX,
36    /// Vary velocity component Y
37    VelocityY,
38    /// Vary velocity component Z
39    VelocityZ,
40    /// Maneuver's in-plane alpha
41    MnvrAlpha,
42    /// Maneuver's in-plane alpha-dot
43    MnvrAlphaDot,
44    /// Maneuver's in-plane alpha double-dot
45    MnvrAlphaDDot,
46    /// Maneuver's out-of-plane beta
47    MnvrDelta,
48    /// Maneuver's out-of-plane beta dot
49    MnvrDeltaDot,
50    /// Maneuver's out-of-plane beta double-dot
51    MnvrDeltaDDot,
52    /// Start epoch difference in seconds
53    StartEpoch,
54    /// Burn duration difference in seconds
55    Duration,
56    /// End epoch difference in seconds
57    EndEpoch,
58    /// Thrust direction in X
59    ThrustX,
60    /// Thrust direction in Y
61    ThrustY,
62    /// Thrust direction in Z
63    ThrustZ,
64    /// Thrust level during the burn.
65    ThrustLevel,
66    /// Thrust direction rate in X
67    ThrustRateX,
68    /// Thrust direction rate in Y
69    ThrustRateY,
70    /// Thrust direction rate in Z
71    ThrustRateZ,
72    /// Thrust direction acceleration in X
73    ThrustAccelX,
74    /// Thrust direction acceleration in Y
75    ThrustAccelY,
76    /// Thrust direction acceleration in Z
77    ThrustAccelZ,
78}
79
80impl Vary {
81    #[allow(clippy::nonminimal_bool)]
82    pub fn is_finite_burn(&self) -> bool {
83        *self == Self::MnvrAlpha
84            || *self == Self::MnvrAlphaDDot
85            || *self == Self::MnvrAlphaDDot
86            || *self == Self::MnvrDelta
87            || *self == Self::MnvrDeltaDDot
88            || *self == Self::MnvrDeltaDDot
89            || *self == Self::StartEpoch
90            || *self == Self::Duration
91            || *self == Self::EndEpoch
92            || *self == Self::ThrustX
93            || *self == Self::ThrustY
94            || *self == Self::ThrustZ
95            || *self == Self::ThrustLevel
96            || *self == Self::ThrustRateX
97            || *self == Self::ThrustRateY
98            || *self == Self::ThrustRateZ
99            || *self == Self::ThrustAccelX
100            || *self == Self::ThrustAccelY
101            || *self == Self::ThrustAccelZ
102    }
103
104    #[allow(clippy::nonminimal_bool)]
105    pub fn vec_index(&self) -> usize {
106        match self {
107            Self::PositionX | Self::ThrustX | Self::MnvrAlphaDDot | Self::MnvrDeltaDDot => 0,
108            Self::PositionY | Self::ThrustY | Self::MnvrAlphaDot | Self::MnvrDeltaDot => 1,
109            Self::PositionZ | Self::ThrustZ | Self::MnvrAlpha | Self::MnvrDelta => 2,
110            Self::VelocityX | Self::ThrustRateX => 3,
111            Self::VelocityY | Self::ThrustRateY => 4,
112            Self::VelocityZ | Self::ThrustRateZ => 5,
113            Self::StartEpoch | Self::ThrustAccelX => 6,
114            Self::Duration | Self::EndEpoch | Self::ThrustAccelY => 7,
115            Self::ThrustAccelZ => 8,
116            _ => unreachable!(),
117        }
118    }
119}
120
121#[derive(Copy, Clone, Debug, PartialEq)]
122pub struct Variable {
123    /// The component that will be varied in the targeter
124    pub component: Vary,
125    /// The perturbation for the finite differencing algorithm
126    pub perturbation: f64,
127    /// The initial guess of this variable
128    pub init_guess: f64,
129    /// The maximum step this variable may have between each iteration
130    pub max_step: f64,
131    /// The absolute maximum value this parameter can ever have
132    pub max_value: f64,
133    /// The absolute minimum value this parameter can ever have
134    pub min_value: f64,
135    /// The frame in which this variable should be applied, must be either a local frame or inertial
136    pub frame: Option<Frame>,
137}
138
139impl Variable {
140    /// Returns whether the configuration of this variable is valid
141    #[allow(clippy::result_large_err)]
142    pub fn valid(&self) -> Result<(), TargetingError> {
143        if self.max_step < 0.0 {
144            let msg = format!(
145                "{:?}: max step is negative: {}",
146                self.component, self.max_step
147            );
148            error!("{}", msg);
149            return Err(TargetingError::VariableError { msg });
150        }
151        if self.max_value < 0.0 {
152            let msg = format!(
153                "{:?}: max value is negative: {}",
154                self.component, self.max_value
155            );
156            error!("{}", msg);
157            return Err(TargetingError::VariableError { msg });
158        }
159        if self.min_value > self.max_value {
160            let msg = format!(
161                "{:?}: min value is greater than max value: {} > {}",
162                self.component, self.min_value, self.max_value
163            );
164            error!("{}", msg);
165            return Err(TargetingError::VariableError { msg });
166        }
167        Ok(())
168    }
169
170    pub fn with_initial_guess(mut self, guess: f64) -> Self {
171        self.init_guess = guess;
172        self
173    }
174
175    pub fn with_pert(mut self, pert: f64) -> Self {
176        self.perturbation = pert;
177        self
178    }
179
180    pub fn with_min(mut self, min_val: f64) -> Self {
181        self.min_value = min_val;
182        self
183    }
184
185    pub fn with_max(mut self, max_val: f64) -> Self {
186        self.max_value = max_val;
187        self
188    }
189
190    /// Ensure that `val` is within the variable bounds
191    pub fn apply_bounds(&self, val: f64) -> f64 {
192        if val > self.max_value {
193            self.max_value
194        } else if val < self.min_value {
195            self.min_value
196        } else {
197            val
198        }
199    }
200
201    /// Ensure that `val` is within the variable bounds
202    pub fn ensure_bounds(&self, val: &mut f64) {
203        *val = self.check_bounds(*val).0;
204    }
205
206    /// Returns the input value unless it is out of bounds, then it returns the bound, and whether the input value was OK
207    pub fn check_bounds(&self, val: f64) -> (f64, bool) {
208        if val > self.max_value {
209            (self.max_value, false)
210        } else if val < self.min_value {
211            (self.min_value, false)
212        } else {
213            (val, true)
214        }
215    }
216}
217
218impl Default for Variable {
219    fn default() -> Self {
220        Self {
221            component: Vary::VelocityX,
222            perturbation: 0.0001,
223            init_guess: 0.0,
224            max_step: 0.2,
225            max_value: 5.0,
226            min_value: -5.0,
227            frame: None,
228        }
229    }
230}
231
232impl From<Vary> for Variable {
233    fn from(vary: Vary) -> Self {
234        match vary {
235            Vary::PositionX
236            | Vary::PositionY
237            | Vary::PositionZ
238            | Vary::VelocityX
239            | Vary::VelocityY
240            | Vary::VelocityZ => Self {
241                component: vary,
242                ..Default::default()
243            },
244            Vary::MnvrAlpha | Vary::MnvrAlphaDot | Vary::MnvrAlphaDDot => Self {
245                component: vary,
246                perturbation: 0.1 * PI,
247                max_step: FRAC_PI_8,
248                max_value: PI,
249                min_value: 0.0,
250                ..Default::default()
251            },
252            Vary::MnvrDelta | Vary::MnvrDeltaDot | Vary::MnvrDeltaDDot => Self {
253                component: vary,
254                perturbation: 0.1 * PI,
255                max_step: FRAC_PI_8,
256                max_value: FRAC_PI_2,
257                min_value: -FRAC_PI_2,
258                ..Default::default()
259            },
260            Vary::StartEpoch | Vary::EndEpoch => Self {
261                component: vary,
262                perturbation: 0.5,
263                max_step: 60.0,
264                max_value: 600.0,
265                min_value: -600.0,
266                ..Default::default()
267            },
268            Vary::Duration => Self {
269                component: vary,
270                perturbation: 1.0,
271                max_step: 60.0,
272                max_value: 600.0,
273                min_value: 0.0,
274                ..Default::default()
275            },
276            Vary::ThrustX | Vary::ThrustY | Vary::ThrustZ => Self {
277                component: vary,
278                max_value: 1.0,
279                min_value: -1.0,
280                ..Default::default()
281            },
282            Vary::ThrustRateX | Vary::ThrustRateY | Vary::ThrustRateZ => Self {
283                component: vary,
284                perturbation: 1e-10,
285                max_value: 1.0,
286                min_value: -1.0,
287                ..Default::default()
288            },
289            Vary::ThrustAccelX | Vary::ThrustAccelY | Vary::ThrustAccelZ => Self {
290                component: vary,
291                perturbation: 1e-15,
292                max_value: 1.0,
293                min_value: -1.0,
294                ..Default::default()
295            },
296            Vary::ThrustLevel => Self {
297                component: vary,
298                perturbation: -0.0001, // Perturb the thrust by -1%
299                min_value: 0.0001,
300                max_value: 1.0,
301                init_guess: 1.0,
302                ..Default::default()
303            },
304        }
305    }
306}
307
308impl fmt::Display for Variable {
309    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
310        write!(
311            f,
312            "{}{:?} = {} ± {:} ∈ [{}; {}]",
313            match self.frame {
314                Some(f) => format!("{f}"),
315                None => "".to_string(),
316            },
317            self.component,
318            self.init_guess,
319            self.perturbation,
320            self.min_value,
321            self.max_value
322        )
323    }
324}