This commit is contained in:
firestar5683
2026-05-05 12:41:05 -05:00
parent 4747792c37
commit 2f74f93308
7 changed files with 341 additions and 5 deletions
@@ -55,6 +55,8 @@ IONIQ_6_STOP_RELEASE_JERK_V = [3.6 * IONIQ_6_RESPONSE_MULTIPLIER,
4.8 * IONIQ_6_RESPONSE_MULTIPLIER]
IONIQ_6_IPEDAL_PRESS_SEND_COUNT = 6
IONIQ_6_IPEDAL_LATCH_PRESS_SEND_COUNT = 10
IONIQ_6_MAX_REGEN_STATE = 0x3C
IONIQ_6_MAX_REGEN_STATE_2 = 0x01
IONIQ_6_IPEDAL_REGEN_STATE = 0x50
IONIQ_6_IPEDAL_REGEN_STATE_2_PENDING = 0x01
IONIQ_6_IPEDAL_PROGRESS_RETRY_WAIT_FRAMES = 10
@@ -248,6 +250,7 @@ class CarController(CarControllerBase):
self._ioniq_6_last_ipedal_regen_state = 0
self._ioniq_6_last_ipedal_regen_state_2 = 0
self._ioniq_6_last_buttons_counter = 0
self._ioniq_6_last_regen_control_counter = -1
self._ioniq_6_last_gear = structs.CarState.GearShifter.unknown
self._genesis_g90_long_tuning = GenesisG90LongitudinalTuningState()
@@ -270,6 +273,7 @@ class CarController(CarControllerBase):
self._ioniq_6_last_ipedal_regen_state = int(getattr(CS, "ipedal_regen_state", 0))
self._ioniq_6_last_ipedal_regen_state_2 = int(getattr(CS, "ipedal_regen_state_2", 0))
self._ioniq_6_last_buttons_counter = int(getattr(CS, "buttons_counter", 0))
self._ioniq_6_last_regen_control_counter = int(getattr(CS, "ioniq_6_regen_control_msg", {}).get("COUNTER", -1))
self._ioniq_6_last_gear = CS.out.gearShifter
return can_sends
@@ -277,8 +281,13 @@ class CarController(CarControllerBase):
regen_state = int(getattr(CS, "ipedal_regen_state", 0))
regen_state_2 = int(getattr(CS, "ipedal_regen_state_2", 0))
buttons_counter = int(getattr(CS, "buttons_counter", 0))
regen_control_msg = getattr(CS, "ioniq_6_regen_control_msg", {})
regen_control_counter = int(regen_control_msg.get("COUNTER", -1))
has_regen_control_msg = bool(regen_control_msg) and getattr(CS, "ioniq_6_regen_control_ts", 0) > 0
regen_state_changed = regen_state != self._ioniq_6_last_ipedal_regen_state or regen_state_2 != self._ioniq_6_last_ipedal_regen_state_2
buttons_counter_changed = buttons_counter != self._ioniq_6_last_buttons_counter
regen_control_counter_changed = regen_control_counter != self._ioniq_6_last_regen_control_counter
max_regen_state = regen_state == IONIQ_6_MAX_REGEN_STATE and regen_state_2 == IONIQ_6_MAX_REGEN_STATE_2
ipedal_latch_pending = regen_state == IONIQ_6_IPEDAL_REGEN_STATE and regen_state_2 == IONIQ_6_IPEDAL_REGEN_STATE_2_PENDING
drive = gear == structs.CarState.GearShifter.drive
park = gear == structs.CarState.GearShifter.park
@@ -302,7 +311,7 @@ class CarController(CarControllerBase):
if target_gear and self._ioniq_6_always_ipedal_pending and not CS.ipedal_active and not CC.enabled:
if self._ioniq_6_always_ipedal_press_remaining == 0 and self.frame >= self._ioniq_6_always_ipedal_retry_frame:
self._ioniq_6_always_ipedal_press_remaining = IONIQ_6_IPEDAL_LATCH_PRESS_SEND_COUNT if ipedal_latch_pending \
self._ioniq_6_always_ipedal_press_remaining = IONIQ_6_IPEDAL_LATCH_PRESS_SEND_COUNT if (max_regen_state or ipedal_latch_pending) \
else IONIQ_6_IPEDAL_PRESS_SEND_COUNT
# Mirror the current stock counter after seeing the real CRUISE_BUTTONS frame land on the bus.
@@ -317,9 +326,16 @@ class CarController(CarControllerBase):
retry_wait_frames = IONIQ_6_IPEDAL_PROGRESS_RETRY_WAIT_FRAMES if regen_state_changed else IONIQ_6_IPEDAL_RETRY_WAIT_FRAMES
self._ioniq_6_always_ipedal_retry_frame = self.frame + retry_wait_frames
# The drivetrain latch uses a second HKG CAN-FD request path in addition to the left paddle bit.
# Once the cluster-facing regen state reaches max regen, mirror the live stock frame and only
# flip the request bytes that the physical paddle toggles for the final i-Pedal latch step.
if max_regen_state and has_regen_control_msg and regen_control_counter_changed:
can_sends.append(hyundaicanfd.create_ioniq_6_regen_control(self.packer, self.CP, self.CAN, regen_control_msg))
self._ioniq_6_last_ipedal_regen_state = regen_state
self._ioniq_6_last_ipedal_regen_state_2 = regen_state_2
self._ioniq_6_last_buttons_counter = buttons_counter
self._ioniq_6_last_regen_control_counter = regen_control_counter
self._ioniq_6_last_gear = gear
return can_sends
@@ -24,6 +24,8 @@ BUTTONS_DICT = {Buttons.RES_ACCEL: ButtonType.accelCruise, Buttons.SET_DECEL: Bu
IONIQ_6_BLINDSPOT_RIGHT_MASK = 0x08
IONIQ_6_BLINDSPOT_LEFT_MASK = 0x10
IONIQ_6_MAX_REGEN_STATE = 0x3C
IONIQ_6_MAX_REGEN_STATE_2 = 0x01
IONIQ_6_IPEDAL_INTERMEDIATE_REGEN_STATE = 0x50
IONIQ_6_IPEDAL_INTERMEDIATE_REGEN_STATE_2 = 0x01
IONIQ_6_IPEDAL_REGEN_STATE = 0x50
@@ -51,6 +53,10 @@ def decode_ioniq_6_ipedal_state(regen_state: int, regen_state_2: int) -> bool:
return int(regen_state) == IONIQ_6_IPEDAL_REGEN_STATE and int(regen_state_2) == IONIQ_6_IPEDAL_REGEN_STATE_2
def decode_ioniq_6_max_regen_state(regen_state: int, regen_state_2: int) -> bool:
return int(regen_state) == IONIQ_6_MAX_REGEN_STATE and int(regen_state_2) == IONIQ_6_MAX_REGEN_STATE_2
def decode_ioniq_6_ipedal_intermediate_state(regen_state: int, regen_state_2: int) -> bool:
return int(regen_state) == IONIQ_6_IPEDAL_INTERMEDIATE_REGEN_STATE and int(regen_state_2) == IONIQ_6_IPEDAL_INTERMEDIATE_REGEN_STATE_2
@@ -77,6 +83,8 @@ class CarState(CarStateBase):
self.ipedal_active = False
self.ipedal_regen_state = 0
self.ipedal_regen_state_2 = 0
self.ioniq_6_regen_control_msg = {}
self.ioniq_6_regen_control_ts = 0
self.gear_msg_canfd = "ACCELERATOR" if CP.flags & HyundaiFlags.EV else \
"GEAR_ALT" if CP.flags & HyundaiFlags.CANFD_ALT_GEARS else \
@@ -408,6 +416,9 @@ class CarState(CarStateBase):
self.ipedal_regen_state = int(msla.get("EV_REGEN_STATE", 0))
self.ipedal_regen_state_2 = int(msla.get("EV_REGEN_STATE_2", 0))
self.ipedal_active = decode_ioniq_6_ipedal_state(self.ipedal_regen_state, self.ipedal_regen_state_2)
if cp.ts_nanos["IONIQ_6_REGEN_CONTROL"]["CHECKSUM"] > 0:
self.ioniq_6_regen_control_msg = copy.copy(cp.vl["IONIQ_6_REGEN_CONTROL"])
self.ioniq_6_regen_control_ts = cp.ts_nanos["IONIQ_6_REGEN_CONTROL"]["CHECKSUM"]
prev_cruise_buttons = self.cruise_buttons[-1]
prev_main_buttons = self.main_buttons[-1]
@@ -484,6 +495,8 @@ class CarState(CarStateBase):
if CP.flags & HyundaiFlags.EV:
msgs.append(("DRIVE_MODE_EV", 0)) # optional: not all CAN-FD EV variants publish drive mode
msgs.append(("MANUAL_SPEED_LIMIT_ASSIST", 0)) # optional: used for non-adaptive cruise state and Ioniq 6 i-Pedal latch detection
if CP.carFingerprint == CAR.HYUNDAI_IONIQ_6:
msgs.append(("IONIQ_6_REGEN_CONTROL", 0))
msgs.append(("STEERING_WHEEL_MEDIA_BUTTONS", 0)) # optional: absent or slower on some CAN-FD variants
cam_msgs.append(("ADAS_0x380", 0)) # optional: dashboard stop-sign signal, only on ADAS-equipped HKG CANFD
return {
@@ -164,6 +164,8 @@ IONIQ_6_CRUISE_BUTTONS_BASE_CHECKSUMS = (
)
IONIQ_6_CRUISE_BUTTONS_LEFT_PADDLE_CHECKSUM_XOR = 0x77
IONIQ_6_CRUISE_BUTTONS_RIGHT_PADDLE_CHECKSUM_XOR = 0xD4
IONIQ_6_REGEN_CONTROL_REQ_BYTE24 = 0xC0
IONIQ_6_REGEN_CONTROL_REQ_BYTE27 = 0x00
def get_ioniq_6_cruise_buttons_next_counter(counter: int) -> int:
@@ -200,6 +202,21 @@ def create_ioniq_6_paddle_buttons(packer, CP, CAN, cnt, left_paddle=False, right
return address, bytes(dat), bus
def create_ioniq_6_regen_control(packer, CP, CAN, base_values,
req_byte24=IONIQ_6_REGEN_CONTROL_REQ_BYTE24,
req_byte27=IONIQ_6_REGEN_CONTROL_REQ_BYTE27):
values = {k: v for k, v in base_values.items() if k != "CHECKSUM"}
address = packer.dbc.name_to_msg["IONIQ_6_REGEN_CONTROL"].address
dat = bytearray(packer.pack(address, values))
dat[24] = req_byte24
dat[27] = req_byte27
checksum = hkg_can_fd_checksum(address, None, dat)
dat[0] = checksum & 0xFF
dat[1] = (checksum >> 8) & 0xFF
return address, bytes(dat), CAN.ECAN
def create_buttons(packer, CP, CAN, cnt, btn=0, base_values=None, left_paddle=False, right_paddle=False):
values = {k: v for k, v in base_values.items() if k not in ("_CHECKSUM", "COUNTER")} if base_values else {}
values.update({
@@ -10,7 +10,7 @@ from opendbc.car.fw_versions import build_fw_dict, match_fw_to_car
from opendbc.car.hyundai.carcontroller import CarController, Ioniq6LongitudinalTuningState, GenesisG90LongitudinalTuningState, \
update_ioniq_6_longitudinal_tuning, update_genesis_g90_longitudinal_tuning
from opendbc.car.hyundai.carstate import CarState, decode_ioniq_6_blindspot_radar_state, decode_ioniq_6_ipedal_intermediate_state, \
decode_ioniq_6_ipedal_state
decode_ioniq_6_ipedal_state, decode_ioniq_6_max_regen_state
from opendbc.car.hyundai.interface import CarInterface
from opendbc.car.hyundai import hyundaican, hyundaicanfd
from opendbc.car.hyundai.hyundaicanfd import CanBus
@@ -296,6 +296,7 @@ class TestHyundaiFingerprint:
assert any(be.type == ButtonType.altButton2 and not be.pressed for be in ret.buttonEvents)
def test_ioniq_6_ipedal_state_decode(self):
assert decode_ioniq_6_max_regen_state(0x3C, 0x01)
assert not decode_ioniq_6_ipedal_state(0x3C, 0x01)
assert not decode_ioniq_6_ipedal_state(0x50, 0x01)
assert decode_ioniq_6_ipedal_intermediate_state(0x50, 0x01)
@@ -342,6 +343,26 @@ class TestHyundaiFingerprint:
assert hyundaicanfd.get_ioniq_6_cruise_buttons_next_counter(13) == 14
assert hyundaicanfd.get_ioniq_6_cruise_buttons_next_counter(14) == 0
def test_ioniq_6_regen_control_message_preserves_stock_frame_and_flips_only_ipedal_request_bytes(self):
CP = CarParams.new_message()
CP.carFingerprint = CAR.HYUNDAI_IONIQ_6
CP.flags = int(HyundaiFlags.CANFD | HyundaiFlags.CANFD_LKA_STEERING)
packer = CANPacker(DBC[CP.carFingerprint][Bus.pt])
can_bus = CanBus(CP)
parser = CANParser(DBC[CP.carFingerprint][Bus.pt], [("IONIQ_6_REGEN_CONTROL", 0)], can_bus.ECAN)
stock_dat = bytes.fromhex("45421440801f000000000000a865170000a000000080c071a80c120e00000000")
parser.update([(1, [(0x25A, stock_dat, can_bus.ECAN)])])
msg = hyundaicanfd.create_ioniq_6_regen_control(packer, CP, can_bus, parser.vl["IONIQ_6_REGEN_CONTROL"])
assert msg[1][2:24] == stock_dat[2:24]
assert msg[1][24] == hyundaicanfd.IONIQ_6_REGEN_CONTROL_REQ_BYTE24
assert msg[1][25:27] == stock_dat[25:27]
assert msg[1][27] == hyundaicanfd.IONIQ_6_REGEN_CONTROL_REQ_BYTE27
assert msg[1][28:] == stock_dat[28:]
checksum = hyundaicanfd.hkg_can_fd_checksum(msg[0], None, bytearray(msg[1]))
assert msg[1][0] | (msg[1][1] << 8) == checksum
def test_ioniq_6_always_ipedal_spoofs_left_paddle_in_startup_park_and_drive_until_latched(self):
toggles = get_test_toggles()
toggles.always_ipedal = True
@@ -367,6 +388,8 @@ class TestHyundaiFingerprint:
"LEFT_PADDLE": 0,
"SET_ME_1": 1,
},
ioniq_6_regen_control_msg={},
ioniq_6_regen_control_ts=0,
)
cc = SimpleNamespace(enabled=False)
@@ -426,6 +449,8 @@ class TestHyundaiFingerprint:
"LEFT_PADDLE": 0,
"SET_ME_1": 1,
},
ioniq_6_regen_control_msg={},
ioniq_6_regen_control_ts=0,
)
cc = SimpleNamespace(enabled=False)
@@ -435,6 +460,76 @@ class TestHyundaiFingerprint:
assert sends
assert controller._ioniq_6_always_ipedal_press_remaining == 9
def test_ioniq_6_always_ipedal_sends_regen_control_companion_once_max_regen_is_shown(self):
toggles = get_test_toggles()
toggles.always_ipedal = True
CP = CarInterface.get_params(CAR.HYUNDAI_IONIQ_6, gen_empty_fingerprint(), [], True, False, False, toggles)
controller = CarController(DBC[CP.carFingerprint], CP)
cs = SimpleNamespace(
out=SimpleNamespace(gearShifter=structs.CarState.GearShifter.drive),
ipedal_active=False,
ipedal_regen_state=0x3C,
ipedal_regen_state_2=0x01,
buttons_counter=5,
cruise_buttons_msg={
"_CHECKSUM": 0,
"COUNTER": 5,
"CRUISE_BUTTONS": 0,
"ADAPTIVE_CRUISE_MAIN_BTN": 0,
"NORMAL_CRUISE_MAIN_BTN": 0,
"LDA_BTN": 0,
"RIGHT_PADDLE": 0,
"LEFT_PADDLE": 0,
"SET_ME_1": 1,
},
ioniq_6_regen_control_msg={
"COUNTER": 0x14,
"BYTE3": 0x40,
"BYTE4": 0x80,
"BYTE5": 0x1F,
"BYTE6": 0x00,
"BYTE7": 0x00,
"BYTE8": 0x00,
"BYTE9": 0x00,
"BYTE10": 0x00,
"BYTE11": 0x00,
"BYTE12": 0xA8,
"BYTE13": 0x65,
"BYTE14": 0x17,
"BYTE15": 0x00,
"BYTE16": 0x00,
"BYTE17": 0xA0,
"BYTE18": 0x00,
"BYTE19": 0x00,
"BYTE20": 0x00,
"BYTE21": 0x80,
"BYTE22": 0xC0,
"BYTE23": 0x71,
"BYTE24": 0xA8,
"BYTE25": 0x0C,
"BYTE26": 0x12,
"BYTE27": 0x0E,
"BYTE28": 0x00,
"BYTE29": 0x00,
"BYTE30": 0x00,
"BYTE31": 0x00,
},
ioniq_6_regen_control_ts=1,
)
cc = SimpleNamespace(enabled=False)
controller._ioniq_6_last_gear = structs.CarState.GearShifter.reverse
controller._ioniq_6_last_regen_control_counter = 0x13
sends = controller._update_ioniq_6_always_ipedal(cc, cs, toggles)
assert any(msg[0] == 0x1CF for msg in sends)
regen_cmd = next(msg for msg in sends if msg[0] == 0x25A)
assert regen_cmd[1][24] == hyundaicanfd.IONIQ_6_REGEN_CONTROL_REQ_BYTE24
assert regen_cmd[1][27] == hyundaicanfd.IONIQ_6_REGEN_CONTROL_REQ_BYTE27
checksum = hyundaicanfd.hkg_can_fd_checksum(regen_cmd[0], None, bytearray(regen_cmd[1]))
assert regen_cmd[1][0] | (regen_cmd[1][1] << 8) == checksum
def test_ioniq_6_longitudinal_params_match_canfd_tune(self):
toggles = get_test_toggles()
CP = CarInterface.get_params(CAR.HYUNDAI_IONIQ_6, gen_empty_fingerprint(), [], True, False, False, toggles)
@@ -774,6 +774,39 @@ BO_ 593 RADAR_0x251: 16 FRONT_RADAR
SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX
SG_ COUNTER : 16|8@1+ (1,0) [0|255] "" XXX
BO_ 602 IONIQ_6_REGEN_CONTROL: 32 XXX
SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX
SG_ COUNTER : 16|8@1+ (1,0) [0|255] "" XXX
SG_ BYTE3 : 24|8@1+ (1,0) [0|255] "" XXX
SG_ BYTE4 : 32|8@1+ (1,0) [0|255] "" XXX
SG_ BYTE5 : 40|8@1+ (1,0) [0|255] "" XXX
SG_ BYTE6 : 48|8@1+ (1,0) [0|255] "" XXX
SG_ BYTE7 : 56|8@1+ (1,0) [0|255] "" XXX
SG_ BYTE8 : 64|8@1+ (1,0) [0|255] "" XXX
SG_ BYTE9 : 72|8@1+ (1,0) [0|255] "" XXX
SG_ BYTE10 : 80|8@1+ (1,0) [0|255] "" XXX
SG_ BYTE11 : 88|8@1+ (1,0) [0|255] "" XXX
SG_ BYTE12 : 96|8@1+ (1,0) [0|255] "" XXX
SG_ BYTE13 : 104|8@1+ (1,0) [0|255] "" XXX
SG_ BYTE14 : 112|8@1+ (1,0) [0|255] "" XXX
SG_ BYTE15 : 120|8@1+ (1,0) [0|255] "" XXX
SG_ BYTE16 : 128|8@1+ (1,0) [0|255] "" XXX
SG_ BYTE17 : 136|8@1+ (1,0) [0|255] "" XXX
SG_ BYTE18 : 144|8@1+ (1,0) [0|255] "" XXX
SG_ BYTE19 : 152|8@1+ (1,0) [0|255] "" XXX
SG_ BYTE20 : 160|8@1+ (1,0) [0|255] "" XXX
SG_ BYTE21 : 168|8@1+ (1,0) [0|255] "" XXX
SG_ BYTE22 : 176|8@1+ (1,0) [0|255] "" XXX
SG_ BYTE23 : 184|8@1+ (1,0) [0|255] "" XXX
SG_ BYTE24 : 192|8@1+ (1,0) [0|255] "" XXX
SG_ BYTE25 : 200|8@1+ (1,0) [0|255] "" XXX
SG_ BYTE26 : 208|8@1+ (1,0) [0|255] "" XXX
SG_ BYTE27 : 216|8@1+ (1,0) [0|255] "" XXX
SG_ BYTE28 : 224|8@1+ (1,0) [0|255] "" XXX
SG_ BYTE29 : 232|8@1+ (1,0) [0|255] "" XXX
SG_ BYTE30 : 240|8@1+ (1,0) [0|255] "" XXX
SG_ BYTE31 : 248|8@1+ (1,0) [0|255] "" XXX
BO_ 687 HOD_FD_01_100ms: 8 XXX
SG_ HOD_Dir_Status : 18|3@0+ (1,0) [0|7] "" XXX
SG_ NEW_SIGNAL_2 : 32|8@1+ (1,0) [0|255] "" XXX
@@ -6,6 +6,9 @@
#define HYUNDAI_CANFD_CRUISE_BUTTON_TX_MSGS(bus) \
{0x1CF, bus, 8, .check_relay = false}, /* CRUISE_BUTTON */ \
#define HYUNDAI_CANFD_IPEDAL_REGEN_TX_MSGS(bus) \
{0x25A, bus, 32, .check_relay = false}, /* IONIQ_6_REGEN_CONTROL */ \
#define HYUNDAI_CANFD_LKA_STEERING_COMMON_TX_MSGS(a_can, e_can) \
HYUNDAI_CANFD_CRUISE_BUTTON_TX_MSGS(e_can) \
{0x50, a_can, 16, .check_relay = (a_can) == 0}, /* LKAS */ \
@@ -57,6 +60,8 @@ static bool hyundai_canfd_alt_buttons = false;
static bool hyundai_canfd_lka_steering_alt = false;
static bool hyundai_canfd_angle_steering = false;
static bool hyundai_canfd_allow_ipedal_paddle = false;
static bool hyundai_canfd_last_regen_control_seen = false;
static uint8_t hyundai_canfd_last_regen_control[32] = {0U};
static unsigned int hyundai_canfd_get_lka_addr(void) {
return hyundai_canfd_lka_steering_alt ? 0x110U : 0x50U;
@@ -77,6 +82,17 @@ static uint32_t hyundai_canfd_get_checksum(const CANPacket_t *msg) {
return chksum;
}
static void hyundai_canfd_rx_all_hook(const CANPacket_t *msg) {
const unsigned pt_bus = hyundai_canfd_lka_steering ? 1U : 0U;
if (hyundai_canfd_allow_ipedal_paddle && (msg->bus == pt_bus) && (msg->addr == 0x25AU) && (GET_LEN(msg) == 32U)) {
for (int i = 0; i < 32; i++) {
hyundai_canfd_last_regen_control[i] = msg->data[i];
}
hyundai_canfd_last_regen_control_seen = true;
}
}
static void hyundai_canfd_rx_hook(const CANPacket_t *msg) {
const unsigned pt_bus = hyundai_canfd_lka_steering ? 1U : 0U;
@@ -249,6 +265,31 @@ static bool hyundai_canfd_tx_hook(const CANPacket_t *msg) {
}
}
if (msg->addr == 0x25AU) {
const unsigned pt_bus = hyundai_canfd_lka_steering ? 1U : 0U;
bool allowed = hyundai_canfd_allow_ipedal_paddle &&
!controls_allowed &&
(msg->bus == pt_bus) &&
(GET_LEN(msg) == 32U) &&
hyundai_canfd_last_regen_control_seen &&
(hyundai_canfd_get_checksum(msg) == hyundai_common_canfd_compute_checksum(msg)) &&
(msg->data[24] == 0xC0U) &&
(msg->data[27] == 0x00U);
if (allowed) {
for (int i = 2; i < 32; i++) {
if ((i != 24) && (i != 27) && (msg->data[i] != hyundai_canfd_last_regen_control[i])) {
allowed = false;
break;
}
}
}
if (!allowed) {
tx = false;
}
}
// UDS: only tester present ("\x02\x3E\x80\x00\x00\x00\x00\x00") allowed on diagnostics address
if (((msg->addr == 0x730U) && hyundai_canfd_lka_steering) || ((msg->addr == 0x7D0U) && !hyundai_camera_scc)) {
if ((GET_BYTES(msg, 0, 4) != 0x00803E02U) || (GET_BYTES(msg, 4, 4) != 0x0U)) {
@@ -296,10 +337,20 @@ static safety_config hyundai_canfd_init(uint16_t param) {
HYUNDAI_CANFD_LKA_STEERING_COMMON_TX_MSGS(0, 1)
};
static const CanMsg HYUNDAI_CANFD_LKA_STEERING_IPEDAL_TX_MSGS[] = {
HYUNDAI_CANFD_LKA_STEERING_COMMON_TX_MSGS(0, 1)
HYUNDAI_CANFD_IPEDAL_REGEN_TX_MSGS(1)
};
static const CanMsg HYUNDAI_CANFD_LKA_STEERING_ALT_TX_MSGS[] = {
HYUNDAI_CANFD_LKA_STEERING_ALT_COMMON_TX_MSGS(0, 1)
};
static const CanMsg HYUNDAI_CANFD_LKA_STEERING_ALT_IPEDAL_TX_MSGS[] = {
HYUNDAI_CANFD_LKA_STEERING_ALT_COMMON_TX_MSGS(0, 1)
HYUNDAI_CANFD_IPEDAL_REGEN_TX_MSGS(1)
};
static const CanMsg HYUNDAI_CANFD_LKA_STEERING_LONG_TX_MSGS[] = {
HYUNDAI_CANFD_LKA_STEERING_COMMON_TX_MSGS(0, 1)
HYUNDAI_CANFD_LFA_STEERING_COMMON_TX_MSGS(1)
@@ -314,6 +365,21 @@ static safety_config hyundai_canfd_init(uint16_t param) {
{0x1DA, 1, 32, .check_relay = false}, // ADRV_0x1da
};
static const CanMsg HYUNDAI_CANFD_LKA_STEERING_LONG_IPEDAL_TX_MSGS[] = {
HYUNDAI_CANFD_LKA_STEERING_COMMON_TX_MSGS(0, 1)
HYUNDAI_CANFD_LFA_STEERING_COMMON_TX_MSGS(1)
HYUNDAI_CANFD_SCC_CONTROL_COMMON_TX_MSGS(1, true)
HYUNDAI_CANFD_BLINDSPOT_DASH_TX_MSGS(1)
HYUNDAI_CANFD_IPEDAL_REGEN_TX_MSGS(1)
{0x51, 0, 32, .check_relay = false}, // ADRV_0x51
{0x730, 1, 8, .check_relay = false}, // tester present for ADAS ECU disable
{0x160, 1, 16, .check_relay = false}, // ADRV_0x160
{0x1EA, 1, 32, .check_relay = false}, // ADRV_0x1ea
{0x200, 1, 8, .check_relay = false}, // ADRV_0x200
{0x345, 1, 8, .check_relay = false}, // ADRV_0x345
{0x1DA, 1, 32, .check_relay = false}, // ADRV_0x1da
};
static const CanMsg HYUNDAI_CANFD_LFA_STEERING_TX_MSGS[] = {
HYUNDAI_CANFD_CRUISE_BUTTON_TX_MSGS(2)
HYUNDAI_CANFD_LFA_STEERING_COMMON_TX_MSGS(0)
@@ -344,6 +410,7 @@ static safety_config hyundai_canfd_init(uint16_t param) {
hyundai_canfd_lka_steering_alt = GET_FLAG(param, HYUNDAI_PARAM_CANFD_LKA_STEERING_ALT);
hyundai_canfd_angle_steering = GET_FLAG(param, HYUNDAI_PARAM_CANFD_ANGLE_STEERING);
hyundai_canfd_allow_ipedal_paddle = GET_FLAG(param, HYUNDAI_PARAM_ALLOW_IPEDAL_PADDLE);
hyundai_canfd_last_regen_control_seen = false;
safety_config ret;
if (hyundai_longitudinal) {
@@ -352,7 +419,12 @@ static safety_config hyundai_canfd_init(uint16_t param) {
HYUNDAI_CANFD_STD_BUTTONS_RX_CHECKS(1)
};
ret = BUILD_SAFETY_CFG(hyundai_canfd_lka_steering_long_rx_checks, HYUNDAI_CANFD_LKA_STEERING_LONG_TX_MSGS);
SET_RX_CHECKS(hyundai_canfd_lka_steering_long_rx_checks, ret);
if (hyundai_canfd_allow_ipedal_paddle) {
SET_TX_MSGS(HYUNDAI_CANFD_LKA_STEERING_LONG_IPEDAL_TX_MSGS, ret);
} else {
SET_TX_MSGS(HYUNDAI_CANFD_LKA_STEERING_LONG_TX_MSGS, ret);
}
} else {
// Longitudinal checks for LFA steering
@@ -394,9 +466,17 @@ static safety_config hyundai_canfd_init(uint16_t param) {
SET_RX_CHECKS(hyundai_canfd_lka_steering_rx_checks, ret);
if (hyundai_canfd_lka_steering_alt) {
SET_TX_MSGS(HYUNDAI_CANFD_LKA_STEERING_ALT_TX_MSGS, ret);
if (hyundai_canfd_allow_ipedal_paddle) {
SET_TX_MSGS(HYUNDAI_CANFD_LKA_STEERING_ALT_IPEDAL_TX_MSGS, ret);
} else {
SET_TX_MSGS(HYUNDAI_CANFD_LKA_STEERING_ALT_TX_MSGS, ret);
}
} else {
SET_TX_MSGS(HYUNDAI_CANFD_LKA_STEERING_TX_MSGS, ret);
if (hyundai_canfd_allow_ipedal_paddle) {
SET_TX_MSGS(HYUNDAI_CANFD_LKA_STEERING_IPEDAL_TX_MSGS, ret);
} else {
SET_TX_MSGS(HYUNDAI_CANFD_LKA_STEERING_TX_MSGS, ret);
}
}
} else if (!hyundai_camera_scc) {
@@ -452,6 +532,7 @@ static safety_config hyundai_canfd_init(uint16_t param) {
const safety_hooks hyundai_canfd_hooks = {
.init = hyundai_canfd_init,
.rx_all = hyundai_canfd_rx_all_hook,
.rx = hyundai_canfd_rx_hook,
.tx = hyundai_canfd_tx_hook,
.get_counter = hyundai_canfd_get_counter,
@@ -440,6 +440,52 @@ class TestHyundaiCanfdLKASteeringEV(TestHyundaiCanfdBase):
}
return self.packer.make_can_msg_safety("CRUISE_BUTTONS", bus, values)
def _regen_control_msg(self, byte24=0xA8, byte27=0x0E, counter=0x14, bus=None):
if bus is None:
bus = self.PT_BUS
values = {
"COUNTER": counter,
"BYTE3": 0x40,
"BYTE4": 0x80,
"BYTE5": 0x1F,
"BYTE6": 0x00,
"BYTE7": 0x00,
"BYTE8": 0x00,
"BYTE9": 0x00,
"BYTE10": 0x00,
"BYTE11": 0x00,
"BYTE12": 0xA8,
"BYTE13": 0x65,
"BYTE14": 0x17,
"BYTE15": 0x00,
"BYTE16": 0x00,
"BYTE17": 0xA0,
"BYTE18": 0x00,
"BYTE19": 0x00,
"BYTE20": 0x00,
"BYTE21": 0x80,
"BYTE22": 0xC0,
"BYTE23": 0x71,
"BYTE24": byte24,
"BYTE25": 0x0C,
"BYTE26": 0x12,
"BYTE27": byte27,
"BYTE28": 0x00,
"BYTE29": 0x00,
"BYTE30": 0x00,
"BYTE31": 0x00,
}
def fix_checksum(msg):
addr, dat, msg_bus = msg
dat = bytearray(dat)
checksum = hyundaicanfd.hkg_can_fd_checksum(addr, None, dat)
dat[0] = checksum & 0xFF
dat[1] = (checksum >> 8) & 0xFF
return addr, bytes(dat), msg_bus
return self.packer.make_can_msg_safety("IONIQ_6_REGEN_CONTROL", bus, values, fix_checksum=fix_checksum)
def test_left_paddle_send(self):
for controls_allowed in (True, False):
self.safety.set_controls_allowed(controls_allowed)
@@ -448,6 +494,8 @@ class TestHyundaiCanfdLKASteeringEV(TestHyundaiCanfdBase):
class TestHyundaiCanfdLKASteeringEVAlwaysIPedal(TestHyundaiCanfdLKASteeringEV):
TX_MSGS = TestHyundaiCanfdLKASteeringEV.TX_MSGS + [[0x25A, 1]]
def setUp(self):
self.packer = CANPackerSafety("hyundai_canfd_generated")
self.safety = libsafety_py.libsafety
@@ -468,6 +516,19 @@ class TestHyundaiCanfdLKASteeringEVAlwaysIPedal(TestHyundaiCanfdLKASteeringEV):
self.safety.set_controls_allowed(True)
self.assertFalse(self._tx(self._paddle_msg(left_paddle=1)))
def test_regen_control_send(self):
stock_msg = self._regen_control_msg(byte24=0xA8, byte27=0x0E, counter=0x14)
self._rx(stock_msg)
self.safety.set_controls_allowed(False)
self.assertTrue(self._tx(self._regen_control_msg(byte24=0xC0, byte27=0x00, counter=0x14)))
self.assertFalse(self._tx(self._regen_control_msg(byte24=0xA8, byte27=0x00, counter=0x14)))
self.assertFalse(self._tx(self._regen_control_msg(byte24=0xC0, byte27=0x0E, counter=0x14)))
self.assertFalse(self._tx(self._regen_control_msg(byte24=0xC0, byte27=0x00, counter=0x15)))
self.safety.set_controls_allowed(True)
self.assertFalse(self._tx(self._regen_control_msg(byte24=0xC0, byte27=0x00, counter=0x14)))
# TODO: Handle ICE and HEV configurations once we see cars that use the new messages
class TestHyundaiCanfdLKASteeringAltEV(TestHyundaiCanfdBase):
@@ -519,6 +580,26 @@ class TestHyundaiCanfdLKASteeringLongEV(HyundaiLongitudinalBase, TestHyundaiCanf
return self.packer.make_can_msg_safety("SCC_CONTROL", 1, values)
class TestHyundaiCanfdLKASteeringLongEVAlwaysIPedal(TestHyundaiCanfdLKASteeringLongEV):
TX_MSGS = TestHyundaiCanfdLKASteeringLongEV.TX_MSGS + [[0x25A, 1]]
def setUp(self):
self.packer = CANPackerSafety("hyundai_canfd_generated")
self.safety = libsafety_py.libsafety
self.safety.set_safety_hooks(CarParams.SafetyModel.hyundaiCanfd, HyundaiSafetyFlags.CANFD_LKA_STEERING |
HyundaiSafetyFlags.LONG | HyundaiSafetyFlags.EV_GAS |
HyundaiStarPilotSafetyFlags.ALLOW_IPEDAL_PADDLE)
self.safety.init_tests()
def test_regen_control_send(self):
stock_msg = self._regen_control_msg(byte24=0xA8, byte27=0x0E, counter=0x14)
self._rx(stock_msg)
self.safety.set_controls_allowed(False)
self.assertTrue(self._tx(self._regen_control_msg(byte24=0xC0, byte27=0x00, counter=0x14)))
# Tests longitudinal for ICE, hybrid, EV cars with LFA steering
class TestHyundaiCanfdLFASteeringLongBase(HyundaiLongitudinalBase, TestHyundaiCanfdLFASteeringBase):