mirror of
https://github.com/firestar5683/StarPilot.git
synced 2026-11-08 13:33:42 +08:00
Preserve independent GM steering across speed and input transitions
This commit is contained in:
@@ -1463,7 +1463,8 @@ class SelfdriveD:
|
||||
if (toyota_qualified(self.CP, marked_only=True) and CS.standstill or
|
||||
angle_qualified(self.CP, marked_only=True) or
|
||||
self.CP.brand == 'hyundai' and len(self.CP.safetyConfigs) == 1 and
|
||||
stock_canfd_qualified(self.CP, marked_only=True)):
|
||||
stock_canfd_qualified(self.CP, marked_only=True) or
|
||||
self.CP.brand == 'gm' and qualified_gm(self.CP) and self.CP.pcmCruise and not self.CP.openpilotLongitudinalControl):
|
||||
native = current_native(self.sm, self.CP, now_ns=now_ns, axis_session_id=self.aol_session_id)
|
||||
if stock_lateral_role(self, CS, native, intent, allow_independent_cancel=True,
|
||||
allow_independent_brake=True, allow_independent_gas=True, now_ns=now_ns):
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
"""Conventional CC's longitudinal speed minimum cannot inhibit independent LAT."""
|
||||
from dataclasses import replace
|
||||
from contextlib import ExitStack
|
||||
from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
from opendbc.can import CANPacker
|
||||
from opendbc.car import Bus
|
||||
from opendbc.car.gm import gmcan
|
||||
from opendbc.car.gm.tests.test_ascm_intercept import params
|
||||
from opendbc.car.gm.tests.test_bolt_cc import native
|
||||
from opendbc.car.gm.tests.test_ordinary_cc import qualified_frames
|
||||
from opendbc.car.gm.tests.test_sierra_cc_layout import params as sierra_params
|
||||
from opendbc.car.gm.values import CAR, DBC, ORDINARY_CC_CAR, CruiseButtons, GMFlags
|
||||
from opendbc.safety.tests.libsafety import libsafety_py
|
||||
from openpilot.cereal import log
|
||||
from openpilot.common.prefix import OpenpilotPrefix
|
||||
from openpilot.starpilot.aol.intent import read_settings
|
||||
from openpilot.starpilot.aol.tests import test_gm_parked_startup as parked, test_ioniq_availability as fixture
|
||||
from openpilot.starpilot.aol.tests.test_gm_cc_lateral_minimum import setup
|
||||
from openpilot.starpilot.car.gm.aol import cc_independent_lateral_below_engage
|
||||
|
||||
|
||||
def cc_params(candidate):
|
||||
cp = sierra_params() if candidate == CAR.CHEVROLET_SILVERADO_CC else params(candidate)
|
||||
cp.alternativeExperience = 32
|
||||
return cp
|
||||
|
||||
|
||||
@pytest.mark.parametrize('candidate', sorted(ORDINARY_CC_CAR | {CAR.CHEVROLET_SILVERADO_CC}))
|
||||
def test_all_exact_ordinary_cc_owners_keep_independent_steering_minimum(candidate):
|
||||
cp = cc_params(candidate)
|
||||
_, intent = setup()
|
||||
assert cc_independent_lateral_below_engage(cp, intent, enabled=False, active=False)
|
||||
for enabled, active in ((True, True), (True, False), (False, True)):
|
||||
assert not cc_independent_lateral_below_engage(cp, intent, enabled=enabled, active=active)
|
||||
for key in ('settingsQualified', 'allowedLatch', 'lateralArmed'):
|
||||
assert not cc_independent_lateral_below_engage(cp, replace(intent, **{key: False}), enabled=False, active=False)
|
||||
assert not cc_independent_lateral_below_engage(cp, replace(intent, pauseLateral=True), enabled=False, active=False)
|
||||
for flag in (GMFlags.PEDAL_LONG, GMFlags.NO_CAMERA, 1 << 30):
|
||||
denied = cp.as_reader().as_builder()
|
||||
denied.flags |= int(flag)
|
||||
assert not cc_independent_lateral_below_engage(denied, intent, enabled=False, active=False)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('main_action', [0, 9])
|
||||
def test_sierra_parser_card_selfdrived_controls_and_native_steer_below_cruise_minimum(main_action):
|
||||
cp = cc_params(CAR.CHEVROLET_SILVERADO_CC)
|
||||
original = parked.parsed_packets
|
||||
|
||||
def packets(*args):
|
||||
packer = args[1]
|
||||
return original(*args) + [packer.make_can_msg('BCMBlindSpotMonitor', 0, {})]
|
||||
|
||||
with patch.object(parked, 'parsed_packets', side_effect=packets):
|
||||
parked._exercise_parked_main(3, main_action, below_engage=True, cp=cp)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('candidate', [CAR.CHEVROLET_SILVERADO_CC, CAR.CHEVROLET_MALIBU_CC])
|
||||
@pytest.mark.parametrize('speed_mph,allowed', [(20., False), (30., True)])
|
||||
def test_native_longitudinal_direction_buttons_keep_existing_24mph_floor(candidate, speed_mph, allowed):
|
||||
cp = cc_params(candidate)
|
||||
assert cp.minEnableSpeed == pytest.approx(24 * .44704)
|
||||
safety = libsafety_py.libsafety
|
||||
safety.init_tests()
|
||||
safety.set_alternative_experience(cp.alternativeExperience)
|
||||
assert safety.set_safety_hooks(cp.safetyConfigs[0].safetyModel.raw, cp.safetyConfigs[0].safetyParam) == 0
|
||||
packer = CANPacker(DBC[candidate][Bus.pt])
|
||||
for tick in range(5):
|
||||
now = 1_000_000_000 + tick * 10_000_000
|
||||
frames = [f for f in qualified_frames(packer, tick % 4) if f[0] not in (0x34A, 0x3D1)]
|
||||
frames += [packer.make_can_msg('EBCMWheelSpdRear', 0, {'RLWheelSpd': speed_mph * 1.609344,
|
||||
'RRWheelSpd': speed_mph * 1.609344, 'RLWheelDir': 1, 'RRWheelDir': 1}),
|
||||
packer.make_can_msg('ECMCruiseControl', 0, {'CruiseActive': 1, 'CruiseSetSpeed': 50})]
|
||||
for frame in frames:
|
||||
assert native('rx', frame, now // 1000)
|
||||
safety.safety_tick()
|
||||
assert safety.safety_config_valid() and safety.get_controls_allowed()
|
||||
safety.set_aol_test_heartbeat(True)
|
||||
safety.aol_set_host_request(3)
|
||||
if allowed:
|
||||
assert safety.aol_get_permission_mask() & 2
|
||||
command = gmcan.create_buttons(packer, 0, (tick + 1) % 4, CruiseButtons.RES_ACCEL)
|
||||
assert bool(native('tx', command, now // 1000)) == allowed
|
||||
|
||||
|
||||
@pytest.mark.parametrize('braking', [False, True])
|
||||
def test_sierra_actual_lateral_callers_keep_main_intent_when_crossing_24mph(braking):
|
||||
cp = cc_params(CAR.CHEVROLET_SILVERADO_CC)
|
||||
case = fixture.IoniqAvailabilityTests(methodName='runTest')
|
||||
with OpenpilotPrefix(), ExitStack() as cleanup:
|
||||
cleanup.callback(case.doCleanups)
|
||||
sd, _, step = case._authority_callers(False, cp=cp, genuine_events=True)
|
||||
sd.aol_settings = read_settings(sd.params)
|
||||
for tick, mph in enumerate((30., 25., 23.85, 20., 10., 7.5, 6.5, 10.)):
|
||||
cs = fixture.car_state(brake=braking)
|
||||
cs.vEgo = cs.vEgoRaw = mph * .44704
|
||||
axis, state, command = step(tick, observed_state=cs, native_request_long=False, panda_controls_allowed=False)
|
||||
assert cs.cruiseState.available
|
||||
assert not state.enabled and not command.enabled
|
||||
assert not axis.longitudinalActive and not command.longActive
|
||||
assert axis.desiredLateral and axis.lateralActive, (mph, axis.to_dict(), sd.events.names)
|
||||
assert bool(command.latActive) == (mph > 7.)
|
||||
assert (log.OnroadEvent.EventName.belowEngageSpeed in sd.events.names) == (mph < 24.)
|
||||
assert log.OnroadEvent.EventName.controlsMismatch not in sd.events.names
|
||||
@@ -0,0 +1,62 @@
|
||||
"""A fresh stock-GM LAT source does not inherit an expired output's PCM-off veto."""
|
||||
from contextlib import ExitStack
|
||||
from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
from openpilot.cereal import messaging
|
||||
from openpilot.common.prefix import OpenpilotPrefix
|
||||
from openpilot.selfdrive.selfdrived.events import EventName
|
||||
from openpilot.starpilot.aol.intent import read_settings
|
||||
from openpilot.starpilot.aol.tests import test_ioniq_availability as fixture
|
||||
from openpilot.starpilot.aol.tests.test_gm_stock_gas_source_gap import params
|
||||
from openpilot.starpilot.aol.tests.test_input_deadline_continuity import intent
|
||||
|
||||
BASE = 10_000_000_000
|
||||
|
||||
|
||||
@pytest.mark.parametrize('name', ['ascm', 'sdgm', 'bolt_acc'])
|
||||
@pytest.mark.parametrize('fault', ['none', 'native', 'can', 'eps', 'cancel', 'main'])
|
||||
def test_fresh_stock_gas_source_keeps_lateral_after_previous_output_expires(name, fault):
|
||||
with OpenpilotPrefix(), ExitStack() as cleanup:
|
||||
case = fixture.IoniqAvailabilityTests(methodName='runTest')
|
||||
cleanup.callback(case.doCleanups)
|
||||
sd, controls, step = case._authority_callers(False, cp=params(name), genuine_events=True)
|
||||
sd.aol_settings = read_settings(sd.params)
|
||||
state = fixture.car_state()
|
||||
state.vEgo = state.vEgoRaw = 12.1
|
||||
state.cruiseState.enabled = False
|
||||
for tick in (0, 1):
|
||||
axis, host, command = step(tick, observed_state=state, gas=True,
|
||||
native_request_long=False, panda_controls_allowed=False)
|
||||
assert axis.lateralActive and command.latActive and not command.longActive and not host.enabled
|
||||
prior_deadline = axis.validUntilMonoTime
|
||||
now = BASE + 54_063_646
|
||||
with patch('openpilot.selfdrive.controls.controlsd.time.monotonic_ns', return_value=now):
|
||||
expired, _ = controls.state_control()
|
||||
assert now > prior_deadline and not expired.latActive and not expired.longActive
|
||||
if fault == 'can':
|
||||
state.canValid = False
|
||||
elif fault == 'eps':
|
||||
state.steerFaultPermanent = True
|
||||
elif fault == 'main':
|
||||
state.cruiseState.available = False
|
||||
elif fault == 'cancel':
|
||||
state.buttonEvents = [fixture.car.CarState.ButtonEvent(type=fixture.car.CarState.ButtonEvent.Type.cancel, pressed=True)]
|
||||
source = BASE + 44_038_177
|
||||
raw = messaging.new_message('carState', valid=True, logMonoTime=source)
|
||||
raw.carState = state
|
||||
authority = messaging.new_message('starpilotCarState', valid=True, logMonoTime=source)
|
||||
authority.starpilotCarState.sourceCarStateMonoTime = source
|
||||
if sd.steering_companion is not None:
|
||||
sd.steering_companion._observe(source, True, authority.starpilotCarState)
|
||||
axis, host, command = step(5, offset_ns=4_063_646, observed_state=state, gas=True,
|
||||
car_state_message=raw, authority_message=authority, intent_message=intent(source, 6), native_request_long=False,
|
||||
panda_controls_allowed=False, native_valid=fault != 'native')
|
||||
assert not command.longActive and not axis.longitudinalActive and not host.enabled
|
||||
if fault != 'none':
|
||||
assert not command.latActive and not axis.lateralActive
|
||||
return
|
||||
assert axis.sourceCarStateMonoTime == source and axis.validUntilMonoTime > prior_deadline
|
||||
assert axis.lateralActive and command.latActive and axis.nativeAcknowledged
|
||||
assert not sd.aol_authority_lost and EventName.controlsMismatch not in sd.events.names
|
||||
assert EventName.pcmDisable in sd.events.names
|
||||
@@ -30,9 +30,10 @@ def native_accepts_cp(cp, model: int, param: int) -> bool:
|
||||
|
||||
def cc_independent_lateral_below_engage(cp, intent, *, enabled, active):
|
||||
"""An armed conventional-cruise LAT request uses its steering speed gate."""
|
||||
from opendbc.car.gm.values import is_bolt_cc_profile, is_volt_cc_profile
|
||||
from opendbc.car.gm.values import is_bolt_cc_profile, is_volt_cc_profile, is_ordinary_cc_profile
|
||||
return bool(not enabled and not active and qualified_gm(cp) and aol_enabled(cp) and
|
||||
(is_bolt_cc_profile(cp) or is_volt_cc_profile(cp)) and intent is not None and intent.settingsQualified and
|
||||
(is_bolt_cc_profile(cp) or is_volt_cc_profile(cp) or is_ordinary_cc_profile(cp)) and
|
||||
intent is not None and intent.settingsQualified and
|
||||
intent.allowedLatch and intent.lateralArmed and not intent.pauseLateral)
|
||||
|
||||
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
{"protected_inventory_sha256": "68e37513f82da5dfd36a359db864fdc92a984ed68ade1edc3a924fad108cf493", "schema_version": 1}
|
||||
{"protected_inventory_sha256": "b02cde93f4bbbd868768eee6f18c3f2ed5275dc4d3ee1972f6ed728204777f03", "schema_version": 1}
|
||||
|
||||
Reference in New Issue
Block a user