Components::ModeManager
The ModeManager component manages system operational modes and orchestrates transitions between NORMAL and SAFE_MODE. It evaluates voltage conditions and detects unintended reboots to make mode decisions, controls power to non-critical subsystems during transitions, and maintains/persists mode state across reboots.
Requirements
| Name |
Description |
Validation |
| MM0001 |
The ModeManager shall maintain two operational modes: NORMAL and SAFE_MODE |
Integration Testing |
| MM0002 |
The ModeManager shall enter safe mode when commanded manually via FORCE_SAFE_MODE command |
Integration Testing |
| MM0003 |
The ModeManager shall enter safe mode when requested by external components via forceSafeMode port |
Integration Testing |
| MM0004 |
The ModeManager shall exit safe mode only via explicit EXIT_SAFE_MODE command or automatic voltage recovery |
Integration Testing |
| MM0005 |
The ModeManager shall turn off all 8 load switches when entering safe mode |
Integration Testing |
| MM0006 |
The ModeManager shall turn on face load switches (0-5) when exiting safe mode; payload switches (6-7) remain off |
Integration Testing |
| MM0007 |
The ModeManager shall persist mode state to non-volatile storage and restore on initialization |
Integration Testing |
| MM0008 |
The ModeManager shall detect unintended reboots and enter safe mode with reason SYSTEM_FAULT |
Integration Testing |
| MM0009 |
The ModeManager shall automatically enter safe mode when voltage drops below configurable threshold |
Integration Testing |
| MM0010 |
The ModeManager shall automatically exit safe mode (LOW_BATTERY only) when voltage recovers above configurable threshold |
Integration Testing |
| MM0011 |
The ModeManager shall enter safe mode with reason COMMAND_LOSS if no authenticated packet is received within COMM_LOSS_TIME after the first packet |
Integration Testing |
Class Diagram
classDiagram
class ModeManager {
<<Active Component>>
- m_mode: SystemMode
- m_safeModeEntryCount: U32
- m_safeModeReason: SafeModeReason
- m_safeModeVoltageCounter: U32
- m_recoveryVoltageCounter: U32
- m_lastPacketRoutedTime: Fw::Time
+ init(queueDepth, instance)
- run_handler()
- forceSafeMode_handler(reason)
- getMode_handler(): SystemMode
- prepareForReboot_handler()
- packetRouted_handler()
- enterSafeMode(reason)
- exitSafeMode()
- exitSafeModeAutomatic(voltage)
}
class SystemMode {
<<enumeration>>
SAFE_MODE = 1
NORMAL = 2
}
class SafeModeReason {
<<enumeration>>
NONE = 0
LOW_BATTERY = 1
SYSTEM_FAULT = 2
GROUND_COMMAND = 3
EXTERNAL_REQUEST = 4
LORA = 5
}
ModeManager --> SystemMode
ModeManager --> SafeModeReason
Ports
| Name |
Type |
Kind |
Description |
| run |
Svc.Sched |
sync |
1Hz periodic calls for telemetry, voltage monitoring, and command loss detection |
| forceSafeMode |
ForceSafeModeWithReason |
async |
Safe mode requests from external components |
| getMode |
GetSystemMode |
sync |
Query current system mode |
| prepareForReboot |
Fw.Signal |
sync |
Set clean shutdown flag before intentional reboot |
| packetRouted |
Fw.Signal |
sync |
Resets the command loss timer when an authenticated packet is received from ProvesRouter |
Output Ports
| Name |
Type |
Description |
| modeChanged |
SystemModeChanged |
Notifies components of mode changes |
| loadSwitchTurnOn |
Fw.Signal [8] |
Turn on load switches |
| loadSwitchTurnOff |
Fw.Signal [8] |
Turn off load switches |
| voltageGet |
Drv.VoltageGet |
Query system voltage |
| stopWatchdog |
Fw.Signal |
Stops the hardware watchdog to trigger a power cycle (called on command loss) |
Commands
| Name |
Description |
| FORCE_SAFE_MODE |
Forces safe mode with reason GROUND_COMMAND |
| EXIT_SAFE_MODE |
Exits safe mode (fails if not in safe mode) |
Parameters
Voltage thresholds and command loss timeout are configurable via F-Prime parameters:
| Parameter |
Type |
Default |
Description |
| SafeModeEntryVoltage |
F32 |
6.7 |
Voltage (V) below which safe mode is entered |
| SafeModeRecoveryVoltage |
F32 |
8.0 |
Voltage (V) above which safe mode can be exited |
| SafeModeDebounceSeconds |
U32 |
10 |
Consecutive seconds required for transitions |
| COMM_LOSS_TIME |
Fw.TimeIntervalValue |
{seconds=36060*24} |
Time without an authenticated packet before command loss safe mode entry (default: 3 days) |
Parameters can be modified at runtime via PRM_SET commands.
Events
| Name |
Severity |
Description |
| EnteringSafeMode |
WARNING_HI |
Entering safe mode with reason string |
| ExitingSafeMode |
ACTIVITY_HI |
Manually exiting safe mode |
| AutoSafeModeEntry |
WARNING_HI |
Auto-entry due to low voltage |
| AutoSafeModeExit |
ACTIVITY_HI |
Auto-exit due to voltage recovery |
| UnintendedRebootDetected |
WARNING_HI |
Unintended reboot detected on startup |
| ManualSafeModeEntry |
ACTIVITY_HI |
Safe mode commanded via FORCE_SAFE_MODE |
| ExternalFaultDetected |
WARNING_HI |
External component triggered safe mode |
| PreparingForReboot |
ACTIVITY_HI |
Clean shutdown flag being set |
| CommandValidationFailed |
WARNING_LO |
Command validation failed |
| StatePersistenceFailure |
WARNING_LO |
State save/load failed |
| CommandLossDetected |
WARNING_HI |
Command loss timeout exceeded; entering safe mode with reason COMMAND_LOSS |
Telemetry
| Name |
Type |
Description |
| CurrentMode |
U8 |
Current mode (1=SAFE_MODE, 2=NORMAL) |
| SafeModeEntryCount |
U32 |
Times safe mode entered (persists across reboots) |
| CurrentSafeModeReason |
SafeModeReason |
Current reason (NONE if not in safe mode) |
State Persistence
State is persisted to /mode_state.bin:
- Current mode (U8)
- Safe mode entry count (U32)
- Safe mode reason (U8)
- Clean shutdown flag (U8)
Safe Mode Reason Logic
| Reason |
Trigger |
Auto-Recovery |
| LOW_BATTERY |
Voltage below threshold |
Yes (when voltage recovers) |
| SYSTEM_FAULT |
Unintended reboot detected |
No |
| GROUND_COMMAND |
FORCE_SAFE_MODE command |
No |
| EXTERNAL_REQUEST |
forceSafeMode port call or command loss timeout |
No |
| LORA |
LoRa communication fault |
No |
Load Switch Mapping
| Index |
Subsystem |
NORMAL |
SAFE_MODE |
| 0-5 |
Satellite Faces |
ON |
OFF |
| 6-7 |
Payload Power/Battery |
OFF |
OFF |
When exiting to NORMAL, only face switches (0-5) turn ON. Payload switches must be controlled separately.
Sequence Diagrams
Command Loss Detection
sequenceDiagram
participant AuthRouter as ProvesRouter
participant ModeManager
participant RateGroup
AuthRouter->>ModeManager: packetRouted() [on each authenticated packet]
Note over ModeManager: Resets m_commandLossStartTime to now
loop Every 1Hz (no packets received)
RateGroup->>ModeManager: run()
ModeManager->>ModeManager: Check if now > start + COMM_LOSS_TIME
end
Note over ModeManager: Timeout exceeded
ModeManager->>ModeManager: log CommandLossDetected event
ModeManager->>ModeManager: enterSafeMode(COMMAND_LOSS)
Safe Mode Entry (Low Voltage)
sequenceDiagram
participant RateGroup
participant ModeManager
participant INA219
participant LoadSwitches
loop Every 1Hz
RateGroup->>ModeManager: run()
ModeManager->>INA219: voltageGet_out()
INA219-->>ModeManager: voltage < threshold
ModeManager->>ModeManager: Increment counter
end
Note over ModeManager: After debounce period
ModeManager->>ModeManager: enterSafeMode(LOW_BATTERY)
ModeManager->>LoadSwitches: Turn off all 8 switches
Unintended Reboot Detection
sequenceDiagram
participant Boot
participant ModeManager
participant FlashStorage
Boot->>ModeManager: init()
ModeManager->>FlashStorage: Load state
FlashStorage-->>ModeManager: cleanShutdown=0, mode=NORMAL
ModeManager->>ModeManager: Detect unintended reboot
ModeManager->>ModeManager: enterSafeMode(SYSTEM_FAULT)
Design Notes
- Hysteresis: Entry threshold (6.7V) < Recovery threshold (8.0V) prevents oscillation
- Debounce: Configurable consecutive samples prevent spurious transitions
- Reason tracking: Only LOW_BATTERY allows auto-recovery; other reasons require manual EXIT_SAFE_MODE
- Mode query: Both pull (getMode) and push (modeChanged) patterns supported
- Command loss ownership: ProvesRouter signals
packetRouted on each routed packet; ModeManager owns the timer and the mode transition, keeping routing and mode management as separate concerns
- Command loss thread safety:
m_commandLossStartTime is protected by m_commandLossMutex since packetRouted_handler (called from the radio thread) and run_handler (called from the rate group thread) may run concurrently