From 5ef9fbda99cbc2834b5649de01575847d9744cdf Mon Sep 17 00:00:00 2001 From: firestar5683 <168790843+firestar5683@users.noreply.github.com> Date: Sat, 15 Aug 2026 23:35:50 -0500 Subject: [PATCH] yaas --- .../opendbc/car/hyundai/carcontroller.py | 7 ++- .../opendbc/car/hyundai/hyundaicanfd.py | 2 + .../opendbc/car/hyundai/tests/test_hyundai.py | 53 +++++++++++++++++++ selfdrive/ui/soundd.py | 11 ++-- starpilot/common/cpu_throttle.py | 11 ++-- starpilot/common/tests/test_cpu_throttle.py | 9 ++++ starpilot/system/adj_spot_monitor_vision.py | 3 +- starpilot/system/speed_limit_vision.py | 4 +- system/micd.py | 3 -- 9 files changed, 88 insertions(+), 15 deletions(-) diff --git a/opendbc_repo/opendbc/car/hyundai/carcontroller.py b/opendbc_repo/opendbc/car/hyundai/carcontroller.py index 3245b7809..b5c6fb9ab 100644 --- a/opendbc_repo/opendbc/car/hyundai/carcontroller.py +++ b/opendbc_repo/opendbc/car/hyundai/carcontroller.py @@ -412,6 +412,10 @@ def preserve_stock_canfd_lfa_status(car_fingerprint) -> bool: return car_fingerprint not in (CAR.KIA_CARNIVAL_4TH_GEN, CAR.KIA_CARNIVAL_2025, CAR.KIA_CARNIVAL_HEV_4TH_GEN) +def preserve_stock_canfd_lkas_status(car_fingerprint) -> bool: + return car_fingerprint not in (CAR.KIA_CARNIVAL_4TH_GEN, CAR.KIA_CARNIVAL_2025, CAR.KIA_CARNIVAL_HEV_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 @@ -806,7 +810,8 @@ class CarController(CarControllerBase): # payload. Forwarding its stock status bits leaves lane-safety state asserted # while StarPilot is suppressing the stock LFA path. preserve_stock_lkas = bool(self.CP.flags & HyundaiFlags.CANFD_LKA_STEERING) and \ - not self.long_active_ecu and self.CP.carFingerprint != CAR.GENESIS_GV70_ELECTRIFIED_1ST_GEN + not self.long_active_ecu and self.CP.carFingerprint != CAR.GENESIS_GV70_ELECTRIFIED_1ST_GEN and \ + preserve_stock_canfd_lkas_status(self.CP.carFingerprint) angle_lkas_alt = bool(self.CP.flags & HyundaiFlags.CANFD_ANGLE_STEERING and self.CP.flags & HyundaiFlags.CANFD_LKA_STEERING_ALT) ccnc_angle_long = self.CP.carFingerprint in CANFD_ANGLE_LONGITUDINAL_CAR and \ diff --git a/opendbc_repo/opendbc/car/hyundai/hyundaicanfd.py b/opendbc_repo/opendbc/car/hyundai/hyundaicanfd.py index 14070bb6c..fe5b8b457 100644 --- a/opendbc_repo/opendbc/car/hyundai/hyundaicanfd.py +++ b/opendbc_repo/opendbc/car/hyundai/hyundaicanfd.py @@ -183,6 +183,8 @@ def create_steering_messages(packer, CP, CAN, enabled, lat_active, apply_torque, else: lkas_values = copy.copy(control_values) lkas_values["LKA_AVAILABLE"] = 0 + if CP.carFingerprint in (CAR.KIA_CARNIVAL_4TH_GEN, CAR.KIA_CARNIVAL_2025, CAR.KIA_CARNIVAL_HEV_4TH_GEN): + lkas_values["DAMP_FACTOR"] = 100 if lfa_base_values: # Preserve stock UI/status fields and only override the actuation-relevant signals. diff --git a/opendbc_repo/opendbc/car/hyundai/tests/test_hyundai.py b/opendbc_repo/opendbc/car/hyundai/tests/test_hyundai.py index 1d5748f42..30267eb48 100644 --- a/opendbc_repo/opendbc/car/hyundai/tests/test_hyundai.py +++ b/opendbc_repo/opendbc/car/hyundai/tests/test_hyundai.py @@ -19,6 +19,7 @@ from opendbc.car.hyundai.carcontroller import CarController, Ioniq6LongitudinalT should_use_ev6_gt_line_stop_direct_tracking, \ should_track_stop_accel_directly_for_car, \ preserve_stock_canfd_lfa_status, \ + preserve_stock_canfd_lkas_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 @@ -128,7 +129,9 @@ class TestHyundaiFingerprint: @pytest.mark.parametrize("candidate", (CAR.KIA_CARNIVAL_4TH_GEN, CAR.KIA_CARNIVAL_2025, CAR.KIA_CARNIVAL_HEV_4TH_GEN)) def test_carnival_uses_clean_canfd_lfa_status(self, candidate): assert not preserve_stock_canfd_lfa_status(candidate) + assert not preserve_stock_canfd_lkas_status(candidate) assert preserve_stock_canfd_lfa_status(CAR.HYUNDAI_IONIQ_6) + assert preserve_stock_canfd_lkas_status(CAR.HYUNDAI_IONIQ_6) CP = CarParams.new_message() CP.carFingerprint = candidate @@ -154,6 +157,56 @@ class TestHyundaiFingerprint: active_cluster_msg = hyundaicanfd.create_lfahda_cluster(packer, can_bus, True, None, lfa_icon=2) assert active_cluster_msg[1] == bytes.fromhex("cdfb0180000001000000000000000000") + @pytest.mark.parametrize("candidate", (CAR.KIA_CARNIVAL_2025, CAR.KIA_CARNIVAL_HEV_4TH_GEN)) + def test_carnival_hda2_clears_stock_lkas_warning_status(self, candidate): + CP = CarParams.new_message() + CP.carFingerprint = candidate + CP.flags = int(HyundaiFlags.CANFD | HyundaiFlags.CCNC | HyundaiFlags.CANFD_LKA_STEERING | + HyundaiFlags.CANFD_LKA_STEERING_ALT) + CP.openpilotLongitudinalControl = False + + controller = CarController(DBC[CP.carFingerprint], CP) + controller.frame = 1 + can_bus = CanBus(CP) + parser = CANParser(DBC[CP.carFingerprint][Bus.pt], [("LKAS_ALT", 0)], can_bus.ACAN) + stock_lkas = { + "CHECKSUM": 1234, + "COUNTER": 42, + "LKA_MODE": 0, + "LKA_AVAILABLE": 0, + "LKA_WARNING": 0, + "LKA_ICON": 0, + "FCA_SYSWARN": 1, + "TORQUE_REQUEST": 0, + "STEER_REQ": 0, + "LFA_BUTTON": 0, + "LKA_ASSIST": 1, + "STEER_MODE": 2, + "NEW_SIGNAL_2": 3, + "HAS_LANE_SAFETY": 0, + "DAMP_FACTOR": 100, + } + cc = SimpleNamespace(enabled=False, latActive=True, + actuators=SimpleNamespace(longControlState=LongCtrlState.off), + leftBlinker=False, rightBlinker=False, + hudControl=SimpleNamespace()) + cs = SimpleNamespace(stock_lfa_msg=None, stock_lkas_msg=stock_lkas, + out=SimpleNamespace(standstill=False, steeringAngleDeg=0.0, + gearShifter=structs.CarState.GearShifter.drive)) + + msgs = controller.create_canfd_msgs(0, True, 123, 0.0, 0.0, 0.0, False, + cc.hudControl, cs, cc, get_test_toggles(), lka_icon=2, lfa_icon=2) + lkas_msgs = [msg for msg in msgs if msg[0] == 0x110] + assert len(lkas_msgs) == 1 + + parser.update([(1, lkas_msgs)]) + assert parser.can_valid + assert parser.vl["LKAS_ALT"]["FCA_SYSWARN"] == 0 + assert parser.vl["LKAS_ALT"]["LKA_ASSIST"] == 0 + assert parser.vl["LKAS_ALT"]["DAMP_FACTOR"] == 100 + assert parser.vl["LKAS_ALT"]["TORQUE_REQUEST"] == 123 + assert parser.vl["LKAS_ALT"]["STEER_REQ"] == 1 + def test_canfd_torque_bsm_parser_registers_rear_blindspots(self): CP = CarParams.new_message() CP.carFingerprint = CAR.GENESIS_GV70_ELECTRIFIED_1ST_GEN diff --git a/selfdrive/ui/soundd.py b/selfdrive/ui/soundd.py index 4aa02cf78..6028ffafa 100644 --- a/selfdrive/ui/soundd.py +++ b/selfdrive/ui/soundd.py @@ -111,6 +111,7 @@ class Soundd: self.openpilot_crashed_played = False self.auto_volume = 0 + self.pending_stream_status = None self.previous_sound_pack = None self.previous_sound_source_signature = None @@ -193,7 +194,7 @@ class Soundd: def callback(self, data_out: np.ndarray, frames: int, time, status) -> None: if status: - cloudlog.warning(f"soundd stream over/underflow: {status}") + self.pending_stream_status = status data_out[:frames, 0] = self.get_sound_data(frames) def update_alert(self, new_alert): @@ -256,9 +257,6 @@ class Soundd: @retry(attempts=10, delay=3) def get_stream(self, sd): - # reload sounddevice to reinitialize portaudio - sd._terminate() - sd._initialize() return sd.OutputStream(channels=1, samplerate=SAMPLE_RATE, callback=self.callback, blocksize=SAMPLE_BUFFER) def start_stream(self, sd): @@ -297,6 +295,11 @@ class Soundd: while True: sm.update(0) + if self.pending_stream_status is not None: + status = self.pending_stream_status + self.pending_stream_status = None + cloudlog.warning(f"soundd stream over/underflow: {status}") + if sm.updated['soundPressure'] and self.current_alert == AudibleAlert.none: # only update volume filter when not playing alert self.spl_filter_weighted.update(sm["soundPressure"].soundPressureWeightedDb) self.current_volume = self.calculate_volume(float(self.spl_filter_weighted.x)) diff --git a/starpilot/common/cpu_throttle.py b/starpilot/common/cpu_throttle.py index 3bdee5d55..80afeda49 100644 --- a/starpilot/common/cpu_throttle.py +++ b/starpilot/common/cpu_throttle.py @@ -30,17 +30,20 @@ def _online_cpu_count() -> int | None: return None -def _online_cpu_usage(cpu_usage) -> list[float]: +def _online_cpu_usage(cpu_usage, cores=None) -> list[float]: usage = [float(value) for value in cpu_usage] online_count = _online_cpu_count() if online_count is not None and online_count < len(usage): - return usage[:online_count] + usage = usage[:online_count] + + if cores is not None: + usage = [usage[core] for core in cores if 0 <= core < len(usage)] return usage -def device_cpu_throttle_factor(cpu_usage, name="vision"): +def device_cpu_throttle_factor(cpu_usage, name="vision", cores=None): """Return a process-local, low-pass-filtered CPU throttle factor.""" - usage = _online_cpu_usage(cpu_usage) + usage = _online_cpu_usage(cpu_usage, cores=cores) if not usage: return 1.0 diff --git a/starpilot/common/tests/test_cpu_throttle.py b/starpilot/common/tests/test_cpu_throttle.py index 5a1d7bd58..74384562e 100644 --- a/starpilot/common/tests/test_cpu_throttle.py +++ b/starpilot/common/tests/test_cpu_throttle.py @@ -13,6 +13,15 @@ def test_offline_cpu_placeholders_do_not_hide_sustained_load(monkeypatch): assert cpu_throttle._compute_throttle_factor(average, 0) == pytest.approx(1.75) +def test_idle_reserved_cores_do_not_hide_affinity_core_saturation(monkeypatch): + monkeypatch.setattr(cpu_throttle, "_online_cpu_count", lambda: 8) + + usage = cpu_throttle._online_cpu_usage([74, 82, 100, 85, 92, 98, 0, 47], cores=[2]) + + assert usage == [100.0] + assert cpu_throttle._compute_throttle_factor(sum(usage) / len(usage), 1) == 4.0 + + @pytest.mark.parametrize(("online_spec", "expected"), ( ("0-3", 4), ("0-3,6-7", 6), diff --git a/starpilot/system/adj_spot_monitor_vision.py b/starpilot/system/adj_spot_monitor_vision.py index 19840d8cb..1753baa60 100644 --- a/starpilot/system/adj_spot_monitor_vision.py +++ b/starpilot/system/adj_spot_monitor_vision.py @@ -151,7 +151,8 @@ class VASMDaemon: in_followup = now < self.followup_until base = FOLLOWUP_INTERVAL if in_followup else BASE_INTERVAL cpu_usage = list(self.sm["deviceState"].cpuUsagePercent) if self.sm.valid.get("deviceState", False) else [] - factor = device_cpu_throttle_factor(cpu_usage, name="VASM") + affinity_cores = V_ASM_AFFINITY_CORES if self._slv_enabled else V_ASM_SOLO_AFFINITY_CORES + factor = device_cpu_throttle_factor(cpu_usage, name="VASM", cores=affinity_cores) self._throttle_factor = factor interval = base * factor self._throttle_reason = f"cpu_{factor:.1f}x" if factor > 1.05 else ("followup" if in_followup else "steady") diff --git a/starpilot/system/speed_limit_vision.py b/starpilot/system/speed_limit_vision.py index 282f15f74..75285b574 100644 --- a/starpilot/system/speed_limit_vision.py +++ b/starpilot/system/speed_limit_vision.py @@ -997,7 +997,7 @@ class SpeedLimitVisionDaemon: if self.coexistence_mode: cpu_usage = list(self.sm["deviceState"].cpuUsagePercent) if self.sm is not None and self.sm.valid.get("deviceState", False) else [] - factor = device_cpu_throttle_factor(cpu_usage, name="SpeedLimit") + factor = device_cpu_throttle_factor(cpu_usage, name="SpeedLimit", cores=SPEED_LIMIT_VISION_COEXISTENCE_AFFINITY_CORES) if factor > 1.05: self.last_cpu_busy = True interval *= factor @@ -1348,7 +1348,7 @@ class SpeedLimitVisionDaemon: return max(interval, MEMORY_PRESSURE_CLASSIFICATION_INTERVAL) if self.coexistence_mode: cpu_usage = list(self.sm["deviceState"].cpuUsagePercent) if self.sm is not None and self.sm.valid.get("deviceState", False) else [] - if device_cpu_throttle_factor(cpu_usage, name="SpeedLimit") > 1.05: + if device_cpu_throttle_factor(cpu_usage, name="SpeedLimit", cores=SPEED_LIMIT_VISION_COEXISTENCE_AFFINITY_CORES) > 1.05: return max(interval, TRACK_BUSY_CLASSIFICATION_INTERVAL) elif self._device_cpu_busy(): return max(interval, TRACK_BUSY_CLASSIFICATION_INTERVAL) diff --git a/system/micd.py b/system/micd.py index 580ba39d6..bfbc27227 100755 --- a/system/micd.py +++ b/system/micd.py @@ -97,9 +97,6 @@ class Mic: @retry(attempts=10, delay=3) def get_stream(self, sd, device=None): - # reload sounddevice to reinitialize portaudio - sd._terminate() - sd._initialize() kwargs = { "channels": 1, "samplerate": SAMPLE_RATE,