mirror of
https://github.com/firestar5683/StarPilot.git
synced 2026-08-20 15:54:13 +08:00
Tubular
This commit is contained in:
@@ -413,6 +413,13 @@ def preserve_stock_canfd_lfa_status(car_fingerprint) -> bool:
|
||||
return car_fingerprint != CAR.KIA_CARNIVAL_4TH_GEN
|
||||
|
||||
|
||||
def suppress_redundant_gv70_brake_cancel(CP, brake_pressed: bool, lat_active: bool) -> bool:
|
||||
return bool(
|
||||
CP.carFingerprint == CAR.GENESIS_GV70_ELECTRIFIED_1ST_GEN and
|
||||
not CP.openpilotLongitudinalControl and brake_pressed and lat_active
|
||||
)
|
||||
|
||||
|
||||
class CarController(CarControllerBase):
|
||||
def __init__(self, dbc_names, CP):
|
||||
super().__init__(dbc_names, CP)
|
||||
@@ -992,7 +999,10 @@ class CarController(CarControllerBase):
|
||||
if (self.frame - self.last_button_frame) * DT_CTRL > 0.25:
|
||||
# cruise cancel - suppress when stock ACC is the fallback (ECU disable failed),
|
||||
# so openpilot doesn't fight/cancel the user's stock cruise
|
||||
if CC.cruiseControl.cancel and not self.ecu_disable_failed:
|
||||
suppress_brake_cancel = suppress_redundant_gv70_brake_cancel(
|
||||
self.CP, CS.out.brakePressed, CC.latActive,
|
||||
)
|
||||
if CC.cruiseControl.cancel and not self.ecu_disable_failed and not suppress_brake_cancel:
|
||||
if self.CP.flags & HyundaiFlags.CANFD_ALT_BUTTONS:
|
||||
can_sends.append(hyundaicanfd.create_acc_cancel(self.packer, self.CP, self.CAN, CS.cruise_info))
|
||||
self.last_button_frame = self.frame
|
||||
|
||||
@@ -18,7 +18,8 @@ from opendbc.car.hyundai.carcontroller import CarController, Ioniq6LongitudinalT
|
||||
direct_angle_request_allowed, get_angle_smoothing_alpha, \
|
||||
should_use_ev6_gt_line_stop_direct_tracking, \
|
||||
should_track_stop_accel_directly_for_car, \
|
||||
preserve_stock_canfd_lfa_status
|
||||
preserve_stock_canfd_lfa_status, \
|
||||
suppress_redundant_gv70_brake_cancel
|
||||
from opendbc.car.hyundai.carstate import CarState, decode_canfd_camera_lead, decode_ioniq_6_blindspot_radar_state, \
|
||||
get_canfd_cruise_available
|
||||
from opendbc.car.hyundai.interface import CarInterface, KIA_EV9_ACCEL_MAX
|
||||
@@ -142,11 +143,18 @@ class TestHyundaiFingerprint:
|
||||
lfa_msg = hyundaicanfd.create_steering_messages(packer, CP, can_bus, False, False, 0, 0.0, lfa_base)[0]
|
||||
assert lfa_msg[1] == bytes.fromhex("05100002400008000000000000640000")
|
||||
|
||||
active_lfa_msg = hyundaicanfd.create_steering_messages(packer, CP, can_bus, True, True, 100, 0.0, None,
|
||||
lka_icon=2)[0]
|
||||
assert active_lfa_msg[1] == bytes.fromhex("9a17010280c818000000000000640000")
|
||||
|
||||
stock_cluster = {"NEW_SIGNAL_5": 1}
|
||||
cluster_base = stock_cluster if preserve_stock_canfd_lfa_status(CP.carFingerprint) else None
|
||||
cluster_msg = hyundaicanfd.create_lfahda_cluster(packer, can_bus, False, cluster_base)
|
||||
assert cluster_msg[1] == bytes.fromhex("8e040000000000000000000000000000")
|
||||
|
||||
active_cluster_msg = hyundaicanfd.create_lfahda_cluster(packer, can_bus, True, None, lfa_icon=2)
|
||||
assert active_cluster_msg[1] == bytes.fromhex("cdfb0180000001000000000000000000")
|
||||
|
||||
def test_canfd_torque_bsm_parser_registers_rear_blindspots(self):
|
||||
CP = CarParams.new_message()
|
||||
CP.carFingerprint = CAR.GENESIS_GV70_ELECTRIFIED_1ST_GEN
|
||||
@@ -576,6 +584,21 @@ class TestHyundaiFingerprint:
|
||||
palisade_2023 = CarInterface.get_params(CAR.HYUNDAI_PALISADE_2023, gen_empty_fingerprint(), [], True, False, False, None)
|
||||
assert palisade_2023.safetyConfigs[-1].safetyParam & HyundaiStarPilotSafetyFlags.HAS_LDA_BUTTON
|
||||
|
||||
def test_carnival_lka_button_does_not_enable_angle_steering_safety(self):
|
||||
fingerprint = gen_empty_fingerprint()
|
||||
fingerprint[0][0x391] = 8
|
||||
toggles = SimpleNamespace(always_on_lateral_lkas=True)
|
||||
|
||||
CP = CarInterface.get_params(CAR.KIA_CARNIVAL_4TH_GEN, fingerprint, [], True, False, False, toggles)
|
||||
FPCP = CarInterface.get_starpilot_params(CAR.KIA_CARNIVAL_4TH_GEN, fingerprint, [], CP, toggles)
|
||||
combined_safety_param = CP.safetyConfigs[-1].safetyParam | FPCP.safetyConfigs[-1].safetyParam
|
||||
|
||||
assert CP.steerControlType == CarParams.SteerControlType.torque
|
||||
assert not (CP.flags & HyundaiFlags.CANFD_ANGLE_STEERING)
|
||||
assert not (combined_safety_param & HyundaiSafetyFlags.CANFD_ANGLE_STEERING)
|
||||
assert combined_safety_param & HyundaiSafetyFlags.LONG
|
||||
assert combined_safety_param & HyundaiStarPilotSafetyFlags.AOL_LKAS_ON_ENGAGE
|
||||
|
||||
def test_sonata_hybrid_aol_main_lkas_sync_is_scoped(self):
|
||||
toggles = SimpleNamespace(always_on_lateral_lkas=True, main_cruise_aol_toggle=True)
|
||||
|
||||
@@ -2034,6 +2057,22 @@ class TestHyundaiFingerprint:
|
||||
assert parser.vl["SCC_CONTROL"]["aReqValue"] == pytest.approx(-0.1)
|
||||
assert parser.vl["SCC_CONTROL"]["aReqRaw"] == pytest.approx(-1.0)
|
||||
|
||||
def test_gv70_electrified_suppresses_only_stock_scc_brake_cancel(self):
|
||||
CP = CarParams.new_message()
|
||||
CP.carFingerprint = CAR.GENESIS_GV70_ELECTRIFIED_1ST_GEN
|
||||
CP.openpilotLongitudinalControl = False
|
||||
|
||||
assert suppress_redundant_gv70_brake_cancel(CP, brake_pressed=True, lat_active=True)
|
||||
assert not suppress_redundant_gv70_brake_cancel(CP, brake_pressed=False, lat_active=True)
|
||||
assert not suppress_redundant_gv70_brake_cancel(CP, brake_pressed=True, lat_active=False)
|
||||
|
||||
CP.openpilotLongitudinalControl = True
|
||||
assert not suppress_redundant_gv70_brake_cancel(CP, brake_pressed=True, lat_active=True)
|
||||
|
||||
CP.carFingerprint = CAR.HYUNDAI_IONIQ_6
|
||||
CP.openpilotLongitudinalControl = False
|
||||
assert not suppress_redundant_gv70_brake_cancel(CP, brake_pressed=True, lat_active=True)
|
||||
|
||||
def test_ev9_inactive_angle_steering_lets_safety_forward_stock_lkas(self):
|
||||
CP = CarParams.new_message()
|
||||
CP.carFingerprint = CAR.KIA_EV9
|
||||
|
||||
@@ -248,7 +248,7 @@ class CarInterfaceBase(ABC):
|
||||
fp_ret.pcmCruiseSpeed = False
|
||||
CP.openpilotLongitudinalControl = True
|
||||
|
||||
hyundai_has_lda_button = (
|
||||
hyundai_has_lda_button = not (CP.flags & HyundaiFlags.CANFD) and (
|
||||
0x391 in fingerprint[0] or
|
||||
0x50C in fingerprint[0] or
|
||||
candidate in ALT_BUS_LDA_BUTTON_CARS or
|
||||
|
||||
@@ -226,23 +226,23 @@ def test_legacy_2025_uses_validated_angle_request_limits():
|
||||
assert parser.vl["ES_LKAS_ANGLE"]["LKAS_Output"] == pytest.approx(CS.out.steeringAngleDeg)
|
||||
|
||||
|
||||
def test_ascent_2023_uses_d_platform_bus_layout():
|
||||
def test_ascent_2023_uses_gen2_angle_bus_layout():
|
||||
CP = CarInterface.get_non_essential_params(CAR.SUBARU_ASCENT_2023)
|
||||
parsers = CarState.get_can_parsers(CP)
|
||||
controller = CarController({}, CP)
|
||||
|
||||
assert CP.flags & SubaruFlags.D_PLATFORM
|
||||
assert CP.safetyConfigs[0].safetyParam & SubaruSafetyFlags.D_PLATFORM
|
||||
assert CP.flags & SubaruFlags.D_PLATFORM_CAMERA
|
||||
assert CP.safetyConfigs[0].safetyParam & SubaruSafetyFlags.D_PLATFORM_CAMERA
|
||||
assert CanBus.main_for_cp(CP) == CanBus.alt
|
||||
assert CanBus.angle_for_cp(CP) == CanBus.camera
|
||||
assert parsers[Bus.pt].bus == CanBus.alt
|
||||
assert not (CP.flags & SubaruFlags.D_PLATFORM)
|
||||
assert not (CP.safetyConfigs[0].safetyParam & SubaruSafetyFlags.D_PLATFORM)
|
||||
assert not (CP.flags & SubaruFlags.D_PLATFORM_CAMERA)
|
||||
assert not (CP.safetyConfigs[0].safetyParam & SubaruSafetyFlags.D_PLATFORM_CAMERA)
|
||||
assert CanBus.main_for_cp(CP) == CanBus.main
|
||||
assert CanBus.angle_for_cp(CP) == CanBus.main
|
||||
assert parsers[Bus.pt].bus == CanBus.main
|
||||
assert parsers[Bus.cam].bus == CanBus.camera
|
||||
assert parsers[Bus.alt].bus == CanBus.alt
|
||||
assert parsers[Bus.main].bus == CanBus.main
|
||||
assert controller.angle_bus == CanBus.camera
|
||||
assert controller.status_bus == CanBus.camera
|
||||
assert Bus.main not in parsers
|
||||
assert controller.angle_bus == CanBus.main
|
||||
assert controller.status_bus == CanBus.main
|
||||
|
||||
|
||||
def test_other_angle_platforms_keep_existing_bus_layout():
|
||||
|
||||
@@ -253,7 +253,7 @@ class CAR(Platforms):
|
||||
SUBARU_ASCENT_2023 = SubaruGen2PlatformConfig(
|
||||
[SubaruCarDocs("Subaru Ascent 2023-25", "All", car_parts=CarParts.common([CarHarness.subaru_d]))],
|
||||
SUBARU_ASCENT.specs,
|
||||
flags=SubaruFlags.LKAS_ANGLE | SubaruFlags.D_PLATFORM | SubaruFlags.D_PLATFORM_CAMERA,
|
||||
flags=SubaruFlags.LKAS_ANGLE,
|
||||
)
|
||||
SUBARU_CROSSTREK_2025 = SubaruGen2PlatformConfig(
|
||||
[SubaruCarDocs("Subaru Crosstrek 2025", "All", car_parts=CarParts.common([CarHarness.subaru_d]))],
|
||||
|
||||
@@ -67,8 +67,10 @@ class CarInterface(CarInterfaceBase):
|
||||
# These messages are normally absent there on pre-TSS2 platforms.
|
||||
camera_fingerprint = fingerprint.get(2, {})
|
||||
has_dsu_bypass = 0x343 in camera_fingerprint or 0x4CB in camera_fingerprint
|
||||
if candidate == CAR.LEXUS_IS:
|
||||
# The IS mirrors its native buses onto camera bus during startup without a bypass adapter.
|
||||
late_prius_camera = candidate == CAR.TOYOTA_PRIUS and any(
|
||||
fw.ecu == Ecu.fwdCamera and bytes(fw.fwVersion).startswith(b'8646F4705') for fw in car_fw
|
||||
)
|
||||
if candidate == CAR.LEXUS_IS or late_prius_camera:
|
||||
has_dsu_bypass = ((0x343 in camera_fingerprint and 0x343 not in fingerprint.get(1, {})) or
|
||||
(0x4CB in camera_fingerprint and 0x4CB not in fingerprint.get(0, {})))
|
||||
if not use_sdsu and candidate not in TSS2_CAR and has_dsu_bypass:
|
||||
|
||||
@@ -258,6 +258,43 @@ class TestToyotaInterfaces:
|
||||
assert not car_params.safetyConfigs[0].safetyParam & ToyotaSafetyFlags.STOCK_LONGITUDINAL.value
|
||||
assert not car_params.safetyConfigs[0].safetyParam & ToyotaSafetyFlags.ALT_CRUISE.value
|
||||
|
||||
@pytest.mark.parametrize(("native_bus", "message"), [(1, 0x343), (0, 0x4CB)])
|
||||
def test_late_prius_ignores_startup_bus_mirror(self, native_bus, message):
|
||||
fingerprint = {bus: {} for bus in range(8)}
|
||||
fingerprint[native_bus][message] = 8
|
||||
fingerprint[2][message] = 8
|
||||
car_fw = [CarParams.CarFw(
|
||||
ecu=Ecu.fwdCamera,
|
||||
address=0x750,
|
||||
subAddress=0x6D,
|
||||
fwVersion=b'8646F4705200\x00\x00\x00\x00',
|
||||
)]
|
||||
|
||||
car_params = CarInterface.get_params(
|
||||
CAR.TOYOTA_PRIUS,
|
||||
fingerprint,
|
||||
car_fw,
|
||||
alpha_long=False,
|
||||
is_release=False,
|
||||
docs=False,
|
||||
starpilot_toggles=SimpleNamespace(),
|
||||
)
|
||||
|
||||
assert not car_params.flags & ToyotaFlags.DSU_BYPASS.value
|
||||
assert not car_params.openpilotLongitudinalControl
|
||||
assert car_params.safetyConfigs[0].safetyParam & ToyotaSafetyFlags.STOCK_LONGITUDINAL.value
|
||||
|
||||
starpilot_params = CarInterface.get_starpilot_params(
|
||||
CAR.TOYOTA_PRIUS, fingerprint, car_fw, car_params, SimpleNamespace(),
|
||||
)
|
||||
car_state = CarState(car_params, starpilot_params)
|
||||
can_parsers = car_state.get_can_parsers(car_params)
|
||||
car_state.update(can_parsers, SimpleNamespace(cluster_offset=1.0))
|
||||
|
||||
assert "PRE_COLLISION" in can_parsers[Bus.pt].vl
|
||||
for acc_message in ("ACC_CONTROL", "PRE_COLLISION", "PCS_HUD"):
|
||||
assert acc_message not in can_parsers[Bus.cam].vl
|
||||
|
||||
def test_dsu_bypass_does_not_change_tss2_or_smart_dsu(self):
|
||||
fingerprint = {bus: {} for bus in range(8)}
|
||||
fingerprint[0][0x2FF] = 8
|
||||
|
||||
@@ -365,6 +365,9 @@ class TestSubaruGen2Legacy2025AngleSafety(TestSubaruGen2AngleStockLongitudinalSa
|
||||
ANGLE_RATE_UP = [5., .8, .15]
|
||||
ANGLE_RATE_DOWN = [5., .8, .15]
|
||||
|
||||
def test_rt_limits(self):
|
||||
raise unittest.SkipTest("Breakpoint angle limits do not enforce a real-time message frequency")
|
||||
|
||||
|
||||
class TestSubaruDPlatformAngleSafety(TestSubaruStockLongitudinalSafetyBase, TestSubaruAngleSafetyBase):
|
||||
FLAGS = SubaruSafetyFlags.GEN2 | SubaruSafetyFlags.LKAS_ANGLE | SubaruSafetyFlags.D_PLATFORM
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,2 +1,2 @@
|
||||
extern const uint8_t gitversion[19];
|
||||
const uint8_t gitversion[19] = "DEV-187318b5-DEBUG";
|
||||
const uint8_t gitversion[19] = "DEV-cbf7f35c-DEBUG";
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1 +1 @@
|
||||
DEV-187318b5-DEBUG
|
||||
DEV-cbf7f35c-DEBUG
|
||||
@@ -1,8 +1,6 @@
|
||||
#!/usr/bin/env python3
|
||||
import argparse
|
||||
import codecs
|
||||
import ctypes
|
||||
import glob
|
||||
import hashlib
|
||||
import json
|
||||
import os
|
||||
@@ -47,19 +45,6 @@ MODEL_CONTEXT_FREQ = 5
|
||||
REPOSITORY_FILE_LIMIT = 100_000_000
|
||||
DEFAULT_MULTIPART_SIZE = 95 * 1024 * 1024
|
||||
USBGPU_PROBE_ATTEMPTS = 10
|
||||
USBGPU_PROBE_TIMEOUT = 2
|
||||
USBDEVFS_CONTROL = 0xC0185500
|
||||
USBGPU_VID_PIDS = (("add1", "0001"), ("3801", "0001"))
|
||||
USBGPU_FIRMWARE_PRODUCT = "custom ed4e39b7-CLEAN"
|
||||
|
||||
|
||||
class _UsbdevfsControl(ctypes.Structure):
|
||||
_fields_ = [("request_type", ctypes.c_uint8), ("request", ctypes.c_uint8),
|
||||
("value", ctypes.c_uint16), ("index", ctypes.c_uint16),
|
||||
("length", ctypes.c_uint16), ("timeout", ctypes.c_uint32),
|
||||
("data", ctypes.c_void_p)]
|
||||
|
||||
|
||||
def build_compile_env(*, supercombo: bool = False) -> dict[str, str]:
|
||||
env = os.environ.copy()
|
||||
existing_pythonpath = env.get("PYTHONPATH", "")
|
||||
@@ -85,79 +70,15 @@ def build_compile_env(*, supercombo: bool = False) -> dict[str, str]:
|
||||
return env
|
||||
|
||||
|
||||
def _probe_external_gpu_link_once() -> tuple[bool, str]:
|
||||
"""Probe the bridge without initializing tinygrad or resetting the USB device."""
|
||||
import fcntl
|
||||
def wait_for_external_gpu() -> None:
|
||||
"""Use openpilot's Chestnut link probe before starting a USB-GPU build."""
|
||||
from openpilot.system.hardware.chestnut.flash import link_up
|
||||
|
||||
diagnostics: list[str] = []
|
||||
for path in glob.glob("/sys/bus/usb/devices/*"):
|
||||
try:
|
||||
if not Path(path, "idVendor").is_file():
|
||||
continue
|
||||
vendor = Path(path, "idVendor").read_text().strip().lower()
|
||||
product = Path(path, "idProduct").read_text().strip().lower()
|
||||
if (vendor, product) not in USBGPU_VID_PIDS:
|
||||
continue
|
||||
bus = int(Path(path, "busnum").read_text())
|
||||
device = int(Path(path, "devnum").read_text())
|
||||
location = f"usb:{bus}-{device}"
|
||||
firmware = Path(path, "product").read_text().strip()
|
||||
if firmware and firmware != USBGPU_FIRMWARE_PRODUCT:
|
||||
return False, f"{location}: firmware {firmware!r}, expected {USBGPU_FIRMWARE_PRODUCT!r}"
|
||||
fd = os.open(f"/dev/bus/usb/{bus:03d}/{device:03d}", os.O_RDWR)
|
||||
except (OSError, ValueError) as exc:
|
||||
diagnostics.append(f"{path}: open failed ({exc})")
|
||||
continue
|
||||
|
||||
try:
|
||||
fcntl.ioctl(fd, USBDEVFS_CONTROL, _UsbdevfsControl(0x40, 0xF3, 1, 0, 0, USBGPU_PROBE_TIMEOUT * 1000, None))
|
||||
state = (ctypes.c_ubyte * 1)()
|
||||
fcntl.ioctl(fd, USBDEVFS_CONTROL, _UsbdevfsControl(0xC0, 0xE4, 0xB450, 0, 1, 1000, ctypes.cast(state, ctypes.c_void_p)))
|
||||
if state[0] == 0x78:
|
||||
return True, f"{location}: LTSSM=0x78"
|
||||
diagnostics.append(f"{location}: LTSSM=0x{state[0]:02X}")
|
||||
except OSError as exc:
|
||||
diagnostics.append(f"{location}: control probe failed ({exc})")
|
||||
finally:
|
||||
os.close(fd)
|
||||
return False, diagnostics[-1] if diagnostics else "no ASM2464PD device found"
|
||||
|
||||
|
||||
def wait_for_external_gpu(compile_env: dict[str, str]) -> bool:
|
||||
"""Wait for the USB GPU's PCIe link before starting the large model build.
|
||||
|
||||
The dock can enumerate on USB before its PCIe link has finished training.
|
||||
Probe the bridge's control endpoint directly, like upstream openpilot. Do
|
||||
not instantiate tinygrad here: opening the GPU resets/claims the USB
|
||||
interface, and doing that in a probe process can leave the bridge in a state
|
||||
where the authoritative compiler cannot train the link.
|
||||
"""
|
||||
del compile_env # retained in the public helper signature for callers/tests
|
||||
diagnostics: list[str] = []
|
||||
|
||||
for attempt in range(USBGPU_PROBE_ATTEMPTS):
|
||||
if attempt:
|
||||
time.sleep(1)
|
||||
try:
|
||||
ready, detail = _probe_external_gpu_link_once()
|
||||
except Exception as exc: # probe is advisory; compile_modeld remains authoritative
|
||||
ready, detail = False, str(exc)
|
||||
if ready:
|
||||
return True
|
||||
if "firmware" in detail and "expected" in detail:
|
||||
raise RuntimeError(
|
||||
f"External GPU firmware is out of date: {detail}. "
|
||||
"Wait for hardwared to flash the dock, or run "
|
||||
"sudo python3 system/hardware/chestnut/flash.py ed4e39b7."
|
||||
)
|
||||
diagnostics.append(detail)
|
||||
|
||||
detail = diagnostics[-1] if diagnostics else "unknown error"
|
||||
print(
|
||||
f"Warning: external GPU link did not become ready after {USBGPU_PROBE_ATTEMPTS} probes: {detail}\n"
|
||||
" Continuing; compile_modeld will perform the authoritative link wait and initialization."
|
||||
)
|
||||
return False
|
||||
for _ in range(USBGPU_PROBE_ATTEMPTS):
|
||||
if link_up():
|
||||
return
|
||||
time.sleep(1)
|
||||
raise RuntimeError("Chestnut not ready; external GPU PCIe link did not come up")
|
||||
|
||||
|
||||
def parse_args() -> argparse.Namespace:
|
||||
@@ -612,7 +533,7 @@ def compile_driving(
|
||||
"TC_OPT": "2",
|
||||
})
|
||||
command.append("--out-of-band")
|
||||
wait_for_external_gpu(compile_env)
|
||||
wait_for_external_gpu()
|
||||
subprocess.run(command, cwd=REPO_ROOT, env=compile_env, check=True)
|
||||
return output_path
|
||||
|
||||
|
||||
@@ -128,6 +128,7 @@ class LatControlTorque(LatControl):
|
||||
self.is_tucson_4th_gen = CP.carFingerprint in TUCSON_4TH_GEN_CARS
|
||||
self.is_civic_bosch_modified = CP.carFingerprint == HONDA_CAR.HONDA_CIVIC_BOSCH and bool(CP.flags & HondaFlags.EPS_MODIFIED)
|
||||
self.is_silverado = CP.carFingerprint in SILVERADO_CARS
|
||||
self.is_gmc_yukon_cc = CP.carFingerprint in GMC_YUKON_CC_CARS
|
||||
self.is_ram_1500 = CP.carFingerprint in RAM_1500_CARS
|
||||
self.is_gm = CP.brand == "gm"
|
||||
self.is_hkg_canfd_torque = CP.brand == "hyundai" and bool(CP.flags & HyundaiFlags.CANFD)
|
||||
@@ -289,6 +290,8 @@ class LatControlTorque(LatControl):
|
||||
ff *= ff_scale
|
||||
if self.is_ram_1500:
|
||||
ff *= get_ram_1500_ff_scale(setpoint, desired_lateral_jerk, CS.vEgo)
|
||||
if self.is_gmc_yukon_cc:
|
||||
ff *= get_gmc_yukon_cc_ff_scale(setpoint, desired_lateral_jerk, CS.vEgo)
|
||||
trailer_load_kg = float(max(getattr(starpilot_toggles, "trailer_load_kg", 0.0) or 0.0, 0.0))
|
||||
bolt_2022_2023_tuned_path_active = self.is_bolt_2022_2023
|
||||
bolt_2018_2021_tuned_path_active = self.is_bolt_2018_2021
|
||||
@@ -526,6 +529,12 @@ class LatControlTorque(LatControl):
|
||||
output_torque *= get_ioniq_6_highway_transition_output_taper_scale(setpoint, desired_lateral_jerk, CS.vEgo)
|
||||
if self.is_ioniq_6_2025:
|
||||
output_torque *= get_ioniq_6_2025_center_output_scale(setpoint, CS.vEgo)
|
||||
low_speed_output_limit = get_ioniq_6_2025_low_speed_output_limit(setpoint, desired_lateral_jerk, CS.vEgo)
|
||||
output_torque = float(np.clip(
|
||||
output_torque,
|
||||
-low_speed_output_limit,
|
||||
low_speed_output_limit,
|
||||
))
|
||||
elif self.is_ram_1500 and output_torque * setpoint > 0.0:
|
||||
output_torque *= get_ram_1500_transition_output_scale(setpoint, desired_lateral_jerk, CS.vEgo)
|
||||
elif self.is_kona_non_scc:
|
||||
@@ -563,6 +572,10 @@ class LatControlTorque(LatControl):
|
||||
)
|
||||
low_speed_output_limit = get_genesis_g70_low_speed_output_limit(setpoint, CS.vEgo)
|
||||
output_torque = float(np.clip(output_torque, -low_speed_output_limit, low_speed_output_limit))
|
||||
elif self.is_genesis_gv70:
|
||||
output_torque *= get_genesis_gv70_high_speed_error_scale(
|
||||
setpoint, measurement, desired_lateral_jerk, CS.vEgo,
|
||||
)
|
||||
elif sonata_hybrid_active:
|
||||
output_torque *= sonata_hybrid_center_taper
|
||||
output_torque *= sonata_hybrid_center_output_taper
|
||||
|
||||
@@ -84,6 +84,9 @@ SILVERADO_CARS = (
|
||||
GM_CAR.CHEVROLET_SILVERADO,
|
||||
GM_CAR.CHEVROLET_SILVERADO_CC,
|
||||
)
|
||||
GMC_YUKON_CC_CARS = (
|
||||
GM_CAR.GMC_YUKON_CC,
|
||||
)
|
||||
GENESIS_G90_CARS = (
|
||||
HYUNDAI_CAR.GENESIS_G90,
|
||||
)
|
||||
@@ -207,6 +210,13 @@ GENESIS_GV70_UNWIND_FF_JERK = 0.10
|
||||
GENESIS_GV70_UNWIND_FF_JERK_WIDTH = 0.10
|
||||
GENESIS_GV70_UNWIND_FF_SPEED = 10.0 * CV.MPH_TO_MS
|
||||
GENESIS_GV70_UNWIND_FF_SPEED_WIDTH = 4.0 * CV.MPH_TO_MS
|
||||
GENESIS_GV70_HIGH_SPEED_ERROR_DAMPING_MAX = 0.18
|
||||
GENESIS_GV70_HIGH_SPEED_ERROR_DAMPING_SPEED = 50.0 * CV.MPH_TO_MS
|
||||
GENESIS_GV70_HIGH_SPEED_ERROR_DAMPING_SPEED_WIDTH = 8.0 * CV.MPH_TO_MS
|
||||
GENESIS_GV70_HIGH_SPEED_ERROR_DAMPING_ERROR = 0.18
|
||||
GENESIS_GV70_HIGH_SPEED_ERROR_DAMPING_ERROR_WIDTH = 0.15
|
||||
GENESIS_GV70_HIGH_SPEED_ERROR_DAMPING_JERK = 0.15
|
||||
GENESIS_GV70_HIGH_SPEED_ERROR_DAMPING_JERK_WIDTH = 0.10
|
||||
|
||||
GENESIS_G70_FRICTION_THRESHOLD_GAIN = 0.10
|
||||
GENESIS_G70_FRICTION_SPEED_ONSET = 10.0
|
||||
@@ -240,7 +250,7 @@ GENESIS_G70_LOW_SPEED_ANGLE_DAMPING_ERROR_WIDTH = 3.0
|
||||
GENESIS_G70_LOW_SPEED_ANGLE_DAMPING_ACTUAL = 8.0
|
||||
GENESIS_G70_LOW_SPEED_ANGLE_DAMPING_ACTUAL_WIDTH = 4.0
|
||||
GENESIS_G70_LOW_SPEED_ANGLE_DAMPING_BLEND = 0.50
|
||||
GENESIS_G70_LOW_SPEED_OUTPUT_LIMIT_REDUCTION = 0.65
|
||||
GENESIS_G70_LOW_SPEED_OUTPUT_LIMIT_REDUCTION = 0.85
|
||||
GENESIS_G70_LOW_SPEED_OUTPUT_LIMIT_LAT = 0.14
|
||||
GENESIS_G70_LOW_SPEED_OUTPUT_LIMIT_LAT_WIDTH = 0.05
|
||||
GENESIS_G70_LOW_SPEED_OUTPUT_LIMIT_SPEED = 6.0
|
||||
@@ -394,6 +404,14 @@ SILVERADO_CENTER_TAPER_LAT_WIDTH = 0.05
|
||||
SILVERADO_CENTER_TAPER_SPEED = 12.0
|
||||
SILVERADO_CENTER_TAPER_SPEED_WIDTH = 2.5
|
||||
|
||||
GMC_YUKON_CC_PHASE_SCALE = 0.14
|
||||
GMC_YUKON_CC_PHASE_SPEED_ONSET = 12.0
|
||||
GMC_YUKON_CC_PHASE_SPEED_FULL = 30.0
|
||||
GMC_YUKON_CC_PHASE_LAT_ONSET = 0.35
|
||||
GMC_YUKON_CC_PHASE_LAT_WIDTH = 0.18
|
||||
GMC_YUKON_CC_TURN_IN_FF_BOOST = 0.08
|
||||
GMC_YUKON_CC_UNWIND_FF_REDUCTION = 0.12
|
||||
|
||||
SONATA_HYBRID_BASE_LAT_ACCEL_FACTOR_MULT = 1.05
|
||||
SONATA_HYBRID_FF_REDUCTION_LEFT = 0.09
|
||||
SONATA_HYBRID_FF_REDUCTION_RIGHT = 0.22
|
||||
@@ -417,7 +435,7 @@ SONATA_HYBRID_LOW_SPEED_CENTER_TAPER_LAT = 0.10
|
||||
SONATA_HYBRID_LOW_SPEED_CENTER_TAPER_LAT_WIDTH = 0.02
|
||||
SONATA_HYBRID_LOW_SPEED_CENTER_TAPER_SPEED_MAX = 7.5
|
||||
SONATA_HYBRID_LOW_SPEED_CENTER_TAPER_SPEED_WIDTH = 1.0
|
||||
SONATA_HYBRID_CENTER_OUTPUT_TAPER_MAX = 0.06
|
||||
SONATA_HYBRID_CENTER_OUTPUT_TAPER_MAX = 0.08
|
||||
SONATA_HYBRID_CENTER_OUTPUT_TAPER_LAT = 0.18
|
||||
SONATA_HYBRID_CENTER_OUTPUT_TAPER_LAT_WIDTH = 0.05
|
||||
SONATA_HYBRID_CENTER_OUTPUT_TAPER_SPEED = 12.5
|
||||
@@ -817,7 +835,7 @@ IONIQ_6_FRICTION_CENTER_FADE_SPEED_WIDTH = 2.5
|
||||
# Newer Ioniq 6 highway center-chatter correction; activation is firmware-gated.
|
||||
IONIQ_6_2025_FRICTION_SCALE_MULT = 0.80
|
||||
IONIQ_6_2025_FRICTION_JERK_DEADZONE = 0.45
|
||||
IONIQ_6_2025_CENTER_OUTPUT_TAPER_MAX = 0.28
|
||||
IONIQ_6_2025_CENTER_OUTPUT_TAPER_MAX = 0.32
|
||||
IONIQ_6_2025_CENTER_OUTPUT_TAPER_LAT = 0.35
|
||||
IONIQ_6_2025_CENTER_OUTPUT_TAPER_LAT_WIDTH = 0.10
|
||||
IONIQ_6_2025_CENTER_OUTPUT_TAPER_SPEED = 22.0
|
||||
@@ -830,6 +848,11 @@ IONIQ_6_2025_LOW_SPEED_CENTER_LAT = 0.22
|
||||
IONIQ_6_2025_LOW_SPEED_CENTER_LAT_WIDTH = 0.10
|
||||
IONIQ_6_2025_LOW_SPEED_CENTER_JERK = 0.30
|
||||
IONIQ_6_2025_LOW_SPEED_CENTER_JERK_WIDTH = 0.13
|
||||
IONIQ_6_2025_LOW_SPEED_OUTPUT_LIMIT_BASE = 0.22
|
||||
IONIQ_6_2025_LOW_SPEED_OUTPUT_LIMIT_TURN_RELIEF = 0.50
|
||||
IONIQ_6_2025_LOW_SPEED_OUTPUT_LIMIT_SPEED_RELIEF = 0.20
|
||||
IONIQ_6_2025_LOW_SPEED_OUTPUT_LIMIT_SPEED = 6.5
|
||||
IONIQ_6_2025_LOW_SPEED_OUTPUT_LIMIT_SPEED_WIDTH = 1.5
|
||||
IONIQ_6_HEAVY_DIRECTIONAL_TAPER_LAT_START = 0.90
|
||||
IONIQ_6_HEAVY_DIRECTIONAL_TAPER_LAT_WIDTH = 0.18
|
||||
IONIQ_6_HEAVY_DIRECTIONAL_TAPER_BASE_LEFT = 0.03
|
||||
@@ -851,7 +874,7 @@ KIA_EV6_FF_CUTOFF = 1.90
|
||||
KIA_EV6_FF_CUTOFF_WIDTH = 0.40
|
||||
KIA_EV6_TRANSITION_SPEED = 14.5
|
||||
KIA_EV6_PHASE_SCALE = 0.09
|
||||
KIA_EV6_TURN_IN_BOOST_LEFT = 0.62
|
||||
KIA_EV6_TURN_IN_BOOST_LEFT = 0.54
|
||||
KIA_EV6_TURN_IN_BOOST_RIGHT = 0.60
|
||||
KIA_EV6_UNWIND_TAPER_LEFT = 0.56
|
||||
KIA_EV6_UNWIND_TAPER_RIGHT = 0.54
|
||||
@@ -892,7 +915,7 @@ KIA_EV6_LOW_SPEED_CENTER_TAPER_LAT = 0.08
|
||||
KIA_EV6_LOW_SPEED_CENTER_TAPER_LAT_WIDTH = 0.02
|
||||
KIA_EV6_LOW_SPEED_CENTER_TAPER_SPEED_MAX = 8.5
|
||||
KIA_EV6_LOW_SPEED_CENTER_TAPER_SPEED_WIDTH = 1.4
|
||||
KIA_EV6_CENTER_OUTPUT_TAPER_MAX = 0.12
|
||||
KIA_EV6_CENTER_OUTPUT_TAPER_MAX = 0.14
|
||||
KIA_EV6_CENTER_OUTPUT_TAPER_LAT = 0.30
|
||||
KIA_EV6_CENTER_OUTPUT_TAPER_LAT_WIDTH = 0.08
|
||||
KIA_EV6_CENTER_OUTPUT_TAPER_SPEED = 12.0
|
||||
@@ -1535,6 +1558,20 @@ def get_ram_1500_ff_scale(desired_lateral_accel: float, desired_lateral_jerk: fl
|
||||
RAM_1500_UNWIND_FF_REDUCTION * unwind_weight) * speed_weight * lat_weight)
|
||||
|
||||
|
||||
def get_gmc_yukon_cc_ff_scale(desired_lateral_accel: float, desired_lateral_jerk: float, v_ego: float) -> float:
|
||||
"""Add turn-in authority and soften the high-speed unwind transient on Yukon CC."""
|
||||
phase = math.tanh((desired_lateral_accel * desired_lateral_jerk) / GMC_YUKON_CC_PHASE_SCALE)
|
||||
turn_in_weight = max(phase, 0.0)
|
||||
unwind_weight = max(-phase, 0.0)
|
||||
speed_weight = float(np.interp(v_ego,
|
||||
[GMC_YUKON_CC_PHASE_SPEED_ONSET, GMC_YUKON_CC_PHASE_SPEED_FULL],
|
||||
[0.0, 1.0]))
|
||||
lat_weight = _sigmoid((abs(desired_lateral_accel) - GMC_YUKON_CC_PHASE_LAT_ONSET) /
|
||||
GMC_YUKON_CC_PHASE_LAT_WIDTH)
|
||||
return 1.0 + ((GMC_YUKON_CC_TURN_IN_FF_BOOST * turn_in_weight -
|
||||
GMC_YUKON_CC_UNWIND_FF_REDUCTION * unwind_weight) * speed_weight * lat_weight)
|
||||
|
||||
|
||||
def get_kona_non_scc_highway_transition_output_scale(desired_lateral_accel: float, desired_lateral_jerk: float,
|
||||
v_ego: float) -> float:
|
||||
speed_weight = float(np.interp(v_ego, [KONA_NON_SCC_TRANSITION_SPEED_ONSET, KONA_NON_SCC_TRANSITION_SPEED_FULL], [0.0, 1.0]))
|
||||
@@ -2621,6 +2658,23 @@ def get_genesis_gv70_unwind_ff_scale(setpoint: float, measured_lateral_accel: fl
|
||||
return 1.0 - GENESIS_GV70_UNWIND_FF_REDUCTION_MAX * overshoot_weight * jerk_weight * speed_weight
|
||||
|
||||
|
||||
def get_genesis_gv70_high_speed_error_scale(setpoint: float, measured_lateral_accel: float,
|
||||
desired_lateral_jerk: float, v_ego: float) -> float:
|
||||
tracking_error = abs(measured_lateral_accel - setpoint)
|
||||
if tracking_error <= 0.0:
|
||||
return 1.0
|
||||
speed_weight = _sigmoid((v_ego - GENESIS_GV70_HIGH_SPEED_ERROR_DAMPING_SPEED) /
|
||||
GENESIS_GV70_HIGH_SPEED_ERROR_DAMPING_SPEED_WIDTH)
|
||||
error_weight = _sigmoid((tracking_error - GENESIS_GV70_HIGH_SPEED_ERROR_DAMPING_ERROR) /
|
||||
GENESIS_GV70_HIGH_SPEED_ERROR_DAMPING_ERROR_WIDTH)
|
||||
jerk_weight = _sigmoid((abs(desired_lateral_jerk) - GENESIS_GV70_HIGH_SPEED_ERROR_DAMPING_JERK) /
|
||||
GENESIS_GV70_HIGH_SPEED_ERROR_DAMPING_JERK_WIDTH)
|
||||
phase_weight = 1.0 if setpoint * desired_lateral_jerk < 0.0 else 0.45
|
||||
reduction = (GENESIS_GV70_HIGH_SPEED_ERROR_DAMPING_MAX * speed_weight * error_weight *
|
||||
(0.35 + (0.65 * jerk_weight)) * phase_weight)
|
||||
return 1.0 - reduction
|
||||
|
||||
|
||||
def get_genesis_g70_friction_threshold(v_ego: float, desired_lateral_accel: float = 0.0,
|
||||
desired_lateral_jerk: float = 0.0) -> float:
|
||||
base_threshold = get_standard_friction_threshold(v_ego)
|
||||
@@ -2993,6 +3047,22 @@ def get_ioniq_6_2025_center_output_scale(desired_lateral_accel: float, v_ego: fl
|
||||
return 1.0 - IONIQ_6_2025_CENTER_OUTPUT_TAPER_MAX * speed_weight * center_weight
|
||||
|
||||
|
||||
def get_ioniq_6_2025_low_speed_output_limit(desired_lateral_accel: float,
|
||||
desired_lateral_jerk: float, v_ego: float) -> float:
|
||||
"""Limit small-signal torque at crawl speed while leaving real turn commands open."""
|
||||
speed_weight = _ioniq_6_sigmoid((IONIQ_6_2025_LOW_SPEED_OUTPUT_LIMIT_SPEED - max(v_ego, 0.0)) /
|
||||
IONIQ_6_2025_LOW_SPEED_OUTPUT_LIMIT_SPEED_WIDTH)
|
||||
center_weight = _ioniq_6_sigmoid((IONIQ_6_2025_LOW_SPEED_CENTER_LAT - abs(desired_lateral_accel)) /
|
||||
IONIQ_6_2025_LOW_SPEED_CENTER_LAT_WIDTH)
|
||||
calm_weight = _ioniq_6_sigmoid((IONIQ_6_2025_LOW_SPEED_CENTER_JERK - abs(desired_lateral_jerk)) /
|
||||
IONIQ_6_2025_LOW_SPEED_CENTER_JERK_WIDTH)
|
||||
center_weight *= calm_weight
|
||||
limit = (IONIQ_6_2025_LOW_SPEED_OUTPUT_LIMIT_BASE +
|
||||
IONIQ_6_2025_LOW_SPEED_OUTPUT_LIMIT_TURN_RELIEF * (1.0 - center_weight) +
|
||||
IONIQ_6_2025_LOW_SPEED_OUTPUT_LIMIT_SPEED_RELIEF * (1.0 - speed_weight))
|
||||
return float(np.clip(limit, IONIQ_6_2025_LOW_SPEED_OUTPUT_LIMIT_BASE, 1.0))
|
||||
|
||||
|
||||
def _ioniq_6_2025_low_speed_center_envelope(desired_lateral_accel: float,
|
||||
desired_lateral_jerk: float, v_ego: float) -> float:
|
||||
speed_weight = _ioniq_6_sigmoid((IONIQ_6_2025_LOW_SPEED_CENTER_SPEED - max(v_ego, 0.0)) /
|
||||
|
||||
@@ -264,6 +264,9 @@ class LongControl:
|
||||
if output_accel > starpilot_toggles.stopAccel:
|
||||
output_accel = min(output_accel, 0.0)
|
||||
output_accel -= starpilot_toggles.stoppingDecelRate * DT_CTRL
|
||||
output_accel = self.vehicle_tuning.shape_stopping_accel(
|
||||
output_accel, a_target, should_stop, CS.vEgo, has_lead, starpilot_toggles.stopAccel,
|
||||
)
|
||||
output_accel = self._apply_moving_stop_target_follow(output_accel, a_target, should_stop, CS, starpilot_toggles)
|
||||
self.reset(preserve_stop_release=True)
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ GM_TRUCK_TARGET_FILTER_DOWN_TAU = 0.06
|
||||
GM_TRUCK_TARGET_FILTER_BRAKE_BYPASS = -0.65
|
||||
GM_TRUCK_TARGET_FILTER_DROP_BYPASS = 0.45
|
||||
TOYOTA_SIENNA_TARGET_FILTER_MIN_SPEED = 12.0
|
||||
TOYOTA_SIENNA_TARGET_FILTER_UP_TAU = 0.18
|
||||
TOYOTA_SIENNA_TARGET_FILTER_UP_TAU = 0.32
|
||||
TOYOTA_SIENNA_TARGET_FILTER_DOWN_TAU = 0.24
|
||||
TOYOTA_SIENNA_LOW_SPEED_ACCEL_UP_TAU = 0.35
|
||||
TOYOTA_SIENNA_TARGET_FILTER_BRAKE_BYPASS = -0.75
|
||||
@@ -47,6 +47,7 @@ VOLT_CRUISE_INTEGRATOR_ERROR_MAX = 0.12
|
||||
VOLT_CRUISE_INTEGRATOR_LEAK = 0.995
|
||||
SUBARU_IMPREZA_STOP_RELEASE_TIME = 0.75
|
||||
SUBARU_IMPREZA_STOP_RELEASE_MAX_ACCEL = 0.8
|
||||
HYUNDAI_ELANTRA_STOPPING_HOLD_TARGET_GAP = 0.25
|
||||
|
||||
|
||||
def get_bolt_acc_pedal_friction_bias(output_accel, a_target, v_ego):
|
||||
@@ -123,6 +124,9 @@ class LongControlVehicleTuning:
|
||||
CP.brand == "subaru" and
|
||||
getattr(CP, "carFingerprint", None) == SUBARU_CAR.SUBARU_IMPREZA_2020
|
||||
)
|
||||
self.is_hyundai_elantra_2021 = bool(
|
||||
CP.brand == "hyundai" and str(getattr(CP, "carFingerprint", "")) == "HYUNDAI_ELANTRA_2021"
|
||||
)
|
||||
self.is_bolt_acc_pedal_friction_car = bool(
|
||||
CP.brand == "gm" and
|
||||
CP.enableGasInterceptorDEPRECATED and
|
||||
@@ -143,6 +147,16 @@ class LongControlVehicleTuning:
|
||||
self.bolt_start_handoff_frames = 0
|
||||
self.subaru_stop_release_frames = 0
|
||||
|
||||
def shape_stopping_accel(self, output_accel, a_target, should_stop, v_ego, has_lead, stop_accel):
|
||||
"""Release a stale hard lead brake once the stop target has eased."""
|
||||
if (
|
||||
not self.is_hyundai_elantra_2021 or
|
||||
not has_lead or not should_stop or v_ego > 2.0 or
|
||||
a_target <= stop_accel - HYUNDAI_ELANTRA_STOPPING_HOLD_TARGET_GAP
|
||||
):
|
||||
return output_accel
|
||||
return max(float(output_accel), float(stop_accel))
|
||||
|
||||
def cap_subaru_stop_release_accel(self, output_accel, stopping_handoff, should_stop):
|
||||
"""Prevent an Impreza stop-sign handoff from stepping straight into full throttle."""
|
||||
if not self.is_subaru_impreza_2020:
|
||||
|
||||
@@ -38,6 +38,7 @@ from openpilot.selfdrive.controls.lib.latcontrol_vehicle_tunes import (
|
||||
KIA_FORTE_BASE_LAT_ACCEL_FACTOR_MULT,
|
||||
RAM_1500_BASE_LAT_ACCEL_FACTOR_MULT,
|
||||
RAM_1500_MAX_LAT_JERK_UP,
|
||||
get_gmc_yukon_cc_ff_scale,
|
||||
get_ram_1500_transition_output_scale,
|
||||
get_ram_1500_ff_scale,
|
||||
get_subaru_impreza_pid_output_scale,
|
||||
@@ -78,6 +79,7 @@ from openpilot.selfdrive.controls.lib.latcontrol_torque import (
|
||||
get_genesis_g70_low_speed_angle_damping,
|
||||
get_genesis_g70_low_speed_output_limit,
|
||||
get_genesis_gv70_friction_threshold,
|
||||
get_genesis_gv70_high_speed_error_scale,
|
||||
get_genesis_gv70_unwind_ff_scale,
|
||||
get_elantra_non_scc_ff_scale,
|
||||
get_palisade_ff_scale,
|
||||
@@ -116,6 +118,7 @@ from openpilot.selfdrive.controls.lib.latcontrol_torque import (
|
||||
get_ioniq_6_friction_threshold,
|
||||
get_ioniq_6_low_speed_angle_assist_torque,
|
||||
get_ioniq_6_2025_center_output_scale,
|
||||
get_ioniq_6_2025_low_speed_output_limit,
|
||||
is_ioniq_6_2025_model,
|
||||
get_kia_forte_center_taper_scale,
|
||||
get_kia_forte_ff_scale,
|
||||
@@ -793,6 +796,12 @@ class TestLatControl:
|
||||
assert highway_turn == pytest.approx(base, rel=0.01)
|
||||
assert highway_center < center
|
||||
|
||||
def test_genesis_gv70_high_speed_error_damping(self):
|
||||
assert get_genesis_gv70_high_speed_error_scale(0.2, 0.2, 0.8, 20.0) == 1.0
|
||||
assert get_genesis_gv70_high_speed_error_scale(-0.7, 0.58, -0.8, 33.5) < 1.0
|
||||
assert get_genesis_gv70_high_speed_error_scale(-0.7, 0.58, -0.8, 20.0) > \
|
||||
get_genesis_gv70_high_speed_error_scale(-0.7, 0.58, -0.8, 33.5)
|
||||
|
||||
def test_genesis_g70_center_chatter_tune(self):
|
||||
base = get_standard_friction_threshold(25.0)
|
||||
center = get_genesis_g70_friction_threshold(25.0, 0.0, 0.0)
|
||||
@@ -808,6 +817,7 @@ class TestLatControl:
|
||||
assert get_genesis_g70_center_output_scale(0.0, 0.0) < get_genesis_g70_center_output_scale(0.0, 10.0)
|
||||
assert get_genesis_g70_low_speed_output_limit(0.0, 2.0) < get_genesis_g70_low_speed_output_limit(0.5, 2.0)
|
||||
assert get_genesis_g70_low_speed_output_limit(0.0, 2.0) < get_genesis_g70_low_speed_output_limit(0.0, 10.0)
|
||||
assert get_genesis_g70_low_speed_output_limit(0.0, 2.0) < 0.30
|
||||
assert get_genesis_g70_low_speed_angle_damping(0.0, -20.0, 0.0, 2.0) < 0.0
|
||||
assert get_genesis_g70_low_speed_angle_damping(0.0, 20.0, 0.0, 2.0) > 0.0
|
||||
assert get_genesis_g70_curve_unwind_output_scale(0.7, -0.5, 25.0) > 1.0
|
||||
@@ -958,6 +968,29 @@ class TestLatControl:
|
||||
assert get_ram_1500_ff_scale(1.2, -1.1, 17.0) < 1.0
|
||||
assert get_ram_1500_ff_scale(1.2, 1.1, 6.0) < get_ram_1500_ff_scale(1.2, 1.1, 17.0)
|
||||
|
||||
def test_gmc_yukon_cc_phase_feedforward_curve(self):
|
||||
assert get_gmc_yukon_cc_ff_scale(0.0, 1.0, 30.0) == pytest.approx(1.0)
|
||||
assert get_gmc_yukon_cc_ff_scale(1.2, 1.1, 30.0) > 1.0
|
||||
assert get_gmc_yukon_cc_ff_scale(1.2, -1.1, 30.0) < 1.0
|
||||
assert get_gmc_yukon_cc_ff_scale(1.2, 1.1, 8.0) < get_gmc_yukon_cc_ff_scale(1.2, 1.1, 30.0)
|
||||
|
||||
def test_gmc_yukon_cc_phase_feedforward_update_path(self, monkeypatch):
|
||||
controller, VM, CS, params, starpilot_toggles = self._build_torque_controller(GM.GMC_YUKON_CC)
|
||||
CS.vEgo = 25.0
|
||||
base_output, _, _ = controller.update(
|
||||
True, CS, VM, params, False, 0.0025, False, 0.2, None, None, starpilot_toggles,
|
||||
)
|
||||
|
||||
monkeypatch.setattr(latcontrol_torque, "get_gmc_yukon_cc_ff_scale", lambda *_args: 0.5)
|
||||
tuned_controller, tuned_VM, tuned_CS, tuned_params, tuned_toggles = self._build_torque_controller(GM.GMC_YUKON_CC)
|
||||
tuned_CS.vEgo = 25.0
|
||||
tuned_output, _, _ = tuned_controller.update(
|
||||
True, tuned_CS, tuned_VM, tuned_params, False, 0.0025, False, 0.2, None, None, tuned_toggles,
|
||||
)
|
||||
|
||||
assert controller.is_gmc_yukon_cc
|
||||
assert tuned_output != pytest.approx(base_output)
|
||||
|
||||
def test_ram_1500_jerk_limit_update_path(self):
|
||||
controller, VM, CS, params, starpilot_toggles = self._build_torque_controller(CHRYSLER.RAM_1500_5TH_GEN)
|
||||
jerk_samples = []
|
||||
@@ -1197,6 +1230,8 @@ class TestLatControl:
|
||||
assert low_speed_center_error < low_speed_turn_error
|
||||
assert low_speed_center_error < high_speed_center_error
|
||||
assert low_speed_center_friction < 1.0
|
||||
assert get_ioniq_6_2025_low_speed_output_limit(0.02, 0.05, 2.5) < get_ioniq_6_2025_low_speed_output_limit(0.60, 0.80, 2.5)
|
||||
assert get_ioniq_6_2025_low_speed_output_limit(0.02, 0.05, 2.5) < get_ioniq_6_2025_low_speed_output_limit(0.02, 0.05, 8.0)
|
||||
|
||||
def test_ioniq_6_center_taper_curve(self):
|
||||
assert get_ioniq_6_center_taper_scale(0.0, 10.0) > get_ioniq_6_center_taper_scale(0.0, 30.0)
|
||||
@@ -1418,6 +1453,17 @@ class TestLatControl:
|
||||
assert controller.torque_params.latAccelFactor == pytest.approx(3.0 * 1.22)
|
||||
assert controller.low_speed_reset_threshold == pytest.approx(0.1 * 0.44704)
|
||||
|
||||
def test_ioniq_6_2025_low_speed_output_limit_update_path(self, monkeypatch):
|
||||
monkeypatch.setattr(latcontrol_torque, "get_ioniq_6_2025_low_speed_output_limit", lambda *_args: 0.05)
|
||||
controller, VM, CS, params, starpilot_toggles = self._build_torque_controller(HYUNDAI.HYUNDAI_IONIQ_6)
|
||||
controller.is_ioniq_6_2025 = True
|
||||
CS.vEgo = 3.2
|
||||
|
||||
output, _, lac_log = controller.update(True, CS, VM, params, False, 0.0025, False, 0.2, None, None, starpilot_toggles)
|
||||
|
||||
assert lac_log.active
|
||||
assert abs(output) <= 0.05
|
||||
|
||||
def test_elantra_non_scc_default_update_path(self):
|
||||
controller, VM, CS, params, starpilot_toggles = self._build_torque_controller(HYUNDAI.HYUNDAI_ELANTRA_HEV_2022_NON_SCC)
|
||||
CarInterface = interfaces[HYUNDAI.HYUNDAI_ELANTRA_HEV_2022_NON_SCC]
|
||||
@@ -1807,7 +1853,7 @@ class TestLatControl:
|
||||
def test_kia_ev6_center_output_taper_curve(self):
|
||||
assert get_kia_ev6_center_output_scale(0.0, 10.0) > get_kia_ev6_center_output_scale(0.0, 20.0)
|
||||
assert get_kia_ev6_center_output_scale(0.0, 20.0) < get_kia_ev6_center_output_scale(0.5, 20.0)
|
||||
assert get_kia_ev6_center_output_scale(0.0, 20.0) > 0.87
|
||||
assert get_kia_ev6_center_output_scale(0.0, 20.0) > 0.86
|
||||
|
||||
def test_kia_ev6_center_output_taper_update_path(self, monkeypatch):
|
||||
controller, VM, CS, params, starpilot_toggles = self._build_torque_controller(HYUNDAI.KIA_EV6)
|
||||
|
||||
@@ -755,6 +755,15 @@ def test_stopping_state_follows_stronger_moving_stop_target():
|
||||
assert output_accel < -1.43
|
||||
|
||||
|
||||
def test_elantra_lead_stop_releases_stale_hard_brake_after_target_eases():
|
||||
CP = make_longcontrol_cp(brand="hyundai", carFingerprint="HYUNDAI_ELANTRA_2021")
|
||||
tuning = vehicle_tunes.LongControlVehicleTuning(CP)
|
||||
|
||||
assert tuning.shape_stopping_accel(-1.20, -0.25, True, 1.0, True, -0.85) == pytest.approx(-0.85)
|
||||
assert tuning.shape_stopping_accel(-1.20, -1.50, True, 1.0, True, -0.85) == pytest.approx(-1.20)
|
||||
assert tuning.shape_stopping_accel(-1.20, -0.25, True, 1.0, False, -0.85) == pytest.approx(-1.20)
|
||||
|
||||
|
||||
def test_volt_testing_ground_handoff_freezes_integrator(monkeypatch):
|
||||
CP = car.CarParams.new_message()
|
||||
CP.brand = "gm"
|
||||
@@ -1177,6 +1186,16 @@ def test_toyota_sienna_target_filter_smooths_mild_high_speed_handoffs():
|
||||
assert -0.20 < filtered < 0.30
|
||||
|
||||
|
||||
def test_toyota_sienna_target_filter_unwinds_braking_before_acceleration():
|
||||
CP = make_longcontrol_cp(brand="toyota", carFingerprint=TOYOTA_CAR.TOYOTA_SIENNA_4TH_GEN)
|
||||
tuning = vehicle_tunes.LongControlVehicleTuning(CP)
|
||||
|
||||
tuning.shape_toyota_sienna_accel_target(-1.2, 20.0, False)
|
||||
recovering = tuning.shape_toyota_sienna_accel_target(1.2, 20.0, False)
|
||||
|
||||
assert recovering == pytest.approx(-1.1272727273)
|
||||
|
||||
|
||||
def test_toyota_sienna_target_filter_ramps_low_speed_acceleration():
|
||||
CP = make_longcontrol_cp(brand="toyota", carFingerprint=TOYOTA_CAR.TOYOTA_SIENNA_4TH_GEN)
|
||||
tuning = vehicle_tunes.LongControlVehicleTuning(CP)
|
||||
|
||||
@@ -10,6 +10,7 @@ from openpilot.starpilot.controls.lib.starpilot_vcruise import (
|
||||
FORCE_STOP_TURN_VETO_STOP_SEEN_HOLD_TIME,
|
||||
StarPilotVCruise,
|
||||
get_active_slc_control_target,
|
||||
get_lead_veto_distance,
|
||||
get_slc_lead_drop_relaxed_target,
|
||||
)
|
||||
from types import SimpleNamespace
|
||||
@@ -110,6 +111,11 @@ def test_active_slc_control_target_does_not_require_set_speed_limit():
|
||||
assert target == pytest.approx((48.0 * CV.MPH_TO_MS) - 0.4)
|
||||
|
||||
|
||||
def test_elantra_gets_lead_veto_margin_before_force_stop():
|
||||
assert get_lead_veto_distance(SimpleNamespace(carFingerprint="HYUNDAI_ELANTRA_2021")) == pytest.approx(90.0)
|
||||
assert get_lead_veto_distance(SimpleNamespace(carFingerprint="OTHER_CAR")) == pytest.approx(75.0)
|
||||
|
||||
|
||||
def test_curve_speed_controller_holds_target_through_brief_detector_dropout():
|
||||
planner, vcruise = make_vcruise()
|
||||
sm = make_sm(standstill=False)
|
||||
|
||||
@@ -23,28 +23,14 @@ def test_out_of_band_artifact_round_trip():
|
||||
np.testing.assert_array_equal(restored["weights"], artifact["weights"])
|
||||
|
||||
|
||||
def test_external_gpu_probe_retries_until_pcie_is_ready(monkeypatch):
|
||||
def test_external_gpu_probe_matches_upstream_retry_loop(monkeypatch):
|
||||
from openpilot.system.hardware.chestnut import flash
|
||||
|
||||
calls = []
|
||||
probe_count = 0
|
||||
def probe():
|
||||
nonlocal probe_count
|
||||
probe_count += 1
|
||||
calls.append("probe")
|
||||
return (False, "LTSSM=0x00") if probe_count < 3 else (True, "LTSSM=0x78")
|
||||
monkeypatch.setattr(
|
||||
model_compiler,
|
||||
"_probe_external_gpu_link_once",
|
||||
probe,
|
||||
)
|
||||
results = iter((False, False, True))
|
||||
monkeypatch.setattr(flash, "link_up", lambda: calls.append("probe") or next(results))
|
||||
monkeypatch.setattr(model_compiler.time, "sleep", lambda seconds: calls.append(("sleep", seconds)))
|
||||
|
||||
model_compiler.wait_for_external_gpu({"PYTHONPATH": "/tmp/openpilot"})
|
||||
model_compiler.wait_for_external_gpu()
|
||||
|
||||
assert calls == ["probe", ("sleep", 1), "probe", ("sleep", 1), "probe"]
|
||||
|
||||
|
||||
def test_external_gpu_probe_reports_failure(monkeypatch):
|
||||
monkeypatch.setattr(model_compiler, "_probe_external_gpu_link_once", lambda: (False, "link unavailable"))
|
||||
monkeypatch.setattr(model_compiler.time, "sleep", lambda _: None)
|
||||
|
||||
assert model_compiler.wait_for_external_gpu({}) is False
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import importlib.util
|
||||
from pathlib import Path
|
||||
import re
|
||||
import subprocess
|
||||
|
||||
import pytest
|
||||
|
||||
@@ -10,6 +12,19 @@ assert SPEC is not None and SPEC.loader is not None
|
||||
PANDAD = importlib.util.module_from_spec(SPEC)
|
||||
SPEC.loader.exec_module(PANDAD)
|
||||
|
||||
REPO_ROOT = PANDAD_PATH.parents[2]
|
||||
PANDA_H7_FIRMWARE = REPO_ROOT / "panda/board/obj/panda_h7.bin.signed"
|
||||
PANDA_FIRMWARE_SOURCE_PREFIXES = (
|
||||
"opendbc_repo/opendbc/safety/",
|
||||
"panda/board/",
|
||||
"panda/crypto/",
|
||||
"panda/drivers/",
|
||||
)
|
||||
PANDA_FIRMWARE_SOURCE_EXCLUSIONS = (
|
||||
"opendbc_repo/opendbc/safety/tests/",
|
||||
"panda/board/obj/",
|
||||
)
|
||||
|
||||
|
||||
class FakeParams:
|
||||
def __init__(self, ignore_ignition_line):
|
||||
@@ -26,3 +41,26 @@ class FakeParams:
|
||||
])
|
||||
def test_ignore_ignition_line_follows_toggle(enabled, expected):
|
||||
assert PANDAD.get_ignore_ignition_line(FakeParams(enabled)) == expected
|
||||
|
||||
|
||||
def test_tracked_panda_firmware_includes_current_safety_sources():
|
||||
version_match = re.search(rb"DEV-([0-9a-f]{8})-DEBUG", PANDA_H7_FIRMWARE.read_bytes())
|
||||
assert version_match is not None
|
||||
firmware_commit = version_match.group(1).decode()
|
||||
|
||||
try:
|
||||
changed_files = subprocess.check_output(
|
||||
["git", "diff", "--name-only", f"{firmware_commit}..HEAD", "--", *PANDA_FIRMWARE_SOURCE_PREFIXES],
|
||||
cwd=REPO_ROOT,
|
||||
text=True,
|
||||
).splitlines()
|
||||
changed_files += subprocess.check_output(
|
||||
["git", "diff", "--name-only", "HEAD", "--", *PANDA_FIRMWARE_SOURCE_PREFIXES],
|
||||
cwd=REPO_ROOT,
|
||||
text=True,
|
||||
).splitlines()
|
||||
except subprocess.CalledProcessError:
|
||||
pytest.skip("firmware source commit is unavailable in this checkout")
|
||||
|
||||
stale_sources = sorted({path for path in changed_files if not path.startswith(PANDA_FIRMWARE_SOURCE_EXCLUSIONS)})
|
||||
assert stale_sources == [], f"panda firmware must be rebuilt after changing: {stale_sources}"
|
||||
|
||||
@@ -12,6 +12,7 @@ from msgq.visionipc import VisionIpcClient, VisionStreamType
|
||||
|
||||
from opendbc.car.chrysler.values import pacifica_hybrid_aol_stock_acc_mode
|
||||
from opendbc.car.gm.values import GMFlags
|
||||
from opendbc.car.hyundai.values import CAR as HYUNDAI_CAR
|
||||
|
||||
from openpilot.common.params import Params
|
||||
from openpilot.common.realtime import config_realtime_process, Priority, Ratekeeper, DT_CTRL
|
||||
@@ -59,7 +60,8 @@ IGNORED_SAFETY_MODES = (SafetyModel.silent, SafetyModel.noOutput)
|
||||
def commanded_torque_at_max_for_saturation(CP, output: float) -> bool:
|
||||
torque_controller = (CP.steerControlType == car.CarParams.SteerControlType.torque and
|
||||
CP.lateralTuning.which() == "torque")
|
||||
return torque_controller and abs(output) > 0.99
|
||||
has_controller_grace = CP.carFingerprint == HYUNDAI_CAR.GENESIS_GV70_ELECTRIFIED_1ST_GEN
|
||||
return torque_controller and not has_controller_grace and abs(output) > 0.99
|
||||
|
||||
|
||||
def should_loud_blindspot_alert_without_lateral(CS, sm, starpilot_toggles, combined_left_bsm=None, combined_right_bsm=None) -> bool:
|
||||
@@ -94,6 +96,11 @@ def get_starpilot_alert_filters(current_alert_types: list[str], clear_event_type
|
||||
starpilot_alert_types = list(current_alert_types)
|
||||
starpilot_clear_event_types = set(clear_event_types)
|
||||
|
||||
if int(StarPilotEventName.lkasEnable) in starpilot_events.names:
|
||||
if ET.WARNING not in starpilot_alert_types:
|
||||
starpilot_alert_types.append(ET.WARNING)
|
||||
starpilot_clear_event_types.discard(ET.WARNING)
|
||||
|
||||
# This alert is explicitly allowed while lateral is paused/off. The state
|
||||
# machine only exposes WARNING while active/AOL, so let this warning through.
|
||||
if StarPilotEventName.laneChangeBlockedLoud in starpilot_events.names:
|
||||
|
||||
@@ -99,6 +99,21 @@ def test_loud_blindspot_alert_survives_disabled_warning_filter():
|
||||
assert alert_manager.current_alert.alert_type == "laneChangeBlockedLoud/warning"
|
||||
|
||||
|
||||
def test_lkas_enable_sound_survives_disabled_warning_filter():
|
||||
events = Events(starpilot=True)
|
||||
events.add(StarPilotEventName.lkasEnable)
|
||||
|
||||
alert_types, clear_event_types = get_starpilot_alert_filters([ET.PERMANENT], {ET.WARNING}, events)
|
||||
|
||||
alerts = events.create_alerts(alert_types)
|
||||
alert_manager = AlertManager()
|
||||
alert_manager.add_many(0, alerts)
|
||||
alert_manager.process_alerts(0, clear_event_types)
|
||||
|
||||
assert alert_manager.current_alert.alert_type == "lkasEnable/warning"
|
||||
assert alert_manager.current_alert.audible_alert == log.SelfdriveState.AudibleAlert.engage
|
||||
|
||||
|
||||
def test_disabled_starpilot_warnings_stay_filtered_without_blindspot_event():
|
||||
events = Events(starpilot=True)
|
||||
events.add(StarPilotEventName.noLaneAvailable)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
from cereal import car
|
||||
from opendbc.car.hyundai.values import CAR as HYUNDAI_CAR
|
||||
|
||||
from openpilot.selfdrive.selfdrived.selfdrived import commanded_torque_at_max_for_saturation
|
||||
|
||||
@@ -17,3 +18,12 @@ def test_immediate_max_output_saturation_is_torque_controller_only():
|
||||
CP.lateralTuning.init("torque")
|
||||
CP.steerControlType = car.CarParams.SteerControlType.angle
|
||||
assert not commanded_torque_at_max_for_saturation(CP, 1.0)
|
||||
|
||||
|
||||
def test_gv70_uses_normal_saturation_timer_at_max_output():
|
||||
CP = car.CarParams.new_message()
|
||||
CP.carFingerprint = HYUNDAI_CAR.GENESIS_GV70_ELECTRIFIED_1ST_GEN
|
||||
CP.steerControlType = car.CarParams.SteerControlType.torque
|
||||
CP.lateralTuning.init("torque")
|
||||
|
||||
assert not commanded_torque_at_max_for_saturation(CP, 1.0)
|
||||
|
||||
@@ -45,6 +45,9 @@ ACTIVATION_M = 100.0 # m — CEM/model path activates when model_length < t
|
||||
ACTIVATION_HYSTERESIS_M = 8.0 # m — release margin; absorbs model_length jitter at the gate
|
||||
LEAD_VETO_M = 75.0 # m — lead proximity that vetoes Force Stop (kept off ACTIVATION_M
|
||||
# so raising activation can't silently widen the veto)
|
||||
LEAD_VETO_M_OVERRIDES = {
|
||||
"HYUNDAI_ELANTRA_2021": 90.0,
|
||||
}
|
||||
MPC_HANDOFF_M = 6.0 # m — below this, command 0 and let MPC finish the stop
|
||||
FORCE_STOP_APPROACH_DECEL = 0.65 # m/s^2 — speed ceiling before commit. LOWER = more early
|
||||
# braking; don't go under FORCE_STOP_MODEL_APPROACH_DECEL
|
||||
@@ -70,6 +73,11 @@ OFFSET_FT_MIN = -20
|
||||
OFFSET_FT_MAX = 20
|
||||
|
||||
|
||||
def get_lead_veto_distance(car_params):
|
||||
fingerprint = str(getattr(car_params, "carFingerprint", ""))
|
||||
return LEAD_VETO_M_OVERRIDES.get(fingerprint, LEAD_VETO_M)
|
||||
|
||||
|
||||
def get_active_slc_control_target(speed_limit_controller, set_speed_limit, slc_target, slc_offset, overridden_speed,
|
||||
v_ego_diff, allow_lower_override=False):
|
||||
# `SetSpeedLimit` only controls engage-time set-speed initialization. Ongoing
|
||||
@@ -333,8 +341,13 @@ class StarPilotVCruise:
|
||||
# waiting for the tracking_lead filter (~1s ramp). Without this, Force Stop can latch
|
||||
# during the filter's settling window and stay committed for the whole stop.
|
||||
lead = self.starpilot_planner.lead_one
|
||||
try:
|
||||
car_params = sm["carParams"]
|
||||
except (KeyError, IndexError, TypeError, AttributeError):
|
||||
car_params = None
|
||||
lead_veto_m = get_lead_veto_distance(car_params)
|
||||
lead_present = (bool(getattr(lead, "status", False))
|
||||
and float(getattr(lead, "dRel", float("inf"))) < LEAD_VETO_M
|
||||
and float(getattr(lead, "dRel", float("inf"))) < lead_veto_m
|
||||
and float(getattr(lead, "vLead", float("inf"))) < v_ego + 2.0)
|
||||
curved_approach_scene = (
|
||||
abs(float(getattr(self.starpilot_planner, "road_curvature", 0.0))) >= FORCE_STOP_CURVE_VETO_MAX_ROAD_CURVATURE
|
||||
|
||||
@@ -1,54 +0,0 @@
|
||||
from tinygrad.runtime.support.usb import USBMMIOInterface
|
||||
|
||||
|
||||
class FakeUSB:
|
||||
def __init__(self):
|
||||
self.calls = []
|
||||
|
||||
def pcie_mem_req(self, address, value=None, size=4):
|
||||
self.calls.append(("scalar", address, value, size))
|
||||
return 0x11223344 if value is None else None
|
||||
|
||||
def pcie_mem_read(self, address, size):
|
||||
self.calls.append(("read", address, size))
|
||||
return bytes(size)
|
||||
|
||||
def pcie_mem_write(self, address, data):
|
||||
self.calls.append(("write", address, data))
|
||||
|
||||
|
||||
def test_scalar_mmio_uses_single_tlp():
|
||||
usb = FakeUSB()
|
||||
mmio = USBMMIOInterface(usb, 0x1000, 0x100, "I")
|
||||
|
||||
mmio[2] = 0xAABBCCDD
|
||||
assert mmio[3] == 0x11223344
|
||||
|
||||
assert usb.calls == [
|
||||
("scalar", 0x1008, 0xAABBCCDD, 4),
|
||||
("scalar", 0x100C, None, 4),
|
||||
]
|
||||
|
||||
|
||||
def test_slice_mmio_keeps_streaming_path():
|
||||
usb = FakeUSB()
|
||||
mmio = USBMMIOInterface(usb, 0x2000, 0x100, "I")
|
||||
|
||||
mmio[0:2] = b"\x01\x02\x03\x04\x05\x06\x07\x08"
|
||||
assert mmio[0:2] == bytes(8)
|
||||
|
||||
assert usb.calls == [
|
||||
("write", 0x2000, b"\x01\x02\x03\x04\x05\x06\x07\x08"),
|
||||
("read", 0x2000, 8),
|
||||
]
|
||||
|
||||
|
||||
def test_scalar_mmio_falls_back_to_streaming_transport():
|
||||
class StreamingUSB:
|
||||
def __init__(self): self.data = bytearray(4)
|
||||
def pcie_mem_read(self, address, size): return self.data[address-0x3000:address-0x3000+size]
|
||||
def pcie_mem_write(self, address, data): self.data[address-0x3000:address-0x3000+len(data)] = data
|
||||
|
||||
mmio = USBMMIOInterface(StreamingUSB(), 0x3000, 4, "I")
|
||||
mmio[0] = 0xAABBCCDD
|
||||
assert mmio[0] == 0xAABBCCDD
|
||||
@@ -1,7 +1,7 @@
|
||||
from __future__ import annotations
|
||||
import time
|
||||
START_TIME = time.perf_counter()
|
||||
import os, functools, platform, re, contextlib, operator, hashlib, pickle, sqlite3, tempfile, pathlib, string, ctypes, sys, gzip, getpass, gc
|
||||
import os, functools, platform, re, contextlib, operator, hashlib, pickle, sqlite3, tempfile, pathlib, string, ctypes, sys, gzip, getpass, gc, io
|
||||
from collections import defaultdict
|
||||
import subprocess, shutil, math, types, copyreg, inspect, importlib, decimal, itertools, difflib
|
||||
from dataclasses import dataclass, field, replace
|
||||
@@ -492,7 +492,8 @@ def _decompress_zstd(data:bytes) -> bytes:
|
||||
from compression.zstd import decompress
|
||||
return decompress(data)
|
||||
from zstandard import ZstdDecompressor
|
||||
return ZstdDecompressor().decompress(data)
|
||||
with ZstdDecompressor().stream_reader(io.BytesIO(data)) as reader:
|
||||
return reader.read()
|
||||
|
||||
def fetch_fw(path:str, name:str, sha256:str) -> bytes:
|
||||
if (p:=pathlib.Path(f"/lib/firmware/{path}/{name}.zst")).is_file():
|
||||
|
||||
@@ -135,9 +135,6 @@ class CustomASM24Controller:
|
||||
address = (bus << 24) | (dev << 19) | (fn << 16) | (byte_addr & 0xfff)
|
||||
return self.pcie_request(fmt_type, address, value, size)
|
||||
|
||||
def pcie_mem_req(self, address:int, value:int|None=None, size:int=4):
|
||||
return self.pcie_request(0x60 if value is not None else 0x20, address, value, size)
|
||||
|
||||
def pcie_mem_write(self, address:int, data:bytes):
|
||||
"""Streaming PCIe memory write via 0xF0 mode 1 + bulk OUT. Data is little-endian dwords on the wire."""
|
||||
if not data: return
|
||||
@@ -186,31 +183,16 @@ class USBMMIOInterface(MMIOInterface):
|
||||
if isinstance(index, slice): return ((index.start or 0) * self.el_sz, ((index.stop or len(self))-(index.start or 0)) * self.el_sz)
|
||||
return (index * self.el_sz, self.el_sz)
|
||||
|
||||
def _scalar(self, off:int, size:int, value:int|None=None):
|
||||
assert size in (1, 2, 4, 8), f"invalid scalar PCIe access size {size}"
|
||||
if not hasattr(self.usb, "pcie_mem_req"):
|
||||
if value is not None:
|
||||
self.usb.pcie_mem_write(self.addr + off, value.to_bytes(size, "little"))
|
||||
return
|
||||
return int.from_bytes(self.usb.pcie_mem_read(self.addr + off, size), "little")
|
||||
upper = 0 if size < 8 else self.usb.pcie_mem_req(self.addr + off + 4, value if value is None else value >> 32, 4)
|
||||
lower = self.usb.pcie_mem_req(self.addr + off, value if value is None else value & 0xffffffff, min(size, 4))
|
||||
if value is None: return lower | (upper << 32)
|
||||
|
||||
def __getitem__(self, index):
|
||||
off, sz = self._off_from_index(index)
|
||||
if self.pcimem:
|
||||
if not isinstance(index, slice): return self._scalar(off, sz)
|
||||
assert sz % 4 == 0 and off % 4 == 0, f"pcie_mem_read requires 4-byte aligned access, got off={off}, sz={sz}"
|
||||
data = self.usb.pcie_mem_read(self.addr + off, sz)
|
||||
else: data = self.usb.scsi_read(sz) if self.addr == 0xf000 else self.usb.read(self.addr + off, sz)
|
||||
return int.from_bytes(data, "little") if sz == self.el_sz else data
|
||||
|
||||
def __setitem__(self, index, data):
|
||||
off, sz = self._off_from_index(index)
|
||||
if self.pcimem and not isinstance(index, slice) and isinstance(data, int):
|
||||
self._scalar(off, sz, data)
|
||||
return
|
||||
off, _ = self._off_from_index(index)
|
||||
data = struct.pack(self.fmt, data) if isinstance(data, int) else bytes(data)
|
||||
if not self.pcimem: self.usb.scsi_write(data) if self.addr == 0xf000 else self.usb.write(self.addr + off, data)
|
||||
else: self.usb.pcie_mem_write(self.addr+off, data)
|
||||
|
||||
Reference in New Issue
Block a user