Skip to content
Merged
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
9 changes: 8 additions & 1 deletion include/core/raps_definitions.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,11 @@ struct ITLEntry {
AileeStatus status;
};

struct WnnAlertPayload {
double curvature_proxy;
double oscillatory_prefactor;
};

// --- Union Payload Container ---

union PayloadData {
Expand All @@ -123,6 +128,7 @@ struct ITLEntry {
AileeSafetyStatusPayload ailee_safety_status;
AileeGraceResultPayload ailee_grace_result;
AileeConsensusResultPayload ailee_consensus_result;
WnnAlertPayload wnn_alert;
};

// --- Entry Type ---
Expand All @@ -144,7 +150,8 @@ struct ITLEntry {
SUPERVISOR_EXCEPTION,
AILEE_SAFETY_STATUS,
AILEE_GRACE_RESULT,
AILEE_CONSENSUS_RESULT
AILEE_CONSENSUS_RESULT,
WNN_ALERT
};

// --- ITL Entry Header ---
Expand Down
23 changes: 21 additions & 2 deletions include/itl/itl_manager.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
#include <cstddef>
#include <cstdint>

#include "RAPSDefinitions.hpp"
#include "PlatformHAL.hpp"
#include "core/raps_definitions.hpp"
#include "platform/platform_hal.hpp"

// Immutable Telemetry Ledger (ITL) Manager
// Owns queueing, durability, flash IO, and Merkle batching lifecycle.
Expand Down Expand Up @@ -35,4 +35,23 @@ class ITLManager {

// Background processing (low-priority task)
void flush_pending();

// Log WNN rollback event
void log_wnn_rollback_event(double curvature, double prefactor);
};

inline void ITLManager::log_wnn_rollback_event(double curvature, double prefactor) {
ITLEntry wnn_entry{};
wnn_entry.type = ITLEntry::Type::WNN_ALERT;
wnn_entry.timestamp_ms = PlatformHAL::now_ms();
wnn_entry.payload.wnn_alert.curvature_proxy = curvature;
wnn_entry.payload.wnn_alert.oscillatory_prefactor = prefactor;
commit(wnn_entry);

ITLEntry rollback_entry{};
rollback_entry.type = ITLEntry::Type::ROLLBACK_COMMIT;
rollback_entry.timestamp_ms = PlatformHAL::now_ms();
// Payload for rollback commit (CommandExecutionPayload)
// we just commit the entry to mark the rollback execution triggered by WNN
commit(rollback_entry);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Record rollback commits only after success

This helper commits a ROLLBACK_COMMIT before trigger_wnn_immediate_rollback has run, so when rollback_count == 0 or execute_rollback_plan fails, pollWnnAndEnforce returns false but the immutable ledger already contains a successful rollback commit. The reference rollback flow records rollback_commit only after actuator success (reference/python/hlv_governance_reference.py:137-143), so this should be moved after a successful rollback or changed to a pending/failure event.

Useful? React with 👍 / 👎.

}
Comment on lines +43 to +57
43 changes: 43 additions & 0 deletions include/raps/safety/deterministic_safety_monitor.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
#include <iostream>
#include <limits>

#include "raps/rollback_execution.hpp"
#include "itl/itl_manager.hpp"

// =====================================================
// Deterministic Safety Monitor (DSM)
// =====================================================
Expand All @@ -25,8 +28,17 @@ constexpr double MAX_TCC_COUPLING_J = 1.0e+04;
// Failsafe parameters
constexpr double MIN_RESONANCE_AMPLITUDE_CUTOFF = 0.10;

// WNN Constraints
constexpr double WNN_MAX_CURVATURE_PROXY = 5.0e-11;
constexpr double WNN_MIN_OSCILLATORY_PREFACTOR = 0.85;

} // namespace DSM_Config

struct WnnTelemetry {
double curvature_proxy;
double oscillatory_prefactor;
};

