mirror of
https://github.com/firestar5683/StarPilot.git
synced 2026-09-06 08:03:44 +08:00
Guten Morgen
This commit is contained in:
@@ -708,6 +708,7 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
|
||||
{"SubaruSNG", {PERSISTENT, BOOL, "1", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"SubaruSNGManualParkingBrake", {PERSISTENT, BOOL, "0", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"SubaruStopStartOff", {PERSISTENT, BOOL, "0", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"SubaruRedneckCruise", {PERSISTENT, BOOL, "0", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"TacoTune", {PERSISTENT, BOOL, "0", "0", 2}},
|
||||
{"TeslaCoopSteering", {PERSISTENT, BOOL, "0", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"TestAlert", {CLEAR_ON_MANAGER_START, STRING, "", ""}},
|
||||
|
||||
@@ -245,7 +245,10 @@ class CarState(CarStateBase):
|
||||
return button_events
|
||||
|
||||
def create_lkas_button_events(self, cp: CANParser, prev_lda_button: int) -> list[structs.CarState.ButtonEvent]:
|
||||
if self.CP.carFingerprint == CAR.HYUNDAI_SONATA:
|
||||
if self.CP.carFingerprint == CAR.KIA_RAY_EV:
|
||||
self.lda_button = int(cp.vl["BCM_PO_11"]["RAY_LKAS_BTN"] != 0) \
|
||||
if cp.ts_nanos["BCM_PO_11"]["RAY_LKAS_BTN"] > 0 else 0
|
||||
elif self.CP.carFingerprint == CAR.HYUNDAI_SONATA:
|
||||
self.lda_button = int(cp.vl["BCM_PO_11"]["LDA_BTN"]) if cp.ts_nanos["BCM_PO_11"]["LDA_BTN"] > 0 else 0
|
||||
elif self.CP.carFingerprint == CAR.HYUNDAI_SONATA_HYBRID:
|
||||
self.lda_button = self.get_sonata_hybrid_lkas_button_state(cp)
|
||||
|
||||
@@ -1018,6 +1018,46 @@ class TestHyundaiFingerprint:
|
||||
assert ret.cruiseState.enabled
|
||||
assert ret.cruiseState.speed == pytest.approx(10 * 0.2777778)
|
||||
|
||||
def test_kia_ray_ev_decodes_bcm_lkas_button_pulse(self):
|
||||
toggles = get_test_toggles()
|
||||
CP = CarInterface.get_params(CAR.KIA_RAY_EV, gen_empty_fingerprint(), [], True, False, False, toggles)
|
||||
FPCP = CarInterface.get_starpilot_params(CAR.KIA_RAY_EV, gen_empty_fingerprint(), [], CP, toggles)
|
||||
car_state = CarState(CP, FPCP)
|
||||
can_parsers = car_state.get_can_parsers(CP)
|
||||
packer = CANPacker(DBC[CP.carFingerprint][Bus.pt])
|
||||
|
||||
def update(ray_lkas_button: int, frame: int):
|
||||
msg = packer.make_can_msg("BCM_PO_11", 0, {"RAY_LKAS_BTN": ray_lkas_button})
|
||||
can_parsers[Bus.pt].update([(frame, [msg])])
|
||||
return car_state.update(can_parsers, toggles)[0]
|
||||
|
||||
update(0, 1)
|
||||
ret = update(1, 2)
|
||||
assert any(be.type == ButtonType.lkas and be.pressed for be in ret.buttonEvents)
|
||||
|
||||
ret = update(0, 3)
|
||||
assert any(be.type == ButtonType.lkas and not be.pressed for be in ret.buttonEvents)
|
||||
|
||||
ret = update(2, 4)
|
||||
assert any(be.type == ButtonType.lkas and be.pressed for be in ret.buttonEvents)
|
||||
|
||||
def test_non_ray_does_not_use_ray_lkas_signal(self):
|
||||
CP = CarInterface.get_params(CAR.KIA_FORTE_2021_NON_SCC, gen_empty_fingerprint(), [], False, False, False, None)
|
||||
car_state = CarState(CP, CarInterface.get_starpilot_params(CAR.KIA_FORTE_2021_NON_SCC,
|
||||
gen_empty_fingerprint(), [], CP, get_test_toggles()))
|
||||
parser_cycle = SimpleNamespace(
|
||||
vl={
|
||||
"CLU13": {"CF_Clu_LdwsLkasSW": 0},
|
||||
"BCM_PO_11": {"LDA_BTN": 0, "RAY_LKAS_BTN": 1},
|
||||
},
|
||||
ts_nanos={
|
||||
"CLU13": {"CF_Clu_LdwsLkasSW": 1},
|
||||
"BCM_PO_11": {"LDA_BTN": 1, "RAY_LKAS_BTN": 1},
|
||||
},
|
||||
)
|
||||
|
||||
assert not car_state.create_lkas_button_events(parser_cycle, 0)
|
||||
|
||||
def test_hyundai_redneck_cruise_availability(self, monkeypatch):
|
||||
class FakeParams:
|
||||
def __init__(self, *args, **kwargs):
|
||||
|
||||
@@ -22,7 +22,7 @@ from opendbc.car.honda.values import CAR as HONDA, HONDA_BOSCH, HondaFlags, Hond
|
||||
from opendbc.car.hyundai.hyundaicanfd import CanBus
|
||||
from opendbc.car.hyundai.values import CAR as HYUNDAI, CANFD_CAR, HyundaiFlags, HyundaiStarPilotFlags, HyundaiStarPilotSafetyFlags, ALT_BUS_LDA_BUTTON_CARS
|
||||
from opendbc.car.mock.values import CAR as MOCK
|
||||
from opendbc.car.subaru.values import CAR as SUBARU, SubaruSafetyFlags
|
||||
from opendbc.car.subaru.values import CAR as SUBARU, SUBARU_REDNECK_CRUISE_CARS, SubaruSafetyFlags
|
||||
from opendbc.car.toyota.values import CAR as TOYOTA, NO_DSU_CAR, TSS2_CAR, UNSUPPORTED_DSU_CAR, ToyotaStarPilotFlags, ToyotaSafetyFlags
|
||||
from opendbc.car.values import PLATFORMS
|
||||
from opendbc.can import CANParser
|
||||
@@ -300,6 +300,14 @@ class CarInterfaceBase(ABC):
|
||||
if getattr(starpilot_toggles, "subaru_sng", False):
|
||||
fp_ret.safetyConfigs[-1].safetyParam |= SubaruSafetyFlags.STOP_AND_GO.value
|
||||
|
||||
fp_ret.redneckCruiseAvailable = candidate in SUBARU_REDNECK_CRUISE_CARS
|
||||
if fp_ret.redneckCruiseAvailable and params.get_bool("SubaruRedneckCruise") and \
|
||||
not CP.openpilotLongitudinalControl:
|
||||
fp_ret.pcmCruiseSpeed = False
|
||||
CP.openpilotLongitudinalControl = True
|
||||
CP.safetyConfigs[-1].safetyParam |= SubaruSafetyFlags.REDNECK_CRUISE.value
|
||||
fp_ret.safetyConfigs[-1].safetyParam |= SubaruSafetyFlags.REDNECK_CRUISE.value
|
||||
|
||||
return fp_ret
|
||||
|
||||
@staticmethod
|
||||
|
||||
@@ -37,6 +37,8 @@ _STOP_START_STARTUP_DELAY_FRAMES = 100
|
||||
_STOP_START_STARTUP_DEADLINE_FRAMES = 1000
|
||||
_STOP_START_PULSE_FRAMES = 30
|
||||
_STOP_START_PULSE_PERIOD_FRAMES = 5
|
||||
_REDNECK_BUTTON_INTERVAL_FRAMES = 10
|
||||
_REDNECK_BUTTON_COPIES = 2
|
||||
|
||||
|
||||
def get_safety_CP():
|
||||
@@ -87,6 +89,7 @@ class CarController(CarControllerBase):
|
||||
self.stop_start_initial_state = None
|
||||
self.stop_start_counter = 0
|
||||
self.stop_start_acknowledged = False
|
||||
self.last_redneck_button_frame = 0
|
||||
|
||||
def _stop_start_off_request(self, CC, CS, starpilot_toggles):
|
||||
"""Send one bounded Subaru Stop/Start OFF request after ignition.
|
||||
@@ -409,6 +412,10 @@ class CarController(CarControllerBase):
|
||||
actuators = CC.actuators
|
||||
hud_control = CC.hudControl
|
||||
pcm_cancel_cmd = CC.cruiseControl.cancel
|
||||
subaru_redneck_cruise = bool(
|
||||
self.CP.carFingerprint == CAR.SUBARU_IMPREZA_2020 and
|
||||
getattr(starpilot_toggles, "subaru_redneck_cruise", False)
|
||||
)
|
||||
|
||||
can_sends = []
|
||||
|
||||
@@ -473,7 +480,8 @@ class CarController(CarControllerBase):
|
||||
else:
|
||||
if self.frame % 10 == 0:
|
||||
can_sends.append(subarucan.create_es_dashstatus(self.packer, self.frame // 10, CS.es_dashstatus_msg, CC.enabled,
|
||||
self.CP.openpilotLongitudinalControl, CC.longActive, hud_control.leadVisible,
|
||||
self.CP.openpilotLongitudinalControl and not subaru_redneck_cruise,
|
||||
CC.longActive, hud_control.leadVisible,
|
||||
self.status_bus))
|
||||
|
||||
can_sends.append(subarucan.create_es_lkas_state(self.packer, self.frame // 10, CS.es_lkas_state_msg, CC.latActive, hud_control.visualAlert,
|
||||
@@ -491,7 +499,7 @@ class CarController(CarControllerBase):
|
||||
can_sends.append(subarucan.create_brake_pedal(self.packer, self.frame // 2, CS.brake_pedal_msg,
|
||||
speed_cmd, pcm_cancel_cmd))
|
||||
|
||||
if self.CP.openpilotLongitudinalControl:
|
||||
if self.CP.openpilotLongitudinalControl and not subaru_redneck_cruise:
|
||||
if self.frame % 5 == 0:
|
||||
can_sends.append(subarucan.create_es_status(self.packer, self.frame // 5, CS.es_status_msg,
|
||||
self.CP.openpilotLongitudinalControl, CC.longActive, cruise_rpm))
|
||||
@@ -507,6 +515,20 @@ class CarController(CarControllerBase):
|
||||
bus = CanBus.alt_for_cp(self.CP) if self.CP.flags & SubaruFlags.GLOBAL_GEN2 else self.main_bus
|
||||
can_sends.append(subarucan.create_es_distance(self.packer, CS.es_distance_msg["COUNTER"] + 1, CS.es_distance_msg, bus, pcm_cancel_cmd))
|
||||
|
||||
if subaru_redneck_cruise:
|
||||
redneck_button = {
|
||||
1: subarucan.CRUISE_BUTTON_RESUME,
|
||||
2: subarucan.CRUISE_BUTTON_SET,
|
||||
}.get(getattr(CS, "redneck_send_button", 0))
|
||||
cruise_buttons_msg = getattr(CS, "cruise_buttons_msg", None)
|
||||
if redneck_button and cruise_buttons_msg and self.frame - self.last_redneck_button_frame >= _REDNECK_BUTTON_INTERVAL_FRAMES:
|
||||
counter = (int(cruise_buttons_msg["COUNTER"]) + 1) % 0x10
|
||||
for copy_idx in range(_REDNECK_BUTTON_COPIES):
|
||||
can_sends.append(subarucan.create_cruise_buttons(
|
||||
self.packer, counter + copy_idx, cruise_buttons_msg, redneck_button, self.main_bus,
|
||||
))
|
||||
self.last_redneck_button_frame = self.frame
|
||||
|
||||
if self.CP.flags & SubaruFlags.DISABLE_EYESIGHT:
|
||||
# Tester present (keeps eyesight disabled)
|
||||
if self.frame % 100 == 0:
|
||||
|
||||
@@ -1,12 +1,20 @@
|
||||
import copy
|
||||
from cereal import custom
|
||||
from opendbc.can import CANDefine, CANParser
|
||||
from opendbc.car import Bus, structs
|
||||
from opendbc.car import Bus, create_button_events, structs
|
||||
from opendbc.car.common.conversions import Conversions as CV
|
||||
from opendbc.car.interfaces import CarStateBase
|
||||
from opendbc.car.subaru.values import DBC, CanBus, SUBARU_STOP_START_CARS, SubaruFlags
|
||||
from opendbc.car.subaru.values import DBC, CanBus, SUBARU_REDNECK_CRUISE_CARS, SUBARU_STOP_START_CARS, SubaruFlags
|
||||
from opendbc.car import CanSignalRateCalculator
|
||||
|
||||
ButtonType = structs.CarState.ButtonEvent.Type
|
||||
|
||||
SUBARU_CRUISE_BUTTONS = {
|
||||
"Main": ButtonType.mainCruise,
|
||||
"Set": ButtonType.decelCruise,
|
||||
"Resume": ButtonType.accelCruise,
|
||||
}
|
||||
|
||||
|
||||
class CarState(CarStateBase):
|
||||
def __init__(self, CP, FPCP):
|
||||
@@ -18,6 +26,8 @@ class CarState(CarStateBase):
|
||||
self.dashlights_msg = {}
|
||||
self.dashlights_dat = b""
|
||||
self.stop_start_state = 0
|
||||
self.cruise_buttons_msg = {}
|
||||
self.cruise_buttons = {button: 0 for button in SUBARU_CRUISE_BUTTONS}
|
||||
|
||||
def update(self, can_parsers, starpilot_toggles) -> structs.CarState:
|
||||
cp = can_parsers[Bus.pt]
|
||||
@@ -136,6 +146,17 @@ class CarState(CarStateBase):
|
||||
self.es_status_msg = copy.copy(cp_es_brake.vl["ES_Status"])
|
||||
self.cruise_control_msg = copy.copy(cp_cruise.vl["CruiseControl"])
|
||||
|
||||
if self.CP.carFingerprint in SUBARU_REDNECK_CRUISE_CARS:
|
||||
cruise_buttons = cp.vl["Cruise_Buttons"]
|
||||
if getattr(starpilot_toggles, "subaru_redneck_cruise", False):
|
||||
ret.buttonEvents = []
|
||||
for button, button_type in SUBARU_CRUISE_BUTTONS.items():
|
||||
ret.buttonEvents.extend(create_button_events(
|
||||
int(bool(cruise_buttons[button])), self.cruise_buttons[button], {1: button_type},
|
||||
))
|
||||
self.cruise_buttons = {button: int(bool(cruise_buttons[button])) for button in SUBARU_CRUISE_BUTTONS}
|
||||
self.cruise_buttons_msg = copy.copy(cruise_buttons)
|
||||
|
||||
if not (self.CP.flags & SubaruFlags.HYBRID):
|
||||
self.es_distance_msg = copy.copy(cp_es_distance.vl["ES_Distance"])
|
||||
|
||||
|
||||
@@ -3,6 +3,10 @@ from opendbc.car.subaru.values import CanBus
|
||||
|
||||
VisualAlert = structs.CarControl.HUDControl.VisualAlert
|
||||
|
||||
CRUISE_BUTTON_MAIN = 1
|
||||
CRUISE_BUTTON_SET = 2
|
||||
CRUISE_BUTTON_RESUME = 3
|
||||
|
||||
|
||||
def create_steering_control(packer, apply_torque, steer_req):
|
||||
values = {
|
||||
@@ -67,6 +71,19 @@ def create_es_distance(packer, frame, es_distance_msg, bus, pcm_cancel_cmd, long
|
||||
return packer.make_can_msg("ES_Distance", bus, values)
|
||||
|
||||
|
||||
def create_cruise_buttons(packer, frame, cruise_buttons_msg, button, bus=CanBus.main):
|
||||
values = {s: cruise_buttons_msg[s] for s in [
|
||||
"CHECKSUM",
|
||||
"Signal1",
|
||||
"Signal2",
|
||||
]}
|
||||
values["COUNTER"] = frame % 0x10
|
||||
values["Main"] = button == CRUISE_BUTTON_MAIN
|
||||
values["Set"] = button == CRUISE_BUTTON_SET
|
||||
values["Resume"] = button == CRUISE_BUTTON_RESUME
|
||||
return packer.make_can_msg("Cruise_Buttons", bus, values)
|
||||
|
||||
|
||||
def create_es_lkas_state(packer, frame, es_lkas_state_msg, enabled, visual_alert, left_line, right_line, left_lane_depart, right_lane_depart,
|
||||
bus=CanBus.main):
|
||||
values = {s: es_lkas_state_msg[s] for s in [
|
||||
|
||||
@@ -5,7 +5,7 @@ from types import SimpleNamespace
|
||||
import pytest
|
||||
|
||||
from opendbc.can import CANPacker, CANParser
|
||||
from opendbc.car import Bus, fw_versions, structs
|
||||
from opendbc.car import Bus, fw_versions, gen_empty_fingerprint, structs
|
||||
from opendbc.car.fw_query_definitions import StdQueries
|
||||
from opendbc.car.subaru import subarucan
|
||||
from opendbc.car.subaru.carcontroller import CarController
|
||||
@@ -67,6 +67,56 @@ def test_preglobal_sng_does_not_send_standstill_keepalive_without_manual_toggle(
|
||||
assert speed_cmd is False
|
||||
|
||||
|
||||
def test_redneck_cruise_buttons_use_resume_for_increase_and_set_for_decrease():
|
||||
dbc = DBC[CAR.SUBARU_IMPREZA_2020][Bus.pt]
|
||||
packer = CANPacker(dbc)
|
||||
parser = CANParser(dbc, [("Cruise_Buttons", 0)], CanBus.main)
|
||||
stock_buttons = defaultdict(int)
|
||||
|
||||
resume_msg = subarucan.create_cruise_buttons(
|
||||
packer, 1, stock_buttons, subarucan.CRUISE_BUTTON_RESUME, CanBus.main,
|
||||
)
|
||||
parser.update([(1, [resume_msg])])
|
||||
assert parser.vl["Cruise_Buttons"]["Resume"] == 1
|
||||
assert parser.vl["Cruise_Buttons"]["Set"] == 0
|
||||
|
||||
set_msg = subarucan.create_cruise_buttons(
|
||||
packer, 2, stock_buttons, subarucan.CRUISE_BUTTON_SET, CanBus.main,
|
||||
)
|
||||
parser.update([(2, [set_msg])])
|
||||
assert parser.vl["Cruise_Buttons"]["Resume"] == 0
|
||||
assert parser.vl["Cruise_Buttons"]["Set"] == 1
|
||||
|
||||
|
||||
def test_redneck_cruise_is_only_available_on_the_experimental_impreza(monkeypatch):
|
||||
class FakeParams:
|
||||
def __init__(self, **_kwargs):
|
||||
pass
|
||||
|
||||
def get_bool(self, key):
|
||||
return key == "SubaruRedneckCruise"
|
||||
|
||||
monkeypatch.setattr("opendbc.car.interfaces.Params", FakeParams)
|
||||
toggles = SimpleNamespace(subaru_sng=False)
|
||||
|
||||
impreza_cp = CarInterface.get_non_essential_params(CAR.SUBARU_IMPREZA_2020)
|
||||
impreza_fpcp = CarInterface.get_starpilot_params(
|
||||
CAR.SUBARU_IMPREZA_2020, gen_empty_fingerprint(), [], impreza_cp, toggles,
|
||||
)
|
||||
assert impreza_fpcp.redneckCruiseAvailable
|
||||
assert not impreza_fpcp.pcmCruiseSpeed
|
||||
assert impreza_cp.openpilotLongitudinalControl
|
||||
assert impreza_cp.safetyConfigs[0].safetyParam & SubaruSafetyFlags.REDNECK_CRUISE
|
||||
|
||||
old_impreza_cp = CarInterface.get_non_essential_params(CAR.SUBARU_IMPREZA)
|
||||
old_impreza_fpcp = CarInterface.get_starpilot_params(
|
||||
CAR.SUBARU_IMPREZA, gen_empty_fingerprint(), [], old_impreza_cp, toggles,
|
||||
)
|
||||
assert not old_impreza_fpcp.redneckCruiseAvailable
|
||||
assert old_impreza_fpcp.pcmCruiseSpeed
|
||||
assert not old_impreza_cp.openpilotLongitudinalControl
|
||||
|
||||
|
||||
class TestSubaruFingerprint:
|
||||
def test_eyesight_queries_do_not_change_diagnostic_state(self, monkeypatch):
|
||||
camera_requests = [request for request in FW_QUERY_CONFIG.requests if CarParams.Ecu.fwdCamera in request.whitelist_ecus]
|
||||
|
||||
@@ -89,6 +89,7 @@ class SubaruSafetyFlags(IntFlag):
|
||||
D_PLATFORM_CAMERA = 64
|
||||
FIXED_ANGLE_LIMITS = 128
|
||||
STOP_START_BUTTON = 256
|
||||
REDNECK_CRUISE = 512
|
||||
LEGACY_2025_ANGLE_LIMITS = FIXED_ANGLE_LIMITS
|
||||
|
||||
|
||||
@@ -275,6 +276,10 @@ SUBARU_STOP_START_CARS = (
|
||||
CAR.SUBARU_LEGACY_2025,
|
||||
)
|
||||
|
||||
SUBARU_REDNECK_CRUISE_CARS = (
|
||||
CAR.SUBARU_IMPREZA_2020,
|
||||
)
|
||||
|
||||
SUBARU_VERSION_REQUEST = bytes([uds.SERVICE_TYPE.READ_DATA_BY_IDENTIFIER]) + \
|
||||
p16(uds.DATA_IDENTIFIER_TYPE.APPLICATION_DATA_IDENTIFICATION)
|
||||
SUBARU_VERSION_RESPONSE = bytes([uds.SERVICE_TYPE.READ_DATA_BY_IDENTIFIER + 0x40]) + \
|
||||
|
||||
@@ -11,7 +11,7 @@ from opendbc.car.interfaces import CarControllerBase
|
||||
from opendbc.car.toyota import toyotacan
|
||||
from opendbc.car.toyota.values import CAR, MIN_ACC_SPEED, NO_STOP_TIMER_CAR, PEDAL_TRANSITION, TSS2_CAR, \
|
||||
CarControllerParams, ToyotaFlags, \
|
||||
UNSUPPORTED_DSU_CAR, LEGACY_PRIUS_CAR
|
||||
UNSUPPORTED_DSU_CAR, LEGACY_PRIUS_CAR, RADAR_ACC_CAR, SECOC_CAR
|
||||
from opendbc.can import CANPacker
|
||||
|
||||
Ecu = structs.CarParams.Ecu
|
||||
@@ -49,6 +49,8 @@ MAX_USER_TORQUE = 500
|
||||
PARK = structs.CarState.GearShifter.park
|
||||
REVERSE = structs.CarState.GearShifter.reverse
|
||||
|
||||
TOYOTA_AUTO_HOLD_CARS = TSS2_CAR - RADAR_ACC_CAR - SECOC_CAR
|
||||
|
||||
# Lock / unlock door commands - Credit goes to AlexandreSato!
|
||||
LOCK_CMD = b"\x40\x05\x30\x11\x00\x80\x00\x00"
|
||||
UNLOCK_CMD = b"\x40\x05\x30\x11\x00\x40\x00\x00"
|
||||
@@ -72,6 +74,14 @@ def should_bypass_toyota_long_pid(CP, starpilot_toggles=None) -> bool:
|
||||
) or highlander_sdsu)
|
||||
|
||||
|
||||
def supports_toyota_auto_hold(CP, auto_hold_enabled: bool) -> bool:
|
||||
return (
|
||||
auto_hold_enabled and
|
||||
CP.carFingerprint in TOYOTA_AUTO_HOLD_CARS and
|
||||
bool(CP.flags & ToyotaFlags.AUTO_BRAKE_HOLD.value)
|
||||
)
|
||||
|
||||
|
||||
def get_long_tune(CP, params):
|
||||
kiBP = [2., 5.]
|
||||
kiV = [0.5, 0.25]
|
||||
@@ -243,11 +253,8 @@ class CarController(CarControllerBase):
|
||||
self.secoc_prev_reset_counter = 0
|
||||
|
||||
self.doors_locked = False
|
||||
self.auto_brake_hold = bool(self.CP.flags & ToyotaFlags.AUTO_BRAKE_HOLD.value)
|
||||
self.brake_hold_active = False
|
||||
self._brake_hold_counter = 0
|
||||
self._brake_hold_reset = False
|
||||
self._prev_brake_pressed = False
|
||||
|
||||
def _compute_interceptor_gas_cmd(self, CC, CS):
|
||||
if not (self.CP.enableGasInterceptorDEPRECATED and self.CP.openpilotLongitudinalControl and CC.longActive):
|
||||
@@ -299,15 +306,12 @@ class CarController(CarControllerBase):
|
||||
not CS.out.gasPressed and not CS.out.cruiseState.enabled and
|
||||
CS.out.gearShifter not in (PARK, REVERSE))
|
||||
|
||||
if brake_hold_allowed:
|
||||
if brake_hold_allowed and not self.brake_hold_active and CS.out.brakePressed:
|
||||
self._brake_hold_counter += 1
|
||||
self.brake_hold_active = self._brake_hold_counter > brake_hold_allowed_timer and not self._brake_hold_reset
|
||||
self._brake_hold_reset = not self._prev_brake_pressed and CS.out.brakePressed and not self._brake_hold_reset
|
||||
else:
|
||||
self.brake_hold_active = self._brake_hold_counter > brake_hold_allowed_timer
|
||||
elif not brake_hold_allowed:
|
||||
self._brake_hold_counter = 0
|
||||
self.brake_hold_active = False
|
||||
self._brake_hold_reset = False
|
||||
self._prev_brake_pressed = CS.out.brakePressed
|
||||
|
||||
if self.frame % 2 == 0:
|
||||
can_sends.append(toyotacan.create_brake_hold_command(self.packer, self.frame, CS.pre_collision_2, self.brake_hold_active))
|
||||
@@ -409,8 +413,11 @@ class CarController(CarControllerBase):
|
||||
# *** gas and brake ***
|
||||
|
||||
self._update_standstill_request(CC, CS, actuators, starpilot_toggles)
|
||||
if self.auto_brake_hold:
|
||||
if supports_toyota_auto_hold(self.CP, getattr(starpilot_toggles, "toyota_auto_hold", False)):
|
||||
can_sends.extend(self.create_auto_brake_hold_messages(CS))
|
||||
elif self.brake_hold_active:
|
||||
self._brake_hold_counter = 0
|
||||
self.brake_hold_active = False
|
||||
|
||||
interceptor_gas_cmd = self._compute_interceptor_gas_cmd(CC, CS)
|
||||
|
||||
|
||||
@@ -12,7 +12,8 @@ from opendbc.car.toyota.carcontroller import CarController, get_camry_hybrid_fee
|
||||
get_prius_positive_feedforward_scale, \
|
||||
limit_interceptor_pcm_accel, \
|
||||
limit_interceptor_stopping_accel, limit_no_lead_cruise_sign_flip, \
|
||||
limit_prius_stopping_accel, should_bypass_toyota_long_pid, update_permit_braking
|
||||
limit_prius_stopping_accel, should_bypass_toyota_long_pid, supports_toyota_auto_hold, \
|
||||
update_permit_braking
|
||||
from opendbc.car.toyota.carstate import CarState, LKAS_BUTTON_CAR, calculate_interceptor_gas_pressed, create_lkas_button_events
|
||||
from opendbc.car.toyota.fingerprints import FW_VERSIONS
|
||||
from opendbc.car.toyota.interface import CarInterface
|
||||
@@ -205,6 +206,22 @@ class TestToyotaInterfaces:
|
||||
assert car_params.flags & ToyotaFlags.AUTO_BRAKE_HOLD.value
|
||||
assert car_params.alternativeExperience & ALTERNATIVE_EXPERIENCE.ALLOW_AEB
|
||||
|
||||
def test_auto_hold_is_disabled_by_default(self):
|
||||
params = Params()
|
||||
params.remove("ToyotaAutoHold")
|
||||
car_params = CarInterface.get_params(
|
||||
CAR.TOYOTA_CAMRY_TSS2,
|
||||
{bus: {} for bus in range(8)},
|
||||
[],
|
||||
alpha_long=False,
|
||||
is_release=False,
|
||||
docs=False,
|
||||
starpilot_toggles=SimpleNamespace(),
|
||||
)
|
||||
|
||||
assert not car_params.flags & ToyotaFlags.AUTO_BRAKE_HOLD.value
|
||||
assert not car_params.alternativeExperience & ALTERNATIVE_EXPERIENCE.ALLOW_AEB
|
||||
|
||||
def test_prius_openpilot_long_uses_hybrid_long_defaults(self):
|
||||
car_params = CarInterface.get_params(
|
||||
CAR.TOYOTA_PRIUS,
|
||||
@@ -750,6 +767,74 @@ class TestToyotaCarController:
|
||||
|
||||
assert controller.standstill_req is True
|
||||
|
||||
def test_toyota_auto_hold_requires_toggle_supported_car_and_capability(self):
|
||||
CP = SimpleNamespace(
|
||||
carFingerprint=CAR.TOYOTA_CAMRY_TSS2,
|
||||
flags=ToyotaFlags.AUTO_BRAKE_HOLD.value,
|
||||
)
|
||||
|
||||
assert supports_toyota_auto_hold(CP, True)
|
||||
assert not supports_toyota_auto_hold(CP, False)
|
||||
assert not supports_toyota_auto_hold(SimpleNamespace(
|
||||
carFingerprint=CAR.TOYOTA_CAMRY_TSS2,
|
||||
flags=0,
|
||||
), True)
|
||||
assert not supports_toyota_auto_hold(SimpleNamespace(
|
||||
carFingerprint=CAR.TOYOTA_CAMRY,
|
||||
flags=ToyotaFlags.AUTO_BRAKE_HOLD.value,
|
||||
), True)
|
||||
|
||||
def test_toyota_auto_hold_latches_after_brake_press_until_gas(self):
|
||||
controller = self._make_controller()
|
||||
controller.packer = CANPacker(DBC[CAR.TOYOTA_CAMRY_TSS2][Bus.pt])
|
||||
controller.frame = 0
|
||||
controller.brake_hold_active = False
|
||||
controller._brake_hold_counter = 0
|
||||
|
||||
cs = SimpleNamespace(
|
||||
out=SimpleNamespace(
|
||||
standstill=True,
|
||||
cruiseState=SimpleNamespace(available=True, enabled=False),
|
||||
gasPressed=False,
|
||||
brakePressed=True,
|
||||
gearShifter=structs.CarState.GearShifter.drive,
|
||||
),
|
||||
pre_collision_2={},
|
||||
)
|
||||
|
||||
controller.create_auto_brake_hold_messages(cs, brake_hold_allowed_timer=0)
|
||||
assert controller.brake_hold_active
|
||||
|
||||
cs.out.brakePressed = False
|
||||
controller.frame = 2
|
||||
controller.create_auto_brake_hold_messages(cs, brake_hold_allowed_timer=0)
|
||||
assert controller.brake_hold_active
|
||||
|
||||
cs.out.gasPressed = True
|
||||
controller.frame = 4
|
||||
controller.create_auto_brake_hold_messages(cs, brake_hold_allowed_timer=0)
|
||||
assert not controller.brake_hold_active
|
||||
|
||||
def test_toyota_auto_hold_does_not_trigger_without_brake_press(self):
|
||||
controller = self._make_controller()
|
||||
controller.packer = CANPacker(DBC[CAR.TOYOTA_CAMRY_TSS2][Bus.pt])
|
||||
controller.frame = 0
|
||||
controller.brake_hold_active = False
|
||||
controller._brake_hold_counter = 0
|
||||
cs = SimpleNamespace(
|
||||
out=SimpleNamespace(
|
||||
standstill=True,
|
||||
cruiseState=SimpleNamespace(available=True, enabled=False),
|
||||
gasPressed=False,
|
||||
brakePressed=False,
|
||||
gearShifter=structs.CarState.GearShifter.drive,
|
||||
),
|
||||
pre_collision_2={},
|
||||
)
|
||||
|
||||
controller.create_auto_brake_hold_messages(cs, brake_hold_allowed_timer=0)
|
||||
assert not controller.brake_hold_active
|
||||
|
||||
def test_prius_resume_request_releases_standstill_latch(self):
|
||||
controller = self._make_controller(standstill_req=True, last_standstill=True)
|
||||
|
||||
@@ -893,14 +978,12 @@ class TestToyotaCarController:
|
||||
controller.frame = 0
|
||||
controller.brake_hold_active = False
|
||||
controller._brake_hold_counter = 0
|
||||
controller._brake_hold_reset = False
|
||||
controller._prev_brake_pressed = False
|
||||
cs = SimpleNamespace(
|
||||
out=SimpleNamespace(
|
||||
standstill=True,
|
||||
cruiseState=SimpleNamespace(available=True, enabled=False),
|
||||
gasPressed=False,
|
||||
brakePressed=False,
|
||||
brakePressed=True,
|
||||
gearShifter=structs.CarState.GearShifter.drive,
|
||||
),
|
||||
pre_collision_2={},
|
||||
|
||||
@@ -1497,6 +1497,7 @@ BO_ 913 BCM_PO_11: 8 Vector__XXX
|
||||
SG_ BCM_Door_Dri_Status : 5|1@0+ (1,0) [0|1] "" PT_ESC_ABS
|
||||
SG_ BCM_Shift_R_MT_SW_Status : 39|2@0+ (1,0) [0|3] "" PT_ESC_ABS
|
||||
SG_ LDA_BTN : 4|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ RAY_LKAS_BTN : 0|2@1+ (1,0) [0|3] "" XXX
|
||||
|
||||
BO_ 1426 LABEL11: 8 XXX
|
||||
SG_ CC_React : 34|1@1+ (1,0) [0|1] "" XXX
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
#define MSG_SUBARU_ES_DashStatus 0x321U
|
||||
#define MSG_SUBARU_ES_LKAS_State 0x322U
|
||||
#define MSG_SUBARU_ES_Infotainment 0x323U
|
||||
#define MSG_SUBARU_Cruise_Buttons 0x146U
|
||||
|
||||
#define MSG_SUBARU_ES_UDS_Request 0x787U
|
||||
|
||||
@@ -56,6 +57,9 @@
|
||||
#define SUBARU_COMMON_TX_MSGS(alt_bus) \
|
||||
{MSG_SUBARU_ES_Distance, alt_bus, 8, .check_relay = false}, \
|
||||
|
||||
#define SUBARU_REDNECK_TX_MSGS() \
|
||||
{MSG_SUBARU_Cruise_Buttons, SUBARU_MAIN_BUS, 8, .check_relay = false}, \
|
||||
|
||||
#define SUBARU_D_PLATFORM_ANGLE_TX_MSGS(bus) \
|
||||
{MSG_SUBARU_ES_LKAS_ANGLE, bus, 8, .check_relay = true}, \
|
||||
{MSG_SUBARU_ES_DashStatus, bus, 8, .check_relay = true}, \
|
||||
@@ -113,6 +117,7 @@ static bool subaru_lkas_angle = false;
|
||||
static bool subaru_d_platform = false;
|
||||
static bool subaru_fixed_angle_limits = false;
|
||||
static bool subaru_stop_start_button = false;
|
||||
static bool subaru_redneck_cruise = false;
|
||||
|
||||
static uint32_t subaru_get_checksum(const CANPacket_t *msg) {
|
||||
return (uint8_t)msg->data[0];
|
||||
@@ -297,6 +302,12 @@ static bool subaru_tx_hook(const CANPacket_t *msg) {
|
||||
violation |= subaru_get_checksum(msg) != subaru_compute_checksum(msg);
|
||||
}
|
||||
|
||||
if (msg->addr == MSG_SUBARU_Cruise_Buttons) {
|
||||
violation |= !subaru_redneck_cruise;
|
||||
violation |= msg->bus != SUBARU_MAIN_BUS;
|
||||
violation |= subaru_get_checksum(msg) != subaru_compute_checksum(msg);
|
||||
}
|
||||
|
||||
if (violation){
|
||||
tx = false;
|
||||
}
|
||||
@@ -309,6 +320,19 @@ static safety_config subaru_init(uint16_t param) {
|
||||
SUBARU_COMMON_TX_MSGS(SUBARU_MAIN_BUS)
|
||||
};
|
||||
|
||||
static const CanMsg SUBARU_REDNECK_TX_MSGS_CONFIG[] = {
|
||||
SUBARU_BASE_TX_MSGS(SUBARU_MAIN_BUS, MSG_SUBARU_ES_LKAS)
|
||||
SUBARU_COMMON_TX_MSGS(SUBARU_MAIN_BUS)
|
||||
SUBARU_REDNECK_TX_MSGS()
|
||||
};
|
||||
|
||||
static const CanMsg SUBARU_REDNECK_STOP_AND_GO_TX_MSGS_CONFIG[] = {
|
||||
SUBARU_BASE_TX_MSGS(SUBARU_MAIN_BUS, MSG_SUBARU_ES_LKAS)
|
||||
SUBARU_COMMON_TX_MSGS(SUBARU_MAIN_BUS)
|
||||
SUBARU_REDNECK_TX_MSGS()
|
||||
SUBARU_STOP_AND_GO_ADDITIONAL_TX_MSGS()
|
||||
};
|
||||
|
||||
static const CanMsg SUBARU_LONG_TX_MSGS[] = {
|
||||
SUBARU_BASE_TX_MSGS(SUBARU_MAIN_BUS, MSG_SUBARU_ES_LKAS)
|
||||
SUBARU_COMMON_LONG_TX_MSGS(SUBARU_MAIN_BUS)
|
||||
@@ -405,6 +429,9 @@ static safety_config subaru_init(uint16_t param) {
|
||||
const uint16_t SUBARU_PARAM_STOP_START_BUTTON = 256;
|
||||
subaru_stop_start_button = GET_FLAG(param, SUBARU_PARAM_STOP_START_BUTTON);
|
||||
|
||||
const uint16_t SUBARU_PARAM_REDNECK_CRUISE = 512;
|
||||
subaru_redneck_cruise = GET_FLAG(param, SUBARU_PARAM_REDNECK_CRUISE);
|
||||
|
||||
#ifdef ALLOW_DEBUG
|
||||
const uint16_t SUBARU_PARAM_LONGITUDINAL = 2;
|
||||
subaru_longitudinal = GET_FLAG(param, SUBARU_PARAM_LONGITUDINAL);
|
||||
@@ -422,7 +449,9 @@ static safety_config subaru_init(uint16_t param) {
|
||||
ret = subaru_longitudinal ? BUILD_SAFETY_CFG(subaru_gen2_rx_checks, SUBARU_GEN2_LONG_TX_MSGS) : \
|
||||
BUILD_SAFETY_CFG(subaru_gen2_rx_checks, SUBARU_GEN2_TX_MSGS);
|
||||
} else {
|
||||
ret = subaru_longitudinal ? BUILD_SAFETY_CFG(subaru_rx_checks, SUBARU_LONG_TX_MSGS) : \
|
||||
ret = subaru_redneck_cruise ? (subaru_stop_and_go ? BUILD_SAFETY_CFG(subaru_rx_checks, SUBARU_REDNECK_STOP_AND_GO_TX_MSGS_CONFIG) : \
|
||||
BUILD_SAFETY_CFG(subaru_rx_checks, SUBARU_REDNECK_TX_MSGS_CONFIG)) : \
|
||||
subaru_longitudinal ? BUILD_SAFETY_CFG(subaru_rx_checks, SUBARU_LONG_TX_MSGS) : \
|
||||
subaru_stop_and_go ? BUILD_SAFETY_CFG(subaru_rx_checks, SUBARU_STOP_AND_GO_TX_MSGS) : \
|
||||
BUILD_SAFETY_CFG(subaru_rx_checks, SUBARU_TX_MSGS);
|
||||
}
|
||||
|
||||
@@ -202,7 +202,7 @@ class CarSpecificEvents:
|
||||
ray_ev = self.CP.carFingerprint == HYUNDAI_CAR.KIA_RAY_EV
|
||||
events = self.create_common_events(
|
||||
CS, CS_prev, extra_gears=extra_gears,
|
||||
pcm_enable=self.CP.pcmCruise and not ray_ev,
|
||||
pcm_enable=self.CP.pcmCruise,
|
||||
allow_button_cancel=False,
|
||||
ignore_cruise_state=ray_ev,
|
||||
)
|
||||
|
||||
@@ -203,7 +203,8 @@ class Car:
|
||||
|
||||
self.mock_carstate = MockCarState()
|
||||
self.v_cruise_helper = VCruiseHelper(self.CP, self.FPCP)
|
||||
self.redneck_cruise = RedneckCruise(self.CP, self.FPCP) if self.CP.brand == "hyundai" and self.FPCP.redneckCruiseAvailable and not self.FPCP.pcmCruiseSpeed else None
|
||||
self.redneck_cruise = RedneckCruise(self.CP, self.FPCP) if self.CP.brand in ("hyundai", "subaru") and \
|
||||
self.FPCP.redneckCruiseAvailable and not self.FPCP.pcmCruiseSpeed else None
|
||||
|
||||
self.is_metric = self.params.get_bool("IsMetric")
|
||||
self.safe_mode = self.params.get_bool("SafeMode")
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
from cereal import car
|
||||
from types import SimpleNamespace
|
||||
|
||||
from cereal import car
|
||||
from opendbc.car.hyundai.values import CAR as HYUNDAI_CAR
|
||||
|
||||
from openpilot.selfdrive.car.car_specific import CarSpecificEvents
|
||||
from openpilot.selfdrive.car.cruise_state import should_cancel_stock_cruise, should_flag_cruise_mismatch
|
||||
|
||||
|
||||
@@ -39,3 +43,16 @@ def test_pcm_cruise_behavior_is_unchanged():
|
||||
assert should_cancel_stock_cruise(cp, cruise_enabled=True, controls_enabled=False)
|
||||
assert not should_flag_cruise_mismatch(cp, cruise_enabled=True, controls_enabled=True, effective_pcm_cruise=True)
|
||||
assert should_flag_cruise_mismatch(cp, cruise_enabled=True, controls_enabled=False, effective_pcm_cruise=True)
|
||||
|
||||
|
||||
def test_kia_ray_ev_allows_stock_cruise_to_enable_controls():
|
||||
for candidate, ignore_cruise_state in ((HYUNDAI_CAR.KIA_RAY_EV, True), (HYUNDAI_CAR.HYUNDAI_SONATA, False)):
|
||||
cp = SimpleNamespace(brand="hyundai", carFingerprint=candidate, flags=0)
|
||||
handler = CarSpecificEvents(cp)
|
||||
captured = {}
|
||||
handler.create_common_events = lambda *args, **kwargs: captured.update(kwargs)
|
||||
|
||||
handler.update(SimpleNamespace(), SimpleNamespace(), SimpleNamespace())
|
||||
|
||||
assert captured["pcm_enable"] is True
|
||||
assert captured["ignore_cruise_state"] is ignore_cruise_state
|
||||
|
||||
@@ -3713,6 +3713,7 @@
|
||||
"picker_description": "Holds Toyota/Lexus brakes at stops when cruise is available.",
|
||||
"data_type": "bool",
|
||||
"ui_type": "toggle",
|
||||
"galaxy_only": true,
|
||||
"settings_tier": "simple"
|
||||
},
|
||||
{
|
||||
@@ -4947,6 +4948,18 @@
|
||||
"parent_key": "GalaxyDeveloperMode",
|
||||
"settings_tier": "advanced"
|
||||
},
|
||||
{
|
||||
"key": "SubaruRedneckCruise",
|
||||
"label": "Subaru Redneck Cruise",
|
||||
"description": "On supported Subaru Impreza-family cars, use stock Resume/Set button presses to match the cluster set speed to StarPilot's target.",
|
||||
"picker_description": "Matches StarPilot's target speed with Subaru Resume/Set.",
|
||||
"data_type": "bool",
|
||||
"ui_type": "toggle",
|
||||
"galaxy_only": true,
|
||||
"vehicle_makes": ["Subaru"],
|
||||
"parent_key": "GalaxyDeveloperMode",
|
||||
"settings_tier": "advanced"
|
||||
},
|
||||
{
|
||||
"key": "TurnSteeringLimitMuteSpeed",
|
||||
"label": "Mute Turn Limit Alert Below",
|
||||
|
||||
@@ -199,6 +199,7 @@ SAFE_MODE_MANAGED_KEYS = (
|
||||
"SubaruSNG",
|
||||
"SubaruSNGManualParkingBrake",
|
||||
"SubaruStopStartOff",
|
||||
"SubaruRedneckCruise",
|
||||
"VoltSNG",
|
||||
"JeepBrakeHold",
|
||||
"GMAutoHold",
|
||||
@@ -217,6 +218,7 @@ SAFE_MODE_FIXED_VALUES = {
|
||||
"LongitudinalPersonality": int(log.LongitudinalPersonality.relaxed),
|
||||
"UseAutoSteerDelay": True,
|
||||
"SubaruStopStartOff": False,
|
||||
"SubaruRedneckCruise": False,
|
||||
}
|
||||
|
||||
SAFE_MODE_STOCK_PARAM_MAP = {
|
||||
|
||||
@@ -381,6 +381,8 @@ def update_maps(now, params, params_memory, manual_update=False):
|
||||
if maps_downloaded and params.get("LastMapsUpdate") == todays_date and not manual_update:
|
||||
return
|
||||
|
||||
params_memory.put_bool("DownloadMaps", True)
|
||||
|
||||
pm = messaging.PubMaster(["mapdIn"])
|
||||
sm = messaging.SubMaster(["mapdExtendedOut"])
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ from opendbc.car.gm.values import CAR as GM_CAR, EV_CAR as GM_EV_CAR, GMFlags
|
||||
from opendbc.car.hyundai.values import CAR as HYUNDAI_CAR, EV_CAR as HYUNDAI_EV_CAR, HyundaiFlags, HyundaiStarPilotSafetyFlags
|
||||
from opendbc.car.interfaces import TORQUE_SUBSTITUTE_PATH, CarInterfaceBase, GearShifter
|
||||
from opendbc.car.mock.values import CAR as MOCK
|
||||
from opendbc.car.subaru.values import SUBARU_STOP_START_CARS, SubaruFlags
|
||||
from opendbc.car.subaru.values import SUBARU_REDNECK_CRUISE_CARS, SUBARU_STOP_START_CARS, SubaruFlags
|
||||
from opendbc.car.tesla.values import CAR as TESLA_CAR
|
||||
from opendbc.car.toyota.values import CAR as TOYOTA_CAR, ToyotaStarPilotFlags
|
||||
from openpilot.common.basedir import BASEDIR
|
||||
@@ -672,14 +672,24 @@ class StarPilotVariables:
|
||||
toggle.experimental_mode_available = (
|
||||
toggle.openpilot_longitudinal or lateral_only_experimental_available(CP)
|
||||
)
|
||||
if not toggle.redneck_cruise_available or (toggle.openpilot_longitudinal and FPCP.pcmCruiseSpeed):
|
||||
hyundai_redneck_available = toggle.car_make == "hyundai" and toggle.redneck_cruise_available
|
||||
if toggle.car_make == "hyundai" and (not toggle.redneck_cruise_available or
|
||||
(toggle.openpilot_longitudinal and FPCP.pcmCruiseSpeed)):
|
||||
self.params.put_bool("RedneckCruise", False)
|
||||
toggle.redneck_cruise = self.get_value(
|
||||
"RedneckCruise",
|
||||
condition=toggle.redneck_cruise_available and not toggle.openpilot_longitudinal,
|
||||
condition=hyundai_redneck_available and not toggle.openpilot_longitudinal,
|
||||
)
|
||||
if toggle.redneck_cruise_available and not FPCP.pcmCruiseSpeed:
|
||||
if hyundai_redneck_available and not FPCP.pcmCruiseSpeed:
|
||||
toggle.redneck_cruise = True
|
||||
|
||||
toggle.subaru_redneck_cruise = self.get_value(
|
||||
"SubaruRedneckCruise", condition=toggle.car_model in SUBARU_REDNECK_CRUISE_CARS,
|
||||
)
|
||||
if toggle.car_model in SUBARU_REDNECK_CRUISE_CARS and not FPCP.pcmCruiseSpeed:
|
||||
toggle.subaru_redneck_cruise = True
|
||||
if toggle.car_make == "subaru":
|
||||
toggle.redneck_cruise = bool(toggle.subaru_redneck_cruise and not FPCP.pcmCruiseSpeed)
|
||||
pcm_cruise = CP.pcmCruise
|
||||
prohibited_main_aol = not toggle.openpilot_longitudinal and hyundai_can_use_lkas_for_aol
|
||||
startAccel = CP.startAccel
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
#!/usr/bin/env python3
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import signal
|
||||
import subprocess
|
||||
@@ -7,12 +9,7 @@ import time
|
||||
from collections import defaultdict, deque
|
||||
from pathlib import Path
|
||||
|
||||
from cereal import messaging
|
||||
from openpilot.common.basedir import BASEDIR
|
||||
from openpilot.common.params import Params
|
||||
from openpilot.common.swaglog import cloudlog
|
||||
|
||||
MAPD_DIR = Path(BASEDIR) / "starpilot/navigation"
|
||||
MAPD_DIR = Path(__file__).resolve().parent
|
||||
MAPD_BIN = MAPD_DIR / "mapd"
|
||||
OFFLINE_ROOT = Path("/data/media/0/osm/offline")
|
||||
RESTART_DELAY_S = 0.25
|
||||
@@ -24,6 +21,11 @@ WAIT_FOR_GPS_EXIT_CODE = 4
|
||||
ROAD_STATE_POLL_S = 1.0
|
||||
|
||||
|
||||
def _cloudlog():
|
||||
from openpilot.common.swaglog import cloudlog
|
||||
return cloudlog
|
||||
|
||||
|
||||
def extract_bounds_filename(line: str) -> str | None:
|
||||
try:
|
||||
payload = json.loads(line)
|
||||
@@ -89,7 +91,7 @@ def quarantine_offline_tile(filename: str) -> Path | None:
|
||||
try:
|
||||
tile_path.relative_to(OFFLINE_ROOT)
|
||||
except ValueError:
|
||||
cloudlog.warning(f"mapd_wrapper refusing to quarantine unexpected path: {filename}")
|
||||
_cloudlog().warning(f"mapd_wrapper refusing to quarantine unexpected path: {filename}")
|
||||
return None
|
||||
|
||||
if not tile_path.is_file():
|
||||
@@ -99,7 +101,7 @@ def quarantine_offline_tile(filename: str) -> Path | None:
|
||||
try:
|
||||
tile_path.rename(quarantined)
|
||||
except OSError:
|
||||
cloudlog.exception(f"mapd_wrapper failed to quarantine offline data: {tile_path}")
|
||||
_cloudlog().exception(f"mapd_wrapper failed to quarantine offline data: {tile_path}")
|
||||
return None
|
||||
return quarantined
|
||||
|
||||
@@ -116,17 +118,17 @@ def terminate_child(proc: subprocess.Popen[str]) -> None:
|
||||
try:
|
||||
proc.wait(timeout=2)
|
||||
except subprocess.TimeoutExpired:
|
||||
cloudlog.error(f"mapd_wrapper child did not exit after kill: pid={proc.pid}")
|
||||
_cloudlog().error(f"mapd_wrapper child did not exit after kill: pid={proc.pid}")
|
||||
|
||||
|
||||
def run_mapd_once() -> int:
|
||||
try:
|
||||
OFFLINE_ROOT.mkdir(parents=True, exist_ok=True)
|
||||
except PermissionError:
|
||||
cloudlog.exception(f"mapd_wrapper cannot create offline directory: {OFFLINE_ROOT}")
|
||||
_cloudlog().exception(f"mapd_wrapper cannot create offline directory: {OFFLINE_ROOT}")
|
||||
return 2
|
||||
except OSError:
|
||||
cloudlog.exception(f"mapd_wrapper failed to prepare offline directory: {OFFLINE_ROOT}")
|
||||
_cloudlog().exception(f"mapd_wrapper failed to prepare offline directory: {OFFLINE_ROOT}")
|
||||
return 2
|
||||
|
||||
proc = subprocess.Popen(
|
||||
@@ -157,11 +159,11 @@ def run_mapd_once() -> int:
|
||||
missing_tile = monitor.current_filename
|
||||
if is_offline_read_error(line) and missing_tile is not None and not Path(missing_tile).is_file():
|
||||
if is_null_island_tile(missing_tile):
|
||||
cloudlog.info(f"mapd_wrapper received a location before GPS fix; waiting to restart mapd: {missing_tile}")
|
||||
_cloudlog().info(f"mapd_wrapper received a location before GPS fix; waiting to restart mapd: {missing_tile}")
|
||||
terminate_child(proc)
|
||||
return WAIT_FOR_GPS_EXIT_CODE
|
||||
|
||||
cloudlog.info(f"mapd_wrapper has no offline tile for {missing_tile}; stopping mapd until the next drive")
|
||||
_cloudlog().info(f"mapd_wrapper has no offline tile for {missing_tile}; stopping mapd until the next drive")
|
||||
terminate_child(proc)
|
||||
return MISSING_COVERAGE_EXIT_CODE
|
||||
|
||||
@@ -171,15 +173,15 @@ def run_mapd_once() -> int:
|
||||
quarantined = quarantine_offline_tile(bad_tile)
|
||||
if quarantined is None:
|
||||
if not OFFLINE_ROOT.exists():
|
||||
cloudlog.warning(f"mapd_wrapper detected repeated offline read failures for {bad_tile}, but {OFFLINE_ROOT} does not exist; backing off mapd restarts")
|
||||
_cloudlog().warning(f"mapd_wrapper detected repeated offline read failures for {bad_tile}, but {OFFLINE_ROOT} does not exist; backing off mapd restarts")
|
||||
terminate_child(proc)
|
||||
return 2
|
||||
|
||||
cloudlog.warning(f"mapd_wrapper detected repeated offline read failures for {bad_tile}, but could not quarantine it")
|
||||
_cloudlog().warning(f"mapd_wrapper detected repeated offline read failures for {bad_tile}, but could not quarantine it")
|
||||
else:
|
||||
message = f"mapd_wrapper quarantined corrupt offline tile: {bad_tile} -> {quarantined}"
|
||||
print(message, flush=True)
|
||||
cloudlog.warning(message)
|
||||
_cloudlog().warning(message)
|
||||
|
||||
terminate_child(proc)
|
||||
return 1 if quarantined is not None else 2
|
||||
@@ -195,7 +197,9 @@ def wait_for_road_state_change(params: Params) -> None:
|
||||
|
||||
def wait_for_gps_fix_or_road_state_change(params: Params, sm=None) -> None:
|
||||
initial_onroad = params.get_bool("IsOnroad")
|
||||
sm = sm or messaging.SubMaster(["gpsLocationExternal"])
|
||||
if sm is None:
|
||||
from cereal import messaging
|
||||
sm = messaging.SubMaster(["gpsLocationExternal"])
|
||||
|
||||
while params.get_bool("IsOnroad") == initial_onroad:
|
||||
sm.update(1000)
|
||||
@@ -204,6 +208,8 @@ def wait_for_gps_fix_or_road_state_change(params: Params, sm=None) -> None:
|
||||
|
||||
|
||||
def main() -> None:
|
||||
from openpilot.common.params import Params
|
||||
|
||||
params = Params()
|
||||
while True:
|
||||
exit_code = run_mapd_once()
|
||||
|
||||
@@ -40,6 +40,7 @@ const VEHICLE_SETTING_MAKES = {
|
||||
SubaruSNG: ["Subaru"],
|
||||
SubaruSNGManualParkingBrake: ["Subaru"],
|
||||
SubaruStopStartOff: ["Subaru"],
|
||||
SubaruRedneckCruise: ["Subaru"],
|
||||
ClusterOffset: ["Lexus", "Toyota"],
|
||||
SNGHack: ["Lexus", "Toyota"],
|
||||
ToyotaAutoHold: ["Lexus", "Toyota"],
|
||||
|
||||
@@ -28,6 +28,7 @@ const VEHICLE_SETTING_MAKES = {
|
||||
SubaruSNG: ["Subaru"],
|
||||
SubaruSNGManualParkingBrake: ["Subaru"],
|
||||
SubaruStopStartOff: ["Subaru"],
|
||||
SubaruRedneckCruise: ["Subaru"],
|
||||
ClusterOffset: ["Lexus", "Toyota"],
|
||||
SNGHack: ["Lexus", "Toyota"],
|
||||
ToyotaAutoHold: ["Lexus", "Toyota"],
|
||||
|
||||
@@ -321,6 +321,13 @@ def test_hidden_feature_defaults_remain_enabled():
|
||||
assert _declared_default(key) == "1"
|
||||
|
||||
|
||||
def test_toyota_auto_hold_is_galaxy_only():
|
||||
setting = _params_by_section(_layout())["Vehicle"]["ToyotaAutoHold"]
|
||||
assert setting["galaxy_only"] is True
|
||||
assert setting["ui_type"] == "toggle"
|
||||
assert setting["data_type"] == "bool"
|
||||
|
||||
|
||||
def test_human_acceleration_param_is_removed():
|
||||
params_source = PARAM_KEYS_PATH.read_text(encoding="utf-8")
|
||||
assert '{"HumanAcceleration",' not in params_source
|
||||
|
||||
@@ -117,6 +117,14 @@ def run_navigationd(started: bool, params: Params, CP: car.CarParams, starpilot_
|
||||
return started and params.get("NavDestination") is not None
|
||||
|
||||
|
||||
def run_mapd(started: bool, params: Params, CP: car.CarParams, starpilot_toggles: SimpleNamespace) -> bool:
|
||||
if started:
|
||||
return True
|
||||
|
||||
memory_params = Params(memory=True)
|
||||
return memory_params.get_bool("DownloadMaps") or memory_params.get_bool("CancelDownloadMaps")
|
||||
|
||||
|
||||
def bluetooth_enabled(started: bool, params: Params, CP: car.CarParams, starpilot_toggles: SimpleNamespace) -> bool:
|
||||
return params.get_bool("BluetoothEnabled")
|
||||
|
||||
@@ -216,7 +224,7 @@ else:
|
||||
procs += [
|
||||
PythonProcess("device_syncd", "starpilot.system.device_syncd", always_run),
|
||||
PythonProcess("starpilot_process", "starpilot.starpilot_process", always_run),
|
||||
PythonProcess("mapd", "starpilot.navigation.mapd_wrapper", always_run, nice=19),
|
||||
PythonProcess("mapd", "starpilot.navigation.mapd_wrapper", run_mapd, nice=19),
|
||||
PythonProcess("navigationd", "starpilot.navigation.navigationd", run_navigationd, nice=19),
|
||||
PythonProcess("speed_limit_filler", "starpilot.system.speed_limit_filler", run_speed_limit_filler, nice=19),
|
||||
PythonProcess("speed_limit_vision", "starpilot.system.speed_limit_vision", run_speed_limit_vision, nice=19),
|
||||
|
||||
@@ -4,6 +4,7 @@ import pytest
|
||||
|
||||
from cereal import car
|
||||
from opendbc.car.ford.values import CAR as FORD_CAR
|
||||
import openpilot.system.manager.process_config as process_config
|
||||
from openpilot.system.manager.process_config import (
|
||||
allow_uploads,
|
||||
bluetooth_enabled,
|
||||
@@ -48,6 +49,31 @@ def test_uploader_runs_at_background_priority():
|
||||
assert managed_processes["uploader"].nice == 19
|
||||
|
||||
|
||||
def test_mapd_runs_onroad_or_during_offroad_map_transfer(monkeypatch):
|
||||
class MemoryParams:
|
||||
values = {}
|
||||
|
||||
def __init__(self, *, memory=False):
|
||||
assert memory
|
||||
|
||||
def get_bool(self, key):
|
||||
return self.values.get(key, False)
|
||||
|
||||
monkeypatch.setattr(process_config, "Params", MemoryParams)
|
||||
params = object()
|
||||
CP = car.CarParams.new_message()
|
||||
toggles = SimpleNamespace()
|
||||
|
||||
assert process_config.run_mapd(True, params, CP, toggles)
|
||||
assert not process_config.run_mapd(False, params, CP, toggles)
|
||||
|
||||
MemoryParams.values["DownloadMaps"] = True
|
||||
assert process_config.run_mapd(False, params, CP, toggles)
|
||||
|
||||
MemoryParams.values = {"DownloadMaps": False, "CancelDownloadMaps": True}
|
||||
assert process_config.run_mapd(False, params, CP, toggles)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("enabled", [False, True])
|
||||
def test_bluetooth_process_is_param_gated(enabled):
|
||||
params = SimpleNamespace(get_bool=lambda key: enabled if key == "BluetoothEnabled" else False)
|
||||
|
||||
Reference in New Issue
Block a user