nyx_space/dynamics/guidance/finiteburns.rs
1use anise::prelude::Almanac;
2/*
3 Nyx, blazing fast astrodynamics
4 Copyright (C) 2018-onwards Christopher Rabotin <christopher.rabotin@gmail.com>
5
6 This program is free software: you can redistribute it and/or modify
7 it under the terms of the GNU Affero General Public License as published
8 by the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU Affero General Public License for more details.
15
16 You should have received a copy of the GNU Affero General Public License
17 along with this program. If not, see <https://www.gnu.org/licenses/>.
18*/
19use hifitime::Epoch;
20
21use super::{GuidanceError, GuidanceLaw, Maneuver};
22use crate::cosmic::{GuidanceMode, Spacecraft};
23use crate::linalg::Vector3;
24use crate::State;
25use std::fmt;
26use std::sync::Arc;
27
28/// A guidance law for a set of pre-determined maneuvers.
29#[derive(Clone, Debug)]
30pub struct FiniteBurns {
31 /// Maneuvers should be provided in chronological order, first maneuver first in the list
32 pub mnvrs: Vec<Maneuver>,
33}
34
35impl FiniteBurns {
36 /// Builds a schedule from the vector of maneuvers, must be provided in chronological order.
37 pub fn from_mnvrs(mnvrs: Vec<Maneuver>) -> Arc<Self> {
38 Arc::new(Self { mnvrs })
39 }
40
41 /// Find the maneuver with the closest start epoch that is less than or equal to the current epoch
42 fn maneuver_at(&self, epoch: Epoch) -> Option<&Maneuver> {
43 let index = self.mnvrs.binary_search_by_key(&epoch, |mnvr| mnvr.start);
44 match index {
45 Err(0) => None, // No maneuvers start before the current epoch
46 Ok(index) => Some(&self.mnvrs[index]),
47 Err(index) => Some(&self.mnvrs[index - 1]), // Return the maneuver with the closest start epoch
48 }
49 }
50}
51
52impl fmt::Display for FiniteBurns {
53 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
54 write!(f, "FiniteBurns with {} maneuvers", self.mnvrs.len())
55 }
56}
57
58impl GuidanceLaw for FiniteBurns {
59 fn direction(&self, osc: &Spacecraft) -> Result<Vector3<f64>, GuidanceError> {
60 // NOTE: We do not increment the mnvr number here. The power function is called first,
61 // so we let that function handle starting and stopping of the maneuver.
62 match osc.mode() {
63 GuidanceMode::Thrust => {
64 if let Some(next_mnvr) = self.maneuver_at(osc.epoch()) {
65 if next_mnvr.start <= osc.epoch() {
66 <Maneuver as GuidanceLaw>::direction(next_mnvr, osc)
67 } else {
68 Ok(Vector3::zeros())
69 }
70 } else {
71 Ok(Vector3::zeros())
72 }
73 }
74 _ => Ok(Vector3::zeros()),
75 }
76 }
77
78 fn throttle(&self, osc: &Spacecraft) -> Result<f64, GuidanceError> {
79 match osc.mode() {
80 GuidanceMode::Thrust => {
81 if let Some(next_mnvr) = self.maneuver_at(osc.epoch()) {
82 if next_mnvr.start <= osc.epoch() {
83 Ok(next_mnvr.thrust_prct)
84 } else {
85 Ok(0.0)
86 }
87 } else {
88 Ok(0.0)
89 }
90 }
91 _ => {
92 // We aren't in maneuver mode, so return 0% throttle
93 Ok(0.0)
94 }
95 }
96 }
97
98 fn next(&self, sc: &mut Spacecraft, _almanac: Arc<Almanac>) {
99 // Grab the last maneuver
100 if let Some(last_mnvr) = self.mnvrs.last() {
101 // If the last maneuver ends before the current epoch, switch back into coast
102 if last_mnvr.end < sc.epoch() {
103 sc.mut_mode(GuidanceMode::Coast)
104 } else {
105 // Get ready for the maneuver
106 sc.mut_mode(GuidanceMode::Thrust)
107 }
108 } else {
109 // There aren't any maneuvers
110 sc.mut_mode(GuidanceMode::Coast)
111 }
112 }
113}