smaller fixes

This commit is contained in:
Robin Mueller
2026-09-24 16:14:23 +02:00
parent 7c9150676d
commit 4c2da7cc50
5 changed files with 95 additions and 7 deletions
+2 -2
View File
@@ -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.
<img src="../images/minisim-arch/minisim-arch.png" alt="Mini simulator architecture" width="500" class="center"/>
<img src="../../images/minisim-arch/minisim-arch.png" alt="Mini simulator architecture" width="500" class="center"/>
+4
View File
@@ -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 {
+82 -4
View File
@@ -15,6 +15,7 @@ pub struct MgtModel {
torquing: bool,
torque_dipole: mgt::Dipole,
pub gen_magnetic_field: Output<mgm::SensorValuesMicroTesla>,
pub clear_magnetic_field: Output<()>,
reply_sender: mpsc::Sender<SimReply>,
}
@@ -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<Self>) {
@@ -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<SimRequest>) {
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
);
}
}
+6
View File
@@ -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)
+1 -1
View File
@@ -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,