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 and non-overlapping, 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 /// and return it if its end epoch is after the current epoch
43 fn maneuver_at(&self, epoch: Epoch) -> Option<&Maneuver> {
44 let index = self.mnvrs.binary_search_by_key(&epoch, |mnvr| mnvr.start);
45 let mnvr = match index {
46 Err(0) => None, // No maneuvers start before the current epoch
47 Ok(index) => Some(&self.mnvrs[index]),
48 Err(index) => Some(&self.mnvrs[index - 1]), // Return the maneuver with the closest start epoch
49 }?;
50
51 (epoch <= mnvr.end).then_some(mnvr)
52 }
53}
54
55impl fmt::Display for FiniteBurns {
56 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
57 write!(f, "FiniteBurns with {} maneuvers", self.mnvrs.len())
58 }
59}
60
61impl GuidanceLaw for FiniteBurns {
62 fn direction(&self, osc: &Spacecraft) -> Result<Vector3<f64>, GuidanceError> {
63 // NOTE: We do not increment the mnvr number here. The power function is called first,
64 // so we let that function handle starting and stopping of the maneuver.
65 match osc.mode() {
66 GuidanceMode::Thrust => {
67 if let Some(next_mnvr) = self.maneuver_at(osc.epoch()) {
68 <Maneuver as GuidanceLaw>::direction(next_mnvr, osc)
69 } else {
70 Ok(Vector3::zeros())
71 }
72 }
73 _ => Ok(Vector3::zeros()),
74 }
75 }
76
77 fn throttle(&self, osc: &Spacecraft) -> Result<f64, GuidanceError> {
78 match osc.mode() {
79 GuidanceMode::Thrust => {
80 if let Some(next_mnvr) = self.maneuver_at(osc.epoch()) {
81 Ok(next_mnvr.thrust_prct)
82 } else {
83 Ok(0.0)
84 }
85 }
86 _ => {
87 // We aren't in maneuver mode, so return 0% throttle
88 Ok(0.0)
89 }
90 }
91 }
92
93 fn next(&self, sc: &mut Spacecraft, _almanac: Arc<Almanac>) {
94 // check if there is a current maneuver and put into Thrust mode otherwise Coast
95 if self.maneuver_at(sc.epoch()).is_some() {
96 sc.mut_mode(GuidanceMode::Thrust)
97 } else {
98 // There aren't any maneuvers
99 sc.mut_mode(GuidanceMode::Coast)
100 }
101 }
102}