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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 10 additions & 52 deletions board/safety/safety_hyundai.h
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#include "safety_hyundai_common.h"

const SteeringLimits HYUNDAI_STEERING_LIMITS = {
.max_steer = 384,
.max_rt_delta = 112,
Expand All @@ -9,8 +11,6 @@ const SteeringLimits HYUNDAI_STEERING_LIMITS = {
.type = TorqueDriverLimited,
};

const int HYUNDAI_STANDSTILL_THRSLD = 30; // ~1kph

const int HYUNDAI_MAX_ACCEL = 200; // 1/100 m/s2
const int HYUNDAI_MIN_ACCEL = -350; // 1/100 m/s2

Expand Down Expand Up @@ -82,23 +82,10 @@ const int HYUNDAI_PARAM_HYBRID_GAS = 2;
const int HYUNDAI_PARAM_LONGITUDINAL = 4;
const int HYUNDAI_PARAM_CAMERA_SCC = 8;

enum {
HYUNDAI_BTN_NONE = 0,
HYUNDAI_BTN_RESUME = 1,
HYUNDAI_BTN_SET = 2,
HYUNDAI_BTN_CANCEL = 4,
};

// some newer HKG models can re-enable after spamming cancel button,
// so keep track of user button presses to deny engagement if no interaction
const uint8_t HYUNDAI_PREV_BUTTON_SAMPLES = 8; // roughly 160 ms
uint8_t hyundai_last_button_interaction; // button messages since the user pressed an enable button

bool hyundai_legacy = false;
bool hyundai_ev_gas_signal = false;
bool hyundai_hybrid_gas_signal = false;
bool hyundai_camera_scc = false;
bool hyundai_longitudinal = false;

addr_checks hyundai_rx_checks = {hyundai_addr_checks, HYUNDAI_ADDR_CHECK_LEN};

Expand Down Expand Up @@ -186,20 +173,10 @@ static int hyundai_rx_hook(CANPacket_t *to_push) {
int addr = GET_ADDR(to_push);

// SCC12 is on bus 2 for camera-based SCC cars, bus 0 on all others
if (valid && (((bus == 0) && !hyundai_camera_scc) || ((bus == 2) && hyundai_camera_scc))) {
// enter controls on rising edge of ACC and user button press, exit controls when ACC off
if (!hyundai_longitudinal && (addr == 1057)) {
// 2 bits: 13-14
int cruise_engaged = (GET_BYTES_04(to_push) >> 13) & 0x3U;
if (cruise_engaged && !cruise_engaged_prev && (hyundai_last_button_interaction < HYUNDAI_PREV_BUTTON_SAMPLES)) {
controls_allowed = 1;
}

if (!cruise_engaged) {
controls_allowed = 0;
}
cruise_engaged_prev = cruise_engaged;
}
if (valid && (addr == 1057) && (((bus == 0) && !hyundai_camera_scc) || ((bus == 2) && hyundai_camera_scc))) {
// 2 bits: 13-14
int cruise_engaged = (GET_BYTES_04(to_push) >> 13) & 0x3U;
hyundai_common_cruise_state_check(cruise_engaged);
}

if (valid && (bus == 0)) {
Expand All @@ -213,28 +190,7 @@ static int hyundai_rx_hook(CANPacket_t *to_push) {
if (addr == 1265) {
int cruise_button = GET_BYTE(to_push, 0) & 0x7U;
int main_button = GET_BIT(to_push, 3U);

if ((cruise_button == HYUNDAI_BTN_RESUME) || (cruise_button == HYUNDAI_BTN_SET) || (cruise_button == HYUNDAI_BTN_CANCEL) || (main_button != 0)) {
hyundai_last_button_interaction = 0U;
} else {
hyundai_last_button_interaction = MIN(hyundai_last_button_interaction + 1U, HYUNDAI_PREV_BUTTON_SAMPLES);
}

if (hyundai_longitudinal) {
// exit controls on cancel press
if (cruise_button == HYUNDAI_BTN_CANCEL) {
controls_allowed = 0;
}

// enter controls on falling edge of resume or set
bool set = (cruise_button == HYUNDAI_BTN_NONE) && (cruise_button_prev == HYUNDAI_BTN_SET);
bool res = (cruise_button == HYUNDAI_BTN_NONE) && (cruise_button_prev == HYUNDAI_BTN_RESUME);
if (set || res) {
controls_allowed = 1;
}

cruise_button_prev = cruise_button;
}
hyundai_common_cruise_buttons_check(cruise_button, main_button);
}

// read gas pressed signal
Expand All @@ -249,7 +205,7 @@ static int hyundai_rx_hook(CANPacket_t *to_push) {

// sample wheel speed, averaging opposite corners
if (addr == 902) {
int hyundai_speed = (GET_BYTES_04(to_push) & 0x3FFFU) + ((GET_BYTES_48(to_push) >> 16) & 0x3FFFU); // FL + RR
uint32_t hyundai_speed = (GET_BYTES_04(to_push) & 0x3FFFU) + ((GET_BYTES_48(to_push) >> 16) & 0x3FFFU); // FL + RR
hyundai_speed /= 2;
vehicle_moving = hyundai_speed > HYUNDAI_STANDSTILL_THRSLD;
}
Expand Down Expand Up @@ -377,6 +333,8 @@ static const addr_checks* hyundai_init(uint16_t param) {
#ifdef ALLOW_DEBUG
// TODO: add longitudinal support for camera-based SCC platform
hyundai_longitudinal = GET_FLAG(param, HYUNDAI_PARAM_LONGITUDINAL) && !hyundai_camera_scc;
#else
hyundai_longitudinal = false;
#endif

if (hyundai_longitudinal) {
Expand Down
24 changes: 7 additions & 17 deletions board/safety/safety_hyundai_canfd.h
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#include "safety_hyundai_common.h"

const SteeringLimits HYUNDAI_CANFD_STEERING_LIMITS = {
.max_steer = 270,
.max_rt_delta = 112,
Expand All @@ -9,8 +11,6 @@ const SteeringLimits HYUNDAI_CANFD_STEERING_LIMITS = {
.type = TorqueDriverLimited,
};

const uint32_t HYUNDAI_CANFD_STANDSTILL_THRSLD = 30; // ~1kph

const CanMsg HYUNDAI_CANFD_HDA2_TX_MSGS[] = {
{0x50, 0, 16}, // LKAS
{0x1CF, 1, 8}, // CRUISE_BUTTON
Expand Down Expand Up @@ -127,25 +127,13 @@ static int hyundai_canfd_rx_hook(CANPacket_t *to_push) {
cruise_button = (GET_BYTE(to_push, 4) >> 4) & 0x7U;
main_button = GET_BIT(to_push, 34U);
}

if ((cruise_button == HYUNDAI_BTN_RESUME) || (cruise_button == HYUNDAI_BTN_SET) || (cruise_button == HYUNDAI_BTN_CANCEL) || (main_button != 0)) {
hyundai_last_button_interaction = 0U;
} else {
hyundai_last_button_interaction = MIN(hyundai_last_button_interaction + 1U, HYUNDAI_PREV_BUTTON_SAMPLES);
}
hyundai_common_cruise_buttons_check(cruise_button, main_button);
}

// cruise state
if (addr == 0x175) {
bool cruise_engaged = GET_BIT(to_push, 68U);
if (cruise_engaged && !cruise_engaged_prev && (hyundai_last_button_interaction < HYUNDAI_PREV_BUTTON_SAMPLES)) {
controls_allowed = 1;
}

if (!cruise_engaged) {
controls_allowed = 0;
}
cruise_engaged_prev = cruise_engaged;
hyundai_common_cruise_state_check(cruise_engaged);
}

// gas press
Expand All @@ -167,7 +155,7 @@ static int hyundai_canfd_rx_hook(CANPacket_t *to_push) {
for (int i = 8; i < 15; i+=2) {
speed += GET_BYTE(to_push, i) | (GET_BYTE(to_push, i + 1) << 8U);
}
vehicle_moving = (speed / 4U) > HYUNDAI_CANFD_STANDSTILL_THRSLD;
vehicle_moving = (speed / 4U) > HYUNDAI_STANDSTILL_THRSLD;
}
}

Expand Down Expand Up @@ -248,6 +236,8 @@ static const addr_checks* hyundai_canfd_init(uint16_t param) {
hyundai_canfd_hda2 = GET_FLAG(param, HYUNDAI_PARAM_CANFD_HDA2);
hyundai_canfd_alt_buttons = GET_FLAG(param, HYUNDAI_PARAM_CANFD_ALT_BUTTONS);

hyundai_longitudinal = false;

return &hyundai_canfd_rx_checks;
}

Expand Down
61 changes: 61 additions & 0 deletions board/safety/safety_hyundai_common.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
#ifndef SAFETY_HYUNDAI_COMMON_H
#define SAFETY_HYUNDAI_COMMON_H

const uint8_t HYUNDAI_PREV_BUTTON_SAMPLES = 8; // roughly 160 ms
const uint32_t HYUNDAI_STANDSTILL_THRSLD = 30; // ~1kph

enum {
HYUNDAI_BTN_NONE = 0,
HYUNDAI_BTN_RESUME = 1,
HYUNDAI_BTN_SET = 2,
HYUNDAI_BTN_CANCEL = 4,
};

// common state
bool hyundai_longitudinal = false;
uint8_t hyundai_last_button_interaction; // button messages since the user pressed an enable button


void hyundai_common_cruise_state_check(const int cruise_engaged) {
// some newer HKG models can re-enable after spamming cancel button,
// so keep track of user button presses to deny engagement if no interaction

// enter controls on rising edge of ACC and recent user button press, exit controls when ACC off
if (!hyundai_longitudinal) {
if (cruise_engaged && !cruise_engaged_prev && (hyundai_last_button_interaction < HYUNDAI_PREV_BUTTON_SAMPLES)) {
controls_allowed = 1;
}

if (!cruise_engaged) {
controls_allowed = 0;
}
cruise_engaged_prev = cruise_engaged;
}
}

void hyundai_common_cruise_buttons_check(const int cruise_button, const int main_button) {
if ((cruise_button == HYUNDAI_BTN_RESUME) || (cruise_button == HYUNDAI_BTN_SET) || (cruise_button == HYUNDAI_BTN_CANCEL) ||
(main_button != 0)) {
hyundai_last_button_interaction = 0U;
} else {
hyundai_last_button_interaction = MIN(hyundai_last_button_interaction + 1U, HYUNDAI_PREV_BUTTON_SAMPLES);
}

if (hyundai_longitudinal) {
// exit controls on cancel press
if (cruise_button == HYUNDAI_BTN_CANCEL) {
controls_allowed = 0;
}

// enter controls on falling edge of resume or set
bool set = (cruise_button == HYUNDAI_BTN_NONE) && (cruise_button_prev == HYUNDAI_BTN_SET);
bool res = (cruise_button == HYUNDAI_BTN_NONE) && (cruise_button_prev == HYUNDAI_BTN_RESUME);
if (set || res) {
controls_allowed = 1;
}

cruise_button_prev = cruise_button;
}
}

#endif
65 changes: 65 additions & 0 deletions tests/safety/hyundai_common.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
class Buttons:
NONE = 0
RESUME = 1
SET = 2
CANCEL = 4

PREV_BUTTON_SAMPLES = 8
ENABLE_BUTTONS = (Buttons.RESUME, Buttons.SET, Buttons.CANCEL)


class HyundaiButtonBase: #(common.PandaSafetyTest):
# pylint: disable=no-member,abstract-method
BUTTONS_BUS = 0 # tx on this bus, rx on 0. added to all `self._tx(self._button_msg(...))`
SCC_BUS = 0 # rx on this bus

def test_button_sends(self):
"""
Only RES and CANCEL buttons are allowed
- RES allowed while controls allowed
- CANCEL allowed while cruise is enabled
"""
self.safety.set_controls_allowed(0)
self.assertFalse(self._tx(self._button_msg(Buttons.RESUME, bus=self.BUTTONS_BUS)))
self.assertFalse(self._tx(self._button_msg(Buttons.SET, bus=self.BUTTONS_BUS)))

self.safety.set_controls_allowed(1)
self.assertTrue(self._tx(self._button_msg(Buttons.RESUME, bus=self.BUTTONS_BUS)))
self.assertFalse(self._tx(self._button_msg(Buttons.SET, bus=self.BUTTONS_BUS)))

for enabled in (True, False):
self._rx(self._pcm_status_msg(enabled))
self.assertEqual(enabled, self._tx(self._button_msg(Buttons.CANCEL, bus=self.BUTTONS_BUS)))

def test_enable_control_allowed_from_cruise(self):
"""
Hyundai non-longitudinal only enables on PCM rising edge and recent button press. Tests PCM enabling with:
- disallowed: No buttons
- disallowed: Buttons that don't enable cruise
- allowed: Buttons that do enable cruise
- allowed: Main button with all above combinations
"""
for main_button in (0, 1):
for btn in range(8):
for _ in range(PREV_BUTTON_SAMPLES): # reset
self._rx(self._button_msg(Buttons.NONE))

self._rx(self._pcm_status_msg(False))
self.assertFalse(self.safety.get_controls_allowed())
self._rx(self._button_msg(btn, main_button=main_button))
self._rx(self._pcm_status_msg(True))
controls_allowed = btn in ENABLE_BUTTONS or main_button
self.assertEqual(controls_allowed, self.safety.get_controls_allowed())

def test_sampling_cruise_buttons(self):
"""
Test that we allow controls on recent button press, but not as button leaves sliding window
"""
self._rx(self._button_msg(Buttons.SET))
for i in range(2 * PREV_BUTTON_SAMPLES):
self._rx(self._pcm_status_msg(False))
self.assertFalse(self.safety.get_controls_allowed())
self._rx(self._pcm_status_msg(True))
controls_allowed = i < PREV_BUTTON_SAMPLES
self.assertEqual(controls_allowed, self.safety.get_controls_allowed())
self._rx(self._button_msg(Buttons.NONE))
55 changes: 1 addition & 54 deletions tests/safety/test_hyundai.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from panda.tests.safety import libpandasafety_py
import panda.tests.safety.common as common
from panda.tests.safety.common import CANPackerPanda, make_msg
from panda.tests.safety.hyundai_common import HyundaiButtonBase

MAX_ACCEL = 2.0
MIN_ACCEL = -3.5
Expand Down Expand Up @@ -56,61 +57,7 @@ def checksum(msg):
return addr, t, ret, bus


class HyundaiButtonBase: #(common.PandaSafetyTest):
# pylint: disable=no-member,abstract-method
BUTTONS_BUS = 0 # tx on this bus, rx on 0. added to all `self._tx(self._button_msg(...))`
SCC_BUS = 0 # rx on this bus

def test_button_sends(self):
"""
Only RES and CANCEL buttons are allowed
- RES allowed while controls allowed
- CANCEL allowed while cruise is enabled
"""
self.safety.set_controls_allowed(0)
self.assertFalse(self._tx(self._button_msg(Buttons.RESUME, bus=self.BUTTONS_BUS)))
self.assertFalse(self._tx(self._button_msg(Buttons.SET, bus=self.BUTTONS_BUS)))

self.safety.set_controls_allowed(1)
self.assertTrue(self._tx(self._button_msg(Buttons.RESUME, bus=self.BUTTONS_BUS)))
self.assertFalse(self._tx(self._button_msg(Buttons.SET, bus=self.BUTTONS_BUS)))

for enabled in (True, False):
self._rx(self._pcm_status_msg(enabled))
self.assertEqual(enabled, self._tx(self._button_msg(Buttons.CANCEL, bus=self.BUTTONS_BUS)))

def test_enable_control_allowed_from_cruise(self):
"""
Hyundai non-longitudinal only enables on PCM rising edge and recent button press. Tests PCM enabling with:
- disallowed: No buttons
- disallowed: Buttons that don't enable cruise
- allowed: Buttons that do enable cruise
- allowed: Main button with all above combinations
"""
for main_button in (0, 1):
for btn in range(8):
for _ in range(PREV_BUTTON_SAMPLES): # reset
self._rx(self._button_msg(Buttons.NONE))

self._rx(self._pcm_status_msg(False))
self.assertFalse(self.safety.get_controls_allowed())
self._rx(self._button_msg(btn, main_button=main_button))
self._rx(self._pcm_status_msg(True))
controls_allowed = btn in ENABLE_BUTTONS or main_button
self.assertEqual(controls_allowed, self.safety.get_controls_allowed())

def test_sampling_cruise_buttons(self):
"""
Test that we allow controls on recent button press, but not as button leaves sliding window
"""
self._rx(self._button_msg(Buttons.SET))
for i in range(2 * PREV_BUTTON_SAMPLES):
self._rx(self._pcm_status_msg(False))
self.assertFalse(self.safety.get_controls_allowed())
self._rx(self._pcm_status_msg(True))
controls_allowed = i < PREV_BUTTON_SAMPLES
self.assertEqual(controls_allowed, self.safety.get_controls_allowed())
self._rx(self._button_msg(Buttons.NONE))


class TestHyundaiSafety(HyundaiButtonBase, common.PandaSafetyTest, common.DriverTorqueSteeringSafetyTest):
Expand Down
2 changes: 1 addition & 1 deletion tests/safety/test_hyundai_canfd.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from panda.tests.safety import libpandasafety_py
import panda.tests.safety.common as common
from panda.tests.safety.common import CANPackerPanda
from panda.tests.safety.test_hyundai import HyundaiButtonBase
from panda.tests.safety.hyundai_common import HyundaiButtonBase

class TestHyundaiCanfdBase(HyundaiButtonBase, common.PandaSafetyTest, common.DriverTorqueSteeringSafetyTest):

Expand Down