diff --git a/PROVESFlightControllerReference/Components/Drv/RtcManager/RtcManager.cpp b/PROVESFlightControllerReference/Components/Drv/RtcManager/RtcManager.cpp index 866b76d3..3129d68b 100644 --- a/PROVESFlightControllerReference/Components/Drv/RtcManager/RtcManager.cpp +++ b/PROVESFlightControllerReference/Components/Drv/RtcManager/RtcManager.cpp @@ -17,7 +17,8 @@ RtcManager ::RtcManager(const char* const compName) m_rtcHelper(), m_RtcNotReadyThrottle(false), m_RtcGetTimeFailedThrottle(false), - m_RtcInvalidTimeThrottle(false) { + m_RtcInvalidTimeThrottle(false), + m_ProcTimeSet(false) { // alarm time initialization memset(&this->m_alarm_time, 0, sizeof(struct rtc_time)); } @@ -48,9 +49,17 @@ void RtcManager ::timeGetPort_handler(FwIndexType portNum, Fw::Time& time) { U32 seconds_since_boot = static_cast(t / 1000); U32 useconds_since_boot = static_cast((t % 1000) * 1000); + // check if proc time mode is set + if (this->m_ProcTimeSet) { + // use proc time + time.set(TimeBase::TB_PROC_TIME, 0, seconds_since_boot, useconds_since_boot); + return; + } + // Check device readiness if (!device_is_ready(this->m_dev)) { this->log_CONSOLE_RtcNotReady(); + // Don't set the proc time flag if device is not ready // Use uptime as fallback time.set(TimeBase::TB_PROC_TIME, 0, seconds_since_boot, useconds_since_boot); @@ -151,13 +160,37 @@ void RtcManager ::TIME_SET_cmdHandler(FwOpcodeType opCode, U32 cmdSeq, Drv::Time return; } + this->m_ProcTimeSet = false; // proc time flag + // Emit time set event, include previous time for reference this->log_ACTIVITY_HI_TimeSet(time_before_set.getSeconds(), time_before_set.getUSeconds()); + this->log_ACTIVITY_HI_TimeBase(Fw::String("RTC")); // Send command response this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK); } +void RtcManager ::TO_PROC_TIME_cmdHandler(FwOpcodeType opCode, U32 cmdSeq) { + // Cancel any running sequences before setting time, as time change may impact their behavior + for (FwIndexType i = 0; i < this->getNum_cancelSequences_OutputPorts(); i++) { + if (!this->isConnected_cancelSequences_OutputPort(i)) { + continue; + } + this->cancelSequences_out(i); + } + + // Switch the time source to proc time + int64_t uptime = k_uptime_get(); + U32 seconds_since_boot = static_cast(uptime / 1000); + this->log_ACTIVITY_HI_ProcTimeSet(seconds_since_boot); // Log the current uptime in seconds + + this->m_ProcTimeSet = true; // proc time flag + this->log_ACTIVITY_HI_TimeBase(Fw::String("PROC")); + + // Command response + this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK); +} + void RtcManager ::ALARM_SET_cmdHandler(FwOpcodeType opCode, U32 cmdSeq, Drv::TimeData t) { // retrieve info about current alarm diff --git a/PROVESFlightControllerReference/Components/Drv/RtcManager/RtcManager.fpp b/PROVESFlightControllerReference/Components/Drv/RtcManager/RtcManager.fpp index f912fbf5..4c8d57ad 100644 --- a/PROVESFlightControllerReference/Components/Drv/RtcManager/RtcManager.fpp +++ b/PROVESFlightControllerReference/Components/Drv/RtcManager/RtcManager.fpp @@ -31,6 +31,9 @@ module Drv { t: Drv.TimeData @< Set the time ) + @ TO_PROC_TIME command to use proc time instead of RTC time + sync command TO_PROC_TIME() + @ ALARM_SET command to set an alarm on the RTC sync command ALARM_SET( t: Drv.TimeData @< Time to set the alarm for @@ -55,6 +58,16 @@ module Drv { useconds: U32 @< Microseconds ) severity activity high id 3 format "Time set on RTC, previous time: {}.{}" + @ ProcTimeSet event indicates that proc time was set + event ProcTimeSet( + seconds: U32 @< Uptime in seconds + ) severity activity high id 17 format "Proc time set, current uptime: {}" + + @ Timebase event fires when the timebase changes and indicates the current timebase + event TimeBase( + timeBase: string size 4 + ) severity activity high id 18 format "Timebase is currently: {}" + @ TimeNotSet event indicates that the time was not set successfully event TimeNotSet(rc: I32) severity warning high id 4 format "Time not set on RTC: {}" diff --git a/PROVESFlightControllerReference/Components/Drv/RtcManager/RtcManager.hpp b/PROVESFlightControllerReference/Components/Drv/RtcManager/RtcManager.hpp index 06fd7dbd..da5a7491 100644 --- a/PROVESFlightControllerReference/Components/Drv/RtcManager/RtcManager.hpp +++ b/PROVESFlightControllerReference/Components/Drv/RtcManager/RtcManager.hpp @@ -93,6 +93,13 @@ class RtcManager final : public RtcManagerComponentBase { U32 cmdSeq //!< The command sequence number ) override; + //! Handler implementation for command TO_PROC_TIME + //! + //! Switches the time source to use proc time + void TO_PROC_TIME_cmdHandler(FwOpcodeType opCode, //!< The opcode + U32 cmdSeq //!< The command sequence number + ) override; + private: // ---------------------------------------------------------------------- // Private helper methods @@ -135,6 +142,7 @@ class RtcManager final : public RtcManagerComponentBase { std::atomic m_RtcNotReadyThrottle; //!< Throttle for RtcNotReady std::atomic m_RtcGetTimeFailedThrottle; //!< Throttle for RtcGetTimeFailed std::atomic m_RtcInvalidTimeThrottle; //!< Throttle for RtcInvalidTime + std::atomic m_ProcTimeSet; //!< Proc time flag // rtc alarm members U16 m_curr_mask; //!< The mask of the alarm present on hardware diff --git a/PROVESFlightControllerReference/Components/Drv/RtcManager/docs/sdd.md b/PROVESFlightControllerReference/Components/Drv/RtcManager/docs/sdd.md index a90941d1..51db3a20 100644 --- a/PROVESFlightControllerReference/Components/Drv/RtcManager/docs/sdd.md +++ b/PROVESFlightControllerReference/Components/Drv/RtcManager/docs/sdd.md @@ -11,13 +11,22 @@ The RTC Manager component interfaces with the Real Time Clock (RTC) to provide t 1. The component is instantiated and initialized during system startup 2. A ground station sends a `TIME_SET` command with the desired time 3. On each command, the component: + - Cancels any running sequences - Validates the time data (year >= 1900, month [1-12], day [1-31], hour [0-23], minute [0-59], second [0-59]) - Emits validation failure events if any field is invalid - Sets the time on the RTC if validation passes - Emits a `TimeSet` event with the previous time if the time is set successfully + - Emits a `TimeBase` event with RTC time if time was set successfully - Emits a `TimeNotSet` event if the time is not set successfully - Emits a `DeviceNotReady` event if the device is not ready +#### `TO_PROC_TIME` Command Usage +1. A ground station sends a `TO_PROC_TIME` command to switch the timebase +2. When the command is received the component: + - Cancels any running sequences + - Emits a `TimeBase` event with proc time + - Emits a `ProcTimeSet` event with seconds since boot + #### `ALARM_SET` Command Usage 1. The component is instantiated and initialized during system startup 2. A ground station sends a `ALARM_SET` command with the desired time @@ -99,6 +108,7 @@ This logic applies both when using the RTC (`TB_SC_TIME`) and when in failover m | RtcManager-015 | Alarm is set with an impossible time and an event is emitted, the alarm is not set | Integration test | | RtcManager-016 | Alarm is set and then another alarm is set. An event is emitted and the second alarm is not set | Integration test | | RtcManager-017 | Errors occurring during timeGetPort calls are logged to the console with throttling to prevent flooding | Manual testing and code review | +| RtcManager-018 | Spacecraft switches between RTC time and PROC time and listens for event emission | Integration test | ## Port Descriptions @@ -322,6 +332,24 @@ sequenceDiagram RTC Manager-->>Ground Station: Command response EXECUTION_ERROR ``` +### `TO_PROC_TIME` Command + +The `TO_PROC_TIME` command is called to toggle the timebase + +#### Success + +```mermaid +sequenceDiagram + participant Ground Station + participant Event Log + participant RTC Manager + participant Zephyr RTC API + participant RTC Sensor + + Ground Station->>RTC Manager: Command TO_PROC_TIME + RTC Manager->>RTC Manager: set m_ProcTimeSet to true +``` + ### `ALARM_SET` Command The `ALARM_SET` command is called to set the current time on the RTC alarm. The component validates that the time is in the future before setting the alarm diff --git a/PROVESFlightControllerReference/test/int/rtc_test.py b/PROVESFlightControllerReference/test/int/rtc_test.py index 2c0ec765..40920492 100644 --- a/PROVESFlightControllerReference/test/int/rtc_test.py +++ b/PROVESFlightControllerReference/test/int/rtc_test.py @@ -436,3 +436,20 @@ def test_10_double_set_test(fprime_test_api: IntegrationTestAPI, start_gds): fprime_test_api.send_command(f"{rtcManager}.ALARM_SET", [alarm_time_data_str]) # Assert that we receive an AlarmNotSet event within 10 seconds fprime_test_api.await_event(f"{rtcManager}.AlarmNotSet", timeout=10) + + +@pytest.mark.uart_only(reason="Test functionality of to_proc_time") +def test_11_proc_toggle(fprime_test_api: IntegrationTestAPI, start_gds): + """Test for events emitted by proc time toggle""" + + # Set time to Curiosity landing on Mars (7 minutes of terror! https://youtu.be/Ki_Af_o9Q9s) + curiosity_landing = datetime(2012, 8, 6, 5, 17, 57, tzinfo=timezone.utc) + set_time(fprime_test_api, curiosity_landing) + + # Assert that we receive a TimeBase event within 10 seconds + fprime_test_api.await_event(f"{rtcManager}.TimeBase", timeout=10) + + fprime_test_api.send_command(f"{rtcManager}.TO_PROC_TIME") + + # Assert that we receive a TimeBase event within 10 seconds + fprime_test_api.await_event(f"{rtcManager}.TimeBase", timeout=10) diff --git a/docs-site/components/RtcManager.md b/docs-site/components/RtcManager.md index a90941d1..51db3a20 100644 --- a/docs-site/components/RtcManager.md +++ b/docs-site/components/RtcManager.md @@ -11,13 +11,22 @@ The RTC Manager component interfaces with the Real Time Clock (RTC) to provide t 1. The component is instantiated and initialized during system startup 2. A ground station sends a `TIME_SET` command with the desired time 3. On each command, the component: + - Cancels any running sequences - Validates the time data (year >= 1900, month [1-12], day [1-31], hour [0-23], minute [0-59], second [0-59]) - Emits validation failure events if any field is invalid - Sets the time on the RTC if validation passes - Emits a `TimeSet` event with the previous time if the time is set successfully + - Emits a `TimeBase` event with RTC time if time was set successfully - Emits a `TimeNotSet` event if the time is not set successfully - Emits a `DeviceNotReady` event if the device is not ready +#### `TO_PROC_TIME` Command Usage +1. A ground station sends a `TO_PROC_TIME` command to switch the timebase +2. When the command is received the component: + - Cancels any running sequences + - Emits a `TimeBase` event with proc time + - Emits a `ProcTimeSet` event with seconds since boot + #### `ALARM_SET` Command Usage 1. The component is instantiated and initialized during system startup 2. A ground station sends a `ALARM_SET` command with the desired time @@ -99,6 +108,7 @@ This logic applies both when using the RTC (`TB_SC_TIME`) and when in failover m | RtcManager-015 | Alarm is set with an impossible time and an event is emitted, the alarm is not set | Integration test | | RtcManager-016 | Alarm is set and then another alarm is set. An event is emitted and the second alarm is not set | Integration test | | RtcManager-017 | Errors occurring during timeGetPort calls are logged to the console with throttling to prevent flooding | Manual testing and code review | +| RtcManager-018 | Spacecraft switches between RTC time and PROC time and listens for event emission | Integration test | ## Port Descriptions @@ -322,6 +332,24 @@ sequenceDiagram RTC Manager-->>Ground Station: Command response EXECUTION_ERROR ``` +### `TO_PROC_TIME` Command + +The `TO_PROC_TIME` command is called to toggle the timebase + +#### Success + +```mermaid +sequenceDiagram + participant Ground Station + participant Event Log + participant RTC Manager + participant Zephyr RTC API + participant RTC Sensor + + Ground Station->>RTC Manager: Command TO_PROC_TIME + RTC Manager->>RTC Manager: set m_ProcTimeSet to true +``` + ### `ALARM_SET` Command The `ALARM_SET` command is called to set the current time on the RTC alarm. The component validates that the time is in the future before setting the alarm