diff --git a/satrs-example/satrs-tmtc/pus_tc.py b/satrs-example/satrs-tmtc/pus_tc.py index f73b755..0a1b03a 100644 --- a/satrs-example/satrs-tmtc/pus_tc.py +++ b/satrs-example/satrs-tmtc/pus_tc.py @@ -22,6 +22,25 @@ def create_cmd_definition_tree() -> CmdTreeNode: root_node = CmdTreeNode.root_node() + hk_node = CmdTreeNode("hk", "Housekeeping Node", hide_children_for_print=True) + hk_node.add_child(CmdTreeNode("one_shot_hk", "Request One Shot HK set")) + hk_node.add_child( + CmdTreeNode("enable", "Enable periodic housekeeping data generation") + ) + hk_node.add_child( + CmdTreeNode("disable", "Disable periodic housekeeping data generation") + ) + + mode_node = CmdTreeNode("mode", "Mode Node", hide_children_for_print=True) + set_mode_node = CmdTreeNode( + "set_mode", "Set Node", hide_children_which_are_leaves=True + ) + set_mode_node.add_child(CmdTreeNode("off", "Set OFF Mode")) + set_mode_node.add_child(CmdTreeNode("on", "Set ON Mode")) + set_mode_node.add_child(CmdTreeNode("normal", "Set NORMAL Mode")) + mode_node.add_child(set_mode_node) + mode_node.add_child(CmdTreeNode("read_mode", "Read Mode")) + test_node = CmdTreeNode("test", "Test Node") test_node.add_child(CmdTreeNode("ping", "Send PUS ping TC")) test_node.add_child(CmdTreeNode("trigger_event", "Send PUS test to trigger event")) @@ -37,7 +56,9 @@ def create_cmd_definition_tree() -> CmdTreeNode: acs_node = CmdTreeNode("acs", "ACS Subsystem Node") mgm_node = CmdTreeNode("mgms", "MGM devices node") - mgm_node.add_child(CmdTreeNode("one_shot_hk", "Request one shot HK")) + mgm_node.add_child(mode_node) + mgm_node.add_child(hk_node) + acs_node.add_child(mgm_node) root_node.add_child(acs_node) diff --git a/satrs-example/src/acs/mgm.rs b/satrs-example/src/acs/mgm.rs index d0729c6..e399f81 100644 --- a/satrs-example/src/acs/mgm.rs +++ b/satrs-example/src/acs/mgm.rs @@ -4,7 +4,7 @@ use satrs::queue::{GenericSendError, GenericTargetedMessagingError}; use satrs::spacepackets::ecss::hk; use satrs::spacepackets::ecss::tm::{PusTmCreator, PusTmSecondaryHeader}; use satrs::spacepackets::SpHeader; -use satrs_example::TimeStampHelper; +use satrs_example::{DeviceMode, TimeStampHelper}; use std::sync::mpsc::{self}; use std::sync::{Arc, Mutex}; @@ -20,6 +20,10 @@ use crate::requests::CompositeRequest; use serde::{Deserialize, Serialize}; +const GAUSS_TO_MICROTESLA_FACTOR: f32 = 100.0; +// This is the selected resoltion for the STM LIS3MDL device for the 4 Gauss sensitivity setting. +const FIELD_LSB_PER_GAUSS_4_SENS: f32 = 1.0 / 6842.0; + pub trait SpiInterface { type Error; fn transfer(&mut self, tx: &[u8], rx: &mut [u8]) -> Result<(), Self::Error>; @@ -27,9 +31,9 @@ pub trait SpiInterface { #[derive(Default)] pub struct SpiDummyInterface { - dummy_val_0: i16, - dummy_val_1: i16, - dummy_val_2: i16, + pub dummy_val_0: i16, + pub dummy_val_1: i16, + pub dummy_val_2: i16, } impl SpiInterface for SpiDummyInterface { @@ -45,6 +49,7 @@ impl SpiInterface for SpiDummyInterface { #[derive(Default, Debug, Copy, Clone, Serialize, Deserialize)] pub struct MgmData { + pub valid: bool, pub x: f32, pub y: f32, pub z: f32, @@ -56,9 +61,10 @@ pub struct MpscModeLeafInterface { pub reply_tx_to_parent: mpsc::Sender>, } +/// Example MGM device handler strongly based on the LIS3MDL MEMS device. #[derive(new)] #[allow(clippy::too_many_arguments)] -pub struct MgmHandler { +pub struct MgmHandlerLis3Mdl { id: UniqueApidTargetId, dev_str: &'static str, mode_interface: MpscModeLeafInterface, @@ -68,26 +74,41 @@ pub struct MgmHandler { com_interface: ComInterface, shared_mgm_set: Arc>, #[new(value = "ModeAndSubmode::new(satrs_example::DeviceMode::Off as u32, 0)")] - mode: ModeAndSubmode, + mode_and_submode: ModeAndSubmode, #[new(default)] tx_buf: [u8; 12], #[new(default)] rx_buf: [u8; 12], #[new(default)] - tm_buf: [u8; 12], + tm_buf: [u8; 16], + #[new(default)] stamp_helper: TimeStampHelper, } -impl MgmHandler { +impl + MgmHandlerLis3Mdl +{ pub fn periodic_operation(&mut self) { self.stamp_helper.update_from_now(); // Handle requests. self.handle_composite_requests(); self.handle_mode_requests(); - // Communicate with the device. - let result = self.com_interface.transfer(&self.tx_buf, &mut self.rx_buf); - assert!(result.is_ok()); - // TODO: Convert the raw data back to floats. + if self.mode() == DeviceMode::Normal as u32 { + log::trace!("polling LIS3MDL sensor {}", self.dev_str); + // Communicate with the device. + let result = self.com_interface.transfer(&self.tx_buf, &mut self.rx_buf); + assert!(result.is_ok()); + // Actual data begins on the second byte, similarly to how a lot of SPI devices behave. + let x_raw = i16::from_be_bytes(self.rx_buf[1..3].try_into().unwrap()); + let y_raw = i16::from_be_bytes(self.rx_buf[3..5].try_into().unwrap()); + let z_raw = i16::from_be_bytes(self.rx_buf[5..7].try_into().unwrap()); + // Simple scaling to retrieve the float value, assuming a sensor resolution of + let mut mgm_guard = self.shared_mgm_set.lock().unwrap(); + mgm_guard.x = x_raw as f32 * GAUSS_TO_MICROTESLA_FACTOR * FIELD_LSB_PER_GAUSS_4_SENS; + mgm_guard.y = y_raw as f32 * GAUSS_TO_MICROTESLA_FACTOR * FIELD_LSB_PER_GAUSS_4_SENS; + mgm_guard.z = z_raw as f32 * GAUSS_TO_MICROTESLA_FACTOR * FIELD_LSB_PER_GAUSS_4_SENS; + drop(mgm_guard); + } } pub fn handle_composite_requests(&mut self) { @@ -134,13 +155,14 @@ impl MgmHandler MgmHandler ModeProvider - for MgmHandler + for MgmHandlerLis3Mdl { fn mode_and_submode(&self) -> ModeAndSubmode { - self.mode + self.mode_and_submode } } impl ModeRequestHandler - for MgmHandler + for MgmHandlerLis3Mdl { type Error = ModeError; fn start_transition( @@ -195,14 +217,22 @@ impl ModeRequestHandler requestor: MessageMetadata, mode_and_submode: ModeAndSubmode, ) -> Result<(), satrs::mode::ModeError> { - self.mode = mode_and_submode; + log::info!( + "{}: transitioning to mode {:?}", + self.dev_str, + mode_and_submode + ); + self.mode_and_submode = mode_and_submode; self.handle_mode_reached(Some(requestor))?; Ok(()) } fn announce_mode(&self, _requestor_info: Option, _recursive: bool) { - log::info!("{} announcing mode: {:?}", self.dev_str, self.mode); - // TODO: Trigger event. + log::info!( + "{} announcing mode: {:?}", + self.dev_str, + self.mode_and_submode + ); } fn handle_mode_reached( diff --git a/satrs-example/src/main.rs b/satrs-example/src/main.rs index dc6223c..ae315e5 100644 --- a/satrs-example/src/main.rs +++ b/satrs-example/src/main.rs @@ -27,7 +27,7 @@ use satrs_example::config::{OBSW_SERVER_ADDR, SERVER_PORT}; use tmtc::PusTcSourceProviderDynamic; use udp::DynamicUdpTmHandler; -use crate::acs::mgm::{MgmHandler, MpscModeLeafInterface, SpiDummyInterface}; +use crate::acs::mgm::{MgmHandlerLis3Mdl, MpscModeLeafInterface, SpiDummyInterface}; use crate::ccsds::CcsdsReceiver; use crate::logger::setup_logger; use crate::pus::action::{create_action_service_dynamic, create_action_service_static}; @@ -211,7 +211,7 @@ fn static_tmtc_pool_main() { reply_tx_to_pus: pus_mode_reply_tx, reply_tx_to_parent: mgm_handler_mode_reply_to_parent_tx, }; - let mut mgm_handler = MgmHandler::new( + let mut mgm_handler = MgmHandlerLis3Mdl::new( MGM_HANDLER_0, "MGM_0", mode_leaf_interface, @@ -429,7 +429,7 @@ fn dyn_tmtc_pool_main() { reply_tx_to_pus: pus_mode_reply_tx, reply_tx_to_parent: mgm_handler_mode_reply_to_parent_tx, }; - let mut mgm_handler = MgmHandler::new( + let mut mgm_handler = MgmHandlerLis3Mdl::new( MGM_HANDLER_0, "MGM_0", mode_leaf_interface, diff --git a/satrs/src/mode.rs b/satrs/src/mode.rs index df2590e..65519a5 100644 --- a/satrs/src/mode.rs +++ b/satrs/src/mode.rs @@ -179,6 +179,14 @@ impl From for ModeError { pub trait ModeProvider { fn mode_and_submode(&self) -> ModeAndSubmode; + + fn mode(&self) -> Mode { + self.mode_and_submode().mode() + } + + fn submode(&self) -> Submode { + self.mode_and_submode().submode() + } } pub trait ModeRequestHandler: ModeProvider {