#[derive(Debug, Clone, PartialEq)]
pub enum RwCommand {
Speeds(Vec<f64>),
Torques(Vec<f64>),
}
impl RwCommand {
pub fn is_finite(&self) -> bool {
match self {
RwCommand::Speeds(v) | RwCommand::Torques(v) => v.iter().all(|x| x.is_finite()),
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum MtqCommand {
Moments(Vec<f64>),
NormalizedMoments(Vec<f64>),
}
impl MtqCommand {
pub fn is_finite(&self) -> bool {
match self {
MtqCommand::Moments(v) | MtqCommand::NormalizedMoments(v) => {
v.iter().all(|x| x.is_finite())
}
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum ThrusterCommand {
Throttles(Vec<f64>),
}
impl ThrusterCommand {
pub fn is_finite(&self) -> bool {
match self {
ThrusterCommand::Throttles(v) => v.iter().all(|x| x.is_finite()),
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct Command {
pub mtq: Option<MtqCommand>,
pub rw: Option<RwCommand>,
pub thruster: Option<ThrusterCommand>,
}
impl Command {
pub fn mtq(moments: Vec<f64>) -> Self {
Self {
mtq: Some(MtqCommand::Moments(moments)),
rw: None,
thruster: None,
}
}
pub fn mtq_normalized(values: Vec<f64>) -> Self {
Self {
mtq: Some(MtqCommand::NormalizedMoments(values)),
rw: None,
thruster: None,
}
}
pub fn rw_cmd(cmd: RwCommand) -> Self {
Self {
mtq: None,
rw: Some(cmd),
thruster: None,
}
}
pub fn rw_torques(torques: Vec<f64>) -> Self {
Self::rw_cmd(RwCommand::Torques(torques))
}
pub fn rw_speeds(speeds: Vec<f64>) -> Self {
Self::rw_cmd(RwCommand::Speeds(speeds))
}
pub fn thruster(throttles: Vec<f64>) -> Self {
Self {
mtq: None,
rw: None,
thruster: Some(ThrusterCommand::Throttles(throttles)),
}
}
pub fn is_finite(&self) -> bool {
let mtq_ok = self.mtq.as_ref().is_none_or(|cmd| cmd.is_finite());
let rw_ok = self.rw.as_ref().is_none_or(|cmd| cmd.is_finite());
let thruster_ok = self.thruster.as_ref().is_none_or(|cmd| cmd.is_finite());
mtq_ok && rw_ok && thruster_ok
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn mtq_finite_detects_nan() {
let good = Command::mtq(vec![1.0, -2.0, 0.0]);
assert!(good.is_finite());
let nan = Command::mtq(vec![1.0, f64::NAN, 0.0]);
assert!(!nan.is_finite());
let inf = Command::mtq(vec![f64::INFINITY, 0.0, 0.0]);
assert!(!inf.is_finite());
}
#[test]
fn mtq_normalized_finite_detects_nan() {
let good = Command::mtq_normalized(vec![0.5, -1.0, 0.0]);
assert!(good.is_finite());
let nan = Command::mtq_normalized(vec![0.5, f64::NAN, 0.0]);
assert!(!nan.is_finite());
}
#[test]
fn rw_finite_detects_nan() {
let good = Command::rw_torques(vec![0.01, -0.02, 0.0]);
assert!(good.is_finite());
let nan = Command::rw_torques(vec![f64::NAN, 0.0, 0.0]);
assert!(!nan.is_finite());
}
#[test]
fn rw_speeds_finite_detects_nan() {
let good = Command::rw_speeds(vec![10.0, -5.0, 0.0]);
assert!(good.is_finite());
let nan = Command::rw_speeds(vec![f64::NAN, 0.0, 0.0]);
assert!(!nan.is_finite());
}
#[test]
fn field_access() {
let mm = Command::mtq(vec![1.0, 2.0, 3.0]);
assert!(mm.mtq.is_some());
assert!(mm.rw.is_none());
let mn = Command::mtq_normalized(vec![0.1, 0.2, 0.3]);
assert!(matches!(mn.mtq, Some(MtqCommand::NormalizedMoments(_))));
assert!(mn.rw.is_none());
let rw = Command::rw_torques(vec![0.1, 0.2, 0.3]);
assert!(rw.mtq.is_none());
assert!(rw.rw.is_some());
}
#[test]
fn both_fields_set() {
let cmd = Command {
mtq: Some(MtqCommand::Moments(vec![1.0, 0.0, 0.0])),
rw: Some(RwCommand::Torques(vec![0.0, 0.1, 0.0])),
thruster: None,
};
assert!(cmd.is_finite());
assert!(cmd.mtq.is_some());
assert!(cmd.rw.is_some());
}
#[test]
fn both_fields_nan_in_one() {
let cmd = Command {
mtq: Some(MtqCommand::Moments(vec![f64::NAN, 0.0, 0.0])),
rw: Some(RwCommand::Torques(vec![0.0, 0.1, 0.0])),
thruster: None,
};
assert!(!cmd.is_finite());
}
#[test]
fn empty_vec_is_finite() {
let cmd = Command {
mtq: Some(MtqCommand::Moments(vec![])),
rw: Some(RwCommand::Torques(vec![])),
thruster: None,
};
assert!(cmd.is_finite());
}
#[test]
fn thruster_finite_detects_nan() {
let good = Command::thruster(vec![0.5, 1.0, 0.0]);
assert!(good.is_finite());
let nan = Command::thruster(vec![0.5, f64::NAN, 0.0]);
assert!(!nan.is_finite());
let inf = Command::thruster(vec![f64::INFINITY, 0.0, 0.0]);
assert!(!inf.is_finite());
}
#[test]
fn thruster_field_access() {
let cmd = Command::thruster(vec![0.5, 1.0]);
assert!(cmd.thruster.is_some());
assert!(cmd.mtq.is_none());
assert!(cmd.rw.is_none());
}
#[test]
fn all_fields_set() {
let cmd = Command {
mtq: Some(MtqCommand::Moments(vec![1.0])),
rw: Some(RwCommand::Torques(vec![0.1])),
thruster: Some(ThrusterCommand::Throttles(vec![0.5])),
};
assert!(cmd.is_finite());
}
}