From 0a2039a4c00f608d070d6020c6fb48dcc74cc149 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Thu, 24 Sep 2026 12:04:58 +0200 Subject: [PATCH] more simplifications --- satrs-example/client/src/main.rs | 22 +-- satrs-example/minisim/src/acs/mgm.rs | 82 +++++----- satrs-example/minisim/src/acs/mgt.rs | 47 +++--- satrs-example/minisim/src/controller.rs | 32 ++-- satrs-example/minisim/src/lib.rs | 202 ++++++++++++------------ satrs-example/minisim/src/main.rs | 36 +++-- satrs-example/src/acs/mgm.rs | 34 ++-- 7 files changed, 220 insertions(+), 235 deletions(-) diff --git a/satrs-example/client/src/main.rs b/satrs-example/client/src/main.rs index 4456f7b..9a46051 100644 --- a/satrs-example/client/src/main.rs +++ b/satrs-example/client/src/main.rs @@ -3,8 +3,8 @@ use arbitrary_int::u11; use clap::Parser as _; use satrs_example::config::{OBSW_SERVER_ADDR, SERVER_PORT}; use satrs_minisim::{ - SimCtrlReply, SimCtrlRequest, SimReply, SimRequest, SimRequestWithTime, acs, - acs::MgmRequestLis3Mdl, acs::SpiFault, acs::mgm::MgmId, udp::SIM_CTRL_PORT, + SimCtrlReply, SimCtrlRequest, SimReply, SimRequest, SimRequestWithTime, acs::mgm, + udp::SIM_CTRL_PORT, }; use spacepackets::{CcsdsPacketIdAndPsc, SpacePacketHeader}; use std::{ @@ -97,12 +97,12 @@ enum FaultMode { AllOnes, } -impl From for acs::SpiFaultMode { +impl From for mgm::SpiFaultMode { fn from(mode: FaultMode) -> Self { match mode { - FaultMode::None => acs::SpiFaultMode::None, - FaultMode::AllZeros => acs::SpiFaultMode::AllZeros, - FaultMode::AllOnes => acs::SpiFaultMode::AllOnes, + FaultMode::None => mgm::SpiFaultMode::None, + FaultMode::AllZeros => mgm::SpiFaultMode::AllZeros, + FaultMode::AllOnes => mgm::SpiFaultMode::AllOnes, } } } @@ -204,7 +204,7 @@ fn handle_mgm_command( if let Some(mode) = args.fault { inject_mgm_failure( target_id, - SpiFault { + mgm::SpiFault { mode: mode.into(), cleared_by_power_cycle: args.fault_kind == FaultKind::Transient, }, @@ -499,7 +499,7 @@ fn main() -> anyhow::Result<()> { /// Confirms the simulator is actually reachable first (same ping/pong check the OBSW's own /// internal sim client does, see `SimClientUdp::attempt_connection`), since a fire-and-forget /// UDP send would otherwise silently do nothing if minisim is not running. -fn inject_mgm_failure(target_id: types::ComponentId, fault: SpiFault) -> anyhow::Result<()> { +fn inject_mgm_failure(target_id: types::ComponentId, fault: mgm::SpiFault) -> anyhow::Result<()> { let sim_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), SIM_CTRL_PORT); let sim_socket = UdpSocket::bind("127.0.0.1:0")?; sim_socket.set_read_timeout(Some(Duration::from_millis(200)))?; @@ -526,13 +526,13 @@ fn inject_mgm_failure(target_id: types::ComponentId, fault: SpiFault) -> anyhow: } let id = match target_id { - types::ComponentId::AcsMgm0 => MgmId::Mgm0, - types::ComponentId::AcsMgm1 => MgmId::Mgm1, + types::ComponentId::AcsMgm0 => mgm::Id::Mgm0, + types::ComponentId::AcsMgm1 => mgm::Id::Mgm1, _ => bail!("SPI fault injection is not supported for {target_id:?}"), }; let request = SimRequestWithTime::new_with_epoch_time(SimRequest::Mgm { id, - request: MgmRequestLis3Mdl::SetSpiFault(fault), + request: mgm::Request::SetSpiFault(fault), }); sim_socket.send_to(&serde_json::to_vec(&request)?, sim_addr)?; log::info!("injected SPI fault {fault:?} into minisim {target_id:?}"); diff --git a/satrs-example/minisim/src/acs/mgm.rs b/satrs-example/minisim/src/acs/mgm.rs index 84a5ea4..2e7c140 100644 --- a/satrs-example/minisim/src/acs/mgm.rs +++ b/satrs-example/minisim/src/acs/mgm.rs @@ -1,13 +1,7 @@ use std::{f32::consts::PI, sync::mpsc, time::Duration}; use nexosim::model::{Context, Model}; -use satrs_minisim::{ - acs::{ - mgm::{MgmId, MgmReply}, - MgmSensorValuesMicroTesla, SpiFault, - }, - SimReply, -}; +use satrs_minisim::{acs::mgm, SimReply}; use types::pcdu::SwitchStateBinary; use crate::time::current_millis; @@ -25,24 +19,28 @@ const PHASE_Z: f32 = 0.2; /// An ideal sensor would sample the magnetic field at a high fixed rate. This might not be /// possible for a general purpose OS, but self self-sampling at a relatively high rate (20-40 ms) /// might still be possible and is probably sufficient for many OBSW needs. -pub struct MagnetometerModel { - pub id: MgmId, +pub struct MgmModel { + pub id: mgm::Id, pub switch_state: SwitchStateBinary, #[allow(dead_code)] pub periodicity: Duration, - pub external_mag_field: Option, - pub spi_fault: SpiFault, + pub external_mag_field: Option, + pub spi_fault: mgm::SpiFault, pub reply_sender: mpsc::Sender, } -impl MagnetometerModel { - pub fn new(mgm_id: MgmId, periodicity: Duration, reply_sender: mpsc::Sender) -> Self { +impl MgmModel { + pub fn new( + mgm_id: mgm::Id, + periodicity: Duration, + reply_sender: mpsc::Sender, + ) -> Self { Self { id: mgm_id, switch_state: SwitchStateBinary::Off, periodicity, external_mag_field: None, - spi_fault: SpiFault::default(), + spi_fault: mgm::SpiFault::default(), reply_sender, } } @@ -50,19 +48,19 @@ impl MagnetometerModel { pub async fn switch_device(&mut self, switch_state: SwitchStateBinary) { self.switch_state = switch_state; if switch_state == SwitchStateBinary::Off && self.spi_fault.cleared_by_power_cycle { - self.spi_fault = SpiFault::default(); + self.spi_fault = mgm::SpiFault::default(); } } /// Force (or clear) a stuck-bus SPI fault, for FDIR testing purposes. - pub async fn set_spi_fault(&mut self, fault: SpiFault) { + pub async fn set_spi_fault(&mut self, fault: mgm::SpiFault) { self.spi_fault = fault; } pub async fn send_sensor_values(&mut self, _: (), scheduler: &mut Context) { let reply = SimReply::Mgm { id: self.id, - reply: MgmReply::new( + reply: mgm::Reply::new( self.switch_state, self.calculate_current_mgm_tuple(current_millis(scheduler.time())), self.spi_fault.mode, @@ -75,23 +73,23 @@ impl MagnetometerModel { // Devices like magnetorquers generate a strong magnetic field which overrides the default // model for the measured magnetic field. - pub async fn apply_external_magnetic_field(&mut self, field: MgmSensorValuesMicroTesla) { + pub async fn apply_external_magnetic_field(&mut self, field: mgm::SensorValuesMicroTesla) { self.external_mag_field = Some(field); } - fn calculate_current_mgm_tuple(&self, time_ms: u64) -> MgmSensorValuesMicroTesla { + fn calculate_current_mgm_tuple(&self, time_ms: u64) -> mgm::SensorValuesMicroTesla { if SwitchStateBinary::On == self.switch_state { if let Some(ext_field) = self.external_mag_field { return ext_field; } let base_sin_val = 2.0 * PI * FREQUENCY_MGM * (time_ms as f32 / 1000.0); - return MgmSensorValuesMicroTesla { + return mgm::SensorValuesMicroTesla { x: AMPLITUDE_MGM_UT * (base_sin_val + PHASE_X).sin(), y: AMPLITUDE_MGM_UT * (base_sin_val + PHASE_Y).sin(), z: AMPLITUDE_MGM_UT * (base_sin_val + PHASE_Z).sin(), }; } - MgmSensorValuesMicroTesla { + mgm::SensorValuesMicroTesla { x: 0.0, y: 0.0, z: 0.0, @@ -99,19 +97,13 @@ impl MagnetometerModel { } } -impl Model for MagnetometerModel {} +impl Model for MgmModel {} #[cfg(test)] mod tests { use std::time::Duration; - use satrs_minisim::{ - acs::{ - mgm::{self, MgmId, MgmReply}, - MgmRequestLis3Mdl, SpiFault, SpiFaultMode, - }, - SimComponent, SimReply, SimRequest, SimRequestWithTime, - }; + use satrs_minisim::{acs::mgm, SimComponent, SimReply, SimRequest, SimRequestWithTime}; use types::pcdu::{SwitchId, SwitchStateBinary}; use crate::{ @@ -119,11 +111,11 @@ mod tests { test_helpers::SimTestbench, }; - fn mgm_request(id: MgmId, request: MgmRequestLis3Mdl) -> SimRequestWithTime { + fn mgm_request(id: mgm::Id, request: mgm::Request) -> SimRequestWithTime { SimRequestWithTime::new_with_epoch_time(SimRequest::Mgm { id, request }) } - fn unwrap_mgm_reply(sim_reply: SimReply) -> (MgmId, MgmReply) { + fn unwrap_mgm_reply(sim_reply: SimReply) -> (mgm::Id, mgm::Reply) { match sim_reply { SimReply::Mgm { id, reply } => (id, reply), _ => panic!("unexpected reply {sim_reply:?}"), @@ -133,7 +125,7 @@ mod tests { #[test] fn test_basic_mgm_request() { let mut sim_testbench = SimTestbench::new(); - let request = mgm_request(MgmId::Mgm0, MgmRequestLis3Mdl::RequestSensorData); + let request = mgm_request(mgm::Id::Mgm0, mgm::Request::RequestSensorData); sim_testbench .send_request(request) .expect("sending MGM request failed"); @@ -144,7 +136,7 @@ mod tests { let sim_reply = sim_reply.unwrap(); assert_eq!(sim_reply.component(), SimComponent::Mgm0Lis3Mdl); let (id, reply) = unwrap_mgm_reply(sim_reply); - assert_eq!(id, MgmId::Mgm0); + assert_eq!(id, mgm::Id::Mgm0); assert_eq!(reply.switch_state, SwitchStateBinary::Off); assert_eq!(reply.sensor_values.x, 0.0); assert_eq!(reply.sensor_values.y, 0.0); @@ -153,9 +145,9 @@ mod tests { fn inject_spi_fault(sim_testbench: &mut SimTestbench, cleared_by_power_cycle: bool) { let fault_request = mgm_request( - MgmId::Mgm0, - MgmRequestLis3Mdl::SetSpiFault(SpiFault { - mode: SpiFaultMode::AllOnes, + mgm::Id::Mgm0, + mgm::Request::SetSpiFault(mgm::SpiFault { + mode: mgm::SpiFaultMode::AllOnes, cleared_by_power_cycle, }), ); @@ -166,8 +158,8 @@ mod tests { sim_testbench.step().unwrap(); } - fn request_mgm_reply(sim_testbench: &mut SimTestbench) -> MgmReply { - let data_request = mgm_request(MgmId::Mgm0, MgmRequestLis3Mdl::RequestSensorData); + fn request_mgm_reply(sim_testbench: &mut SimTestbench) -> mgm::Reply { + let data_request = mgm_request(mgm::Id::Mgm0, mgm::Request::RequestSensorData); sim_testbench .send_request(data_request) .expect("sending MGM request failed"); @@ -179,7 +171,7 @@ mod tests { unwrap_mgm_reply(sim_reply).1 } - fn is_stuck_bus_reply(reply: &MgmReply) -> bool { + fn is_stuck_bus_reply(reply: &mgm::Reply) -> bool { reply.raw.x == -1 && reply.raw.y == -1 && reply.raw.z == -1 } @@ -226,7 +218,7 @@ mod tests { let mut sim_testbench = SimTestbench::new(); switch_device_on(&mut sim_testbench, SwitchId::Mgm0); - let mut request = mgm_request(MgmId::Mgm0, MgmRequestLis3Mdl::RequestSensorData); + let mut request = mgm_request(mgm::Id::Mgm0, mgm::Request::RequestSensorData); sim_testbench .send_request(request) .expect("sending MGM request failed"); @@ -239,7 +231,7 @@ mod tests { let first_reply = unwrap_mgm_reply(sim_reply).1; sim_testbench.step_until(Duration::from_millis(50)).unwrap(); - request = mgm_request(MgmId::Mgm0, MgmRequestLis3Mdl::RequestSensorData); + request = mgm_request(mgm::Id::Mgm0, mgm::Request::RequestSensorData); sim_testbench .send_request(request) .expect("sending MGM request failed"); @@ -276,8 +268,8 @@ mod tests { switch_device_on(&mut sim_testbench, SwitchId::Mgm1); for request in [ - mgm_request(MgmId::Mgm0, MgmRequestLis3Mdl::RequestSensorData), - mgm_request(MgmId::Mgm1, MgmRequestLis3Mdl::RequestSensorData), + mgm_request(mgm::Id::Mgm0, mgm::Request::RequestSensorData), + mgm_request(mgm::Id::Mgm1, mgm::Request::RequestSensorData), ] { sim_testbench .send_request(request) @@ -291,7 +283,7 @@ mod tests { .expect("no MGM0 reply received"); assert_eq!(sim_reply.component(), SimComponent::Mgm0Lis3Mdl); let (id, reply) = unwrap_mgm_reply(sim_reply); - assert_eq!(id, MgmId::Mgm0); + assert_eq!(id, mgm::Id::Mgm0); assert_eq!(reply.switch_state, SwitchStateBinary::Off); let sim_reply = sim_testbench @@ -299,7 +291,7 @@ mod tests { .expect("no MGM1 reply received"); assert_eq!(sim_reply.component(), SimComponent::Mgm1Lis3Mdl); let (id, reply) = unwrap_mgm_reply(sim_reply); - assert_eq!(id, MgmId::Mgm1); + assert_eq!(id, mgm::Id::Mgm1); assert_eq!(reply.switch_state, SwitchStateBinary::On); } } diff --git a/satrs-example/minisim/src/acs/mgt.rs b/satrs-example/minisim/src/acs/mgt.rs index 71967a4..f07b65c 100644 --- a/satrs-example/minisim/src/acs/mgt.rs +++ b/satrs-example/minisim/src/acs/mgt.rs @@ -3,7 +3,7 @@ use nexosim::{ ports::Output, }; use satrs_minisim::{ - acs::{MgmSensorValuesMicroTesla, MgtDipole, MgtHkSet, MgtReply, MGT_GEN_MAGNETIC_FIELD}, + acs::{mgm, mgt}, SimReply, }; use std::{sync::mpsc, time::Duration}; @@ -12,8 +12,8 @@ use types::pcdu::SwitchStateBinary; pub struct MagnetorquerModel { switch_state: SwitchStateBinary, torquing: bool, - torque_dipole: MgtDipole, - pub gen_magnetic_field: Output, + torque_dipole: mgt::Dipole, + pub gen_magnetic_field: Output, reply_sender: mpsc::Sender, } @@ -22,7 +22,7 @@ impl MagnetorquerModel { Self { switch_state: SwitchStateBinary::Off, torquing: false, - torque_dipole: MgtDipole::default(), + torque_dipole: mgt::Dipole::default(), gen_magnetic_field: Output::new(), reply_sender, } @@ -30,7 +30,7 @@ impl MagnetorquerModel { pub async fn apply_torque( &mut self, - duration_and_dipole: (Duration, MgtDipole), + duration_and_dipole: (Duration, mgt::Dipole), cx: &mut Context, ) { self.torque_dipole = duration_and_dipole.1; @@ -45,7 +45,7 @@ impl MagnetorquerModel { } pub async fn clear_torque(&mut self, _: ()) { - self.torque_dipole = MgtDipole::default(); + self.torque_dipole = mgt::Dipole::default(); self.torquing = false; self.generate_magnetic_field(()).await; } @@ -65,17 +65,17 @@ impl MagnetorquerModel { pub fn send_housekeeping_data(&mut self) { self.reply_sender - .send(SimReply::from(MgtReply::Hk(MgtHkSet { + .send(SimReply::from(mgt::Reply::Hk(mgt::HkSet { dipole: self.torque_dipole, torquing: self.torquing, }))) .unwrap(); } - fn calc_magnetic_field(&self, _: MgtDipole) -> MgmSensorValuesMicroTesla { + fn calc_magnetic_field(&self, _: mgt::Dipole) -> mgm::SensorValuesMicroTesla { // Simplified model: Just returns some fixed magnetic field for now. // Later, we could make this more fancy by incorporating the commanded dipole. - MGT_GEN_MAGNETIC_FIELD + mgm::MGT_GEN_MAGNETIC_FIELD } /// A torquing magnetorquer generates a magnetic field. This function can be used to apply @@ -96,10 +96,7 @@ impl Model for MagnetorquerModel {} mod tests { use std::time::Duration; - use satrs_minisim::{ - acs::{MgtDipole, MgtHkSet, MgtReply, MgtRequest}, - SimReply, SimRequestWithTime, - }; + use satrs_minisim::{acs::mgt, SimReply, SimRequestWithTime}; use types::pcdu::SwitchId; use crate::{eps::tests::switch_device_on, test_helpers::SimTestbench}; @@ -107,7 +104,7 @@ mod tests { #[test] fn test_basic_mgt_request_is_off() { let mut sim_testbench = SimTestbench::new(); - let request = SimRequestWithTime::new_with_epoch_time(MgtRequest::RequestHk); + let request = SimRequestWithTime::new_with_epoch_time(mgt::Request::RequestHk); sim_testbench .send_request(request) .expect("sending MGM request failed"); @@ -121,7 +118,7 @@ mod tests { fn test_basic_mgt_request_is_on() { let mut sim_testbench = SimTestbench::new(); switch_device_on(&mut sim_testbench, SwitchId::Mgt); - let request = SimRequestWithTime::new_with_epoch_time(MgtRequest::RequestHk); + let request = SimRequestWithTime::new_with_epoch_time(mgt::Request::RequestHk); sim_testbench .send_request(request) @@ -135,16 +132,16 @@ mod tests { panic!("unexpected reply"); }; match mgt_reply { - MgtReply::Hk(hk) => { - assert_eq!(hk.dipole, MgtDipole::default()); + mgt::Reply::Hk(hk) => { + assert_eq!(hk.dipole, mgt::Dipole::default()); assert!(!hk.torquing); } _ => panic!("unexpected reply"), } } - fn check_mgt_hk(sim_testbench: &mut SimTestbench, expected_hk_set: MgtHkSet) { - let request = SimRequestWithTime::new_with_epoch_time(MgtRequest::RequestHk); + fn check_mgt_hk(sim_testbench: &mut SimTestbench, expected_hk_set: mgt::HkSet) { + let request = SimRequestWithTime::new_with_epoch_time(mgt::Request::RequestHk); sim_testbench .send_request(request) .expect("sending MGM request failed"); @@ -157,7 +154,7 @@ mod tests { panic!("unexpected reply"); }; match mgt_reply { - MgtReply::Hk(hk) => { + mgt::Reply::Hk(hk) => { assert_eq!(hk, expected_hk_set); } _ => panic!("unexpected reply"), @@ -168,12 +165,12 @@ mod tests { fn test_basic_mgt_request_is_on_and_torquing() { let mut sim_testbench = SimTestbench::new(); switch_device_on(&mut sim_testbench, SwitchId::Mgt); - let commanded_dipole = MgtDipole { + let commanded_dipole = mgt::Dipole { x: -200, y: 200, z: 1000, }; - let request = SimRequestWithTime::new_with_epoch_time(MgtRequest::ApplyTorque { + let request = SimRequestWithTime::new_with_epoch_time(mgt::Request::ApplyTorque { duration: Duration::from_millis(100), dipole: commanded_dipole, }); @@ -185,7 +182,7 @@ mod tests { check_mgt_hk( &mut sim_testbench, - MgtHkSet { + mgt::HkSet { dipole: commanded_dipole, torquing: true, }, @@ -195,8 +192,8 @@ mod tests { .unwrap(); check_mgt_hk( &mut sim_testbench, - MgtHkSet { - dipole: MgtDipole::default(), + mgt::HkSet { + dipole: mgt::Dipole::default(), torquing: false, }, ); diff --git a/satrs-example/minisim/src/controller.rs b/satrs-example/minisim/src/controller.rs index 2fb430f..a49d312 100644 --- a/satrs-example/minisim/src/controller.rs +++ b/satrs-example/minisim/src/controller.rs @@ -5,13 +5,13 @@ use nexosim::{ time::{Clock, MonotonicTime, SystemClock}, }; use satrs_minisim::{ - acs::{mgm::MgmId, MgmRequestLis3Mdl, MgtRequest}, + acs::{mgm, mgt}, eps::PcduRequest, SimCtrlReply, SimCtrlRequest, SimReply, SimRequest, SimRequestWithTime, }; use crate::{ - acs::{mgm::MagnetometerModel, mgt::MagnetorquerModel}, + acs::{mgm::MgmModel, mgt::MagnetorquerModel}, eps::PcduModel, }; @@ -23,8 +23,8 @@ const PCDU_REQ_WIRETAPPING: bool = false; const MGT_REQ_WIRETAPPING: bool = false; pub struct ModelAddrWrapper { - mgm_0_addr: Address, - mgm_1_addr: Address, + mgm_0_addr: Address, + mgm_1_addr: Address, pcdu_addr: Address, mgt_addr: Address, } @@ -42,8 +42,8 @@ pub struct SimController { impl ModelAddrWrapper { pub fn new( - mgm_0_addr: Address, - mgm_1_addr: Address, + mgm_0_addr: Address, + mgm_1_addr: Address, pcdu_addr: Address, mgt_addr: Address, ) -> Self { @@ -127,24 +127,24 @@ impl SimController { } } - fn handle_mgm_request(&mut self, mgm_id: MgmId, mgm_request: MgmRequestLis3Mdl) { + fn handle_mgm_request(&mut self, mgm_id: mgm::Id, mgm_request: mgm::Request) { let addr = match mgm_id { - MgmId::Mgm0 => &self.addr_wrapper.mgm_0_addr, - MgmId::Mgm1 => &self.addr_wrapper.mgm_1_addr, + mgm::Id::Mgm0 => &self.addr_wrapper.mgm_0_addr, + mgm::Id::Mgm1 => &self.addr_wrapper.mgm_1_addr, }; if MGM_REQ_WIRETAPPING { log::info!("received {mgm_id:?} request: {mgm_request:?}"); } match mgm_request { - MgmRequestLis3Mdl::RequestSensorData => { + mgm::Request::RequestSensorData => { self.simulation - .process_event(MagnetometerModel::send_sensor_values, (), addr) + .process_event(MgmModel::send_sensor_values, (), addr) .expect("event execution error for mgm"); } - MgmRequestLis3Mdl::SetSpiFault(fault_mode) => { + mgm::Request::SetSpiFault(fault_mode) => { log::info!("{mgm_id:?}: setting SPI fault mode to {fault_mode:?}"); self.simulation - .process_event(MagnetometerModel::set_spi_fault, fault_mode, addr) + .process_event(MgmModel::set_spi_fault, fault_mode, addr) .expect("event execution error for mgm"); } } @@ -176,12 +176,12 @@ impl SimController { } } - fn handle_mgt_request(&mut self, mgt_request: MgtRequest) { + fn handle_mgt_request(&mut self, mgt_request: mgt::Request) { if MGT_REQ_WIRETAPPING { log::info!("received MGT request: {mgt_request:?}"); } match mgt_request { - MgtRequest::ApplyTorque { duration, dipole } => self + mgt::Request::ApplyTorque { duration, dipole } => self .simulation .process_event( MagnetorquerModel::apply_torque, @@ -189,7 +189,7 @@ impl SimController { &self.addr_wrapper.mgt_addr, ) .unwrap(), - MgtRequest::RequestHk => self + mgt::Request::RequestHk => self .simulation .process_event( MagnetorquerModel::request_housekeeping_data, diff --git a/satrs-example/minisim/src/lib.rs b/satrs-example/minisim/src/lib.rs index c2f7209..470af78 100644 --- a/satrs-example/minisim/src/lib.rs +++ b/satrs-example/minisim/src/lib.rs @@ -2,10 +2,7 @@ use nexosim::time::MonotonicTime; use serde::{Deserialize, Serialize}; use crate::{ - acs::{ - mgm::{MgmId, MgmReply}, - MgmRequestLis3Mdl, MgtReply, MgtRequest, - }, + acs::{mgm, mgt}, eps::{PcduReply, PcduRequest}, }; @@ -22,11 +19,8 @@ pub enum SimComponent { #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub enum SimRequest { SimCtrl(SimCtrlRequest), - Mgm { - id: MgmId, - request: MgmRequestLis3Mdl, - }, - Mgt(MgtRequest), + Mgm { id: mgm::Id, request: mgm::Request }, + Mgt(mgt::Request), Pcdu(PcduRequest), } @@ -36,8 +30,8 @@ impl From for SimRequest { } } -impl From for SimRequest { - fn from(request: MgtRequest) -> Self { +impl From for SimRequest { + fn from(request: mgt::Request) -> Self { Self::Mgt(request) } } @@ -70,8 +64,8 @@ impl SimRequestWithTime { #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub enum SimReply { SimCtrl(SimCtrlReply), - Mgm { id: MgmId, reply: MgmReply }, - Mgt(MgtReply), + Mgm { id: mgm::Id, reply: mgm::Reply }, + Mgt(mgt::Reply), Pcdu(PcduReply), } @@ -92,8 +86,8 @@ impl From for SimReply { } } -impl From for SimReply { - fn from(reply: MgtReply) -> Self { +impl From for SimReply { + fn from(reply: mgt::Reply) -> Self { Self::Mgt(reply) } } @@ -142,62 +136,50 @@ pub mod eps { } pub mod acs { - use std::time::Duration; - - use types::pcdu::SwitchStateBinary; - - use super::*; - - /// Fault mode injected on the simulated SPI bus, independent of the switch state. - /// - /// Models the classic symptom of a stuck SPI bus: an undriven MISO line commonly reads - /// back as all-1s, a shorted/grounded one as all-0s. - #[derive(Debug, Default, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)] - pub enum SpiFaultMode { - #[default] - None, - AllZeros, - AllOnes, - } - - #[derive(Debug, Default, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)] - pub struct SpiFault { - pub mode: SpiFaultMode, - /// The fault is cleared when the device is switched off, so a power cycle recovers - /// from it. - pub cleared_by_power_cycle: bool, - } - - #[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)] - pub enum MgmRequestLis3Mdl { - RequestSensorData, - /// Force the raw register reply into a stuck-bus pattern, regardless of switch state. - /// Used to test FDIR handling of SPI bus faults. - SetSpiFault(SpiFault), - } - - // Normally, small magnetometers generate their output as a signed 16 bit raw format or something - // similar which needs to be converted to a signed float value with physical units. We will - // simplify this now and generate the signed float values directly. The unit is micro tesla. - #[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)] - pub struct MgmSensorValuesMicroTesla { - pub x: f32, - pub y: f32, - pub z: f32, - } - - pub const MGT_GEN_MAGNETIC_FIELD: MgmSensorValuesMicroTesla = MgmSensorValuesMicroTesla { - x: 30.0, - y: -30.0, - z: 30.0, - }; - pub const ALL_ONES_SENSOR_VAL: i16 = 0xffff_u16 as i16; - pub const ALL_ZEROS_SENSOR_VAL: i16 = 0; - /// MGM module strongly based on the LIS3MDL device. pub mod mgm { + use serde::{Deserialize, Serialize}; + use types::pcdu::SwitchStateBinary; - use super::*; + use crate::SimComponent; + + /// Fault mode injected on the simulated SPI bus, independent of the switch state. + /// + /// Models the classic symptom of a stuck SPI bus: an undriven MISO line commonly reads + /// back as all-1s, a shorted/grounded one as all-0s. + #[derive(Debug, Default, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)] + pub enum SpiFaultMode { + #[default] + None, + AllZeros, + AllOnes, + } + + #[derive(Debug, Default, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)] + pub struct SpiFault { + pub mode: SpiFaultMode, + /// The fault is cleared when the device is switched off, so a power cycle recovers + /// from it. + pub cleared_by_power_cycle: bool, + } + + // Normally, small magnetometers generate their output as a signed 16 bit raw format or something + // similar which needs to be converted to a signed float value with physical units. We will + // simplify this now and generate the signed float values directly. The unit is micro tesla. + #[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)] + pub struct SensorValuesMicroTesla { + pub x: f32, + pub y: f32, + pub z: f32, + } + + pub const MGT_GEN_MAGNETIC_FIELD: SensorValuesMicroTesla = SensorValuesMicroTesla { + x: 30.0, + y: -30.0, + z: 30.0, + }; + pub const ALL_ONES_SENSOR_VAL: i16 = 0xffff_u16 as i16; + pub const ALL_ZEROS_SENSOR_VAL: i16 = 0; // Field data register scaling pub const GAUSS_TO_MICROTESLA_FACTOR: u32 = 100; @@ -212,35 +194,42 @@ pub mod acs { pub y: i16, pub z: i16, } + #[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)] + pub enum Request { + RequestSensorData, + /// Force the raw register reply into a stuck-bus pattern, regardless of switch state. + /// Used to test FDIR handling of SPI bus faults. + SetSpiFault(SpiFault), + } #[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)] - pub struct MgmReply { + pub struct Reply { pub switch_state: SwitchStateBinary, - pub sensor_values: MgmSensorValuesMicroTesla, + pub sensor_values: SensorValuesMicroTesla, // Raw sensor values which are transmitted by the LIS3 device in little-endian // order. pub raw: RawValues, } #[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)] - pub enum MgmId { + pub enum Id { Mgm0, Mgm1, } - impl MgmId { + impl Id { pub const fn sim_component(&self) -> SimComponent { match self { - MgmId::Mgm0 => SimComponent::Mgm0Lis3Mdl, - MgmId::Mgm1 => SimComponent::Mgm1Lis3Mdl, + Id::Mgm0 => SimComponent::Mgm0Lis3Mdl, + Id::Mgm1 => SimComponent::Mgm1Lis3Mdl, } } } - impl MgmReply { + impl Reply { pub fn new( switch_state: SwitchStateBinary, - sensor_values: MgmSensorValuesMicroTesla, + sensor_values: SensorValuesMicroTesla, fault_mode: SpiFaultMode, ) -> Self { match fault_mode { @@ -308,39 +297,42 @@ pub mod acs { } } - // Simple model using i16 values. - #[derive(Default, Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)] - pub struct MgtDipole { - pub x: i16, - pub y: i16, - pub z: i16, - } + pub mod mgt { + use std::time::Duration; - #[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)] - pub enum MgtRequestType { - ApplyTorque, - } + use serde::{Deserialize, Serialize}; - #[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)] - pub enum MgtRequest { - ApplyTorque { - duration: Duration, - dipole: MgtDipole, - }, - RequestHk, - } + // Simple model using i16 values. + #[derive(Default, Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)] + pub struct Dipole { + pub x: i16, + pub y: i16, + pub z: i16, + } - #[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)] - pub struct MgtHkSet { - pub dipole: MgtDipole, - pub torquing: bool, - } + #[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)] + pub enum RequestType { + ApplyTorque, + } - #[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)] - pub enum MgtReply { - Ack(MgtRequestType), - Nak(MgtRequestType), - Hk(MgtHkSet), + #[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)] + pub enum Request { + ApplyTorque { duration: Duration, dipole: Dipole }, + RequestHk, + } + + #[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)] + pub struct HkSet { + pub dipole: Dipole, + pub torquing: bool, + } + + #[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)] + pub enum Reply { + Ack(RequestType), + Nak(RequestType), + Hk(HkSet), + } } } diff --git a/satrs-example/minisim/src/main.rs b/satrs-example/minisim/src/main.rs index 0af35f6..2af4518 100644 --- a/satrs-example/minisim/src/main.rs +++ b/satrs-example/minisim/src/main.rs @@ -1,9 +1,9 @@ -use acs::{mgm::MagnetometerModel, mgt::MagnetorquerModel}; +use acs::{mgm::MgmModel, mgt::MagnetorquerModel}; use controller::{ModelAddrWrapper, SimController}; use eps::PcduModel; use nexosim::simulation::{Mailbox, SimInit}; use nexosim::time::{MonotonicTime, SystemClock}; -use satrs_minisim::acs::mgm::MgmId; +use satrs_minisim::acs::mgm; use satrs_minisim::udp::SIM_CTRL_PORT; use satrs_minisim::{SimReply, SimRequestWithTime}; use std::sync::mpsc; @@ -32,10 +32,16 @@ fn create_sim_controller( request_receiver: mpsc::Receiver, ) -> SimController { // Instantiate models and their mailboxes. - let mgm_0_model = - MagnetometerModel::new(MgmId::Mgm0, Duration::from_millis(50), reply_sender.clone()); - let mgm_1_model = - MagnetometerModel::new(MgmId::Mgm1, Duration::from_millis(50), reply_sender.clone()); + let mgm_0_model = MgmModel::new( + mgm::Id::Mgm0, + Duration::from_millis(50), + reply_sender.clone(), + ); + let mgm_1_model = MgmModel::new( + mgm::Id::Mgm1, + Duration::from_millis(50), + reply_sender.clone(), + ); let mgm_0_mailbox = Mailbox::new(); let mgm_0_addr = mgm_0_mailbox.address(); @@ -49,10 +55,10 @@ fn create_sim_controller( let mut pcdu_model = PcduModel::new(reply_sender.clone()); pcdu_model .mgm_0_switch - .connect(MagnetometerModel::switch_device, &mgm_0_addr); + .connect(MgmModel::switch_device, &mgm_0_addr); pcdu_model .mgm_1_switch - .connect(MagnetometerModel::switch_device, &mgm_1_addr); + .connect(MgmModel::switch_device, &mgm_1_addr); let mut mgt_model = MagnetorquerModel::new(reply_sender.clone()); // Input connections. @@ -60,14 +66,12 @@ fn create_sim_controller( .mgt_switch .connect(MagnetorquerModel::switch_device, &mgt_addr); // Output connections. - mgt_model.gen_magnetic_field.connect( - MagnetometerModel::apply_external_magnetic_field, - &mgm_0_addr, - ); - mgt_model.gen_magnetic_field.connect( - MagnetometerModel::apply_external_magnetic_field, - &mgm_1_addr, - ); + mgt_model + .gen_magnetic_field + .connect(MgmModel::apply_external_magnetic_field, &mgm_0_addr); + mgt_model + .gen_magnetic_field + .connect(MgmModel::apply_external_magnetic_field, &mgm_1_addr); // Instantiate the simulator let sys_clock = SystemClock::from_system_time(start_time, SystemTime::now()); diff --git a/satrs-example/src/acs/mgm.rs b/satrs-example/src/acs/mgm.rs index a5152c4..14cc6ae 100644 --- a/satrs-example/src/acs/mgm.rs +++ b/satrs-example/src/acs/mgm.rs @@ -2,8 +2,8 @@ use satrs::fdir::{FaultCounterStd, FaultResponse, RecoveryEvent, RecoveryFdir}; use satrs::health::HealthTableMapSync; use satrs::spacepackets::CcsdsPacketIdAndPsc; use satrs_example::{HkHelperSingleSet, TimestampHelper, TmtcQueues}; -use satrs_minisim::acs::MgmRequestLis3Mdl; -use satrs_minisim::acs::mgm::{FIELD_LSB_PER_GAUSS_4_SENS, GAUSS_TO_MICROTESLA_FACTOR, RawValues}; +use satrs_minisim::acs::mgm as sim_mgm; +use satrs_minisim::acs::mgm::{FIELD_LSB_PER_GAUSS_4_SENS, GAUSS_TO_MICROTESLA_FACTOR}; use satrs_minisim::{SimReply, SimRequest, SimRequestWithTime}; use std::sync::mpsc; use std::sync::{Arc, Mutex}; @@ -74,7 +74,7 @@ impl MgmId { #[derive(Default)] pub struct SpiDummyInterface { - pub dummy_values: RawValues, + pub dummy_values: sim_mgm::RawValues, } impl SpiDummyInterface { @@ -88,7 +88,7 @@ impl SpiDummyInterface { #[derive(Default)] pub struct TestSpiInterface { pub call_count: u32, - pub next_mgm_data: RawValues, + pub next_mgm_data: sim_mgm::RawValues, } impl TestSpiInterface { @@ -110,12 +110,12 @@ impl SpiSimInterface { // Right now, we only support requesting sensor data and not configuration of the sensor. fn transfer(&mut self, _tx: &[u8], rx: &mut [u8]) { let sim_id = match self.id { - MgmId::_0 => satrs_minisim::acs::mgm::MgmId::Mgm0, - MgmId::_1 => satrs_minisim::acs::mgm::MgmId::Mgm1, + MgmId::_0 => sim_mgm::Id::Mgm0, + MgmId::_1 => sim_mgm::Id::Mgm1, }; let sim_request = SimRequestWithTime::new_with_epoch_time(SimRequest::Mgm { id: sim_id, - request: MgmRequestLis3Mdl::RequestSensorData, + request: sim_mgm::Request::RequestSensorData, }); if let Err(e) = self.sim_request_tx.send(sim_request) { log::error!("failed to send MGM LIS3 request: {e}"); @@ -652,7 +652,7 @@ mod tests { use arbitrary_int::u11; use satrs::health::{HealthState, HealthTableProvider}; use satrs::spacepackets::SpacePacketHeader; - use satrs_minisim::acs::mgm::RawValues; + use satrs_minisim::acs::mgm as sim_mgm; use types::{ Apid, ComponentId, TcHeader, acs::mgm::request::HkRequest, @@ -771,7 +771,7 @@ mod tests { } pub fn inject_stuck_bus(&mut self) { - self.test_spi_interface().next_mgm_data = RawValues { + self.test_spi_interface().next_mgm_data = sim_mgm::RawValues { x: -1, y: -1, z: -1, @@ -912,7 +912,7 @@ mod tests { #[test] fn test_normal_handler_mgm_set_conversion() { let mut testbench = MgmTestbench::new(); - let raw_values = RawValues { + let raw_values = sim_mgm::RawValues { x: 1000, y: -1000, z: 1000, @@ -1065,7 +1065,7 @@ mod tests { fn test_spi_fault_below_threshold_stays_healthy() { let mut testbench = MgmTestbench::new(); testbench.switch_to_normal(); - testbench.test_spi_interface().next_mgm_data = RawValues { + testbench.test_spi_interface().next_mgm_data = sim_mgm::RawValues { x: -1, y: -1, z: -1, @@ -1106,7 +1106,7 @@ mod tests { testbench.drain_switch_requests(); testbench.mode_report_rx.try_iter().for_each(drop); testbench.exceed_spi_fault_threshold(); - testbench.test_spi_interface().next_mgm_data = RawValues::default(); + testbench.test_spi_interface().next_mgm_data = sim_mgm::RawValues::default(); let call_count = testbench.test_spi_interface().call_count; testbench.complete_power_cycle(); @@ -1279,7 +1279,7 @@ mod tests { testbench.drain_events(); testbench.mode_report_rx.try_iter().for_each(drop); testbench.exceed_spi_fault_threshold(); - testbench.test_spi_interface().next_mgm_data = RawValues::default(); + testbench.test_spi_interface().next_mgm_data = sim_mgm::RawValues::default(); // The switch never turns off. Every failed power cycle costs a recovery attempt. for _ in 0..RECOVERY_THRESHOLD { @@ -1317,7 +1317,7 @@ mod tests { let mut testbench = MgmTestbench::new(); testbench.switch_to_normal(); testbench.exceed_spi_fault_threshold(); - testbench.test_spi_interface().next_mgm_data = RawValues::default(); + testbench.test_spi_interface().next_mgm_data = sim_mgm::RawValues::default(); testbench .tc_tx .send(create_request_tc( @@ -1382,7 +1382,7 @@ mod tests { testbench .health_table .set_health(ComponentId::AcsMgm0.into(), HealthState::ExternalControl); - testbench.test_spi_interface().next_mgm_data = RawValues { + testbench.test_spi_interface().next_mgm_data = sim_mgm::RawValues { x: -1, y: -1, z: -1, @@ -1401,7 +1401,7 @@ mod tests { fn test_recovering_from_spi_fault_clears_invalid_data_flag() { let mut testbench = MgmTestbench::new(); testbench.switch_to_normal(); - testbench.test_spi_interface().next_mgm_data = RawValues { + testbench.test_spi_interface().next_mgm_data = sim_mgm::RawValues { x: -1, y: -1, z: -1, @@ -1410,7 +1410,7 @@ mod tests { assert!(!testbench.handler.shared_mgm_set.lock().unwrap().valid); // Bus recovers before the threshold is exceeded. - testbench.test_spi_interface().next_mgm_data = RawValues::default(); + testbench.test_spi_interface().next_mgm_data = sim_mgm::RawValues::default(); testbench.handler.periodic_operation(); assert_eq!( testbench.health_table.health(ComponentId::AcsMgm0.into()),