Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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));
}
Expand Down Expand Up @@ -48,9 +49,17 @@ void RtcManager ::timeGetPort_handler(FwIndexType portNum, Fw::Time& time) {
U32 seconds_since_boot = static_cast<U32>(t / 1000);
U32 useconds_since_boot = static_cast<U32>((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();
Comment thread
coderabbitai[bot] marked this conversation as resolved.
// 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);
Expand Down Expand Up @@ -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<U32>(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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Comment thread
ineskhou marked this conversation as resolved.

@ ALARM_SET command to set an alarm on the RTC
sync command ALARM_SET(
t: Drv.TimeData @< Time to set the alarm for
Expand All @@ -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: {}"

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -135,6 +142,7 @@ class RtcManager final : public RtcManagerComponentBase {
std::atomic<bool> m_RtcNotReadyThrottle; //!< Throttle for RtcNotReady
std::atomic<bool> m_RtcGetTimeFailedThrottle; //!< Throttle for RtcGetTimeFailed
std::atomic<bool> m_RtcInvalidTimeThrottle; //!< Throttle for RtcInvalidTime
std::atomic<bool> m_ProcTimeSet; //!< Proc time flag

// rtc alarm members
U16 m_curr_mask; //!< The mask of the alarm present on hardware
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
17 changes: 17 additions & 0 deletions PROVESFlightControllerReference/test/int/rtc_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Comment thread
ineskhou marked this conversation as resolved.
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)
Comment thread
coderabbitai[bot] marked this conversation as resolved.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could be good to have this test assert what the timebase is that is being set (PROC vs RTC) and/or actually check the physcial timebase. Checking the event doesn't let us know if the system of the sat is on PROC time. At minimum before merge, woudl want manual evidence on this happening on a board

That being said, test updates will not block merge.

28 changes: 28 additions & 0 deletions docs-site/components/RtcManager.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
Loading