IQ.Pilot Release Commit @ 24b1ade

This commit is contained in:
IQ.Lvbs CI [bot]
2026-08-31 20:34:00 -05:00
parent 67a2b4955e
commit 9312ded8e2
37 changed files with 141 additions and 141 deletions

View File

@@ -429,55 +429,38 @@ class CarController(CarControllerBase, AolCarController, GasInterceptorCarContro
self.gas = pcm_accel / self.params.NIDEC_GAS_MAX
# Render OP's lane and lead cars on the dash. On CAN FD these are radar look-alikes that only
# exist (and are only allowed by panda safety) when the radar is disabled; in stock ACC the real
# radar still owns LANE_PATH/HUD_OBJECTS
if ((self.frame % 2 == 0 and self.CP.carFingerprint in HONDA_BOSCH_RADARLESS) or
(CS.radar_50hz_tick and self.CP.carFingerprint in HONDA_BOSCH_CANFD and self.CP.openpilotLongitudinalControl
and not CS.stock_acc_alive)):
# exist (and are only allowed by panda safety) when the radar is disabled. Radarless keeps the
# camera as the dash authority (known-good), so OP does not author these there
if (CS.radar_50hz_tick and self.CP.carFingerprint in HONDA_BOSCH_CANFD and self.CP.openpilotLongitudinalControl
and not CS.stock_acc_alive):
leads = dash_objects.leads_from_model(self.model, CS.out.vEgo)
lead = leads[0]
lead_d = lead.dRel if lead.status else 0.0
self.rendered_lane = self.lane_renderer.update(self.model, CS.out.vEgo, lead_d)
# the dash freezes the lane display if LANE_PATH and HUD_OBJECTS muxes don't match
if self.CP.carFingerprint in HONDA_BOSCH_CANFD:
mux = self.radar_mux
# no LKAS_HUD_2 on CAN FD: the dash reads the lane length from the in-band terminator, so the
# path is reshaped into the terminated-prefix form
lane_offsets = dash_lane.canfd_lane_offsets(self.rendered_lane)
else:
mux = dash_lane.MUX_CYCLE[(self.frame // 2) % len(dash_lane.MUX_CYCLE)]
lane_offsets = self.rendered_lane.offsets
mux = self.radar_mux
# no LKAS_HUD_2 on CAN FD: the dash reads the lane length from the in-band terminator, so the
# path is reshaped into the terminated-prefix form
lane_offsets = dash_lane.canfd_lane_offsets(self.rendered_lane)
lane_msg = dash_lane.create_lane_path(self.packer, self.CAN.lkas, lane_offsets, mux)
can_sends.append(lane_msg)
# CAN FD cars have no camera HUD_OBJECTS to poll (the disabled radar owned it): author OP's
# lead in slot 0 with the other slots blank (tracks=None)
tracks = CS.camera_object_tracker.snapshot() if CS.camera_object_tracker is not None else None
if self.CP.openpilotLongitudinalControl:
hud_msg = self.dash_object_author.create(self.packer, self.CAN.lkas, lead, tracks, mux, now_nanos * 1e-9,
extra_leads=leads[1:])
else:
# for stock ACC, forward the camera's objects but with our mux
hud_msg = dash_objects.forward_hud_object(self.packer, self.CAN.lkas, mux, tracks)
hud_msg = self.dash_object_author.create(self.packer, self.CAN.lkas, lead, tracks, mux, now_nanos * 1e-9,
extra_leads=leads[1:])
can_sends.append(hud_msg)
# on CAN FD the camera (behind the relay) also consumes these; mirror the identical packed
# bytes onto the camera bus (packed once, so the counter/checksum stay in lockstep)
if self.CP.carFingerprint in HONDA_BOSCH_CANFD:
for addr, dat, _ in (lane_msg, hud_msg):
can_sends.append((addr, dat, self.CAN.camera))
# the camera (behind the relay) also consumes these; mirror the identical packed bytes onto the
# camera bus (packed once, so the counter/checksum stay in lockstep)
for addr, dat, _ in (lane_msg, hud_msg):
can_sends.append((addr, dat, self.CAN.camera))
if self.frame % 20 == 0 and self.CP.carFingerprint in HONDA_BOSCH_RADARLESS:
# COUNTER_2 trails the packer's COUNTER (frame//20 % 4) by one
rl = self.rendered_lane
can_sends.append(dash_lane.create_lkas_hud_2(self.packer, self.CAN.lkas, (self.frame // 20 - 1) % 4,
rl.reach, rl.lane_cross, rl.left_line, rl.right_line))
# Radarless + CAN FD: when stock LKAS is active, the touch-steering-wheel nag eventually forces an
# ACC disengagement (on CAN FD it shows up as a brake tap from the VSA). Disable LKAS automatically
# and block the driver's LKAS button by taking over SCM_BUTTONS on the camera bus while engaged
# (panda blocks the forwarded stock SCM_BUTTONS while this stream flows)
if self.CP.carFingerprint in (HONDA_BOSCH_RADARLESS | HONDA_BOSCH_CANFD) and CC.enabled and self.frame % 4 == 0 and \
# CAN FD: when stock LKAS is active, the touch-steering-wheel nag eventually forces an ACC
# disengagement (a brake tap from the VSA). Disable LKAS automatically and block the driver's LKAS
# button by taking over SCM_BUTTONS on the camera bus while engaged (panda blocks the forwarded
# stock SCM_BUTTONS while this stream flows). Radarless keeps the stock camera LKAS untouched
if self.CP.carFingerprint in HONDA_BOSCH_CANFD and CC.enabled and self.frame % 4 == 0 and \
not pcm_cancel_cmd and not CC.cruiseControl.resume:
if self.lkas_button_send_remaining == 0 and CS.lkas_hud["LKAS_READY"] and self.frame >= self.last_lkas_button_frame + 500:
self.lkas_button_send_remaining = 3

View File

@@ -197,18 +197,19 @@ def create_lkas_hud(packer, bus, CP, hud_control, lat_active, steering_available
if CP.carFingerprint in (HONDA_BOSCH_RADARLESS | HONDA_BOSCH_CANFD):
lkas_hud_values['LANE_LINES'] = 3
lkas_hud_values['LKAS_PROBLEM'] = steer_fault_permanent
lkas_hud_values['DASHED_LANES'] = lat_active
# car likely needs to see LKAS_PROBLEM fall within a specific time frame, so forward from camera
if CP.carFingerprint in HONDA_BOSCH_RADARLESS:
# gray lanes when disengaged
lkas_hud_values['DASHED_LANES'] = 1
else:
lkas_hud_values['LKAS_PROBLEM'] = lkas_hud['LKAS_PROBLEM']
if CP.carFingerprint in HONDA_BOSCH_CANFD:
lkas_hud_values['LKAS_PROBLEM'] = steer_fault_permanent
# CAN FD: dashed lanes are the AOL armed indication (dashed_lanes is aol.enabled and not
# latActive, which is not standstill-gated - so parked LKAS button presses produce cluster
# feedback). ORed with lat_active so the engaged payload keeps SOLID and DASHED set together,
# byte-matching the stock camera's lanes-on state
lkas_hud_values['DASHED_LANES'] = dashed_lanes or lat_active
if CP.carFingerprint in HONDA_BOSCH_CANFD:
# every payload change must coincide with an LKAS_STATE_CHANGE pulse (see carcontroller); keyed
# on lat_active, not lanesVisible, so the dash LKAS indication follows AOL's lateral state
lkas_hud_values['SOLID_LANES'] = lat_active

View File

@@ -323,9 +323,9 @@ static bool honda_tx_hook(const CANPacket_t *msg) {
// FORCE CANCEL: safety check only relevant when spamming the cancel button in Bosch HW
// ensuring that only the cancel button press is sent (VAL 2) when controls are off.
// This avoids unintended engagements while still allowing resume spam
// On CAN FD and radarless, buttons are also sent to the camera (bus 2) to take over SCM_BUTTONS
// while engaged, so the same check applies there
const bool is_buttons_bus = (msg->bus == bus_buttons) || ((honda_bosch_canfd || honda_bosch_radarless) && (msg->bus == 2U));
// On CAN FD, buttons are also sent to the camera (bus 2) to take over SCM_BUTTONS while engaged, so
// the same check applies there (radarless already sends buttons on bus 2 via bus_buttons)
const bool is_buttons_bus = (msg->bus == bus_buttons) || (honda_bosch_canfd && (msg->bus == 2U));
if ((msg->addr == 0x296U) && !controls_allowed && is_buttons_bus) {
if (((msg->data[0] >> 5) & 0x7U) != 2U) {
tx = false;
@@ -454,13 +454,10 @@ static safety_config honda_bosch_init(uint16_t param) {
{0x33DA, 1, 5, .check_relay = true}, {0x33DB, 1, 8, .check_relay = true}, {0x39F, 1, 8, .check_relay = false},
{0x18DAB0F1, 1, 8, .check_relay = false}}; // Bosch w/ gas and brakes
static CanMsg HONDA_RADARLESS_TX_MSGS[] = {{0xE4, 0, 5, .check_relay = true}, {0x296, 2, 4, .check_relay = false}, {0x33D, 0, 8, .check_relay = true},
{0x6CD5554, 0, 8, .check_relay = true}, {0xF31AA54, 0, 8, .check_relay = true},
{0x6CD5557, 0, 8, .check_relay = true}}; // Bosch radarless (LANE_PATH/LKAS_HUD_2/HUD_OBJECTS authored in stock ACC too)
static CanMsg HONDA_RADARLESS_TX_MSGS[] = {{0xE4, 0, 5, .check_relay = true}, {0x296, 2, 4, .check_relay = false}, {0x33D, 0, 8, .check_relay = true}}; // Bosch radarless
static CanMsg HONDA_RADARLESS_LONG_TX_MSGS[] = {{0xE4, 0, 5, .check_relay = true}, {0x33D, 0, 8, .check_relay = true}, {0x1C8, 0, 8, .check_relay = true},
{0x30C, 0, 8, .check_relay = true}, {0x296, 2, 4, .check_relay = false}, {0x6CD5554, 0, 8, .check_relay = true},
{0xF31AA54, 0, 8, .check_relay = true}, {0x6CD5557, 0, 8, .check_relay = true}}; // Bosch radarless w/ gas and brakes
{0x30C, 0, 8, .check_relay = true}}; // Bosch radarless w/ gas and brakes
// 0x296 on bus 2: OP takes over SCM_BUTTONS towards the camera to auto-disable stock LKAS and to block
// the driver's LKAS button while engaged (the physical SCM_BUTTONS is blocked from forwarding, see fwd hook)
@@ -585,7 +582,7 @@ static bool honda_bosch_fwd_hook(int bus_num, int addr) {
// actually flowing (honda_op_buttons_fresh): the camera needs SCM_BUTTONS content beyond the buttons
// (it raises an adaptive high beam error when the message goes missing), so a bare controls_allowed
// gate would starve it whenever the panda allows controls but OP refuses to engage
if ((honda_bosch_radarless || honda_bosch_canfd) && controls_allowed && (honda_op_buttons_fresh > 0) &&
if (honda_bosch_canfd && controls_allowed && (honda_op_buttons_fresh > 0) &&
(bus_num == 0) && (addr == 0x296)) {
block_msg = true;
}

View File

@@ -39,6 +39,7 @@
#define TOYOTA_COMMON_RX_CHECKS(lta) \
{.msg = {{ 0xaa, 0, 8, 83U, .ignore_checksum = true, .ignore_counter = true, .ignore_quality_flag = true}, { 0 }, { 0 }}}, \
{.msg = {{0x260, 0, 8, 50U, .ignore_counter = true, .ignore_quality_flag=!(lta)}, { 0 }, { 0 }}}, \
{.msg = {{0x412, 2, 8, .ignore_checksum = true, .ignore_counter = true, .ignore_quality_flag = true, .frequency = 1U}, { 0 }, { 0 }}}, \
#define TOYOTA_RX_CHECKS(lta) \
TOYOTA_COMMON_RX_CHECKS(lta) \
@@ -98,6 +99,15 @@ static int TOYOTA_GET_INTERCEPTOR(const CANPacket_t *msg) {
}
static void toyota_rx_hook(const CANPacket_t *msg) {
if (msg->bus == 2U) {
// TSS2 LKAS/LDA button is only observable via the camera's LKAS_HUD; it reads 1 or 2 while
// pressed and rests at 0, and without this edge AOL can never be granted lateral on Toyota.
if (msg->addr == 0x412U) {
unsigned int lda_on_message = (msg->data[3] >> 6U) & 0x3U;
aol_button_press = (lda_on_message != 0U) ? AOL_BUTTON_PRESSED : AOL_BUTTON_NOT_PRESSED;
}
}
if (msg->bus == 0U) {
// get eps motor torque (0.66 factor in dbc)

View File

@@ -609,41 +609,14 @@ class TestHondaBoschRadarlessSafetyBase(TestHondaBoschSafetyBase):
STEER_BUS = 0
BUTTONS_BUS = 2 # camera controls ACC, need to send buttons on bus 2
TX_MSGS = [[0xE4, 0], [0x296, 2], [0x33D, 0], [0x6CD5554, 0], [0xF31AA54, 0], [0x6CD5557, 0]]
FWD_BLACKLISTED_ADDRS = {2: [0xE4, 0x33D, 0x6CD5554, 0xF31AA54, 0x6CD5557]}
# STEERING_CONTROL, LANE_PATH, LKAS_HUD_2, HUD_OBJECTS
RELAY_MALFUNCTION_ADDRS = {0: (0xE4, 0x33D, 0x6CD5554, 0xF31AA54, 0x6CD5557)}
TX_MSGS = [[0xE4, 0], [0x296, 2], [0x33D, 0]]
FWD_BLACKLISTED_ADDRS = {2: [0xE4, 0x33D]}
RELAY_MALFUNCTION_ADDRS = {0: (0xE4, 0x33D)} # STEERING_CONTROL
def setUp(self):
self.packer = CANPackerSafety("honda_bosch_radarless_generated")
self.safety = libsafety_py.libsafety
def test_buttons_fwd(self):
# SCM_BUTTONS (0x296) forwards to the camera unless OP's replacement button stream is flowing
# (engaged + a recent OP SCM_BUTTONS tx on the camera bus). The camera needs the message content
# beyond the buttons, so the block fails safe back to forwarding when OP stops sending
self.safety.set_controls_allowed(False)
self.assertEqual(2, self.safety.safety_fwd_hook(0, 0x296))
# engaged but OP not sending buttons: keep forwarding
self.safety.set_controls_allowed(True)
self.assertEqual(2, self.safety.safety_fwd_hook(0, 0x296))
# OP button stream flowing: block the stock buttons
self.assertTrue(self._tx(self._button_msg(Btn.NONE, bus=2)))
self.assertEqual(-1, self.safety.safety_fwd_hook(0, 0x296))
# never blocked while disengaged
self.safety.set_controls_allowed(False)
self.assertEqual(2, self.safety.safety_fwd_hook(0, 0x296))
self.safety.set_controls_allowed(True)
self.assertEqual(-1, self.safety.safety_fwd_hook(0, 0x296))
# freshness decays after 10 stock button frames without an OP tx
for _ in range(10):
self._rx(self._button_msg(Btn.NONE, main_on=True))
self.assertEqual(2, self.safety.safety_fwd_hook(0, 0x296))
class TestHondaBoschRadarlessSafety(HondaPcmEnableBase, TestHondaBoschRadarlessSafetyBase):
"""
@@ -672,9 +645,9 @@ class TestHondaBoschRadarlessLongSafety(common.LongitudinalAccelSafetyTest, Hond
"""
Covers the Honda Bosch Radarless safety mode with longitudinal control
"""
TX_MSGS = [[0xE4, 0], [0x33D, 0], [0x1C8, 0], [0x30C, 0], [0x296, 2], [0x6CD5554, 0], [0xF31AA54, 0], [0x6CD5557, 0]]
FWD_BLACKLISTED_ADDRS = {2: [0xE4, 0x33D, 0x1C8, 0x30C, 0x6CD5554, 0xF31AA54, 0x6CD5557]}
RELAY_MALFUNCTION_ADDRS = {0: (0xE4, 0x1C8, 0x30C, 0x33D, 0x6CD5554, 0xF31AA54, 0x6CD5557)}
TX_MSGS = [[0xE4, 0], [0x33D, 0], [0x1C8, 0], [0x30C, 0]]
FWD_BLACKLISTED_ADDRS = {2: [0xE4, 0x33D, 0x1C8, 0x30C]}
RELAY_MALFUNCTION_ADDRS = {0: (0xE4, 0x1C8, 0x30C, 0x33D)}
def setUp(self):
super().setUp()

View File

@@ -91,6 +91,35 @@ class TestToyotaSafetyBase(common.CarSafetyTest, common.LongitudinalAccelSafetyT
values = {"MAIN_ON": enabled}
return self.packer.make_can_msg_safety(msg, 0, values)
def _lkas_button_msg(self, lkas_button=False, lda_value=None):
values = {"LDA_ON_MESSAGE": (1 if lkas_button else 0) if lda_value is None else lda_value}
return self.packer.make_can_msg_safety("LKAS_HUD", 2, values)
def test_enable_control_allowed_with_aol_button(self):
for enable_aol in (True, False):
with self.subTest("enable_aol", aol_enabled=enable_aol):
self.safety.set_aol_params(enable_aol, False, False)
self._rx(self._lkas_button_msg(False))
self.assertEqual(0, self.safety.get_aol_button_press())
self.assertFalse(self.safety.get_controls_allowed_lat())
self._rx(self._lkas_button_msg(True))
self.assertEqual(1, self.safety.get_aol_button_press())
self.assertEqual(enable_aol, self.safety.get_controls_allowed_lat())
self._rx(self._lkas_button_msg(False))
self.assertEqual(0, self.safety.get_aol_button_press())
self.assertEqual(enable_aol, self.safety.get_controls_allowed_lat())
self.safety.set_controls_allowed_lat(False)
self._rx(self._lkas_button_msg(False, lda_value=2))
self.assertEqual(1, self.safety.get_aol_button_press())
self.assertEqual(enable_aol, self.safety.get_controls_allowed_lat())
self._rx(self._lkas_button_msg(False))
self.safety.set_controls_allowed_lat(False)
self.safety.set_aol_params(False, False, False)
def test_diagnostics(self, stock_longitudinal: bool = False, ecu_disabled: bool = True):
for should_tx, msg in ((False, b"\x6D\x02\x3E\x00\x00\x00\x00\x00"), # fwdCamera tester present
(False, b"\x0F\x03\xAA\xAA\x00\x00\x00\x00"), # non-tester present