use super::{GuidanceError, GuidanceLaw};
use crate::State;
use crate::cosmic::{GuidanceMode, Spacecraft};
use crate::linalg::Vector3;
use crate::md::Trajectory;
use anise::prelude::Almanac;
use std::fmt;
use std::sync::Arc;
#[derive(Clone)]
pub struct ThrustDirectionReplay {
pub profile: Trajectory,
}
impl ThrustDirectionReplay {
pub fn from_trajectory(profile: Trajectory) -> Arc<Self> {
Arc::new(Self { profile })
}
fn command_at_or_before(&self, state: &Spacecraft) -> Option<&Spacecraft> {
if self.profile.states.is_empty() {
return None;
}
let epoch = state.epoch();
if epoch < self.profile.first().epoch() || epoch > self.profile.last().epoch() {
return None;
}
let command = match self
.profile
.states
.binary_search_by(|sample| sample.epoch().cmp(&epoch))
{
Ok(idx) => self.profile.states.get(idx),
Err(0) => None,
Err(idx) => self.profile.states.get(idx - 1),
};
if let Some(command) = command
&& (command.thrust_direction().is_some() || command.mode() != GuidanceMode::Thrust)
{
return Some(command);
}
if self.profile.first().mode() == GuidanceMode::Thrust {
return self
.profile
.states
.iter()
.find(|sample| sample.epoch() >= epoch && sample.thrust_direction().is_some());
}
command
}
}
impl fmt::Display for ThrustDirectionReplay {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"Thrust direction replay from {} to {} ({} samples)",
self.profile.first().epoch(),
self.profile.last().epoch(),
self.profile.states.len()
)
}
}
impl GuidanceLaw for ThrustDirectionReplay {
fn direction(&self, osc_state: &Spacecraft) -> Result<Vector3<f64>, GuidanceError> {
Ok(self
.command_at_or_before(osc_state)
.and_then(|sample| sample.thrust_direction())
.unwrap_or_else(Vector3::zeros))
}
fn throttle(&self, osc_state: &Spacecraft) -> Result<f64, GuidanceError> {
Ok(
if self
.command_at_or_before(osc_state)
.and_then(|sample| sample.thrust_direction())
.is_some()
{
1.0
} else {
0.0
},
)
}
fn next(&self, next_state: &mut Spacecraft, _almanac: &Almanac) {
let thrust_direction = self
.command_at_or_before(next_state)
.and_then(|sample| sample.thrust_direction());
next_state.mut_thrust_direction(thrust_direction);
next_state.mut_mode(if thrust_direction.is_some() {
GuidanceMode::Thrust
} else {
GuidanceMode::Coast
});
}
fn achieved(&self, osc_state: &Spacecraft) -> Result<bool, GuidanceError> {
Ok(osc_state.epoch() > self.profile.last().epoch())
}
}