mirror of
https://github.com/firestar5683/StarPilot.git
synced 2026-11-06 04:23:42 +08:00
Build matching Outback MAIN lateral firmware
This commit is contained in:
@@ -85,6 +85,7 @@ class CarController(CarControllerBase):
|
||||
self.last_angle = 0
|
||||
self.alert_active = False
|
||||
self.standstill_req = False
|
||||
self.sng_hack = False
|
||||
self.last_standstill = False
|
||||
self.prius_longitudinal = prius_longitudinal.enabled(CP)
|
||||
self.sdsu_hybrid = (eps8_sdsu.configured(CP) or legacy_sdsu.configured(CP)) and bool(CP.flags & ToyotaFlags.HYBRID)
|
||||
@@ -360,6 +361,9 @@ class CarController(CarControllerBase):
|
||||
if not should_resume and CS.out.cruiseState.standstill:
|
||||
self.standstill_req = True
|
||||
|
||||
if self.sng_hack and long_active:
|
||||
self.standstill_req = False
|
||||
|
||||
if (bypass_long or rav4h_long or pedal_owner or advert_long or original_prius_long) and (not filter_allowed or not long_active and CS.out.standstill):
|
||||
self.standstill_req = True
|
||||
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
"""StarPilot stop-latch override for existing Toyota longitudinal owners."""
|
||||
from opendbc.car import structs
|
||||
from opendbc.car.toyota.values import ToyotaFlags
|
||||
|
||||
|
||||
def eligible(cp):
|
||||
from opendbc.car.toyota import prius_longitudinal, rav4h_filter, advert_filter, legacy_stop_timer
|
||||
return bool(cp is not None and cp.brand == 'toyota' and cp.openpilotLongitudinalControl and
|
||||
not cp.autoResumeSng and not cp.deprecated.enableGasInterceptor and
|
||||
not cp.flags & ToyotaFlags.GAS_INTERCEPTOR and
|
||||
not cp.passive and not cp.dashcamOnly and not cp.notCar and
|
||||
len(cp.safetyConfigs) == 1 and cp.safetyConfigs[0].safetyModel == structs.CarParams.SafetyModel.toyota and
|
||||
(prius_longitudinal.enabled(cp) or rav4h_filter.enabled(cp) or
|
||||
advert_filter.configured(cp) or legacy_stop_timer.enabled(cp)))
|
||||
Binary file not shown.
Binary file not shown.
@@ -21,6 +21,7 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
|
||||
{"PromptVolume", {PERSISTENT, INT}},
|
||||
{"AlwaysOnLateral", {PERSISTENT, BOOL, "1"}},
|
||||
{"NostalgiaMode", {PERSISTENT, BOOL, "0"}},
|
||||
{"SNGHack", {PERSISTENT, BOOL, "0"}},
|
||||
{"ToyotaAutoHold", {PERSISTENT, BOOL, "0"}},
|
||||
{"JeepBrakeHold", {PERSISTENT, BOOL, "0"}},
|
||||
{"ToyotaRadarMode", {PERSISTENT, INT, "0"}},
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
"""The saved donor option reaches the real settings and controller owners."""
|
||||
from dataclasses import replace
|
||||
|
||||
import pytest
|
||||
from opendbc.car.toyota.interface import CarInterface
|
||||
from openpilot.common.params import Params
|
||||
from openpilot.common.prefix import OpenpilotPrefix
|
||||
from openpilot.starpilot.car.toyota.tests.test_gas_override_cancel import setup, acc_frames, PROFILES
|
||||
from openpilot.starpilot.car.toyota.tests.test_prius_sdsu_filter import factory
|
||||
from openpilot.starpilot.ui.feature_settings_owner import FeatureSettingsOwner
|
||||
from openpilot.starpilot.ui.feature_settings_state import row_change
|
||||
from openpilot.starpilot.vehicle_preferences import VehicleStartupPreferences
|
||||
|
||||
|
||||
def eligible(cp):
|
||||
from opendbc.car.toyota.sng import eligible as check
|
||||
return check(cp)
|
||||
|
||||
|
||||
def owner_for(params, cp, parked=True):
|
||||
return FeatureSettingsOwner(params, lambda _: parked, vehicle_params=lambda: cp,
|
||||
vehicle_fingerprint=lambda: cp.carFingerprint)
|
||||
|
||||
|
||||
def rows(owner, parked=True):
|
||||
return owner.snapshot('vehicle', parked=parked, system_long=True, lateral_context=True, metric=False).rows
|
||||
|
||||
|
||||
def test_saved_sng_option_is_exposed():
|
||||
with OpenpilotPrefix():
|
||||
params = Params()
|
||||
assert any(row.key == 'SNGHack' for row in rows(owner_for(params, factory()))), 'TOYOTA_SNG_OPTION_MISSING'
|
||||
|
||||
|
||||
def test_saved_ui_choice_binds_next_controller_without_changing_cp():
|
||||
with OpenpilotPrefix():
|
||||
params, cp = Params(), factory()
|
||||
before = cp.to_dict()
|
||||
owner = owner_for(params, cp)
|
||||
row = next(row for row in rows(owner) if row.key == 'SNGHack')
|
||||
assert row.available and row.value == 'Off'
|
||||
request = row_change(row)
|
||||
assert request is not None
|
||||
request = replace(request, confirmation=True)
|
||||
from openpilot.starpilot.ui.vehicle_bool import VEHICLE_BOOL_KEYS, confirmation_question
|
||||
assert request.key in VEHICLE_BOOL_KEYS and 'Toyota stop-and-go' in confirmation_question(request)
|
||||
assert owner.apply(request) and params.get_bool('SNGHack')
|
||||
assert not owner.apply(request)
|
||||
ci = CarInterface(cp)
|
||||
prefs = VehicleStartupPreferences.read(params, enabled=True)
|
||||
prefs.configure_controller(ci)
|
||||
assert ci.CC.sng_hack and cp.to_dict() == before
|
||||
params.put_bool('SNGHack', False, block=True)
|
||||
assert ci.CC.sng_hack
|
||||
VehicleStartupPreferences.read(params, enabled=True).configure_controller(ci)
|
||||
assert not ci.CC.sng_hack
|
||||
|
||||
|
||||
@pytest.mark.parametrize('veto', ('stock', 'automatic', 'pedal', 'passive', 'dashcam', 'notcar', 'native'))
|
||||
def test_donor_eligibility_and_native_identity_are_preserved(veto):
|
||||
cp = factory()
|
||||
assert eligible(cp)
|
||||
if veto == 'stock':
|
||||
cp.openpilotLongitudinalControl = False
|
||||
elif veto == 'pedal':
|
||||
cp.deprecated.enableGasInterceptor = True
|
||||
elif veto == 'native':
|
||||
cp.safetyConfigs[0].safetyModel = 'noOutput'
|
||||
else:
|
||||
setattr(cp, {'automatic': 'autoResumeSng', 'passive': 'passive', 'dashcam': 'dashcamOnly', 'notcar': 'notCar'}[veto], True)
|
||||
assert not eligible(cp)
|
||||
with OpenpilotPrefix():
|
||||
assert all(row.key != 'SNGHack' for row in rows(owner_for(Params(), cp)))
|
||||
|
||||
|
||||
def test_onroad_and_safe_mode_cannot_enable_override():
|
||||
with OpenpilotPrefix():
|
||||
params, cp = Params(), factory()
|
||||
owner = owner_for(params, cp, parked=False)
|
||||
assert not next(row for row in rows(owner, False) if row.key == 'SNGHack').available
|
||||
params.put_bool('SNGHack', True, block=True)
|
||||
params.put_bool('SafeMode', True, block=True)
|
||||
ci = CarInterface(cp)
|
||||
VehicleStartupPreferences.read(params, enabled=True).configure_controller(ci)
|
||||
assert not ci.CC.sng_hack
|
||||
|
||||
|
||||
@pytest.mark.parametrize('profile', PROFILES)
|
||||
@pytest.mark.parametrize('active', (False, True))
|
||||
def test_stop_latch_override_requires_qualified_active_long(profile, active):
|
||||
cp, controller, command, state = setup(profile)
|
||||
controller.sng_hack = eligible(cp)
|
||||
state.out.gasPressed = False
|
||||
state.out.standstill = state.out.cruiseState.standstill = True
|
||||
state.out.vEgo = state.out.vEgoRaw = 0.
|
||||
state.pcm_acc_status = 8
|
||||
command.longActive = active
|
||||
command.actuators.accel = -.5
|
||||
controller.standstill_req = True
|
||||
frames = acc_frames(cp, controller, command, state)
|
||||
assert frames
|
||||
assert all(bool(frame['RELEASE_STANDSTILL']) == (active and eligible(cp)) for frame in frames)
|
||||
assert all(not frame['CANCEL_REQ'] for frame in frames)
|
||||
@@ -0,0 +1,33 @@
|
||||
"""Saved SNG opt-in retains the physical/native stop-engagement contract."""
|
||||
import pytest
|
||||
from opendbc.car.toyota.sng import eligible
|
||||
from openpilot.common.params import Params
|
||||
from openpilot.starpilot.vehicle_preferences import VehicleStartupPreferences
|
||||
from openpilot.starpilot.car.toyota.tests import test_prius_stop_engagement_callers as helpers
|
||||
from openpilot.starpilot.car.toyota.tests.test_toyota_cpp_transport import host_heartbeat_bridge as host_heartbeat_bridge
|
||||
|
||||
source_matched_native = helpers.source_matched_native
|
||||
daemon_gc_scope = helpers.daemon_gc_scope
|
||||
|
||||
|
||||
@pytest.mark.parametrize('advert', (False, True))
|
||||
@pytest.mark.parametrize('stock', (False, True))
|
||||
def test_saved_sng_reaches_real_native_stop_callers(monkeypatch, host_heartbeat_bridge, advert, stock):
|
||||
put, configure = Params.put_bool, VehicleStartupPreferences.configure_controller
|
||||
bound = []
|
||||
|
||||
def saved(params, key, value, *args, **kwargs):
|
||||
result = put(params, key, value, *args, **kwargs)
|
||||
if key == 'ToyotaAutoHold':
|
||||
put(params, 'SNGHack', True, block=True)
|
||||
return result
|
||||
|
||||
def bind(prefs, ci):
|
||||
configure(prefs, ci)
|
||||
assert ci.CC.sng_hack == eligible(ci.CP)
|
||||
bound.append(ci.CC.sng_hack)
|
||||
|
||||
monkeypatch.setattr(Params, 'put_bool', saved)
|
||||
monkeypatch.setattr(VehicleStartupPreferences, 'configure_controller', bind)
|
||||
helpers.test_prius_accepts_stop_engagement_before_brake_release(monkeypatch, host_heartbeat_bridge, advert, stock)
|
||||
assert bound and bound[-1] == (not stock)
|
||||
@@ -1121,7 +1121,7 @@ class SettingsGatewayTest(unittest.TestCase):
|
||||
from opendbc.car.mazda.values import CAR
|
||||
from openpilot.starpilot.lateral.controller_selection import policy_for
|
||||
cp = interfaces[CAR.MAZDA_CX5_2022].get_non_essential_params(CAR.MAZDA_CX5_2022)
|
||||
self.assertIsNone(policy_for(cp))
|
||||
self.assertEqual(policy_for(cp), 'generic')
|
||||
self.context.value = AuthorityContext(True, cp, cp.as_reader().as_builder().to_bytes())
|
||||
page = self.page('torque')
|
||||
index = next(i for i, row in enumerate(page['rows']) if row['label'] == 'Turn Assist')
|
||||
|
||||
@@ -5,9 +5,11 @@ from __future__ import annotations
|
||||
from dataclasses import dataclass
|
||||
from enum import StrEnum
|
||||
import json
|
||||
import math
|
||||
from pathlib import Path
|
||||
|
||||
from opendbc.car import structs
|
||||
from opendbc.car.values import PLATFORMS
|
||||
|
||||
|
||||
DOCUMENT_KEY = 'LateralControllerSelection'
|
||||
@@ -62,6 +64,25 @@ KNOWN_POLICIES.update(dict.fromkeys((
|
||||
'HYUNDAI_KONA_EV_2022',
|
||||
), 'hyundai'))
|
||||
|
||||
KNOWN_POLICIES.update({str(identity): platform.__class__.__module__.split('.')[-2]
|
||||
for identity, platform in PLATFORMS.items()})
|
||||
|
||||
|
||||
def generic_torque_supported(cp) -> bool:
|
||||
try:
|
||||
platform = PLATFORMS.get(str(cp.carFingerprint))
|
||||
if (platform is None or platform.__class__.__module__.split('.')[-2] != str(cp.brand) or
|
||||
cp.notCar or cp.passive or cp.dashcamOnly or
|
||||
cp.steerControlType != structs.CarParams.SteerControlType.torque or cp.lateralTuning.which() != 'torque'):
|
||||
return False
|
||||
tune = cp.lateralTuning.torque
|
||||
return (math.isfinite(tune.latAccelFactor) and tune.latAccelFactor > 0 and
|
||||
math.isfinite(tune.latAccelOffset) and math.isfinite(tune.friction) and tune.friction >= 0 and
|
||||
math.isfinite(tune.steeringAngleDeadzoneDeg) and tune.steeringAngleDeadzoneDeg >= 0)
|
||||
except (AttributeError, TypeError, ValueError, OverflowError):
|
||||
return False
|
||||
|
||||
|
||||
class ControllerMode(StrEnum):
|
||||
STANDARD = 'standard'
|
||||
STARPILOT = 'starpilot'
|
||||
@@ -75,7 +96,7 @@ class ControllerSelection:
|
||||
|
||||
|
||||
def policy_for(CP) -> str | None:
|
||||
"""Return only an already-implemented, exact torque policy identity."""
|
||||
"""Prefer exact vehicle policies, otherwise use the shared torque law."""
|
||||
if CP.notCar:
|
||||
return None
|
||||
from openpilot.starpilot.lateral.hkg_vehicle_policy import policy_for as hkg_vehicle_policy
|
||||
@@ -132,7 +153,7 @@ def policy_for(CP) -> str | None:
|
||||
CP.steerControlType == structs.CarParams.SteerControlType.torque and
|
||||
CP.lateralTuning.which() == 'torque' and not CP.dashcamOnly and not CP.passive):
|
||||
return 'ioniq6'
|
||||
return None
|
||||
return "generic" if generic_torque_supported(CP) else None
|
||||
|
||||
|
||||
def turn_assist_supported(CP) -> bool:
|
||||
@@ -151,7 +172,7 @@ def default_selection(CP) -> ControllerSelection:
|
||||
policy = policy_for(CP)
|
||||
from openpilot.starpilot.lateral.hkg_vehicle_policy import PROFILES
|
||||
forte_non_scc = policy == 'kia_forte' and str(CP.carFingerprint) in ('KIA_FORTE_2019_NON_SCC', 'KIA_FORTE_2021_NON_SCC')
|
||||
mode = ControllerMode.STARPILOT if policy and (policy not in PROFILES or forte_non_scc) else ControllerMode.STANDARD
|
||||
mode = ControllerMode.STARPILOT if policy and policy != 'generic' and (policy not in PROFILES or forte_non_scc) else ControllerMode.STANDARD
|
||||
return ControllerSelection(mode, policy, 'default')
|
||||
|
||||
|
||||
@@ -239,9 +260,12 @@ def learning_allowed(params, CP, *, selection: ControllerSelection | None = None
|
||||
preference = read_preference(params, CP)
|
||||
if not preference.valid or not preference.enabled:
|
||||
return False
|
||||
if policy_for(CP) is None:
|
||||
policy = policy_for(CP)
|
||||
if policy is None:
|
||||
return True
|
||||
selected = read_selection(params, CP) if selection is None else selection
|
||||
if policy == 'generic':
|
||||
return selected.source != 'invalid'
|
||||
from openpilot.starpilot.lateral.gm_geometry_runtime import read_geometry
|
||||
geometry = read_geometry(params, CP)
|
||||
if geometry.force_off:
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
"""Shared StarPilot torque shaping using the vehicle's own calibration and conversion."""
|
||||
|
||||
from openpilot.common.scalar_clip import clip_scalar
|
||||
from openpilot.starpilot.lateral.controller_selection import generic_torque_supported
|
||||
from openpilot.starpilot.lateral.hkg_torque_policy import HKGShapedTorquePolicy
|
||||
from openpilot.starpilot.lateral.torque_shaping import get_standard_friction_threshold
|
||||
|
||||
|
||||
class GenericTorquePolicy(HKGShapedTorquePolicy):
|
||||
supported_cp = staticmethod(generic_torque_supported)
|
||||
|
||||
def feedforward(self, value, setpoint, jerk, speed):
|
||||
return value
|
||||
|
||||
def friction(self, setpoint, jerk, speed):
|
||||
return get_standard_friction_threshold(speed), 1.0
|
||||
|
||||
def output(self, value, setpoint, speed):
|
||||
return clip_scalar(value, -self.parent.steer_max, self.parent.steer_max)
|
||||
@@ -95,10 +95,11 @@ class TestControllerSelection(unittest.TestCase):
|
||||
wrong = ioniq.as_reader().as_builder()
|
||||
wrong.notCar = True
|
||||
self.assertIsNone(policy_for(wrong))
|
||||
with self.assertRaises(ValueError):
|
||||
unsupported = cp_for(TOYOTA.TOYOTA_RAV4_TSS2_2022)
|
||||
LatControlTorque(unsupported.as_reader(), interfaces[TOYOTA.TOYOTA_RAV4_TSS2_2022](unsupported), DT_CTRL,
|
||||
controller_mode=ControllerMode.STARPILOT)
|
||||
generic = cp_for(TOYOTA.TOYOTA_RAV4_TSS2_2022)
|
||||
controller = LatControlTorque(generic.as_reader(), interfaces[TOYOTA.TOYOTA_RAV4_TSS2_2022](generic), DT_CTRL,
|
||||
controller_mode=ControllerMode.STARPILOT)
|
||||
self.assertEqual(controller.controller_policy, 'generic')
|
||||
self.assertIsNotNone(selected_policy(controller))
|
||||
|
||||
def test_standard_skips_policy_constructor_and_ioniq_factor_multiplier(self):
|
||||
for vehicle in (HYUNDAI.HYUNDAI_IONIQ_6, HYUNDAI.GENESIS_G70_2020, TOYOTA.TOYOTA_COROLLA_TSS2):
|
||||
|
||||
@@ -76,10 +76,10 @@ def test_exact_admission_and_saved_standard_keeps_upstream_controller():
|
||||
changed = cp.as_reader().as_builder()
|
||||
changed.lateralTuning.init('pid')
|
||||
assert not policy.supported_cp(changed)
|
||||
for vehicle in (CAR.GENESIS_GV70_1ST_GEN, CAR.GENESIS_GV70_ELECTRIFIED_2ND_GEN):
|
||||
for vehicle, expected in ((CAR.GENESIS_GV70_1ST_GEN, 'generic'), (CAR.GENESIS_GV70_ELECTRIFIED_2ND_GEN, None)):
|
||||
sibling = interfaces[vehicle].get_non_essential_params(vehicle)
|
||||
assert not policy.supported_cp(sibling)
|
||||
assert policy_for(sibling) is None
|
||||
assert policy_for(sibling) == expected
|
||||
|
||||
|
||||
def test_stabilization_only_changes_feedforward_feedback_remains_immediate():
|
||||
|
||||
@@ -0,0 +1,149 @@
|
||||
import json
|
||||
import math
|
||||
from types import SimpleNamespace
|
||||
|
||||
import pytest
|
||||
|
||||
from opendbc.car import DT_CTRL, structs
|
||||
from opendbc.car.car_helpers import interfaces
|
||||
from opendbc.car.hyundai.values import CAR
|
||||
from opendbc.car.vehicle_model import VehicleModel
|
||||
from openpilot.common.params import Params
|
||||
from openpilot.selfdrive.controls.lib.latcontrol_torque import LatControlTorque
|
||||
from openpilot.starpilot.lateral.controller_selection import (
|
||||
DOCUMENT_KEY, ControllerMode, default_selection, learning_allowed,
|
||||
parse_document, policy_for, read_selection, replace_mode,
|
||||
)
|
||||
from openpilot.starpilot.lateral.torque_extension import selected_policy
|
||||
from openpilot.starpilot.ui.feature_settings_owner import FeatureSettingsOwner
|
||||
from openpilot.starpilot.ui.feature_settings_state import row_change
|
||||
|
||||
|
||||
def cp_for(identity):
|
||||
return interfaces[identity].get_non_essential_params(identity)
|
||||
|
||||
|
||||
def owner_for(params, cp):
|
||||
return FeatureSettingsOwner(params, lambda group: True, vehicle_fingerprint=lambda: str(cp.carFingerprint),
|
||||
vehicle_params=lambda: cp)
|
||||
|
||||
|
||||
def test_niro_2023_choice_is_visible_and_saves(tmp_path):
|
||||
params = Params(str(tmp_path))
|
||||
cp = cp_for(CAR.KIA_NIRO_EV_2ND_GEN)
|
||||
assert cp.steerControlType == structs.CarParams.SteerControlType.torque
|
||||
assert cp.lateralTuning.which() == 'torque'
|
||||
owner = owner_for(params, cp)
|
||||
snapshot = owner.snapshot('torque', parked=True, system_long=False, lateral_context=False, metric=False)
|
||||
row = next((row for row in snapshot.rows if row.key == DOCUMENT_KEY), None)
|
||||
assert row is not None, '2023 Niro EV must expose the controller selector'
|
||||
assert row.choices == ('Stock Controller', 'StarPilot Controller')
|
||||
assert row.value == 'Stock Controller'
|
||||
request = row_change(row)
|
||||
assert request is not None and request.value == 'StarPilot Controller'
|
||||
assert owner.apply(request)
|
||||
assert read_selection(params, cp).mode == ControllerMode.STARPILOT
|
||||
assert not owner.apply(request), 'stale expected bytes must remain rejected'
|
||||
ci = interfaces[CAR.KIA_NIRO_EV_2ND_GEN](cp)
|
||||
controller = LatControlTorque(cp.as_reader(), ci, DT_CTRL, controller_mode=read_selection(params, cp).mode)
|
||||
assert selected_policy(controller) is not None
|
||||
assert controller.controller_policy == 'generic'
|
||||
assert controller.torque_params.to_dict() == cp.lateralTuning.torque.to_dict()
|
||||
|
||||
|
||||
def test_every_registered_torque_tune_can_save_both_choices():
|
||||
from openpilot.starpilot.lateral.controller_selection import generic_torque_supported
|
||||
checked = []
|
||||
for identity in interfaces:
|
||||
cp = cp_for(identity)
|
||||
if not generic_torque_supported(cp):
|
||||
continue
|
||||
assert policy_for(cp) is not None, identity
|
||||
for mode in ControllerMode:
|
||||
raw = replace_mode(None, cp, mode)
|
||||
assert parse_document(raw)['vehicles'][str(identity)]['brand'] == str(cp.brand)
|
||||
from openpilot.starpilot.lateral.controller_selection import selection_from_bytes
|
||||
selected = selection_from_bytes(cp, raw)
|
||||
assert selected.mode == mode and selected.source == 'saved', identity
|
||||
checked.append(str(identity))
|
||||
assert str(CAR.KIA_NIRO_EV_2ND_GEN) in checked
|
||||
assert len(checked) > 100
|
||||
print('Registered torque controller selection coverage:', len(checked))
|
||||
|
||||
|
||||
def test_saved_vehicle_choices_remain_independent():
|
||||
from opendbc.car.honda.values import CAR as HONDA
|
||||
niro = cp_for(CAR.KIA_NIRO_EV_2ND_GEN)
|
||||
honda = cp_for(HONDA.HONDA_FIT_4G)
|
||||
raw = replace_mode(None, niro, ControllerMode.STARPILOT)
|
||||
raw = replace_mode(raw, honda, ControllerMode.STANDARD)
|
||||
from openpilot.starpilot.lateral.controller_selection import selection_from_bytes
|
||||
assert selection_from_bytes(niro, raw).mode == ControllerMode.STARPILOT
|
||||
assert selection_from_bytes(honda, raw).mode == ControllerMode.STANDARD
|
||||
|
||||
|
||||
@pytest.mark.parametrize('failure', ['brand', 'unknown', 'angle', 'pid', 'passive', 'dashcam', 'notCar', 'nan', 'factor', 'friction'])
|
||||
def test_generic_refuses_invalid_controller_configuration(failure):
|
||||
from openpilot.starpilot.lateral.controller_selection import generic_torque_supported
|
||||
cp = cp_for(CAR.KIA_NIRO_EV_2ND_GEN)
|
||||
if failure == 'brand':
|
||||
cp.brand = 'toyota'
|
||||
elif failure == 'unknown':
|
||||
cp.carFingerprint = 'UNREGISTERED'
|
||||
elif failure == 'angle':
|
||||
cp.steerControlType = structs.CarParams.SteerControlType.angle
|
||||
elif failure == 'pid':
|
||||
cp.lateralTuning.init('pid')
|
||||
elif failure in ('passive', 'dashcam', 'notCar'):
|
||||
setattr(cp, 'dashcamOnly' if failure == 'dashcam' else failure, True)
|
||||
elif failure == 'nan':
|
||||
cp.lateralTuning.torque.latAccelFactor = float('nan')
|
||||
elif failure == 'factor':
|
||||
cp.lateralTuning.torque.latAccelFactor = 0
|
||||
elif failure == 'friction':
|
||||
cp.lateralTuning.torque.friction = -1
|
||||
assert not generic_torque_supported(cp)
|
||||
assert policy_for(cp) is None
|
||||
|
||||
|
||||
def test_generic_defaults_and_existing_learning_are_preserved(tmp_path):
|
||||
params = Params(str(tmp_path))
|
||||
cp = cp_for(CAR.KIA_NIRO_EV_2ND_GEN)
|
||||
assert default_selection(cp).mode == ControllerMode.STANDARD
|
||||
assert learning_allowed(params, cp)
|
||||
params.put_bool('ForceAutoTuneOff', True, block=True)
|
||||
assert not learning_allowed(params, cp)
|
||||
params.put(DOCUMENT_KEY, json.loads(replace_mode(None, cp, ControllerMode.STARPILOT)), block=True)
|
||||
assert not learning_allowed(params, cp)
|
||||
|
||||
|
||||
def test_shared_controller_uses_real_interfaces_and_keeps_output_limits():
|
||||
checked = set()
|
||||
for identity in interfaces:
|
||||
cp = cp_for(identity)
|
||||
if policy_for(cp) != 'generic' or str(cp.brand) in checked:
|
||||
continue
|
||||
checked.add(str(cp.brand))
|
||||
ci = interfaces[identity](cp)
|
||||
standard = LatControlTorque(cp.as_reader(), ci, DT_CTRL, controller_mode=ControllerMode.STANDARD)
|
||||
custom = LatControlTorque(cp.as_reader(), ci, DT_CTRL, controller_mode=ControllerMode.STARPILOT)
|
||||
assert standard.starpilot_extension is None
|
||||
assert custom.starpilot_extension is not None
|
||||
assert custom.torque_params.to_dict() == standard.torque_params.to_dict()
|
||||
for acceleration in (-1., -.2, 0., .2, 1.):
|
||||
assert (custom.torque_from_lateral_accel(acceleration, custom.torque_params) ==
|
||||
standard.torque_from_lateral_accel(acceleration, standard.torque_params))
|
||||
assert custom.pid.pos_limit == standard.pid.pos_limit
|
||||
assert custom.pid.neg_limit == standard.pid.neg_limit
|
||||
state = structs.CarState.new_message()
|
||||
vm = VehicleModel(cp)
|
||||
params = SimpleNamespace(angleOffsetDeg=0., roll=0.01)
|
||||
for active, speed, curvature, pressed, limited in ((False, 0., 0., False, False), (True, 12., .001, False, False),
|
||||
(True, 25., -.001, False, True), (True, 15., .002, True, False), (False, 15., .002, False, False)):
|
||||
state.vEgo, state.steeringPressed = speed, pressed
|
||||
output, _, message = custom.update(active, state, vm, params, limited, curvature, False, .2)
|
||||
assert math.isfinite(output) and abs(output) <= custom.steer_max
|
||||
assert message.active == active
|
||||
if not active:
|
||||
assert output == 0
|
||||
assert 'hyundai' in checked and len(checked) >= 3
|
||||
@@ -92,7 +92,7 @@ def test_generic_partial_manual_tune_honors_scoped_learning_preference(enabled):
|
||||
from openpilot.starpilot.lateral.tests.test_torque_family import learner
|
||||
from openpilot.starpilot.lateral.torque_runtime import TorqueHost, read_settings
|
||||
cp = interfaces['HONDA_FIT_4G'].get_non_essential_params('HONDA_FIT_4G')
|
||||
assert cp.lateralTuning.which() == 'torque' and policy_for(cp) is None
|
||||
assert cp.lateralTuning.which() == 'torque' and policy_for(cp) == 'generic'
|
||||
with OpenpilotPrefix():
|
||||
params = Params()
|
||||
params.put(KEY, replace_preference(None, cp, enabled), block=True)
|
||||
|
||||
@@ -31,6 +31,9 @@ def create_extension(parent, cp, mode, selected, *, turn_assist=False):
|
||||
raise ValueError('HKG torque selection does not match CarParams')
|
||||
policy = HKGVehicleTorquePolicy(parent, cp)
|
||||
return TorqueExtension(policy, parameter_factor=policy.FACTOR_MULT)
|
||||
if selected == 'generic':
|
||||
from openpilot.starpilot.lateral.generic_torque_policy import GenericTorquePolicy
|
||||
return TorqueExtension(GenericTorquePolicy(parent, cp))
|
||||
if selected == 'ioniq6':
|
||||
from openpilot.starpilot.lateral.ioniq6_policy import Ioniq6TorquePolicy
|
||||
policy = Ioniq6TorquePolicy(parent, cp, turn_assist=turn_assist)
|
||||
|
||||
@@ -187,7 +187,7 @@ class TorqueHost:
|
||||
# existing live manual preference for other optional replay-host vehicles.
|
||||
if allow_learning is None:
|
||||
from openpilot.starpilot.lateral.controller_selection import policy_for
|
||||
if policy_for(CP) is None:
|
||||
if policy_for(CP) in (None, 'generic'):
|
||||
from openpilot.starpilot.lateral.learning_preferences import read_preference
|
||||
preference = read_preference(params, CP)
|
||||
allow_learning = preference.valid and preference.enabled
|
||||
|
||||
@@ -82,7 +82,7 @@ BOOL_DEFAULTS = {
|
||||
"SubaruStopStartOff": False,
|
||||
"SubaruSNG": False,
|
||||
"SubaruRedneckCruise": False,
|
||||
"ReverseCruise": False, "ToyotaAutoHold": False, "JeepBrakeHold": False, "VoltSNG": False, "GMAutoHold": False, "VoltOnePedalMode": False,
|
||||
"ReverseCruise": False, "ToyotaAutoHold": False, "SNGHack": False, "JeepBrakeHold": False, "VoltSNG": False, "GMAutoHold": False, "VoltOnePedalMode": False,
|
||||
"RemapCancelToDistance": False, "LongPitch": True, "DisableOpenpilotLongitudinal": False,
|
||||
"SpeedLimitController": False, "ShowSpeedLimits": True,
|
||||
"SLCConfirmation": False,
|
||||
@@ -427,6 +427,25 @@ class FeatureSettingsOwner:
|
||||
return commit_exact(self.params, key="JeepBrakeHold", max_bytes=128, raw=b"1" if request.value == "On" else b"0",
|
||||
expected=request.expected, authorized=authorized, temp_prefix=".jeep-hold-").verified
|
||||
|
||||
def _toyota_sng_capability(self):
|
||||
from opendbc.car.toyota.sng import eligible
|
||||
cp = self.vehicle_params()
|
||||
if not eligible(cp):
|
||||
return None
|
||||
return (str(cp.carFingerprint), int(cp.flags), bool(cp.autoResumeSng),
|
||||
tuple((str(config.safetyModel), int(config.safetyParam)) for config in cp.safetyConfigs))
|
||||
|
||||
def _apply_toyota_sng(self, request):
|
||||
if not request.confirmation or request.value not in ("Off", "On") or request.dependencies:
|
||||
return False
|
||||
def authorized():
|
||||
return (self.authority("parked_preferences") and self.authority("vehicle") and
|
||||
request.capability is not None and request.capability == self._toyota_sng_capability() and
|
||||
request.vehicle_fingerprint == self.vehicle_fingerprint() and request.expected in (None, b"0", b"1"))
|
||||
return commit_exact(self.params, key="SNGHack", max_bytes=8, expected=request.expected,
|
||||
raw=b"1" if request.value == "On" else b"0", authorized=authorized,
|
||||
temp_prefix=".toyota-sng-").verified
|
||||
|
||||
def _auto_hold_capability(self) -> tuple | None:
|
||||
from opendbc.car.toyota.interface import toyota_auto_hold_supported
|
||||
cp = self.vehicle_params()
|
||||
@@ -820,7 +839,7 @@ class FeatureSettingsOwner:
|
||||
FeatureRow("", "Always On Lateral", "Keep steering assistance active independently of cruise control", page=FeaturePage.AOL, available=True),
|
||||
FeatureRow("", "Wheel Controls", "Assign steering-wheel buttons and cruise behavior", page=FeaturePage.WHEEL, available=True)]
|
||||
if (self._long_pitch_capability() is not None or self._bolt_disable_capability() is not None or
|
||||
self._auto_hold_capability() is not None or self._jeep_hold_capability() is not None or
|
||||
self._auto_hold_capability() is not None or self._toyota_sng_capability() is not None or self._jeep_hold_capability() is not None or
|
||||
self._toyota_radar_capability() is not None or self._pedal_setup_capability() is not None or
|
||||
self.subaru_smoothing.capability() is not None or
|
||||
self._subaru_avh_capability() is not None or self._subaru_startup_capability() is not None or self._subaru_sng_capability() is not None or
|
||||
@@ -845,6 +864,12 @@ class FeatureSettingsOwner:
|
||||
"Hold the brake for over one second at a complete stop with cruise main on and cruise disengaged. " +
|
||||
"Holds until you press the gas; normal cruise stop-and-go is unchanged. Applies after the next startup.")
|
||||
rows.extend([replace(row, available=row.available and self._readable("ToyotaAutoHold"), capability=capability)] if capability is not None else [])
|
||||
sng_capability = self._toyota_sng_capability()
|
||||
if sng_capability is not None:
|
||||
row = self._bool_row("SNGHack", "Stop-and-Go Hack",
|
||||
configurable and parked and self.authority("vehicle") and self.authority("parked_preferences"),
|
||||
"Automatic resume for Toyota/Lexus vehicles without factory stop-and-go. Applies after the next startup.")
|
||||
rows.append(replace(row, available=row.available and self._readable("SNGHack"), capability=sng_capability))
|
||||
radar_capability = self._toyota_radar_capability()
|
||||
if radar_capability is not None:
|
||||
from openpilot.starpilot.car.toyota.radar_preferences import CHOICES, KEY
|
||||
@@ -1380,6 +1405,8 @@ class FeatureSettingsOwner:
|
||||
return self._apply_jeep_hold(request)
|
||||
if key == "AlphaLongitudinalEnabled":
|
||||
return self._apply_alpha_long(request)
|
||||
if key == "SNGHack":
|
||||
return self._apply_toyota_sng(request)
|
||||
if key == "ToyotaAutoHold":
|
||||
return self._apply_auto_hold(request)
|
||||
if key == "ToyotaRadarMode":
|
||||
|
||||
@@ -71,10 +71,11 @@ class TestControllerFeature(unittest.TestCase):
|
||||
self.cp.notCar = True
|
||||
self.assertIsNone(self.row())
|
||||
|
||||
def test_registered_stock_torque_uses_scoped_learning_without_controller_choice(self):
|
||||
def test_registered_stock_torque_has_both_controllers_and_scoped_learning(self):
|
||||
self.cp = interfaces[HONDA.HONDA_FIT_4G].get_non_essential_params(HONDA.HONDA_FIT_4G)
|
||||
self.assertEqual(self.cp.lateralTuning.which(), 'torque')
|
||||
self.assertIsNone(self.row())
|
||||
self.assertEqual(self.require_row().choices, ('Stock Controller', 'StarPilot Controller'))
|
||||
self.assertEqual(self.require_row().value, 'Stock Controller')
|
||||
learning = self.require_row('ForceAutoTuneOff')
|
||||
self.assertEqual(learning.label, 'Use StarPilot Auto Learn')
|
||||
self.assertEqual(learning.value, 'On')
|
||||
|
||||
@@ -365,7 +365,7 @@ class FeatureSettingsOwnerTests(unittest.TestCase):
|
||||
from openpilot.starpilot.lateral.controller_selection import policy_for
|
||||
from openpilot.starpilot.vehicle_preferences import VehicleStartupPreferences
|
||||
cp = interfaces[CAR.MAZDA_CX5_2022].get_non_essential_params(CAR.MAZDA_CX5_2022)
|
||||
self.assertIsNone(policy_for(cp))
|
||||
self.assertEqual(policy_for(cp), 'generic')
|
||||
self.owner.vehicle_params = lambda: cp
|
||||
self.owner.controller.vehicle_params = self.owner.vehicle_params
|
||||
self.fingerprint = cp.carFingerprint
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"""Confirmation text for parked vehicle preferences."""
|
||||
|
||||
VEHICLE_BOOL_KEYS = frozenset(("AlphaLongitudinalEnabled", "SubaruAvhStartup", "SubaruStopStartOff", "SubaruSNG", "SubaruRedneckCruise",
|
||||
"GMPedalLongitudinal", "DisableOpenpilotLongitudinal", "LongPitch", "VoltSNG", "GMAutoHold", "VoltOnePedalMode",
|
||||
"GMPedalLongitudinal", "DisableOpenpilotLongitudinal", "LongPitch", "VoltSNG", "SNGHack", "GMAutoHold", "VoltOnePedalMode",
|
||||
"TeslaAOLScreenTap", "TeslaAOLDisengageOnBrake",
|
||||
"RemoteStartBootsComma", "HKGRemoteStartBootsComma", "IgnoreIgnitionLine", "TeslaWakeOnCAN"))
|
||||
|
||||
@@ -30,6 +30,8 @@ def confirmation_question(request):
|
||||
return f"{verb} Subaru stop-and-go assistance after the next startup? Factory ACC retains speed control."
|
||||
if request.key == "SubaruRedneckCruise":
|
||||
return f"{verb} software cruise after the next startup? Stock ACC keeps pedal control."
|
||||
if request.key == "SNGHack":
|
||||
return f"{verb} Toyota stop-and-go assistance after the next startup?"
|
||||
if request.key == "VoltSNG":
|
||||
return f"{verb} Volt stop-and-go assistance after the next startup?"
|
||||
if request.key == "GMAutoHold":
|
||||
|
||||
@@ -20,6 +20,7 @@ def bolt_disable_supported(cp) -> bool:
|
||||
@dataclass(frozen=True)
|
||||
class VehicleStartupPreferences:
|
||||
toyota_auto_hold: bool = False
|
||||
toyota_sng: bool = field(default=False, kw_only=True)
|
||||
jeep_brake_hold: bool = field(default=False, kw_only=True)
|
||||
turn_assist: bool = False
|
||||
gm_long_pitch: bool = True
|
||||
@@ -116,7 +117,9 @@ class VehicleStartupPreferences:
|
||||
ford_lateral_mode=ford_mode(params, enabled=enabled),
|
||||
ford_pinion=bool(enabled and read_saved(params, "SafeMode", 8) in ((None, True), (b"0", True)) and
|
||||
read_saved(params, "FordPrefSteerAngleCurvature", 8) == (b"1", True)), tesla_screen=screen,
|
||||
tesla_screen_brake=screen_brake, toyota_auto_hold=toyota, jeep_brake_hold=jeep_hold,
|
||||
tesla_screen_brake=screen_brake, toyota_auto_hold=toyota,
|
||||
toyota_sng=bool(enabled and readable and safe in (None, b"0") and
|
||||
read_saved(params, "SNGHack", 8) == (b"1", True)), jeep_brake_hold=jeep_hold,
|
||||
gm_longitudinal_tune=selected_tune(params, enabled=enabled),
|
||||
toyota_lock_doors=bool(enabled and readable and safe in (None, b"0") and
|
||||
read_saved(params, "LockDoors", 8) == (b"1", True)),
|
||||
@@ -276,6 +279,8 @@ class VehicleStartupPreferences:
|
||||
is_volt_one_pedal, camera_acc_pedal_profile, volt_cc_pedal_profile)
|
||||
cp = ci.CP
|
||||
if ci.CC is not None and cp.brand == "toyota" and not cp.passive and not cp.dashcamOnly and not cp.notCar:
|
||||
from opendbc.car.toyota.sng import eligible
|
||||
ci.CC.sng_hack = self.toyota_sng and eligible(cp)
|
||||
ci.CC.door_locks.lock = self.toyota_lock_doors
|
||||
ci.CC.door_locks.unlock = self.toyota_unlock_doors
|
||||
if ci.CC is not None and is_volt_longitudinal(cp):
|
||||
|
||||
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,4 +1,4 @@
|
||||
#pragma once
|
||||
|
||||
extern const uint8_t gitversion[29];
|
||||
const uint8_t gitversion[29] = "DEV-b3b96ade-Pa5919516-DEBUG";
|
||||
const uint8_t gitversion[29] = "DEV-0fcf1277-Pa5919516-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.
@@ -1 +1 @@
|
||||
DEV-b3b96ade-Pa5919516-DEBUG
|
||||
DEV-0fcf1277-Pa5919516-DEBUG
|
||||
Binary file not shown.
+1
-1
@@ -1 +1 @@
|
||||
{"protected_inventory_sha256": "d5495bdc0d7d08ad60b75642bb28e5b88d60766332d0d8af6a0d1af0cfa8c4b4", "schema_version": 1}
|
||||
{"protected_inventory_sha256": "2c4e52ddf1a87907cb47f05c74f79df2e6762489e5d13d58a53469c7c9664d69", "schema_version": 1}
|
||||
|
||||
@@ -142,6 +142,8 @@ AOL_LIFECYCLE_TARGETS = (
|
||||
"openpilot/starpilot/car/toyota/tests/test_camera_2aa_native.py",
|
||||
"openpilot/starpilot/car/toyota/tests/test_prius_sdsu_filter.py",
|
||||
"openpilot/starpilot/car/toyota/tests/test_prius_sdsu_callers.py",
|
||||
"openpilot/starpilot/car/toyota/tests/test_sng.py",
|
||||
"openpilot/starpilot/car/toyota/tests/test_sng_callers.py",
|
||||
"openpilot/starpilot/car/toyota/tests/test_brake_stop_cancel.py",
|
||||
"openpilot/starpilot/car/toyota/tests/test_prius_stop_engagement_callers.py",
|
||||
"opendbc_repo/opendbc/safety/tests/test_toyota_prius_sdsu.py",
|
||||
|
||||
Reference in New Issue
Block a user