diff --git a/FprimeZephyrReference/Components/CMakeLists.txt b/FprimeZephyrReference/Components/CMakeLists.txt index 10c9dad2..79100334 100644 --- a/FprimeZephyrReference/Components/CMakeLists.txt +++ b/FprimeZephyrReference/Components/CMakeLists.txt @@ -1,4 +1,6 @@ # Include project-wide components here +add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/Drv/") add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/FatalHandler") +add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/ImuManager/") add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/Watchdog") diff --git a/FprimeZephyrReference/Components/Drv/CMakeLists.txt b/FprimeZephyrReference/Components/Drv/CMakeLists.txt new file mode 100644 index 00000000..cfcd92d7 --- /dev/null +++ b/FprimeZephyrReference/Components/Drv/CMakeLists.txt @@ -0,0 +1,5 @@ +# Include project-wide components here +add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/Helpers/") +add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/Lis2mdlManager/") +add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/Lsm6dsoManager/") +add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/Types/") diff --git a/FprimeZephyrReference/Components/Drv/Helpers/CMakeLists.txt b/FprimeZephyrReference/Components/Drv/Helpers/CMakeLists.txt new file mode 100644 index 00000000..5bac4cd6 --- /dev/null +++ b/FprimeZephyrReference/Components/Drv/Helpers/CMakeLists.txt @@ -0,0 +1,34 @@ +#### +# F Prime CMakeLists.txt: +# +# SOURCES: list of source files (to be compiled) +# AUTOCODER_INPUTS: list of files to be passed to the autocoders +# DEPENDS: list of libraries that this module depends on +# +# More information in the F´ CMake API documentation: +# https://fprime.jpl.nasa.gov/latest/docs/reference/api/cmake/API/ +# +#### + +# Module names are derived from the path from the nearest project/library/framework +# root when not specifically overridden by the developer. i.e. The module defined by +# `Ref/SignalGen/CMakeLists.txt` will be named `Ref_SignalGen`. + +register_fprime_library( + SOURCES + "${CMAKE_CURRENT_LIST_DIR}/Helpers.cpp" + DEPENDS + Fw_Types +) + +### Unit Tests ### +# register_fprime_ut( +# AUTOCODER_INPUTS +# "${CMAKE_CURRENT_LIST_DIR}/Helpers.fpp" +# SOURCES +# "${CMAKE_CURRENT_LIST_DIR}/test/ut/HelpersTestMain.cpp" +# "${CMAKE_CURRENT_LIST_DIR}/test/ut/HelpersTester.cpp" +# DEPENDS +# STest # For rules-based testing +# UT_AUTO_HELPERS +# ) diff --git a/FprimeZephyrReference/Components/Drv/Helpers/Helpers.cpp b/FprimeZephyrReference/Components/Drv/Helpers/Helpers.cpp new file mode 100644 index 00000000..fac9e1f0 --- /dev/null +++ b/FprimeZephyrReference/Components/Drv/Helpers/Helpers.cpp @@ -0,0 +1,16 @@ +// ====================================================================== +// \title Helpers.cpp +// \brief cpp file for Helpers component implementation class +// ====================================================================== + +#include "FprimeZephyrReference/Components/Drv/Helpers/Helpers.hpp" + +#include + +namespace Drv { + +F64 sensor_value_to_f64(const struct sensor_value& val) { + return val.val1 + (val.val2 / 1000000.0f); +} + +} // namespace Drv diff --git a/FprimeZephyrReference/Components/Drv/Helpers/Helpers.hpp b/FprimeZephyrReference/Components/Drv/Helpers/Helpers.hpp new file mode 100644 index 00000000..02c9325d --- /dev/null +++ b/FprimeZephyrReference/Components/Drv/Helpers/Helpers.hpp @@ -0,0 +1,19 @@ +// ====================================================================== +// \title Helpers.hpp +// \brief hpp file for Helpers component implementation class +// ====================================================================== + +#ifndef Components_Helpers_HPP +#define Components_Helpers_HPP + +#include +#include + +namespace Drv { + +//! Convert a Zephyr sensor_value to an Fprime F64 +F64 sensor_value_to_f64(const struct sensor_value& val); + +} // namespace Drv + +#endif diff --git a/FprimeZephyrReference/Components/Drv/Lis2mdlManager/CMakeLists.txt b/FprimeZephyrReference/Components/Drv/Lis2mdlManager/CMakeLists.txt new file mode 100644 index 00000000..1bdafd80 --- /dev/null +++ b/FprimeZephyrReference/Components/Drv/Lis2mdlManager/CMakeLists.txt @@ -0,0 +1,36 @@ +#### +# F Prime CMakeLists.txt: +# +# SOURCES: list of source files (to be compiled) +# AUTOCODER_INPUTS: list of files to be passed to the autocoders +# DEPENDS: list of libraries that this module depends on +# +# More information in the F´ CMake API documentation: +# https://fprime.jpl.nasa.gov/latest/docs/reference/api/cmake/API/ +# +#### + +# Module names are derived from the path from the nearest project/library/framework +# root when not specifically overridden by the developer. i.e. The module defined by +# `Ref/SignalGen/CMakeLists.txt` will be named `Ref_SignalGen`. + +register_fprime_library( + AUTOCODER_INPUTS + "${CMAKE_CURRENT_LIST_DIR}/Lis2mdlManager.fpp" + SOURCES + "${CMAKE_CURRENT_LIST_DIR}/Lis2mdlManager.cpp" + DEPENDS + FprimeZephyrReference_Components_Drv_Helpers +) + +### Unit Tests ### +# register_fprime_ut( +# AUTOCODER_INPUTS +# "${CMAKE_CURRENT_LIST_DIR}/Lis2mdlManager.fpp" +# SOURCES +# "${CMAKE_CURRENT_LIST_DIR}/test/ut/Lis2mdlManagerTestMain.cpp" +# "${CMAKE_CURRENT_LIST_DIR}/test/ut/Lis2mdlManagerTester.cpp" +# DEPENDS +# STest # For rules-based testing +# UT_AUTO_HELPERS +# ) diff --git a/FprimeZephyrReference/Components/Drv/Lis2mdlManager/Lis2mdlManager.cpp b/FprimeZephyrReference/Components/Drv/Lis2mdlManager/Lis2mdlManager.cpp new file mode 100644 index 00000000..e0b133f5 --- /dev/null +++ b/FprimeZephyrReference/Components/Drv/Lis2mdlManager/Lis2mdlManager.cpp @@ -0,0 +1,51 @@ +// ====================================================================== +// \title Lis2mdlManager.cpp +// \brief cpp file for Lis2mdlManager component implementation class +// ====================================================================== + +#include "FprimeZephyrReference/Components/Drv/Lis2mdlManager/Lis2mdlManager.hpp" + +#include + +namespace Drv { + +// ---------------------------------------------------------------------- +// Component construction and destruction +// ---------------------------------------------------------------------- + +Lis2mdlManager ::Lis2mdlManager(const char* const compName) : Lis2mdlManagerComponentBase(compName) { + dev = device_get_binding("LIS2MDL"); +} + +Lis2mdlManager ::~Lis2mdlManager() {} + +// ---------------------------------------------------------------------- +// Handler implementations for typed input ports +// ---------------------------------------------------------------------- + +Drv::MagneticField Lis2mdlManager ::magneticFieldGet_handler(FwIndexType portNum) { + if (!device_is_ready(dev)) { + this->log_WARNING_HI_DeviceNotReady(); + return Drv::MagneticField(0.0, 0.0, 0.0); + } + this->log_WARNING_HI_DeviceNotReady_ThrottleClear(); + + struct sensor_value x; + struct sensor_value y; + struct sensor_value z; + + sensor_sample_fetch_chan(dev, SENSOR_CHAN_MAGN_XYZ); + + sensor_channel_get(dev, SENSOR_CHAN_MAGN_X, &x); + sensor_channel_get(dev, SENSOR_CHAN_MAGN_Y, &y); + sensor_channel_get(dev, SENSOR_CHAN_MAGN_Z, &z); + + Drv::MagneticField magnetic_readings = + Drv::MagneticField(Drv::sensor_value_to_f64(x), Drv::sensor_value_to_f64(y), Drv::sensor_value_to_f64(z)); + + this->tlmWrite_MagneticField(magnetic_readings); + + return magnetic_readings; +} + +} // namespace Drv diff --git a/FprimeZephyrReference/Components/Drv/Lis2mdlManager/Lis2mdlManager.fpp b/FprimeZephyrReference/Components/Drv/Lis2mdlManager/Lis2mdlManager.fpp new file mode 100644 index 00000000..84a1ac56 --- /dev/null +++ b/FprimeZephyrReference/Components/Drv/Lis2mdlManager/Lis2mdlManager.fpp @@ -0,0 +1,35 @@ +# Port definition +module Drv { + port MagneticFieldGet -> MagneticField +} + +# Component definition +module Drv { + @ LIS2MDL Manager Component for F Prime FSW framework. + passive component Lis2mdlManager { + @ Port to read the current magnetic field in gauss. + sync input port magneticFieldGet: MagneticFieldGet + + @ Event for reporting LSM6DSO not ready error + event DeviceNotReady() severity warning high format "LIS2MDL device not ready" throttle 5 + + @ Telemetry channel for magnetic field in gauss + telemetry MagneticField: MagneticField + + + ############################################################################### + # Standard AC Ports: Required for Channels, Events, Commands, and Parameters # + ############################################################################### + @ Port for requesting the current time + time get port timeCaller + + @ Port for sending textual representation of events + text event port logTextOut + + @ Port for sending events to downlink + event port logOut + + @ Port for sending telemetry channels to downlink + telemetry port tlmOut + } +} diff --git a/FprimeZephyrReference/Components/Drv/Lis2mdlManager/Lis2mdlManager.hpp b/FprimeZephyrReference/Components/Drv/Lis2mdlManager/Lis2mdlManager.hpp new file mode 100644 index 00000000..34f5de33 --- /dev/null +++ b/FprimeZephyrReference/Components/Drv/Lis2mdlManager/Lis2mdlManager.hpp @@ -0,0 +1,52 @@ +// ====================================================================== +// \title Lis2mdlManager.hpp +// \brief hpp file for Lis2mdlManager component implementation class +// ====================================================================== + +#ifndef Components_Lis2mdlManager_HPP +#define Components_Lis2mdlManager_HPP + +// clang-format off +// Keep the includes in this order +#include "FprimeZephyrReference/Components/Drv/Lis2mdlManager/Lis2mdlManagerComponentAc.hpp" +#include "FprimeZephyrReference/Components/Drv/Helpers/Helpers.hpp" +// clang-format on + +#include +#include +#include + +namespace Drv { + +class Lis2mdlManager final : public Lis2mdlManagerComponentBase { + public: + // ---------------------------------------------------------------------- + // Component construction and destruction + // ---------------------------------------------------------------------- + + //! Construct Lis2mdlManager object + Lis2mdlManager(const char* const compName); + + //! Destroy Lis2mdlManager object + ~Lis2mdlManager(); + + private: + // ---------------------------------------------------------------------- + // Handler implementations for typed input ports + // ---------------------------------------------------------------------- + + //! Get the magnetic field reading from the LIS2MDL sensor + Drv::MagneticField magneticFieldGet_handler(const FwIndexType portNum //!< The port number + ) override; + + // ---------------------------------------------------------------------- + // Member variables + // ---------------------------------------------------------------------- + + //! Zephyr device stores the initialized LIS2MDL sensor + const struct device* dev; +}; + +} // namespace Drv + +#endif diff --git a/FprimeZephyrReference/Components/Drv/Lis2mdlManager/docs/sdd.md b/FprimeZephyrReference/Components/Drv/Lis2mdlManager/docs/sdd.md new file mode 100644 index 00000000..45346137 --- /dev/null +++ b/FprimeZephyrReference/Components/Drv/Lis2mdlManager/docs/sdd.md @@ -0,0 +1,69 @@ +# Drv::Lis2mdlManager + +The LIS2MDL Manager component interfaces with the LIS2MDL magnetometer to provide magnetic field measurements. + +## Usage Examples + +The LIS2MDL Manager component is designed to be called periodically to collect and return sensor data. It operates as a passive component that responds to manager calls. + +### Typical Usage + +1. The component is instantiated and initialized during system startup +2. A manager calls the `MagneticFieldGet` port +3. On each call, the component: + - Fetches fresh sensor samples from the sensor + - Converts sensor data to F Prime data structures + - Writes telemetry data + - Returns data in SI units + +## Class Diagram + +```mermaid +classDiagram + namespace Drv { + class Lis2mdlManagerComponentBase { + <> + } + class Lis2mdlManager { + - lis2mdl: const struct device* + + Lis2mdlManager(const char* compName) + + ~Lis2mdlManager() + - magneticFieldGet_handler(const FwIndexType portNum): Drv::MagneticField + } + } + Lis2mdlManagerComponentBase <|-- Lis2mdlManager : inherits +``` + +## Port Descriptions +| Name | Type | Description | +|---|---|---| +| MagneticFieldGet | sync input | Triggers magnetic field data collection and returns a MagneticField struct | + +## Sequence Diagrams + +```mermaid +sequenceDiagram + participant Manager + participant LIS2MDL Manager + participant Zephyr Sensor API + participant LIS2MDL Sensor + + Manager-->>LIS2MDL Manager: Call MagneticFieldGet synchronous input port + LIS2MDL Manager->>Zephyr Sensor API: Fetch sensor data + Zephyr Sensor API->>LIS2MDL Sensor: Read sensor + LIS2MDL Sensor->>Zephyr Sensor API: Return sensor data + Zephyr Sensor API->>LIS2MDL Manager: Return x, y, z sensor_value structs + LIS2MDL Manager->>LIS2MDL Manager: Write telemetry + LIS2MDL Manager-->>Manager: Return MagneticField struct +``` + +## Requirements +Add requirements in the chart below +| Name | Description | Validation | +|---|---|---| +| MagneticFieldGet Port | The component shall provide access magnetic field sensor data and return in MagneticField struct, readings will be in gauss | Verify output matches expected values from sensor datasheet | + +## Change Log +| Date | Description | +|---|---| +| 2025-9-15 | Initial LIS2MDL Manager component | diff --git a/FprimeZephyrReference/Components/Drv/Lsm6dsoManager/CMakeLists.txt b/FprimeZephyrReference/Components/Drv/Lsm6dsoManager/CMakeLists.txt new file mode 100644 index 00000000..9ec411e3 --- /dev/null +++ b/FprimeZephyrReference/Components/Drv/Lsm6dsoManager/CMakeLists.txt @@ -0,0 +1,36 @@ +#### +# F Prime CMakeLists.txt: +# +# SOURCES: list of source files (to be compiled) +# AUTOCODER_INPUTS: list of files to be passed to the autocoders +# DEPENDS: list of libraries that this module depends on +# +# More information in the F´ CMake API documentation: +# https://fprime.jpl.nasa.gov/latest/docs/reference/api/cmake/API/ +# +#### + +# Module names are derived from the path from the nearest project/library/framework +# root when not specifically overridden by the developer. i.e. The module defined by +# `Ref/SignalGen/CMakeLists.txt` will be named `Ref_SignalGen`. + +register_fprime_library( + AUTOCODER_INPUTS + "${CMAKE_CURRENT_LIST_DIR}/Lsm6dsoManager.fpp" + SOURCES + "${CMAKE_CURRENT_LIST_DIR}/Lsm6dsoManager.cpp" + DEPENDS + FprimeZephyrReference_Components_Drv_Helpers +) + +### Unit Tests ### +# register_fprime_ut( +# AUTOCODER_INPUTS +# "${CMAKE_CURRENT_LIST_DIR}/Lsm6dsoManager.fpp" +# SOURCES +# "${CMAKE_CURRENT_LIST_DIR}/test/ut/Lsm6dsoManagerTestMain.cpp" +# "${CMAKE_CURRENT_LIST_DIR}/test/ut/Lsm6dsoManagerTester.cpp" +# DEPENDS +# STest # For rules-based testing +# UT_AUTO_HELPERS +# ) diff --git a/FprimeZephyrReference/Components/Drv/Lsm6dsoManager/Lsm6dsoManager.cpp b/FprimeZephyrReference/Components/Drv/Lsm6dsoManager/Lsm6dsoManager.cpp new file mode 100644 index 00000000..a0d219c8 --- /dev/null +++ b/FprimeZephyrReference/Components/Drv/Lsm6dsoManager/Lsm6dsoManager.cpp @@ -0,0 +1,103 @@ +// ====================================================================== +// \title Lsm6dsoManager.cpp +// \brief cpp file for Lsm6dsoManager component implementation class +// ====================================================================== + +#include "FprimeZephyrReference/Components/Drv/Lsm6dsoManager/Lsm6dsoManager.hpp" + +#include + +namespace Drv { + +// ---------------------------------------------------------------------- +// Component construction and destruction +// ---------------------------------------------------------------------- + +Lsm6dsoManager ::Lsm6dsoManager(const char* const compName) : Lsm6dsoManagerComponentBase(compName) { + dev = DEVICE_DT_GET_ONE(st_lsm6dso); + + // Configure the sensor + struct sensor_value odr = {.val1 = 12, .val2 = 500000}; // 12.5 Hz + + if (sensor_attr_set(dev, SENSOR_CHAN_ACCEL_XYZ, SENSOR_ATTR_SAMPLING_FREQUENCY, &odr) != 0) { + this->log_WARNING_HI_AccelerometerSamplingFrequencyNotConfigured(); + } + + if (sensor_attr_set(dev, SENSOR_CHAN_GYRO_XYZ, SENSOR_ATTR_SAMPLING_FREQUENCY, &odr) != 0) { + this->log_WARNING_HI_GyroscopeSamplingFrequencyNotConfigured(); + } +} + +Lsm6dsoManager ::~Lsm6dsoManager() {} + +// ---------------------------------------------------------------------- +// Handler implementations for typed input ports +// ---------------------------------------------------------------------- + +Drv::Acceleration Lsm6dsoManager ::accelerationGet_handler(FwIndexType portNum) { + if (!device_is_ready(dev)) { + this->log_WARNING_HI_DeviceNotReady(); + return Drv::Acceleration(0.0, 0.0, 0.0); + } + this->log_WARNING_HI_DeviceNotReady_ThrottleClear(); + + struct sensor_value x; + struct sensor_value y; + struct sensor_value z; + + sensor_sample_fetch_chan(dev, SENSOR_CHAN_ACCEL_XYZ); + + sensor_channel_get(dev, SENSOR_CHAN_ACCEL_X, &x); + sensor_channel_get(dev, SENSOR_CHAN_ACCEL_Y, &y); + sensor_channel_get(dev, SENSOR_CHAN_ACCEL_Z, &z); + + Drv::Acceleration acceleration = + Drv::Acceleration(Drv::sensor_value_to_f64(x), Drv::sensor_value_to_f64(y), Drv::sensor_value_to_f64(z)); + + this->tlmWrite_Acceleration(acceleration); + return acceleration; +} + +Drv::AngularVelocity Lsm6dsoManager ::angularVelocityGet_handler(FwIndexType portNum) { + if (!device_is_ready(dev)) { + this->log_WARNING_HI_DeviceNotReady(); + return Drv::AngularVelocity(0.0, 0.0, 0.0); + } + this->log_WARNING_HI_DeviceNotReady_ThrottleClear(); + + struct sensor_value x; + struct sensor_value y; + struct sensor_value z; + + sensor_sample_fetch_chan(dev, SENSOR_CHAN_GYRO_XYZ); + + sensor_channel_get(dev, SENSOR_CHAN_GYRO_X, &x); + sensor_channel_get(dev, SENSOR_CHAN_GYRO_Y, &y); + sensor_channel_get(dev, SENSOR_CHAN_GYRO_Z, &z); + + Drv::AngularVelocity angular_velocity = + Drv::AngularVelocity(Drv::sensor_value_to_f64(x), Drv::sensor_value_to_f64(y), Drv::sensor_value_to_f64(z)); + + this->tlmWrite_AngularVelocity(angular_velocity); + return angular_velocity; +} + +F64 Lsm6dsoManager ::temperatureGet_handler(FwIndexType portNum) { + if (!device_is_ready(dev)) { + this->log_WARNING_HI_DeviceNotReady(); + return 0; + } + this->log_WARNING_HI_DeviceNotReady_ThrottleClear(); + + struct sensor_value temp; + + sensor_sample_fetch_chan(dev, SENSOR_CHAN_DIE_TEMP); + + sensor_channel_get(dev, SENSOR_CHAN_DIE_TEMP, &temp); + + this->tlmWrite_Temperature(Drv::sensor_value_to_f64(temp)); + + return Drv::sensor_value_to_f64(temp); +} + +} // namespace Drv diff --git a/FprimeZephyrReference/Components/Drv/Lsm6dsoManager/Lsm6dsoManager.fpp b/FprimeZephyrReference/Components/Drv/Lsm6dsoManager/Lsm6dsoManager.fpp new file mode 100644 index 00000000..63d676e0 --- /dev/null +++ b/FprimeZephyrReference/Components/Drv/Lsm6dsoManager/Lsm6dsoManager.fpp @@ -0,0 +1,56 @@ +# Port definition +module Drv { + port AccelerationGet -> Acceleration + port AngularVelocityGet -> AngularVelocity + port TemperatureGet -> F64 +} + +# Component definition +module Drv { + @ LSM6DSO Driver Component for F Prime FSW framework. + passive component Lsm6dsoManager { + # Ports + @ Port to read the current acceleration in m/s^2. + sync input port accelerationGet: AccelerationGet + + @ Port to read the current angular velocity in rad/s. + sync input port angularVelocityGet: AngularVelocityGet + + @ Port to read the current temperature in degrees celsius. + sync input port temperatureGet: TemperatureGet + + # Telemetry channels + @ Telemetry channel for current acceleration in m/s^2. + telemetry Acceleration: Acceleration + + @ Telemetry channel for current angular velocity in rad/s. + telemetry AngularVelocity: AngularVelocity + + @ Telemetry channel for current temperature in degrees celsius. + telemetry Temperature: F64 + + @ Event for reporting LSM6DSO not ready error + event DeviceNotReady() severity warning high format "LSM6DSO device not ready" throttle 5 + + @ Event for reporting LSM6DSO accelerometer sampling frequency not configured error + event AccelerometerSamplingFrequencyNotConfigured() severity warning high format "LSM6DSO accelerometer sampling frequency not configured" + + @ Event for reporting LSM6DSO gyroscope sampling frequency not configured error + event GyroscopeSamplingFrequencyNotConfigured() severity warning high format "LSM6DSO gyroscope sampling frequency not configured" + + ############################################################################### + # Standard AC Ports: Required for Channels, Events, Commands, and Parameters # + ############################################################################### + @ Port for requesting the current time + time get port timeCaller + + @ Port for sending textual representation of events + text event port logTextOut + + @ Port for sending events to downlink + event port logOut + + @ Port for sending telemetry channels to downlink + telemetry port tlmOut + } +} diff --git a/FprimeZephyrReference/Components/Drv/Lsm6dsoManager/Lsm6dsoManager.hpp b/FprimeZephyrReference/Components/Drv/Lsm6dsoManager/Lsm6dsoManager.hpp new file mode 100644 index 00000000..2a15c39b --- /dev/null +++ b/FprimeZephyrReference/Components/Drv/Lsm6dsoManager/Lsm6dsoManager.hpp @@ -0,0 +1,60 @@ +// ====================================================================== +// \title Lsm6dsoManager.hpp +// \brief hpp file for Lsm6dsoManager component implementation class +// ====================================================================== + +#ifndef Components_Lsm6dsoManager_HPP +#define Components_Lsm6dsoManager_HPP + +// clang-format off +// Keep the includes in this order +#include "FprimeZephyrReference/Components/Drv/Lsm6dsoManager/Lsm6dsoManagerComponentAc.hpp" +#include "FprimeZephyrReference/Components/Drv/Helpers/Helpers.hpp" +// clang-format on + +#include +#include +#include + +namespace Drv { + +class Lsm6dsoManager final : public Lsm6dsoManagerComponentBase { + public: + // ---------------------------------------------------------------------- + // Component construction and destruction + // ---------------------------------------------------------------------- + + //! Construct Lsm6dsoManager object + Lsm6dsoManager(const char* const compName); + + //! Destroy Lsm6dsoManager object + ~Lsm6dsoManager(); + + private: + // ---------------------------------------------------------------------- + // Handler implementations for typed input ports + // ---------------------------------------------------------------------- + + //! Get the acceleration reading from the LSM6DSO sensor + Drv::Acceleration accelerationGet_handler(const FwIndexType portNum //!< The port number + ) override; + + //! Get the angular velocity reading from the LSM6DSO sensor + Drv::AngularVelocity angularVelocityGet_handler(const FwIndexType portNum //!< The port number + ) override; + + //! Get the temperature reading from the LSM6DSO sensor + F64 temperatureGet_handler(const FwIndexType portNum //!< The port number + ) override; + + // ---------------------------------------------------------------------- + // Member variables + // ---------------------------------------------------------------------- + + //! Zephyr device stores the initialized LSM6DSO sensor + const struct device* dev; +}; + +} // namespace Drv + +#endif diff --git a/FprimeZephyrReference/Components/Drv/Lsm6dsoManager/docs/sdd.md b/FprimeZephyrReference/Components/Drv/Lsm6dsoManager/docs/sdd.md new file mode 100644 index 00000000..efaf5fb9 --- /dev/null +++ b/FprimeZephyrReference/Components/Drv/Lsm6dsoManager/docs/sdd.md @@ -0,0 +1,109 @@ +# Drv::Lsm6dsoManager + +The LSM6DSO Manager component interfaces with the LSM6DSO sensor to provide acceleration, angular velocity, and temperature measurements. + +## Usage Examples + +The LSM6DSO Driver component is designed to be called periodically to collect and return sensor data. It operates as a passive component that responds to manager calls. + +### Typical Usage + +1. The component is instantiated and initialized during system startup +2. A manager calls any of the input ports: `AccelerationGet`, `AngularVelocityGet`, or `TemperatureGet` +3. On each call, the component: + - Fetches fresh sensor samples from the sensor + - Converts sensor data to F Prime data structures + - Writes telemetry data + - Returns data in SI units + +## Class Diagram +```mermaid +classDiagram + namespace Drv { + class Lsm6dsoManagerComponentBase { + <> + } + class Lsm6dsoManager { + - lsm6dso: const struct device* + + Lsm6dsoManager(const char* compName) + + ~Lsm6dsoManager() + - accelerationGet_handler(const FwIndexType portNum): Drv::Acceleration + - angularVelocityGet_handler(const FwIndexType portNum): Drv::AngularVelocity + - temperatureGet_handler(const FwIndexType portNum): F64 + } + } + Lsm6dsoManagerComponentBase <|-- Lsm6dsoManager : inherits +``` + +## Port Descriptions +| Name | Type | Description | +|---|---|---| +| AccelerationGet | sync input | Triggers acceleration data collection and returns an Acceleration struct | +| AngularVelocityGet | sync input | Triggers angular velocity data collection and returns an AngularVelocity struct | +| TemperatureGet | sync input | Triggers temperature data collection and returns temperature in degrees Celsius | + +## Sequence Diagrams + +### AccelerationGet +```mermaid +sequenceDiagram + participant Manager + participant LSM6DSO Manager + participant Zephyr Sensor API + participant LSM6DSO Sensor + + Manager-->>LSM6DSO Manager: Call the Acceleration synchronous input port + LSM6DSO Manager->>Zephyr Sensor API: Fetch sensor data + Zephyr Sensor API->>LSM6DSO Sensor: Read sensor + LSM6DSO Sensor->>Zephyr Sensor API: Return sensor data + Zephyr Sensor API->>LSM6DSO Manager: Return x, y, z sensor_value structs + LSM6DSO Manager->>LSM6DSO Manager: Write telemetry + LSM6DSO Manager-->>Manager: Return an Acceleration struct +``` + +### AngularVelocityGet +```mermaid +sequenceDiagram + participant Manager + participant LSM6DSO Manager + participant Zephyr Sensor API + participant LSM6DSO Sensor + + Manager-->>LSM6DSO Manager: Call the AngularVelocity synchronous input port + LSM6DSO Manager->>Zephyr Sensor API: Fetch sensor data + Zephyr Sensor API->>LSM6DSO Sensor: Read sensor + LSM6DSO Sensor->>Zephyr Sensor API: Return sensor data + Zephyr Sensor API->>LSM6DSO Manager: Return x, y, z sensor_value structs + LSM6DSO Manager->>LSM6DSO Manager: Write telemetry + LSM6DSO Manager-->>Manager: Return an AngularVelocity struct +``` + +### TemperatureGet +```mermaid +sequenceDiagram + participant Manager + participant LSM6DSO Manager + participant Zephyr Sensor API + participant LSM6DSO Sensor + + Manager-->>LSM6DSO Manager: Call the Temperature synchronous input port + LSM6DSO Manager->>Zephyr Sensor API: Fetch sensor data + Zephyr Sensor API->>LSM6DSO Sensor: Read sensor + LSM6DSO Sensor->>Zephyr Sensor API: Return sensor data + Zephyr Sensor API->>LSM6DSO Manager: Return temperature sensor_value struct + LSM6DSO Manager->>LSM6DSO Manager: Write telemetry + LSM6DSO Manager-->>Manager: Return an F64 temperature in degrees Celsius +``` + +## Requirements +Add requirements in the chart below +| Name | Description | Validation | +|---|---|---| +| AccelerationGet Port | The component shall provide access to acceleration sensor data and return in Acceleration struct, readings will be in m/s^2 | Verify output matches expected values from sensor datasheet | +| AngularVelocityGet Port | The component shall provide access to angular velocity sensor data and return in AngularVelocity struct, readings will be in rad/s | Verify output matches expected values from sensor datasheet | +| TemperatureGet Port | The component shall provide access to temperature sensor data and return in degrees Celsius | Verify output matches expected values from sensor datasheet | + +## Change Log +| Date | Description | +|---|---| +| 2025-9-17 | Initial LSM6DSO Driver component | diff --git a/FprimeZephyrReference/Components/Drv/Types/CMakeLists.txt b/FprimeZephyrReference/Components/Drv/Types/CMakeLists.txt new file mode 100644 index 00000000..dcbbe126 --- /dev/null +++ b/FprimeZephyrReference/Components/Drv/Types/CMakeLists.txt @@ -0,0 +1,36 @@ +#### +# F Prime CMakeLists.txt: +# +# SOURCES: list of source files (to be compiled) +# AUTOCODER_INPUTS: list of files to be passed to the autocoders +# DEPENDS: list of libraries that this module depends on +# +# More information in the F´ CMake API documentation: +# https://fprime.jpl.nasa.gov/latest/docs/reference/api/cmake/API/ +# +#### + +# Module names are derived from the path from the nearest project/library/framework +# root when not specifically overridden by the developer. i.e. The module defined by +# `Ref/SignalGen/CMakeLists.txt` will be named `Ref_SignalGen`. + +register_fprime_library( + AUTOCODER_INPUTS + "${CMAKE_CURRENT_LIST_DIR}/Types.fpp" +# SOURCES +# "${CMAKE_CURRENT_LIST_DIR}/Helpers.cpp" +# DEPENDS +# MyPackage_MyOtherModule +) + +### Unit Tests ### +# register_fprime_ut( +# AUTOCODER_INPUTS +# "${CMAKE_CURRENT_LIST_DIR}/Helpers.fpp" +# SOURCES +# "${CMAKE_CURRENT_LIST_DIR}/test/ut/HelpersTestMain.cpp" +# "${CMAKE_CURRENT_LIST_DIR}/test/ut/HelpersTester.cpp" +# DEPENDS +# STest # For rules-based testing +# UT_AUTO_HELPERS +# ) diff --git a/FprimeZephyrReference/Components/Drv/Types/Types.fpp b/FprimeZephyrReference/Components/Drv/Types/Types.fpp new file mode 100644 index 00000000..e129beea --- /dev/null +++ b/FprimeZephyrReference/Components/Drv/Types/Types.fpp @@ -0,0 +1,23 @@ +# Type definitions for sensor readings data structures +module Drv { + @ Acceleration reading in m/s^2 + struct Acceleration { + x: F64 @< Acceleration in m/s^2 in the X direction. + y: F64 @< Acceleration in m/s^2 in the Y direction. + z: F64 @< Acceleration in m/s^2 in the Z direction. + } + + @ Angular velocity reading in rad/s + struct AngularVelocity { + x: F64 @< Angular velocity in rad/s in the X direction. + y: F64 @< Angular velocity in rad/s in the Y direction. + z: F64 @< Angular velocity in rad/s in the Z direction. + } + + @ Magnetic field in gauss. + struct MagneticField { + x: F64 @< Magnetic field in gauss in the X direction. + y: F64 @< Magnetic field in gauss in the Y direction. + z: F64 @< Magnetic field in gauss in the Z direction. + } +} diff --git a/FprimeZephyrReference/Components/ImuManager/CMakeLists.txt b/FprimeZephyrReference/Components/ImuManager/CMakeLists.txt new file mode 100644 index 00000000..19006128 --- /dev/null +++ b/FprimeZephyrReference/Components/ImuManager/CMakeLists.txt @@ -0,0 +1,36 @@ +#### +# F Prime CMakeLists.txt: +# +# SOURCES: list of source files (to be compiled) +# AUTOCODER_INPUTS: list of files to be passed to the autocoders +# DEPENDS: list of libraries that this module depends on +# +# More information in the F´ CMake API documentation: +# https://fprime.jpl.nasa.gov/latest/docs/reference/api/cmake/API/ +# +#### + +# Module names are derived from the path from the nearest project/library/framework +# root when not specifically overridden by the developer. i.e. The module defined by +# `Ref/SignalGen/CMakeLists.txt` will be named `Ref_SignalGen`. + +register_fprime_library( + AUTOCODER_INPUTS + "${CMAKE_CURRENT_LIST_DIR}/ImuManager.fpp" + SOURCES + "${CMAKE_CURRENT_LIST_DIR}/ImuManager.cpp" +# DEPENDS +# MyPackage_MyOtherModule +) + +### Unit Tests ### +# register_fprime_ut( +# AUTOCODER_INPUTS +# "${CMAKE_CURRENT_LIST_DIR}/ImuManager.fpp" +# SOURCES +# "${CMAKE_CURRENT_LIST_DIR}/test/ut/ImuManagerTestMain.cpp" +# "${CMAKE_CURRENT_LIST_DIR}/test/ut/ImuManagerTester.cpp" +# DEPENDS +# STest # For rules-based testing +# UT_AUTO_HELPERS +# ) diff --git a/FprimeZephyrReference/Components/ImuManager/ImuManager.cpp b/FprimeZephyrReference/Components/ImuManager/ImuManager.cpp new file mode 100644 index 00000000..24e520a7 --- /dev/null +++ b/FprimeZephyrReference/Components/ImuManager/ImuManager.cpp @@ -0,0 +1,33 @@ +// ====================================================================== +// \title ImuManager.cpp +// \brief cpp file for ImuManager component implementation class +// ====================================================================== + +#include "FprimeZephyrReference/Components/ImuManager/ImuManager.hpp" + +#include + +namespace Components { + +// ---------------------------------------------------------------------- +// Component construction and destruction +// ---------------------------------------------------------------------- + +ImuManager ::ImuManager(const char* const compName) : ImuManagerComponentBase(compName) {} + +ImuManager ::~ImuManager() {} + +// ---------------------------------------------------------------------- +// Handler implementations for typed input ports +// ---------------------------------------------------------------------- + +void ImuManager ::run_handler(FwIndexType portNum, U32 context) { + // Read from Lsm6dsoManager + this->accelerationGet_out(0); + this->angularVelocityGet_out(0); + this->temperatureGet_out(0); + + // Read from Lis2mdlManager + this->magneticFieldGet_out(0); +} +} // namespace Components diff --git a/FprimeZephyrReference/Components/ImuManager/ImuManager.fpp b/FprimeZephyrReference/Components/ImuManager/ImuManager.fpp new file mode 100644 index 00000000..ae6973c2 --- /dev/null +++ b/FprimeZephyrReference/Components/ImuManager/ImuManager.fpp @@ -0,0 +1,24 @@ +module Components { + @ IMU Manager Component for F Prime FSW framework. + passive component ImuManager { + sync input port run: Svc.Sched + + @ Port for sending accelerationGet calls to the LSM6DSO Driver + output port accelerationGet: Drv.AccelerationGet + + @ Port for sending angularVelocityGet calls to the LSM6DSO Driver + output port angularVelocityGet: Drv.AngularVelocityGet + + @ Port for sending magneticFieldGet calls to the LIS2MDL Manager + output port magneticFieldGet: Drv.MagneticFieldGet + + @ Port for sending temperatureGet calls to the LSM6DSO Driver + output port temperatureGet: Drv.TemperatureGet + + ############################################################################### + # Standard AC Ports: Required for Channels, Events, Commands, and Parameters # + ############################################################################### + @ Port for requesting the current time + time get port timeCaller + } +} diff --git a/FprimeZephyrReference/Components/ImuManager/ImuManager.hpp b/FprimeZephyrReference/Components/ImuManager/ImuManager.hpp new file mode 100644 index 00000000..14bec238 --- /dev/null +++ b/FprimeZephyrReference/Components/ImuManager/ImuManager.hpp @@ -0,0 +1,38 @@ +// ====================================================================== +// \title ImuManager.hpp +// \brief hpp file for ImuManager component implementation class +// ====================================================================== + +#ifndef Components_ImuManager_HPP +#define Components_ImuManager_HPP + +#include "FprimeZephyrReference/Components/ImuManager/ImuManagerComponentAc.hpp" + +namespace Components { + +class ImuManager final : public ImuManagerComponentBase { + public: + // ---------------------------------------------------------------------- + // Component construction and destruction + // ---------------------------------------------------------------------- + + //! Construct ImuManager object + ImuManager(const char* const compName); + + //! Destroy ImuManager object + ~ImuManager(); + + private: + // ---------------------------------------------------------------------- + // Handler implementations for typed input ports + // ---------------------------------------------------------------------- + + //! Handler implementation + void run_handler(FwIndexType portNum, //!< The port number + U32 context //!< The call order + ) override; +}; + +} // namespace Components + +#endif diff --git a/FprimeZephyrReference/Components/ImuManager/docs/sdd.md b/FprimeZephyrReference/Components/ImuManager/docs/sdd.md new file mode 100644 index 00000000..db12b255 --- /dev/null +++ b/FprimeZephyrReference/Components/ImuManager/docs/sdd.md @@ -0,0 +1,69 @@ +# Components::ImuManager + +The IMU Manager (Inertial Measurement Unit) component provides sensor data related to motion and orientation of the craft. It interfaces with two drivers: the LIS2MDL Manager and the LSM6DSO Driver which provide acceleration, angular velocity, magnetic field, and temperature measurements. + +## Usage Examples + +The IMU Manager component is designed to be scheduled periodically to trigger collection of sensor data and telemetering. It operates as a passive component that responds to scheduler calls. + +### Typical Usage + +1. The component is instantiated and initialized during system startup +2. The scheduler calls the `run` port at regular intervals +3. On each run call, the component: + - Fetches sensor data from both the LIS2MDL Manager and LSM6DSO Driver + - Outputs telemetry for acceleration, angular velocity, magnetic field, and temperature + +## Class Diagram + +```mermaid +classDiagram + namespace Components { + class ImuManagerComponentBase { + <> + } + class ImuManager { + + ImuManager(const char* compName) + + ~ImuManager() + - run_handler(FwIndexType portNum, U32 context): void + } + } + ImuManagerComponentBase <|-- ImuManager : inherits +``` + +## Port Descriptions +| Name | Type | Description | +|---|---|---| +| run | sync input | Scheduler port that triggers sensor data collection | +| accelerationGet | output | Port for calling accelerationGet on the LSM6DSO Manager | +| angularVelocityGet | output | Port for calling angularVelocityGet on the LSM6DSO Manager | +| magneticFieldGet | output | Port for calling magneticFieldGet on the LIS2MDL Manager | +| temperatureGet | output | Port for calling temperatureGet on the LSM6DSO Manager | +| timeCaller | time get | Port for requesting current system time | + +## Sequence Diagrams +```mermaid +sequenceDiagram + participant Scheduler + participant IMU Manager + participant LIS2MDL Manager + participant LSM6DSO Manager + + Scheduler-->>IMU Manager: run + IMU Manager->>LSM6DSO Manager: AccelerationGet + IMU Manager->>LSM6DSO Manager: AngularVelocityGet + IMU Manager->>LSM6DSO Manager: TemperatureGet + IMU Manager->>LIS2MDL Manager: MagneticFieldGet +``` + +## Requirements +| Name | Description | Validation | +|---|---|---| +| Sensor Data Collection | The component shall trigger data collection from both LSM6DSO and LIS2MDL sensors when run is called | Verify all sensor manager output ports are called | +| Periodic Operation | The component shall operate as a scheduled component responding to scheduler calls | Verify component responds correctly to scheduler input | + +## Change Log +| Date | Description | +|---|---| +| 2025-9-9 | Initial IMU Manager component | +| 2025-9-18 | Extracted Zephyr calls to discrete LIS2MDL Manager and LSM6DSO Driver | diff --git a/FprimeZephyrReference/ReferenceDeployment/Top/ReferenceDeploymentPackets.fppi b/FprimeZephyrReference/ReferenceDeployment/Top/ReferenceDeploymentPackets.fppi index fc1846de..4a0e736f 100644 --- a/FprimeZephyrReference/ReferenceDeployment/Top/ReferenceDeploymentPackets.fppi +++ b/FprimeZephyrReference/ReferenceDeployment/Top/ReferenceDeploymentPackets.fppi @@ -34,6 +34,14 @@ telemetry packets ReferenceDeploymentPackets { ReferenceDeployment.watchdog.WatchdogTransitions } + packet Imu id 6 group 4 { + ReferenceDeployment.lsm6dsoManager.Acceleration + ReferenceDeployment.lsm6dsoManager.AngularVelocity + ReferenceDeployment.lsm6dsoManager.Temperature + ReferenceDeployment.lis2mdlManager.MagneticField + } + + } omit { CdhCore.cmdDisp.CommandErrors diff --git a/FprimeZephyrReference/ReferenceDeployment/Top/instances.fpp b/FprimeZephyrReference/ReferenceDeployment/Top/instances.fpp index d3bce3e6..597c813b 100644 --- a/FprimeZephyrReference/ReferenceDeployment/Top/instances.fpp +++ b/FprimeZephyrReference/ReferenceDeployment/Top/instances.fpp @@ -63,4 +63,10 @@ module ReferenceDeployment { instance gpioDriver: Zephyr.ZephyrGpioDriver base id 0x10015000 instance watchdog: Components.Watchdog base id 0x10016000 + + instance imuManager: Components.ImuManager base id 0x10017000 + + instance lis2mdlManager: Drv.Lis2mdlManager base id 0x10018000 + + instance lsm6dsoManager: Drv.Lsm6dsoManager base id 0x10019000 } diff --git a/FprimeZephyrReference/ReferenceDeployment/Top/topology.fpp b/FprimeZephyrReference/ReferenceDeployment/Top/topology.fpp index f1946b87..60afd964 100644 --- a/FprimeZephyrReference/ReferenceDeployment/Top/topology.fpp +++ b/FprimeZephyrReference/ReferenceDeployment/Top/topology.fpp @@ -29,6 +29,9 @@ module ReferenceDeployment { instance gpioDriver instance watchdog instance prmDb + instance imuManager + instance lis2mdlManager + instance lsm6dsoManager # ---------------------------------------------------------------------- # Pattern graph specifiers @@ -92,12 +95,20 @@ module ReferenceDeployment { rateGroup1Hz.RateGroupMemberOut[2] -> ComCcsds.commsBufferManager.schedIn rateGroup1Hz.RateGroupMemberOut[3] -> CdhCore.tlmSend.Run rateGroup1Hz.RateGroupMemberOut[4] -> watchdog.run + rateGroup1Hz.RateGroupMemberOut[5] -> imuManager.run } connections Watchdog { watchdog.gpioSet -> gpioDriver.gpioWrite } + connections imuManager { + imuManager.accelerationGet -> lsm6dsoManager.accelerationGet + imuManager.angularVelocityGet -> lsm6dsoManager.angularVelocityGet + imuManager.magneticFieldGet -> lis2mdlManager.magneticFieldGet + imuManager.temperatureGet -> lsm6dsoManager.temperatureGet + } + connections ReferenceDeployment { } diff --git a/FprimeZephyrReference/project/config/TlmPacketizerCfg.hpp b/FprimeZephyrReference/project/config/TlmPacketizerCfg.hpp index 1a9daca1..96f20246 100644 --- a/FprimeZephyrReference/project/config/TlmPacketizerCfg.hpp +++ b/FprimeZephyrReference/project/config/TlmPacketizerCfg.hpp @@ -16,7 +16,7 @@ #include namespace Svc { -static const FwChanIdType MAX_PACKETIZER_PACKETS = 5; +static const FwChanIdType MAX_PACKETIZER_PACKETS = 6; static const FwChanIdType TLMPACKETIZER_NUM_TLM_HASH_SLOTS = 15; // !< Number of slots in the hash table. // Works best when set to about twice the number of components producing telemetry @@ -24,7 +24,7 @@ static const FwChanIdType TLMPACKETIZER_HASH_MOD_VALUE = 999; // !< The modulo value of the hashing function. // Should be set to a little below the ID gaps to spread the entries around -static const FwChanIdType TLMPACKETIZER_HASH_BUCKETS = 40; // !< Buckets assignable to a hash slot. +static const FwChanIdType TLMPACKETIZER_HASH_BUCKETS = 80; // !< Buckets assignable to a hash slot. // Buckets must be >= number of telemetry channels in system static const FwChanIdType TLMPACKETIZER_MAX_MISSING_TLM_CHECK = 25; // !< Maximum number of missing telemetry channel checks diff --git a/FprimeZephyrReference/test/int/imu_manager_test.py b/FprimeZephyrReference/test/int/imu_manager_test.py new file mode 100644 index 00000000..ee98b6f0 --- /dev/null +++ b/FprimeZephyrReference/test/int/imu_manager_test.py @@ -0,0 +1,64 @@ +""" +imu_manager_test.py: + +Integration tests for the IMU Manager component. +""" + +import pytest +from fprime_gds.common.data_types.ch_data import ChData +from fprime_gds.common.testing_fw.api import IntegrationTestAPI + + +@pytest.fixture(autouse=True) +def send_packet(fprime_test_api: IntegrationTestAPI): + """Fixture to clear histories and send the IMU packet before each test""" + fprime_test_api.clear_histories() + fprime_test_api.send_and_assert_command( + "CdhCore.tlmSend.SEND_PKT", ["6"], max_delay=2 + ) + + +def test_01_acceleration_telemetry(fprime_test_api: IntegrationTestAPI): + """Test that we can get Acceleration telemetry""" + result: ChData = fprime_test_api.assert_telemetry( + "ReferenceDeployment.lsm6dsoManager.Acceleration", start="NOW", timeout=3 + ) + + reading: dict[float] = result.get_val() + assert all(reading[k] != 0 for k in ("x", "y", "z")), ( + "Acceleration reading should be non-zero" + ) + + +def test_02_angular_velocity_telemetry(fprime_test_api: IntegrationTestAPI): + """Test that we can get AngularVelocity telemetry""" + result: ChData = fprime_test_api.assert_telemetry( + "ReferenceDeployment.lsm6dsoManager.AngularVelocity", start="NOW", timeout=3 + ) + + reading: dict[float] = result.get_val() + assert all(reading[k] != 0 for k in ("x", "y", "z")), ( + "AngularVelocity reading should be non-zero" + ) + + +def test_03_temperature_telemetry(fprime_test_api: IntegrationTestAPI): + """Test that we can get Temperature telemetry""" + result: ChData = fprime_test_api.assert_telemetry( + "ReferenceDeployment.lsm6dsoManager.Temperature", start="NOW", timeout=3 + ) + + reading: int = result.get_val() + assert reading != 0, "Temperature reading should be non-zero" + + +def test_04_magnetic_field_telemetry(fprime_test_api: IntegrationTestAPI): + """Test that we can get MagneticField telemetry""" + result: ChData = fprime_test_api.assert_telemetry( + "ReferenceDeployment.lis2mdlManager.MagneticField", start="NOW", timeout=3 + ) + + reading: dict[float] = result.get_val() + assert all(reading[k] != 0 for k in ("x", "y", "z")), ( + "MagneticField reading should be non-zero" + )