Functional Specification — acc-simulation

As-built functional specification — reconstructed from acc-simulation, nine-axis decomposition, typed fills.

comms safety supervision command actuation localization motion_control perception power_management cyclic_scheduler
10
modules
90
requirements (9-axis)
375
value slots
16
deterministic fills
214
interpretive fills
100
findings (gaps)
Every requirement is generated blind to the source code from the functional description, driven by a fixed nine-axis checklist (input/output domain, saturation, sign/units, fault reaction, timing, resource limits, state, concurrency). Values are then filled from the code and typed by trust: green = parsed named constant/enum (deterministic), yellow = LLM read of control flow (review required), FINDING = the behaviour is absent in the code (an honest gap, not an invented requirement).

comms ★ showcased

REQ-COM-001Input DomainhighSystem time input validity
The module shall accept tick_ms_ values in the range [0, USER-QUESTION] and shall treat tick_ms_ as monotonically increasing throughout system operation.
interpretive · review min_tick_ms0 — minimum valid system time in milliseconds (typically 0) (link_monitor.cpp:40-45: code computes age as (bus.tick_ms_ - last_frame_tick_ms_) and never validates a lower bound; if no frame ever received, ms_since_last defaults to cal_.link_lost_timeout_ms_ (a positive value), implying tick_ms_ >= 0. The uint64_t type (signal_bus.hpp) naturally enforces non-negativity.)
user-question max_tick_msUSER-QUESTION — maximum valid system time in milliseconds before overflow concern (The code does not specify a practical maximum; this is a system integration decision (e.g., mission duration, reboot policy). The type permits up to UINT64_MAX.)
REQ-COM-002Output DomainmediumLink status packet loss percentage bounds
The module shall constrain the packet loss percentage field in link_status_ to the range [0.0, 100.0] percent.
interpretive · review min_packet_loss_pct0.0 — minimum packet loss percentage (typically 0.0) (link_monitor.cpp:53: packet_loss_pct = std::clamp(packet_loss_pct, 0.0f, 100.0f); explicitly clamps lower bound to 0.0.)
interpretive · review max_packet_loss_pct100.0 — maximum packet loss percentage (typically 100.0) (link_monitor.cpp:53: packet_loss_pct = std::clamp(packet_loss_pct, 0.0f, 100.0f); explicitly clamps upper bound to 100.0.)
REQ-COM-003Saturation / ClampinghighLink health metric saturation
The module shall clamp the latency field in link_status_ to at most FINDING ms and shall clamp the time_since_last_valid_frame field to at most FINDING ms.
finding max_latency_clamp_msFINDING — upper saturation limit for reported latency to prevent overflow artifacts (link_monitor.cpp:59: status.latency_ms_ = static_cast<float>(ms_since_last); no clamp is applied. The value is derived from ms_since_last (uint32_t), which can reach up to UINT32_MAX before cast to float.)
finding max_age_clamp_msFINDING — upper saturation limit for frame age to prevent overflow artifacts (link_monitor.cpp:40-46: ms_since_last is computed as (bus.tick_ms_ - last_frame_tick_ms_) cast to uint32_t, or defaults to cal_.link_lost_timeout_ms_. Line 46: status.ms_since_last_frame_ = ms_since_last; no clamp is applied.)
REQ-COM-004Sign / UnitsmediumTimestamp and timing unit consistency
The module shall interpret tick_ms_, link_lost_timeout_ms_, link_degraded_timeout_ms_, link_max_latency_ms_, and all timestamp fields as non-negative integers in units of milliseconds, and shall compute all time differences as (later_time - earlier_time) assuming monotonic advancement.
interpretive · review time_unitmilliseconds — unit of time measurement for all timing fields (expected: milliseconds) (Variable naming throughout: tick_ms_, rx_tick_ms_, last_frame_tick_ms_, link_lost_timeout_ms_, link_degraded_timeout_ms_, link_max_latency_ms_, ms_since_last_frame_, latency_ms_. The '_ms_' suffix uniformly denotes milliseconds.)
REQ-COM-005Fault / Error ReactionhighInvalid frame and authentication failure handling
The module shall set command_authentic_ to false when sequence number validation fails, shall set replay_detected_ to true when a non-advancing sequence is detected, shall set remote_command_.valid to false when decoding fails, and shall transition link_status_.state to link_state_t::lost when time since last valid frame exceeds link_lost_timeout_ms_.
interpretive · review auth_fail_valuefalse — value assigned to command_authentic_ on authentication failure (expected: false) (command_authenticator.cpp:14,22: bus.command_authentic_ = false; assigned on invalid command (line 14) and on replay/non-advancing sequence (line 22).)
interpretive · review replay_true_valuetrue — value assigned to replay_detected_ when replay attack identified (expected: true) (command_authenticator.cpp:21: bus.replay_detected_ = true; assigned when cmd.sequence_ <= last_authentic_sequence_ (non-advancing sequence).)
interpretive · review invalid_command_valuefalse — value assigned to command validity flag on decode failure (expected: false) (command_decoder.cpp:38: cmd.valid_ = false; assigned when frame.valid_ is false or frame.length_ < min_payload_length (decode failure).)
interpretive · review link_lost_statelink_state_t::lost — state value indicating complete link loss (expected: lost or equivalent enum) (link_monitor.cpp:64: state = link_state_t::lost; assigned when ms_since_last >= cal_.link_lost_timeout_ms_.)
REQ-COM-006TiminghighLink state transition timing and freshness thresholds
The module shall transition link_status_.state from ok to degraded when time since last valid frame exceeds cal_.link_degraded_timeout_ms_ or when latency exceeds cal_.link_max_latency_ms_ or when packet loss exceeds cal_.link_max_packet_loss_pct_, and shall transition to lost when time since last valid frame exceeds cal_.link_lost_timeout_ms_.
interpretive · review degraded_threshold_mscal_.link_degraded_timeout_ms_ — time threshold in ms for ok-to-degraded transition based on frame age (link_monitor.cpp:65-66: else if (ms_since_last >= cal_.link_degraded_timeout_ms_) { state = link_state_t::degraded; } — transition to degraded when frame age exceeds this calibration parameter.)
interpretive · review latency_threshold_mscal_.link_max_latency_ms_ — latency threshold in ms for ok-to-degraded transition (link_monitor.cpp:73: if (status.latency_ms_ > cal_.link_max_latency_ms_ || ...) { state = link_state_t::degraded; } — transition to degraded when latency exceeds this calibration parameter.)
interpretive · review packet_loss_threshold_pctcal_.link_max_packet_loss_pct_ — packet loss percentage threshold for ok-to-degraded transition (link_monitor.cpp:74: if (... || status.packet_loss_pct_ > cal_.link_max_packet_loss_pct_) { state = link_state_t::degraded; } — transition to degraded when packet loss exceeds this calibration parameter.)
interpretive · review lost_threshold_mscal_.link_lost_timeout_ms_ — time threshold in ms for transition to lost state (link_monitor.cpp:63-64: if (ms_since_last >= cal_.link_lost_timeout_ms_) { state = link_state_t::lost; } — transition to lost when frame age exceeds this calibration parameter.)
REQ-COM-007Resource LimitsmediumSliding window packet loss buffer bounds
The module shall limit the sliding window used for packet loss estimation to at most FINDING sequence numbers and shall bound sequence number gap tracking to at most FINDING gaps.
finding max_window_sizeFINDING — maximum number of sequence numbers tracked in the sliding window for loss calculation (link_monitor.cpp:24-25,28-29: received_count_ and expected_count_ are unbounded uint32_t accumulators. No sliding window is implemented; counts grow monotonically without pruning old entries.)
finding max_tracked_gapsFINDING — maximum number of sequence gaps that can be tracked simultaneously (link_monitor.cpp:22-25: sequence gap is computed as (cmd.sequence_ - last_sequence_) and immediately folded into expected_count_. No data structure tracks individual gaps; only aggregate counts exist.)
REQ-COM-008StatehighLink state machine transitions and initialization
The module shall initialize link_status_.state to REVIEW on first execution, shall maintain state until transition conditions are met, and shall permit state transitions in the order lost ↔ degraded ↔ ok (bidirectional between adjacent states; direct lost ↔ ok if conditions jump), where each state persists until its exit condition (frame reception for lost/degraded, threshold violation for ok) is satisfied.
review initial_link_stateREVIEW — initial state of link on module startup before any frames received (expected: lost or ok) (AMBIGUOUS: The code logic produces 'lost' on first cycle if no frame arrives, but the signal_bus_t constructor's initial state for link_status_.state_ is not visible in the provided files. Human must verify signal_bus default initialization.)
interpretive · review state_transition_sequencelost ↔ degraded ↔ ok (bidirectional between adjacent states; direct lost ↔ ok if conditions jump) — allowed state transition paths (e.g., lost↔degraded↔ok or ok→degraded→lost→ok) (link_monitor.cpp:63-79: State is recomputed each cycle from current conditions: ms_since_last determines base state (lost >= link_lost_timeout_ms_, degraded >= link_degraded_timeout_ms_, else ok); then quality metrics can downgrade ok → degraded. No hysteresis or state memory prevents any transition; fresh frame can immediately move lost → ok (ms_since_last = 0), and threshold violations can move ok → degraded or degraded → lost.)
REQ-COM-009ConcurrencyhighExecution order dependencies and signal consistency
The module shall execute its submodules in the order [USER-QUESTION] within each cycle, shall ensure that command_authentic_ and replay_detected_ reflect the authentication result of the current cycle's link_frame_ before link_monitor reads them, and shall ensure all alive_counters_ are incremented exactly once per cycle in their respective submodule.
user-question execution_order_sequenceUSER-QUESTION — ordered list of submodule execution: radio_link_receiver, command_decoder, command_authenticator, link_monitor (The execution order is an integration-level decision made by the main control loop (likely in a module not provided here). The comms module does not self-schedule its submodules.)

safety

REQ-SAF-001Input DomainhighValid ranges for safety-critical inputs
The module shall treat pose as valid only when bus.pose_.validity is true and bus.pose_.confidence is at least cal_.pose_min_confidence_, and shall treat speed values outside the range [FINDING, FINDING] as invalid.
interpretive · review pose_min_confidencecal_.pose_min_confidence_ — Minimum confidence level required for pose validity (fault_memory.cpp:50 - `if (!bus.pose_.valid_ || bus.pose_.confidence_ < cal_.pose_min_confidence_)` treats pose as invalid when confidence is below the calibration parameter)
finding min_speed_mpsFINDING — Minimum admissible speed value (likely 0.0 m/s) (fault_memory.cpp:50-52, geofence_monitor.cpp:17-23 - no validation of speed range lower bound)
finding max_speed_mpsFINDING — Maximum admissible speed value for validity checking (fault_memory.cpp:50-52, entire safety module - no validation of speed upper bound)
REQ-SAF-002Output DomainhighValid ranges and consistency for safety outputs
The module shall ensure bus.geofence_.distance_to_boundary_m is non-negative, bus.safe_stop_.target_decel_mps2 is within [FINDING, FINDING], bus.health_ is one of {health_state_t::ok, health_state_t::degraded, health_state_t::critical}, and bus.faults_.size() does not exceed 64.
finding min_decel_mps2FINDING — Minimum (least aggressive) deceleration value (safe_stop_controller.cpp:21-22, entire safety module - no validation of deceleration range)
finding max_decel_mps2FINDING — Maximum (most aggressive) deceleration value (safe_stop_controller.cpp:21-22 - no validation of deceleration upper bound)
interpretive · review valid_health_states{health_state_t::ok, health_state_t::degraded, health_state_t::critical} — Set of valid health states: ok, degraded, critical (health_monitor.cpp:13-20 - `bus.health_ = health_state_t::critical` (line 14), `health_state_t::degraded` (line 17), `health_state_t::ok` (line 19))
deterministic max_faults64 — Maximum number of concurrent faults allowed in fault list (src/app/ugv_types.hpp: `static constexpr uint32_t max_faults = 64;`)
REQ-SAF-003Saturation / ClampinghighGeofence boundary and deceleration clamping
The module shall clamp bus.geofence_.distance_to_boundary_m to a minimum of FINDING, shall clamp bus.safe_stop_.target_decel_mps2 to the range [FINDING, FINDING], and shall clamp bus.estop_.target_decel_mps2 to cal_.emergency_decel_mps2_.
finding min_distance_mFINDING — Minimum distance to boundary (non-negative requirement) (geofence_monitor.cpp:38-43 - distance_to_boundary_m_ is computed but never clamped to a minimum)
finding min_safe_decel_mps2FINDING — Minimum safe stop deceleration (safe_stop_controller.cpp:21-22 - target_decel_mps2 is assigned but never clamped to a range)
finding max_safe_decel_mps2FINDING — Maximum safe stop deceleration (safe_stop_controller.cpp:21-22 - no clamping of target_decel_mps2 to maximum)
interpretive · review emergency_decel_mps2cal_.emergency_decel_mps2_ — Fixed emergency deceleration value for E-stop conditions (safe_stop_controller.cpp:22 - `emergency ? cal_.emergency_decel_mps2_ : cal_.safe_stop_decel_mps2_`)
REQ-SAF-004Sign / UnitsmediumSign conventions and unit consistency for safety parameters
The module shall interpret geofence boundaries as signed coordinates in meters with X-axis and Y-axis as defined by USER-QUESTION, shall compute distance_to_boundary_m as an unsigned magnitude in meters, shall interpret all deceleration values as positive magnitudes in m/s², and shall compute speed from pose in meters per second (m/s).
user-question coordinate_frameUSER-QUESTION — Reference frame for X and Y coordinates (e.g., ENU, vehicle, map) (The code uses X and Y coordinates from pose but does not specify the coordinate frame (ENU, vehicle, map, etc.). This must be documented in the pose_t interface specification or system architecture.)
interpretive · review speed_unitsmeters per second (m/s) — Units for speed field in pose (m/s assumed) (safe_stop_controller.cpp:24 - `bus.pose_.speed_mps_` field naming convention uses `_mps` suffix; estop_handler.cpp:31 compares against `cal_.standstill_speed_mps_`)
REQ-SAF-005Fault / Error ReactionhighSafety responses to invalid inputs and fault conditions
The module shall treat pose as invalid when bus.pose_.validity is false or confidence is below cal_.pose_min_confidence_, shall activate E-stop when bus.link_status_ indicates link_state_t::lost, shall activate E-stop when bus.collision_.imminent is true, shall set bus.health_ to health_state_t::critical when bus.critical_fault_active_ is true or bus.watchdog_ok_ is false, and shall activate safe stop when bus.geofence_.breach is true.
interpretive · review pose_min_confidence_threshcal_.pose_min_confidence_ — Confidence threshold below which pose is treated as invalid (fault_memory.cpp:50 - `bus.pose_.confidence_ < cal_.pose_min_confidence_` is the exact threshold check for invalidity)
interpretive · review link_lost_statelink_state_t::lost — Link status value indicating communication loss (estop_handler.cpp:22-23 - `if (bus.link_status_.state_ == link_state_t::lost)` triggers E-stop; fault_memory.cpp:37-38 same check for fault)
interpretive · review critical_health_statehealth_state_t::critical — Health state value indicating critical condition (health_monitor.cpp:13-14 - `if (bus.critical_fault_active_) { bus.health_ = health_state_t::critical; }`)
REQ-SAF-006TiminghighTiming requirements for fault confirmation, debouncing, and latching
The module shall confirm a fault after 3 consecutive cycles of detection, shall latch E-stop active state until both all triggers clear and vehicle speed drops below cal_.standstill_speed_mps_ for at least FINDING cycles, shall increment alive counters bus.alive_counters_[module_id::geofence], bus.alive_counters_[module_id::safe_stop], and bus.alive_counters_[module_id::estop] every USER-QUESTION milliseconds, and shall timestamp estop activation with bus.tick_ms_ which increments monotonically.
deterministic confirmation_count3 — Number of consecutive cycles required to confirm a fault (src/safety/fault_memory.hpp: `static constexpr uint32_t confirmation_count_ = 3;`)
interpretive · review standstill_speed_mpscal_.standstill_speed_mps_ — Speed threshold for standstill detection (estop_handler.cpp:31 - `const bool standstill = bus.pose_.speed_mps_ <= cal_.standstill_speed_mps_;` and safe_stop_controller.cpp:24)
finding standstill_duration_cyclesFINDING — Number of cycles vehicle must remain below standstill speed (estop_handler.cpp:45-46 - standstill check is instantaneous, no duration debouncing)
user-question cycle_period_msUSER-QUESTION — Expected period between run() invocations in milliseconds (The modules do not control or know their own invocation period. This is a system-level scheduling parameter that must be provided by the integration/scheduling layer.)
REQ-SAF-007Resource LimitsmediumBounded fault list and buffer management
The module shall limit bus.faults_ to at most USER-QUESTION entries, shall handle fault list overflow by ignore_new (drop new entries when full), shall bound the occurrence count per fault entry to USER-QUESTION, and shall ensure alive counter overflow behavior follows wrap (unsigned integer wraparound per C++ standard).
user-question max_faults_limitUSER-QUESTION — Maximum number of fault entries in the fault list (The value max_faults is referenced but its definition is in signal_bus.hpp (not provided). This is a system-wide constant defining fault array capacity.)
interpretive · review overflow_policyignore_new (drop new entries when full) — Behavior when max_faults is reached (e.g., ignore_new, drop_oldest, deduplicate) (fault_memory.cpp:86-88 - `if (bus.faults_.size() >= max_faults) { continue; }` drops new fault entries when the list is full)
user-question max_occurrence_countUSER-QUESTION — Maximum value for fault occurrence counter before saturation (The occurrence_count_ field is incremented unbounded (line 98). The maximum is determined by the fault_entry_t.occurrence_count_ field type, which must be specified in signal_bus.hpp.)
interpretive · review counter_overflow_behaviorwrap (unsigned integer wraparound per C++ standard) — Behavior of alive counters at maximum value (wrap or saturate) (geofence_monitor.cpp:11, safe_stop_controller.cpp:9, estop_handler.cpp:13 - `++bus.alive_counters_[...]` with no saturation logic; uint32_t type wraps per C++ standard)
REQ-SAF-008StatehighE-stop state machine transitions and entry actions
The module shall initialize E-stop state to INACTIVE (active_=false, source_=estop_source_t::none) with active_=false, shall transition from INACTIVE (active_=false) to ACTIVE_LATCHED (active_=true) when any of {local_button_pressed_, collision_imminent_, link_lost, geofence_breach_, remote_estop_request_} is true and guard !active_ holds, shall remain in ACTIVE_LATCHED (active_=true) while any trigger remains true by updating source_ to Priority order: local_button > obstacle > link_loss > geofence > remote_command (first-match in if-else chain), shall transition from ACTIVE_LATCHED (active_=true) to INACTIVE (active_=false) only when all triggers are false AND vehicle speed is below cal_.standstill_speed_mps_ and guard active_ && !any_trigger && standstill holds, and shall execute entry actions of ACTIVE_LATCHED (active_=true) by setting active_=true, recording activated_tick_ms_ from bus.tick_ms_, and recording trigger source_.
interpretive · review initial_estop_stateINACTIVE (active_=false, source_=estop_source_t::none) — Initial state of E-stop state machine (estop_handler.hpp:24-26 - member initializers `bool active_ = false;`, `estop_source_t source_ = estop_source_t::none;`, `uint64_t activated_tick_ms_ = 0;`)
interpretive · review inactive_stateINACTIVE (active_=false) — State identifier for normal operation (no E-stop) (estop_handler.cpp:34-41,46-47 - active_=false represents the inactive state; active_=true represents active/latched)
interpretive · review active_latched_stateACTIVE_LATCHED (active_=true) — State identifier for latched E-stop condition (estop_handler.cpp:34-41 - `active_ = true;` on trigger rising edge (line 36), latching logic in lines 42-50)
interpretive · review estop_trigger_sources{local_button_pressed_, collision_imminent_, link_lost, geofence_breach_, remote_estop_request_} — Set of all conditions that can trigger E-stop activation (estop_handler.cpp:18-28 - evaluates local_button_pressed_ (line 18), bus.collision_.collision_imminent_ (line 20), bus.link_status_.state_ == link_state_t::lost (line 22), bus.geofence_.breach_ (line 24), bus.remote_command_.estop_request_ (line 26))
interpretive · review highest_priority_sourcePriority order: local_button > obstacle > link_loss > geofence > remote_command (first-match in if-else chain) — Source selection logic based on evaluation order priority (estop_handler.cpp:18-28 - if-else chain: local_button (line 18), obstacle (line 20), link_loss (line 22), geofence (line 24), remote_command (line 26))
interpretive · review standstill_threshold_mpscal_.standstill_speed_mps_ — Speed threshold for standstill condition in state transition (estop_handler.cpp:31,45 - `const bool standstill = bus.pose_.speed_mps_ <= cal_.standstill_speed_mps_;` used in latch release guard)
REQ-SAF-009ConcurrencymediumExecution order dependencies and shared data access
The module shall assume fault_memory runs after all fault-generating modules per USER-QUESTION, shall assume bus.tick_ms_ is updated before run() is called in each cycle, shall assume inject_local_estop() is called by USER-QUESTION before each run() cycle per USER-QUESTION, shall read all input bus fields atomically within a single run() invocation without intermediate updates, and shall write all output bus fields atomically before returning from run() to ensure downstream consumers see consistent state.
user-question execution_order_aouUSER-QUESTION — Assumption of use defining deterministic module execution order (The code comment implies fault_memory runs after fault-generating modules, but no explicit AOU-002 reference exists in the source. This assumption must be documented in the system architecture or MODULES.md.)
user-question estop_injection_callerUSER-QUESTION — Entity responsible for calling inject_local_estop() before run() (The comment indicates simulation/tests call inject_local_estop() but does not specify the production integration entity. This must be defined in the hardware abstraction layer interface specification or AOU-008.)
user-question estop_injection_aouUSER-QUESTION — Assumption of use defining inject_local_estop() calling contract (The code does not reference AOU-008. The assumption that inject_local_estop() is called before run() must be documented in the system integration specification or assumptions of use document.)

supervision ★ showcased

REQ-SUP-001Input DomainhighValid input signal ranges and enumeration domains
The module shall accept link_status_.state_ values from the set {lost, degraded, nominal}, health_ values from the set {critical, degraded, nominal}, estop_.source_ values from the set {local_button, obstacle, remote}, alive_counters_ array indices in range 0 to signal_bus_t::num_monitored_modules - 1, geofence_.distance_to_boundary_m_ in range FINDING to FINDING, and pose_.speed_mps_ in range 0.0 to FINDING.
interpretive · review link_state_enum_set{lost, degraded, nominal} — Complete enumeration of valid communication link states (lost, degraded, nominal) (degradation_manager.cpp:11-12 and takeover_monitor.cpp:22 check `link_state_t::lost` and `link_state_t::degraded`; implicit third state nominal (not checked but required for full coverage of a tri-state link health model). The code explicitly handles lost and degraded, leaving nominal as the remaining valid state.)
interpretive · review health_state_enum_set{critical, degraded, nominal} — Complete enumeration of valid system health states (critical, degraded, nominal) (degradation_manager.cpp:14 checks `health_state_t::critical`, line 20 checks `health_state_t::degraded`. Implicit third state nominal (not explicitly tested but required for completeness). The code handles critical and degraded explicitly, leaving nominal as the baseline.)
interpretive · review estop_source_enum_set{local_button, obstacle, remote} — Complete enumeration of valid emergency stop sources (local_button, obstacle, remote, etc.) (vehicle_state_machine.cpp:13-14 enumerates `estop_source_t::local_button` and `estop_source_t::obstacle` as emergency sources. Line 21 distinguishes soft_estop (non-emergency) via `!is_emergency_estop`, implying at least one other source (likely remote). The code explicitly names local_button and obstacle; remote is inferred from the safe_stop logic.)
interpretive · review alive_counters_min_index0 — Minimum valid index into alive_counters_ array (typically 0) (watchdog.cpp:17 loop starts at `uint32_t i = 0`, standard C++ array indexing convention. The for-loop explicitly begins at index 0.)
interpretive · review alive_counters_max_indexsignal_bus_t::num_monitored_modules - 1 — Maximum valid index into alive_counters_ array (number of monitored modules minus 1) (watchdog.cpp:17 loop condition `i < signal_bus_t::num_monitored_modules` yields max valid index num_monitored_modules - 1. Standard zero-indexed array upper bound.)
finding geofence_distance_min_mFINDING — Minimum physically valid distance to geofence boundary (may be negative for breach) (degradation_manager.cpp:24 compares `bus.geofence_.distance_to_boundary_m_ <= cal_.geofence_margin_m_` but does not validate a minimum bound. No lower-bound check or clamp on distance_to_boundary_m_ is present.)
finding geofence_distance_max_mFINDING — Maximum physically valid distance to geofence boundary (degradation_manager.cpp:24 reads `bus.geofence_.distance_to_boundary_m_` without any upper-bound validation or sanitization.)
interpretive · review speed_min_mps0.0 — Minimum valid vehicle speed (typically 0.0 or small negative for reverse) (vehicle_state_machine.cpp:39 tests `bus.pose_.speed_mps_ <= cal_.standstill_speed_mps_` for standstill; takeover_monitor.cpp:18 tests `std::fabs(bus.plausible_command_.speed_mps_) > 0.0f` (implying non-negative semantics). The code treats speed as non-negative magnitude (fabs usage, standstill test >= 0).)
finding speed_max_mpsFINDING — Maximum valid vehicle speed from sensor or physical limit (vehicle_state_machine.cpp:39, vehicle_state_machine.cpp:42, takeover_monitor.cpp:18 all read speed values without any upper-bound validation or saturation.)
REQ-SUP-002Output DomainhighValid output signal ranges and enumeration domains
The module shall emit vehicle_mode_ from the set {init, idle, remote, autonomous, safe_stop, emergency, fault}, previous_mode_ from the set {init, idle, remote, autonomous, safe_stop, emergency, fault}, degradation_level_ from the set {full, reduced_speed, geofence_hold, safe_stop_only}, remote_takeover_requested_ as boolean {true, false}, and watchdog_ok_ as boolean {true, false}.
interpretive · review vehicle_mode_enum_set{init, idle, remote, autonomous, safe_stop, emergency, fault} — Complete enumeration of vehicle operating modes (init, idle, remote, autonomous, safe_stop, emergency, fault) (vehicle_state_machine.cpp:47-118 switch statement enumerates all seven modes: init (line 47), idle (55), remote (67), autonomous (68), safe_stop (88), emergency (98), fault (114). Complete coverage in state machine.)
interpretive · review degradation_level_enum_set{full, reduced_speed, geofence_hold, safe_stop_only} — Complete enumeration of functional degradation levels (full, reduced_speed, geofence_hold, safe_stop_only) (degradation_manager.cpp lines 9, 16, 22, 26, 28 assign all four values: full (line 28), reduced_speed (22), geofence_hold (26), safe_stop_only (16). Complete enumeration in degradation logic.)
interpretive · review bool_value_set{true, false} — Valid boolean values {true, false} (C++ bool type per language standard. watchdog.cpp:12,25 assigns true/false to watchdog_ok_; takeover_monitor.cpp:24-25 assigns boolean expression to remote_takeover_requested_.)
REQ-SUP-003Saturation / ClampingmediumWatchdog counter overflow and initialization cycle saturation
The module shall saturate init_cycles_ counter at FINDING to prevent overflow during prolonged init state, and shall detect alive_counters_ increment via modulo-2^32 (4294967296) arithmetic to tolerate counter wraparound.
finding init_cycles_maxFINDING — Maximum value for init_cycles_ counter before saturation (e.g., UINT32_MAX or a practical limit) (vehicle_state_machine.cpp:49 increments `init_cycles_` unconditionally with no saturation or overflow protection (uint32_t wraps at 2^32).)
interpretive · review alive_counter_modulo2^32 (4294967296) — Modulo base for alive counter wraparound detection (typically 2^32 for uint32_t) (watchdog.cpp:18 compares uint32_t counters directly with `==`; vehicle_state_machine.cpp:28 increments `++bus.alive_counters_[module_id::state_machine]` (uint32_t). Natural modulo-2^32 wraparound per C++ unsigned integer semantics.)
REQ-SUP-004Sign / UnitsmediumPhysical units and sign conventions for continuous signals
The module shall interpret geofence_.distance_to_boundary_m_ and geofence_margin_m_ in meters with positive values indicating distance outside the boundary and negative values indicating breach depth, and shall interpret pose_.speed_mps_, plausible_command_.speed_mps_, and standstill_speed_mps_ in meters per second with non-negative values indicating forward speed magnitude.
interpretive · review distance_unitmeters — Physical unit for distance measurements (meters as declared in interface) (Variable names: degradation_manager.cpp:24 `geofence_.distance_to_boundary_m_` and `cal_.geofence_margin_m_` (suffix `_m_` denotes meters). Standard SI unit naming convention.)
interpretive · review speed_unitmeters per second — Physical unit for speed measurements (meters per second as declared in interface) (Variable names: vehicle_state_machine.cpp:39 `bus.pose_.speed_mps_` and `cal_.standstill_speed_mps_` (suffix `_mps_` denotes meters per second). Standard SI unit naming convention.)
REQ-SUP-005Fault / Error ReactionhighInvalid input and fault condition handling
The module shall transition to vehicle_mode FINDING and set degradation_level_ to FINDING when any input signal (link_status_.state_, health_, estop_.active_, geofence_.breach_, remote_command_.valid_, plausible_command_.plausible_) is detected as invalid or out-of-domain, and shall set watchdog_ok_ to false when any alive_counters_ element exhibits non-monotonic behavior indicating module stall or counter corruption.
finding mode_on_invalid_inputFINDING — Operating mode to enter on invalid input detection (likely emergency or fault) (No input validation logic exists in the supervision module files. The code reads all inputs (link_status_, health_, estop_, geofence_, remote_command_, plausible_command_) without any validity checks or domain enforcement.)
finding degradation_on_invalid_inputFINDING — Degradation level to set on invalid input detection (likely safe_stop_only) (No input validation or invalid-input handling exists in supervision module. degradation_manager.cpp reads inputs directly without validation.)
interpretive · review watchdog_on_counter_anomalyfalse — Watchdog status value on counter anomaly detection (must be false to indicate failure) (watchdog.cpp:19 sets `ok = false` when counter anomaly detected (line 18: `bus.alive_counters_[i] == last_counters_[i]`), then line 25 assigns `bus.watchdog_ok_ = ok`. False indicates watchdog failure.)
REQ-SUP-006TiminghighCycle timing, watchdog period, and debounce requirements
The module shall execute with cycle period USER-QUESTION, shall require init_cycles_ to reach 3 before transitioning from init to idle state, shall detect alive_counters_ stalls when any counter fails to increment for 1 consecutive cycles, and shall evaluate standstill condition when pose_.speed_mps_ remains below standstill_speed_mps_ for at least FINDING cycles.
user-question supervision_cycle_period_msUSER-QUESTION — Fixed cycle time for supervision module execution in milliseconds (The module code does not and should not hardcode its scheduling period. This is a deployment/integration configuration decision (RTOS tick rate, cyclic executive period, etc.).)
deterministic self_test_cycles3 — Number of init cycles required for subsystem initialization before transitioning to idle (src/supervision/vehicle_state_machine.hpp: `static constexpr uint32_t self_test_cycles_ = 3;`)
interpretive · review watchdog_timeout_cycles1 — Number of consecutive cycles without alive counter increment before declaring stall (watchdog.cpp:18 triggers fault immediately when `bus.alive_counters_[i] == last_counters_[i]` (no increment in a single cycle). No debounce or multi-cycle tolerance; one missed increment = fault.)
finding standstill_confirmation_cyclesFINDING — Number of consecutive low-speed cycles required to confirm standstill state (vehicle_state_machine.cpp:39 evaluates standstill as instantaneous condition `bus.pose_.speed_mps_ <= cal_.standstill_speed_mps_` with no debounce or multi-cycle confirmation.)
REQ-SUP-007Resource LimitshighArray bounds and monitored module count limits
The module shall monitor exactly 12 modules via the alive_counters_ array of length signal_bus_t::num_monitored_modules, shall reject configurations where 12 exceeds FINDING, and shall access alive_counters_ elements only within index range 0 to signal_bus_t::num_monitored_modules - 1.
deterministic num_monitored_modules12 — Number of modules being monitored for liveness (determines array iteration bounds) (src/app/signal_bus.hpp: `static constexpr uint32_t num_monitored_modules = 12;`)
interpretive · review alive_counters_array_lengthsignal_bus_t::num_monitored_modules — Declared length of alive_counters_ array (must match num_monitored_modules) (watchdog.cpp:17,23 references `signal_bus_t::num_monitored_modules` as the array length; watchdog.hpp:23 declares `std::array<uint32_t, signal_bus_t::num_monitored_modules> last_counters_`. Declared array size.)
finding max_monitored_modules_limitFINDING — Maximum number of modules that can be monitored (design or resource constraint) (No validation or limit check on num_monitored_modules exists in the supervision module. watchdog.cpp:17 loops to num_monitored_modules without bound checking.)
interpretive · review alive_counters_min_index0 — Minimum valid array index (typically 0) (watchdog.cpp:17 loop starts at `uint32_t i = 0`. Standard array indexing from zero.)
interpretive · review alive_counters_max_indexsignal_bus_t::num_monitored_modules - 1 — Maximum valid array index (num_monitored_modules - 1) (watchdog.cpp:17 loop condition `i < signal_bus_t::num_monitored_modules` yields max index num_monitored_modules - 1.)
REQ-SUP-008StatehighState machine structure, initial state, and entry actions
The module shall initialize vehicle_mode_ to REVIEW, shall execute entry action FINDING upon entering init state, shall populate previous_mode_ with the value of vehicle_mode_ from the prior cycle before updating vehicle_mode_, shall classify estop_.source_ values {local_button, obstacle} as emergency triggers and {remote} (and any estop_source_t not in {local_button, obstacle}) as safe-stop triggers, shall evaluate state transitions in priority order emergency_trigger > safe_stop_trigger > operational_transitions, and shall prevent transition from fault state except via none (no automatic recovery).
review initial_vehicle_modeREVIEW — Initial operating mode on module startup (must be init per state machine diagram) (The supervision module does not initialize vehicle_mode_ to init; it assumes this is done by the signal bus or application initialization. This initialization responsibility is ambiguous and should be clarified.)
finding init_entry_actionFINDING — Action performed on init state entry (reset init_cycles_ to 0 per state machine) (vehicle_state_machine.cpp:48-52 handles init state but does NOT reset init_cycles_ to 0 on entry. Line 49 only increments it. If the state machine re-enters init (e.g., after a reset), init_cycles_ will not be cleared.)
interpretive · review emergency_estop_sources{local_button, obstacle} — Set of estop_source_t values that trigger emergency mode (e.g., local_button, obstacle) (vehicle_state_machine.cpp:11-15 function `is_emergency_estop` explicitly lists `estop_source_t::local_button` and `estop_source_t::obstacle` as emergency triggers (line 13-14).)
interpretive · review safe_stop_estop_sources{remote} (and any estop_source_t not in {local_button, obstacle}) — Set of estop_source_t values that trigger safe_stop mode (e.g., remote soft estop) (vehicle_state_machine.cpp:21 defines soft_estop as `bus.estop_.active_ && !is_emergency_estop(bus.estop_)`, meaning any active estop source NOT classified as emergency is a safe-stop trigger. Only remote is named in the earlier slot resolution; code uses complement logic.)
interpretive · review transition_priority_orderemergency_trigger > safe_stop_trigger > operational_transitions — Priority sequence for evaluating competing transitions (emergency > safe_stop > operational per description) (vehicle_state_machine.cpp:56-57, 69-72, 90-91 all check emergency_trigger first (highest priority), then safe_stop_trigger, then operational transitions (remote/autonomous requests). Consistent if-else ordering enforces priority.)
interpretive · review fault_recovery_mechanismnone (no automatic recovery) — External intervention mechanism required to exit fault state (no automatic recovery per state machine) (vehicle_state_machine.cpp:114-117 fault state case always assigns `next = vehicle_mode_t::fault` with comment 'no automatic recovery'. Fault is a terminal state within the state machine.)
REQ-SUP-009ConcurrencyhighExecution order dependencies and shared data consistency
The module shall execute after all modules contributing to alive_counters_ have incremented their counters in the current cycle, shall execute after input signal providers (link status, health monitor, estop handler, geofence monitor, plausibility checker, remote command receiver) have updated their outputs for the current cycle, shall read alive_counters_ array elements atomically to detect increments without race conditions, and shall handle first-cycle execution with have_snapshot_ flag initialized to false to prevent false watchdog faults per AOU-007.
interpretive · review have_snapshot_initial_valuefalse — Initial value of snapshot validity flag on first cycle (must be false per AOU-007) (watchdog.hpp:24 declares `bool have_snapshot_ = false;` (explicit member initialization). This prevents false watchdog faults on the first cycle (line watchdog.cpp:14 guards comparison with `if (have_snapshot_)`).)

command

REQ-COM-001Input DomainhighRemote command input range validity
The module shall reject remote_command_ inputs where desired_speed_mps_ exceeds 2.0 * max(max_command_speed_mps_, max_reverse_speed_mps_) or desired_yaw_rate_radps_ exceeds 2.0 * max_command_yaw_rate_radps_, or where either value is non-finite (NaN or infinity), setting plausible_command_.plausible_ to false.
interpretive · review hard_plausibility_speed_limit_mps2.0 * max(max_command_speed_mps_, max_reverse_speed_mps_) — Gross out-of-range speed threshold for corruption detection (2x normal limits per declared behavior) (command_plausibility.cpp:28-29: `const float hard_speed_limit = 2.0f * std::max(cal_.max_command_speed_mps_, cal_.max_reverse_speed_mps_);` then checked at line 32 `std::fabs(req_speed) > hard_speed_limit`)
interpretive · review hard_plausibility_yaw_limit_radps2.0 * max_command_yaw_rate_radps_ — Gross out-of-range yaw rate threshold for corruption detection (2x normal limits per declared behavior) (command_plausibility.cpp:30: `const float hard_yaw_limit = 2.0f * cal_.max_command_yaw_rate_radps_;` then checked at line 33 `std::fabs(req_yaw) > hard_yaw_limit`)
interpretive · review plausibility_reject_valuefalse — Value assigned to plausible_command_.plausible_ flag when input is rejected (command_plausibility.cpp:34: `bus.plausible_command_.plausible_ = false;` when hard limits exceeded (lines 31-39))
REQ-COM-002Output DomainhighMotion setpoint output value ranges
The module shall ensure that motion_setpoint_.target_speed_mps_ remains within [FINDING, FINDING], motion_setpoint_.target_yaw_rate_radps_ remains within [FINDING, FINDING], and motion_setpoint_.target_accel_mps2_ remains within [-max_decel_mps2_, max_accel_mps2_].
finding min_output_speed_mpsFINDING — Minimum (most negative) speed setpoint output, corresponding to maximum reverse (setpoint_generator.cpp:30: `bus.motion_setpoint_.target_speed_mps_ = target_speed;` — no clamping applied to output)
finding max_output_speed_mpsFINDING — Maximum forward speed setpoint output (setpoint_generator.cpp:30: `bus.motion_setpoint_.target_speed_mps_ = target_speed;` — no clamping applied to output)
finding min_output_yaw_radpsFINDING — Minimum (most negative) yaw rate setpoint output (setpoint_generator.cpp:31-32: `bus.motion_setpoint_.target_yaw_rate_radps_ = bus.arbitrated_command_.yaw_setpoint_radps_;` — no clamping)
finding max_output_yaw_radpsFINDING — Maximum yaw rate setpoint output (setpoint_generator.cpp:31-32: `bus.motion_setpoint_.target_yaw_rate_radps_ = bus.arbitrated_command_.yaw_setpoint_radps_;` — no clamping)
interpretive · review min_output_accel_mps2-max_decel_mps2_ — Minimum (most negative) acceleration setpoint, representing maximum deceleration (setpoint_generator.cpp:28: `accel = std::clamp(accel, -cal_.max_decel_mps2_, cal_.max_accel_mps2_);`)
interpretive · review max_output_accel_mps2max_accel_mps2_ — Maximum acceleration setpoint output (setpoint_generator.cpp:28: `accel = std::clamp(accel, -cal_.max_decel_mps2_, cal_.max_accel_mps2_);`)
REQ-COM-003Saturation / ClampinghighCommand range clamping and rate limiting
The module shall clamp plausible_command_.speed_mps_ to the range [-max_reverse_speed_mps_, max_command_speed_mps_] and plausible_command_.yaw_rate_radps_ to the range [-max_command_yaw_rate_radps_, max_command_yaw_rate_radps_], and shall limit the per-cycle change in speed to at most max_command_speed_step_mps_ and the per-cycle change in yaw rate to at most max_command_yaw_step_radps_ relative to the last accepted command value.
interpretive · review min_clamp_speed_mps-max_reverse_speed_mps_ — Normal operating minimum speed clamp (negative for reverse) (command_plausibility.cpp:43-45: `float speed = std::clamp(req_speed, -cal_.max_reverse_speed_mps_, cal_.max_command_speed_mps_);`)
interpretive · review max_clamp_speed_mpsmax_command_speed_mps_ — Normal operating maximum speed clamp (command_plausibility.cpp:43-45: `float speed = std::clamp(req_speed, -cal_.max_reverse_speed_mps_, cal_.max_command_speed_mps_);`)
interpretive · review min_clamp_yaw_radps-max_command_yaw_rate_radps_ — Normal operating minimum yaw rate clamp (command_plausibility.cpp:46-48: `float yaw = std::clamp(req_yaw, -cal_.max_command_yaw_rate_radps_, cal_.max_command_yaw_rate_radps_);`)
interpretive · review max_clamp_yaw_radpsmax_command_yaw_rate_radps_ — Normal operating maximum yaw rate clamp (command_plausibility.cpp:46-48: `float yaw = std::clamp(req_yaw, -cal_.max_command_yaw_rate_radps_, cal_.max_command_yaw_rate_radps_);`)
interpretive · review max_speed_step_mpsmax_command_speed_step_mps_ — Maximum allowed per-cycle speed change for rate limiting (command_plausibility.cpp:51-53: `const float speed_lo = last_speed_mps_ - cal_.max_command_speed_step_mps_; const float speed_hi = last_speed_mps_ + cal_.max_command_speed_step_mps_; speed = std::clamp(speed, speed_lo, speed_hi);`)
interpretive · review max_yaw_step_radpsmax_command_yaw_step_radps_ — Maximum allowed per-cycle yaw rate change for rate limiting (command_plausibility.cpp:55-57: `const float yaw_lo = last_yaw_rate_radps_ - cal_.max_command_yaw_step_radps_; const float yaw_hi = last_yaw_rate_radps_ + cal_.max_command_yaw_step_radps_; yaw = std::clamp(yaw, yaw_lo, yaw_hi);`)
REQ-COM-004Sign / UnitsmediumSign conventions and unit consistency
The module shall interpret negative values of speed_mps_ as reverse motion with magnitude not exceeding max_reverse_speed_mps_, shall interpret yaw_rate_radps_ with sign convention USER-QUESTION, shall compute target_accel_mps2_ as (target_speed_mps - last_target_speed_mps) / period_s, and shall ensure all speed values are in meters per second, all yaw rates in radians per second, and all accelerations in meters per second squared throughout processing.
interpretive · review reverse_speed_magnitude_mpsmax_reverse_speed_mps_ — Maximum magnitude of reverse speed (stored as positive value per description) (command_plausibility.cpp:44: `-cal_.max_reverse_speed_mps_` used as lower clamp bound; negative speed values represent reverse motion with magnitude up to this limit)
user-question yaw_rate_sign_conventionUSER-QUESTION — Sign convention for yaw rate: which direction (left/right turn) corresponds to positive radians per second (USER DECISION REQUIRED: The code treats yaw rate as a signed value in radians/sec but does not specify the vehicle coordinate frame convention (positive left turn vs. positive right turn). This is a vehicle-level configuration choice.)
interpretive · review accel_computation_formula(target_speed_mps - last_target_speed_mps) / period_s — Formula used to derive acceleration from speed delta in setpoint generation (setpoint_generator.cpp:27: `float accel = (target_speed - last_target_speed_mps_) / period_s_;` where period_s_ = 0.020f (line 20))
REQ-COM-005Fault / Error ReactionhighAuthentication and command validity fault handling
The module shall set plausible_command_.plausible_ to false when command_authentic_ equals false or when remote_command_.valid_ equals false, shall select arbitrated_command_.source_ as command_source_t::safe_stop_controller when safe_stop_.active_ equals true regardless of other inputs, and shall set motion setpoint outputs to {speed: 0.0, yaw: 0.0} under these fault conditions.
interpretive · review auth_fail_plausible_valuefalse — Value of plausible_ flag when authentication fails (command_plausibility.cpp:13-14: `if (!bus.command_authentic_ || !bus.remote_command_.valid_) { bus.plausible_command_.plausible_ = false;`)
interpretive · review auth_fail_conditionfalse — Value of command_authentic_ that indicates authentication failure (command_plausibility.cpp:13: `if (!bus.command_authentic_` — authentication failure detected when command_authentic_ is false)
interpretive · review invalid_command_conditionfalse — Value of remote_command_.valid_ that indicates invalid command (command_plausibility.cpp:13: `if (!bus.command_authentic_ || !bus.remote_command_.valid_)` — invalid when valid_ is false)
interpretive · review safe_stop_source_valuecommand_source_t::safe_stop_controller — Arbitration source enumeration value indicating safe-stop has control (command_arbiter.cpp:16-17: `if (bus.safe_stop_.active_) { out.source_ = command_source_t::safe_stop_controller;`)
interpretive · review safe_stop_active_conditiontrue — Value of safe_stop_.active_ flag that indicates emergency stop condition (command_arbiter.cpp:16: `if (bus.safe_stop_.active_)` — safe-stop triggered when active_ flag is true)
interpretive · review fault_setpoint_values{speed: 0.0, yaw: 0.0} — Set of motion setpoint values applied during fault conditions (safe-stop, auth failure, invalid command) (command_arbiter.cpp:18-19 (safe-stop), 32-33 (none source), command_plausibility.cpp:15-16 (auth fail): all set speed and yaw to 0.0f)
REQ-COM-006TiminghighModule cycle execution and output staleness
The module shall execute with a cycle period of 20 milliseconds with tolerance USER-QUESTION, shall increment alive_counters_[module_id::command_arbiter] by 1 each cycle, and shall treat remote_command_ inputs as stale if not updated within FINDING milliseconds, setting plausible_command_.plausible_ to FINDING when staleness is detected.
interpretive · review cycle_period_ms20 — Fixed cycle time in milliseconds (setpoint_generator.hpp:20: `static constexpr float period_s_ = 0.020f;` — 20 milliseconds (0.020 seconds))
user-question cycle_jitter_tolerance_msUSER-QUESTION — Acceptable timing jitter around nominal cycle period (USER DECISION REQUIRED: The code assumes exactly 20ms cycles (setpoint_generator.hpp:20) but does not implement jitter detection or tolerance enforcement. Timing tolerance is a scheduler / RTOS configuration decision not enforced by this module.)
interpretive · review heartbeat_increment1 — Value added to alive counter each cycle (command_arbiter.cpp:11: `++bus.alive_counters_[module_id::command_arbiter];` — pre-increment operator adds 1)
finding command_staleness_timeout_msFINDING — Maximum age of remote_command_ before considered stale (command_plausibility.cpp entire file — no timestamp checking or staleness detection implemented)
finding stale_command_plausible_valueFINDING — Value of plausible_ flag when command input is stale (command_plausibility.cpp — no staleness detection, therefore no plausible_ assignment for stale condition)
REQ-COM-007Resource LimitslowHeartbeat counter overflow handling
The module shall ensure that the array index module_id::command_arbiter for alive_counters_[module_id::command_arbiter] remains within the allocated bounds of the alive_counters_ array, and shall handle overflow of the uint32_t heartbeat counter at value 4294967295 (UINT32_MAX) by wrap_to_zero.
interpretive · review command_arbiter_module_idmodule_id::command_arbiter — Array index corresponding to command_arbiter module ID (command_arbiter.cpp:11: `++bus.alive_counters_[module_id::command_arbiter];` — enum or constant value used as array index)
interpretive · review heartbeat_overflow_value4294967295 (UINT32_MAX) — Maximum value of uint32_t counter before overflow (command_arbiter.cpp:11: `++bus.alive_counters_[module_id::command_arbiter];` operates on implicit uint32_t array (standard practice); overflow at 2^32-1)
interpretive · review overflow_behaviorwrap_to_zero — Behavior when heartbeat counter reaches maximum value (natural wrap, explicit reset, etc.) (command_arbiter.cpp:11: `++bus.alive_counters_[...]` — C++ unsigned integer overflow wraps to 0 per language standard (well-defined modulo arithmetic))
REQ-COM-008StatehighCommand arbitration mode priority and transitions
The module shall select arbitrated_command_.source_ according to priority order safe_stop > autonomous > remote_operator > none where safe-stop has highest priority, shall transition to source command_source_t::autonomous_mission when vehicle_mode_ equals vehicle_mode_t::autonomous and no higher-priority source is active, shall transition to source command_source_t::remote_operator when remote command is plausible and authenticated and vehicle_mode_ equals REVIEW, and shall default to source command_source_t::none when no command source is active.
interpretive · review arbitration_priority_ordersafe_stop > autonomous > remote_operator > none — Ordered list of command source priorities from highest to lowest (command_arbiter.cpp:16-34: if-else chain: safe_stop_ (line 16) checked first, then autonomous mode (line 20), then plausible remote (line 26), else none (line 31))
interpretive · review autonomous_source_valuecommand_source_t::autonomous_mission — Arbitration source enumeration value for autonomous mode (command_arbiter.cpp:22: `out.source_ = command_source_t::autonomous_mission;` when vehicle_mode_ == autonomous)
interpretive · review autonomous_mode_conditionvehicle_mode_t::autonomous — Value of vehicle_mode_ that indicates autonomous operation (command_arbiter.cpp:20: `} else if (bus.vehicle_mode_ == vehicle_mode_t::autonomous) {`)
interpretive · review remote_operator_source_valuecommand_source_t::remote_operator — Arbitration source enumeration value for remote operator control (command_arbiter.cpp:27: `out.source_ = command_source_t::remote_operator;` when plausible_command_.plausible_ is true)
review remote_mode_conditionREVIEW — Value of vehicle_mode_ that permits remote operator control (AMBIGUITY: Remote operator is selected based only on plausible_command_.plausible_ (line 26), with NO explicit vehicle_mode_ check. This differs from the requirement pattern which expects a mode check. The code allows remote commands in any mode (other than safe-stop or autonomous), which may be intentional but contradicts the requirement's expectation of a specific remote_mode_condition.)
interpretive · review none_source_valuecommand_source_t::none — Arbitration source enumeration value when no command source is active (command_arbiter.cpp:31: `out.source_ = command_source_t::none;` in final else clause)
REQ-COM-009ConcurrencyhighIntra-cycle execution order and data dependencies
The module shall enforce execution order such that command_plausibility completes before command_arbiter which completes before setpoint_generator within each 20 ms cycle, shall ensure that plausible_command_ is fully written by command_plausibility before being read by command_arbiter, shall ensure that arbitrated_command_ is fully written by command_arbiter before being read by setpoint_generator, and shall prevent concurrent modification of shared state including {last_speed_mps_, last_yaw_rate_radps_, last_target_speed_mps_} across these stages.
interpretive · review cycle_time_ms20 — Cycle period within which ordered execution must complete (setpoint_generator.hpp:20: `static constexpr float period_s_ = 0.020f;` — 20 milliseconds)
interpretive · review shared_state_elements{last_speed_mps_, last_yaw_rate_radps_, last_target_speed_mps_} — Set of state variables shared across plausibility, arbitration, and setpoint generation stages that require data coherency (command_plausibility.hpp:21-22 (last_speed_mps_, last_yaw_rate_radps_); setpoint_generator.hpp:25 (last_target_speed_mps_) — private member state persisting across cycles)

actuation

REQ-ACT-001Input DomainhighValid ranges for motion command inputs
The module shall accept limited_accel_mps2 in the range [FINDING, FINDING] and limited_steering_rad in the range [FINDING, FINDING], and shall accept safe_stop target_decel_mps2 in the range [FINDING, FINDING].
finding min_accel_mps2FINDING — Minimum admissible longitudinal acceleration input (typically negative for deceleration) (drive_arbitrator.cpp:27-28 and brake_arbitrator.cpp:17)
finding max_accel_mps2FINDING — Maximum admissible longitudinal acceleration input (drive_arbitrator.cpp:27-28)
finding min_steering_input_radFINDING — Minimum admissible steering angle input (typically negative for left) (steering_arbitrator.cpp:21-23)
finding max_steering_input_radFINDING — Maximum admissible steering angle input (typically positive for right) (steering_arbitrator.cpp:21-23)
finding min_safe_stop_decel_mps2FINDING — Minimum admissible safe-stop deceleration (typically zero or slightly negative) (brake_arbitrator.cpp:21)
finding max_safe_stop_decel_mps2FINDING — Maximum admissible safe-stop deceleration magnitude (brake_arbitrator.cpp:21)
REQ-ACT-002Output DomainhighValid ranges for actuator command outputs
The module shall produce drive_torque_nm in the range [0.0, FINDING], brake_force_n in the range [0.0, FINDING], and steering_angle_rad in the range [-cal_.max_steering_rad_, cal_.max_steering_rad_].
interpretive · review min_drive_torque_nm0.0 — Minimum motor torque command (typically negative for regenerative braking) (drive_arbitrator.cpp:27-28, 'std::max(0.0f, bus.limited_motion_.limited_accel_mps2_)' — only non-negative acceleration produces drive torque; negative values are clamped to zero)
finding max_drive_torque_nmFINDING — Maximum motor torque command (drive_arbitrator.cpp:31, actuator_interface.cpp:28)
interpretive · review min_brake_force_n0.0 — Minimum brake force (typically zero) (brake_arbitrator.cpp:17, 'std::max(0.0f, -bus.limited_motion_.limited_accel_mps2_)' — deceleration is clamped to non-negative, so brake_force_n ≥ 0)
finding max_brake_force_nFINDING — Maximum brake force (brake_arbitrator.cpp:29-31, actuator_interface.cpp:29)
interpretive · review min_steering_output_rad-cal_.max_steering_rad_ — Minimum output steering angle (negative limit) (steering_arbitrator.cpp:21-23, 'std::clamp(bus.limited_motion_.limited_steering_rad_, -cal_.max_steering_rad_, cal_.max_steering_rad_)' — output is clamped to [-max_steering_rad_, +max_steering_rad_])
interpretive · review max_steering_output_radcal_.max_steering_rad_ — Maximum output steering angle (positive limit) (steering_arbitrator.cpp:21-23, 'std::clamp(..., -cal_.max_steering_rad_, cal_.max_steering_rad_)' — output upper limit is +max_steering_rad_)
REQ-ACT-003Saturation / ClampinghighActuator output saturation and safety limits
The module shall clamp steering_angle_request_rad to the range [-cal_.max_steering_rad_, cal_.max_steering_rad_] using the calibration parameter max_steering_rad_, shall limit brake_force_request_n to not exceed FINDING, and shall apply a holding brake force of 700.0 when actuators_enabled is false.
interpretive · review neg_max_steering_rad-cal_.max_steering_rad_ — Negative mechanical steering limit (typically -max_steering_rad_) (steering_arbitrator.cpp:21-23, 'std::clamp(..., -cal_.max_steering_rad_, cal_.max_steering_rad_)' — negative limit is -max_steering_rad_)
interpretive · review pos_max_steering_radcal_.max_steering_rad_ — Positive mechanical steering limit (max_steering_rad_) (steering_arbitrator.cpp:21-23, 'std::clamp(..., -cal_.max_steering_rad_, cal_.max_steering_rad_)' — positive limit is +max_steering_rad_)
finding max_brake_force_limit_nFINDING — Upper saturation limit for brake force command (brake_arbitrator.cpp:29-31, actuator_interface.cpp:29)
interpretive · review holding_brake_force_n700.0 — Brake force applied when actuators disabled (per AOU-005, 700 N) (actuator_interface.cpp:11-14, 'holding_brake_force_n = vehicle_mass_kg * holding_decel_mps2 = 350.0f * 2.0f = 700.0f')
REQ-ACT-004Sign / UnitshighPhysical unit conversions and sign conventions
The module shall convert acceleration [m/s²] to motor torque [Nm] using the formula torque = acceleration × 350.0 × 0.15 × 10.0, shall convert deceleration [m/s²] to brake force [N] using force = deceleration × 350.0, shall interpret positive steering_rad as USER-QUESTION, and shall interpret negative limited_accel_mps2 as deceleration.
deterministic vehicle_mass_kg350.0 — Vehicle mass used in drivetrain torque calculation (per AOU-003, 350 kg) (src/actuation/brake_arbitrator.cpp: `static constexpr float vehicle_mass_kg = 350.0f;`)
deterministic wheel_radius_m0.15 — Wheel radius used in torque calculation (per AOU-003, 0.15 m) (src/actuation/drive_arbitrator.cpp: `static constexpr float wheel_radius_m = 0.15f;`)
deterministic gear_ratio10.0 — Gear ratio used in torque calculation (per AOU-003, 10:1) (src/actuation/drive_arbitrator.cpp: `static constexpr float gear_ratio = 10.0f;`)
interpretive · review vehicle_mass_for_brake_kg350.0 — Vehicle mass used in brake force calculation (likely same as vehicle_mass_kg) (brake_arbitrator.cpp:9, 'static constexpr float vehicle_mass_kg = 350.0f' — used in line 29: brake_force_n = decel_mps2 * vehicle_mass_kg)
user-question positive_steering_directionUSER-QUESTION — Interpretation of positive steering angle (right or left turn) (The code passes steering angle through without interpretation or sign convention enforcement. The physical meaning (left/right) depends on vehicle hardware configuration and is not specified in the actuation module code. This is a system-level convention that must be provided by integration documentation.)
interpretive · review negative_accel_meaningdeceleration — Interpretation of negative acceleration (deceleration or reverse thrust) (drive_arbitrator.cpp:25-26 comment 'deceleration is handled by the brake_arbitrator', brake_arbitrator.cpp:16-17 'Deceleration demanded by the motion controller (negative accel)' and 'std::max(0.0f, -bus.limited_motion_.limited_accel_mps2_)')
REQ-ACT-005Fault / Error ReactionhighSafety intervention and fault mode behavior
The module shall disable actuators (set actuators_enabled to false) when vehicle_mode is vehicle_mode_t::fault or vehicle_mode_t::init or when power_ok is false, shall override all motion commands and apply emergency deceleration of USER-QUESTION when estop active is true, shall apply safe-stop target deceleration safe_stop_.target_decel_mps2_ when safe_stop active is true and estop is not active, and shall set drive_torque to 0.0 when actuators are disabled.
interpretive · review fault_mode_valuevehicle_mode_t::fault — vehicle_mode_t enumeration value indicating fault state (actuator_interface.cpp:23, 'bus.vehicle_mode_ != vehicle_mode_t::fault' — actuators disabled when mode equals fault)
interpretive · review init_mode_valuevehicle_mode_t::init — vehicle_mode_t enumeration value indicating initialization state (actuator_interface.cpp:24, 'bus.vehicle_mode_ != vehicle_mode_t::init' — actuators disabled when mode equals init)
user-question emergency_decel_value_mps2USER-QUESTION — Calibration parameter for E-stop deceleration magnitude (The emergency deceleration is a calibration parameter (cal_.emergency_decel_mps2_) whose value must be supplied by the customer/integrator via ugv_calibration_t. The code does not hardcode this safety-critical tuning value.)
interpretive · review safe_stop_decel_sourcesafe_stop_.target_decel_mps2_ — Source of deceleration value during safe-stop (input field) (brake_arbitrator.cpp:20-21, 'if (bus.safe_stop_.active_) { decel_mps2 = std::max(decel_mps2, bus.safe_stop_.target_decel_mps2_); }')
interpretive · review drive_torque_on_disable0.0 — Drive torque value when actuators disabled (typically zero) (actuator_interface.cpp:35, 'bus.actuator_command_.drive_torque_nm_ = 0.0f' in the else branch (disabled case))
REQ-ACT-006TimingmediumCycle time and alive counter update
The module shall execute with a cycle time of 20 ms and shall increment alive_counters[module_id::actuation] by 1 modulo USER-QUESTION on each successful execution cycle.
interpretive · review cycle_time_ms20 — Module execution period (20 ms per description) (drive_arbitrator.hpp:10, brake_arbitrator.hpp:10-11, steering_arbitrator.hpp:10, actuator_interface.hpp:10-11 — all comments state 'Task module (20 ms period)')
interpretive · review alive_counter_increment1 — Increment value for alive counter each cycle (typically 1) (actuator_interface.cpp:21, '++bus.alive_counters_[module_id::actuation]' — prefix increment by 1)
user-question alive_counter_moduloUSER-QUESTION — Wrap-around value for alive counter (e.g., 256 for uint8_t) (The wrap-around modulo depends on the data type of alive_counters_[]. The code uses native unsigned integer overflow behavior, but the array element type is not visible in these files. The modulo value (256 for uint8_t, 65536 for uint16_t, etc.) must be determined from the signal_bus_t definition.)
REQ-ACT-007Resource LimitslowArray index bounds and module enumeration
The module shall only write to alive_counters at index USER-QUESTION where this value is less than USER-QUESTION.
user-question actuation_module_id_valueUSER-QUESTION — Enumeration value for actuation module ID used as array index (The numeric value of the module_id::actuation enumeration constant is not defined in the provided source files. This must come from the module_id enum definition (likely in app/signal_bus.hpp or app/ugv_config.hpp not provided).)
user-question alive_counters_array_sizeUSER-QUESTION — Size of the alive_counters array to prevent out-of-bounds access (The size of the alive_counters_ array is not defined in the provided source files. This must come from the signal_bus_t structure definition (not provided in these files).)
REQ-ACT-008StatehighActuator enable state management and mode transitions
The module shall set actuators_enabled to true when vehicle_mode is in the set {init, idle, remote, autonomous, safe_stop, emergency, fault} and power_ok is true, shall set actuators_enabled to false in all other states, shall hold the last valid steering_angle_request_rad value when transitioning to a stop state (safe-stop or E-stop active), and shall zero drive_torque_request_nm when (estop_.active_ || safe_stop_.active_ || !actuators_enabled_).
deterministic normal_operation_modes{init, idle, remote, autonomous, safe_stop, emergency, fault} — Set of vehicle_mode_t values that permit actuator enable (e.g., {DRIVE, REVERSE}) (src/app/ugv_types.hpp: enum vehicle_mode_t)
interpretive · review drive_torque_zero_conditions(estop_.active_ || safe_stop_.active_ || !actuators_enabled_) — Logical conditions under which drive torque is set to zero (e.g., during braking or stops) (drive_arbitrator.cpp:20-22 'if (bus.estop_.active_ || bus.safe_stop_.active_) { bus.drive_torque_request_nm_ = 0.0f; }' and actuator_interface.cpp:35 'bus.actuator_command_.drive_torque_nm_ = 0.0f' when not enabled)
REQ-ACT-009ConcurrencymediumExecution order dependencies and signal coherence
The module shall read all input signals (limited_motion, estop, safe_stop, vehicle_mode, power_ok) as a coherent snapshot at the USER-QUESTION of the cycle, shall write all three actuator_command output fields (drive_torque_nm, brake_force_n, steering_angle_rad) and actuators_enabled atomically before USER-QUESTION, and shall complete execution before the actuator_interface run() method is invoked per the fixed sequence in AOU-002.
user-question input_read_phaseUSER-QUESTION — Point in cycle when inputs are sampled (e.g., start, post-arbitration) (The actuation modules read inputs via signal_bus_t& reference, but the code does not enforce or document the timing of when this snapshot is taken relative to the cycle. This is a system-level integration concern determined by the task scheduler/executor, not implemented in the actuation module itself.)
user-question output_write_deadlineUSER-QUESTION — Point in cycle by which all outputs must be written (e.g., before actuator_interface call) (The code writes all outputs before returning from actuator_interface::run(), but the deadline relative to other system tasks (e.g., before actuator_interface is invoked) is a system-level scheduling constraint not enforced in this module code.)
interpretive · review execution_sequence_referenceAOU-002 — Reference to the fixed call sequence (arbitrators → actuation → actuator_interface) (REQ-ACT-009 requirement text itself references 'per the fixed sequence in AOU-002')

localization

REQ-LOC-001Input DomainhighValid ranges for sensor inputs
The module shall accept tick_ms_ values in the range [FINDING, FINDING], wheel encoder tick values in the range [FINDING, FINDING], IMU yaw rate values in the range [FINDING, FINDING], and odometry speed values in the range [FINDING, FINDING].
finding min_tick_msFINDING — minimum valid system timestamp in milliseconds (wheel_odometry.cpp:38, imu_interface.cpp:29, pose_estimator.cpp:15 — tick_ms_ is read from bus without any validation)
finding max_tick_msFINDING — maximum valid system timestamp in milliseconds (typically uint64_t max or practical system limit) (wheel_odometry.cpp:38, imu_interface.cpp:29, pose_estimator.cpp:15 — tick_ms_ is read from bus without any validation)
finding min_encoder_ticksFINDING — minimum valid encoder tick count value (wheel_odometry.cpp:8-11 — inject_ticks accepts int64_t left/right without validation)
finding max_encoder_ticksFINDING — maximum valid encoder tick count value (wheel_odometry.cpp:8-11 — inject_ticks accepts int64_t left/right without validation)
finding min_yaw_rateFINDING — minimum valid IMU yaw rate in radians per second (imu_interface.cpp:8-10 — inject_imu accepts imu_data_t without validation of yaw_rate_radps_)
finding max_yaw_rateFINDING — maximum valid IMU yaw rate in radians per second (imu_interface.cpp:8-10 — inject_imu accepts imu_data_t without validation of yaw_rate_radps_)
finding min_speedFINDING — minimum valid odometry speed value (wheel_odometry.cpp:42 — speed_mps_ computed without bounds validation)
finding max_speedFINDING — maximum valid odometry speed value (wheel_odometry.cpp:42 — speed_mps_ computed without bounds validation)
REQ-LOC-002Output DomainhighValid ranges for pose estimate outputs
The module shall produce pose_ outputs with x position in range [FINDING, FINDING], y position in range [FINDING, FINDING], heading in range [-π (approximately -3.14159), +π (approximately +3.14159)], speed in range [0.0, FINDING], and confidence in range [0.0, 1.0].
finding min_x_positionFINDING — minimum valid x coordinate in meters (pose_estimator.cpp:36-47 — x_m_ accumulated without bounds checking)
finding max_x_positionFINDING — maximum valid x coordinate in meters (pose_estimator.cpp:36-47 — x_m_ accumulated without bounds checking)
finding min_y_positionFINDING — minimum valid y coordinate in meters (pose_estimator.cpp:37-48 — y_m_ accumulated without bounds checking)
finding max_y_positionFINDING — maximum valid y coordinate in meters (pose_estimator.cpp:37-48 — y_m_ accumulated without bounds checking)
interpretive · review min_heading-π (approximately -3.14159) — minimum valid heading angle in radians (typically -π) (pose_estimator.cpp:30-33 — while (heading_rad_ < -static_cast<float>(M_PI)) heading_rad_ += two_pi;)
interpretive · review max_heading+π (approximately +3.14159) — maximum valid heading angle in radians (typically +π) (pose_estimator.cpp:30-32 — while (heading_rad_ > static_cast<float>(M_PI)) heading_rad_ -= two_pi;)
interpretive · review min_pose_speed0.0 — minimum valid pose speed value (pose_estimator.cpp:23-24,50 — const float speed_mps = odo_ok ? bus.odometry_.speed_mps_ : 0.0f; out.speed_mps_ = speed_mps;)
finding max_pose_speedFINDING — maximum valid pose speed value (pose_estimator.cpp:23,50 — speed_mps copied from bus.odometry_.speed_mps_ without upper limit check)
interpretive · review min_confidence0.0 — minimum confidence value (typically 0.0) (pose_estimator.cpp:44 — confidence = std::clamp(confidence, 0.0f, 1.0f);)
interpretive · review max_confidence1.0 — maximum confidence value (typically 1.0) (pose_estimator.cpp:44 — confidence = std::clamp(confidence, 0.0f, 1.0f);)
REQ-LOC-003Saturation / ClampinghighClamping of integrated pose position and confidence
The module shall clamp pose_.x to [FINDING, FINDING], pose_.y to [FINDING, FINDING], and pose_.confidence to [0.0, 1.0] after each integration step.
finding clamp_min_xFINDING — lower clamp limit on x position in meters (pose_estimator.cpp:36-47 — x_m_ is integrated and assigned without clamping)
finding clamp_max_xFINDING — upper clamp limit on x position in meters (pose_estimator.cpp:36-47 — x_m_ is integrated and assigned without clamping)
finding clamp_min_yFINDING — lower clamp limit on y position in meters (pose_estimator.cpp:37-48 — y_m_ is integrated and assigned without clamping)
finding clamp_max_yFINDING — upper clamp limit on y position in meters (pose_estimator.cpp:37-48 — y_m_ is integrated and assigned without clamping)
interpretive · review clamp_min_conf0.0 — lower clamp limit on confidence (typically 0.0) (pose_estimator.cpp:44 — confidence = std::clamp(confidence, 0.0f, 1.0f);)
interpretive · review clamp_max_conf1.0 — upper clamp limit on confidence (typically 1.0) (pose_estimator.cpp:44 — confidence = std::clamp(confidence, 0.0f, 1.0f);)
REQ-LOC-004Sign / UnitshighPhysical units and sign conventions
The module shall use wheel_ticks_per_meter with units of ticks/meter to convert encoder ticks to meters, shall express heading in radians with positive rotation direction counterclockwise, shall express IMU yaw rate in radians/second, shall express positions (x, y) in meters, and shall use milliseconds for all timing calculations.
interpretive · review wheel_ticks_unitticks/meter — unit of wheel_ticks_per_meter parameter (ticks/meter) (wheel_odometry.cpp:27-28,35 — ticks_per_meter = cal_.wheel_ticks_per_meter_; ds_m = avg_ticks / ticks_per_meter;)
interpretive · review heading_unitradians — unit of heading angle (radians or degrees) (pose_estimator.cpp:29-33 — heading_rad_ += yaw_rate * dt_s; comparison with M_PI)
interpretive · review positive_rotation_directioncounterclockwise — sign convention for positive rotation (counterclockwise or clockwise) (pose_estimator.cpp:29 — heading_rad_ += yaw_rate * dt_s;)
interpretive · review yaw_rate_unitradians/second — unit of IMU yaw rate (rad/s or deg/s) (pose_estimator.cpp:24,29 — const float yaw_rate = ... bus.imu_.yaw_rate_radps_; heading_rad_ += yaw_rate * dt_s;)
interpretive · review position_unitmeters — unit of position coordinates (meters) (pose_estimator.cpp:20-21,47-48 — float x_m_ = 0.0f; float y_m_ = 0.0f; out.x_m_ = x_m_; out.y_m_ = y_m_;)
interpretive · review time_unitmilliseconds — unit of time for delta calculations (milliseconds) (pose_estimator.cpp:15 — static_cast<float>(bus.tick_ms_ - last_tick_ms_) / 1000.0f;)
REQ-LOC-005Fault / Error ReactionhighBehavior on invalid or stale sensor data
The module shall mark imu_.valid as false when IMU age exceeds imu_stale_timeout_ms, shall mark odometry_.valid as false when wheel encoder data fails validity checks with condition !injected_ (no fresh sensor data injected), shall reduce pose_.confidence by 0.5 for each invalid sensor, shall mark pose_.valid as false when pose_.confidence falls below pose_min_confidence, and shall apply fallback integration strategy zero substitution when sensor data is invalid.
interpretive · review imu_invalid_valuefalse — value to assign to imu_.valid when IMU data is stale (typically false) (imu_interface.cpp:24,31 — out.valid_ = false; out.valid_ = latest_.valid_ && fresh;)
interpretive · review odo_invalid_valuefalse — value to assign to odometry_.valid when odometry data fails checks (typically false) (wheel_odometry.cpp:21 — out.valid_ = false;)
interpretive · review odo_validity_condition!injected_ (no fresh sensor data injected) — condition under which odometry is marked invalid (e.g., tick delta out of range, non-monotonic) (wheel_odometry.cpp:19-24 — if (!injected_) { out.valid_ = false; ... })
interpretive · review confidence_degradation_per_invalid_sensor0.5 — amount by which confidence is reduced per invalid sensor (described as 0.5) (pose_estimator.cpp:41-43 — float confidence = 1.0f; if (!odo_ok) confidence -= 0.5f; if (!imu_ok) confidence -= 0.5f;)
interpretive · review pose_invalid_valuefalse — value to assign to pose_.valid when confidence is below threshold (typically false) (pose_estimator.cpp:52 — out.valid_ = confidence >= cal_.pose_min_confidence_;)
interpretive · review fallback_integration_strategyzero substitution — strategy when sensor invalid: zero-order hold, use last valid, skip update, etc. (pose_estimator.cpp:23-24 — const float speed_mps = odo_ok ? bus.odometry_.speed_mps_ : 0.0f; const float yaw_rate = imu_ok ? bus.imu_.yaw_rate_radps_ : 0.0f;)
REQ-LOC-006TiminghighCycle time, staleness detection, and monotonicity
The module shall compute time delta dt using tick_ms_ with formula (current_tick_ms - last_tick_ms) / 1000.0, shall detect IMU staleness by comparing (tick_ms_ - imu_last_update_time) against imu_stale_timeout_ms with threshold USER-QUESTION, shall assume a nominal cycle time of USER-QUESTION milliseconds for integration, shall handle non-monotonic tick_ms_ with behavior FINDING, and shall debounce sensor validity over FINDING cycles if applicable.
interpretive · review dt_calculation_formula(current_tick_ms - last_tick_ms) / 1000.0 — formula for computing time delta from tick_ms_ (e.g., current - previous) (pose_estimator.cpp:14-16 — const float dt_s = have_last_ ? static_cast<float>(bus.tick_ms_ - last_tick_ms_) / 1000.0f : 0.0f;)
user-question staleness_threshold_msUSER-QUESTION — timeout value in milliseconds for marking IMU data stale (The staleness threshold is read from calibration parameter cal_.imu_stale_timeout_ms_. This is a tuning parameter that must be supplied by the user/configuration; the code does not hardcode a specific value.)
user-question nominal_cycle_time_msUSER-QUESTION — expected cycle time in milliseconds (from AOU-002) (The code computes actual cycle time dynamically from timestamp deltas; no nominal cycle time constant is hardcoded. This is an operational assumption external to the code (depends on scheduler/caller behavior).)
finding non_monotonic_tick_behaviorFINDING — behavior when tick_ms_ is non-monotonic: clamp dt, skip update, error flag (pose_estimator.cpp:14-16, wheel_odometry.cpp:37-38 — tick_ms_ subtraction performed without monotonicity check)
finding validity_debounce_countFINDING — number of consecutive cycles required to confirm sensor validity state change (imu_interface.cpp:31, wheel_odometry.cpp:21,43 — valid_ set immediately without debouncing logic)
REQ-LOC-007Resource LimitsmediumBounded arrays and buffer indices
The module shall access alive_counters_ at index module_id::localization which shall be within bounds [FINDING, FINDING], shall limit the maximum value of alive_counters_[module_id::localization] to UINT64_MAX (18446744073709551615), and shall handle counter overflow with behavior wrap to 0.
interpretive · review alive_counter_indexmodule_id::localization — array index used for localization alive counter (pose_estimator.cpp:12 — ++bus.alive_counters_[module_id::localization];)
finding alive_counter_min_indexFINDING — minimum valid index for alive_counters_ array (pose_estimator.cpp:12 — alive_counters_ accessed without bounds check)
finding alive_counter_max_indexFINDING — maximum valid index for alive_counters_ array (pose_estimator.cpp:12 — alive_counters_ accessed without bounds check)
interpretive · review max_alive_counter_valueUINT64_MAX (18446744073709551615) — maximum value for uint64_t alive counter (typically UINT64_MAX) (pose_estimator.cpp:12 — ++bus.alive_counters_[module_id::localization]; (uint64_t type, no explicit limit))
interpretive · review counter_overflow_behaviorwrap to 0 — behavior on counter overflow: wrap to 0, saturate, etc. (pose_estimator.cpp:12 — ++bus.alive_counters_[module_id::localization]; (uint64_t increment))
REQ-LOC-008StatehighState-dependent integration and initialization
The module shall initialize pose_ to (0.0, 0.0, 0.0) with confidence REVIEW at startup, shall transition to state FINDING after receiving first valid sensor data, shall maintain accumulated heading state with wrapping behavior iterative while-loop normalization at boundaries [-π (approximately -3.14159), +π (approximately +3.14159)], and shall reset accumulated state to FINDING under condition FINDING.
interpretive · review initial_x0.0 — initial x position at startup (assumed 0 per AOU-006) (pose_estimator.cpp:20 — float x_m_ = 0.0f;)
interpretive · review initial_y0.0 — initial y position at startup (assumed 0 per AOU-006) (pose_estimator.cpp:21 — float y_m_ = 0.0f;)
interpretive · review initial_heading0.0 — initial heading at startup (assumed 0 per AOU-006) (pose_estimator.cpp:22 — float heading_rad_ = 0.0f;)
review initial_confidenceREVIEW — initial confidence value at startup (Confidence is computed from scratch each cycle (starting at 1.0, then degraded). There is no explicit initialization of a persistent confidence state. The first output will have confidence based on first-cycle sensor validity (potentially 0.0, 0.5, or 1.0). Ambiguous what 'initial confidence' means: first output value or a persistent initial state.)
finding state_after_first_valid_sensorFINDING — module state after first valid sensor injection (e.g., INITIALIZED, RUNNING) (pose_estimator.cpp — no explicit state machine or state transition logic present)
interpretive · review heading_wrap_behavioriterative while-loop normalization — method for wrapping heading to [-π, π] (modulo, conditional branch) (pose_estimator.cpp:31-33 — while (heading_rad_ > M_PI) heading_rad_ -= two_pi; while (heading_rad_ < -M_PI) heading_rad_ += two_pi;)
interpretive · review heading_wrap_min-π (approximately -3.14159) — minimum heading value after wrapping (typically -π) (pose_estimator.cpp:33 — while (heading_rad_ < -static_cast<float>(M_PI)) heading_rad_ += two_pi;)
interpretive · review heading_wrap_max+π (approximately +3.14159) — maximum heading value after wrapping (typically +π) (pose_estimator.cpp:32 — while (heading_rad_ > static_cast<float>(M_PI)) heading_rad_ -= two_pi;)
finding state_reset_valuesFINDING — values to which pose state is reset (x, y, heading, confidence) (pose_estimator.cpp — no reset logic present; x_m_, y_m_, heading_rad_ accumulate indefinitely)
finding state_reset_conditionFINDING — condition under which pose state is reset (e.g., external command, prolonged invalidity) (pose_estimator.cpp — no reset logic present; x_m_, y_m_, heading_rad_ accumulate indefinitely)
REQ-LOC-009ConcurrencymediumExecution order and shared data access
The module shall enforce that inject_ticks() and inject_imu() are called before run() in each cycle with enforcement method flag check with safe default, shall access tick_ms_ at execution point at run() entry (from bus) to ensure consistent time reference, shall update pose_ atomically with respect to single write (struct assignment) to prevent partial reads by consumers, and shall handle concurrent access to alive_counters_ with synchronization mechanism REVIEW.
interpretive · review injection_order_enforcementflag check with safe default — method to enforce injection before run: assertion, flag check, error return (wheel_odometry.cpp:19-24 — if (!injected_) { out.valid_ = false; ... return; })
interpretive · review tick_read_pointat run() entry (from bus) — when tick_ms_ is read: at run() entry, before integration, cached from injection (pose_estimator.cpp:15,17 — dt_s = ... (bus.tick_ms_ - last_tick_ms_) ...; last_tick_ms_ = bus.tick_ms_;)
interpretive · review pose_update_atomicitysingle write (struct assignment) — atomicity guarantee for pose_ updates: single write, mutex, lock-free (pose_estimator.cpp:46-54 — pose_estimate_t out{}; ... bus.pose_ = out;)
review alive_counter_syncREVIEW — synchronization for alive_counters_ access: atomic increment, mutex, none (if single-threaded) (The counter is incremented with plain ++ operator, no std::atomic or mutex protection. If the code is single-threaded this is safe; if multi-threaded this is a data race. The code does not indicate thread safety provisions—requires review of system architecture.)

motion_control

REQ-MOT-001Input DomainhighInput signal validity ranges
The module shall accept motion_setpoint_.target_speed_mps_ in the range [-max_reverse_speed_mps_, max_speed_mps_], motion_setpoint_.target_yaw_rate_radps_ in the range [-max_command_yaw_rate_radps_, max_command_yaw_rate_radps_], pose_.speed_mps_ in the range [FINDING, FINDING], and imu_.yaw_rate_radps_ in the range [FINDING, FINDING].
interpretive · review min_target_speed_mps-max_reverse_speed_mps_ — Minimum admissible target speed (negative, derived from reverse limit) (trajectory_limiter.cpp:36 — std::clamp(bus.motion_setpoint_.target_speed_mps_, -cal_.max_reverse_speed_mps_, max_speed). The code enforces the lower bound as the negative of max_reverse_speed_mps_.)
interpretive · review max_target_speed_mpsmax_speed_mps_ — Maximum admissible target speed (forward) (trajectory_limiter.cpp:19,36 — max_speed defaults to cal_.max_speed_mps_ and is used as the upper bound in the clamp. The setpoint is clamped to [−max_reverse_speed_mps_, max_speed].)
interpretive · review min_target_yaw_rate_radps-max_command_yaw_rate_radps_ — Minimum admissible target yaw rate (negative of symmetric limit) (trajectory_limiter.cpp:54-55 — std::clamp(bus.motion_setpoint_.target_yaw_rate_radps_, -cal_.max_command_yaw_rate_radps_, cal_.max_command_yaw_rate_radps_). Lower bound is the negative of the command limit.)
interpretive · review max_target_yaw_rate_radpsmax_command_yaw_rate_radps_ — Maximum admissible target yaw rate (positive) (trajectory_limiter.cpp:54-55 — the upper bound of the clamp is cal_.max_command_yaw_rate_radps_.)
finding min_pose_speed_mpsFINDING — Minimum admissible pose speed feedback value (speed_controller.cpp:19 — const float measured = bus.pose_.speed_mps_; no clamping or validation occurs. The PID simply reads it.)
finding max_pose_speed_mpsFINDING — Maximum admissible pose speed feedback value (speed_controller.cpp:19 — measured is read directly without validation.)
finding min_imu_yaw_rate_radpsFINDING — Minimum admissible IMU yaw rate feedback value (steering_controller.cpp:19 — const float measured = bus.imu_.yaw_rate_radps_; no clamping or validation.)
finding max_imu_yaw_rate_radpsFINDING — Maximum admissible IMU yaw rate feedback value (steering_controller.cpp:19 — measured is used directly without bounds checking.)
REQ-MOT-002Output DomainhighActuator command output ranges
The module shall produce speed_control_output_mps2_ in the range [-max_decel_mps2_, max_accel_mps2_], steering_control_output_rad_ in the range [-max_steering_rad_, max_steering_rad_], limited_motion_.limited_accel_mps2_ in the range [-max_decel_mps2_, max_accel_mps2_], and limited_motion_.limited_steering_rad_ in the range [-max_steering_rad_, max_steering_rad_].
interpretive · review min_speed_output_mps2-max_decel_mps2_ — Minimum speed controller output (maximum deceleration, negative) (speed_controller.cpp:14,24 — pid_(..., -cal.max_decel_mps2_, ...) and std::clamp(output, -cal_.max_decel_mps2_, cal_.max_accel_mps2_). The minimum is the negative of max_decel_mps2_.)
interpretive · review max_speed_output_mps2max_accel_mps2_ — Maximum speed controller output (maximum acceleration, positive) (speed_controller.cpp:14,24 — pid_(..., cal.max_accel_mps2_) and clamp upper bound is cal_.max_accel_mps2_.)
interpretive · review min_steering_output_rad-max_steering_rad_ — Minimum steering controller output (negative of symmetric limit) (steering_controller.cpp:14,24 — pid_(..., -cal.max_steering_rad_, ...) and std::clamp(output, -cal_.max_steering_rad_, cal_.max_steering_rad_). The minimum is the negative.)
interpretive · review max_steering_output_radmax_steering_rad_ — Maximum steering controller output (positive) (steering_controller.cpp:14,24 — clamp upper bound is cal_.max_steering_rad_.)
interpretive · review min_limited_accel_mps2-max_decel_mps2_ — Minimum limited acceleration after jerk and envelope constraints (trajectory_limiter.cpp:40-41 — std::clamp(bus.speed_control_output_mps2_, -cal_.max_decel_mps2_, cal_.max_accel_mps2_). The jerk limiter (lines 44-48) further constrains but does not reduce this minimum.)
interpretive · review max_limited_accel_mps2max_accel_mps2_ — Maximum limited acceleration after jerk and envelope constraints (trajectory_limiter.cpp:40-41 — clamp upper bound is cal_.max_accel_mps2_. Jerk limiting can reduce the actual value per cycle but not exceed this.)
interpretive · review min_limited_steering_rad-max_steering_rad_ — Minimum limited steering angle after envelope constraints (trajectory_limiter.cpp:59-61 — std::clamp(bus.steering_control_output_rad_, -cal_.max_steering_rad_, cal_.max_steering_rad_).)
interpretive · review max_limited_steering_radmax_steering_rad_ — Maximum limited steering angle after envelope constraints (trajectory_limiter.cpp:59-61 — clamp upper bound is cal_.max_steering_rad_.)
REQ-MOT-003Saturation / ClampinghighMulti-stage output saturation and anti-windup
The module shall clamp speed_control_output_mps2_ to [-max_decel_mps2_, max_accel_mps2_] and shall prevent integral accumulation in the speed PID when clamped; shall clamp steering_control_output_rad_ to [-max_steering_rad_, max_steering_rad_] and shall prevent integral accumulation in the steering PID when clamped; and shall apply jerk rate limiting to limited_motion_.limited_accel_mps2_ such that its rate of change does not exceed max_jerk_mps3_ per cycle.
interpretive · review speed_clamp_min_mps2-max_decel_mps2_ — Lower saturation limit for speed PID output (deceleration) (pid_controller.cpp:7-9,29 — pid_controller constructor receives out_min, out_max; speed_controller.cpp:14 passes -cal.max_decel_mps2_. Line 29 in pid_controller.cpp applies std::clamp(raw_output, out_min_, out_max_).)
interpretive · review speed_clamp_max_mps2max_accel_mps2_ — Upper saturation limit for speed PID output (acceleration) (pid_controller.cpp:29, speed_controller.cpp:14 — upper clamp is cal.max_accel_mps2_.)
interpretive · review steer_clamp_min_rad-max_steering_rad_ — Lower saturation limit for steering PID output (pid_controller.cpp:29, steering_controller.cpp:14 — lower clamp is -cal.max_steering_rad_.)
interpretive · review steer_clamp_max_radmax_steering_rad_ — Upper saturation limit for steering PID output (pid_controller.cpp:29, steering_controller.cpp:14 — upper clamp is cal.max_steering_rad_.)
interpretive · review jerk_limit_mps3max_jerk_mps3_ — Maximum permitted rate of change of acceleration command (trajectory_limiter.cpp:45 — const float max_delta = cal_.max_jerk_mps3_ * kLoopPeriodS; then clamp to ±max_delta around prev_accel_mps2_. The rate of change per cycle is limited to max_jerk_mps3_ * 0.02.)
REQ-MOT-004Sign / UnitsmediumSign conventions and unit consistency
The module shall interpret positive values of motion_setpoint_.target_speed_mps_ and pose_.speed_mps_ as forward motion in meters per second, negative values as reverse motion; shall interpret positive motion_setpoint_.target_yaw_rate_radps_ and imu_.yaw_rate_radps_ as USER-QUESTION rotation in radians per second; shall interpret positive steering_control_output_rad_ and limited_motion_.limited_steering_rad_ as steering toward USER-QUESTION in radians; and shall express speed_control_output_mps2_ and limited_motion_.limited_accel_mps2_ in meters per second squared with positive values representing forward acceleration.
interpretive · review speed_unitmeters per second — Physical unit for velocity signals (expected: meters per second) (Variable names consistently use _mps_ suffix (e.g., target_speed_mps_, speed_mps_) across all files. This is the conventional abbreviation for meters per second.)
user-question positive_yaw_directionUSER-QUESTION — Rotation direction for positive yaw rate (expected: counterclockwise or left) (The code treats yaw rate as a signed scalar but does not specify whether positive is left (CCW) or right (CW). This is a vehicle coordinate frame convention that must be supplied by the integration team or system spec.)
interpretive · review yaw_rate_unitradians per second — Physical unit for angular velocity (expected: radians per second) (Variable names consistently use _radps_ suffix (e.g., target_yaw_rate_radps_, yaw_rate_radps_). This is the conventional abbreviation for radians per second.)
user-question positive_steer_directionUSER-QUESTION — Steering direction for positive angle (expected: left or right) (The code treats steering as a signed scalar but does not specify whether positive is left or right. This is a vehicle-specific mechanical convention.)
interpretive · review steering_unitradians — Physical unit for steering angle (expected: radians) (Variable names consistently use _rad_ suffix (e.g., max_steering_rad_, steering_control_output_rad_). This is the conventional abbreviation for radians.)
interpretive · review accel_unitmeters per second squared — Physical unit for acceleration (expected: meters per second squared) (Variable names consistently use _mps2_ suffix (e.g., max_accel_mps2_, speed_control_output_mps2_). This is the conventional abbreviation for m/s².)
REQ-MOT-005Fault / Error ReactionhighDegradation-aware envelope and invalid input handling
The module shall reduce limited_motion_.limited_speed_mps_ to reduced_max_speed_mps_ when degradation_level_ is {full, reduced_speed, geofence_hold, safe_stop_only, disabled}, shall set limited_motion_.limited_speed_mps_ to 0.0 when degradation_level_ is {full, reduced_speed, geofence_hold, safe_stop_only, disabled}, shall apply degradation-dependent acceleration and yaw rate envelopes per FINDING, and shall output FINDING and FINDING when input signals are FINDING.
interpretive · review reduced_speed_limit_mpsreduced_max_speed_mps_ — Maximum speed enforced in reduced-speed degradation modes (trajectory_limiter.cpp:21-23 — case degradation_level_t::reduced_speed: max_speed = std::min(max_speed, cal_.reduced_max_speed_mps_). The effective speed is limited to reduced_max_speed_mps_.)
deterministic reduced_speed_degradation_set{full, reduced_speed, geofence_hold, safe_stop_only, disabled} — Set of degradation_level_t enum values triggering reduced speed (e.g., reduced_speed, geofence_hold) (src/app/ugv_types.hpp: enum degradation_level_t)
interpretive · review stopped_speed_mps0.0 — Speed limit (expected zero) when vehicle must halt (trajectory_limiter.cpp:25-27 — case degradation_level_t::safe_stop_only: case degradation_level_t::disabled: max_speed = 0.0f. The vehicle is commanded to stop.)
deterministic stopped_degradation_set{full, reduced_speed, geofence_hold, safe_stop_only, disabled} — Set of degradation_level_t enum values forcing zero speed (e.g., emergency_stop, critical) (src/app/ugv_types.hpp: enum degradation_level_t)
finding degradation_envelope_formulaFINDING — Mapping from degradation level to max_accel, max_decel, max_yaw_rate envelopes (trajectory_limiter.cpp:18-32 — degradation level affects only max_speed (lines 19-32). Acceleration (max_accel_mps2_, max_decel_mps2_) and yaw rate (max_command_yaw_rate_radps_) envelopes are NOT modified by degradation_level_.)
finding failsafe_accel_mps2FINDING — Safe acceleration command on invalid input (expected: zero or gentle decel) (speed_controller.cpp, steering_controller.cpp, trajectory_limiter.cpp — no input validation or failsafe logic for invalid/NaN/stale inputs.)
finding failsafe_steering_radFINDING — Safe steering command on invalid input (expected: zero or hold) (steering_controller.cpp, trajectory_limiter.cpp — no input validation or failsafe steering command logic.)
finding invalid_input_conditionFINDING — Condition defining invalid inputs (e.g., NaN, out-of-range, stale timestamp) (All motion source files lack input validation checks.)
REQ-MOT-006TiminghighCycle period and derivative kick prevention
The module shall execute at a period of 20 milliseconds, shall compute PID derivative terms using a time step of 0.02 seconds, shall compute PID integral terms accumulating over 0.02 seconds per cycle, shall set the derivative term to 0.0 on the first cycle after initialization when has_prev_ is false, shall increment alive_counters_[module_id::motion] by 1 per cycle, and shall apply jerk rate limiting using a maximum acceleration change of max_jerk_mps3_ * 0.02 per cycle.
interpretive · review cycle_period_ms20 — Module execution period in milliseconds (expected: 20) (speed_controller.cpp:9, steering_controller.cpp:9, trajectory_limiter.cpp:9 — constexpr float kLoopPeriodS = 0.02f; (0.02 seconds = 20 milliseconds). This constant is used throughout for time-step calculations.)
interpretive · review derivative_time_step_s0.02 — Time delta for PID derivative calculation in seconds (expected: 0.02) (pid_controller.cpp:17 — derivative = (error - prev_error_) / dt_s; where dt_s is passed as kLoopPeriodS (0.02f) from speed_controller.cpp:21 and steering_controller.cpp:21.)
interpretive · review integral_time_step_s0.02 — Time delta for PID integral accumulation in seconds (expected: 0.02) (pid_controller.cpp:23 — const float candidate_integral = integral_ + error * dt_s; where dt_s is kLoopPeriodS (0.02f).)
interpretive · review derivative_kick_avoid_value0.0 — Derivative term value on first cycle to avoid kick (expected: 0.0) (pid_controller.cpp:15-16 — float derivative = 0.0f; if (has_prev_ && dt_s > 0.0f) { derivative = ...; }. On first cycle (has_prev_ == false), derivative remains 0.0f.)
interpretive · review has_prev_flaghas_prev_ — Flag indicating whether previous cycle data exists for derivative computation (pid_controller.hpp:28 — bool has_prev_ = false; pid_controller.cpp:16,20 — checked and set to true after first compute call.)
interpretive · review alive_counter_increment1 — Increment step for alive counter per cycle (expected: 1) (trajectory_limiter.cpp:16 — ++bus.alive_counters_[module_id::motion]; pre-increment operator increments by 1.)
interpretive · review jerk_rate_limit_per_cycle_mps2max_jerk_mps3_ * 0.02 — Maximum acceleration change per 20 ms cycle (max_jerk * 0.02) (trajectory_limiter.cpp:45 — const float max_delta = cal_.max_jerk_mps3_ * kLoopPeriodS; (kLoopPeriodS = 0.02f). The maximum acceleration change per cycle is max_jerk_mps3_ * 0.02.)
REQ-MOT-007Resource LimitsmediumPID state and counter bounds
The module shall bound the speed PID integral term to [FINDING, FINDING], shall bound the steering PID integral term to [FINDING, FINDING], and shall wrap alive_counters_[module_id::motion] modulo REVIEW to remain within REVIEW range.
finding speed_integral_minFINDING — Lower bound on speed PID integral accumulator to prevent underflow (pid_controller.cpp:23,38 — integral accumulation occurs but no explicit lower bound on integral_ itself is enforced. The anti-windup logic (lines 33-39) prevents accumulation when saturated but does not bound the integral term directly.)
finding speed_integral_maxFINDING — Upper bound on speed PID integral accumulator to prevent overflow (pid_controller.cpp:23,38 — no explicit upper bound on integral_.)
finding steer_integral_minFINDING — Lower bound on steering PID integral accumulator to prevent underflow (pid_controller.cpp — same PID implementation used for steering (steering_controller.cpp:14,21). No explicit integral bounds.)
finding steer_integral_maxFINDING — Upper bound on steering PID integral accumulator to prevent overflow (pid_controller.cpp — same PID implementation. No explicit integral bounds.)
review alive_counter_moduloREVIEW — Modulo for alive counter wraparound (expected: 256 for uint8_t) (The alive counter is incremented but the type of alive_counters_[module_id::motion] is not defined in the provided motion source files. The modulo depends on the underlying type (likely uint8_t -> 256, but must be confirmed from signal_bus definition).)
review alive_counter_typeREVIEW — Data type of alive counter (expected: uint8_t) (The data type of alive_counters_[module_id::motion] is not visible in the motion module source files. Must be determined from the signal_bus definition.)
REQ-MOT-008StatemediumPID state persistence across degradation transitions
The module shall initialize the speed PID integral and derivative states to 0.0 and 0.0 respectively on first execution, shall preserve speed PID integral and derivative states across transitions in degradation_level_ per preserve, shall initialize the steering PID integral and derivative states to 0.0 and 0.0 on first execution, and shall set false to indicate uninitialized state until the first cycle completes.
interpretive · review pid_init_integral_value0.0 — Initial value of speed PID integral accumulator (expected: 0.0) (pid_controller.hpp:26 — float integral_ = 0.0f; member initializer. The integral starts at zero.)
interpretive · review pid_init_derivative_value0.0 — Initial value of speed PID derivative term (expected: 0.0) (pid_controller.cpp:15 — float derivative = 0.0f; on first call (has_prev_ == false), derivative remains 0.0f and is used in the output calculation (line 27).)
interpretive · review degradation_transition_policypreserve — Policy for PID state handling during degradation transitions (e.g., preserve, reset, conditional) (pid_controller.cpp — the PID state (integral_, prev_error_, has_prev_) is never reset except via explicit reset() call. No reset() call appears in speed_controller.cpp, steering_controller.cpp, or trajectory_limiter.cpp. State persists across degradation transitions.)
interpretive · review steer_pid_init_integral0.0 — Initial value of steering PID integral accumulator (expected: 0.0) (pid_controller.hpp:26 — same PID class used for steering (steering_controller.cpp:14). integral_ = 0.0f.)
interpretive · review steer_pid_init_derivative0.0 — Initial value of steering PID derivative term (expected: 0.0) (pid_controller.cpp:15 — same PID class. derivative = 0.0f on first cycle.)
deterministic first_cycle_flagfalse — Flag indicating module is executing its first cycle (has_prev_ = false) (boolean flag polarity from interface contract)
REQ-MOT-009ConcurrencyhighSub-module execution order and shared state access
The module shall execute speed_controller before trajectory_limiter within each cycle, shall execute steering_controller before trajectory_limiter within each cycle, shall ensure trajectory_limiter reads speed_control_output_mps2_ and steering_control_output_rad_ only after both controllers have written them per USER-QUESTION, shall update all fields of limited_motion_ atomically before external consumers read them per single-writer, no concurrency, and shall ensure alive_counters_[module_id::motion] is incremented exactly once per cycle by trajectory_limiter.
user-question execution_order_dependencyUSER-QUESTION — Mechanism enforcing sub-module execution order (e.g., explicit sequencing, dependency graph) (The motion module source does not contain scheduling logic. The requirement states speed_controller and steering_controller must execute before trajectory_limiter, but the mechanism enforcing this (e.g., explicit sequencing in main.cpp, dependency graph) is not visible in the motion module code. This is a system-level integration detail.)
interpretive · review output_atomicity_mechanismsingle-writer, no concurrency — Mechanism ensuring atomic writes to output structure (e.g., mutex, single-writer, copy-on-write) (trajectory_limiter.cpp:63-66 — all fields of bus.limited_motion_ are written sequentially in a single function without interruption. No mutex or lock is visible. Atomicity is achieved by single-threaded execution (implied by 20 ms task model).)
interpretive · review alive_counter_writertrajectory_limiter — Sub-module responsible for incrementing alive counter (expected: trajectory_limiter) (trajectory_limiter.cpp:16 — ++bus.alive_counters_[module_id::motion]; This is the only increment of the motion alive counter in the provided source.)

perception

REQ-PER-001Input DomainhighValid input ranges for ego speed and timestamp
The module shall accept pose_.speed_mps_ values in the range [FINDING, FINDING] and shall accept tick_ms_ values that are monotonically increasing with maximum permissible backwards delta of FINDING ms.
finding min_ego_speed_mpsFINDING — minimum admissible ego vehicle speed (expected 0.0 per AOU-003) (collision_predictor.cpp:16 reads bus.pose_.speed_mps_ without any validation or range check)
finding max_ego_speed_mpsFINDING — maximum admissible ego vehicle speed for validity check (collision_predictor.cpp:16 reads bus.pose_.speed_mps_ without any validation or range check)
finding max_tick_backwards_msFINDING — maximum allowable backwards jump in system timestamp before triggering fault (lidar_interface.cpp:28 computes age_ms = bus.tick_ms_ - last_update_tick_ms_ without checking monotonicity)
REQ-PER-002Output DomainhighValid output ranges for collision prediction and obstacle data
The module shall produce collision_.time_to_collision_s in the range [0.0, collision_prediction_t default value (large sentinel, likely 999.9 or FLT_MAX from signal_bus.hpp definition)], collision_.nearest_range_m in the range [0.0, collision_prediction_t default nearest_range_m_ (likely 999.9 or similar large sentinel from signal_bus.hpp)], obstacle range values in [0.0 (exclusive, enforced as > 0.0f), FINDING], and obstacle bearing values in [FINDING, FINDING].
interpretive · review min_ttc_s0.0 — minimum time-to-collision value (likely 0.0 or special sentinel) (collision_predictor.cpp:14,21: out{} uses safe defaults via collision_prediction_t default constructor, min_ttc initialized to out.time_to_collision_s_ which defaults to a large sentinel (likely std::numeric_limits or a config constant). TTC is only reduced (line 38: min_ttc = std::min(min_ttc, ttc)) when positive closing speed yields a finite value. The computed ttc (line 37) is always non-negative (range/speed, both non-negative). Minimum observed TTC is implicitly 0.0 when an obstacle is at zero range or infinitely fast closing.)
interpretive · review max_ttc_scollision_prediction_t default value (large sentinel, likely 999.9 or FLT_MAX from signal_bus.hpp definition) — maximum or invalid sentinel for time-to-collision (collision_predictor.cpp:14,21: collision_prediction_t out{}; initializes with safe defaults; out.time_to_collision_s_ is the default-constructed value from collision_prediction_t. Line 45 writes this value to bus.collision_ when no collision threat found. This is the sentinel 'no collision' value, effectively the maximum reportable TTC.)
interpretive · review min_collision_range_m0.0 — minimum physically valid range for collision prediction (collision_predictor.cpp:26-29: nearest_range initialized to out.nearest_range_m_ (default safe value), then updated only if obs.range_m_ < nearest_range. Obstacle ranges come from clusters which require p.range_m_ > 0.0f (obstacle_detector.cpp:39). The code permits ranges arbitrarily close to zero; no explicit minimum is enforced. Practical minimum is 0.0 (exclusive in clustering, but nearest can approach zero).)
interpretive · review max_collision_range_mcollision_prediction_t default nearest_range_m_ (likely 999.9 or similar large sentinel from signal_bus.hpp) — maximum range for collision prediction reporting (collision_predictor.cpp:19: nearest_range = out.nearest_range_m_; where out is default-constructed collision_prediction_t{}. This is the 'no obstacle' sentinel. Line 26 only updates if obs.range_m_ < nearest_range, so the default value is the effective maximum reportable range.)
interpretive · review min_obstacle_range_m0.0 (exclusive, enforced as > 0.0f) — minimum physically valid obstacle range (obstacle_detector.cpp:39: if (!p.valid_ || p.range_m_ <= 0.0f) { close_cluster(); continue; })
finding max_obstacle_range_mFINDING — maximum LiDAR detection range for obstacles (obstacle_detector.cpp:39-42 and entire cluster_scan() function show no upper-bound validation on p.range_m_)
finding min_bearing_radFINDING — minimum bearing angle (likely -π or -π/2) (obstacle_detector.cpp:38-60 processes p.bearing_rad_ without any range validation)
finding max_bearing_radFINDING — maximum bearing angle (likely +π or +π/2) (obstacle_detector.cpp:38-60 processes p.bearing_rad_ without any range validation)
REQ-PER-003Saturation / ClampingmediumClamping of computed collision metrics
The module shall clamp computed time_to_collision_s to a maximum of FINDING seconds and shall clamp obstacle relative_speed to the range [FINDING, FINDING] mps.
finding ttc_clamp_max_sFINDING — upper saturation limit for time-to-collision to avoid infinity (collision_predictor.cpp:37-38: const float ttc = obs.range_m_ / std::max(closing_speed, eps); min_ttc = std::min(min_ttc, ttc); — no upper clamp applied to ttc)
finding min_relative_speed_mpsFINDING — lower clamp on relative speed (negative = approaching) (obstacle_detector.cpp:113: trk.relative_speed_mps_ = c.range_m_ - prev_range; — no clamp applied; collision_predictor.cpp:34 uses obs.relative_speed_mps_ directly)
finding max_relative_speed_mpsFINDING — upper clamp on relative speed (positive = receding) (obstacle_detector.cpp:113: trk.relative_speed_mps_ = c.range_m_ - prev_range; — no clamp applied; collision_predictor.cpp:34 uses obs.relative_speed_mps_ directly)
REQ-PER-004Sign / UnitshighSign conventions and unit consistency
The module shall interpret bearing angles with USER-QUESTION convention, shall interpret relative_speed such that positive = opening (receding), negative = closing (approaching), shall compute time_to_collision_s using the formula ttc = range / closing_speed, where closing_speed = ego_speed - relative_speed, and shall maintain all range values in meters and all speed values in meters-per-second.
user-question bearing_sign_conventionUSER-QUESTION — bearing sign convention (e.g., 'positive_counterclockwise', 'positive_clockwise', 'RHR') (The code does not specify whether positive bearing is left/right or clockwise/counterclockwise. This is a coordinate-frame / sensor-mounting decision that must be provided by the integration specification or calibration documentation.)
interpretive · review relative_speed_sign_conventionpositive = opening (receding), negative = closing (approaching) — relative speed sign interpretation (e.g., 'negative_approaching', 'positive_approaching') (obstacle_detector.cpp:113: trk.relative_speed_mps_ = c.range_m_ - prev_range; (current_range - previous_range). If range increases, relative_speed is positive (object moving away). collision_predictor.cpp:34: closing_speed = ego_speed - obs.relative_speed_mps_; subtracts relative_speed from ego_speed, consistent with relative_speed being the rate-of-opening.)
interpretive · review ttc_computation_formulattc = range / closing_speed, where closing_speed = ego_speed - relative_speed — formula used for TTC computation (e.g., 'range / closing_speed' or 'range / (ego_speed - relative_speed)') (collision_predictor.cpp:34,37: const float closing_speed = ego_speed - obs.relative_speed_mps_; const float ttc = obs.range_m_ / std::max(closing_speed, eps);)
REQ-PER-005Fault / Error ReactionhighFault detection and degraded-mode behavior
The module shall set lidar_status_ to sensor_status_t::stale when tick_ms_ minus last scan timestamp exceeds lidar_stale_timeout_ms_, shall set lidar_status_ to sensor_status_t::failed when {ok, degraded, stale, failed}, shall set collision_.imminent to FINDING when sensor status is not sensor_status_t::ok, and shall FINDING the obstacles_ vector on sensor fault.
interpretive · review stale_status_valuesensor_status_t::stale — enum value assigned to lidar_status_ when data is stale (lidar_interface.cpp:31: bus.lidar_status_ = ... : sensor_status_t::stale; when age_ms > cal_.lidar_stale_timeout_ms_)
interpretive · review failed_status_valuesensor_status_t::failed — enum value assigned to lidar_status_ when sensor has failed (lidar_interface.cpp:22: bus.lidar_status_ = sensor_status_t::failed; when !have_scan_ (no scan ever received))
deterministic lidar_failure_conditions{ok, degraded, stale, failed} — set of conditions triggering failed status (e.g., 'empty_scan_N_cycles', 'invalid_scan_structure') (src/app/ugv_types.hpp: enum sensor_status_t)
finding imminent_default_on_faultFINDING — safe default value for imminent flag when sensor is faulted (collision_predictor.cpp:46-48 sets collision_imminent_ based on TTC/range thresholds but does NOT check bus.lidar_status_)
interpretive · review ok_status_valuesensor_status_t::ok — enum value representing healthy sensor status (lidar_interface.cpp:30: bus.lidar_status_ = ... ? sensor_status_t::ok : ...; when age_ms <= timeout)
finding obstacle_list_fault_behaviorFINDING — action taken on obstacle list during fault (e.g., 'clear', 'freeze', 'mark_unconfirmed') (obstacle_detector.cpp:73-163 unconditionally processes bus.lidar_scan_ and publishes bus.obstacles_ without checking bus.lidar_status_)
REQ-PER-006TiminghighCycle timing, staleness, and confirmation debouncing
The module shall detect LiDAR data as stale when the age exceeds lidar_stale_timeout_ms_ (USER-QUESTION), shall confirm an obstacle track after USER-QUESTION consecutive detection cycles, shall delete an unmatched track after USER-QUESTION consecutive non-detection cycles, and shall increment alive_counters_[module_id::perception] by 1 every USER-QUESTION ms.
user-question lidar_stale_timeout_msUSER-QUESTION — calibration parameter: max age before LiDAR data is stale (This is a calibration parameter from the ugv_calibration_t structure. The value is application-specific (depends on sensor update rate, latency tolerance, safety margins). The customer/integrator must specify this timeout based on system requirements.)
user-question obstacle_confirmation_cyclesUSER-QUESTION — calibration parameter: consecutive cycles to confirm obstacle (This is a calibration parameter from ugv_calibration_t. The number of consecutive detection cycles required to confirm a track is a tuning decision balancing false-positive suppression vs. detection latency. The customer must specify this based on operational requirements.)
user-question obstacle_deletion_cyclesUSER-QUESTION — calibration parameter: consecutive cycles without match to delete track (This is a calibration parameter from ugv_calibration_t. The number of non-detection cycles before deleting a track is a tuning decision balancing memory/clutter vs. track persistence through occlusions. The customer must specify this based on operational requirements.)
interpretive · review alive_counter_increment1 — increment value for heartbeat counter each cycle (collision_predictor.cpp:12: ++bus.alive_counters_[module_id::perception]; (pre-increment operator increments by 1))
user-question run_cycle_period_msUSER-QUESTION — expected module execution period for alive counter timing (The module execution period is determined by the application scheduler (not visible in perception source). This is an integration/deployment parameter that the customer must specify based on the control loop design and real-time requirements.)
REQ-PER-007Resource LimitshighBounded collections and array limits
The module shall limit the obstacles_ vector to at most 32 tracked obstacles, shall limit the lidar_scan_ vector to at most 360 points, shall apply reject_new (stop creating new tracks when limit reached) when obstacle limit is reached, and shall apply FINDING when scan point limit is reached.
deterministic max_obstacles32 — maximum number of simultaneously tracked obstacles (per AOU-006) (src/app/ugv_types.hpp: `static constexpr uint32_t max_obstacles = 32;`)
deterministic max_lidar_points360 — maximum number of LiDAR points accepted in one scan (src/app/ugv_types.hpp: `static constexpr uint32_t max_lidar_points = 360;`)
interpretive · review overflow_behavior_obstaclesreject_new (stop creating new tracks when limit reached) — action when max_obstacles reached (e.g., 'reject_new', 'evict_oldest', 'evict_farthest') (obstacle_detector.cpp:62-66,146: if (clusters.size() + 1 >= max_obstacles) { close_cluster(); break; } and line 146: if (tracks_.size() >= max_obstacles) break; — when capacity is reached, clustering stops emitting new clusters and new track creation is halted.)
finding overflow_behavior_scanFINDING — action when max_lidar_points reached (e.g., 'truncate', 'reject_scan', 'subsample') (lidar_interface.cpp:26: bus.lidar_scan_ = latest_scan_; — unconditional copy with no size check; obstacle_detector.cpp:74 processes bus.lidar_scan_ with no size validation)
REQ-PER-008StatemediumState-dependent behavior and mode transitions
The module shall transition obstacle tracks from confirmed_ == false to confirmed_ == true after obstacle_confirmation_cycles_ consecutive matches, shall only include obstacles with state confirmed_only (confirmed_ == true) in collision prediction and nearest-obstacle search, shall transition lidar_status_ from REVIEW to REVIEW when staleness timeout is exceeded, and shall transition from REVIEW to REVIEW when fresh_scan_received (injected_ == true and age <= timeout).
interpretive · review unconfirmed_stateconfirmed_ == false — state value representing an unconfirmed obstacle track (obstacle_detector.cpp:118,133,155: trk.confirmed_ is a boolean; set to false when consecutive_matches_ < confirm_cycles (line 133, 155 for new tracks))
interpretive · review confirmed_stateconfirmed_ == true — state value representing a confirmed obstacle track (obstacle_detector.cpp:118: trk.confirmed_ = trk.consecutive_matches_ >= confirm_cycles; — confirmed state is confirmed_ == true)
interpretive · review collision_eligible_statesconfirmed_only (confirmed_ == true) — set of obstacle states eligible for collision prediction (e.g., 'confirmed_only' or 'confirmed_and_provisional') (collision_predictor.cpp:24: if (!obs.confirmed_) continue; — only confirmed obstacles are considered for collision prediction and nearest-obstacle search)
interpretive · review recovery_conditionfresh_scan_received (injected_ == true and age <= timeout) — condition for sensor status recovery (e.g., 'fresh_scan_received', 'N_consecutive_valid_scans') (lidar_interface.cpp:14-18,29-31: when inject_scan() is called, injected_ is set true, then run() updates last_update_tick_ms_ and sets status to ok if age_ms <= timeout. Transition from stale→ok happens immediately when a fresh scan arrives and age constraint is satisfied.)
review ok_status_valueREVIEW (Slot left unresolved by the fill pass; manual review required.)
review stale_status_valueREVIEW (Slot left unresolved by the fill pass; manual review required.)
REQ-PER-009ConcurrencymediumExecution sequencing and data consistency
The module shall require that inject_scan() completes before run() is invoked in each cycle as specified in REVIEW, shall ensure that lidar_scan_ is not modified during entire_run_cycle (from cluster_scan entry to obstacles_ publication), shall update all output fields (obstacles_, collision_, lidar_status_, alive_counters_) atomically with respect to end_of_run (all outputs written at function exit), and shall FINDING reentrant calls to run() or inject_scan().
review injection_run_orderingREVIEW — enforced ordering between inject_scan and run (e.g., 'inject_before_run_required', 'inject_optional') (The code is designed expecting inject_scan() before run() (line 14-18 checks injected_ flag), but it is permissive: if inject_scan() was not called, run() uses stale data or marks sensor as failed. There is no explicit error or enforcement of the ordering. The requirement's intent ('required' vs. 'optional') is ambiguous in the implementation.)
interpretive · review scan_read_critical_sectionentire_run_cycle (from cluster_scan entry to obstacles_ publication) — phase during which lidar_scan_ must remain stable (e.g., 'during_clustering', 'entire_run_cycle') (obstacle_detector.cpp:74: const std::vector<cluster_t> clusters = cluster_scan(bus.lidar_scan_); — lidar_scan_ is read once at the start and clustering operates on that snapshot. No further modification to bus.lidar_scan_ occurs during run().)
interpretive · review output_atomicity_boundaryend_of_run (all outputs written at function exit) — synchronization boundary for output updates (e.g., 'end_of_run', 'per_field_independent') (collision_predictor.cpp:12,51: alive counter incremented at line 12, collision_ written at line 51; obstacle_detector.cpp:162: obstacles_ written at line 162; lidar_interface.cpp:21-22,26,29-31: lidar_scan_ and lidar_status_ written during run(). Each module's run() method writes its outputs before returning, providing a per-module atomicity boundary at function exit.)
finding reentrancy_policyFINDING — reentrancy handling (e.g., 'not_reentrant_error', 'not_reentrant_ignored', 'reentrant_safe') (obstacle_detector.cpp:73-163, collision_predictor.cpp:11-52, lidar_interface.cpp:8-32: no mutex, no reentrancy guard, no state checks to detect concurrent calls)

power_management

REQ-PWR-001Input DomainhighValid voltage measurement ranges
The module shall accept voltage measurements for main and backup rails within the range [USER-QUESTION, USER-QUESTION] volts, and shall treat values outside this range as invalid sensor data.
user-question min_physical_voltage_vUSER-QUESTION — Minimum physically plausible voltage for rail measurements, lower bound of sensor range (The code uses min_voltage_v_ as an undervoltage fault threshold but does NOT implement input validation to reject physically implausible sensor readings. The actual minimum plausible voltage must be supplied by the customer based on sensor specifications and expected operating environment. The fault threshold (min_voltage_v_) may differ from sensor range limits.)
user-question max_physical_voltage_vUSER-QUESTION — Maximum physically plausible voltage for rail measurements, upper bound of sensor range (The code uses max_voltage_v_ as an overvoltage fault threshold but does NOT implement input validation to reject physically implausible sensor readings. The actual maximum plausible voltage must be supplied by the customer based on sensor specifications and expected operating environment. The fault threshold (max_voltage_v_) may differ from sensor range limits.)
REQ-PWR-002Output DomainmediumPower status output value ranges
The module shall ensure power_main_.voltage and power_backup_.voltage are bounded to [FINDING, FINDING] volts, and all boolean flags (undervoltage, overvoltage, health) shall be strictly true or false with no intermediate states.
finding output_min_voltage_vFINDING — Minimum voltage value reported in power status outputs (power_supply.cpp:15-22, evaluate() function directly assigns voltage_v to status.voltage_v_ without any clamping or bounds checking.)
finding output_max_voltage_vFINDING — Maximum voltage value reported in power status outputs (power_supply.cpp:15-22, evaluate() function directly assigns voltage_v to status.voltage_v_ without any clamping or bounds checking.)
REQ-PWR-003Saturation / ClampinghighVoltage measurement saturation
When voltage measurements exceed physical rail limits, the module shall clamp reported voltage values to FINDING volts minimum and FINDING volts maximum before fault detection and output publication.
finding clamp_min_vFINDING — Lower saturation limit for voltage measurements (power_supply.cpp:15-22, evaluate() function assigns status.voltage_v_ = voltage_v without any clamping logic.)
finding clamp_max_vFINDING — Upper saturation limit for voltage measurements (power_supply.cpp:15-22, evaluate() function assigns status.voltage_v_ = voltage_v without any clamping logic.)
REQ-PWR-004Sign / UnitsmediumVoltage units and polarity convention
The module shall interpret all voltage measurements and calibration thresholds in units of volts with positive polarity representing higher electrical potential, using USER-QUESTION as the reference nominal operating point.
interpretive · review voltage_unitvolts — Physical unit for voltage (volts), confirmed from calibration parameter unit annotation (power_supply.hpp:17 parameter names 'main_v, backup_v' and power_supply.cpp:17 'status.voltage_v_'; struct member naming convention '_v_' suffix; power_distribution.cpp:15 'voltage_v_' and 'backup_switch_voltage_v_' all use '_v' or '_v_' suffix consistently indicating volts as the unit.)
user-question nominal_voltage_vUSER-QUESTION — Nominal operating voltage defining the reference point for normal operation (The nominal_voltage_v_ is a calibration parameter (cal_.nominal_voltage_v_) used as the default/initialization value for voltage measurements. This is a system design parameter that must be supplied by the customer based on the electrical architecture (e.g., 12V, 24V, 48V system). The code references it but does not define a concrete value.)
REQ-PWR-005Fault / Error ReactionhighPower rail fault detection and response
The module shall set the undervoltage flag when measured voltage falls below cal_.min_voltage_v_ volts, shall set the overvoltage flag when voltage exceeds cal_.max_voltage_v_ volts, shall mark rail health as failed when either fault is active, and shall activate backup power when main rail health is false or main voltage drops below cal_.backup_switch_voltage_v_ volts while backup health is true.
interpretive · review undervoltage_threshold_vcal_.min_voltage_v_ — Voltage threshold below which undervoltage fault is declared (power_supply.cpp:18 'status.undervoltage_ = (voltage_v < cal_.min_voltage_v_);' — undervoltage flag is set when measured voltage falls below cal_.min_voltage_v_.)
interpretive · review overvoltage_threshold_vcal_.max_voltage_v_ — Voltage threshold above which overvoltage fault is declared (power_supply.cpp:19 'status.overvoltage_ = (voltage_v > cal_.max_voltage_v_);' — overvoltage flag is set when measured voltage exceeds cal_.max_voltage_v_.)
interpretive · review main_unhealthy_statefalse — Boolean state indicating main rail is unhealthy (false) (power_supply.cpp:20 'status.ok_ = !status.undervoltage_ && !status.overvoltage_;' and power_distribution.cpp:17 '!bus.power_main_.ok_' — health/ok is true when healthy, false when unhealthy. Backup activates when main is NOT ok (line 17).)
interpretive · review backup_activation_threshold_vcal_.backup_switch_voltage_v_ — Voltage threshold for triggering backup power switchover (power_distribution.cpp:14-15 'const bool main_below_switch = (bus.power_main_.voltage_v_ < cal_.backup_switch_voltage_v_);' and line 17 activates backup when 'main_below_switch' is true.)
interpretive · review backup_healthy_statetrue — Boolean state indicating backup rail is healthy (true) (power_distribution.cpp:17 'bus.backup_power_active_ = (!bus.power_main_.ok_ || main_below_switch) && bus.power_backup_.ok_;' — backup activates only when 'bus.power_backup_.ok_' is true (healthy).)
REQ-PWR-006TimingmediumVoltage measurement staleness and cycle execution
The module shall use voltage measurements only if inject_voltage() was called in the current cycle, otherwise shall fall back to cal_.nominal_voltage_v_ volts, shall increment alive_counters_[module_id::power] by 1 each execution cycle, and shall execute before power_distribution in the USER-QUESTION millisecond task cycle.
interpretive · review default_voltage_vcal_.nominal_voltage_v_ — Default voltage value used when no measurement has been injected (power_supply.cpp:7-8 constructor initializes 'main_v_(cal.nominal_voltage_v_), backup_v_(cal.nominal_voltage_v_)' and power_supply.hpp:24-25 shows default member initialization to 'default_calibration.nominal_voltage_v_'. These values are used by run() when inject_voltage() has not been called.)
interpretive · review heartbeat_increment1 — Increment value for heartbeat counter per cycle (typically 1) (power_supply.cpp:25 '++bus.alive_counters_[module_id::power];' — pre-increment operator increments by 1 each cycle.)
user-question cycle_period_msUSER-QUESTION — Expected execution period of the power management task in milliseconds (The execution period of the power management task is a system-level configuration parameter not defined in the power_supply or power_distribution modules. This must be specified in the task scheduler configuration or system integration layer by the customer based on timing requirements.)
REQ-PWR-007Resource LimitslowHeartbeat counter overflow protection
The module shall allow alive_counters_[module_id::power] to increment modulo REVIEW without overflow error, wrapping to 0 when the maximum is reached.
review counter_max_valueREVIEW — Maximum value of the heartbeat counter before wraparound (2^32-1 for uint32_t) (The alive_counters_ array element is incremented but its underlying data type is not defined in the provided sources. If uint32_t, the max is 2^32-1 (4294967295). If uint8_t, max is 255. The actual type must be confirmed from signal_bus_t definition to determine the exact maximum value before wraparound.)
interpretive · review counter_wrap_value0 — Value to which counter resets after overflow (0 for unsigned types) (power_supply.cpp:25 '++bus.alive_counters_[module_id::power];' — C++ unsigned integer overflow behavior wraps to 0 by standard (modulo arithmetic). For any unsigned type (uint8_t, uint16_t, uint32_t), overflow wraps to 0.)
REQ-PWR-008StatehighBackup power activation state transitions
The module shall transition backup_power_active_ from false to true when main rail health becomes false OR main voltage drops below cal_.backup_switch_voltage_v_ AND backup rail health is true, and shall transition back to false when bus.power_main_.ok_ == true AND bus.power_main_.voltage_v_ >= cal_.backup_switch_voltage_v_.
interpretive · review backup_inactive_statefalse — State value when backup is not active (false) (power_distribution.cpp:17 'bus.backup_power_active_ = (!bus.power_main_.ok_ || main_below_switch) && bus.power_backup_.ok_;' — backup_power_active_ is assigned false when the condition is not met (inactive state).)
interpretive · review backup_active_statetrue — State value when backup is actively powering the system (true) (power_distribution.cpp:17 'bus.backup_power_active_ = (!bus.power_main_.ok_ || main_below_switch) && bus.power_backup_.ok_;' — backup_power_active_ is assigned true when activation conditions are met.)
interpretive · review trigger_unhealthyfalse — Main rail health state that triggers backup activation (false/unhealthy) (power_distribution.cpp:17 '!bus.power_main_.ok_' — backup activates when main ok_ is NOT true (i.e., when ok_ is false/unhealthy).)
interpretive · review switchover_voltage_vcal_.backup_switch_voltage_v_ — Main rail voltage threshold for backup activation (power_distribution.cpp:14-15 'const bool main_below_switch = (bus.power_main_.voltage_v_ < cal_.backup_switch_voltage_v_);' used in backup activation condition at line 17.)
interpretive · review backup_oktrue — Backup rail health state required for activation (true/healthy) (power_distribution.cpp:17 'bus.backup_power_active_ = (!bus.power_main_.ok_ || main_below_switch) && bus.power_backup_.ok_;' — requires 'bus.power_backup_.ok_' to be true for activation.)
interpretive · review deactivation_conditionbus.power_main_.ok_ == true AND bus.power_main_.voltage_v_ >= cal_.backup_switch_voltage_v_ — Condition under which backup power is deactivated (e.g., main restored above threshold) (power_distribution.cpp:17 'bus.backup_power_active_ = (!bus.power_main_.ok_ || main_below_switch) && bus.power_backup_.ok_;' — the assignment is re-evaluated every cycle, so backup deactivates when the condition becomes false: when main is healthy (ok_==true) AND main voltage is at or above switch threshold AND (implicitly) backup remains healthy.)
REQ-PWR-009ConcurrencyhighExecution ordering and shared state access
The module shall complete all updates to power_main_, power_backup_, power_ok_, and backup_power_active_ atomically before returning from run(), shall execute in task order position USER-QUESTION ensuring completion before power_distribution reads these outputs, and shall treat injected voltage measurements as latest value (single-buffered, last-write-wins) within the execution cycle.
user-question task_sequence_positionUSER-QUESTION — Sequence position in task schedule ensuring power_supply runs before power_distribution (The task execution order is a system-level scheduler configuration not defined within the power_supply or power_distribution modules. The customer must configure the task scheduler to ensure power_supply::run() executes before power_distribution::run() in each cycle. The specific sequence position or priority values depend on the RTOS/scheduler implementation.)
interpretive · review voltage_access_patternlatest value (single-buffered, last-write-wins) — Concurrency pattern for voltage data (e.g., 'snapshot at cycle start', 'latest value', 'double-buffered') (power_supply.cpp:10-12 'inject_voltage()' directly writes to main_v_ and backup_v_ member variables; power_supply.cpp:27-28 'run()' reads these same variables. No double-buffering, locking, or snapshot mechanism exists. The pattern is single-buffered last-write-wins.)

cyclic_scheduler

REQ-CYC-001Input DomainhighTask registration parameter validity
The module shall reject task registration if the period_ms is not evenly divisible by 10, or if offset_ms is greater than or equal to period_ms, or if priority is not one of {high, medium, low}.
interpretive · review base_period_ms10 — The fundamental scheduler tick period against which all task periods must align (cyclic_scheduler.hpp:31 — constructor default parameter `base_period_ms = 10`; this is the fundamental tick period stored in base_period_ms_ and used throughout the scheduler.)
interpretive · review valid_priority_values{high, medium, low} — The enumerated set of valid priority levels (e.g., high, medium, low) (ugv_types.hpp:81-85 — `enum class task_priority_t : uint8_t { high = 0, medium = 1, low = 2 };` — this is the exhaustive set of valid priority values.)
REQ-CYC-002Output DomainmediumTick counter and time progression validity
The module shall increment tick_ms_ by exactly base_period_ms_ (constructor parameter, defaults to 10) on each successful tick, and tick_ms_ shall be monotonically increasing modulo 2^64.
interpretive · review cycle_time_msbase_period_ms_ (constructor parameter, defaults to 10) — The fixed time increment applied to tick_ms_ each scheduler cycle (cyclic_scheduler.cpp:17 — `const uint64_t now_ms = tick_counter_ * base_period_ms_;` — the tick advances by base_period_ms_ each cycle; line 43 shows `++tick_counter_` increments by 1, thus tick_ms_ advances by base_period_ms_ each tick.)
interpretive · review tick_overflow_bound2^64 — The maximum value before tick_ms_ wraps (per AOU-006 overflow is acceptable) (cyclic_scheduler.hpp:42 — `uint64_t tick_counter_ = 0;` and cyclic_scheduler.cpp:17 — `const uint64_t now_ms = tick_counter_ * base_period_ms_;` — both are uint64_t, wrapping at 2^64.)
REQ-CYC-003Saturation / ClampinghighTask queue and registration limits
The module shall reject task registration attempts when the number of registered tasks reaches FINDING, and shall limit the number of tasks executed per tick to at most FINDING.
finding max_registered_tasksFINDING — Maximum number of tasks that can be registered with the scheduler (cyclic_scheduler.cpp:11-14 — `register_task` unconditionally appends to `tasks_` (std::vector) with no capacity check or rejection logic.)
finding max_tasks_per_tickFINDING — Maximum number of task callbacks executed in a single tick (may equal max_registered_tasks or be separately limited) (cyclic_scheduler.cpp:21-34 — `due` vector is built from all tasks that meet timing condition, with no iteration limit or early-exit; lines 36-41 execute all tasks in `due` with no cap.)
REQ-CYC-004Sign / UnitsmediumTime unit consistency and conversion
The module shall maintain tick_ms_ in units of milliseconds, shall convert all period and offset parameters specified in milliseconds to tick counts by dividing by 10, and shall convert frequency parameters in Hz to periods using the formula FINDING.
interpretive · review time_unitmilliseconds — The fundamental time unit used throughout the scheduler (milliseconds) (cyclic_scheduler.cpp:17 — `const uint64_t now_ms = tick_counter_ * base_period_ms_;` then line 18 `bus.tick_ms_ = now_ms;` — the `_ms_` suffix and computation confirm milliseconds.)
interpretive · review base_period_ms10 — The base period for converting time durations to tick counts (Same as REQ-CYC-001 fill: cyclic_scheduler.hpp:31 default parameter `base_period_ms = 10`.)
finding hz_to_ms_formulaFINDING — Formula to convert frequency in Hz to period in milliseconds (e.g., period_ms = 1000 / freq_hz) (No frequency-based registration API exists in cyclic_scheduler.hpp or cyclic_scheduler.cpp. The register_task interface (cyclic_scheduler.hpp:13-19, cyclic_scheduler.cpp:11-14) accepts only period_ms_, not frequency_hz.)
REQ-CYC-005Fault / Error ReactionhighTask callback exception and failure handling
The module shall catch any exception thrown by a task callback despite AOU-003, shall log the fault to FINDING, shall mark the offending task with status FINDING, and shall either skip or disable the task according to policy FINDING; if the signal_bus_t is uninitialized when run_tick is called despite AOU-004, the module shall return error code FINDING.
finding fault_log_destinationFINDING — Destination for logging task callback exceptions (likely faults_ output) (cyclic_scheduler.cpp:36-41 — task callbacks are invoked with no try-catch block; exceptions are not caught, contradicting requirement.)
finding task_fault_statusFINDING — The status value assigned to a task that has thrown an exception (No task status tracking exists in task_config_t (cyclic_scheduler.hpp:13-19) or task_stats_t (hpp:21-24). The code does not mark tasks with fault status.)
finding fault_task_policyFINDING — Policy determining whether a faulted task is skipped once or permanently disabled (No exception handling or fault policy logic exists (see cyclic_scheduler.cpp:36-41). The code unconditionally executes all due tasks each tick.)
finding uninitialized_bus_error_codeFINDING — Error code returned when run_tick is called with uninitialized signal_bus_t (cyclic_scheduler.cpp:16 — `run_tick(signal_bus_t& bus)` returns void and performs no initialization check on the bus parameter.)
REQ-CYC-006TiminghighPeriodic task scheduling timing accuracy
The module shall execute a task when ((tick_ms_ - 0 (implicit baseline)) % period_ms) equals offset_ms within tolerance 0, shall execute tasks in priority order (high (0), then medium (1), then low (2)) using stable sort preserving registration order within each priority level, and shall complete all due task callbacks within FINDING of tick start.
interpretive · review task_registration_time_ms0 (implicit baseline) — The tick_ms_ value at which task registration occurred, used as timing baseline (cyclic_scheduler.cpp:24-27 — condition is `if (now_ms < t.offset_ms_) continue;` then line 27 `if (((now_ms - t.offset_ms_) % t.period_ms_) == 0)` — the modulo uses offset_ms_ as the phase shift from tick zero, implying registration time is effectively tick 0.)
interpretive · review scheduling_jitter_ms0 — Acceptable timing jitter for task execution (typically zero for integer modulo) (cyclic_scheduler.cpp:27 — exact equality check `((now_ms - t.offset_ms_) % t.period_ms_) == 0` with no tolerance.)
interpretive · review priority_order_sequencehigh (0), then medium (1), then low (2) — The execution order sequence for task priorities (e.g., descending from high to low) (cyclic_scheduler.cpp:31-34 — `std::stable_sort` with comparator `tasks_[a].priority_ < tasks_[b].priority_` (ascending by uint8_t value); ugv_types.hpp:81-85 defines high=0, medium=1, low=2, thus sorts high first.)
finding max_tick_execution_time_msFINDING — Maximum allowed execution time for all tasks in a single tick (per AOU-002) (cyclic_scheduler.cpp:36-41 — tasks execute in a loop with no timeout check, deadline enforcement, or execution time measurement.)
REQ-CYC-007Resource LimitsmediumSignal bus and module tracking bounds
The module shall limit the alive_counters_ array to exactly 12 entries corresponding to the fixed module count, shall prevent writes beyond this bound, and shall ensure the signal_bus_t container for lidar_scan_ points does not exceed 360 and obstacles_ does not exceed 32.
interpretive · review max_alive_counter_modules12 — Fixed number of module alive counters (12 per interface description) (signal_bus.hpp:73-74 — `static constexpr uint32_t num_monitored_modules = 12; std::array<uint32_t, num_monitored_modules> alive_counters_ = {};` — fixed array of size 12.)
deterministic max_lidar_points360 — Maximum number of lidar points that can be stored in lidar_scan_ vector (src/app/ugv_types.hpp: `static constexpr uint32_t max_lidar_points = 360;`)
deterministic max_obstacles32 — Maximum number of obstacle entries that can be stored in obstacles_ vector (src/app/ugv_types.hpp: `static constexpr uint32_t max_obstacles = 32;`)
REQ-CYC-008StatehighScheduler lifecycle state transitions
The module shall enforce state FINDING before signal_bus_t initialization, shall transition to state FINDING after initialization allowing task registration, shall transition to state FINDING on first run_tick() call and prohibit further task registration, and shall reject run_tick() if in state FINDING.
finding uninitialized_stateFINDING — Initial state before signal_bus_t initialization (per AOU-004) (No state machine or state tracking exists in cyclic_scheduler (cyclic_scheduler.hpp, cyclic_scheduler.cpp). The scheduler has no state field or initialization tracking.)
finding registration_stateFINDING — State after initialization where task registration is allowed (per AOU-005) (Same as uninitialized_state: no state machine exists in the scheduler.)
finding running_stateFINDING — State after first run_tick() where execution occurs and registration is prohibited (Same as uninitialized_state: no state machine exists. Additionally, register_task (cyclic_scheduler.cpp:11-14) has no state check or rejection logic preventing registration after run_tick starts.)
REQ-CYC-009ConcurrencyhighSignal bus data race prevention
The module shall execute all task callbacks sequentially within a single tick with no concurrent execution, shall ensure that signal_bus_t writes by task N are visible to task N+1 in the same tick execution sequence, shall protect signal_bus_t access with synchronization mechanism NONE if run_tick() can be called from multiple threads, and shall prevent task callbacks from directly modifying scheduler state by providing non-const reference (READ_WRITE) access to signal_bus_t.
interpretive · review bus_sync_mechanismNONE — Synchronization mechanism protecting signal_bus_t if multi-threaded access is possible (cyclic_scheduler.cpp:16-44 and signal_bus.hpp:12-78 — no mutex, lock_guard, atomic, or synchronization primitive is present. The signal_bus_t is a plain struct with no thread-safety mechanisms.)
interpretive · review bus_access_modenon-const reference (READ_WRITE) — Access mode provided to task callbacks for signal_bus_t (e.g., non-const reference, pointer) (cyclic_scheduler.cpp:16 — `void run_tick(signal_bus_t& bus)` and line 38 — `tasks_[idx].run_(bus)` passes non-const reference; task_config_t::run_ (cyclic_scheduler.hpp:18) is `std::function<void(signal_bus_t&)>` — non-const reference gives full read-write access.)