// =====================================================
// DSM Sensor Inputs (Independent Channels)
// =====================================================
Expand Down Expand Up @@ -55,6 +67,14 @@ class DeterministicSafetyMonitor {

int evaluateSafety(const DsmSensorInputs& inputs);

bool pollWnnAndEnforce(
const WnnTelemetry& wnn_telem,
ITLManager& itl_manager,
const RollbackPlan* rollback_store,
uint32_t rollback_count,
PhysicsState& active_state_pointer
);

private:
double last_estimated_Rmax_;
bool safing_sequence_active_;
Expand Down Expand Up @@ -164,3 +184,26 @@ DeterministicSafetyMonitor::evaluateSafety(

return ACTION_NONE;
}

inline bool
DeterministicSafetyMonitor::pollWnnAndEnforce(
const WnnTelemetry& wnn_telem,
ITLManager& itl_manager,
const RollbackPlan* rollback_store,
uint32_t rollback_count,
PhysicsState& active_state_pointer
) {
if (wnn_telem.curvature_proxy > DSM_Config::WNN_MAX_CURVATURE_PROXY ||
wnn_telem.oscillatory_prefactor < DSM_Config::WNN_MIN_OSCILLATORY_PREFACTOR) {
Comment on lines +196 to +197
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Treat non-finite WNN telemetry as unsafe

When either WNN value is NaN (or an otherwise nonsensical non-finite prefactor), these comparisons evaluate false and the DSM takes no action, even though the existing DSM sensor path treats non-finite inputs as a full-shutdown condition. A WNN sensor/calculation fault can therefore bypass the immediate rollback enforcement; validate std::isfinite before threshold checks and fail safe on invalid telemetry.

Useful? React with 👍 / 👎.


// Breach detected! Log to ITL and execute immediate rollback
itl_manager.log_wnn_rollback_event(wnn_telem.curvature_proxy, wnn_telem.oscillatory_prefactor);

return trigger_wnn_immediate_rollback(
rollback_store,
rollback_count,
active_state_pointer
);
Comment on lines +199 to +206
}
return false; // No breach
}
12 changes: 12 additions & 0 deletions include/raps/telemetry/telemetry_ring_buffer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,18 @@ class TelemetryRingBuffer final {
return true;
}

// Peek the latest written item without popping.
bool try_peek_latest(T& out) const noexcept {
const uint64_t w = _write_idx.load(std::memory_order_acquire);
const uint64_t r = _read_idx.load(std::memory_order_relaxed);

if (r == w) return false;

// The most recent valid write is at w - 1
out = _data[(w - 1) & (CapacityPow2 - 1)];
return true;
}
Comment on lines +47 to +56

// Pop one item if available.
bool try_pop(T& out) noexcept {
const uint64_t r = _read_idx.load(std::memory_order_relaxed);
Expand Down
3 changes: 3 additions & 0 deletions src/itl/itl_payload_sizing.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,9 @@ inline size_t itl_effective_payload_len(
case ITLEntry::Type::AILEE_CONSENSUS_RESULT:
return sizeof(ITLEntry::AileeConsensusResultPayload);

case ITLEntry::Type::WNN_ALERT:
return sizeof(ITLEntry::WnnAlertPayload);

default:
return 0;
}
Expand Down
2 changes: 2 additions & 0 deletions src/itl/itl_state_snapshot.hpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
#pragma once

#include "itl/itl_manager.hpp"

inline void commit_state_snapshot(
ITLManager& itl_manager,
const PhysicsState& current_state) {
Expand Down
28 changes: 28 additions & 0 deletions src/raps/rollback_execution.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

#include "raps/core/raps_core_types.hpp"
#include "platform/platform_hal.hpp"
#include "safety/rollback_store.hpp"

// Executes a rollback plan via the actuator interface.
// Returns true if execution succeeded.
Expand Down Expand Up @@ -45,3 +46,30 @@ inline bool execute_rollback_plan(
RAPSConfig::WATCHDOG_MS / 4
);
}

// Triggers an immediate rollback due to WNN constraints breach
inline bool trigger_wnn_immediate_rollback(
const RollbackPlan* rollback_store,
uint32_t rollback_count,
PhysicsState& active_state_pointer
) {
Comment on lines +52 to +55
if (rollback_count == 0) {
return false;
}

const RollbackPlan& latest_plan = rollback_store[rollback_count - 1];

std::string tx_id;
if (!execute_rollback_plan(latest_plan, tx_id)) {
return false;
}
Comment on lines +60 to +65

// Peek the latest snapshot without destructive reading
PhysicsState last_valid_snapshot;
if (StateSnapshotBuffer.try_peek_latest(last_valid_snapshot)) {
// Point the active state pointer to the last valid state
active_state_pointer = last_valid_snapshot;
}

Comment on lines +67 to +73
return true;
}
Comment on lines +50 to +75
10 changes: 10 additions & 0 deletions src/safety/rollback_store.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,16 @@
#include <optional>
#include <cstring>

#include "raps/telemetry/telemetry_ring_buffer.hpp"
#include "itl/itl_state_snapshot.hpp"

// Continuous, statically allocated snapshot buffer
inline raps::telemetry::TelemetryRingBuffer<PhysicsState, 64> StateSnapshotBuffer;

inline void store_state_snapshot_tick(const PhysicsState& state) {
StateSnapshotBuffer.try_push(state);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Keep snapshot writes from stalling after 64 ticks

Because TelemetryRingBuffer::try_push drops writes once (w - r) >= CapacityPow2 and StateSnapshotBuffer is never popped anywhere in the repo (only this push and the rollback peek are referenced), the snapshot store becomes permanently full after 64 calls. In any longer-running mission, a later WNN rollback restores the 64th stored snapshot instead of the latest safe state, so this needs overwrite/eviction semantics or another way to advance the read index.

Useful? React with 👍 / 👎.

}

inline void store_rollback_plan(
RollbackPlan* rollback_store,
uint32_t& rollback_count,
Expand Down
Loading