Compare commits

...

26 Commits

Author SHA1 Message Date
firestar5683 f857a55f59 Reapply build 2026-08-24 22:11:36 -05:00
firestar5683 05140149fb Reapply Agnos 19.6.10 2026-08-24 22:11:35 -05:00
firestarsdog 6a64493512 Revert "Guards, arrest this man"
This reverts commit 9c1d39b859.
2026-08-24 19:25:05 -04:00
firestarsdog 9c1d39b859 Guards, arrest this man 2026-08-24 19:23:25 -04:00
firestarsdog ffab4a8321 A pitstop! 2026-08-24 19:11:30 -04:00
firestarsdog 71c1d79807 Radial corner betterness 2026-08-24 10:27:22 -04:00
firestar5683 36d0c074f1 Revert "agnosis"
This reverts commit 2db8d95905.
2026-08-23 14:51:08 -05:00
firestar5683 6dccb85b96 build 2026-08-23 14:38:57 -05:00
firestar5683 d11ecaf457 End The Suffering 2026-08-23 13:12:07 -05:00
firestar5683 b302637e4d build 2026-08-23 12:57:27 -05:00
firestar5683 e28f72c661 AHHHH 2026-08-23 12:54:45 -05:00
firestar5683 2db8d95905 agnosis 2026-08-23 11:29:46 -05:00
firestar5683 db5922db43 fix 2026-08-23 11:29:18 -05:00
firestar5683 96f5819c6a build 2026-08-23 10:41:01 -05:00
firestar5683 cfd221b89c fine day, Sunday. 2026-08-23 10:40:20 -05:00
firestar5683 013f8579d7 squeak squeak 2026-08-23 09:19:58 -05:00
firestarsdog 2b53b59bee Bready 2026-08-23 05:12:19 -04:00
firestarsdog 33f6d69a32 Sound panel blinky 2026-08-23 04:48:35 -04:00
firestarsdog 58e919c264 Breadcrumb 2026-08-23 04:28:04 -04:00
firestarsdog 17e5542b1b Model Manager 2026-08-23 03:59:15 -04:00
firestarsdog e1458d4723 Consistency 2026-08-23 03:38:38 -04:00
firestarsdog 06f9e43e4e Consistency 2026-08-23 02:30:57 -04:00
firestarsdog f8e03a9fc5 Tweaker of the Toggletiles 2026-08-23 02:15:00 -04:00
firestarsdog 7c6741a93e cleanup 2026-08-22 04:51:33 -04:00
firestar5683 4b3372d006 BUMP 2026-08-21 20:43:16 -05:00
firestar5683 9a9e7cd1e9 why no s-t-z 2026-08-21 20:42:46 -05:00
185 changed files with 5473 additions and 4080 deletions
+51 -4
View File
@@ -1,4 +1,5 @@
import os
import importlib
import shutil
import subprocess
import sys
@@ -128,6 +129,41 @@ elif arch == "aarch64" and AGNOS:
arch = "larch64"
assert arch in ["larch64", "aarch64", "x86_64", "Darwin"]
# AGNOS 19.6 ships native dependencies as versioned Python packages. Link
# Cap'n Proto statically from that managed package so release binaries don't
# depend on the removed libcapnp-1.0.2.so system library.
try:
capnproto = importlib.import_module("capnproto")
except ModuleNotFoundError:
capnproto = None
try:
ffmpeg = importlib.import_module("ffmpeg")
except ModuleNotFoundError:
ffmpeg = None
capnproto_include_dirs = [capnproto.INCLUDE_DIR] if capnproto is not None else []
capnproto_lib_dirs = [capnproto.LIB_DIR] if capnproto is not None else []
ffmpeg_include_dirs = [ffmpeg.INCLUDE_DIR] if ffmpeg is not None else []
ffmpeg_lib_dirs = [ffmpeg.LIB_DIR] if ffmpeg is not None else []
# Cross-builds install managed dependencies in /work/.venv-linux-arm64, but
# comma devices expose the same packages from /usr/local/venv. Never embed the
# host/container mount path in release binaries.
ffmpeg_runtime_lib_dirs = ffmpeg_lib_dirs
if arch == "larch64" and ffmpeg is not None:
ffmpeg_runtime_lib_dirs = [os.path.join("/usr/local/venv", os.path.relpath(ffmpeg.LIB_DIR, sys.prefix))]
# The managed native-dependency packages keep their tools inside the package
# instead of installing them into /usr/local/venv/bin. cereal invokes capnpc
# directly while SConscript files are evaluated, so make the packaged tools
# discoverable to both SCons actions and configure-time subprocesses.
dependency_bin_dirs = [
package.BIN_DIR for package in (capnproto, ffmpeg)
if package is not None and os.path.isdir(package.BIN_DIR)
]
if dependency_bin_dirs:
os.environ["PATH"] = os.pathsep.join([*dependency_bin_dirs, os.environ["PATH"]])
# Homebrew llvm can shadow Apple clang and break macOS SDK header resolution.
# Use the system toolchain explicitly on macOS for reliable local builds.
cc = '/usr/bin/clang' if arch == "Darwin" else 'clang'
@@ -269,7 +305,10 @@ env = Environment(
"-Wno-vla-cxx-extension",
] + cflags + ccflags,
CPPPATH=cpppath + [
# Managed dependencies must precede the compatibility sysroot. The sysroot
# can intentionally retain legacy libraries for C3 support, but new release
# binaries must link against the versions shipped in the managed venv.
CPPPATH=capnproto_include_dirs + ffmpeg_include_dirs + cpppath + [
"#",
"#third_party/acados/include",
"#third_party/acados/include/blasfeo/include",
@@ -288,11 +327,11 @@ env = Environment(
RANLIB=ranlib,
LINKFLAGS=ldflags,
RPATH=rpath,
RPATH=ffmpeg_runtime_lib_dirs + rpath,
CFLAGS=["-std=gnu11"] + cflags,
CXXFLAGS=["-std=c++1z"] + cxxflags,
LIBPATH=libpath + [
LIBPATH=capnproto_lib_dirs + ffmpeg_lib_dirs + libpath + [
"#msgq_repo",
"#third_party",
"#selfdrive/pandad",
@@ -371,7 +410,15 @@ SConscript(['opendbc_repo/SConscript'], exports={'env': env_swaglog})
SConscript(['cereal/SConscript'])
Import('socketmaster', 'msgq')
messaging = [socketmaster, msgq, 'capnp', 'kj',]
if capnproto is not None:
messaging = [
socketmaster,
msgq,
File(os.path.join(capnproto.LIB_DIR, "libcapnp.a")),
File(os.path.join(capnproto.LIB_DIR, "libkj.a")),
]
else:
messaging = [socketmaster, msgq, 'capnp', 'kj']
Export('messaging')
+1
View File
@@ -265,6 +265,7 @@ struct StarPilotSelfdriveState @0xf416ec09499d9d19 {
alertSize @3 :AlertSize;
alertType @4 :Text;
alertSound @5 :Car.CarControl.HUDControl.AudibleAlert;
vEgo @6 :Float32;
enum AlertStatus {
normal @0;
Binary file not shown.
Binary file not shown.
Binary file not shown.
+2
View File
@@ -345,6 +345,7 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
{"FordCurvatureBlendHigh", {PERSISTENT, FLOAT, "0.4", "0.4", 2}},
{"FordCurvatureBlendLow", {PERSISTENT, FLOAT, "0.4", "0.4", 2}},
{"FordCurvatureLaneChangeFactor", {PERSISTENT, FLOAT, "0.85", "0.85", 2}},
{"FordHandsFreeCluster", {PERSISTENT, BOOL, "0", "0", 2}},
{"FordHumanTurnDetection", {PERSISTENT, BOOL, "1", "1", 2}},
{"FordLateralMode", {PERSISTENT, INT, "1", "1", 2}},
{"FLMActiveOverrides", {PERSISTENT, JSON, "{}", "{}", 2}},
@@ -679,6 +680,7 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
{"TuningLevel", {PERSISTENT, INT, "0", "0", 0}},
{"TuningLevelConfirmed", {PERSISTENT, BOOL, "0", "0", 0}},
{"TurnDesires", {PERSISTENT, BOOL, "0", "0", 2}},
{"TurnSteeringLimitMuteSpeed", {PERSISTENT, INT, "0", "0", 0}},
{"UnlockDoors", {PERSISTENT, BOOL, "1", "0", 0}},
{"Updated", {PERSISTENT, STRING, "0", "0"}},
{"UpdateSpeedLimits", {CLEAR_ON_MANAGER_START, BOOL, "0", "0"}},
Binary file not shown.
Binary file not shown.
+3 -94
View File
@@ -147,101 +147,10 @@ function launch {
while true; do sleep 1; done
fi
function prebuilt_runtime_compatible {
python3 - <<'PY'
import importlib
import os
from pathlib import Path
import sys
import time
from openpilot.common.file_chunker import get_existing_chunks
start = time.monotonic()
last = start
log_path = os.environ.get("SP_BOOT_TIMING_LOG")
def emit(line):
print(line, flush=True)
if log_path:
try:
with open(log_path, "a") as f:
f.write(line + "\n")
except OSError:
pass
def log_step(label):
global last
now = time.monotonic()
emit(f"SP_BOOT_TIMING prebuilt_compat {label} +{now - last:.3f}s total={now - start:.3f}s")
last = now
mods = [
"openpilot.common.params_pyx",
"msgq.ipc_pyx",
"msgq.visionipc.visionipc_pyx",
"openpilot.common.transformations.transformations",
"openpilot.selfdrive.pandad.pandad_api_impl",
"openpilot.selfdrive.controls.lib.lateral_mpc_lib.c_generated_code.acados_ocp_solver_pyx",
"openpilot.selfdrive.controls.lib.longitudinal_mpc_lib.c_generated_code.acados_ocp_solver_pyx",
]
for mod in mods:
try:
importlib.import_module(mod)
except Exception as e:
print(f"Prebuilt compatibility failure in {mod}: {e}", file=sys.stderr)
raise
log_step(f"import:{mod}")
repo_root = Path.cwd().parents[1]
required_model_artifacts = [
repo_root / "selfdrive/modeld/models/driving_tinygrad.pkl",
repo_root / "selfdrive/modeld/models/dmonitoring_model_metadata.pkl",
repo_root / "selfdrive/modeld/models/dmonitoring_model_tinygrad.pkl",
repo_root / "selfdrive/modeld/models/dm_warp_1928x1208_tinygrad.pkl",
repo_root / "selfdrive/modeld/models/dm_warp_1344x760_tinygrad.pkl",
]
required_files = [
repo_root / "selfdrive/pandad/pandad_api_impl.so",
repo_root / "selfdrive/controls/lib/lateral_mpc_lib/c_generated_code/acados_ocp_solver_pyx.so",
repo_root / "selfdrive/controls/lib/lateral_mpc_lib/c_generated_code/libacados_ocp_solver_lat.so",
repo_root / "selfdrive/controls/lib/longitudinal_mpc_lib/c_generated_code/acados_ocp_solver_pyx.so",
repo_root / "selfdrive/controls/lib/longitudinal_mpc_lib/c_generated_code/libacados_ocp_solver_long.so",
repo_root / "opendbc_repo/opendbc/dbc/gm_global_a_powertrain_generated.dbc",
]
for path in required_model_artifacts:
try:
artifact_paths = [Path(p) for p in get_existing_chunks(path)]
except Exception as e:
raise FileNotFoundError(f"Missing prebuilt runtime artifact: {path}") from e
missing_chunks = [p for p in artifact_paths if not p.is_file()]
if missing_chunks:
missing = ", ".join(str(p) for p in missing_chunks)
raise FileNotFoundError(f"Missing prebuilt runtime artifact chunks for {path}: {missing}")
log_step("required_model_artifacts")
for path in required_files:
if not path.is_file():
raise FileNotFoundError(f"Missing prebuilt runtime artifact: {path}")
log_step("required_files")
PY
}
USE_PREBUILT=1
if [ -f /data/params/d/UsePrebuilt ]; then
USE_PREBUILT=$(tr -d '\n' < /data/params/d/UsePrebuilt)
fi
sp_launch_timing "prebuilt_decision_done"
if [ "$USE_PREBUILT" = "1" ] && [ -f $DIR/prebuilt ] && ! prebuilt_runtime_compatible; then
echo "Prebuilt runtime artifacts are incompatible on this device; rebuilding locally."
USE_PREBUILT=0
fi
sp_launch_timing "prebuilt_compat_done"
if [ "$USE_PREBUILT" != "1" ] || [ ! -f $DIR/prebuilt ]; then
# Published trees carry this marker and must never compile on-device.
# Developers can remove it explicitly when working from a source tree.
if [ ! -f "$DIR/prebuilt" ]; then
sp_launch_timing "build_start"
./build.py
sp_launch_timing "build_done"
+1 -1
View File
@@ -21,7 +21,7 @@ fi
export QCOM_PRIORITY=12
if [ -z "$AGNOS_VERSION" ]; then
export AGNOS_VERSION="19.6.2"
export AGNOS_VERSION="19.6.10"
fi
if [ -z "$AGNOS_ACCEPTED_VERSIONS" ]; then
Binary file not shown.
Binary file not shown.
@@ -255,9 +255,15 @@ class CarController(CarControllerBase):
if (self.frame % CarControllerParams.ACC_UI_STEP) == 0 or send_ui:
show_distance_bars = self.frame - self.distance_bar_frame < 400
hands_free_cluster = bool(
self.ford_lateral is not None
and self.ford_lateral.mode != FordLateralMode.native
and self.ford_lateral.mode == self.ford_lateral_announced_mode
and self.ford_lateral.hands_free_cluster_enabled)
can_sends.append(fordcan.create_acc_ui_msg(self.packer, self.CAN, self.CP, main_on, CC.latActive,
fcw_alert, CS.out.cruiseState.standstill, show_distance_bars,
hud_control, CS.acc_tja_status_stock_values))
hud_control, CS.acc_tja_status_stock_values,
hands_free_cluster))
self.main_on_last = main_on
self.lkas_enabled_last = CC.latActive
+3 -1
View File
@@ -181,7 +181,7 @@ def create_acc_msg(packer, CAN: CanBus, long_active: bool, gas: float, accel: fl
def create_acc_ui_msg(packer, CAN: CanBus, CP, main_on: bool, enabled: bool, fcw_alert: bool, standstill: bool,
show_distance_bars: bool, hud_control, stock_values: dict):
show_distance_bars: bool, hud_control, stock_values: dict, hands_free_cluster: bool = False):
"""
Creates a CAN message for the Ford IPC adaptive cruise, forward collision warning and traffic jam
assist status.
@@ -197,6 +197,8 @@ def create_acc_ui_msg(packer, CAN: CanBus, CP, main_on: bool, enabled: bool, fcw
status = 3 # ActiveInterventionLeft
elif hud_control.rightLaneDepart:
status = 4 # ActiveInterventionRight
elif hands_free_cluster:
status = 7 # Hands-free assistance display
else:
status = 2 # Active
elif main_on:
@@ -1,10 +1,13 @@
import random
from collections.abc import Iterable
from types import SimpleNamespace
from hypothesis import settings, given, strategies as st
from parameterized import parameterized
from opendbc.car import gen_empty_fingerprint
from opendbc.can import CANPacker
from opendbc.car.ford import fordcan
from opendbc.car.structs import CarParams
from opendbc.car.fw_versions import build_fw_dict
from opendbc.car.ford.interface import CarInterface
@@ -172,3 +175,29 @@ def test_mach_e_longitudinal_toggle_controls_stock_acc_selection():
assert enhanced.alphaLongitudinalAvailable
assert enhanced.openpilotLongitudinalControl
assert enhanced.safetyConfigs[-1].safetyParam & FordSafetyFlags.LONG_CONTROL
def test_hands_free_cluster_status_is_opt_in():
packer = CANPacker("ford_lincoln_base_pt")
CAN = SimpleNamespace(main=0)
CP = SimpleNamespace(openpilotLongitudinalControl=False)
hud = SimpleNamespace(leftLaneDepart=False, rightLaneDepart=False)
stock_values = dict.fromkeys([
"HaDsply_No_Cs", "HaDsply_No_Cnt", "AccStopStat_D_Dsply", "AccTrgDist2_D_Dsply",
"AccStopRes_B_Dsply", "TjaWarn_D_Rq", "TjaMsgTxt_D_Dsply", "IaccLamp_D_Rq",
"AccMsgTxt_D2_Rq", "FcwDeny_B_Dsply", "FcwMemStat_B_Actl", "AccTGap_B_Dsply",
"CadsAlignIncplt_B_Actl", "AccFllwMde_B_Dsply", "CadsRadrBlck_B_Actl",
"CmbbPostEvnt_B_Dsply", "AccStopMde_B_Dsply", "FcwMemSens_D_Actl",
"FcwMsgTxt_D_Rq", "AccWarn_D_Dsply", "FcwVisblWarn_B_Rq", "FcwAudioWarn_B_Rq",
"AccTGap_D_Dsply", "AccMemEnbl_B_RqDrv", "FdaMem_B_Stat",
], 0)
regular = fordcan.create_acc_ui_msg(
packer, CAN, CP, True, True, False, False, False, hud, stock_values)
hands_free = fordcan.create_acc_ui_msg(
packer, CAN, CP, True, True, False, False, False, hud, stock_values, True)
expected_regular = packer.make_can_msg("ACCDATA_3", 0, {"Tja_D_Stat": 2})
expected_hands_free = packer.make_can_msg("ACCDATA_3", 0, {"Tja_D_Stat": 7})
assert regular == expected_regular
assert hands_free == expected_hands_free
@@ -988,6 +988,45 @@ class TestHyundaiFingerprint:
assert long_xceed.safetyConfigs[-1].safetyParam & HyundaiSafetyFlags.HYBRID_GAS
assert long_xceed.safetyConfigs[-1].safetyParam & HyundaiSafetyFlags.LONG
def test_g80_alpha_long_preserves_legacy_safety(self):
toggles = get_test_toggles()
stock_g80 = CarInterface.get_params(CAR.GENESIS_G80, gen_empty_fingerprint(), [], False, False, False, toggles)
assert CAR.GENESIS_G80 in LEGACY_LONGITUDINAL_CAR
assert stock_g80.alphaLongitudinalAvailable
assert not stock_g80.openpilotLongitudinalControl
assert stock_g80.pcmCruise
assert stock_g80.safetyConfigs[-1].safetyModel == CarParams.SafetyModel.hyundaiLegacy
assert not (stock_g80.safetyConfigs[-1].safetyParam & HyundaiSafetyFlags.LONG)
long_g80 = CarInterface.get_params(CAR.GENESIS_G80, gen_empty_fingerprint(), [], True, False, False, toggles)
assert long_g80.alphaLongitudinalAvailable
assert long_g80.openpilotLongitudinalControl
assert not long_g80.pcmCruise
assert long_g80.safetyConfigs[-1].safetyModel == CarParams.SafetyModel.hyundaiLegacy
assert long_g80.safetyConfigs[-1].safetyParam & HyundaiSafetyFlags.LONG
@pytest.mark.parametrize("ecu_disabled", (True, False))
def test_g80_alpha_long_disables_stock_scc(self, monkeypatch, ecu_disabled):
toggles = get_test_toggles()
CP = CarInterface.get_params(CAR.GENESIS_G80, gen_empty_fingerprint(), [], True, False, False, toggles)
called = {}
def fake_disable_ecu(*args, **kwargs):
called.update(kwargs)
return ecu_disabled
monkeypatch.setattr("opendbc.car.hyundai.interface.disable_ecu", fake_disable_ecu)
CarInterface.init(CP, None, None)
assert called["addr"] == 0x7d0
assert called["bus"] == 0
assert called["reset"] is False
assert CP.openpilotLongitudinalControl == ecu_disabled
assert CP.pcmCruise != ecu_disabled
assert bool(CP.safetyConfigs[-1].safetyParam & HyundaiSafetyFlags.LONG) == ecu_disabled
def test_xceed_phev_disable_failure_falls_back_to_stock_acc(self, monkeypatch):
toggles = get_test_toggles()
CP = CarInterface.get_params(CAR.KIA_XCEED_PHEV, gen_empty_fingerprint(), [], True, False, False, toggles)
+4 -1
View File
@@ -1213,6 +1213,9 @@ NON_SCC_CAR = CAR.with_flags(HyundaiFlags.NON_SCC)
# HyundaiFlags.CANFD_RADAR_SCC | HyundaiFlags.CANFD_NO_RADAR_DISABLE | )
UNSUPPORTED_LONGITUDINAL_CAR = CAR.with_flags(HyundaiFlags.LEGACY) | CAR.with_flags(HyundaiFlags.UNSUPPORTED_LONGITUDINAL)
LEGACY_LONGITUDINAL_CAR = {CAR.KIA_XCEED_PHEV}
LEGACY_LONGITUDINAL_CAR = {
CAR.GENESIS_G80,
CAR.KIA_XCEED_PHEV,
}
DBC = CAR.create_dbc_map()
+2 -1
View File
@@ -15,6 +15,7 @@ LEAF_ADAS_ECU_BUS = 0
LEAF_ADAS_COMMAND_BUS = 1
LEAF_ADAS_COMMAND_ADDRS = frozenset((0x1C3, 0x2B0))
LEAF_2025_SV_PLUS_CAMERA_FW = b'6WK2CDB\x04\x18\x00\x00\x00\x00\x00R=1\x18\x99\x10\x00\x00\x00\x80'
LEAF_2025_SV_PLUS_ALPHA_LONG_ENABLED = False
LEAF_KWP_EXTENDED_REQUEST = b"\x10\xC0"
LEAF_KWP_EXTENDED_RESPONSE = b"\x50\xC0"
@@ -26,7 +27,7 @@ LEAF_KWP_TAKEOVER_SESSIONS = (
def is_leaf_2025_sv_plus_longitudinal(candidate, car_fw):
return candidate == CAR.NISSAN_LEAF and any(
return LEAF_2025_SV_PLUS_ALPHA_LONG_ENABLED and candidate == CAR.NISSAN_LEAF and any(
fw.address == LEAF_ADAS_ECU_ADDR and bytes(fw.fwVersion) == LEAF_2025_SV_PLUS_CAMERA_FW
for fw in car_fw
)
@@ -4,6 +4,7 @@ import pytest
from opendbc.car import Bus, ButtonType, gen_empty_fingerprint, structs
from opendbc.car.can_definitions import CanData
from opendbc.car.nissan import interface as nissan_interface
from opendbc.car.nissan.carstate import CarState
from opendbc.car.nissan.interface import CarInterface, LEAF_2025_SV_PLUS_CAMERA_FW, leaf_adas_commands_present, \
leaf_adas_commands_silent, restore_leaf_adas_tx
@@ -18,6 +19,12 @@ SUPPORTED_LEAF_FW = [structs.CarParams.CarFw(
)]
@pytest.fixture
def experimental_leaf_long(monkeypatch):
"""Exercise the dormant implementation without making it available in production."""
monkeypatch.setattr(nissan_interface, "LEAF_2025_SV_PLUS_ALPHA_LONG_ENABLED", True)
def run_controller(alpha_long, accel=0.0, long_active=True, long_state=structs.CarControl.Actuators.LongControlState.pid):
CP = CarInterface.get_params(CAR.NISSAN_LEAF, gen_empty_fingerprint(), SUPPORTED_LEAF_FW, alpha_long, False, False, TEST_TOGGLES)
FPCP = CarInterface.get_starpilot_params(CAR.NISSAN_LEAF, gen_empty_fingerprint(), SUPPORTED_LEAF_FW, CP, TEST_TOGGLES)
@@ -33,7 +40,28 @@ def run_controller(alpha_long, accel=0.0, long_active=True, long_state=structs.C
return {msg[0]: msg for msg in can_sends}
def test_leaf_2025_sv_plus_alpha_long_params():
def test_leaf_2025_sv_plus_alpha_long_is_disabled(monkeypatch):
stock = CarInterface.get_params(CAR.NISSAN_LEAF, gen_empty_fingerprint(), SUPPORTED_LEAF_FW, False, False, False, None)
alpha_long = CarInterface.get_params(CAR.NISSAN_LEAF, gen_empty_fingerprint(), SUPPORTED_LEAF_FW, True, False, False, None)
assert not stock.alphaLongitudinalAvailable
assert not stock.openpilotLongitudinalControl
assert stock.pcmCruise
assert not (stock.safetyConfigs[-1].safetyParam & NissanSafetyFlags.LONG_CONTROL)
assert not alpha_long.alphaLongitudinalAvailable
assert not alpha_long.openpilotLongitudinalControl
assert alpha_long.pcmCruise
assert not alpha_long.autoResumeSng
assert not (alpha_long.safetyConfigs[-1].safetyParam & NissanSafetyFlags.LONG_CONTROL)
disable_calls = []
monkeypatch.setattr("opendbc.car.nissan.interface.disable_ecu", lambda *args, **kwargs: disable_calls.append((args, kwargs)))
CarInterface.init(alpha_long, None, None)
assert not disable_calls
def test_dormant_leaf_2025_sv_plus_alpha_long_params(experimental_leaf_long):
stock = CarInterface.get_params(CAR.NISSAN_LEAF, gen_empty_fingerprint(), SUPPORTED_LEAF_FW, False, False, False, None)
alpha_long = CarInterface.get_params(CAR.NISSAN_LEAF, gen_empty_fingerprint(), SUPPORTED_LEAF_FW, True, False, False, None)
@@ -79,7 +107,13 @@ def test_stock_controller_does_not_send_longitudinal_messages():
assert not ({0x2B0, 0x1C3, 0x707} & can_sends.keys())
def test_alpha_long_controller_sends_stock_shaped_commands_and_keepalive():
def test_disabled_alpha_long_controller_does_not_send_longitudinal_messages():
can_sends = run_controller(True)
assert not ({0x2B0, 0x1C3, 0x707} & can_sends.keys())
def test_alpha_long_controller_sends_stock_shaped_commands_and_keepalive(experimental_leaf_long):
can_sends = run_controller(True)
assert can_sends[0x2B0][1].hex() == "ff6090ac5b000e03"
@@ -89,13 +123,13 @@ def test_alpha_long_controller_sends_stock_shaped_commands_and_keepalive():
assert can_sends[0x707][2] == 0
def test_alpha_long_controller_clamps_to_panda_accel_limit():
def test_alpha_long_controller_clamps_to_panda_accel_limit(experimental_leaf_long):
can_sends = run_controller(True, accel=5.0)
assert can_sends[0x2B0][1].hex() == "007f8fac5b000e0c"
def test_alpha_long_controller_blends_friction_brake_below_regen_limit():
def test_alpha_long_controller_blends_friction_brake_below_regen_limit(experimental_leaf_long):
can_sends = run_controller(True, accel=-2.0)
assert can_sends[0x2B0][1].hex() == "a827d5ac5b000e09"
@@ -104,7 +138,7 @@ def test_alpha_long_controller_blends_friction_brake_below_regen_limit():
assert brake[5] & 0x84 == 0x84
def test_alpha_long_controller_sends_inactive_commands_when_disengaged():
def test_alpha_long_controller_sends_inactive_commands_when_disengaged(experimental_leaf_long):
can_sends = run_controller(True, accel=1.0, long_active=False)
assert can_sends[0x2B0][1].hex() == "dc53a2ac1b000e03"
@@ -113,7 +147,7 @@ def test_alpha_long_controller_sends_inactive_commands_when_disengaged():
@pytest.mark.parametrize(("signal", "button_type"), [("SET_BUTTON", ButtonType.decelCruise),
("RES_BUTTON", ButtonType.accelCruise)])
def test_leaf_set_resume_release_enables_alpha_long(signal, button_type):
def test_leaf_set_resume_release_enables_alpha_long(signal, button_type, experimental_leaf_long):
CP = CarInterface.get_params(CAR.NISSAN_LEAF, gen_empty_fingerprint(), SUPPORTED_LEAF_FW, True, False, False, TEST_TOGGLES)
FPCP = CarInterface.get_starpilot_params(CAR.NISSAN_LEAF, gen_empty_fingerprint(), SUPPORTED_LEAF_FW, CP, TEST_TOGGLES)
CS = CarState(CP, FPCP)
@@ -130,7 +164,7 @@ def test_leaf_set_resume_release_enables_alpha_long(signal, button_type):
@pytest.mark.parametrize("ecu_disabled", [False, True])
def test_leaf_ecu_disable_is_strict_and_falls_back(monkeypatch, ecu_disabled):
def test_leaf_ecu_disable_is_strict_and_falls_back(monkeypatch, ecu_disabled, experimental_leaf_long):
CP = CarInterface.get_params(CAR.NISSAN_LEAF, gen_empty_fingerprint(), SUPPORTED_LEAF_FW, True, False, False, None)
calls = []
@@ -158,7 +192,7 @@ def test_leaf_ecu_disable_is_strict_and_falls_back(monkeypatch, ecu_disabled):
assert bool(CP.safetyConfigs[-1].safetyParam & NissanSafetyFlags.LONG_CONTROL) is ecu_disabled
def test_leaf_kwp_no_response_disable_can_confirm_ecu_silence(monkeypatch):
def test_leaf_kwp_no_response_disable_can_confirm_ecu_silence(monkeypatch, experimental_leaf_long):
CP = CarInterface.get_params(CAR.NISSAN_LEAF, gen_empty_fingerprint(), SUPPORTED_LEAF_FW, True, False, False, None)
monkeypatch.setattr("opendbc.car.nissan.interface.disable_ecu", lambda *args, **kwargs: True)
@@ -171,7 +205,7 @@ def test_leaf_kwp_no_response_disable_can_confirm_ecu_silence(monkeypatch):
assert CP.safetyConfigs[-1].safetyParam & NissanSafetyFlags.LONG_CONTROL
def test_leaf_positive_disable_response_without_command_silence_falls_back(monkeypatch):
def test_leaf_positive_disable_response_without_command_silence_falls_back(monkeypatch, experimental_leaf_long):
CP = CarInterface.get_params(CAR.NISSAN_LEAF, gen_empty_fingerprint(), SUPPORTED_LEAF_FW, True, False, False, None)
restore_calls = []
@@ -165,13 +165,18 @@ class TestCarInterfaces:
"Center_Stack_2": {"LKAS_Button": 1},
}, is_ram=True)
def test_chrysler_wd_mod_enables_steer_to_zero(self):
@pytest.mark.parametrize("candidate", (
CHRYSLER_CAR.CHRYSLER_PACIFICA_2020,
CHRYSLER_CAR.JEEP_GRAND_CHEROKEE,
CHRYSLER_CAR.JEEP_GRAND_CHEROKEE_2019,
))
def test_chrysler_steer_to_zero_module(self, candidate):
fingerprint = {bus: {} for bus in range(8)}
fingerprint[0][0x4FF] = 8
toggles = get_test_starpilot_toggles()
car_params = ChryslerCarInterface.get_params(
CHRYSLER_CAR.CHRYSLER_PACIFICA_2020,
candidate,
fingerprint,
[],
alpha_long=False,
@@ -182,7 +187,7 @@ class TestCarInterfaces:
assert car_params.minSteerSpeed > 0.
fp_car_params = ChryslerCarInterface.get_starpilot_params(
CHRYSLER_CAR.CHRYSLER_PACIFICA_2020,
candidate,
fingerprint,
[],
car_params,
@@ -206,11 +211,14 @@ class TestCarInterfaces:
button_message="CRUISE_BUTTONS",
auto_high_beam=0,
)
controller.update(CC, CS, 0, toggles)
controller.update(CC, CS, 0, toggles)
_, can_sends = controller.update(CC, CS, 0, toggles)
lkas_parser = CANParser(CHRYSLER_DBC[car_params.carFingerprint][Bus.pt], [("LKAS_COMMAND", 50)], 0)
lkas_parser.update([0, can_sends])
assert lkas_parser.vl["LKAS_COMMAND"]["LKAS_CONTROL_BIT"] == 1
assert lkas_parser.vl["LKAS_COMMAND"]["STEERING_TORQUE"] != 0
@pytest.mark.parametrize("candidate", (CHRYSLER_CAR.JEEP_GRAND_CHEROKEE, CHRYSLER_CAR.JEEP_GRAND_CHEROKEE_2019))
def test_jeep_brake_hold_safety_capability_is_provisioned(self, candidate):
@@ -1103,6 +1103,7 @@ class SafetyTest(SafetyTestBase):
'TestHyundaiSafetyFCEVLong', 'TestHyundaiLongitudinalAolLkasOnEngageSafety',
'TestHyundaiSafetyCanRefreshLong', 'TestHyundaiSafetyCanRefreshLongCameraSCC',
'TestHyundaiCanCanfdBlendedLongitudinalSafety',
'TestHyundaiLegacyLongitudinalSafety',
'TestHyundaiLegacyLongitudinalSafetyHEV'}):
continue
volkswagen_shared = ('TestVolkswagenMqb', 'TestVolkswagenMlb', 'TestVolkswagenMeb')
@@ -1156,6 +1157,7 @@ class SafetyTest(SafetyTestBase):
if attr.startswith('TestHyundaiLongitudinal') or attr in ('TestHyundaiSafetyFCEVLong',
'TestHyundaiLongitudinalAolLkasOnEngageSafety',
'TestHyundaiCanCanfdBlendedLongitudinalSafety',
'TestHyundaiLegacyLongitudinalSafety',
'TestHyundaiLegacyLongitudinalSafetyHEV'):
# exceptions for common msgs across different Hyundai CAN platforms
tx = list(filter(lambda m: m[0] not in [0x420, 0x50A, 0x389, 0x4A2], tx))
@@ -597,6 +597,14 @@ class TestHyundaiSafetyFCEVLong(TestHyundaiLongitudinalSafety, TestHyundaiSafety
self.safety.init_tests()
class TestHyundaiLegacyLongitudinalSafety(TestHyundaiLongitudinalSafety, TestHyundaiLegacySafety):
def setUp(self):
self.packer = CANPackerSafety("hyundai_kia_generic")
self.safety = libsafety_py.libsafety
self.safety.set_safety_hooks(CarParams.SafetyModel.hyundaiLegacy, HyundaiSafetyFlags.LONG)
self.safety.init_tests()
class TestHyundaiLegacyLongitudinalSafetyHEV(TestHyundaiLongitudinalSafety, TestHyundaiLegacySafetyHEV):
def setUp(self):
self.packer = CANPackerSafety("hyundai_kia_generic")
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
View File
@@ -1,2 +1,2 @@
extern const uint8_t gitversion[19];
const uint8_t gitversion[19] = "DEV-284dbddd-DEBUG";
const uint8_t gitversion[19] = "DEV-46ae2472-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.
+1 -1
View File
@@ -1 +1 @@
DEV-284dbddd-DEBUG
DEV-46ae2472-DEBUG
+5
View File
@@ -29,6 +29,11 @@ dependencies = [
"setuptools",
"numpy >=2.0",
# AGNOS 19.6 native build dependencies
"comma-deps-capnproto; python_version >= '3.12'",
"comma-deps-ffmpeg; python_version >= '3.12'",
"libdatachannel-py>=2026.1.0.dev2; python_version >= '3.12'",
# body / webrtcd
"aiohttp",
"aiortc",
Binary file not shown.
+61
View File
@@ -467,6 +467,66 @@ EOF
echo "==> larch64 scons completed in $(( $(date +%s) - started_at ))s"
}
validate_larch64_artifacts() {
local engine
engine="$(detect_engine)"
echo "==> Validating larch64 runtime dependencies"
"${engine}" run --rm --platform linux/arm64 \
--user "${DOCKER_RUN_USER}" \
-v "${HOST_ROOT_DIR}:/work" \
-v "${HOST_VENV_DIR}:/work/.venv-linux-arm64:ro" \
-v "${HOST_SYSROOT_DIR}:/opt/tici-sysroot:ro" \
-v "${HOST_SYSROOT_DIR}/system/vendor/lib64:/system/vendor/lib64:ro" \
-w /work \
"${IMAGE_NAME}" bash -lc '
set -euo pipefail
ffmpeg_lib_dir="/work/.venv-linux-arm64/lib/python3.12/site-packages/ffmpeg/install/lib"
target_ffmpeg_runpath="/usr/local/venv/lib/python3.12/site-packages/ffmpeg/install/lib"
runtime_library_path="${ffmpeg_lib_dir}:/work/third_party/acados/larch64/lib:/work/third_party/libyuv/larch64/lib:/work/selfdrive/controls/lib/lateral_mpc_lib/c_generated_code:/work/selfdrive/controls/lib/longitudinal_mpc_lib/c_generated_code:/opt/tici-sysroot/usr/local/lib:/opt/tici-sysroot/lib/aarch64-linux-gnu:/opt/tici-sysroot/usr/lib/aarch64-linux-gnu:/system/vendor/lib64"
failures=0
while IFS= read -r -d "" artifact; do
if ! readelf -h "${artifact}" 2>/dev/null | grep -q "Machine:.*AArch64"; then
continue
fi
dynamic="$(readelf -d "${artifact}" 2>/dev/null || true)"
if grep -Eq "(RPATH|RUNPATH).*\/work\/" <<<"${dynamic}"; then
echo "ERROR: ${artifact} embeds a build-host runtime path" >&2
failures=1
fi
has_ffmpeg=0
while IFS= read -r needed; do
case "${needed}" in
libavformat.so.*|libavcodec.so.*|libswresample.so.*|libavutil.so.*)
has_ffmpeg=1
if [[ ! -e "${ffmpeg_lib_dir}/${needed}" ]]; then
echo "ERROR: ${artifact} links ${needed}, which is not shipped by the managed FFmpeg package" >&2
failures=1
fi
;;
esac
done < <(sed -n "s/.*Shared library: \[\([^]]*\)\].*/\1/p" <<<"${dynamic}")
if [[ "${has_ffmpeg}" -eq 1 ]] && ! grep -Fq "${target_ffmpeg_runpath}" <<<"${dynamic}"; then
echo "ERROR: ${artifact} links FFmpeg without the target managed-venv RUNPATH" >&2
failures=1
fi
missing="$(LD_LIBRARY_PATH="${runtime_library_path}" ldd "${artifact}" 2>&1 | grep "not found" || true)"
if [[ -n "${missing}" ]]; then
echo "ERROR: ${artifact} has unresolved target dependencies:" >&2
echo "${missing}" >&2
failures=1
fi
done < <(git ls-files -z)
exit "${failures}"
'
}
setup_host_venv() {
if [[ ! -f "${ROOT_DIR}/.venv/bin/activate" ]]; then
if [[ -x "${ROOT_DIR}/tools/install_python_dependencies.sh" ]]; then
@@ -516,6 +576,7 @@ run_larch64_build() {
cereal/messaging/bridge \
msgq_repo/msgq/ipc_pyx.so \
msgq_repo/msgq/visionipc/visionipc_pyx.so
validate_larch64_artifacts
touch "${ROOT_DIR}/prebuilt"
}
+8 -2
View File
@@ -39,6 +39,7 @@ from openpilot.starpilot.controls.starpilot_card import StarPilotCard
REPLAY = "REPLAY" in os.environ
OPENPILOT_LEAD_MIN_DISTANCE = 0.1
REDNECK_DECREASE_LOOKAHEAD_POINTS = 10
SLC_SOURCE_NONE = "None"
EventName = log.OnroadEvent.EventName
# forward
@@ -276,14 +277,19 @@ class Car:
self.CP.openpilotLongitudinalControl and not self.CP.pcmCruise
)
if not preap_software_cruise:
speed_limit_confirmation_pending = is_speed_limit_confirmation_pending(self.sm['starpilotPlan'])
starpilot_plan = self.sm['starpilotPlan']
speed_limit_confirmation_pending = is_speed_limit_confirmation_pending(starpilot_plan)
slc_target_with_offset = 0.0
if self.starpilot_toggles.speed_limit_controller and starpilot_plan.slcSpeedLimitSource != SLC_SOURCE_NONE:
slc_target_with_offset = starpilot_plan.slcSpeedLimit + starpilot_plan.slcSpeedLimitOffset
self.v_cruise_helper.update_v_cruise(
CS,
self.sm['carControl'].enabled,
self.is_metric,
speed_limit_confirmation_pending,
self.starpilot_toggles,
FPCS,
starpilot_car_state=FPCS,
slc_target_with_offset=slc_target_with_offset,
)
else:
preap_v_cruise_kph = float(CS.cruiseState.speed * CV.MS_TO_KPH)
+17 -3
View File
@@ -89,13 +89,15 @@ class VCruiseHelper:
return bool(getattr(starpilot_car_state, "decelHardCruise", False))
return False
def update_v_cruise(self, CS, enabled, is_metric, speed_limit_changed, starpilot_toggles, starpilot_car_state=None):
def update_v_cruise(self, CS, enabled, is_metric, speed_limit_changed, starpilot_toggles, starpilot_car_state=None,
slc_target_with_offset=0.0):
self.v_cruise_kph_last = self.v_cruise_kph
if CS.cruiseState.available:
if self.gm_cc_only or self.redneck_non_pcm or not self.CP.pcmCruise:
# if stock cruise is completely disabled, then we can use our own set speed logic
self._update_v_cruise_non_pcm(CS, enabled, is_metric, speed_limit_changed, starpilot_toggles, starpilot_car_state)
self._update_v_cruise_non_pcm(CS, enabled, is_metric, speed_limit_changed, starpilot_toggles, starpilot_car_state,
slc_target_with_offset)
self.v_cruise_cluster_kph = self.v_cruise_kph
self.update_button_timers(CS, enabled, starpilot_car_state)
else:
@@ -111,7 +113,8 @@ class VCruiseHelper:
self.v_cruise_kph = V_CRUISE_UNSET
self.v_cruise_cluster_kph = V_CRUISE_UNSET
def _update_v_cruise_non_pcm(self, CS, enabled, is_metric, speed_limit_changed, starpilot_toggles, starpilot_car_state=None):
def _update_v_cruise_non_pcm(self, CS, enabled, is_metric, speed_limit_changed, starpilot_toggles, starpilot_car_state=None,
slc_target_with_offset=0.0):
# handle button presses. TODO: this should be in state_control, but a decelCruise press
# would have the effect of both enabling and changing speed is checked after the state transition
if not enabled:
@@ -169,11 +172,22 @@ class VCruiseHelper:
short_interval, long_interval = self._get_cruise_delta_intervals(starpilot_toggles)
v_cruise_delta_interval = long_interval if long_press or button_is_hard else short_interval
v_cruise_delta = v_cruise_delta * v_cruise_delta_interval
previous_v_cruise_kph = self.v_cruise_kph
if v_cruise_delta_interval % 5 == 0 and self.v_cruise_kph % v_cruise_delta != 0: # partial interval
self.v_cruise_kph = CRUISE_NEAREST_FUNC[button_type](self.v_cruise_kph / v_cruise_delta) * v_cruise_delta
else:
self.v_cruise_kph += v_cruise_delta * CRUISE_INTERVAL_SIGN[button_type]
# Round to stored cruise-speed precision so the SLC target can be matched exactly.
slc_target_with_offset_kph = round(slc_target_with_offset * CV.MS_TO_KPH, 1)
# Preserve an active SLC target when an accel increment would otherwise skip it.
crossed_slc_target = (
button_type in ACCEL_CRUISE_BUTTONS and
previous_v_cruise_kph < slc_target_with_offset_kph < self.v_cruise_kph
)
if crossed_slc_target:
self.v_cruise_kph = slc_target_with_offset_kph
# If set is pressed while overriding, clip cruise speed to minimum of vEgo
if CS.gasPressed and button_type in (ButtonType.decelCruise, ButtonType.setCruise):
self.v_cruise_kph = max(self.v_cruise_kph, CS.vEgo * CV.MS_TO_KPH)
+41
View File
@@ -119,6 +119,47 @@ class TestVCruiseHelper:
)
assert pressed == (self.v_cruise_helper.v_cruise_kph == self.v_cruise_helper.v_cruise_kph_last)
def test_accel_stops_at_slc_target_before_crossing_it(self):
self.starpilot_toggles.cruise_increase = 5
self.v_cruise_helper.v_cruise_kph = 30
self.v_cruise_helper.v_cruise_cluster_kph = 30
slc_target_with_offset = 33 * CV.KPH_TO_MS
# A 30 km/h limit with a +3 km/h SLC offset should be an intermediate stop.
for expected_kph in (33, 35, 40):
for pressed in (True, False):
CS = car.CarState(cruiseState={"available": True})
CS.buttonEvents = [ButtonEvent(type=ButtonType.accelCruise, pressed=pressed)]
self.v_cruise_helper.update_v_cruise(
CS,
enabled=True,
is_metric=True,
speed_limit_changed=False,
starpilot_toggles=self.starpilot_toggles,
slc_target_with_offset=slc_target_with_offset,
)
assert self.v_cruise_helper.v_cruise_kph == pytest.approx(expected_kph)
def test_accel_uses_normal_interval_without_an_active_slc_target(self):
self.starpilot_toggles.cruise_increase = 5
self.v_cruise_helper.v_cruise_kph = 30
self.v_cruise_helper.v_cruise_cluster_kph = 30
# Card passes zero when SLC is disabled or has no active speed-limit source.
for pressed in (True, False):
CS = car.CarState(cruiseState={"available": True})
CS.buttonEvents = [ButtonEvent(type=ButtonType.accelCruise, pressed=pressed)]
self.v_cruise_helper.update_v_cruise(
CS,
enabled=True,
is_metric=True,
speed_limit_changed=False,
starpilot_toggles=self.starpilot_toggles,
)
assert self.v_cruise_helper.v_cruise_kph == pytest.approx(35)
def test_hard_press_uses_long_press_interval(self):
self.enable(52 * CV.MPH_TO_MS, False)
initial_v_cruise_kph = self.v_cruise_helper.v_cruise_kph

Some files were not shown because too many files have changed in this diff Show More