diff --git a/satrs-example/minisim/README.md b/satrs-example/minisim/README.md
index 42f949d..b55d3e9 100644
--- a/satrs-example/minisim/README.md
+++ b/satrs-example/minisim/README.md
@@ -2,7 +2,7 @@ sat-rs minisim
======
This crate contains a mini-simulator based on the open-source discrete-event simulation framework
-[asynchronix](https://github.com/asynchronics/asynchronix).
+[nexosim](https://github.com/asynchronics/nexosim).
Right now, this crate is primarily used together with the
[`satrs-example` application](https://egit.irs.uni-stuttgart.de/rust/sat-rs/src/branch/main/satrs-example)
@@ -29,4 +29,4 @@ still have similar device handlers.
The following graph shows the high-level architecture of the mini-simulator.
-
+
diff --git a/satrs-example/minisim/src/acs/mgm.rs b/satrs-example/minisim/src/acs/mgm.rs
index 095c139..4684f66 100644
--- a/satrs-example/minisim/src/acs/mgm.rs
+++ b/satrs-example/minisim/src/acs/mgm.rs
@@ -70,6 +70,10 @@ impl MgmModel {
self.external_mag_field = Some(field);
}
+ pub async fn clear_external_magnetic_field(&mut self, _: ()) {
+ self.external_mag_field = None;
+ }
+
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 {
diff --git a/satrs-example/minisim/src/acs/mgt.rs b/satrs-example/minisim/src/acs/mgt.rs
index fe97297..dbd9b96 100644
--- a/satrs-example/minisim/src/acs/mgt.rs
+++ b/satrs-example/minisim/src/acs/mgt.rs
@@ -15,6 +15,7 @@ pub struct MgtModel {
torquing: bool,
torque_dipole: mgt::Dipole,
pub gen_magnetic_field: Output,
+ pub clear_magnetic_field: Output<()>,
reply_sender: mpsc::Sender,
}
@@ -25,6 +26,7 @@ impl MgtModel {
torquing: false,
torque_dipole: mgt::Dipole::default(),
gen_magnetic_field: Output::new(),
+ clear_magnetic_field: Output::new(),
reply_sender,
}
}
@@ -48,12 +50,15 @@ impl MgtModel {
pub async fn clear_torque(&mut self, _: ()) {
self.torque_dipole = mgt::Dipole::default();
self.torquing = false;
- self.generate_magnetic_field(()).await;
+ self.clear_magnetic_field.send(()).await;
}
pub async fn switch_device(&mut self, switch_state: SwitchStateBinary) {
self.switch_state = switch_state;
- self.generate_magnetic_field(()).await;
+ match switch_state {
+ SwitchStateBinary::On => self.generate_magnetic_field(()).await,
+ SwitchStateBinary::Off => self.clear_torque(()).await,
+ }
}
pub async fn request_housekeeping_data(&mut self, _: (), cx: &mut Context) {
@@ -97,8 +102,12 @@ impl Model for MgtModel {}
mod tests {
use std::time::Duration;
- use satrs_minisim::{acs::mgt, SimReply, SimRequestWithTime};
- use types::pcdu::SwitchId;
+ use satrs_minisim::{
+ acs::{mgm, mgt},
+ eps::PcduRequest,
+ SimReply, SimRequest, SimRequestWithTime,
+ };
+ use types::pcdu::{SwitchId, SwitchStateBinary};
use crate::{eps::tests::switch_device_on, test_helpers::SimTestbench};
@@ -166,4 +175,73 @@ mod tests {
})
);
}
+
+ /// Processes the request without stepping, so scheduled events like the torque clearing do
+ /// not fire.
+ fn process_without_step(sim_testbench: &mut SimTestbench, request: impl Into) {
+ sim_testbench
+ .send_request(SimRequestWithTime::new_with_epoch_time(request))
+ .expect("sending request failed");
+ sim_testbench.handle_sim_requests_time_agnostic();
+ }
+
+ fn read_mgm_0_field(sim_testbench: &mut SimTestbench) -> mgm::SensorValuesMicroTesla {
+ process_without_step(
+ sim_testbench,
+ SimRequest::Mgm {
+ id: mgm::Id::Mgm0,
+ request: mgm::Request::RequestSensorData,
+ },
+ );
+ let sim_reply = sim_testbench
+ .try_receive_next_reply()
+ .expect("no MGM reply received");
+ let SimReply::Mgm { reply, .. } = sim_reply else {
+ panic!("unexpected reply {sim_reply:?}");
+ };
+ reply.sensor_values
+ }
+
+ fn start_torquing(sim_testbench: &mut SimTestbench, duration: Duration) {
+ switch_device_on(sim_testbench, SwitchId::Mgm0);
+ switch_device_on(sim_testbench, SwitchId::Mgt);
+ process_without_step(
+ sim_testbench,
+ mgt::Request::ApplyTorque {
+ duration,
+ dipole: mgt::Dipole { x: 1, y: 2, z: 3 },
+ },
+ );
+ assert_eq!(read_mgm_0_field(sim_testbench), mgm::MGT_GEN_MAGNETIC_FIELD);
+ }
+
+ #[test]
+ fn test_mgm_field_cleared_after_torquing() {
+ let mut sim_testbench = SimTestbench::new();
+ start_torquing(&mut sim_testbench, Duration::from_millis(100));
+ sim_testbench
+ .step_until(Duration::from_millis(100))
+ .unwrap();
+ assert_ne!(
+ read_mgm_0_field(&mut sim_testbench),
+ mgm::MGT_GEN_MAGNETIC_FIELD
+ );
+ }
+
+ #[test]
+ fn test_mgm_field_cleared_by_switching_mgt_off() {
+ let mut sim_testbench = SimTestbench::new();
+ start_torquing(&mut sim_testbench, Duration::from_millis(100));
+ process_without_step(
+ &mut sim_testbench,
+ PcduRequest::SwitchDevice {
+ switch: SwitchId::Mgt,
+ state: SwitchStateBinary::Off,
+ },
+ );
+ assert_ne!(
+ read_mgm_0_field(&mut sim_testbench),
+ mgm::MGT_GEN_MAGNETIC_FIELD
+ );
+ }
}
diff --git a/satrs-example/minisim/src/controller.rs b/satrs-example/minisim/src/controller.rs
index 8c6eeb5..80cdd66 100644
--- a/satrs-example/minisim/src/controller.rs
+++ b/satrs-example/minisim/src/controller.rs
@@ -85,6 +85,12 @@ impl SimController {
mgt_model
.gen_magnetic_field
.connect(MgmModel::apply_external_magnetic_field, &addrs.mgm_1);
+ mgt_model
+ .clear_magnetic_field
+ .connect(MgmModel::clear_external_magnetic_field, &addrs.mgm_0);
+ mgt_model
+ .clear_magnetic_field
+ .connect(MgmModel::clear_external_magnetic_field, &addrs.mgm_1);
let sim_init = if threading_model == ThreadingModel::Single {
SimInit::with_num_threads(1)
diff --git a/satrs-example/src/acs/mgt.rs b/satrs-example/src/acs/mgt.rs
index 4ab7af7..2044ef4 100644
--- a/satrs-example/src/acs/mgt.rs
+++ b/satrs-example/src/acs/mgt.rs
@@ -18,7 +18,7 @@ use crate::ccsds::pack_ccsds_tm_packet_for_now;
use crate::device_mode::{ModeTransitionEvent, SwitchAndModeHelper};
use crate::eps::PowerSwitchHelper;
-/// Ideal device which never fails.
+/// Interface for ideal device which never fails.
#[derive(Default)]
pub struct DummyInterface {
dipole: sim_mgt::Dipole,