Files
sunnypilot-byd/opendbc_repo/opendbc/safety/sunnypilot/mads.h
T
github-actions[bot] de77c89cf6 sunnypilot v2026.002.000
version: sunnypilot v2026.002.000 (staging)
date: 2026-07-21T07:44:02
master commit: 2c334ede443d7391d27575af8c854a095ba702a8
2026-07-21 07:44:02 +00:00

193 lines
7.3 KiB
C

/**
* Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
*
* This file is part of sunnypilot and is licensed under the MIT License.
* See the LICENSE.md file in the root directory for more details.
*/
#pragma once
#include "opendbc/safety/sunnypilot/mads_declarations.h"
// ===============================
// Global Variables
// ===============================
ButtonState mads_button_press = MADS_BUTTON_UNAVAILABLE;
MADSState m_mads_state;
bool controls_allowed_lateral = false;
// state for mads controls_allowed_lateral timeout logic
bool heartbeat_engaged_mads = false; // MADS enabled, passed in heartbeat USB command
uint32_t heartbeat_engaged_mads_mismatches = 0U; // count of mismatches between heartbeat_engaged_mads and controls_allowed_lateral
// ===============================
// State Update Helpers
// ===============================
inline EdgeTransition m_get_edge_transition(const bool current, const bool last) {
EdgeTransition state;
if (current && !last) {
state = MADS_EDGE_RISING;
} else if (!current && last) {
state = MADS_EDGE_FALLING;
} else {
state = MADS_EDGE_NO_CHANGE;
}
return state;
}
inline void m_mads_state_init(void) {
m_mads_state.is_vehicle_moving = NULL;
m_mads_state.acc_main.current = NULL;
m_mads_state.mads_button.current = MADS_BUTTON_UNAVAILABLE;
m_mads_state.system_enabled = false;
m_mads_state.disengage_lateral_on_brake = false;
m_mads_state.pause_lateral_on_brake = false;
m_mads_state.acc_main.previous = false;
m_mads_state.acc_main.transition = MADS_EDGE_NO_CHANGE;
m_mads_state.mads_button.last = MADS_BUTTON_UNAVAILABLE;
m_mads_state.mads_button.transition = MADS_EDGE_NO_CHANGE;
m_mads_state.current_disengage.active_reason = MADS_DISENGAGE_REASON_NONE;
m_mads_state.current_disengage.pending_reasons = MADS_DISENGAGE_REASON_NONE;
m_mads_state.controls_requested_lateral = false;
controls_allowed_lateral = false;
}
inline void m_update_button_state(ButtonStateTracking *button_state) {
if (button_state->current != MADS_BUTTON_UNAVAILABLE) {
button_state->transition = m_get_edge_transition(button_state->current == MADS_BUTTON_PRESSED, button_state->last == MADS_BUTTON_PRESSED);
button_state->last = button_state->current;
}
}
inline void m_update_binary_state(BinaryStateTracking *state) {
state->transition = m_get_edge_transition(state->current, state->previous);
state->previous = state->current;
}
/**
* @brief Updates the MADS control state based on current system conditions
*
* @return void
*/
inline void m_update_control_state(void) {
bool allowed = true;
// Initial control requests from button or ACC transitions
if ((m_mads_state.acc_main.transition == MADS_EDGE_RISING) ||
(m_mads_state.mads_button.transition == MADS_EDGE_RISING) ||
(m_mads_state.op_controls_allowed.transition == MADS_EDGE_RISING)) {
m_mads_state.controls_requested_lateral = true;
}
// Primary control blockers - these prevent any further control processing
if (m_mads_state.acc_main.transition == MADS_EDGE_FALLING) {
mads_exit_controls(MADS_DISENGAGE_REASON_ACC_MAIN_OFF);
allowed = false; // No matter what, no further control processing on this cycle
}
if (m_mads_state.mads_steering_disengage.transition == MADS_EDGE_RISING) {
mads_exit_controls(MADS_DISENGAGE_REASON_STEERING_DISENGAGE);
allowed = false; // No matter what, no further control processing on this cycle
}
if (m_mads_state.disengage_lateral_on_brake && (m_mads_state.braking.transition == MADS_EDGE_RISING)) {
mads_exit_controls(MADS_DISENGAGE_REASON_BRAKE);
allowed = false;
}
// Secondary control conditions - only checked if primary conditions don't block further control processing
if (allowed && m_mads_state.pause_lateral_on_brake) {
// Brake rising edge immediately blocks controls
// Brake release might request controls if brake was the ONLY reason for disengagement
if (m_mads_state.braking.transition == MADS_EDGE_RISING) {
mads_exit_controls(MADS_DISENGAGE_REASON_BRAKE);
allowed = false;
} else if ((m_mads_state.braking.transition == MADS_EDGE_FALLING) &&
(m_mads_state.current_disengage.active_reason == MADS_DISENGAGE_REASON_BRAKE) &&
(m_mads_state.current_disengage.pending_reasons == MADS_DISENGAGE_REASON_BRAKE)) {
m_mads_state.controls_requested_lateral = true;
} else if (m_mads_state.braking.current) {
allowed = false;
} else {
}
}
// Process control request if conditions allow. Gate the write on system_enabled so that
// stock safety builds (MADS disabled) never flip the global on.
if (allowed && m_mads_state.system_enabled && m_mads_state.controls_requested_lateral && !controls_allowed_lateral) {
m_mads_state.controls_requested_lateral = false;
controls_allowed_lateral = true;
m_mads_state.current_disengage.active_reason = MADS_DISENGAGE_REASON_NONE;
m_mads_state.current_disengage.pending_reasons = MADS_DISENGAGE_REASON_NONE;
}
}
inline void mads_heartbeat_engaged_check(void) {
if (controls_allowed_lateral && !heartbeat_engaged_mads) {
heartbeat_engaged_mads_mismatches += 1U;
if (heartbeat_engaged_mads_mismatches >= 3U) {
mads_exit_controls(MADS_DISENGAGE_REASON_HEARTBEAT_ENGAGED_MISMATCH);
}
} else {
heartbeat_engaged_mads_mismatches = 0U;
}
}
// ===============================
// Function Implementations
// ===============================
inline void mads_set_alternative_experience(const int *mode) {
const bool mads_enabled = (*mode & ALT_EXP_ENABLE_MADS) != 0;
const bool disengage_lateral_on_brake = (*mode & ALT_EXP_MADS_DISENGAGE_LATERAL_ON_BRAKE) != 0;
const bool pause_lateral_on_brake = (*mode & ALT_EXP_MADS_PAUSE_LATERAL_ON_BRAKE) != 0;
mads_set_system_state(mads_enabled, disengage_lateral_on_brake, pause_lateral_on_brake);
}
extern inline void mads_set_system_state(const bool enabled, const bool disengage_lateral_on_brake, const bool pause_lateral_on_brake) {
m_mads_state_init();
m_mads_state.system_enabled = enabled;
m_mads_state.disengage_lateral_on_brake = disengage_lateral_on_brake;
m_mads_state.pause_lateral_on_brake = pause_lateral_on_brake;
}
inline void mads_exit_controls(const DisengageReason reason) {
// Always track this as a pending reason
m_mads_state.current_disengage.pending_reasons |= reason;
if (controls_allowed_lateral) {
m_mads_state.current_disengage.active_reason = reason;
m_mads_state.controls_requested_lateral = false;
controls_allowed_lateral = false;
}
}
inline void mads_state_update(const bool op_vehicle_moving, const bool op_acc_main, const bool op_allowed, const bool is_braking, const bool _steering_disengage) {
m_mads_state.is_vehicle_moving = op_vehicle_moving;
m_mads_state.acc_main.current = op_acc_main;
m_mads_state.op_controls_allowed.current = op_allowed;
m_mads_state.mads_button.current = mads_button_press;
m_mads_state.braking.current = is_braking;
m_mads_state.mads_steering_disengage.current = _steering_disengage;
m_update_binary_state(&m_mads_state.acc_main);
m_update_binary_state(&m_mads_state.op_controls_allowed);
m_update_binary_state(&m_mads_state.braking);
m_update_binary_state(&m_mads_state.mads_steering_disengage);
m_update_button_state(&m_mads_state.mads_button);
m_update_control_state();
}