1use crate::cosmic::Frame;
20use crate::errors::TargetingError;
21use log::error;
22use std::default::Default;
23use std::f64::consts::{FRAC_PI_2, FRAC_PI_8, PI};
24use std::fmt;
25
26#[derive(Copy, Clone, Debug, PartialEq, Eq)]
28pub enum Vary {
29 PositionX,
31 PositionY,
33 PositionZ,
35 VelocityX,
37 VelocityY,
39 VelocityZ,
41 MnvrAlpha,
43 MnvrAlphaDot,
45 MnvrAlphaDDot,
47 MnvrDelta,
49 MnvrDeltaDot,
51 MnvrDeltaDDot,
53 StartEpoch,
55 Duration,
57 EndEpoch,
59 ThrustX,
61 ThrustY,
63 ThrustZ,
65 ThrustLevel,
67 ThrustRateX,
69 ThrustRateY,
71 ThrustRateZ,
73 ThrustAccelX,
75 ThrustAccelY,
77 ThrustAccelZ,
79}
80
81impl Vary {
82 #[allow(clippy::nonminimal_bool)]
83 pub fn is_finite_burn(&self) -> bool {
84 *self == Self::MnvrAlpha
85 || *self == Self::MnvrAlphaDDot
86 || *self == Self::MnvrAlphaDDot
87 || *self == Self::MnvrDelta
88 || *self == Self::MnvrDeltaDDot
89 || *self == Self::MnvrDeltaDDot
90 || *self == Self::StartEpoch
91 || *self == Self::Duration
92 || *self == Self::EndEpoch
93 || *self == Self::ThrustX
94 || *self == Self::ThrustY
95 || *self == Self::ThrustZ
96 || *self == Self::ThrustLevel
97 || *self == Self::ThrustRateX
98 || *self == Self::ThrustRateY
99 || *self == Self::ThrustRateZ
100 || *self == Self::ThrustAccelX
101 || *self == Self::ThrustAccelY
102 || *self == Self::ThrustAccelZ
103 }
104
105 #[allow(clippy::nonminimal_bool)]
106 pub fn vec_index(&self) -> usize {
107 match self {
108 Self::PositionX | Self::ThrustX | Self::MnvrAlphaDDot | Self::MnvrDeltaDDot => 0,
109 Self::PositionY | Self::ThrustY | Self::MnvrAlphaDot | Self::MnvrDeltaDot => 1,
110 Self::PositionZ | Self::ThrustZ | Self::MnvrAlpha | Self::MnvrDelta => 2,
111 Self::VelocityX | Self::ThrustRateX => 3,
112 Self::VelocityY | Self::ThrustRateY => 4,
113 Self::VelocityZ | Self::ThrustRateZ => 5,
114 Self::StartEpoch | Self::ThrustAccelX => 6,
115 Self::Duration | Self::EndEpoch | Self::ThrustAccelY => 7,
116 Self::ThrustAccelZ => 8,
117 _ => unreachable!(),
118 }
119 }
120}
121
122#[derive(Copy, Clone, Debug, PartialEq)]
123pub struct Variable {
124 pub component: Vary,
126 pub perturbation: f64,
128 pub init_guess: f64,
130 pub max_step: f64,
132 pub max_value: f64,
134 pub min_value: f64,
136 pub frame: Option<Frame>,
138}
139
140impl Variable {
141 #[allow(clippy::result_large_err)]
143 pub fn valid(&self) -> Result<(), TargetingError> {
144 if self.max_step < 0.0 {
145 let msg = format!(
146 "{:?}: max step is negative: {}",
147 self.component, self.max_step
148 );
149 error!("{msg}");
150 return Err(TargetingError::VariableError { msg });
151 }
152 if self.max_value < 0.0 {
153 let msg = format!(
154 "{:?}: max value is negative: {}",
155 self.component, self.max_value
156 );
157 error!("{msg}");
158 return Err(TargetingError::VariableError { msg });
159 }
160 if self.min_value > self.max_value {
161 let msg = format!(
162 "{:?}: min value is greater than max value: {} > {}",
163 self.component, self.min_value, self.max_value
164 );
165 error!("{msg}");
166 return Err(TargetingError::VariableError { msg });
167 }
168 Ok(())
169 }
170
171 pub fn with_initial_guess(mut self, guess: f64) -> Self {
172 self.init_guess = guess;
173 self
174 }
175
176 pub fn with_pert(mut self, pert: f64) -> Self {
177 self.perturbation = pert;
178 self
179 }
180
181 pub fn with_min(mut self, min_val: f64) -> Self {
182 self.min_value = min_val;
183 self
184 }
185
186 pub fn with_max(mut self, max_val: f64) -> Self {
187 self.max_value = max_val;
188 self
189 }
190
191 pub fn apply_bounds(&self, val: f64) -> f64 {
193 if val > self.max_value {
194 self.max_value
195 } else if val < self.min_value {
196 self.min_value
197 } else {
198 val
199 }
200 }
201
202 pub fn ensure_bounds(&self, val: &mut f64) {
204 *val = self.check_bounds(*val).0;
205 }
206
207 pub fn check_bounds(&self, val: f64) -> (f64, bool) {
209 if val > self.max_value {
210 (self.max_value, false)
211 } else if val < self.min_value {
212 (self.min_value, false)
213 } else {
214 (val, true)
215 }
216 }
217}
218
219impl Default for Variable {
220 fn default() -> Self {
221 Self {
222 component: Vary::VelocityX,
223 perturbation: 0.0001,
224 init_guess: 0.0,
225 max_step: 0.2,
226 max_value: 5.0,
227 min_value: -5.0,
228 frame: None,
229 }
230 }
231}
232
233impl From<Vary> for Variable {
234 fn from(vary: Vary) -> Self {
235 match vary {
236 Vary::PositionX
237 | Vary::PositionY
238 | Vary::PositionZ
239 | Vary::VelocityX
240 | Vary::VelocityY
241 | Vary::VelocityZ => Self {
242 component: vary,
243 ..Default::default()
244 },
245 Vary::MnvrAlpha | Vary::MnvrAlphaDot | Vary::MnvrAlphaDDot => Self {
246 component: vary,
247 perturbation: 0.1 * PI,
248 max_step: FRAC_PI_8,
249 max_value: PI,
250 min_value: 0.0,
251 ..Default::default()
252 },
253 Vary::MnvrDelta | Vary::MnvrDeltaDot | Vary::MnvrDeltaDDot => Self {
254 component: vary,
255 perturbation: 0.1 * PI,
256 max_step: FRAC_PI_8,
257 max_value: FRAC_PI_2,
258 min_value: -FRAC_PI_2,
259 ..Default::default()
260 },
261 Vary::StartEpoch | Vary::EndEpoch => Self {
262 component: vary,
263 perturbation: 0.5,
264 max_step: 60.0,
265 max_value: 600.0,
266 min_value: -600.0,
267 ..Default::default()
268 },
269 Vary::Duration => Self {
270 component: vary,
271 perturbation: 1.0,
272 max_step: 60.0,
273 max_value: 600.0,
274 min_value: 0.0,
275 ..Default::default()
276 },
277 Vary::ThrustX | Vary::ThrustY | Vary::ThrustZ => Self {
278 component: vary,
279 max_value: 1.0,
280 min_value: -1.0,
281 ..Default::default()
282 },
283 Vary::ThrustRateX | Vary::ThrustRateY | Vary::ThrustRateZ => Self {
284 component: vary,
285 perturbation: 1e-10,
286 max_value: 1.0,
287 min_value: -1.0,
288 ..Default::default()
289 },
290 Vary::ThrustAccelX | Vary::ThrustAccelY | Vary::ThrustAccelZ => Self {
291 component: vary,
292 perturbation: 1e-15,
293 max_value: 1.0,
294 min_value: -1.0,
295 ..Default::default()
296 },
297 Vary::ThrustLevel => Self {
298 component: vary,
299 perturbation: -0.0001, min_value: 0.0001,
301 max_value: 1.0,
302 init_guess: 1.0,
303 ..Default::default()
304 },
305 }
306 }
307}
308
309impl fmt::Display for Variable {
310 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
311 write!(
312 f,
313 "{}{:?} = {} ± {:} ∈ [{}; {}]",
314 match self.frame {
315 Some(f) => format!("{f}"),
316 None => "".to_string(),
317 },
318 self.component,
319 self.init_guess,
320 self.perturbation,
321 self.min_value,
322 self.max_value
323 )
324 }
325}