mirror of
https://github.com/firestar5683/StarPilot.git
synced 2026-10-29 09:33:48 +08:00
Restore Lexus IS DSU MAIN lateral ownership
This commit is contained in:
@@ -3,6 +3,7 @@ import math
|
||||
|
||||
from opendbc.can import CANDefine, CANParser
|
||||
from opendbc.car.toyota import pedal
|
||||
from opendbc.car.toyota.lexus_is import LexusIsCANParser
|
||||
from opendbc.car import Bus, DT_CTRL, create_button_events, structs
|
||||
from opendbc.car.common.conversions import Conversions as CV
|
||||
from opendbc.car.common.filter_simple import FirstOrderFilter
|
||||
@@ -153,6 +154,9 @@ class CarState(CarStateBase):
|
||||
if self.CP.flags & ToyotaFlags.UNSUPPORTED_DSU:
|
||||
# TODO: find the bit likely in DSU_CRUISE that describes an ACC fault. one may also exist in CLUTCH
|
||||
ret.cruiseState.available = cp.vl["DSU_CRUISE"]["MAIN_ON"] != 0
|
||||
if self.CP.carFingerprint == CAR.LEXUS_IS:
|
||||
stamp = cp.ts_nanos["DSU_CRUISE"]["MAIN_ON"]
|
||||
ret.cruiseState.available &= bool(cp.main_valid and stamp > 0 and 0 <= cp._last_update_nanos - stamp <= 303_031_000)
|
||||
ret.cruiseState.speed = cp.vl["DSU_CRUISE"]["SET_SPEED"] * CV.KPH_TO_MS
|
||||
cluster_set_speed = cp.vl["PCM_CRUISE_ALT"]["UI_SET_SPEED"]
|
||||
else:
|
||||
@@ -288,6 +292,8 @@ class CarState(CarStateBase):
|
||||
|
||||
if CP.carFingerprint == CAR.TOYOTA_SIENNA_4TH_GEN:
|
||||
pt_messages.append(("PCM_CRUISE_4", 1))
|
||||
if CP.carFingerprint == CAR.LEXUS_IS:
|
||||
pt_messages.append(("DSU_CRUISE", 10))
|
||||
|
||||
if CP.flags & ToyotaFlags.LONG_FILTER:
|
||||
pt_messages.append(("SDSU", 50))
|
||||
@@ -296,7 +302,8 @@ class CarState(CarStateBase):
|
||||
|
||||
if pedal.configured(CP):
|
||||
pt_messages.append(("GAS_SENSOR", 50))
|
||||
pt_parser = pedal.PedalCANParser if pedal.configured(CP) else Rav4hCANParser if rav4h_filter_configured(CP) else CANParser
|
||||
pt_parser = (LexusIsCANParser if CP.carFingerprint == CAR.LEXUS_IS else pedal.PedalCANParser if pedal.configured(CP) else
|
||||
Rav4hCANParser if rav4h_filter_configured(CP) else CANParser)
|
||||
return {
|
||||
Bus.pt: pt_parser(DBC[CP.carFingerprint][Bus.pt], pt_messages, 0),
|
||||
Bus.cam: CANParser(dbc_name, cam_messages, 2),
|
||||
|
||||
@@ -52,6 +52,8 @@ class CarInterface(CarInterfaceBase):
|
||||
ret.brand = "toyota"
|
||||
ret.safetyConfigs = [get_safety_config(structs.CarParams.SafetyModel.toyota)]
|
||||
ret.safetyConfigs[0].safetyParam = EPS_SCALE[candidate]
|
||||
if candidate == CAR.LEXUS_IS:
|
||||
ret.safetyConfigs[0].safetyParam |= int(ToyotaSafetyFlags.ALT_CRUISE)
|
||||
|
||||
# BRAKE_MODULE is on a different address for these cars
|
||||
if DBC[candidate][Bus.pt] == "toyota_new_mc_pt_generated":
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
"""Exact Lexus IS DSU MAIN source, paired with the native 303031 us lease."""
|
||||
from opendbc.can import CANParser
|
||||
|
||||
|
||||
class LexusIsCANParser(CANParser):
|
||||
def __init__(self, *args):
|
||||
super().__init__(*args)
|
||||
self.main_valid = False
|
||||
|
||||
def update(self, strings, sendcan=False):
|
||||
batches = []
|
||||
for stamp, frames in strings:
|
||||
accepted = []
|
||||
for address, data, bus in frames:
|
||||
if address == 0x365 and bus == self.bus:
|
||||
self.main_valid = len(data) == 7
|
||||
if not self.main_valid:
|
||||
continue
|
||||
accepted.append((address, data, bus))
|
||||
batches.append((stamp, accepted))
|
||||
return super().update(batches, sendcan)
|
||||
@@ -0,0 +1,62 @@
|
||||
import unittest
|
||||
from opendbc.car import Bus, structs, CanData
|
||||
from opendbc.can import CANPacker
|
||||
from opendbc.car.toyota.interface import CarInterface
|
||||
from opendbc.car.toyota.values import CAR, DBC
|
||||
from openpilot.starpilot.car.toyota.aol import qualified, policy_for
|
||||
|
||||
|
||||
class TestLexusIsFactory(unittest.TestCase):
|
||||
def test_exact_factory_stock_owner_and_sibling_boundary(self):
|
||||
for alpha in (False, True):
|
||||
cp = CarInterface.get_params(CAR.LEXUS_IS, {0: {}, 1: {}, 2: {}}, [], alpha, False, False)
|
||||
self.assertEqual(cp.safetyConfigs[0].safetyParam, 0x424D)
|
||||
self.assertFalse(cp.openpilotLongitudinalControl)
|
||||
self.assertTrue(cp.pcmCruise)
|
||||
self.assertTrue(qualified(cp))
|
||||
self.assertEqual(policy_for(cp).alternative_experience_addition, 32)
|
||||
cp.alternativeExperience = 32
|
||||
self.assertTrue(qualified(cp, marked_only=True))
|
||||
for mutation in ('carFingerprint', 'safetyParam', 'long', 'angle', 'passive'):
|
||||
bad = cp.as_reader().as_builder()
|
||||
if mutation == 'carFingerprint':
|
||||
bad.carFingerprint = CAR.LEXUS_RC
|
||||
elif mutation == 'safetyParam':
|
||||
bad.safetyConfigs[0].safetyParam = 0x404D
|
||||
elif mutation == 'long':
|
||||
bad.openpilotLongitudinalControl = True
|
||||
elif mutation == 'angle':
|
||||
bad.steerControlType = structs.CarParams.SteerControlType.angle
|
||||
else:
|
||||
bad.passive = True
|
||||
self.assertFalse(qualified(bad), mutation)
|
||||
rc = CarInterface.get_params(CAR.LEXUS_RC, {0: {}, 1: {}, 2: {}}, [], False, False, False)
|
||||
self.assertEqual(rc.safetyConfigs[0].safetyParam, 589)
|
||||
|
||||
def test_real_parser_dsu_main_and_pcm_cruise(self):
|
||||
cp = CarInterface.get_params(CAR.LEXUS_IS, {0: {}, 1: {}, 2: {}}, [], False, False, False)
|
||||
ci = CarInterface(cp)
|
||||
packer = CANPacker(DBC[cp.carFingerprint][Bus.pt])
|
||||
ci.update([]) # Register signals used by the real CarState before the first CAN sample.
|
||||
for tick in range(50):
|
||||
frames = [packer.make_can_msg('DSU_CRUISE', 0, {'MAIN_ON': tick < 25, 'SET_SPEED': 80}),
|
||||
packer.make_can_msg('PCM_CRUISE', 0, {'GAS_RELEASED': 1, 'CRUISE_ACTIVE': 1}),
|
||||
packer.make_can_msg('PCM_CRUISE_2', 0, {'MAIN_ON': tick >= 25})]
|
||||
state = ci.update([(1_000_000_000 + tick * 100_000_000, frames)])
|
||||
self.assertEqual(state.cruiseState.available, tick < 25)
|
||||
self.assertTrue(state.cruiseState.enabled)
|
||||
self.assertAlmostEqual(state.cruiseState.speed, 80/3.6, places=5)
|
||||
|
||||
def test_dsu_main_lease_and_malformed_frames_cannot_refresh_it(self):
|
||||
cp = CarInterface.get_params(CAR.LEXUS_IS, {0: {}, 1: {}, 2: {}}, [], False, False, False)
|
||||
ci = CarInterface(cp)
|
||||
packer = CANPacker(DBC[cp.carFingerprint][Bus.pt])
|
||||
ci.update([])
|
||||
frame = packer.make_can_msg('DSU_CRUISE', 0, {'MAIN_ON': 1})
|
||||
self.assertTrue(ci.update([(1_000_000_000, [frame])]).cruiseState.available)
|
||||
self.assertTrue(ci.update([(1_303_031_000, [])]).cruiseState.available)
|
||||
self.assertFalse(ci.update([(1_303_031_001, [])]).cruiseState.available)
|
||||
self.assertTrue(ci.update([(1_400_000_000, [frame])]).cruiseState.available)
|
||||
a, d, b = frame
|
||||
self.assertFalse(ci.update([(1_410_000_000, [CanData(a, d + bytes(1), b)])]).cruiseState.available)
|
||||
self.assertTrue(ci.update([(1_420_000_000, [frame])]).cruiseState.available)
|
||||
@@ -58,6 +58,7 @@ class ToyotaSafetyFlags(IntFlag):
|
||||
SECOC = (8 << 8)
|
||||
LONG_FILTER = (16 << 8)
|
||||
GAS_INTERCEPTOR = (32 << 8)
|
||||
ALT_CRUISE = (64 << 8) # Exact Lexus IS DSU MAIN profile
|
||||
RAV4H_FILTER_PROFILE = (128 << 8) # Exact smartDSU owner; bit14 remains reserved for ALT_CRUISE
|
||||
|
||||
|
||||
|
||||
@@ -164,8 +164,8 @@ static bool toyota_get_quality_flag_valid(const CANPacket_t *msg) {
|
||||
}
|
||||
|
||||
static void toyota_optional_rx_hook(const CANPacket_t *msg) {
|
||||
const bool aol_checksum_source = (msg->addr == 0x1D3U) || (msg->addr == 0x262U) || (toyota_aol_secoc && (msg->addr == 0x127U));
|
||||
const unsigned int aol_source_len = (toyota_aol_rav4h_filter && (msg->addr == 0x262U)) ? 5U : 8U;
|
||||
const bool aol_checksum_source = (!toyota_alt_cruise && (msg->addr == 0x1D3U)) || (msg->addr == 0x262U) || (toyota_aol_secoc && (msg->addr == 0x127U));
|
||||
const unsigned int aol_source_len = ((toyota_aol_rav4h_filter || toyota_alt_cruise) && (msg->addr == 0x262U)) ? 5U : 8U;
|
||||
if (!aol_checksum_source || ((GET_LEN(msg) == aol_source_len) && (toyota_get_checksum(msg) == toyota_compute_checksum(msg)))) {
|
||||
toyota_aol_observe(msg);
|
||||
} else if (toyota_aol_enabled && (msg->bus == 0U)) {
|
||||
@@ -201,6 +201,10 @@ static void toyota_optional_rx_hook(const CANPacket_t *msg) {
|
||||
}
|
||||
|
||||
static void toyota_rx_hook(const CANPacket_t *msg) {
|
||||
if (toyota_alt_cruise && msg_matches(msg, 0x365U, 0U) && (GET_LEN(msg) == 7U)) {
|
||||
acc_main_on = GET_BIT(msg, 0U);
|
||||
toyota_aol_observe(msg);
|
||||
}
|
||||
if (msg->bus == 0U) {
|
||||
if (toyota_pedal && (msg->addr == 0x201U) && (GET_LEN(msg) == 6U)) {
|
||||
const unsigned int gas = (((unsigned int)msg->data[0] << 8U) + msg->data[1] +
|
||||
@@ -628,17 +632,17 @@ static safety_config toyota_init(uint16_t param) {
|
||||
// The alternate-brake profile differs only in this source address. Reuse
|
||||
// the same workspace; set_safety_hooks resets all active mutable status.
|
||||
toyota_lka_rx_checks[3].msg[0].addr = toyota_alt_brake ? 0x224U : 0x226U;
|
||||
toyota_lka_rx_checks[4].msg[0] = (CanMsgCheck){.addr = toyota_pedal ? 0x201 : 0x2FF,
|
||||
.bus = 0U, .len = toyota_pedal ? 6 : 8, .frequency = 50U,
|
||||
toyota_lka_rx_checks[4].msg[0] = (CanMsgCheck){.addr = toyota_alt_cruise ? 0x365 : (toyota_pedal ? 0x201 : 0x2FF),
|
||||
.bus = 0U, .len = toyota_alt_cruise ? 7 : (toyota_pedal ? 6 : 8), .frequency = toyota_alt_cruise ? 10U : 50U,
|
||||
.ignore_checksum = !toyota_pedal, .ignore_counter = !toyota_pedal,
|
||||
.max_counter = toyota_pedal ? 15U : 0U, .ignore_quality_flag = false};
|
||||
.max_counter = toyota_pedal ? 15U : 0U, .ignore_quality_flag = toyota_alt_cruise};
|
||||
SET_RX_CHECKS(toyota_lka_rx_checks, ret);
|
||||
if (!toyota_pedal && (param != 0x9049U) && (param != 0x9249U)) {
|
||||
if (!toyota_pedal && !toyota_alt_cruise && (param != 0x9049U) && (param != 0x9249U)) {
|
||||
ret.rx_checks_len -= 1; // Ordinary profiles do not require smartDSU.
|
||||
}
|
||||
}
|
||||
|
||||
if (((param & 0xC000U) != 0U) && (param != 0x9049U) && (param != 0x9249U)) {
|
||||
if (((param & 0xC000U) != 0U) && !toyota_alt_cruise && (param != 0x9049U) && (param != 0x9249U)) {
|
||||
ret.tx_msgs_len = 0;
|
||||
}
|
||||
if (((param & 0x2000U) != 0U) && !toyota_pedal) {
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
static bool toyota_aol_enabled = false;
|
||||
static bool toyota_pedal = false;
|
||||
static bool toyota_alt_cruise = false;
|
||||
|
||||
static bool toyota_aol_secoc = false;
|
||||
static bool toyota_aol_stock = false;
|
||||
@@ -58,10 +59,17 @@ static uint8_t toyota_aol_permission(void) {
|
||||
|
||||
static void toyota_aol_observe(const CANPacket_t *msg) {
|
||||
const uint32_t gear_addr = toyota_aol_secoc ? 0x127U : 0x3BCU;
|
||||
const unsigned int expected_len = (toyota_aol_rav4h_filter && (msg->addr == 0x262U)) ? 5U : 8U;
|
||||
const uint32_t main_addr = toyota_alt_cruise ? 0x365U : 0x1D3U;
|
||||
unsigned int expected_len = 8U;
|
||||
if (toyota_alt_cruise && (msg->addr == main_addr)) {
|
||||
expected_len = 7U;
|
||||
} else if ((toyota_aol_rav4h_filter || toyota_alt_cruise) && (msg->addr == 0x262U)) {
|
||||
expected_len = 5U;
|
||||
} else {
|
||||
}
|
||||
if (toyota_aol_enabled && (msg->bus == 0U) && (GET_LEN(msg) != expected_len)) {
|
||||
unsigned int index = 4U;
|
||||
if (msg->addr == 0x1D3U) {
|
||||
if (msg->addr == main_addr) {
|
||||
index = 0U;
|
||||
} else if (msg->addr == gear_addr) {
|
||||
index = 1U;
|
||||
@@ -76,9 +84,9 @@ static void toyota_aol_observe(const CANPacket_t *msg) {
|
||||
}
|
||||
} else if (toyota_aol_enabled && (msg->bus == 0U) && (GET_LEN(msg) == expected_len)) {
|
||||
unsigned int index = 4U;
|
||||
if (msg->addr == 0x1D3U) {
|
||||
if (msg->addr == main_addr) {
|
||||
index = 0U;
|
||||
toyota_aol_main = GET_BIT(msg, 15U) && !GET_BIT(msg, 47U);
|
||||
toyota_aol_main = toyota_alt_cruise ? GET_BIT(msg, 0U) : (GET_BIT(msg, 15U) && !GET_BIT(msg, 47U));
|
||||
} else if (msg->addr == gear_addr) {
|
||||
index = 1U;
|
||||
if (toyota_aol_secoc) {
|
||||
@@ -109,9 +117,10 @@ static void toyota_aol_observe(const CANPacket_t *msg) {
|
||||
|
||||
static void toyota_aol_configure(uint16_t param) {
|
||||
toyota_aol_reset();
|
||||
toyota_alt_cruise = param == 0x424DU;
|
||||
toyota_pedal = (param == 0x2049U) || (param == 0x2249U);
|
||||
toyota_aol_rav4h_filter = (param == 0x9049U) || (param == 0x9249U);
|
||||
toyota_aol_stock = (param == 585U) || (param == 4681U) || (param == 0x9249U) || (param == 0x2249U);
|
||||
toyota_aol_stock = (param == 585U) || (param == 4681U) || (param == 0x9249U) || (param == 0x2249U) || toyota_alt_cruise;
|
||||
#ifdef ALLOW_DEBUG
|
||||
toyota_aol_secoc = (param == 2121U);
|
||||
#else
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
import unittest
|
||||
from opendbc.safety.tests import common
|
||||
from opendbc.safety.tests.libsafety import libsafety_py
|
||||
from opendbc.safety.tests import test_toyota as toyota_tests
|
||||
|
||||
|
||||
class TestLexusIsNative(unittest.TestCase):
|
||||
init = toyota_tests.TestToyotaHighlanderAol.init
|
||||
rx = toyota_tests.TestToyotaHighlanderAol.rx
|
||||
tearDown = toyota_tests.TestToyotaHighlanderAol.tearDown
|
||||
|
||||
def setUp(self):
|
||||
self.safety = libsafety_py.libsafety
|
||||
self.packer = common.CANPackerSafety('toyota_tnga_k_pt_generated')
|
||||
|
||||
def physical(self, *, main=True, cruise=True, brake=False, gas=False, gear=0, eps=1, door=False):
|
||||
self.rx(0xAA, bytes([0x1A, 0x6F] * 4))
|
||||
self.rx(0x260, bytes(8), True)
|
||||
self.rx(0x1D2, bytes([(0 if gas else 16) | (32 if cruise else 0)]) + bytes(7), True)
|
||||
self.rx(0x226, bytes(4) + bytes([32 if brake else 0]) + bytes(3))
|
||||
self.rx(0x365, bytes([1 if main else 0]) + bytes(6))
|
||||
self.rx(0x3BC, bytes([0, gear]) + bytes(6))
|
||||
self.rx(0x620, bytes(5) + bytes([4 if door else 0, 0, 0]))
|
||||
self.assertTrue(self.safety.safety_rx_hook(self.packer.make_can_msg_safety('EPS_STATUS', 0, {'LKA_STATE': eps})))
|
||||
self.safety.aol_set_host_request(3)
|
||||
|
||||
def test_real_sources_and_stock_longitudinal_withdrawal(self):
|
||||
self.init(0x424D, 32)
|
||||
self.physical()
|
||||
self.assertEqual(self.safety.aol_get_request_mask(), 1)
|
||||
self.assertEqual(self.safety.aol_get_permission_mask(), 1)
|
||||
self.assertTrue(self.safety.get_acc_main_on())
|
||||
for fields, allowed in (({'ACCEL_CMD': 0, 'CANCEL_REQ': 1}, True),
|
||||
({'ACCEL_CMD': 0, 'CANCEL_REQ': 0}, False),
|
||||
({'ACCEL_CMD': .1, 'CANCEL_REQ': 1}, False)):
|
||||
self.assertEqual(bool(self.safety.safety_tx_hook(self.packer.make_can_msg_safety('ACC_CONTROL', 0, fields))), allowed)
|
||||
self.physical(brake=True)
|
||||
self.assertFalse(self.safety.get_controls_allowed())
|
||||
self.assertEqual(self.safety.aol_get_permission_mask(), 1)
|
||||
self.physical(main=False)
|
||||
self.assertFalse(self.safety.get_acc_main_on())
|
||||
self.assertEqual(self.safety.aol_get_permission_mask(), 0)
|
||||
# PCM_CRUISE_2 must never revive the DSU-owned MAIN source.
|
||||
self.rx(0x1D3, bytes([0, 128]) + bytes(6), True)
|
||||
self.assertEqual(self.safety.aol_get_permission_mask(), 0)
|
||||
|
||||
def test_reverse_fault_source_length_bus_and_stale_denials(self):
|
||||
for changes in ({'gear': 16}, {'eps': 9}, {'eps': 17}, {'door': True}):
|
||||
self.init(0x424D, 32)
|
||||
self.physical()
|
||||
self.assertEqual(self.safety.aol_get_permission_mask(), 1)
|
||||
self.physical(**changes)
|
||||
self.assertEqual(self.safety.aol_get_permission_mask(), 0)
|
||||
self.physical()
|
||||
self.assertEqual(self.safety.aol_get_permission_mask(), 1)
|
||||
for address, length in ((0x365, 8), (0x262, 8)):
|
||||
self.init(0x424D, 32)
|
||||
self.physical()
|
||||
self.safety.safety_rx_hook(libsafety_py.make_CANPacket(address, 0, bytes(length)))
|
||||
self.assertEqual(self.safety.aol_get_permission_mask(), 0)
|
||||
self.init(0x424D, 32)
|
||||
self.physical(main=False)
|
||||
self.safety.safety_rx_hook(libsafety_py.make_CANPacket(0x365, 2, bytes([1]) + bytes(6)))
|
||||
self.assertEqual(self.safety.aol_get_permission_mask(), 0)
|
||||
self.physical()
|
||||
self.safety.set_timer(1_303_032)
|
||||
self.safety.aol_set_host_request(1)
|
||||
self.assertEqual(self.safety.aol_get_permission_mask(), 0)
|
||||
|
||||
def test_exact_profile_and_explicit_off(self):
|
||||
for word in (0x404D, 0x424C, 0x424E, 0x434D, 0x524D):
|
||||
self.init(word, 32)
|
||||
self.physical()
|
||||
self.assertEqual(self.safety.aol_get_permission_mask(), 0)
|
||||
self.assertFalse(self.safety.safety_tx_hook(self.packer.make_can_msg_safety('STEERING_LKA', 0, {'STEER_REQUEST': 1, 'STEER_TORQUE_CMD': 1})))
|
||||
self.init(0x424D, 0)
|
||||
self.physical()
|
||||
self.assertTrue(self.safety.get_controls_allowed())
|
||||
self.assertEqual(self.safety.aol_get_permission_mask(), 0)
|
||||
self.safety.set_controls_allowed(False)
|
||||
self.assertFalse(self.safety.safety_tx_hook(self.packer.make_can_msg_safety('STEERING_LKA', 0, {'STEER_REQUEST': 1, 'STEER_TORQUE_CMD': 1})))
|
||||
|
||||
def test_shared_workspace_cold_switch_and_positive_steering(self):
|
||||
for word in (0x424D, 73, 0x9049, 0x2049, 0x424D):
|
||||
self.init(word, 32)
|
||||
self.assertFalse(self.safety.safety_config_valid())
|
||||
self.assertEqual(self.safety.aol_get_permission_mask(), 0)
|
||||
if word == 0x424D:
|
||||
self.physical()
|
||||
self.assertTrue(self.safety.safety_config_valid())
|
||||
self.assertEqual(self.safety.aol_get_permission_mask(), 1)
|
||||
self.assertTrue(self.safety.safety_tx_hook(self.packer.make_can_msg_safety(
|
||||
'STEERING_LKA', 0, {'STEER_REQUEST': 1, 'STEER_TORQUE_CMD': 1})))
|
||||
@@ -97,7 +97,7 @@ int main() {
|
||||
}
|
||||
|
||||
for (uint32_t word = 0U; word <= 65535U; ++word) {
|
||||
const bool expected = (word == 73U) || (word == 585U) || (word == 4169U) || (word == 4681U) || (word == 2121U) || (word == 0x9049U) || (word == 0x9249U) || (word == 0x2049U) || (word == 0x2249U);
|
||||
const bool expected = (word == 73U) || (word == 585U) || (word == 4169U) || (word == 4681U) || (word == 2121U) || (word == 0x9049U) || (word == 0x9249U) || (word == 0x2049U) || (word == 0x2249U) || (word == 0x424DU);
|
||||
auto toyota = status();
|
||||
toyota.safety_mode = 2U;
|
||||
toyota.safety_param = static_cast<uint16_t>(word);
|
||||
|
||||
@@ -5,7 +5,7 @@ from opendbc.car.toyota.values import CAR, ToyotaFlags, ToyotaSafetyFlags, EPS_S
|
||||
from openpilot.starpilot.aol.policy import AolVehiclePolicy
|
||||
from openpilot.starpilot.aol.intent import AolCardIntent, AOL_TOGGLE
|
||||
|
||||
AOL_WORDS = frozenset((73, 585, 4169, 4681, 2121, 0x9049, 0x9249, 0x2049, 0x2249))
|
||||
AOL_WORDS = frozenset((73, 585, 4169, 4681, 2121, 0x9049, 0x9249, 0x2049, 0x2249, 0x424D))
|
||||
|
||||
|
||||
SECOC_TORQUE_CARS = frozenset((CAR.TOYOTA_SIENNA_4TH_GEN,))
|
||||
@@ -33,7 +33,21 @@ def secoc_qualified(cp, *, marked_only=False):
|
||||
config.safetyParam == EPS_SCALE[cp.carFingerprint] | int(ToyotaSafetyFlags.SECOC))
|
||||
|
||||
|
||||
def lexus_is_qualified(cp, *, marked_only=False):
|
||||
if (cp is None or cp.brand != 'toyota' or cp.carFingerprint != CAR.LEXUS_IS or
|
||||
cp.notCar or cp.passive or cp.dashcamOnly or not cp.pcmCruise or cp.openpilotLongitudinalControl or
|
||||
cp.steerControlType != structs.CarParams.SteerControlType.torque or len(cp.safetyConfigs) != 1 or
|
||||
cp.flags & ToyotaFlags.UNSUPPORTED_DSU == 0 or
|
||||
int(cp.flags) & ~int(ToyotaFlags.UNSUPPORTED_DSU | ToyotaFlags.HYBRID) or
|
||||
cp.alternativeExperience not in ((32,) if marked_only else (0, 32))):
|
||||
return False
|
||||
config = cp.safetyConfigs[0]
|
||||
return config.safetyModel == structs.CarParams.SafetyModel.toyota and config.safetyParam == 0x424D
|
||||
|
||||
|
||||
def qualified(cp, *, marked_only=False):
|
||||
if lexus_is_qualified(cp, marked_only=marked_only):
|
||||
return True
|
||||
from opendbc.car.toyota import pedal
|
||||
if pedal.configured(cp):
|
||||
return cp.alternativeExperience in ((32,) if marked_only else (0, 32))
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
#include "selfdrive/pandad/aol_protocol.h"
|
||||
|
||||
inline bool toyota_aol_param(uint16_t param) {
|
||||
return (param == 73U) || (param == 585U) || (param == 4169U) || (param == 4681U) || (param == 2121U) || (param == 0x9049U) || (param == 0x9249U) || (param == 0x2049U) || (param == 0x2249U);
|
||||
return (param == 73U) || (param == 585U) || (param == 4169U) || (param == 4681U) || (param == 2121U) || (param == 0x9049U) || (param == 0x9249U) || (param == 0x2049U) || (param == 0x2249U) || (param == 0x424DU);
|
||||
}
|
||||
|
||||
inline constexpr AolSafetyProfile TOYOTA_AOL_PROFILE{2U, toyota_aol_param, true};
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
"""Lexus DSU MAIN automatic factory through actual daemon/native RX and TX."""
|
||||
import pytest
|
||||
from opendbc.car.toyota.values import CAR
|
||||
from openpilot.starpilot.car.toyota.tests.test_pedal_caller_native import (
|
||||
source_matched_native as source_matched_native, daemon_gc_scope as daemon_gc_scope, run_case,
|
||||
)
|
||||
|
||||
pytestmark = pytest.mark.usefixtures('source_matched_native', 'daemon_gc_scope')
|
||||
|
||||
|
||||
@pytest.mark.parametrize('scenario', ('nominal', 'physical', 'faults', 'bad_dlc', 'off'))
|
||||
def test_stock_lexus_actual_callers(scenario):
|
||||
run_case(True, scenario, candidate=CAR.LEXUS_IS)
|
||||
|
||||
|
||||
def test_stock_lexus_aol_off():
|
||||
run_case(True, aol=False, candidate=CAR.LEXUS_IS)
|
||||
@@ -82,16 +82,16 @@ def firmware(candidate=CAR.TOYOTA_COROLLA_TSS2):
|
||||
]
|
||||
|
||||
|
||||
def physical(packer, *, cruise, main=True, gear=0, brake=False, gas=False, eps=1, sdsu=0, stopped=False):
|
||||
def physical(packer, *, dsu_main=False, cruise, main=True, gear=0, brake=False, gas=False, eps=1, sdsu=0, stopped=False):
|
||||
frames = [
|
||||
packer.make_can_msg(name, 0, {})
|
||||
for name in ('VSC1S07', 'BODY_CONTROL_STATE_2', 'ESP_CONTROL', 'PCM_CRUISE_SM', 'BLINKERS_STATE', 'PRE_COLLISION', 'LIGHT_STALK')
|
||||
for name in ('VSC1S07', 'BODY_CONTROL_STATE_2', 'ESP_CONTROL', 'BLINKERS_STATE', 'PRE_COLLISION', 'LIGHT_STALK')
|
||||
]
|
||||
frames += [
|
||||
packer.make_can_msg('BODY_CONTROL_STATE', 0, {'SEATBELT_DRIVER_UNLATCHED': 0}),
|
||||
packer.make_can_msg('BRAKE_MODULE', 0, {'BRAKE_PRESSED': brake}),
|
||||
packer.make_can_msg('GEAR_PACKET', 0, {'GEAR': gear}),
|
||||
packer.make_can_msg('WHEEL_SPEEDS', 0, {f'WHEEL_SPEED_{w}': 0.0 if stopped else 18.0 for w in ('FL', 'FR', 'RL', 'RR')}),
|
||||
packer.make_can_msg('WHEEL_SPEEDS', 0, {f'WHEEL_SPEED_{w}': 0.0 if stopped else 72.0 if dsu_main else 18.0 for w in ('FL', 'FR', 'RL', 'RR')}),
|
||||
packer.make_can_msg('PCM_CRUISE', 0, {'CRUISE_ACTIVE': cruise, 'GAS_RELEASED': not gas, 'CRUISE_STATE': 7 if cruise and stopped else 8 if cruise else 0}),
|
||||
packer.make_can_msg('PCM_CRUISE_2', 0, {'MAIN_ON': main, 'SET_SPEED': 90}),
|
||||
packer.make_can_msg('STEER_ANGLE_SENSOR', 0, {}),
|
||||
@@ -100,6 +100,12 @@ def physical(packer, *, cruise, main=True, gear=0, brake=False, gas=False, eps=1
|
||||
packer.make_can_msg('LKAS_HUD', 2, {}),
|
||||
packer.make_can_msg('RSA1', 2, {}),
|
||||
]
|
||||
if dsu_main:
|
||||
frames += [packer.make_can_msg('PCM_CRUISE_ALT', 0, {}), packer.make_can_msg('DSU_CRUISE', 0, {'MAIN_ON': main, 'SET_SPEED': 90})]
|
||||
# This source must not revive or withdraw DSU-owned MAIN.
|
||||
frames = [f for f in frames if f[0] != 0x1D3] + [packer.make_can_msg('PCM_CRUISE_2', 0, {'MAIN_ON': not main})]
|
||||
else:
|
||||
frames.append(packer.make_can_msg('PCM_CRUISE_SM', 0, {}))
|
||||
frames += [packer.make_can_msg('ACC_CONTROL', 2, {'ACC_TYPE': 1}), packer.make_can_msg('PCS_HUD', 2, {}), packer.make_can_msg('PRE_COLLISION', 2, {})]
|
||||
return frames
|
||||
|
||||
@@ -195,7 +201,7 @@ def control_inputs(controls, published, state, sd_events, native, now):
|
||||
values.append(sd_events[name])
|
||||
elif name == 'carState':
|
||||
values.append(state['carState'])
|
||||
elif name == 'aolSafetyWire':
|
||||
elif name in ('aolSafetyWire', 'pandaStates'):
|
||||
values.append(next(e for e in native if e.which() == name))
|
||||
elif name in published:
|
||||
values.append(published[name])
|
||||
@@ -211,6 +217,8 @@ def control_inputs(controls, published, state, sd_events, native, now):
|
||||
v = getattr(e, name)
|
||||
if name == 'modelV2':
|
||||
v.from_dict(model().to_dict())
|
||||
elif name == 'lateralManeuverPlan' and controls.CP.carFingerprint == CAR.LEXUS_IS:
|
||||
v.desiredCurvature = .00005 # Synthetic plan exercises nonzero encoded steering.
|
||||
elif name == 'vehicleParameters':
|
||||
v.stiffnessFactor, v.steerRatio = 1.0, controls.CP.steerRatio
|
||||
elif name == 'longitudinalPlan':
|
||||
@@ -249,7 +257,8 @@ def run_case(stock, scenario='nominal', *, aol=True, candidate=CAR.TOYOTA_COROLL
|
||||
):
|
||||
host = card_module.Car()
|
||||
assert host.CP.openpilotLongitudinalControl == (not stock)
|
||||
assert host.CP.safetyConfigs[0].safetyParam == (0x2249 if stock else 0x2049)
|
||||
dsu_main = candidate == CAR.LEXUS_IS
|
||||
assert host.CP.safetyConfigs[0].safetyParam == (0x424D if dsu_main else 0x2249 if stock else 0x2049)
|
||||
assert host.CP.alternativeExperience == 32
|
||||
controls, sd = Controls(), sd_module.SelfdriveD(CP=host.CP)
|
||||
assert controls.CP.to_dict() == host.CP.to_dict()
|
||||
@@ -281,13 +290,17 @@ def run_case(stock, scenario='nominal', *, aol=True, candidate=CAR.TOYOTA_COROLL
|
||||
)
|
||||
)
|
||||
rows = {r.key: r for r in wheel.rows()}
|
||||
assert 'Bookmark' in rows['wheel:LKASButtonControl'].choices
|
||||
assert 'Toggle AOL' not in rows['wheel:LKASButtonControl'].choices
|
||||
if dsu_main:
|
||||
assert 'wheel:LKASButtonControl' not in rows
|
||||
else:
|
||||
assert 'Bookmark' in rows['wheel:LKASButtonControl'].choices
|
||||
assert 'Toggle AOL' not in rows['wheel:LKASButtonControl'].choices
|
||||
assert not any('Cancel' in k or 'MainCruise' in k for k in rows)
|
||||
assert display_supported(host.CP) and longitudinal_supported(host.CP) == (not stock)
|
||||
params.put('CancelButtonControl', 9, block=True) # Unsupported saved remap cannot steal MAIN/physical Cancel ownership.
|
||||
seen_active = False
|
||||
seen_pedal = False
|
||||
seen_steer = False
|
||||
clock = [time.monotonic_ns()]
|
||||
# Deterministic input-time vector for the three serially hosted daemon methods.
|
||||
# Keep the real-time nominal/physical/OFF receipts separate; no scheduling/road claim here.
|
||||
@@ -320,8 +333,15 @@ def run_case(stock, scenario='nominal', *, aol=True, candidate=CAR.TOYOTA_COROLL
|
||||
)
|
||||
elif scenario == 'off' and tick == 250:
|
||||
params.put_bool('DisableOpenpilotLongitudinal', not stock, block=True)
|
||||
frames = physical(packer, **fields)
|
||||
if fields.get('sdsu', 0) is not None:
|
||||
if dsu_main and fields.get('sdsu'):
|
||||
fields['eps'] = 9
|
||||
frames = physical(packer, dsu_main=dsu_main, **fields)
|
||||
if dsu_main and fields.get('sdsu', 0) is None:
|
||||
if scenario == 'bad_dlc':
|
||||
frames = [(a, d + bytes(1) if a == 0x365 else d, b) for a, d, b in frames]
|
||||
else:
|
||||
frames = [f for f in frames if f[0] != 0x365]
|
||||
if not dsu_main and fields.get('sdsu', 0) is not None:
|
||||
sensor = packer.make_can_msg(
|
||||
'GAS_SENSOR',
|
||||
0,
|
||||
@@ -419,7 +439,10 @@ def run_case(stock, scenario='nominal', *, aol=True, candidate=CAR.TOYOTA_COROLL
|
||||
if 415 <= tick < 430:
|
||||
assert not cc.latActive and not cc.longActive, trace[-1]
|
||||
if scenario in ('faults', 'bad_dlc'):
|
||||
if any(a <= tick < b for a, b in ((260, 269), (330, 339), (465, 469), (650, 661))):
|
||||
dropout = ((635, 665) if scenario == 'bad_dlc' else (662, 665)) if dsu_main else (650, 661)
|
||||
if dsu_main and scenario != 'bad_dlc' and 650 <= tick < 656:
|
||||
assert cc.latActive, ('DSU MAIN is still inside its existing 303 ms lease', trace[-1])
|
||||
if any(a <= tick < b for a, b in ((260, 269), (330, 339), (465, 469), dropout)):
|
||||
assert not cc.latActive and not cc.longActive, trace[-1]
|
||||
if 350 <= tick < 356:
|
||||
assert cc.latActive and not cc.longActive, ('cleared EPS must not automatically reengage ordinary LONG', trace[-1])
|
||||
@@ -428,7 +451,7 @@ def run_case(stock, scenario='nominal', *, aol=True, candidate=CAR.TOYOTA_COROLL
|
||||
if scenario == 'off' and tick >= 310:
|
||||
if stock:
|
||||
assert not host.CP.openpilotLongitudinalControl and not cc.longActive
|
||||
assert host.CP.safetyConfigs[0].safetyParam == 0x2249
|
||||
assert host.CP.safetyConfigs[0].safetyParam == (0x424D if dsu_main else 0x2249)
|
||||
else:
|
||||
assert not host.CI.CC.pedal_input.update(time.monotonic_ns())
|
||||
for f in outgoing.sendcan:
|
||||
@@ -437,6 +460,11 @@ def run_case(stock, scenario='nominal', *, aol=True, candidate=CAR.TOYOTA_COROLL
|
||||
assert acc['ACCEL_CMD'] == 0 and acc['CANCEL_REQ'] and not acc['RELEASE_STANDSTILL']
|
||||
|
||||
for frame in outgoing.sendcan:
|
||||
if dsu_main and frame.address == 0x2E4:
|
||||
steer = decode(host.CP, (frame.address, bytes(frame.dat), frame.src), 'STEERING_LKA')
|
||||
if steer['STEER_TORQUE_CMD'] != 0:
|
||||
assert cc.latActive and steer['STEER_REQUEST']
|
||||
seen_steer = True
|
||||
if frame.address == 0x200:
|
||||
data = bytes(frame.dat)
|
||||
assert len(data) == 6 and data[5] == pedal.checksum(data)
|
||||
@@ -449,6 +477,8 @@ def run_case(stock, scenario='nominal', *, aol=True, candidate=CAR.TOYOTA_COROLL
|
||||
prev = current_sd
|
||||
assert seen_active
|
||||
assert seen_pedal == (not stock)
|
||||
if dsu_main:
|
||||
assert seen_steer, "Actual nonzero steering must reach the fresh native TX hook"
|
||||
finally:
|
||||
(OUT / (str(candidate) + '-actual-' + scenario + ('-stock-trace.json' if stock else '-long-trace.json'))).write_text(json.dumps(trace, indent=2) + '\n')
|
||||
safety.set_alternative_experience(0)
|
||||
|
||||
Executable → Regular
+1
@@ -67,6 +67,7 @@ RELEASE_TARGETS = (
|
||||
"opendbc_repo/opendbc/car/hyundai/tests/test_radar_hda1_startup_guards.py",
|
||||
"opendbc_repo/opendbc/car/hyundai/tests/test_radar_hda1_startup_lifecycle.py::test_exact_factory_stock_copy_and_owner_preserve_all_params",
|
||||
"opendbc_repo/opendbc/safety/tests/test_toyota_pedal.py",
|
||||
"opendbc_repo/opendbc/safety/tests/test_toyota_lexus_is.py",
|
||||
"opendbc_repo/opendbc/car/hyundai/tests/test_ordinary_classic_startup_contract.py",
|
||||
"opendbc_repo/opendbc/car/hyundai/tests/test_ordinary_classic_startup_protocol.py",
|
||||
"opendbc_repo/opendbc/car/hyundai/tests/test_ordinary_classic_stock_fallback.py",
|
||||
|
||||
Reference in New Issue
Block a user