1use 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#[derive(Copy, Clone, Debug, PartialEq, Eq)]
27pub enum Vary {
28 PositionX,
30 PositionY,
32 PositionZ,
34 VelocityX,
36 VelocityY,
38 VelocityZ,
40 MnvrAlpha,
42 MnvrAlphaDot,
44 MnvrAlphaDDot,
46 MnvrDelta,
48 MnvrDeltaDot,
50 MnvrDeltaDDot,
52 StartEpoch,
54 Duration,
56 EndEpoch,
58 ThrustX,
60 ThrustY,
62 ThrustZ,
64 ThrustLevel,
66 ThrustRateX,
68 ThrustRateY,
70 ThrustRateZ,
72 ThrustAccelX,
74 ThrustAccelY,
76 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 pub component: Vary,
125 pub perturbation: f64,
127 pub init_guess: f64,
129 pub max_step: f64,
131 pub max_value: f64,
133 pub min_value: f64,
135 pub frame: Option<Frame>,
137}
138
139impl Variable {
140 #[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 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 pub fn ensure_bounds(&self, val: &mut f64) {
203 *val = self.check_bounds(*val).0;
204 }
205
206 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, 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}