mirror of
https://github.com/firestar5683/StarPilot.git
synced 2026-11-10 06:23:43 +08:00
Compare commits
19 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 5b896476d6 | |||
| 7e72a4bd8e | |||
| d7d1fa02a1 | |||
| c86a4ac8d6 | |||
| 3513840095 | |||
| 4e4d258ad3 | |||
| 54f3897174 | |||
| 891454e47c | |||
| ec0221c16f | |||
| 00d46b482d | |||
| f831010290 | |||
| 233a386ab4 | |||
| 2a4676dfca | |||
| 3426045d1e | |||
| a400b93be7 | |||
| fc981552b3 | |||
| e54b3f9e36 | |||
| 5f3ea81785 | |||
| 64df004be1 |
@@ -62,8 +62,6 @@ jobs:
|
||||
run: >-
|
||||
tools/op.sh test
|
||||
openpilot/starpilot/flm/tests/test_offroad_backend.py
|
||||
openpilot/starpilot/flm/tests/test_offroad_coordinate_policy.py
|
||||
openpilot/starpilot/flm/tests/test_offroad_equal_native_output.py
|
||||
--json-output ci-results/flm-offroad.json
|
||||
- name: Test completed-drive and steering-result presentation
|
||||
timeout-minutes: 1
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -360,6 +360,7 @@ class CarController(CarControllerBase):
|
||||
self.volt_cc_cancel_pending = False
|
||||
self.volt_cc_last_cancel_frame = 0
|
||||
self.volt_cc_metric = False
|
||||
self.volt_cc_speed_ceiling_mps = None
|
||||
self.volt_cc_last_button_frame = 0
|
||||
self.volt_cc_last_direction_frame = None
|
||||
self.volt_cc_consumed_source_ns = 0
|
||||
@@ -1396,7 +1397,10 @@ class CarController(CarControllerBase):
|
||||
self.volt_cc_consumed_source_ns = source_ns
|
||||
can_sends.append(gmcan.create_buttons(self.packer_pt, CanBus.POWERTRAIN, (CS.buttons_counter + 1) % 4, button))
|
||||
elif self.frame % 4 == 0:
|
||||
button, rate = button_request(CS.out.vEgo, CS.out.cruiseState.speed, actuators.accel, CS.out.vCruise,
|
||||
cruise_kph = CS.out.vCruise
|
||||
if self.volt_cc_speed_ceiling_mps is not None:
|
||||
cruise_kph = min(cruise_kph, self.volt_cc_speed_ceiling_mps * CV.MS_TO_KPH)
|
||||
button, rate = button_request(CS.out.vEgo, CS.out.cruiseState.speed, actuators.accel, cruise_kph,
|
||||
bool(CC.hudControl.leadVisible), self.volt_cc_metric)
|
||||
source_ns = physical.button_credit_ns
|
||||
if ((self.frame - self.volt_cc_last_button_frame) * DT_CTRL > rate and
|
||||
|
||||
@@ -335,8 +335,8 @@ def test_actual_card_active_observation_requires_fresh_selfdrive_state(monkeypat
|
||||
host.observe_ioniq6_long_authority = lambda *args: None
|
||||
host.update_vehicle_state_context = lambda *args: None
|
||||
|
||||
def update_cruise_speed(_state, _pending, *, approved=None):
|
||||
assert approved is None
|
||||
def update_cruise_speed(_state, _pending, *, wheel_context=None):
|
||||
assert wheel_context is None
|
||||
host.update_cruise_speed = update_cruise_speed
|
||||
host.v_cruise_helper = SimpleNamespace(slc_consumed_button=None, toyota=None)
|
||||
host.startup_panda_configured = lambda: True
|
||||
|
||||
@@ -118,7 +118,6 @@ class CarController(CarControllerBase):
|
||||
self.angle_vm = VehicleModel(CP) if CP.flags & HyundaiFlags.CANFD_ANGLE_STEERING else None
|
||||
self.apply_angle_last = 0.0
|
||||
self.angle_gain_last = 0.0
|
||||
self.angle_fault_driver_override = False
|
||||
self.angle_tx_recovery = (AngleTxRecovery(0xCB, self.CAN.ECAN)
|
||||
if pe_long_qualified(CP) or ev9_long_qualified(CP) else AngleTxRecovery(0x110, self.CAN.ACAN)
|
||||
if CP.carFingerprint in (CAR.HYUNDAI_IONIQ_5_PE, CAR.KIA_EV9) and
|
||||
@@ -437,8 +436,6 @@ class CarController(CarControllerBase):
|
||||
|
||||
# steering control
|
||||
if ev9_long:
|
||||
self.angle_fault_driver_override = bool(CS.out.steeringPressed and (
|
||||
self.angle_fault_driver_override or CS.out.steerFaultTemporary or CS.out.steerFaultPermanent))
|
||||
drive = CS.out.gearShifter == structs.CarState.GearShifter.drive
|
||||
measured = CS.angle_steering_angle
|
||||
if self.angle_tx_recovery is not None and self.angle_tx_recovery.take_rejection(self.frame):
|
||||
@@ -453,8 +450,7 @@ class CarController(CarControllerBase):
|
||||
ev9_lateral_allowed = lateral_request_allowed(CC, CS.out, CS.angle_steering_fault,
|
||||
independent=(ev9_long_aol_qualified(self.CP, marked_only=True) or
|
||||
pe_long_aol_qualified(self.CP, marked_only=True)))
|
||||
active = bool(ev9_lateral_allowed and CC.latActive and not self.angle_fault_driver_override and
|
||||
abs(measured) <= bound and abs(self.apply_angle_last) <= bound)
|
||||
active = bool(ev9_lateral_allowed and CC.latActive and abs(measured) <= bound and abs(self.apply_angle_last) <= bound)
|
||||
self.ev9_angle_filter.update_alpha(float(np.interp(CS.out.vEgo, [5.0, 10.0, 20.0], [0.2, 0.1, 0.0])))
|
||||
desired = self.ev9_angle_filter.update(float(np.clip(CC.actuators.steeringAngleDeg,
|
||||
-(self.params.ANGLE_LIMITS.STEER_ANGLE_MAX if pe_long else EV9_OP_LONG_ANGLE_TARGET_MAX),
|
||||
|
||||
@@ -348,10 +348,9 @@ class CarState(CarStateBase):
|
||||
self.lkas11 = copy.copy(cp_cam.vl["LKAS11"])
|
||||
self.clu11 = copy.copy(cp.vl["CLU11"])
|
||||
from opendbc.car.hyundai.classic_long_aol import independent_buttons_qualified
|
||||
from opendbc.car.hyundai.blended_stock_aol import qualified_alpha
|
||||
self.ev6_aol_samples = ()
|
||||
self.ev6_aol_source_healthy = False
|
||||
if independent_buttons_qualified(self.CP) or qualified_alpha(self.CP, marked_only=True):
|
||||
if independent_buttons_qualified(self.CP):
|
||||
from opendbc.can.parser import MAX_BAD_COUNTER
|
||||
source = cp.message_states.get(cp.dbc.name_to_msg['CLU11'].address)
|
||||
self.ev6_aol_sample_stamp_ns = int(source.timestamps[-1]) if source is not None and source.timestamps else 0
|
||||
|
||||
@@ -58,8 +58,6 @@ def test_actual_controller_fault_and_full_lateral_withdrawal_restart_gain_from_z
|
||||
cs = ci.CS
|
||||
cs.out.vEgoRaw = cs.out.vEgo = 5.
|
||||
cs.out.steeringAngleDeg = cs.angle_steering_angle = 20.
|
||||
cs.out.steeringPressed = False
|
||||
cs.out.steeringTorque = 0.
|
||||
command = structs.CarControl(enabled=True, latActive=True)
|
||||
command.actuators.steeringAngleDeg = 50.
|
||||
ci.CC.apply_angle_last = 20.
|
||||
@@ -126,59 +124,3 @@ def test_long_controller_consumes_current_rejection_without_retaining_filtered_h
|
||||
_, frames = ci.apply(command.as_reader(), now[0])
|
||||
assert abs(ci.CC.apply_angle_last + 20.) <= 5.
|
||||
assert ci.CC.angle_gain_last == .004
|
||||
|
||||
|
||||
@pytest.mark.parametrize('identity', (CAR.HYUNDAI_IONIQ_5_PE, CAR.KIA_EV9))
|
||||
@pytest.mark.parametrize('long', (True,))
|
||||
@pytest.mark.parametrize('aol', (False, True))
|
||||
def test_fault_recovery_yields_until_driver_releases_steering(identity, long, aol):
|
||||
ci, _ = controller(identity, long, aol)
|
||||
cs = ci.CS
|
||||
cs.out.vEgoRaw = cs.out.vEgo = 3.
|
||||
cs.out.steeringAngleDeg = cs.angle_steering_angle = 175.
|
||||
cs.out.steeringPressed = True
|
||||
cs.out.steeringTorque = 500.
|
||||
command = structs.CarControl(enabled=not aol, latActive=True)
|
||||
command.actuators.steeringAngleDeg = 360.
|
||||
ci.CC.apply_angle_last = 175.
|
||||
ci.CC.angle_gain_last = .1
|
||||
address, gain_index = (0xCB, 6) if long else (0x110, 12)
|
||||
active_index, active_mask = (3, 15) if long else (9, 3)
|
||||
for frame in range(80):
|
||||
cs.out.steerFaultTemporary = cs.angle_steering_fault = frame % 40 < 16
|
||||
cs.out.steeringAngleDeg = cs.angle_steering_angle = 175. - frame * 3.
|
||||
ci.CC.frame = frame + 21
|
||||
_, output = ci.apply(command.as_reader(), 200_000_000 + frame * 10_000_000)
|
||||
payload = next((data for addr, data, _ in output if addr == address), None)
|
||||
if payload is not None:
|
||||
assert (payload[active_index] >> 4 & active_mask) == 1, 'FAULT_RECOVERY_FIGHTS_DRIVER'
|
||||
assert payload[gain_index] == 0
|
||||
assert ci.CC.apply_angle_last == pytest.approx(cs.out.steeringAngleDeg)
|
||||
cs.out.steerFaultTemporary = cs.angle_steering_fault = False
|
||||
cs.out.steeringPressed = False
|
||||
cs.out.steeringTorque = 0.
|
||||
ci.CC.frame += 1
|
||||
_, output = ci.apply(command.as_reader(), 1_000_000_000)
|
||||
payload = next(data for addr, data, _ in output if addr == address)
|
||||
assert (payload[active_index] >> 4 & active_mask) == 2
|
||||
assert payload[gain_index] == 1
|
||||
assert abs(ci.CC.apply_angle_last - cs.out.steeringAngleDeg) <= 5.
|
||||
|
||||
|
||||
@pytest.mark.parametrize('identity', (CAR.HYUNDAI_IONIQ_5_PE, CAR.KIA_EV9))
|
||||
@pytest.mark.parametrize('long', (False, True))
|
||||
def test_driver_pressure_without_fault_keeps_existing_angle_blending(identity, long):
|
||||
ci, _ = controller(identity, long, True)
|
||||
cs = ci.CS
|
||||
cs.out.vEgoRaw = cs.out.vEgo = 3.
|
||||
cs.out.steeringAngleDeg = cs.angle_steering_angle = 20.
|
||||
cs.out.steeringPressed = True
|
||||
cs.out.steeringTorque = 500.
|
||||
command = structs.CarControl(enabled=False, latActive=True)
|
||||
command.actuators.steeringAngleDeg = 40.
|
||||
ci.CC.apply_angle_last = 20.
|
||||
ci.CC.frame = 21
|
||||
_, output = ci.apply(command.as_reader(), 200_000_000)
|
||||
address, active_index, active_mask = (0xCB, 3, 15) if long else (0x110, 9, 3)
|
||||
payload = next(data for addr, data, _ in output if addr == address)
|
||||
assert (payload[active_index] >> 4 & active_mask) == 2
|
||||
|
||||
@@ -870,7 +870,7 @@ class TestIoniq6PrepublicationHandoff(unittest.TestCase):
|
||||
card_owner.is_metric = True
|
||||
card_owner.experimental_mode = False
|
||||
card_owner.initial_cruise_speed = 40
|
||||
card_owner.v_cruise_helper = SimpleNamespace(update_v_cruise=lambda *_, slc_enabled=False, slc_approved=None: None,
|
||||
card_owner.v_cruise_helper = SimpleNamespace(update_v_cruise=lambda *_, slc_enabled=False, wheel_context=None: None,
|
||||
initialize_v_cruise=lambda *_, **kwargs: None, slc_consumed_button=None, toyota=None,
|
||||
slc_cruise_change=None, v_cruise_kph=60.0, v_cruise_cluster_kph=60.0)
|
||||
card_owner.RI = SimpleNamespace(update=lambda _: None)
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
"""StarPilot adapter for jacobw's jul-angle-based Crosstrek control.
|
||||
|
||||
Source: jacobwaller/opendbc 8acbeccdab45a47aa7af170d2d17b61f0eb5e4f0.
|
||||
Original low-speed filtering: lukasloetkolben; target-first limiting: elkoled.
|
||||
Low-speed filtering: lukasloetkolben; target-first limiting: elkoled.
|
||||
Jacob Waller integrated the implementation and corrected its VM limit type.
|
||||
Keep StarPilot's model, stricter rate, and withdrawal guards. See CREDITS.md.
|
||||
"""
|
||||
import numpy as np
|
||||
|
||||
from opendbc.car import rate_limit
|
||||
from opendbc.car.lateral import get_max_angle_delta_vm, get_max_angle_vm
|
||||
from opendbc.car.lateral import apply_center_deadzone, get_max_angle_delta_vm, get_max_angle_vm
|
||||
|
||||
MAX_ANGLE = 190.0
|
||||
|
||||
@@ -16,6 +16,9 @@ MAX_ANGLE = 190.0
|
||||
def limit_angle(target, previous, speed, measured, active, limits, model):
|
||||
if not active:
|
||||
return float(np.clip(measured, -MAX_ANGLE, MAX_ANGLE)), False
|
||||
if speed < 10.0:
|
||||
deadzone = float(np.interp(speed, [2.0, 10.0], [6.0, 3.0]))
|
||||
target = previous + apply_center_deadzone(target - previous, deadzone)
|
||||
speed = max(speed, 1.0)
|
||||
maximum = min(MAX_ANGLE, get_max_angle_vm(speed, model, limits))
|
||||
delta = min(get_max_angle_delta_vm(speed, model, limits), limits.ANGLE_LIMITS.MAX_ANGLE_RATE)
|
||||
|
||||
@@ -95,5 +95,5 @@ class TestIndependentAngleEnvelope(unittest.TestCase):
|
||||
self.assertTrue(decoded['LKAS_Request'])
|
||||
if car == CAR.SUBARU_CROSSTREK_2025:
|
||||
# The EPS bound applies to transmitted commands, not measured-angle admission.
|
||||
# Follow the small target without jumping to the measured angle.
|
||||
self.assertAlmostEqual(decoded['LKAS_Output'], -1.45)
|
||||
# Preserve the zero command history: this target is inside the low-speed deadzone.
|
||||
self.assertEqual(decoded['LKAS_Output'], 0.)
|
||||
|
||||
@@ -12,17 +12,19 @@ from opendbc.safety.tests.libsafety import libsafety_py
|
||||
from openpilot.starpilot.car.subaru.tests.caller_helpers import CompleteCan
|
||||
|
||||
|
||||
@pytest.mark.parametrize('speed', (1.0, 2.0, 6.0, 9.0))
|
||||
@pytest.mark.parametrize('speed,deadzone', ((1.0, 6.0), (2.0, 6.0), (6.0, 4.5), (9.0, 3.375)))
|
||||
@pytest.mark.parametrize('sign', (-1, 1))
|
||||
def test_real_controller_tracks_small_low_speed_changes(speed, sign):
|
||||
def test_real_controller_suppresses_small_low_speed_changes(speed, deadzone, sign):
|
||||
_, controller, command, state = setup_controller(CAR.SUBARU_CROSSTREK_2025)
|
||||
state.out.vEgoRaw = speed
|
||||
command.actuators.steeringAngleDeg = sign * 2.0
|
||||
for _ in range(20):
|
||||
_, decoded = step(controller, command, state)
|
||||
assert decoded['LKAS_Request'] == 1
|
||||
assert sign * decoded['LKAS_Output'] > 0
|
||||
assert decoded['LKAS_Output'] == pytest.approx(sign * 2.0, abs=.01)
|
||||
command.actuators.steeringAngleDeg = sign * (deadzone - .01)
|
||||
_, decoded = step(controller, command, state)
|
||||
assert decoded['LKAS_Request'] == 1
|
||||
assert decoded['LKAS_Output'] == 0
|
||||
command.actuators.steeringAngleDeg = sign * (deadzone + .01)
|
||||
_, decoded = step(controller, command, state)
|
||||
assert decoded['LKAS_Request'] == 1
|
||||
assert 0 < sign * decoded['LKAS_Output'] <= 1.0
|
||||
|
||||
|
||||
def test_target_first_limits_recover_to_moving_acceleration_bound():
|
||||
|
||||
@@ -9,8 +9,7 @@ from opendbc.car.tesla.values import CAR, DBC, CANBUS, GEAR_MAP, STEER_THRESHOLD
|
||||
|
||||
class TeslaScreenCANParser(CANParser):
|
||||
def __init__(self):
|
||||
# Screen gestures are optional; their silence is not a control-bus fault.
|
||||
super().__init__('tesla_model3_vehicle', [('UI_status2', float('nan'))], CANBUS.vehicle)
|
||||
super().__init__('tesla_model3_vehicle', [('UI_status2', 0)], CANBUS.vehicle)
|
||||
|
||||
def update(self, strings, sendcan=False):
|
||||
if strings and not isinstance(strings[0], list | tuple):
|
||||
|
||||
@@ -53,7 +53,7 @@ def test_actual_automatic_ap1_vin_fw_can_and_factory_use_normal_wiring(alpha, re
|
||||
assert ci.CP.carFw and all(fw.brand == 'tesla' and fw.bus == 0 for fw in ci.CP.carFw)
|
||||
assert any(fw.fwVersion == HW1_FIRMWARE for fw in ci.CP.carFw)
|
||||
assert ci.CP.safetyConfigs[0].safetyParam == 16 + int(alpha)
|
||||
assert ci.CP.openpilotLongitudinalControl == alpha and ci.CP.pcmCruise
|
||||
assert ci.CP.openpilotLongitudinalControl == alpha and ci.CP.pcmCruise != alpha
|
||||
|
||||
|
||||
@pytest.mark.parametrize('source', list(HW1_NORMAL_BUS0_LAYOUT))
|
||||
|
||||
@@ -80,36 +80,3 @@ def test_literal_optional_screen_frames_baseline_edge_and_exact_dlc():
|
||||
(structs.CarState.ButtonEvent.Type.lkas, True)]
|
||||
parser.update([(1_030_000_000, [(frame[0], frame[1][:-1], 1), (frame[0], frame[1], 0)])])
|
||||
assert cs.update_screen_button(parser) == []
|
||||
|
||||
|
||||
@pytest.mark.parametrize('screen_seen', [False, True])
|
||||
def test_optional_screen_silence_does_not_report_vehicle_bus_loss(screen_seen):
|
||||
parser = TeslaScreenCANParser()
|
||||
if screen_seen:
|
||||
parser.update([(1_000_000_000, [(0x3DF, bytes(8), CANBUS.vehicle)])])
|
||||
for stamp in (1_500_000_001, 12_000_000_000):
|
||||
parser.update([(stamp, [(0x111, bytes(8), CANBUS.vehicle)])])
|
||||
assert not parser.bus_timeout
|
||||
assert parser.can_valid
|
||||
assert parser.vl_all['UI_status2']['UI_activeTouchPoints'] == []
|
||||
|
||||
|
||||
@pytest.mark.parametrize('missing_bus', [None, CANBUS.party, CANBUS.autopilot_party])
|
||||
def test_screen_silence_keeps_required_vehicle_bus_timeouts(missing_bus):
|
||||
cp = screen_params()
|
||||
apply_screen_button(cp, True, False)
|
||||
ci = CarInterface(cp)
|
||||
packer = CANPacker('tesla_model3_party')
|
||||
messages = {
|
||||
CANBUS.party: ['DI_speed', 'DI_systemStatus', 'ESP_B', 'EPAS3S_sysStatus', 'ESP_status', 'UI_warning', 'DI_state'],
|
||||
CANBUS.autopilot_party: ['SCCM_steeringAngleSensor', 'DAS_status', 'DAS_control', 'DAS_steeringControl', 'DAS_settings'],
|
||||
}
|
||||
for index in range(150):
|
||||
frames = [packer.make_can_msg(name, bus, {}) for bus, names in messages.items()
|
||||
if index < 50 or bus != missing_bus for name in names]
|
||||
if index == 0:
|
||||
frames.append((0x3DF, bytes(8), CANBUS.vehicle))
|
||||
result = ci.update([(1_000_000_000 + index * 10_000_000, frames)])
|
||||
assert result.canTimeout is (missing_bus is not None)
|
||||
if missing_bus is None:
|
||||
assert result.canValid
|
||||
|
||||
@@ -231,15 +231,12 @@ typedef void (*rx_hook)(const CANPacket_t *msg);
|
||||
typedef bool (*tx_hook)(const CANPacket_t *msg); // returns true if the message is allowed
|
||||
typedef bool (*fwd_hook)(int bus_num, int addr); // returns true if the message should be blocked from forwarding
|
||||
|
||||
typedef bool (*fwd_packet_hook)(const CANPacket_t *msg);
|
||||
|
||||
typedef struct {
|
||||
safety_hook_init init;
|
||||
rx_hook rx;
|
||||
rx_hook optional_rx;
|
||||
tx_hook tx;
|
||||
fwd_hook fwd;
|
||||
fwd_packet_hook fwd_packet;
|
||||
get_checksum_t get_checksum;
|
||||
compute_checksum_t compute_checksum;
|
||||
get_counter_t get_counter;
|
||||
@@ -370,7 +367,6 @@ extern uint16_t current_safety_param;
|
||||
extern safety_config current_safety_config;
|
||||
|
||||
int safety_fwd_hook(int bus_num, int addr);
|
||||
int safety_fwd_packet_hook(const CANPacket_t *msg);
|
||||
int set_safety_hooks(uint16_t mode, uint16_t param);
|
||||
|
||||
extern const safety_hooks body_hooks;
|
||||
|
||||
@@ -970,7 +970,6 @@ static void hyundai_optional_rx_hook(const CANPacket_t *msg) {
|
||||
}
|
||||
|
||||
const safety_hooks hyundai_hooks = {
|
||||
.fwd_packet = hyundai_daw_fwd,
|
||||
.fwd = blended_aol_fwd,
|
||||
.init = hyundai_init,
|
||||
.optional_rx = hyundai_optional_rx_hook,
|
||||
@@ -982,7 +981,6 @@ const safety_hooks hyundai_hooks = {
|
||||
};
|
||||
|
||||
const safety_hooks hyundai_legacy_hooks = {
|
||||
.fwd_packet = hyundai_daw_fwd,
|
||||
.init = hyundai_legacy_init,
|
||||
.optional_rx = hyundai_optional_rx_hook,
|
||||
.rx = hyundai_rx_hook,
|
||||
|
||||
@@ -158,29 +158,10 @@ static void blended_aol_rx(const CANPacket_t *msg) {
|
||||
bool pressed = false;
|
||||
if ((msg->bus == pt) && (msg->addr == 0x4F1U) && (GET_LEN(msg) == 4U)) {
|
||||
if ((msg->data[0] & 7U) == 4U) {
|
||||
// Mixed LONG Pause/Resume still clears ordinary/LONG authority in the
|
||||
// cruise hook. Retain only an already claimed, current lateral token.
|
||||
// CANCEL cannot arm, renew a request/gesture lease, or clear an inhibit.
|
||||
const bool source_current = (blended_aol_token_source < 3U) &&
|
||||
(safety_get_ts_elapsed(now, blended_aol_source_ts[blended_aol_token_source]) <= 300000U);
|
||||
const bool retain_lateral = blended_aol_long && !GET_BIT(msg, 3U) &&
|
||||
blended_aol_token && blended_aol_claimed && blended_aol_session && heartbeat_engaged &&
|
||||
blended_aol_main_seen && blended_aol_main &&
|
||||
(safety_get_ts_elapsed(now, blended_aol_main_ts) <= 100000U) && source_current &&
|
||||
blended_aol_request_seen && ((aol_host_axis_mask & 1U) != 0U) &&
|
||||
(safety_get_ts_elapsed(now, aol_host_request_ts) <= AOL_HOST_REQUEST_TIMEOUT_US) &&
|
||||
aol_rx_healthy() && !relay_malfunction && !safety_rx_checks_invalid;
|
||||
if (!retain_lateral) {
|
||||
blended_aol_clear();
|
||||
}
|
||||
// A simultaneous MAIN is not an arming gesture. The advancing physical
|
||||
// source remains observable without changing its token or host lease.
|
||||
blended_aol_pressed[0] = GET_BIT(msg, 3U);
|
||||
blended_aol_source_ts[0] = now;
|
||||
} else {
|
||||
source = 0U;
|
||||
pressed = GET_BIT(msg, 3U);
|
||||
blended_aol_clear();
|
||||
}
|
||||
source = 0U;
|
||||
pressed = GET_BIT(msg, 3U);
|
||||
} else if ((msg->bus == pt) && (msg->addr == 0x391U) && (GET_LEN(msg) == 8U)) {
|
||||
source = 1U;
|
||||
pressed = GET_BIT(msg, 4U);
|
||||
|
||||
@@ -14,7 +14,6 @@ static uint32_t hyundai_daw_tx_ts;
|
||||
static bool hyundai_daw_seen;
|
||||
static bool hyundai_daw_counter_seen;
|
||||
static bool hyundai_daw_sent;
|
||||
static uint8_t hyundai_daw_last_classic_tx[6];
|
||||
|
||||
static void hyundai_daw_init(bool canfd, bool enabled) {
|
||||
hyundai_daw_addr = enabled ? (canfd ? 0x11AU : 0x53EU) : 0U;
|
||||
@@ -97,31 +96,10 @@ static bool hyundai_daw_tx(const CANPacket_t *msg) {
|
||||
if (valid) {
|
||||
hyundai_daw_sent = true;
|
||||
hyundai_daw_tx_ts = now;
|
||||
if (!canfd) {
|
||||
(void)memcpy(hyundai_daw_last_classic_tx, msg->data, 6U);
|
||||
}
|
||||
}
|
||||
return valid;
|
||||
}
|
||||
|
||||
static bool hyundai_daw_fwd(const CANPacket_t *msg) {
|
||||
const uint32_t now = microsecond_timer_get();
|
||||
bool blocked = (msg->bus == 2U) && (msg->addr == 0x53EU) && (GET_LEN(msg) == 6U) && (hyundai_daw_addr == 0x53EU) &&
|
||||
(hyundai_daw_len == 6U) && hyundai_daw_seen && hyundai_daw_sent &&
|
||||
!relay_malfunction && !safety_rx_checks_invalid &&
|
||||
(safety_get_ts_elapsed(now, hyundai_daw_rx_ts) <= 100000U) &&
|
||||
(safety_get_ts_elapsed(now, hyundai_daw_tx_ts) <= 100000U);
|
||||
if (blocked) {
|
||||
const uint8_t status = msg->data[5] & 7U;
|
||||
blocked = (status >= 1U) && (status <= 3U);
|
||||
for (unsigned int i = 0U; i < 6U; i++) {
|
||||
const uint8_t mask = (i == 5U) ? 0xF8U : 0xFFU;
|
||||
blocked &= (msg->data[i] & mask) == (hyundai_daw_last_classic_tx[i] & mask);
|
||||
}
|
||||
}
|
||||
return blocked;
|
||||
}
|
||||
|
||||
static safety_config hyundai_daw_configure(safety_config ret, bool canfd, bool enabled) {
|
||||
hyundai_daw_init(canfd, false);
|
||||
if (enabled && (ret.tx_msgs_len > 0) && (ret.tx_msgs_len < 64)) {
|
||||
|
||||
@@ -13,7 +13,6 @@ static bool tesla_screen_belt_latched = false;
|
||||
static bool tesla_screen_eps_ready = false;
|
||||
static bool tesla_screen_session = false;
|
||||
static bool tesla_screen_autopark = false;
|
||||
static bool tesla_screen_acc_fault = false;
|
||||
static int tesla_screen_override = 0;
|
||||
|
||||
static void tesla_screen_clear(void) {
|
||||
@@ -33,7 +32,6 @@ static void tesla_screen_reset(void) {
|
||||
tesla_screen_eps_ready = false;
|
||||
tesla_screen_session = false;
|
||||
tesla_screen_autopark = false;
|
||||
tesla_screen_acc_fault = false;
|
||||
}
|
||||
|
||||
static uint8_t tesla_screen_request(void) {
|
||||
@@ -56,7 +54,7 @@ static uint8_t tesla_screen_request(void) {
|
||||
|
||||
static bool tesla_screen_physical_ready(void) {
|
||||
return tesla_screen_drive && tesla_screen_occupant_ready && tesla_screen_eps_ready &&
|
||||
!steering_disengage && !tesla_screen_autopark && !tesla_screen_acc_fault;
|
||||
!steering_disengage && !tesla_screen_autopark;
|
||||
}
|
||||
|
||||
static uint8_t tesla_screen_permission(void) {
|
||||
@@ -110,17 +108,14 @@ static void tesla_screen_rx(const CANPacket_t *msg, bool autopark) {
|
||||
}
|
||||
if (msg_matches(msg, 0x286U, 0U, 8U)) {
|
||||
const unsigned int state = (msg->data[1] >> 4U) & 7U;
|
||||
tesla_screen_acc_fault = state == 5U;
|
||||
const bool cruise = ((state == 2U) || (state == 3U) || (state == 4U) || (state == 6U) || (state == 7U)) && !tesla_screen_autopark;
|
||||
const bool main_available = ((state == 1U) || cruise) && !tesla_screen_autopark;
|
||||
if (cruise && !tesla_screen_cruise) {
|
||||
tesla_screen_override = 0;
|
||||
}
|
||||
// Stock availability cannot revoke an explicit screen selection.
|
||||
if (tesla_screen_main && !main_available && !brake_pressed && (tesla_screen_override != 1)) {
|
||||
if (tesla_screen_main && !main_available && !brake_pressed) {
|
||||
tesla_screen_override = -1;
|
||||
}
|
||||
if (tesla_screen_acc_fault) { tesla_screen_clear(); }
|
||||
tesla_screen_main = main_available;
|
||||
tesla_screen_cruise = cruise;
|
||||
}
|
||||
|
||||
@@ -4,9 +4,6 @@ static bool vw_aol_enabled = false;
|
||||
static bool vw_aol_longitudinal = false;
|
||||
static bool vw_aol_long_armed = false;
|
||||
static uint8_t vw_aol_buttons = 0U;
|
||||
static bool vw_aol_pending = false;
|
||||
static bool vw_aol_session = false;
|
||||
static uint32_t vw_aol_grant_ts = 0U;
|
||||
static bool vw_aol_manual = false;
|
||||
static bool vw_aol_direct = false;
|
||||
static bool vw_aol_direct_counter_seen = false;
|
||||
@@ -30,8 +27,6 @@ static void vw_aol_clear(void) {
|
||||
vw_aol_token = false;
|
||||
vw_aol_long_armed = false;
|
||||
vw_aol_buttons = 0U;
|
||||
vw_aol_pending = false;
|
||||
vw_aol_session = false;
|
||||
vw_aol_main_neutral = false;
|
||||
vw_aol_owned = false;
|
||||
for (unsigned int i = 0U; i < 3U; i++) {
|
||||
@@ -41,7 +36,6 @@ static void vw_aol_clear(void) {
|
||||
|
||||
static void vw_aol_reset(void) {
|
||||
vw_aol_clear();
|
||||
vw_aol_grant_ts = 0U;
|
||||
vw_aol_enabled = false;
|
||||
vw_aol_initial_main = true;
|
||||
vw_aol_longitudinal = false;
|
||||
@@ -83,23 +77,12 @@ static void vw_aol_request(uint8_t mask) {
|
||||
static uint8_t vw_aol_request_mask(void) {
|
||||
const bool expired = vw_aol_requested &&
|
||||
(safety_get_ts_elapsed(microsecond_timer_get(), aol_host_request_ts) > AOL_HOST_REQUEST_TIMEOUT_US);
|
||||
const uint32_t grant_age = safety_get_ts_elapsed(microsecond_timer_get(), vw_aol_grant_ts);
|
||||
const bool pending_expired = vw_aol_pending && (grant_age > AOL_HOST_REQUEST_TIMEOUT_US);
|
||||
const bool vw_controls_heartbeat_lost = !heartbeat_engaged && (!vw_aol_longitudinal || vw_aol_session);
|
||||
uint8_t request = 0U;
|
||||
if (!vw_aol_enabled || relay_malfunction || expired || safety_rx_checks_invalid ||
|
||||
vw_controls_heartbeat_lost || pending_expired || !aol_rx_healthy() || !vw_aol_current()) {
|
||||
!heartbeat_engaged || !aol_rx_healthy() || !vw_aol_current()) {
|
||||
vw_aol_clear();
|
||||
} else if (heartbeat_engaged) {
|
||||
// A physical release may precede the host join; it cannot authorize output.
|
||||
const bool acknowledged = !vw_aol_pending || (((aol_host_axis_mask & 2U) != 0U) &&
|
||||
(safety_get_ts_elapsed(microsecond_timer_get(), aol_host_request_ts) < grant_age));
|
||||
if (acknowledged) {
|
||||
vw_aol_pending = false;
|
||||
vw_aol_session = true;
|
||||
request = aol_host_axis_mask;
|
||||
}
|
||||
} else {
|
||||
request = aol_host_axis_mask;
|
||||
}
|
||||
return request;
|
||||
}
|
||||
@@ -178,15 +161,14 @@ static void vw_aol_rx(const CANPacket_t *msg) {
|
||||
vw_aol_cruise_engaged = engaged;
|
||||
vw_aol_seen |= 1U;
|
||||
vw_aol_main_ts = microsecond_timer_get();
|
||||
if ((!heartbeat_engaged && (!vw_aol_longitudinal || vw_aol_session)) ||
|
||||
!aol_rx_healthy() || !vw_aol_current() || safety_rx_checks_invalid) {
|
||||
if (!heartbeat_engaged || !aol_rx_healthy() || !vw_aol_current() || safety_rx_checks_invalid) {
|
||||
vw_aol_clear();
|
||||
} else if (!acc_main_on) {
|
||||
vw_aol_initial_main = false;
|
||||
vw_aol_clear();
|
||||
// Only a valid OFF observation may prepare a subsequent main-ON edge.
|
||||
vw_aol_main_neutral = ((msg->data[3] & 7U) < 2U);
|
||||
} else if (heartbeat_engaged && vw_aol_initial_main && vw_aol_gear_drive && vw_aol_eps_ready && !brake_pressed) {
|
||||
} else if (vw_aol_initial_main && vw_aol_gear_drive && vw_aol_eps_ready && !brake_pressed) {
|
||||
// Fresh complete physical MAIN-on startup grants LAT only. It never restores a cleared drive session.
|
||||
vw_aol_initial_main = false;
|
||||
vw_aol_token = true;
|
||||
@@ -204,19 +186,15 @@ static void vw_aol_rx(const CANPacket_t *msg) {
|
||||
vw_aol_initial_main = false;
|
||||
vw_aol_clear();
|
||||
} else if (vw_aol_longitudinal) {
|
||||
const bool healthy = (heartbeat_engaged || !vw_aol_session) && !relay_malfunction && aol_rx_healthy() && vw_aol_current() && !safety_rx_checks_invalid;
|
||||
const bool healthy = heartbeat_engaged && aol_rx_healthy() && vw_aol_current() && !safety_rx_checks_invalid;
|
||||
const uint8_t buttons = (GET_BIT(msg, 16U) ? 1U : 0U) | (GET_BIT(msg, 19U) ? 2U : 0U);
|
||||
if (!healthy) {
|
||||
vw_aol_clear();
|
||||
} else if (buttons == 0U) {
|
||||
if (((vw_aol_buttons & 3U) != 0U) && controls_allowed && acc_main_on &&
|
||||
vw_aol_gear_drive && vw_aol_eps_ready && (!brake_pressed || (!vw_aol_manual && !vehicle_moving))) {
|
||||
vw_aol_initial_main = false;
|
||||
vw_aol_token = true;
|
||||
vw_aol_long_armed = true;
|
||||
vw_aol_pending = !heartbeat_engaged;
|
||||
vw_aol_session = heartbeat_engaged;
|
||||
vw_aol_grant_ts = microsecond_timer_get();
|
||||
}
|
||||
vw_aol_buttons = 4U;
|
||||
} else if ((vw_aol_buttons & 4U) != 0U) {
|
||||
|
||||
@@ -358,14 +358,6 @@ int safety_fwd_hook(int bus_num, int addr) {
|
||||
return blocked ? -1 : destination_bus;
|
||||
}
|
||||
|
||||
int safety_fwd_packet_hook(const CANPacket_t *msg) {
|
||||
int destination_bus = safety_fwd_hook(msg->bus, msg->addr);
|
||||
if ((destination_bus >= 0) && (current_hooks->fwd_packet != NULL) && current_hooks->fwd_packet(msg)) {
|
||||
destination_bus = -1;
|
||||
}
|
||||
return destination_bus;
|
||||
}
|
||||
|
||||
// 1Hz safety function called by main. Now just a check for lagging safety messages
|
||||
void safety_tick(void) {
|
||||
const uint8_t MAX_MISSED_MSGS = 10U;
|
||||
|
||||
@@ -10,7 +10,6 @@ SAFETY_UNUSED(heartbeat_engaged);
|
||||
SAFETY_UNUSED(safety_rx_hook);
|
||||
SAFETY_UNUSED(safety_tx_hook);
|
||||
SAFETY_UNUSED(safety_fwd_hook);
|
||||
SAFETY_UNUSED(safety_fwd_packet_hook);
|
||||
SAFETY_UNUSED(safety_tick);
|
||||
SAFETY_UNUSED(set_safety_hooks);
|
||||
// Exported to Panda's USB status handler and the native safety test library.
|
||||
|
||||
@@ -313,8 +313,8 @@ class TestHyundaiBlendedAlphaAol(unittest.TestCase):
|
||||
self.assertEqual(self.request(1), 0)
|
||||
self.assertFalse(self.safety.get_controls_allowed())
|
||||
|
||||
def test_main_off_and_heartbeat_loss_revoke_physical_token(self):
|
||||
for cause in ('main_off', 'heartbeat'):
|
||||
def test_main_off_cancel_and_heartbeat_loss_revoke_physical_token(self):
|
||||
for cause in ('main_off', 'cancel', 'heartbeat'):
|
||||
self.now = 1_000_000
|
||||
self.counter = 0
|
||||
self.reset()
|
||||
@@ -325,98 +325,13 @@ class TestHyundaiBlendedAlphaAol(unittest.TestCase):
|
||||
continue
|
||||
if cause == 'main_off':
|
||||
self.feed(available=False)
|
||||
elif cause == 'cancel':
|
||||
self.feed(button=4)
|
||||
else:
|
||||
self.safety.set_aol_test_heartbeat(False)
|
||||
self.assertEqual(self.request(1), 0)
|
||||
self.assertFalse(self.safety.get_controls_allowed())
|
||||
|
||||
def test_cancel_retains_only_existing_claimed_lateral_and_stops_long(self):
|
||||
for word in (0x2004, 0x2014):
|
||||
for held in (7, 23): # Both reported short and longer Pause/Resume holds.
|
||||
self.counter, self.now = 0, 1_000_000
|
||||
self.reset(word)
|
||||
self.warm()
|
||||
self.feed(lda=True)
|
||||
self.assertEqual(self.request(1), 0 if self.release else 1)
|
||||
if self.release:
|
||||
continue
|
||||
self.feed(button=2)
|
||||
self.feed()
|
||||
self.assertTrue(self.safety.get_controls_allowed())
|
||||
self.assertEqual(self.request(3), 3)
|
||||
for _ in range(held):
|
||||
self.feed(button=4)
|
||||
self.assertFalse(self.safety.get_controls_allowed())
|
||||
self.assertEqual(self.request(1), 1)
|
||||
self.feed()
|
||||
self.assertFalse(self.safety.get_controls_allowed())
|
||||
self.assertEqual(self.request(1), 1)
|
||||
if self.bus:
|
||||
self.primary_counter = 0
|
||||
steering = TestHyundaiBlendedAlpha.primary(self, 1, True)
|
||||
else:
|
||||
from opendbc.car.hyundai.hyundaican import hyundai_checksum
|
||||
|
||||
def steering_checksum(message):
|
||||
address, data, bus = message
|
||||
return address, bytes([hyundai_checksum(data[1:8])]) + data[1:8], bus
|
||||
steering = self.packer.make_can_msg_safety('LKAS11', 0,
|
||||
{'CF_Lkas_MsgCount': 0, 'CR_Lkas_StrToqReq': 1, 'CF_Lkas_ActToi': 1}, fix_checksum=steering_checksum)
|
||||
self.assertTrue(self.safety.safety_tx_hook(steering), 'Only retained LAT can authorize steering')
|
||||
accel = self.packer.make_can_msg_safety('SCC11', self.bus, {'aReqRaw': .1})
|
||||
self.assertFalse(self.safety.safety_tx_hook(accel))
|
||||
self.assertEqual(self.request(0), 0) # Configured cancellation remains authoritative.
|
||||
|
||||
def test_cancel_cannot_arm_or_restore_expired_or_withdrawn_authority(self):
|
||||
for word in (0x2004, 0x2014):
|
||||
for cause in ('cold', 'unclaimed', 'withdrawn', 'stale_request', 'heartbeat', 'main_off', 'relay'):
|
||||
with self.subTest(word=word, cause=cause):
|
||||
self.counter, self.now = 0, 1_000_000
|
||||
self.reset(word)
|
||||
self.warm()
|
||||
if cause != 'cold':
|
||||
self.feed(lda=True)
|
||||
if cause not in ('cold', 'unclaimed'):
|
||||
self.assertEqual(self.request(1), 0 if self.release else 1)
|
||||
if self.release:
|
||||
continue
|
||||
if cause == 'withdrawn':
|
||||
self.request(0)
|
||||
elif cause == 'stale_request':
|
||||
for _ in range(32):
|
||||
self.feed() # Fresh main and token source, without a host renewal.
|
||||
elif cause == 'heartbeat':
|
||||
self.safety.set_aol_test_heartbeat(False)
|
||||
elif cause == 'relay':
|
||||
self.safety.set_relay_malfunction(True)
|
||||
self.feed(button=4, available=cause != 'main_off')
|
||||
self.assertEqual(self.request(1), 0, cause)
|
||||
|
||||
def test_cancel_cannot_restore_expired_source_simultaneous_main_or_bad_rx(self):
|
||||
for word in (0x2004, 0x2014):
|
||||
for cause in ('source', 'simultaneous_main', 'bad_rx'):
|
||||
with self.subTest(word=word, cause=cause):
|
||||
self.counter, self.now = 0, 1_000_000
|
||||
self.reset(word)
|
||||
self.warm()
|
||||
self.feed(lda=True)
|
||||
self.assertEqual(self.request(1), 0 if self.release else 1)
|
||||
if self.release:
|
||||
continue
|
||||
if cause == 'source':
|
||||
for _ in range(32):
|
||||
self.feed(omit='BCM_PO_11')
|
||||
self.safety.aol_set_host_request(1) # Fresh request, expired physical LDA source.
|
||||
elif cause == 'bad_rx':
|
||||
invalid = self.packer.make_can_msg_safety('TCS13', self.bus, {'AliveCounterTCS': self.counter % 8}, fix_checksum=checksum)
|
||||
invalid[0].data[6] ^= 1
|
||||
self.assertFalse(self.safety.safety_rx_hook(invalid))
|
||||
packet = self.packer.make_can_msg_safety('CLU11', self.bus,
|
||||
{'CF_Clu_CruiseSwState': 4, 'CF_Clu_CruiseSwMain': int(cause == 'simultaneous_main'),
|
||||
'CF_Clu_AliveCnt1': self.counter % 16})
|
||||
self.assertTrue(self.safety.safety_rx_hook(packet))
|
||||
self.assertEqual(self.request(1), 0, cause)
|
||||
|
||||
def test_cluster_lda_source_needs_its_own_neutral_edge(self):
|
||||
self.reset()
|
||||
self.warm()
|
||||
|
||||
@@ -136,75 +136,3 @@ def test_one_genuine_counter_predecessor_cannot_hide_newer_hands_off_request():
|
||||
owner.update([(BASE + 30_000_000, [(0x11A, bytes(hands_off), 1)])])
|
||||
_, current_output, _ = advisory_candidate(owner, BASE + 30_000_000)
|
||||
assert safety.safety_tx_hook(packet(0x11A, current_output, 1))
|
||||
|
||||
|
||||
@pytest.mark.parametrize('mode,word', [(structs.CarParams.SafetyModel.hyundai, 0),
|
||||
(structs.CarParams.SafetyModel.hyundai, 0xC02), (structs.CarParams.SafetyModel.hyundaiLegacy, 0)])
|
||||
@pytest.mark.parametrize('status', range(8))
|
||||
def test_classic_advisory_forwarding_uses_only_current_accepted_replacement(mode, word, status):
|
||||
safety, _ = setup(False, word)
|
||||
assert safety.set_safety_hooks(mode, word) == 0
|
||||
safety.init_tests()
|
||||
safety.set_timer(1_000_000)
|
||||
owner = DawSource(params(False))
|
||||
original = source(False, 0)
|
||||
incoming = packet(0x53E, original, 2)
|
||||
assert safety.safety_fwd_packet_hook(incoming) == 0
|
||||
safety.safety_rx_hook(incoming)
|
||||
owner.update([(BASE, [(0x53E, original, 2)])])
|
||||
addr, output, bus = advisory_candidate(owner, BASE)
|
||||
assert safety.safety_tx_hook(packet(addr, output, bus))
|
||||
safety.set_timer(1_010_000)
|
||||
incoming = packet(0x53E, source(False, status), 2)
|
||||
expected = -1 if 1 <= status <= 3 else 0
|
||||
# Both Panda drivers forward before their RX hook sees the current packet.
|
||||
assert safety.safety_fwd_packet_hook(incoming) == expected
|
||||
safety.safety_rx_hook(incoming)
|
||||
assert safety.safety_fwd_packet_hook(incoming) == expected
|
||||
assert safety.safety_fwd_packet_hook(packet(0x53E, source(False, status), 0)) == 2
|
||||
safety.set_timer(1_100_001)
|
||||
assert safety.safety_fwd_packet_hook(incoming) == 0
|
||||
|
||||
|
||||
@pytest.mark.parametrize('change', ['opaque', 'length', 'reset', 'no_replacement', 'expired_source', 'relay', 'hands_off', 'unknown'])
|
||||
def test_classic_advisory_forwarding_fails_open_without_exact_live_replacement(change):
|
||||
safety, _ = setup(False, 0xC02)
|
||||
owner = DawSource(params(False))
|
||||
original = source(False, 1)
|
||||
safety.safety_rx_hook(packet(0x53E, original, 2))
|
||||
owner.update([(BASE, [(0x53E, original, 2)])])
|
||||
addr, output, bus = advisory_candidate(owner, BASE)
|
||||
if change != 'no_replacement':
|
||||
assert safety.safety_tx_hook(packet(addr, output, bus))
|
||||
safety.set_timer(1_010_000)
|
||||
incoming = bytearray(source(False, 1))
|
||||
if change == 'opaque':
|
||||
incoming[0] ^= 1
|
||||
elif change == 'length':
|
||||
incoming.pop()
|
||||
elif change == 'reset':
|
||||
setup(False, 0xC02)
|
||||
elif change == 'expired_source':
|
||||
safety.set_timer(1_100_001)
|
||||
elif change == 'relay':
|
||||
safety.set_relay_malfunction(True)
|
||||
elif change in ('hands_off', 'unknown'):
|
||||
incoming = source(False, 4 if change == 'hands_off' else 7)
|
||||
message = packet(0x53E, bytes(incoming), 2)
|
||||
expected = -1 if change == 'relay' else 0
|
||||
assert safety.safety_fwd_packet_hook(message) == expected
|
||||
safety.safety_rx_hook(message)
|
||||
assert safety.safety_fwd_packet_hook(message) == expected
|
||||
|
||||
|
||||
def test_packet_forwarding_preserves_existing_other_profile_rules():
|
||||
safety, _ = setup(False, 0)
|
||||
data = source(False, 1)
|
||||
for mode, word in ((structs.CarParams.SafetyModel.hyundaiCanfd, 0),
|
||||
(structs.CarParams.SafetyModel.toyota, 0),
|
||||
(structs.CarParams.SafetyModel.hyundai, 0x8800)):
|
||||
assert safety.set_safety_hooks(mode, word) == 0
|
||||
safety.init_tests()
|
||||
for bus in range(3):
|
||||
for addr in (0x53E, 0x340, 0x4F1, 0x421, 0x110, 0x11A):
|
||||
assert safety.safety_fwd_packet_hook(packet(addr, data, bus)) == safety.safety_fwd_hook(bus, addr)
|
||||
|
||||
@@ -33,8 +33,7 @@ class TestTeslaScreenButton(unittest.TestCase):
|
||||
('DAS_steeringControl', 2, {'DAS_steeringControlType': stock_type}),
|
||||
('DI_speed', 0, {'DI_vehicleSpeed': 0}),
|
||||
('ESP_B', 0, {'ESP_vehicleSpeed': 0, 'ESP_wheelSpeedsQF': 1, 'ESP_vehicleStandstillSts': 1}),
|
||||
('EPAS3S_sysStatus', 0, {'EPAS3S_internalSAS': 0, 'EPAS3S_eacStatus': eps, 'EPAS3S_handsOnLevel': hands,
|
||||
'EPAS3S_torsionBarTorque': 0}),
|
||||
('EPAS3S_sysStatus', 0, {'EPAS3S_internalSAS': 0, 'EPAS3S_eacStatus': eps, 'EPAS3S_handsOnLevel': hands}),
|
||||
('DI_systemStatus', 0, {'DI_gear': gear, 'DI_accelPedalPos': 0}),
|
||||
('ESP_status', 0, {'ESP_driverBrakeApply': 2 if brake else 1}),
|
||||
('UI_warning', 0, {'anyDoorOpen': int(door), 'buckleStatus': int(belt)}),
|
||||
@@ -107,64 +106,6 @@ class TestTeslaScreenButton(unittest.TestCase):
|
||||
self.assertEqual(self.permission(), 0)
|
||||
self.arm()
|
||||
|
||||
def test_availability_loss_preserves_only_explicit_screen_arm(self):
|
||||
tx_packer = CANPackerSafety('tesla_model3_party')
|
||||
for word in (512, 768, 1536, 1792):
|
||||
with self.subTest(word=word):
|
||||
self.start(word)
|
||||
self.arm()
|
||||
self.touch(0)
|
||||
for state in (1, 0, 1, 0, 1):
|
||||
self.feed(state=state)
|
||||
self.assertEqual(self.permission(3), 1)
|
||||
positive = tx_packer.make_can_msg_safety('DAS_control', 0, {'DAS_accelMin': 1, 'DAS_accelMax': 1})
|
||||
self.assertFalse(self.lib.safety_tx_hook(positive))
|
||||
self.touch(3)
|
||||
self.assertEqual(self.permission(), 0)
|
||||
for state in (0, 1, 0, 1):
|
||||
self.feed(state=state)
|
||||
self.assertEqual(self.permission(), 0)
|
||||
|
||||
self.start(word)
|
||||
self.feed(state=1)
|
||||
self.assertEqual(self.permission(), 1)
|
||||
self.feed(state=0)
|
||||
self.assertEqual(self.permission(), 0)
|
||||
self.feed(state=1)
|
||||
self.assertEqual(self.permission(), 0)
|
||||
|
||||
def test_cruise_fault_clears_screen_arm_and_blocks_gestures(self):
|
||||
for word in (512, 768, 1536, 1792):
|
||||
with self.subTest(word=word):
|
||||
self.start(word)
|
||||
self.arm()
|
||||
self.feed(state=1)
|
||||
self.feed(state=5)
|
||||
self.assertEqual(self.permission(), 0)
|
||||
self.touch(0)
|
||||
self.touch(3)
|
||||
self.assertEqual(self.permission(), 0)
|
||||
for state in (0, 1):
|
||||
self.feed(state=state)
|
||||
self.assertEqual(self.permission(), 0)
|
||||
self.arm()
|
||||
|
||||
def test_availability_loss_cannot_hide_physical_fault_or_brake(self):
|
||||
for word in (512, 768, 1536, 1792):
|
||||
faults = [{'door': True}, {'eps': 3}, {'hands': 3}, {'gear': 0}, {'autopark': 3}]
|
||||
if word & 1024:
|
||||
faults.append({'brake': True})
|
||||
for fault in faults:
|
||||
with self.subTest(word=word, fault=fault):
|
||||
self.start(word)
|
||||
self.arm()
|
||||
self.feed(state=1)
|
||||
self.feed(state=0, **fault)
|
||||
self.assertEqual(self.permission(), 0)
|
||||
for state in (0, 1, 0, 1):
|
||||
self.feed(state=state)
|
||||
self.assertEqual(self.permission(), 0)
|
||||
|
||||
def test_brake_disengagement_cannot_be_undone_by_availability(self):
|
||||
self.start(1536)
|
||||
self.arm()
|
||||
|
||||
@@ -1,109 +0,0 @@
|
||||
"""A physical SET/RES release precedes the cold host join; output still needs a new acknowledgement."""
|
||||
|
||||
import unittest
|
||||
|
||||
import pytest
|
||||
from opendbc.safety.tests.libsafety import libsafety_py
|
||||
from opendbc.safety.tests import test_volkswagen_stock_aol as stock
|
||||
from opendbc.car import structs
|
||||
|
||||
|
||||
class Owner(stock.TestVolkswagenStockAol):
|
||||
direct = False
|
||||
|
||||
def packet(self, name, fields, bus=0):
|
||||
if self.direct and name == 'Gateway_73':
|
||||
name, fields = 'Motor_EV_01', {'MO_Waehlpos': 5 if fields['GE_Fahrstufe'] == 8 else 3}
|
||||
return super().packet(name, fields, bus)
|
||||
|
||||
|
||||
def setup(word, *, brake=False):
|
||||
debug = libsafety_py.libsafety.set_safety_hooks(structs.CarParams.SafetyModel.allOutput, 0) == 0
|
||||
o = Owner()
|
||||
o.direct = word == 41
|
||||
o.configure(word)
|
||||
o.manual = word == 25
|
||||
o.safety.set_aol_test_heartbeat(False)
|
||||
for _ in range(12):
|
||||
o.feed(moving=not brake, brake=brake)
|
||||
o.safety.aol_set_host_request(0)
|
||||
return o, debug
|
||||
|
||||
|
||||
def press(o, button, brake):
|
||||
assert o.safety.safety_rx_hook(o.packet('GRA_ACC_01', {button: 1}))
|
||||
o.feed(moving=not brake, brake=brake)
|
||||
o.safety.aol_set_host_request(0)
|
||||
assert o.safety.aol_get_permission_mask() == 0
|
||||
assert not o.safety.safety_tx_hook(o.packet('ACC_06', {'ACC_Sollbeschleunigung_02': 0.5}))
|
||||
|
||||
|
||||
@pytest.mark.parametrize('word', (9, 25, 41))
|
||||
@pytest.mark.parametrize('button', ('GRA_Tip_Setzen', 'GRA_Tip_Wiederaufnahme'))
|
||||
@pytest.mark.parametrize('brake', (False, True))
|
||||
def test_first_press_before_heartbeat_needs_fresh_host_join(word, button, brake):
|
||||
o, debug = setup(word, brake=brake)
|
||||
try:
|
||||
press(o, button, brake)
|
||||
o.safety.set_aol_test_heartbeat(True)
|
||||
assert o.safety.aol_get_permission_mask() == 0
|
||||
o.time += 10_000
|
||||
o.safety.set_timer(o.time)
|
||||
o.safety.aol_set_host_request(3)
|
||||
if brake:
|
||||
assert o.safety.aol_get_permission_mask() == 0
|
||||
o.feed(moving=False)
|
||||
o.safety.aol_set_host_request(3)
|
||||
expected = (2 if brake else 3) if debug and not (brake and word == 25) else 0
|
||||
assert o.safety.aol_get_permission_mask() == expected, 'FIRST_PHYSICAL_RELEASE_LOST_BEFORE_HOST_JOIN'
|
||||
o.safety.set_aol_test_heartbeat(False)
|
||||
assert o.safety.aol_get_permission_mask() == 0
|
||||
o.safety.set_aol_test_heartbeat(True)
|
||||
o.feed(moving=not brake)
|
||||
o.safety.aol_set_host_request(3)
|
||||
assert o.safety.aol_get_permission_mask() == 0
|
||||
finally:
|
||||
o.tearDown()
|
||||
|
||||
|
||||
@pytest.mark.parametrize('word', (9, 25, 41))
|
||||
@pytest.mark.parametrize('fault', ('timeout', 'cancel', 'main', 'reverse', 'permanent', 'relay', 'crc', 'brake'))
|
||||
def test_pending_physical_release_cannot_survive_fault_or_expiry(word, fault):
|
||||
o, _ = setup(word)
|
||||
try:
|
||||
press(o, 'GRA_Tip_Setzen', False)
|
||||
if fault == 'timeout':
|
||||
o.time += 300_001
|
||||
o.safety.set_timer(o.time)
|
||||
elif fault == 'relay':
|
||||
o.safety.set_relay_malfunction(True)
|
||||
elif fault == 'crc':
|
||||
bad = o.packet('TSK_06', {'TSK_Status': 2})
|
||||
bad[0].data[0] ^= 255
|
||||
assert not o.safety.safety_rx_hook(bad)
|
||||
else:
|
||||
o.feed(
|
||||
**{'cancel': {'cancel': True}, 'main': {'main': False}, 'reverse': {'gear': False}, 'permanent': {'permanent': True}, 'brake': {'brake': True}}[fault]
|
||||
)
|
||||
assert o.safety.aol_get_permission_mask() == 0
|
||||
o.safety.set_relay_malfunction(False)
|
||||
o.safety.set_aol_test_heartbeat(True)
|
||||
for _ in range(12):
|
||||
o.feed()
|
||||
o.safety.aol_set_host_request(3)
|
||||
assert o.safety.aol_get_permission_mask() & 2 == 0
|
||||
finally:
|
||||
o.tearDown()
|
||||
|
||||
|
||||
class TestVolkswagenFirstPress(unittest.TestCase):
|
||||
def test_first_physical_release_before_host_join(self):
|
||||
for word in (9, 25, 41):
|
||||
for button in ('GRA_Tip_Setzen', 'GRA_Tip_Wiederaufnahme'):
|
||||
for brake in (False, True):
|
||||
test_first_press_before_heartbeat_needs_fresh_host_join(word, button, brake)
|
||||
|
||||
def test_pending_release_native_vetoes(self):
|
||||
for word in (9, 25, 41):
|
||||
for fault in ('timeout', 'cancel', 'main', 'reverse', 'permanent', 'relay', 'crc', 'brake'):
|
||||
test_pending_physical_release_cannot_survive_fault_or_expiry(word, fault)
|
||||
@@ -339,7 +339,7 @@ struct SlcState @0xa1680744031fdb2d {
|
||||
hasPendingAdjustedLimit @45 :Bool;
|
||||
presentationAdjustedLimit @46 :Float32;
|
||||
hasPresentationAdjustedLimit @47 :Bool;
|
||||
overrideBasis @48 :Text; # none/pedal/persistent; qualified contribution only
|
||||
overrideBasis @48 :Text; # none/pedal/persistent/wheel; qualified contribution only
|
||||
retainedOverride @49 :Bool;
|
||||
commandActionSequenceId @50 :UInt64; # UI sequence that issued this command
|
||||
automaticAdoptionPending @51 :Bool;
|
||||
@@ -352,6 +352,10 @@ struct SlcState @0xa1680744031fdb2d {
|
||||
# Qualified adoption proposal; the current displayed observation stays above.
|
||||
upcomingSpeedLimit @57 :Float32;
|
||||
upcomingBoundaryId @58 :Text;
|
||||
acceptedIsPreview @59 :Bool; # a temporary ceiling, ineligible for initializing SET
|
||||
wheelOverrideSince @60 :UInt64; # nonzero while a wheel tap may cross the current adjusted limit
|
||||
wheelAdoptMonoTime @61 :UInt64; # Card's original wheel event timestamp, only on the approval frame
|
||||
wheelAdoptTargetMps @62 :Float32; # exact adjusted SET approved for that event
|
||||
struct SourceReading {
|
||||
source @0 :Text;
|
||||
enabled @1 :Bool;
|
||||
@@ -453,6 +457,7 @@ struct SlcAction @0x9ccdc8676701b412 {
|
||||
conditionalManual @5 :ConditionalManualAction;
|
||||
controllerCruise @6 :ControllerCruiseAction;
|
||||
controllerMode @7 :ControllerCruiseAction;
|
||||
wheelAdoptMonoTime @8 :UInt64; # original timestamp of a qualified Adopt Speed Limit wheel event
|
||||
|
||||
struct ControllerCruiseAction {
|
||||
version @0 :UInt16;
|
||||
@@ -520,6 +525,7 @@ struct SlcCruiseEvent @0xcd96dafb67a082d0 {
|
||||
trafficMode @13 :TrafficModeGesture;
|
||||
wheelAction @14 :SlcAction.ControllerCruiseAction;
|
||||
teslaStockCruise @15 :TeslaStockCruise;
|
||||
wheelOverrideSince @16 :UInt64; # SLC eligibility captured by a physical + tap; zero for ordinary SET changes
|
||||
|
||||
struct TeslaStockCruise {
|
||||
version @0 :UInt16;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -749,7 +749,7 @@ struct SlcState {
|
||||
struct ConditionalModeState;
|
||||
|
||||
struct _capnpPrivate {
|
||||
CAPNP_DECLARE_STRUCT_HEADER(a1680744031fdb2d, 18, 22)
|
||||
CAPNP_DECLARE_STRUCT_HEADER(a1680744031fdb2d, 21, 22)
|
||||
#if !CAPNP_LITE
|
||||
static constexpr ::capnp::_::RawBrandedSchema const* brand() { return &schema->defaultBrand; }
|
||||
#endif // !CAPNP_LITE
|
||||
@@ -845,7 +845,7 @@ struct SlcAction {
|
||||
|
||||
|
||||
struct _capnpPrivate {
|
||||
CAPNP_DECLARE_STRUCT_HEADER(9ccdc8676701b412, 4, 4)
|
||||
CAPNP_DECLARE_STRUCT_HEADER(9ccdc8676701b412, 5, 4)
|
||||
#if !CAPNP_LITE
|
||||
static constexpr ::capnp::_::RawBrandedSchema const* brand() { return &schema->defaultBrand; }
|
||||
#endif // !CAPNP_LITE
|
||||
@@ -897,7 +897,7 @@ struct SlcCruiseEvent {
|
||||
struct ManualModeGesture;
|
||||
|
||||
struct _capnpPrivate {
|
||||
CAPNP_DECLARE_STRUCT_HEADER(cd96dafb67a082d0, 7, 6)
|
||||
CAPNP_DECLARE_STRUCT_HEADER(cd96dafb67a082d0, 8, 6)
|
||||
#if !CAPNP_LITE
|
||||
static constexpr ::capnp::_::RawBrandedSchema const* brand() { return &schema->defaultBrand; }
|
||||
#endif // !CAPNP_LITE
|
||||
@@ -4524,6 +4524,14 @@ public:
|
||||
inline bool hasUpcomingBoundaryId() const;
|
||||
inline ::capnp::Text::Reader getUpcomingBoundaryId() const;
|
||||
|
||||
inline bool getAcceptedIsPreview() const;
|
||||
|
||||
inline ::uint64_t getWheelOverrideSince() const;
|
||||
|
||||
inline ::uint64_t getWheelAdoptMonoTime() const;
|
||||
|
||||
inline float getWheelAdoptTargetMps() const;
|
||||
|
||||
private:
|
||||
::capnp::_::StructReader _reader;
|
||||
template <typename, ::capnp::Kind>
|
||||
@@ -4817,6 +4825,18 @@ public:
|
||||
inline void adoptUpcomingBoundaryId(::capnp::Orphan< ::capnp::Text>&& value);
|
||||
inline ::capnp::Orphan< ::capnp::Text> disownUpcomingBoundaryId();
|
||||
|
||||
inline bool getAcceptedIsPreview();
|
||||
inline void setAcceptedIsPreview(bool value);
|
||||
|
||||
inline ::uint64_t getWheelOverrideSince();
|
||||
inline void setWheelOverrideSince( ::uint64_t value);
|
||||
|
||||
inline ::uint64_t getWheelAdoptMonoTime();
|
||||
inline void setWheelAdoptMonoTime( ::uint64_t value);
|
||||
|
||||
inline float getWheelAdoptTargetMps();
|
||||
inline void setWheelAdoptTargetMps(float value);
|
||||
|
||||
private:
|
||||
::capnp::_::StructBuilder _builder;
|
||||
template <typename, ::capnp::Kind>
|
||||
@@ -5669,6 +5689,8 @@ public:
|
||||
inline bool hasControllerMode() const;
|
||||
inline ::cereal::SlcAction::ControllerCruiseAction::Reader getControllerMode() const;
|
||||
|
||||
inline ::uint64_t getWheelAdoptMonoTime() const;
|
||||
|
||||
private:
|
||||
::capnp::_::StructReader _reader;
|
||||
template <typename, ::capnp::Kind>
|
||||
@@ -5737,6 +5759,9 @@ public:
|
||||
inline void adoptControllerMode(::capnp::Orphan< ::cereal::SlcAction::ControllerCruiseAction>&& value);
|
||||
inline ::capnp::Orphan< ::cereal::SlcAction::ControllerCruiseAction> disownControllerMode();
|
||||
|
||||
inline ::uint64_t getWheelAdoptMonoTime();
|
||||
inline void setWheelAdoptMonoTime( ::uint64_t value);
|
||||
|
||||
private:
|
||||
::capnp::_::StructBuilder _builder;
|
||||
template <typename, ::capnp::Kind>
|
||||
@@ -6128,6 +6153,8 @@ public:
|
||||
inline bool hasTeslaStockCruise() const;
|
||||
inline ::cereal::SlcCruiseEvent::TeslaStockCruise::Reader getTeslaStockCruise() const;
|
||||
|
||||
inline ::uint64_t getWheelOverrideSince() const;
|
||||
|
||||
private:
|
||||
::capnp::_::StructReader _reader;
|
||||
template <typename, ::capnp::Kind>
|
||||
@@ -6228,6 +6255,9 @@ public:
|
||||
inline void adoptTeslaStockCruise(::capnp::Orphan< ::cereal::SlcCruiseEvent::TeslaStockCruise>&& value);
|
||||
inline ::capnp::Orphan< ::cereal::SlcCruiseEvent::TeslaStockCruise> disownTeslaStockCruise();
|
||||
|
||||
inline ::uint64_t getWheelOverrideSince();
|
||||
inline void setWheelOverrideSince( ::uint64_t value);
|
||||
|
||||
private:
|
||||
::capnp::_::StructBuilder _builder;
|
||||
template <typename, ::capnp::Kind>
|
||||
@@ -20214,6 +20244,62 @@ inline ::capnp::Orphan< ::capnp::Text> SlcState::Builder::disownUpcomingBoundary
|
||||
::capnp::bounded<21>() * ::capnp::POINTERS));
|
||||
}
|
||||
|
||||
inline bool SlcState::Reader::getAcceptedIsPreview() const {
|
||||
return _reader.getDataField<bool>(
|
||||
::capnp::bounded<141>() * ::capnp::ELEMENTS);
|
||||
}
|
||||
|
||||
inline bool SlcState::Builder::getAcceptedIsPreview() {
|
||||
return _builder.getDataField<bool>(
|
||||
::capnp::bounded<141>() * ::capnp::ELEMENTS);
|
||||
}
|
||||
inline void SlcState::Builder::setAcceptedIsPreview(bool value) {
|
||||
_builder.setDataField<bool>(
|
||||
::capnp::bounded<141>() * ::capnp::ELEMENTS, value);
|
||||
}
|
||||
|
||||
inline ::uint64_t SlcState::Reader::getWheelOverrideSince() const {
|
||||
return _reader.getDataField< ::uint64_t>(
|
||||
::capnp::bounded<18>() * ::capnp::ELEMENTS);
|
||||
}
|
||||
|
||||
inline ::uint64_t SlcState::Builder::getWheelOverrideSince() {
|
||||
return _builder.getDataField< ::uint64_t>(
|
||||
::capnp::bounded<18>() * ::capnp::ELEMENTS);
|
||||
}
|
||||
inline void SlcState::Builder::setWheelOverrideSince( ::uint64_t value) {
|
||||
_builder.setDataField< ::uint64_t>(
|
||||
::capnp::bounded<18>() * ::capnp::ELEMENTS, value);
|
||||
}
|
||||
|
||||
inline ::uint64_t SlcState::Reader::getWheelAdoptMonoTime() const {
|
||||
return _reader.getDataField< ::uint64_t>(
|
||||
::capnp::bounded<19>() * ::capnp::ELEMENTS);
|
||||
}
|
||||
|
||||
inline ::uint64_t SlcState::Builder::getWheelAdoptMonoTime() {
|
||||
return _builder.getDataField< ::uint64_t>(
|
||||
::capnp::bounded<19>() * ::capnp::ELEMENTS);
|
||||
}
|
||||
inline void SlcState::Builder::setWheelAdoptMonoTime( ::uint64_t value) {
|
||||
_builder.setDataField< ::uint64_t>(
|
||||
::capnp::bounded<19>() * ::capnp::ELEMENTS, value);
|
||||
}
|
||||
|
||||
inline float SlcState::Reader::getWheelAdoptTargetMps() const {
|
||||
return _reader.getDataField<float>(
|
||||
::capnp::bounded<40>() * ::capnp::ELEMENTS);
|
||||
}
|
||||
|
||||
inline float SlcState::Builder::getWheelAdoptTargetMps() {
|
||||
return _builder.getDataField<float>(
|
||||
::capnp::bounded<40>() * ::capnp::ELEMENTS);
|
||||
}
|
||||
inline void SlcState::Builder::setWheelAdoptTargetMps(float value) {
|
||||
_builder.setDataField<float>(
|
||||
::capnp::bounded<40>() * ::capnp::ELEMENTS, value);
|
||||
}
|
||||
|
||||
inline bool SlcState::SourceReading::Reader::hasSource() const {
|
||||
return !_reader.getPointerField(
|
||||
::capnp::bounded<0>() * ::capnp::POINTERS).isNull();
|
||||
@@ -21721,6 +21807,20 @@ inline ::capnp::Orphan< ::cereal::SlcAction::ControllerCruiseAction> SlcAction::
|
||||
::capnp::bounded<3>() * ::capnp::POINTERS));
|
||||
}
|
||||
|
||||
inline ::uint64_t SlcAction::Reader::getWheelAdoptMonoTime() const {
|
||||
return _reader.getDataField< ::uint64_t>(
|
||||
::capnp::bounded<4>() * ::capnp::ELEMENTS);
|
||||
}
|
||||
|
||||
inline ::uint64_t SlcAction::Builder::getWheelAdoptMonoTime() {
|
||||
return _builder.getDataField< ::uint64_t>(
|
||||
::capnp::bounded<4>() * ::capnp::ELEMENTS);
|
||||
}
|
||||
inline void SlcAction::Builder::setWheelAdoptMonoTime( ::uint64_t value) {
|
||||
_builder.setDataField< ::uint64_t>(
|
||||
::capnp::bounded<4>() * ::capnp::ELEMENTS, value);
|
||||
}
|
||||
|
||||
inline ::uint16_t SlcAction::ControllerCruiseAction::Reader::getVersion() const {
|
||||
return _reader.getDataField< ::uint16_t>(
|
||||
::capnp::bounded<0>() * ::capnp::ELEMENTS);
|
||||
@@ -22589,6 +22689,20 @@ inline ::capnp::Orphan< ::cereal::SlcCruiseEvent::TeslaStockCruise> SlcCruiseEve
|
||||
::capnp::bounded<5>() * ::capnp::POINTERS));
|
||||
}
|
||||
|
||||
inline ::uint64_t SlcCruiseEvent::Reader::getWheelOverrideSince() const {
|
||||
return _reader.getDataField< ::uint64_t>(
|
||||
::capnp::bounded<7>() * ::capnp::ELEMENTS);
|
||||
}
|
||||
|
||||
inline ::uint64_t SlcCruiseEvent::Builder::getWheelOverrideSince() {
|
||||
return _builder.getDataField< ::uint64_t>(
|
||||
::capnp::bounded<7>() * ::capnp::ELEMENTS);
|
||||
}
|
||||
inline void SlcCruiseEvent::Builder::setWheelOverrideSince( ::uint64_t value) {
|
||||
_builder.setDataField< ::uint64_t>(
|
||||
::capnp::bounded<7>() * ::capnp::ELEMENTS, value);
|
||||
}
|
||||
|
||||
inline ::uint16_t SlcCruiseEvent::TeslaStockCruise::Reader::getVersion() const {
|
||||
return _reader.getDataField< ::uint16_t>(
|
||||
::capnp::bounded<0>() * ::capnp::ELEMENTS);
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -327,12 +327,12 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
|
||||
{"HideDMIcon", {PERSISTENT, BOOL, "0"}},
|
||||
{"HidePersonalityAlerts", {PERSISTENT, BOOL, "0"}},
|
||||
{"ShowSpeedLimits", {PERSISTENT, BOOL, "1"}},
|
||||
{"SLCConfirmation", {PERSISTENT, BOOL, "0"}},
|
||||
{"SLCAdvanceSeconds", {PERSISTENT, INT, "0"}},
|
||||
{"SLCConfirmationHigher", {PERSISTENT, BOOL, "0"}},
|
||||
{"SLCConfirmationLower", {PERSISTENT, BOOL, "0"}},
|
||||
{"SLCFallback", {PERSISTENT, INT}}, // Retired; preserve saved bytes for older versions.
|
||||
{"SLCOverride", {PERSISTENT, INT, "1"}},
|
||||
{"SLCOnEngage", {PERSISTENT, BOOL, "0"}},
|
||||
{"SLCConfirmation", {PERSISTENT, INT, "0"}},
|
||||
{"SLCAdvanceHigherSeconds", {PERSISTENT, INT, "0"}},
|
||||
{"SLCAdvanceLowerSeconds", {PERSISTENT, INT, "0"}},
|
||||
{"SLCOverrideMode", {PERSISTENT, INT, "1"}},
|
||||
{"SLCWheelOverride", {PERSISTENT, BOOL, "0"}},
|
||||
{"SLCQualifiedHistory", {PERSISTENT, JSON}},
|
||||
{"ConditionalModeConfig", {PERSISTENT | DONT_LOG, JSON}},
|
||||
{"ConditionalManualState", {PERSISTENT | DONT_LOG, JSON}},
|
||||
@@ -361,7 +361,8 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
|
||||
{"VASMPreferences", {PERSISTENT | DONT_LOG, JSON}},
|
||||
{"LaneChangePreferences", {PERSISTENT | DONT_LOG, JSON}},
|
||||
{"SLCPriority1", {PERSISTENT, STRING, "Vision"}},
|
||||
{"SLCPriority2", {PERSISTENT, STRING, "Dashboard"}},
|
||||
{"SLCPriority2", {PERSISTENT, STRING, "Map Data"}},
|
||||
{"SLCPriority3", {PERSISTENT, STRING, "Off"}},
|
||||
{"SpeedLimitController", {PERSISTENT, BOOL, "0"}},
|
||||
{"RouteCount", {PERSISTENT, INT, "0"}},
|
||||
{"SnoozeUpdate", {CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION, BOOL}},
|
||||
|
||||
@@ -4,7 +4,6 @@ import os
|
||||
import time
|
||||
import threading
|
||||
import uuid
|
||||
from collections import deque
|
||||
from typing import cast
|
||||
|
||||
import openpilot.cereal.messaging as messaging
|
||||
@@ -32,7 +31,7 @@ from opendbc.car.hyundai.ioniq6_media import Ioniq6MediaButtons
|
||||
from opendbc.car.hyundai.values import CAR as HYUNDAI_CAR
|
||||
from opendbc.car.interfaces import CarInterfaceBase, RadarInterfaceBase
|
||||
from openpilot.selfdrive.pandad import can_capnp_to_list, can_list_to_can_capnp
|
||||
from openpilot.selfdrive.car.cruise import VCruiseHelper, SlcPendingConfirmation
|
||||
from openpilot.selfdrive.car.cruise import SlcWheelContext, VCruiseHelper
|
||||
from openpilot.starpilot.speed_limits import physical_actions as slc_physical
|
||||
from openpilot.starpilot.aol.intent import AolProcessFaultContext, independent_axis_requested, read_settings
|
||||
from openpilot.starpilot.aol.transport_pause import TransportPauseFeedback, cp_identity
|
||||
@@ -60,7 +59,6 @@ from opendbc.car.ford.aol import (
|
||||
temporary_restriction as ford_temporary_restriction,
|
||||
)
|
||||
from opendbc.car.hyundai.canfd_angle_aol import native_observation, temporary_restriction, qualified as qualified_angle_aol
|
||||
from opendbc.car.hyundai.blended_stock_aol import qualified_alpha as qualified_blended_alpha_aol
|
||||
from opendbc.car.hyundai.canfd_owner import config_index as hyundai_canfd_config_index
|
||||
from opendbc.car.hyundai.captured_adrv_startup import CapturedADRVStartup
|
||||
from openpilot.starpilot.car.nissan.aol import (
|
||||
@@ -95,7 +93,7 @@ from openpilot.starpilot.vehicle_selection import startup_candidate
|
||||
from openpilot.starpilot.vehicle_preferences import VehicleStartupPreferences
|
||||
from openpilot.starpilot.controller_extensions import configure_controller, observe_controller_can, order_controller_can
|
||||
from openpilot.starpilot.vehicle_startup import VehicleStartupOwner
|
||||
from openpilot.starpilot.controllers.wheel_actions import WheelPublisher, key_for
|
||||
from openpilot.starpilot.controllers.wheel_actions import LIFETIME_NS as WHEEL_ACTION_MAX_AGE_NS, WheelPublisher, key_for
|
||||
from openpilot.starpilot.controllers.cruise_action import CruiseActionConsumer, eligible as cruise_action_eligible
|
||||
from openpilot.starpilot.schema_cache import get_cache, prewarm_cache_contracts, put_cache
|
||||
|
||||
@@ -141,15 +139,15 @@ def can_comm_callbacks(logcan: messaging.SubSocket, sendcan: messaging.PubSocket
|
||||
|
||||
def curve_press_receipt(CS: car.CarState, helper: VCruiseHelper, sm, *, curve_replay: bool,
|
||||
slc_replay: bool, host_long_active: bool, observed_ns: int):
|
||||
"""Reserve SLC presentation presses even when their command is stale or rejected."""
|
||||
"""A confirmation or native standstill RES must not also override Curve control."""
|
||||
accelerate = frozenset((car.CarState.ButtonEvent.Type.accelCruise,
|
||||
car.CarState.ButtonEvent.Type.resumeCruise))
|
||||
pressed = {b.type.raw for b in CS.buttonEvents if b.pressed and b.type.raw in accelerate}
|
||||
if (not curve_replay or not host_long_active or not CS.canValid or CS.canTimeout or not CS.cruiseState.available or
|
||||
not pressed):
|
||||
CS.standstill or CS.cruiseState.standstill or not pressed):
|
||||
return None
|
||||
if slc_replay and (helper.slc_consumed_button is not None or bool(pressed & helper.slc_suppressed_buttons) or
|
||||
bool(sm['slcState'].hasPending)):
|
||||
if (helper.slc_consumed_button is not None or bool(pressed & helper.slc_suppressed_buttons) or
|
||||
slc_replay and bool(sm['slcState'].hasPending)):
|
||||
return None
|
||||
return ('curveAccelPress', None, float(helper.v_cruise_kph_last / 3.6),
|
||||
float(helper.v_cruise_kph / 3.6), observed_ns)
|
||||
@@ -189,10 +187,6 @@ class Car:
|
||||
ignore_valid=['autoSteeringLearning'], ignore_avg_freq=['autoSteeringLearning'])
|
||||
self.slc_cruise_event_id = 0
|
||||
self.slc_producer_session = uuid.uuid4().hex
|
||||
self.slc_command_sock = messaging.sub_sock('slcCruiseCommand', conflate=False) if self.slc_replay else None
|
||||
self.slc_commands = deque(maxlen=8)
|
||||
self.slc_last_command_id = 0
|
||||
self.slc_command_session = ''
|
||||
self.slc_receipts: list[tuple[str, object, float, float, int]] = []
|
||||
self.manual_receipt = None
|
||||
self.manual_event_sequence = 0
|
||||
@@ -429,14 +423,13 @@ class Car:
|
||||
for ignored in (self.sm.ignore_alive, self.sm.ignore_valid, self.sm.ignore_average_freq):
|
||||
if 'slcState' not in ignored:
|
||||
ignored.append('slcState')
|
||||
if self.slc_command_sock is None:
|
||||
self.slc_command_sock = messaging.sub_sock('slcCruiseCommand', conflate=False)
|
||||
if 'slcDashboardObservation' not in self.pm.sock:
|
||||
self.pm.sock['slcDashboardObservation'] = messaging.pub_sock('slcDashboardObservation')
|
||||
self.slc_requested_configuration = self.read_slc_configuration()
|
||||
self.slc_configuration = self.slc_requested_configuration
|
||||
self.slc_replay = self.slc_configuration[0]
|
||||
self.slc_control_enabled = self.slc_replay and self.slc_configuration[1].enabled
|
||||
self.slc_match_on_engage = self.slc_transport_available and self.read_slc_match_on_engage()
|
||||
self.slc_source_floor_ns = 0
|
||||
|
||||
# Write previous route's CarParams
|
||||
@@ -477,6 +470,9 @@ class Car:
|
||||
feature_enabled(self.params, self.CP, 'slc', os.environ))
|
||||
return bool(admitted), settings
|
||||
|
||||
def read_slc_match_on_engage(self) -> bool:
|
||||
return read_saved(self.params, 'SLCOnEngage', 1) == (b'1', True)
|
||||
|
||||
def refresh_slc_configuration(self, now_ns: int) -> None:
|
||||
configuration = getattr(self, 'slc_requested_configuration', None)
|
||||
if configuration is None or configuration == getattr(self, 'slc_configuration', None):
|
||||
@@ -484,12 +480,8 @@ class Car:
|
||||
self.slc_configuration = configuration
|
||||
self.slc_replay = configuration[0]
|
||||
self.slc_control_enabled = self.slc_replay and configuration[1].enabled
|
||||
# Invalidate old SLC choices while keeping consumed buttons latched until release.
|
||||
self.slc_source_floor_ns = max(getattr(self, 'slc_source_floor_ns', 0), now_ns)
|
||||
self.slc_commands.clear()
|
||||
self.slc_command_session = ''
|
||||
self.slc_last_command_id = 0
|
||||
# Keep the shared event producer and held suppression until physical release.
|
||||
# Only SLC's sourced/issued work predating this admission is retired.
|
||||
|
||||
def observe_distance_personality(self, CS: car.CarState, now_ns: int) -> None:
|
||||
tracker = getattr(self, 'distance_personality_tracker', None)
|
||||
@@ -582,18 +574,22 @@ class Car:
|
||||
0 < control_log_ns <= now_ns and now_ns - control_log_ns <= slc_physical.STATE_MAX_AGE_NS and
|
||||
self.sm['carControl'].enabled)
|
||||
host_control_enabled = bool(self.CP.openpilotLongitudinalControl and not self.CP.pcmCruise and host_enabled)
|
||||
host_long_active = host_control_enabled and self.sm['carControl'].longActive
|
||||
host_long_active = bool(self.CP.openpilotLongitudinalControl and host_enabled and self.sm['carControl'].longActive)
|
||||
slc_long_active = bool(getattr(self, 'slc_control_enabled', self.slc_replay) and self.CP.openpilotLongitudinalControl and
|
||||
host_enabled and (self.sm['carControl'].longActive or CS.gasPressed))
|
||||
pending = None
|
||||
if self.slc_replay:
|
||||
pending = slc_physical.pending_confirmation(
|
||||
self.sm, now_ns, long_active=slc_long_active, after_ns=floor_ns)
|
||||
approved = (slc_physical.state_current(self.sm, observation, self.slc_producer_session, now_ns)
|
||||
if self.v_cruise_helper.toyota is not None and self.slc_control_enabled else None)
|
||||
if approved is not None and int(approved.frameMonoTime) <= floor_ns:
|
||||
approved = None
|
||||
self.update_cruise_speed(CS, pending, approved=approved)
|
||||
wheel_context = None
|
||||
slc_settings = getattr(self, 'slc_configuration', (False, None))[1]
|
||||
cruise_helper = self.v_cruise_helper
|
||||
if (self.slc_replay and getattr(slc_settings, 'wheel_override', False) and host_long_active and can_rcv_valid and
|
||||
not self.CP.pcmCruise and pending is None and (cruise_helper.slc_wheel_press is not None or len(CS.buttonEvents) > 0) and
|
||||
not (CS.brakePressed or CS.gasPressed or CS.standstill or CS.cruiseState.standstill)):
|
||||
wheel_context = slc_physical.wheel_override_context(self.sm, now_ns, after_ns=floor_ns,
|
||||
selected_kph=cruise_helper.v_cruise_kph, is_metric=self.is_metric)
|
||||
self.update_cruise_speed(CS, pending, wheel_context=wheel_context)
|
||||
if self.controller_cruise_sock is not None:
|
||||
for _ in range(8):
|
||||
request = messaging.recv_one_or_none(self.controller_cruise_sock)
|
||||
@@ -602,6 +598,8 @@ class Car:
|
||||
self.controller_cruise_consumer.apply(request, self.CP, CS, self.sm, self.v_cruise_helper,
|
||||
now_ns=now_ns, is_metric=self.is_metric,
|
||||
enabled=bool(host_control_enabled and can_rcv_valid and not self.params.get_bool("SafeMode")))
|
||||
if self.slc_replay:
|
||||
self.apply_slc_adopt(now_ns, host_control_enabled=host_control_enabled, after_ns=floor_ns)
|
||||
consumed = self.v_cruise_helper.slc_consumed_button
|
||||
if self.aol_card_intent is not None:
|
||||
if self.aol_process_fault_context is None:
|
||||
@@ -616,10 +614,9 @@ class Car:
|
||||
self.aol_card_intent, cancel_source_ns=cancel_source_ns, now_ns=now_ns,
|
||||
producer=getattr(self, 'slc_producer_session', ''))
|
||||
cancel_feedback = self.aol_transport_feedback
|
||||
if (hkg_cancel_retained and (self.CP.carFingerprint in (HYUNDAI_CAR.KIA_EV9, HYUNDAI_CAR.HYUNDAI_IONIQ_5_PE) or
|
||||
qualified_blended_alpha_aol(self.CP, marked_only=True)) and
|
||||
if (hkg_cancel_retained and self.CP.carFingerprint in (HYUNDAI_CAR.KIA_EV9, HYUNDAI_CAR.HYUNDAI_IONIQ_5_PE) and
|
||||
cancel_feedback is not None and not cancel_feedback.longitudinal_cancel_ns):
|
||||
# The pure guard proved this exact raw CANCEL/FULL join. Bind
|
||||
# The pure guard proved this exact CCNC raw CANCEL/FULL join. Bind
|
||||
# its first local receipt for the existing bounded withdrawal handshake;
|
||||
# held samples never renew it or any source/native/output deadline.
|
||||
if cancel_source_ns > cancel_feedback.last_cancel_source_ns:
|
||||
@@ -800,52 +797,6 @@ class Car:
|
||||
slc_replay=self.slc_replay, host_long_active=host_long_active, observed_ns=now_ns)
|
||||
if curve_receipt is not None:
|
||||
self.slc_receipts.append(curve_receipt)
|
||||
if self.slc_command_sock is not None:
|
||||
for _ in range(8):
|
||||
command_message = messaging.recv_one_or_none(self.slc_command_sock)
|
||||
if command_message is None:
|
||||
break
|
||||
if self.slc_replay and command_message.valid:
|
||||
self.slc_commands.append(command_message)
|
||||
for _ in range(len(self.slc_commands)):
|
||||
command_message = self.slc_commands.popleft()
|
||||
command = command_message.slcCruiseCommand
|
||||
if int(command.issuedMonoTime) <= floor_ns:
|
||||
continue
|
||||
if now_ns < int(command.issuedMonoTime):
|
||||
if now_ns <= int(command.expiresMonoTime):
|
||||
self.slc_commands.append(command_message)
|
||||
continue
|
||||
state = slc_physical.state_current(self.sm, observation, self.slc_producer_session, now_ns)
|
||||
if (state is None or int(self.sm.logMonoTime['slcState']) < int(command.issuedMonoTime)) and now_ns < int(command.expiresMonoTime):
|
||||
self.slc_commands.append(command_message)
|
||||
continue
|
||||
if state is not None and str(state.sessionId) != self.slc_command_session:
|
||||
self.slc_command_session = str(state.sessionId)
|
||||
self.slc_last_command_id = 0
|
||||
if not self.slc_command_session:
|
||||
self.slc_command_session = str(command.sessionId)
|
||||
if str(command.sessionId) != self.slc_command_session or int(command.commandId) <= self.slc_last_command_id:
|
||||
continue
|
||||
self.slc_last_command_id = int(command.commandId)
|
||||
previous_mps = float(self.v_cruise_helper.v_cruise_kph / 3.6)
|
||||
origin = SlcPendingConfirmation(str(command.sessionId), int(command.decisionId), int(command.presentationId))
|
||||
owners = {**self.v_cruise_helper.slc_suppression_owner, **self.v_cruise_helper.slc_released_owner}
|
||||
allowed_buttons = {button for button, owner in owners.items() if owner == origin} if int(command.decisionId) > 0 else set()
|
||||
intervening_button = (bool(self.v_cruise_helper.slc_cruise_change) or
|
||||
any(b.type.raw not in allowed_buttons for b in CS.buttonEvents) or
|
||||
any(timer > 0 and button not in allowed_buttons
|
||||
for button, timer in self.v_cruise_helper.button_timers.items()))
|
||||
applicable = slc_physical.command_applicable(
|
||||
command, state, observation, self.slc_producer_session, now_ns, previous_mps,
|
||||
long_active=slc_long_active,
|
||||
button_event=intervening_button)
|
||||
if self.v_cruise_helper.toyota is not None:
|
||||
applicable = applicable and self.v_cruise_helper.toyota.can_apply(float(command.targetMps))
|
||||
if applicable:
|
||||
self.v_cruise_helper.apply_slc_target(float(command.targetMps), self.is_metric)
|
||||
self.slc_receipts.append(('commandApplied' if applicable else 'commandRejected', command,
|
||||
previous_mps, float(self.v_cruise_helper.v_cruise_kph / 3.6), now_ns))
|
||||
blocked_keys = set()
|
||||
if self.manual_receipt is not None:
|
||||
blocked_keys.add(key_for(self.manual_receipt[0]))
|
||||
@@ -861,28 +812,49 @@ class Car:
|
||||
|
||||
return CS, RD
|
||||
|
||||
def update_cruise_speed(self, CS, pending=None, *, approved=None):
|
||||
def apply_slc_adopt(self, now_ns: int, *, host_control_enabled: bool, after_ns: int = 0) -> None:
|
||||
pending = getattr(self, 'slc_wheel_adopt', None)
|
||||
if pending is None:
|
||||
return
|
||||
wheel_ns, selected_kph = pending
|
||||
helper = self.v_cruise_helper
|
||||
if (not after_ns < wheel_ns <= now_ns <= wheel_ns + WHEEL_ACTION_MAX_AGE_NS or
|
||||
not host_control_enabled or not self.slc_control_enabled or
|
||||
not helper.v_cruise_initialized or helper.v_cruise_kph != selected_kph or any(helper.button_timers.values())):
|
||||
self.slc_wheel_adopt = None
|
||||
return
|
||||
target = slc_physical.adopted_target(self.sm, now_ns, wheel_ns=wheel_ns)
|
||||
if target is not None:
|
||||
self.slc_wheel_adopt = None
|
||||
helper.apply_slc_target(target, self.is_metric)
|
||||
|
||||
def update_cruise_speed(self, CS, pending=None, *, wheel_context: SlcWheelContext | None = None):
|
||||
self.v_cruise_helper.update_v_cruise(CS, self.sm['carControl'].enabled, self.is_metric, pending,
|
||||
slc_enabled=getattr(self, 'slc_control_enabled', getattr(self, 'slc_replay', False)),
|
||||
slc_approved=approved)
|
||||
wheel_context=wheel_context)
|
||||
if self.sm['carControl'].enabled and not self.CC_prev.enabled:
|
||||
initial = None
|
||||
target, initial = None, None
|
||||
now_ns = int(self.can_log_mono_time) if REPLAY and hasattr(self, 'can_log_mono_time') else time.monotonic_ns()
|
||||
if (getattr(self, 'slc_control_enabled', False) and self.CP.openpilotLongitudinalControl and
|
||||
if (getattr(self, 'slc_match_on_engage', False) and getattr(self, 'slc_control_enabled', False) and
|
||||
not self.CP.pcmCruise and self.CP.openpilotLongitudinalControl and
|
||||
CS.canValid and not CS.canTimeout and CS.cruiseState.available and
|
||||
self.sm.valid['carControl'] and self.sm.alive['carControl'] and
|
||||
0 < int(self.sm.logMonoTime['carControl']) <= now_ns and
|
||||
now_ns - int(self.sm.logMonoTime['carControl']) <= slc_physical.STATE_MAX_AGE_NS and
|
||||
(self.sm['carControl'].longActive or CS.gasPressed)):
|
||||
evidence = getattr(self.CI.CS, 'dashboard_limit', None)
|
||||
initial = slc_physical.engagement_state(
|
||||
self.sm, evidence.observation if evidence is not None else None, self.slc_producer_session,
|
||||
now_ns, after_ns=getattr(self, 'slc_source_floor_ns', 0))
|
||||
# Initialization owns this edge; evaluate commands against its final speed.
|
||||
now_ns - int(self.sm.logMonoTime['carControl']) <= slc_physical.STATE_MAX_AGE_NS):
|
||||
long_requested = self.sm['carControl'].longActive or CS.gasPressed
|
||||
if not long_requested and 'aolAxisState' in self.sm.logMonoTime:
|
||||
# AOL can acknowledge the request before longitudinal actuators become active.
|
||||
axis = current_axis(self.sm, now_ns=now_ns)
|
||||
long_requested = axis is not None and axis.nativeAcknowledged and axis.desiredLongitudinal
|
||||
if long_requested:
|
||||
evidence = getattr(self.CI.CS, 'dashboard_limit', None)
|
||||
target, initial = slc_physical.engagement_target(
|
||||
self.sm, evidence.observation if evidence is not None else None, self.slc_producer_session,
|
||||
now_ns, after_ns=getattr(self, 'slc_source_floor_ns', 0))
|
||||
adopted = self.v_cruise_helper.initialize_v_cruise(
|
||||
self.CS_prev, self.experimental_mode, resume=self.vehicle_startup.consume_cruise_resume(), initial_speed=self.initial_cruise_speed,
|
||||
slc_target_mps=float(initial.presentationAdjustedLimit) if initial is not None else None, is_metric=self.is_metric)
|
||||
if adopted:
|
||||
slc_target_mps=target, is_metric=self.is_metric)
|
||||
if adopted and initial is not None:
|
||||
self.slc_receipts.append(('engagementAdopt', initial, 0.0, float(self.v_cruise_helper.v_cruise_kph / 3.6), now_ns))
|
||||
|
||||
def aol_cancel_source(self, CS, now_ns: int) -> int:
|
||||
@@ -1151,11 +1123,6 @@ class Car:
|
||||
elif self.CP.brand == 'hyundai':
|
||||
from openpilot.starpilot.car.hyundai.dashboard import compose as hyundai_compose
|
||||
companion = hyundai_compose(self.CP, self.CI.CS, CS, companion, car_state_stamp=int(cs_send.logMonoTime))
|
||||
if self.v_cruise_helper.toyota is not None:
|
||||
if companion is None:
|
||||
companion = messaging.new_message('starpilotCarState', valid=bool(cs_send.valid), logMonoTime=int(cs_send.logMonoTime))
|
||||
companion.starpilotCarState.sourceCarStateMonoTime = int(cs_send.logMonoTime)
|
||||
self.v_cruise_helper.toyota.publish(companion.starpilotCarState.toyotaCruise)
|
||||
self.car_gps_publisher.update(self.CI.CS, self.pm, companion)
|
||||
self.pm.send('carState', cs_send)
|
||||
if getattr(self, 'learning_stamp_observer', None) is not None:
|
||||
@@ -1163,6 +1130,9 @@ class Car:
|
||||
|
||||
wheel = getattr(self, 'wheel_publisher', None)
|
||||
if wheel is not None:
|
||||
if (getattr(self, 'slc_control_enabled', False) and self.CC_prev.enabled and self.sm['carControl'].enabled and
|
||||
any(action == 10 for _, action in self.wheel_commands)):
|
||||
self.slc_wheel_adopt = (int(cs_send.logMonoTime), self.v_cruise_helper.v_cruise_kph)
|
||||
wheel.publish(self.wheel_commands, self.CP, self.pm, now_ns=int(cs_send.logMonoTime),
|
||||
drive_id=int(self.sm['deviceState'].startedMonoTime), source_car_ns=int(cs_send.logMonoTime),
|
||||
source_control_ns=int(self.sm.logMonoTime['carControl']))
|
||||
@@ -1202,6 +1172,8 @@ class Car:
|
||||
if kind == 'driverChange':
|
||||
assert isinstance(detail, tuple) and len(detail) == 4
|
||||
record.button, record.longPress = detail[2], detail[3]
|
||||
if self.v_cruise_helper.slc_wheel_tap is not None:
|
||||
record.sessionId, record.wheelOverrideSince = self.v_cruise_helper.slc_wheel_tap
|
||||
elif kind in ('confirmationAccept', 'confirmationReject'):
|
||||
record.button = detail.button
|
||||
record.sessionId = detail.pending.session_id
|
||||
@@ -1209,13 +1181,11 @@ class Car:
|
||||
record.presentationId = detail.pending.presentation_id
|
||||
elif kind == 'curveAccelPress':
|
||||
record.button = 'accel'
|
||||
else:
|
||||
elif kind == 'engagementAdopt':
|
||||
record.sessionId = str(detail.sessionId)
|
||||
record.commandId = 0 if kind == 'engagementAdopt' else int(detail.commandId)
|
||||
record.decisionId = int(detail.decisionId)
|
||||
record.presentationId = int(detail.presentationId)
|
||||
if kind == 'engagementAdopt':
|
||||
record.button = 'decel'
|
||||
record.button = 'decel'
|
||||
self.pm.send('slcCruiseEvent', event)
|
||||
|
||||
if getattr(self, 'conditional_replay', False) and self.manual_receipt is not None:
|
||||
@@ -1637,6 +1607,10 @@ class Car:
|
||||
if physical.button_credit_ns - self.volt_cc_boot_offset_ns <= max(self.volt_cc_drive_id, self.volt_cc_source_floor_ns):
|
||||
self.CI.CC.volt_cc_consumed_source_ns = physical.button_credit_ns
|
||||
self.CI.CC.volt_cc_metric = self.is_metric
|
||||
self.CI.CC.volt_cc_speed_ceiling_mps = (slc_physical.active_ceiling(
|
||||
self.sm, self.volt_cc_now_mono_ns,
|
||||
after_ns=max(self.volt_cc_drive_id, self.volt_cc_source_floor_ns, getattr(self, 'slc_source_floor_ns', 0)))
|
||||
if getattr(self, 'slc_control_enabled', False) else None)
|
||||
|
||||
if getattr(self.CI.CC, "bolt_cc_profile", False):
|
||||
self.CI.CC.bolt_cc_metric = self.is_metric
|
||||
@@ -1898,6 +1872,8 @@ class Car:
|
||||
now = time.monotonic()
|
||||
if now >= next_cruise_read:
|
||||
self.slc_requested_configuration = self.read_slc_configuration()
|
||||
if getattr(self, 'slc_transport_available', False):
|
||||
self.slc_match_on_engage = self.read_slc_match_on_engage()
|
||||
self.v_cruise_helper.intervals = read_cruise_intervals(self.params, self.CP)
|
||||
pitch_input = getattr(self.CI.CC, 'long_pitch_input', None)
|
||||
if pitch_input is not None:
|
||||
|
||||
@@ -43,6 +43,9 @@ class SlcConsumedButton:
|
||||
button: str
|
||||
|
||||
|
||||
SlcWheelContext = tuple[str, int]
|
||||
|
||||
|
||||
def validated_initial_cruise_speed(value: object) -> int:
|
||||
if type(value) is int and V_CRUISE_INITIAL <= value <= V_CRUISE_MAX:
|
||||
return value
|
||||
@@ -66,35 +69,27 @@ class VCruiseHelper:
|
||||
self.button_change_states = {btn: {"standstill": False, "enabled": False} for btn in self.button_timers}
|
||||
self.pcm_slc_sample: tuple[float, int] | None = None
|
||||
self.pcm_slc_button = ButtonType.unknown
|
||||
# Set by software cruise steps or a checked Toyota PCM physical +/- change.
|
||||
self.slc_cruise_change: tuple[float, float, str, bool] | None = None
|
||||
self.slc_consumed_button: SlcConsumedButton | None = None
|
||||
self.slc_suppressed_buttons: set[ButtonType] = set()
|
||||
self.slc_suppression_owner: dict[ButtonType, SlcPendingConfirmation] = {}
|
||||
self.slc_released_owner: dict[ButtonType, SlcPendingConfirmation] = {}
|
||||
self.slc_released_suppressed = False
|
||||
self.slc_pcm = False
|
||||
from openpilot.starpilot.speed_limits.toyota import eligible, ToyotaCruise
|
||||
self.toyota = ToyotaCruise() if eligible(CP) else None
|
||||
self.slc_wheel_press: SlcWheelContext | None = None
|
||||
self.slc_wheel_tap: SlcWheelContext | None = None
|
||||
|
||||
@property
|
||||
def v_cruise_initialized(self):
|
||||
return self.v_cruise_kph != V_CRUISE_UNSET
|
||||
|
||||
def update_v_cruise(self, CS, enabled, is_metric, slc_pending: SlcPendingConfirmation | None = None, *, slc_enabled: bool = False,
|
||||
slc_approved=None):
|
||||
self.slc_pcm = bool(slc_enabled and self.CP.pcmCruise and self.CP.openpilotLongitudinalControl)
|
||||
def update_v_cruise(self, CS, enabled, is_metric, slc_pending: SlcPendingConfirmation | None = None, *,
|
||||
slc_enabled: bool = False, wheel_context: SlcWheelContext | None = None):
|
||||
from opendbc.car.subaru.software_cruise import selected as subaru_software_selected
|
||||
subaru_software = subaru_software_selected(self.CP)
|
||||
software_cruise = not self.CP.pcmCruise or self.slc_pcm or subaru_software
|
||||
software_cruise = not self.CP.pcmCruise or subaru_software
|
||||
self.slc_cruise_change = None
|
||||
self.slc_consumed_button = None
|
||||
self.slc_released_suppressed = False
|
||||
self.slc_released_owner = {}
|
||||
self.slc_wheel_tap = None
|
||||
self.v_cruise_kph_last = self.v_cruise_kph
|
||||
if self.toyota is not None:
|
||||
self.slc_pcm = self.toyota.update(self, CS, enabled, slc_enabled, slc_approved, is_metric)
|
||||
software_cruise = self.slc_pcm
|
||||
now_ns = monotonic_ns()
|
||||
if not CS.cruiseState.available or now_ns - self.pending_resume_ns > 500_000_000:
|
||||
self.pending_resume_ns = 0
|
||||
@@ -106,21 +101,26 @@ class VCruiseHelper:
|
||||
button.type in (ButtonType.accelCruise, ButtonType.resumeCruise) and self.v_cruise_initialized):
|
||||
self.pending_resume_ns = now_ns
|
||||
|
||||
if software_cruise or self.slc_suppressed_buttons:
|
||||
if software_cruise or slc_enabled and self.CP.openpilotLongitudinalControl or self.slc_suppressed_buttons:
|
||||
self._update_slc_button_latches(CS, enabled, slc_pending)
|
||||
|
||||
if (not slc_enabled or not enabled or self.CP.pcmCruise or slc_pending is not None or
|
||||
not CS.cruiseState.available or ButtonType.accelCruise in self.slc_suppressed_buttons):
|
||||
wheel_context = None
|
||||
if wheel_context != self.slc_wheel_press:
|
||||
self.slc_wheel_press = None
|
||||
if wheel_context is not None and self.button_timers[ButtonType.accelCruise] == 0:
|
||||
pressed = {b.type.raw for b in CS.buttonEvents if b.pressed}
|
||||
if ButtonType.accelCruise in pressed and ButtonType.resumeCruise not in pressed:
|
||||
self.slc_wheel_press = wheel_context
|
||||
|
||||
if CS.cruiseState.available:
|
||||
if software_cruise:
|
||||
if self.toyota is None and (self.slc_pcm or subaru_software) and (not enabled or not self.v_cruise_initialized):
|
||||
# PCM still owns engagement; SLC owns the software set speed while system long is selected.
|
||||
if subaru_software and (not enabled or not self.v_cruise_initialized):
|
||||
stock_kph = CS.cruiseState.speed * CV.MS_TO_KPH
|
||||
self.v_cruise_kph = stock_kph if 0 < stock_kph < V_CRUISE_UNSET else V_CRUISE_UNSET
|
||||
# if stock cruise is completely disabled, then we can use our own set speed logic
|
||||
self._update_v_cruise_non_pcm(CS, enabled, is_metric)
|
||||
if self.toyota is None:
|
||||
self.v_cruise_cluster_kph = self.v_cruise_kph
|
||||
if self.toyota is not None:
|
||||
self.toyota.constrain(self)
|
||||
self.v_cruise_cluster_kph = self.v_cruise_kph
|
||||
self.update_button_timers(CS, enabled)
|
||||
else:
|
||||
self.v_cruise_kph = CS.cruiseState.speed * CV.MS_TO_KPH
|
||||
@@ -139,6 +139,8 @@ class VCruiseHelper:
|
||||
self.pcm_slc_button = ButtonType.unknown
|
||||
if software_cruise:
|
||||
self.update_button_timers(CS, enabled)
|
||||
if self.button_timers[ButtonType.accelCruise] == 0:
|
||||
self.slc_wheel_press = None
|
||||
|
||||
def _update_toyota_pcm_slc_change(self, CS, enabled: bool, now_ns: int) -> None:
|
||||
"""Observe a physical PCM speed change without writing stock selected speed."""
|
||||
@@ -165,8 +167,9 @@ class VCruiseHelper:
|
||||
held = ButtonType.unknown
|
||||
if continuous:
|
||||
delta = self.v_cruise_kph - previous[0]
|
||||
if ((button == ButtonType.accelCruise and delta > 0.) or
|
||||
(button == ButtonType.decelCruise and delta < 0.)):
|
||||
if (not self.slc_suppressed_buttons and not self.slc_released_suppressed and
|
||||
((button == ButtonType.accelCruise and delta > 0.) or
|
||||
(button == ButtonType.decelCruise and delta < 0.))):
|
||||
self.slc_cruise_change = (previous[0] / 3.6, self.v_cruise_kph / 3.6,
|
||||
'accel' if delta > 0. else 'decel', False)
|
||||
self.pcm_slc_button = held
|
||||
@@ -179,17 +182,13 @@ class VCruiseHelper:
|
||||
continue
|
||||
if not b.pressed and button in self.slc_suppressed_buttons:
|
||||
self.slc_suppressed_buttons.remove(button)
|
||||
prior_owner = self.slc_suppression_owner.pop(button, None)
|
||||
if prior_owner is not None:
|
||||
self.slc_released_owner[button] = prior_owner
|
||||
self.slc_released_suppressed = True
|
||||
self.button_timers[button] = 0
|
||||
elif (b.pressed and enabled and CS.cruiseState.available and slc_pending is not None and
|
||||
not (self.toyota is not None and button == ButtonType.accelCruise and
|
||||
not (self.CP.pcmCruise and button == ButtonType.accelCruise and
|
||||
(CS.standstill or CS.cruiseState.standstill or not CS.cruiseState.enabled)) and
|
||||
button not in self.slc_suppressed_buttons):
|
||||
self.slc_suppressed_buttons.add(button)
|
||||
self.slc_suppression_owner[button] = slc_pending
|
||||
self.slc_consumed_button = SlcConsumedButton(slc_pending, 'accel' if button == ButtonType.accelCruise else 'decel')
|
||||
|
||||
def _update_v_cruise_non_pcm(self, CS, enabled, is_metric):
|
||||
@@ -231,26 +230,20 @@ class VCruiseHelper:
|
||||
if not self.button_change_states[button_type]["enabled"]:
|
||||
return
|
||||
|
||||
self.adjust_v_cruise(button_type, CS, is_metric, long_press=long_press)
|
||||
changed = self.adjust_v_cruise(button_type, CS, is_metric, long_press=long_press)
|
||||
if changed and button_type == ButtonType.accelCruise and not long_press and self.button_timers[button_type] > 0:
|
||||
self.slc_wheel_tap = self.slc_wheel_press
|
||||
|
||||
def adjust_v_cruise(self, button_type, CS, is_metric: bool, *, long_press: bool = False) -> bool:
|
||||
"""Apply the normal software cruise step without synthesizing button events."""
|
||||
from opendbc.car.subaru.software_cruise import selected as subaru_software_selected
|
||||
subaru_software = subaru_software_selected(self.CP)
|
||||
if ((self.CP.pcmCruise and not self.slc_pcm and not subaru_software) or button_type not in (ButtonType.accelCruise, ButtonType.decelCruise) or
|
||||
if ((self.CP.pcmCruise and not subaru_software) or button_type not in (ButtonType.accelCruise, ButtonType.decelCruise) or
|
||||
not 0 < self.v_cruise_kph < V_CRUISE_UNSET or
|
||||
(button_type == ButtonType.accelCruise and CS.cruiseState.standstill)):
|
||||
return False
|
||||
v_cruise_delta = 1. if is_metric else IMPERIAL_INCREMENT
|
||||
previous_kph = self.v_cruise_kph
|
||||
toyota = self.toyota if self.slc_pcm else None
|
||||
previous_cluster_kph = self.v_cruise_cluster_kph
|
||||
if toyota is not None:
|
||||
if toyota.requested is None:
|
||||
return False
|
||||
if button_type == ButtonType.decelCruise:
|
||||
previous_kph = toyota.requested[0]
|
||||
self.v_cruise_kph = toyota.requested[1]
|
||||
interval = self.intervals.held if long_press else self.intervals.short
|
||||
v_cruise_delta = v_cruise_delta * interval
|
||||
display_speed = round(self.v_cruise_kph if is_metric else self.v_cruise_kph * CV.KPH_TO_MPH)
|
||||
@@ -266,11 +259,6 @@ class VCruiseHelper:
|
||||
|
||||
self.v_cruise_kph = np.clip(round(self.v_cruise_kph, 1), V_CRUISE_MIN, V_CRUISE_MAX)
|
||||
self.v_cruise_cluster_kph = self.v_cruise_kph
|
||||
if toyota is not None:
|
||||
toyota.requested = (float(self.v_cruise_kph), float(self.v_cruise_cluster_kph))
|
||||
toyota.manual_step = (previous_kph, previous_cluster_kph, 'accel' if button_type == ButtonType.accelCruise else 'decel', long_press)
|
||||
toyota.constrain(self)
|
||||
return self.v_cruise_kph != previous_kph
|
||||
if 0 < previous_kph < V_CRUISE_UNSET and self.v_cruise_kph != previous_kph:
|
||||
button = "accel" if button_type == ButtonType.accelCruise else "decel"
|
||||
self.slc_cruise_change = (float(previous_kph / 3.6), float(self.v_cruise_kph / 3.6), button, long_press)
|
||||
@@ -291,23 +279,19 @@ class VCruiseHelper:
|
||||
self.button_change_states[b.type.raw] = {"standstill": CS.cruiseState.standstill, "enabled": enabled}
|
||||
|
||||
def apply_slc_target(self, target_mps: float, is_metric: bool) -> None:
|
||||
"""Apply a qualified planner command to the card-owned software set speed."""
|
||||
"""Set the software cruise ceiling for engagement or a qualified driver request."""
|
||||
# Match ordinary button precision so stepping away and back restores this target.
|
||||
self.v_cruise_kph = slc_target_kph(target_mps, is_metric)
|
||||
self.v_cruise_cluster_kph = self.v_cruise_kph
|
||||
if self.toyota is not None:
|
||||
self.toyota.manual_step = None
|
||||
self.toyota.constrain(self)
|
||||
self.toyota.requested = (self.v_cruise_kph, self.v_cruise_cluster_kph)
|
||||
|
||||
def initialize_v_cruise(self, CS, experimental_mode: bool, *, resume: bool | None = None,
|
||||
initial_speed: object = V_CRUISE_INITIAL, slc_target_mps: float | None = None, is_metric: bool = False) -> bool:
|
||||
"""Return whether engagement adopts the qualified current SLC limit."""
|
||||
"""Return whether engagement used the supplied SLC target."""
|
||||
if (not self.CP.openpilotLongitudinalControl or slc_target_mps is None or type(slc_target_mps) not in (int, float) or
|
||||
not math.isfinite(slc_target_mps) or not V_CRUISE_MIN / 3.6 - 1e-5 <= slc_target_mps <= V_CRUISE_MAX / 3.6 + 1e-5):
|
||||
slc_target_mps = None
|
||||
# PCM initializes when SLC does not own SET.
|
||||
if self.CP.pcmCruise and (not self.slc_pcm or slc_target_mps is None):
|
||||
# Native cruise owns PCM SET, including when SLC supplies a planner cap.
|
||||
if self.CP.pcmCruise:
|
||||
return False
|
||||
# A car-owned acknowledgment can outlive the physical button event.
|
||||
if resume is None:
|
||||
@@ -315,15 +299,12 @@ class VCruiseHelper:
|
||||
resume = (0 < self.pending_resume_ns and 0 <= age <= 500_000_000) or any(
|
||||
b.type in (ButtonType.accelCruise, ButtonType.resumeCruise) for b in CS.buttonEvents)
|
||||
self.pending_resume_ns = 0
|
||||
if slc_target_mps is None and resume and self.v_cruise_initialized:
|
||||
if not self.CP.pcmCruise:
|
||||
self.v_cruise_kph = self.v_cruise_kph_last
|
||||
self.v_cruise_cluster_kph = self.v_cruise_kph
|
||||
if resume and self.v_cruise_initialized:
|
||||
self.v_cruise_kph = self.v_cruise_kph_last
|
||||
self.v_cruise_cluster_kph = self.v_cruise_kph
|
||||
return False
|
||||
elif slc_target_mps is not None:
|
||||
# Toyota still needs its native capacity/release approval before application.
|
||||
if self.toyota is None:
|
||||
self.apply_slc_target(slc_target_mps, is_metric)
|
||||
self.apply_slc_target(slc_target_mps, is_metric)
|
||||
return True
|
||||
else:
|
||||
self.v_cruise_kph = int(round(np.clip(max(CS.vEgo * CV.MS_TO_KPH, validated_initial_cruise_speed(initial_speed)), V_CRUISE_INITIAL, V_CRUISE_MAX)))
|
||||
|
||||
@@ -52,7 +52,7 @@ class TestCruiseIntervals(unittest.TestCase):
|
||||
Path(self.params.get_param_path("CustomCruiseLong")).write_bytes(b"200")
|
||||
self.assertEqual(read_cruise_intervals(self.params, self.cp), CruiseIntervals())
|
||||
|
||||
def test_slc_uses_saved_intervals_for_every_other_make_and_pcm_mode(self):
|
||||
def test_slc_preserves_software_intervals_and_native_pcm_set(self):
|
||||
brands = [brand.__module__.split('.')[-2] for brand in BRANDS if brand.__module__.split('.')[-2] != 'toyota'] + ['future']
|
||||
self.params.put("CustomCruise", 3.0, block=True)
|
||||
self.params.put("CustomCruiseLong", 7.0, block=True)
|
||||
@@ -67,6 +67,11 @@ class TestCruiseIntervals(unittest.TestCase):
|
||||
helper.v_cruise_kph = helper.v_cruise_cluster_kph = initial
|
||||
helper.intervals = read_cruise_intervals(self.params, cp)
|
||||
helper.update_v_cruise(state(button, True, native=140.), True, metric, slc_enabled=True)
|
||||
if pcm:
|
||||
helper.update_v_cruise(state(button, False, native=140.), True, metric, slc_enabled=True)
|
||||
self.assertAlmostEqual(helper.v_cruise_kph, 140., places=4)
|
||||
self.assertIsNone(helper.slc_cruise_change)
|
||||
continue
|
||||
if held:
|
||||
for _ in range(CRUISE_LONG_PRESS):
|
||||
helper.update_v_cruise(state(native=140.), True, metric, slc_enabled=True)
|
||||
|
||||
+263
-263
File diff suppressed because one or more lines are too long
BIN
Binary file not shown.
BIN
Binary file not shown.
@@ -37,7 +37,6 @@ from openpilot.starpilot.longitudinal.lead_takeoff import LeadTakeoff, project a
|
||||
from openpilot.starpilot.longitudinal.lead_behavior import adjust as adjust_lead_behavior
|
||||
from openpilot.starpilot.longitudinal.hkg_vision_stop import owner_for as vision_stop_owner
|
||||
from openpilot.starpilot.car.ford.stopped_lead import owner_for as ford_stop_owner
|
||||
from openpilot.starpilot.car.volkswagen.stopped_lead import owner_for as vw_stop_owner
|
||||
from openpilot.selfdrive.controls.lib.longitudinal_mpc_lib.long_mpc import STOP_DISTANCE
|
||||
from openpilot.starpilot.longitudinal.lead_approach import LeadApproach, LeadApproachKey, project as project_approach
|
||||
from openpilot.starpilot.lateral.lane_change_preferences import LaneChangePolicy
|
||||
@@ -133,7 +132,7 @@ class LongitudinalPlanner:
|
||||
self.lead_takeoff = LeadTakeoff()
|
||||
self.vehicle_far_follow = far_follow_owner(CP, dt)
|
||||
self.vision_stop_buffer = vision_stop_owner(CP)
|
||||
self.ford_stop_approach = ford_stop_owner(CP, STOP_DISTANCE - .5) or vw_stop_owner(CP, STOP_DISTANCE - .5)
|
||||
self.ford_stop_approach = ford_stop_owner(CP, STOP_DISTANCE - .5)
|
||||
self.preap_adaptive = preap_adaptive_owner(CP)
|
||||
|
||||
def update(self, sm, *, cruise_ceiling: CruiseCeiling | None = None,
|
||||
@@ -374,12 +373,11 @@ class LongitudinalPlanner:
|
||||
cruise_profile = replace(cruise_profile, cruise_brake_magnitude=global_magnitude)
|
||||
if sm['controlsState'].forceDecel:
|
||||
cruise_profile = None
|
||||
elif (ceiling.effective_mps is not None or curve.effective_mps is not None) and cruise_profile is not None:
|
||||
# An eligible lower ceiling must retain at least native approach
|
||||
# braking even when the saved profile requests a softer cruise response.
|
||||
elif curve.effective_mps is not None and cruise_profile is not None:
|
||||
# Curve approach retains native braking when the saved cruise response is softer.
|
||||
cruise_profile = replace(cruise_profile, cruise_brake_magnitude=max(cruise_profile.cruise_brake_magnitude,
|
||||
abs(A_CRUISE_MIN)))
|
||||
if ceiling.effective_mps is not None or curve.effective_mps is not None:
|
||||
if curve.effective_mps is not None:
|
||||
global_floor = min(global_floor, A_CRUISE_MIN)
|
||||
if selected_acceleration is not None and cruise_profile is not None:
|
||||
# The selected curve owns acceleration; following/jerks retain their owner.
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
#!/usr/bin/env python3
|
||||
import os
|
||||
import json
|
||||
from collections import deque
|
||||
from dataclasses import replace
|
||||
|
||||
from opendbc.car.structs import car
|
||||
from openpilot.common.params import Params
|
||||
@@ -12,8 +10,9 @@ from openpilot.selfdrive.controls.lib.ldw import LaneDepartureWarning
|
||||
from openpilot.selfdrive.controls.lib.longitudinal_planner import LongitudinalPlanner, NATIVE_PLAN_INPUTS
|
||||
import openpilot.cereal.messaging as messaging
|
||||
from openpilot.cereal.services import SERVICE_LIST
|
||||
from openpilot.starpilot.speed_limits import history as slc_history
|
||||
from openpilot.starpilot.speed_limits.runtime import Action as SlcAction, CruiseEvent as SlcCruiseEvent, Runtime as SlcRuntime
|
||||
from openpilot.starpilot.speed_limits.planner_host import (
|
||||
SLC_ACTIONS, SavedSlcLifecycle, action_from_message, cruise_event_from_message, current_cruise_event, receive_slc_events,
|
||||
)
|
||||
from openpilot.starpilot.longitudinal.profile_runtime import ProfileHost
|
||||
from openpilot.starpilot.longitudinal.lane_change_gap import LaneGapPreferences
|
||||
from openpilot.starpilot.longitudinal.experimental_release import ConditionalHandoff
|
||||
@@ -40,62 +39,8 @@ from openpilot.starpilot.feature_capabilities import longitudinal_supported
|
||||
from openpilot.starpilot.conditional_mode.projection import paired_clocks_ns
|
||||
from openpilot.starpilot.conditional_mode.ui_action import observation as ui_manual_observation
|
||||
from openpilot.starpilot.longitudinal.cruise_ceiling import CurveCeiling
|
||||
from openpilot.starpilot.feature_runtime import (enabled as feature_enabled, requested as feature_requested,
|
||||
slc_runtime_settings, slc_transport_capable, vision_control_enabled)
|
||||
from openpilot.starpilot.feature_runtime import enabled as feature_enabled, requested as feature_requested, slc_transport_capable
|
||||
import time
|
||||
from openpilot.starpilot.speed_limits.runtime_settings import read_params as read_slc_settings
|
||||
|
||||
|
||||
class SavedSlcLifecycle:
|
||||
"""Refresh saved admission; offsets and display units preserve driver choices."""
|
||||
|
||||
def __init__(self, params, cp, environment):
|
||||
self.params, self.cp, self.environment = params, cp, dict(environment)
|
||||
self.runtime = None
|
||||
self.configuration = None
|
||||
self.last_refresh_ns = None
|
||||
|
||||
def refresh(self, now_ns: int, *, pending_actions=None, pending_cruise=None):
|
||||
if self.last_refresh_ns is not None and 0 <= now_ns - self.last_refresh_ns < 1_000_000_000:
|
||||
return self.runtime, False
|
||||
self.last_refresh_ns = now_ns
|
||||
# One immutable parsed snapshot owns this refresh. Repeated qualification
|
||||
# used to reopen the same saved keys five times in a due model iteration.
|
||||
saved_slc = read_slc_settings(self.params)
|
||||
admitted = feature_enabled(self.params, self.cp, 'slc', self.environment, saved_slc=saved_slc)
|
||||
settings = slc_runtime_settings(self.params, self.cp, self.environment, saved_slc=saved_slc)
|
||||
vision = admitted and feature_enabled(self.params, self.cp, 'vision', self.environment, saved_slc=saved_slc)
|
||||
configuration = (admitted and not settings.errors, settings, vision,
|
||||
vision_control_enabled(self.params, self.cp, saved_slc=saved_slc),
|
||||
feature_enabled(self.params, self.cp, 'vision', {}, saved_slc=saved_slc),
|
||||
self.params.get_bool('IsMetric'))
|
||||
if configuration == self.configuration:
|
||||
return self.runtime, False
|
||||
previous_runtime = self.runtime
|
||||
if (self.runtime is not None and self.configuration is not None and
|
||||
configuration[:-1] == (self.configuration[0], replace(self.runtime.settings, offsets=settings.offsets,
|
||||
advance_seconds=settings.advance_seconds),
|
||||
*self.configuration[2:-1])):
|
||||
if settings.offsets != self.runtime.settings.offsets or configuration[5] != self.configuration[5]:
|
||||
self.runtime.update_offsets(settings.offsets, is_metric=configuration[5])
|
||||
if settings.advance_seconds != self.runtime.settings.advance_seconds:
|
||||
self.runtime.update_advance(settings.advance_seconds)
|
||||
else:
|
||||
set_speed_after_ns = self.runtime.set_speed_after_ns if self.runtime is not None else 0
|
||||
if self.runtime is not None:
|
||||
self.runtime.reset()
|
||||
self.runtime = (SlcRuntime(settings, vision_enabled=vision,
|
||||
vision_control_qualified=configuration[3],
|
||||
vision_display_qualified=configuration[4], is_metric=configuration[5])
|
||||
if configuration[0] else None)
|
||||
if self.runtime is not None:
|
||||
self.runtime.set_speed_after_ns = set_speed_after_ns
|
||||
self.configuration = configuration
|
||||
if pending_actions is not None:
|
||||
pending_actions.clear()
|
||||
if pending_cruise is not None and (self.runtime is None or self.runtime is not previous_runtime):
|
||||
pending_cruise.clear()
|
||||
return self.runtime, True
|
||||
|
||||
|
||||
def profile_for_frame(host: ProfileHost | None, sm, CP, now_ns: int, *, traffic_mode: bool | None = False):
|
||||
@@ -159,11 +104,13 @@ def traffic_profile_status(mode: bool | None, target, applied, settings) -> tupl
|
||||
|
||||
|
||||
def curve_for_frame(host: CurveHost | None, sm, CP, now_ns: int, *, drive_id: int = 0,
|
||||
event: CurveDriverEvent | None = None, follow_time_s: float | None = None):
|
||||
event: CurveDriverEvent | None = None, follow_time_s: float | None = None,
|
||||
cruise_ceiling_mps: float | None = None):
|
||||
"""A diagnostic runtime result is not planner authority by itself."""
|
||||
if host is None:
|
||||
return None, None
|
||||
result = host.sample(sm, CP, now_ns, drive_id=drive_id, event=event, follow_time_s=follow_time_s)
|
||||
result = host.sample(sm, CP, now_ns, drive_id=drive_id, event=event, follow_time_s=follow_time_s,
|
||||
cruise_ceiling_mps=cruise_ceiling_mps)
|
||||
if result.ceiling_mps is None:
|
||||
return None, result
|
||||
model_ns = sm.logMonoTime.get('modelV2', 0)
|
||||
@@ -177,6 +124,12 @@ def confirm_curve_frame(host: CurveHost | None, planner: LongitudinalPlanner, no
|
||||
host.runtime.confirm_applied(now_ns, applied=bool(planner.last_curve_ceiling_applied))
|
||||
|
||||
|
||||
def wheel_override_blocked(force_stop: ForceStopRuntime | None, curve_host: CurveHost | None, *, curve_backlog: bool = False) -> bool:
|
||||
# Force Stop can consume RES on the tick it commits, before a forcing plan is published.
|
||||
return ((force_stop is not None and (force_stop.policy.can_commit_next_frame or force_stop.plan.speed_ceiling_mps is not None)) or
|
||||
(curve_host is not None and (curve_backlog or curve_host.runtime.was_controlling)))
|
||||
|
||||
|
||||
def conditional_handoff_for_frame(host: ConditionalPlannerHost | None, sm, CP, now_ns: int,
|
||||
drive_id: int) -> ConditionalHandoff | None:
|
||||
if host is None or not CP.openpilotLongitudinalControl or CP.passive or CP.dashcamOnly:
|
||||
@@ -290,7 +243,8 @@ def update_curve_frame(planner: LongitudinalPlanner, sm, CP, now_ns: int, *, hos
|
||||
results = []
|
||||
def provider(follow_time_s: float | None):
|
||||
ceiling, result = curve_for_frame(host, sm, CP, now_ns, drive_id=drive_id,
|
||||
event=event, follow_time_s=follow_time_s)
|
||||
event=event, follow_time_s=follow_time_s,
|
||||
cruise_ceiling_mps=cruise_ceiling.speed_mps if cruise_ceiling is not None else None)
|
||||
results.append(result)
|
||||
return ceiling
|
||||
planner.update(sm, cruise_ceiling=cruise_ceiling, profile_tuning=profile_tuning,
|
||||
@@ -320,18 +274,7 @@ def queue_cruise_event(event, slc_events, curve_events, conditional_events=None,
|
||||
curve_events.append(CurveDriverEvent(str(cruise.producerSessionId), int(cruise.eventId),
|
||||
int(cruise.observedMonoTime), kind, str(cruise.button), int(event.logMonoTime)))
|
||||
else:
|
||||
slc_events.append(SlcCruiseEvent(int(cruise.eventId), int(cruise.observedMonoTime),
|
||||
float(cruise.previousMps), float(cruise.selectedMps), str(cruise.button),
|
||||
str(cruise.producerSessionId), int(event.logMonoTime), kind,
|
||||
str(cruise.sessionId), int(cruise.decisionId),
|
||||
int(cruise.presentationId), int(cruise.commandId)))
|
||||
|
||||
|
||||
def current_cruise_event(events, now_ns: int):
|
||||
max_event_age_ns = int(2e9 / SERVICE_LIST['modelV2'].frequency)
|
||||
while events and now_ns - events[0].observed_ns > max_event_age_ns:
|
||||
events.popleft()
|
||||
return events.popleft() if events and events[0].observed_ns <= now_ns else None
|
||||
slc_events.append(cruise_event_from_message(event))
|
||||
|
||||
|
||||
def queue_ui_action(event, slc_actions, conditional_actions, mode_actions=None) -> None:
|
||||
@@ -343,9 +286,8 @@ def queue_ui_action(event, slc_actions, conditional_actions, mode_actions=None)
|
||||
mode_actions.append(event)
|
||||
elif kind == 'conditionalModeCycle':
|
||||
conditional_actions.append(event)
|
||||
elif kind in ('accept', 'reject', 'adopt'):
|
||||
slc_actions.append((int(event.logMonoTime), SlcAction(str(request.sessionId), int(request.sequenceId),
|
||||
int(request.decisionId), int(request.presentationId), kind)))
|
||||
elif kind in SLC_ACTIONS:
|
||||
slc_actions.append(action_from_message(event))
|
||||
|
||||
|
||||
def current_ui_manual_event(events, now_ns: int):
|
||||
@@ -417,7 +359,7 @@ def starpilot_main():
|
||||
pending_switchback = deque(maxlen=8)
|
||||
mode_settings = ConditionalSettingsOwner(params) if mode_owner is not None else None
|
||||
slc_lifecycle = SavedSlcLifecycle(params, CP, os.environ)
|
||||
slc_runtime, _ = slc_lifecycle.refresh(0)
|
||||
slc_lifecycle.refresh(0)
|
||||
slc_action_sock = (messaging.sub_sock("slcAction", conflate=False)
|
||||
if slc_available or conditional_host is not None or mode_owner is not None else None)
|
||||
slc_cruise_sock = messaging.sub_sock("slcCruiseEvent", conflate=False)
|
||||
@@ -431,18 +373,14 @@ def starpilot_main():
|
||||
publish_services = ['longitudinalPlan', 'starpilotLongitudinalPlan', 'driverAssistance']
|
||||
if slc_available or curve_host is not None or conditional_host is not None or traffic_owner is not None or mode_owner is not None:
|
||||
publish_services.append('slcState')
|
||||
if slc_available:
|
||||
publish_services.append('slcCruiseCommand')
|
||||
pm = messaging.PubMaster(publish_services)
|
||||
native_plan_checks = list(NATIVE_PLAN_INPUTS)
|
||||
subscribed_services = list(NATIVE_PLAN_INPUTS)
|
||||
subscribed_services.append('deviceState')
|
||||
if conditional_host is not None:
|
||||
subscribed_services.append('starpilotRadarState')
|
||||
optional_slc = ['slcDashboardObservation', 'slcVisionObservation', 'mapdOut', 'starpilotCarState'] if slc_available else []
|
||||
if slc_available and feature_enabled(params, CP, 'aol', os.environ):
|
||||
optional_slc.append('aolAxisState')
|
||||
if force_stop_owner is not None and 'starpilotCarState' not in optional_slc:
|
||||
optional_slc = slc_lifecycle.source_services() if slc_available else []
|
||||
if force_stop_owner is not None:
|
||||
optional_slc.append('starpilotCarState')
|
||||
if force_stop_owner is not None and 'slcVisionObservation' not in optional_slc:
|
||||
optional_slc.append('slcVisionObservation')
|
||||
@@ -461,16 +399,14 @@ def starpilot_main():
|
||||
# Due saved-setting verification can block on Params. Complete it
|
||||
# before acquiring the model and fast-source batch, not after that
|
||||
# batch has already begun aging toward the CEM freshness deadline.
|
||||
slc_runtime, _ = slc_lifecycle.refresh(time.monotonic_ns(), pending_actions=pending_slc_actions,
|
||||
pending_cruise=pending_slc_cruise)
|
||||
slc_lifecycle.refresh(time.monotonic_ns(), pending_actions=pending_slc_actions, pending_cruise=pending_slc_cruise)
|
||||
sm.update()
|
||||
if sm.updated['modelV2']:
|
||||
# Only explicit replay uses the recorded model timeline. In a live host,
|
||||
# modelV2 may lag newer inputs in the same SubMaster update.
|
||||
now_ns = int(sm.logMonoTime['modelV2']) if 'REPLAY' in os.environ else time.monotonic_ns()
|
||||
if 'REPLAY' in os.environ:
|
||||
slc_runtime, _ = slc_lifecycle.refresh(now_ns, pending_actions=pending_slc_actions,
|
||||
pending_cruise=pending_slc_cruise)
|
||||
slc_lifecycle.refresh(now_ns, pending_actions=pending_slc_actions, pending_cruise=pending_slc_cruise)
|
||||
current_start_ns = (int(sm['deviceState'].startedMonoTime)
|
||||
if 'deviceState' in subscribed_services and sm.valid['deviceState'] and
|
||||
sm.alive['deviceState'] and 0 < sm.logMonoTime['deviceState'] <= now_ns else 0)
|
||||
@@ -481,10 +417,7 @@ def starpilot_main():
|
||||
planner_status = None
|
||||
drive_changed = current_start_ns > 0 and drive_start_ns > 0 and current_start_ns != drive_start_ns
|
||||
if slc_cruise_sock is not None:
|
||||
for _ in range(8):
|
||||
event = messaging.recv_one_or_none(slc_cruise_sock)
|
||||
if event is None:
|
||||
break
|
||||
for event in receive_slc_events(slc_cruise_sock):
|
||||
if str(event.slcCruiseEvent.kind) == 'wheelAction' and event.valid:
|
||||
pending_wheel.append(event)
|
||||
continue
|
||||
@@ -507,10 +440,7 @@ def starpilot_main():
|
||||
if traffic_owner is not None:
|
||||
traffic_owner.reset()
|
||||
if slc_action_sock is not None:
|
||||
for _ in range(8):
|
||||
event = messaging.recv_one_or_none(slc_action_sock)
|
||||
if event is None:
|
||||
break
|
||||
for event in receive_slc_events(slc_action_sock):
|
||||
queue_ui_action(event, pending_slc_actions, pending_conditional_ui, pending_modes)
|
||||
waiting_wheel = deque(maxlen=32)
|
||||
while pending_wheel:
|
||||
@@ -522,25 +452,13 @@ def starpilot_main():
|
||||
else:
|
||||
wheel_coast.receive(event, params, CP, sm, now_ns=now_ns)
|
||||
pending_wheel = waiting_wheel
|
||||
if slc_runtime is not None:
|
||||
if drive_changed or slc_runtime.state.reset_required or slc_runtime.ledger.reset_required:
|
||||
slc_runtime.reset()
|
||||
pending_slc_actions.clear()
|
||||
pending_slc_cruise.clear()
|
||||
max_event_age_ns = int(2e9 / SERVICE_LIST['modelV2'].frequency)
|
||||
while pending_slc_actions and now_ns - pending_slc_actions[0][0] > max_event_age_ns:
|
||||
pending_slc_actions.popleft()
|
||||
cruise_event = current_cruise_event(pending_slc_cruise, now_ns)
|
||||
request = (pending_slc_actions.popleft()[1] if cruise_event is None and pending_slc_actions and
|
||||
pending_slc_actions[0][0] <= now_ns else None)
|
||||
output = slc_runtime.step(sm, CP, now_ns=now_ns, request=request, cruise_event=cruise_event)
|
||||
planner_status = output.message
|
||||
if output.command is not None:
|
||||
pm.send('slcCruiseCommand', output.command)
|
||||
history_write = output.result.acceptance.history_write if output.result.acceptance is not None else None
|
||||
# A camera episode has no road-location identity for a later drive.
|
||||
if history_write is not None and history_write.candidate.source != 'vision':
|
||||
params.put("SLCQualifiedHistory", json.loads(slc_history.encode(history_write, time.time_ns())))
|
||||
# Curve observes the press before SLC sees the release. A queued Curve press can still own the tap.
|
||||
curve_event = current_cruise_event(pending_curve_events, now_ns)
|
||||
slc_output = slc_lifecycle.step(sm, now_ns, pending_slc_actions, pending_slc_cruise, drive_changed=drive_changed,
|
||||
wheel_override_blocked=wheel_override_blocked(force_stop_owner, curve_host, curve_backlog=bool(pending_curve_events)))
|
||||
cruise_ceiling = slc_output.result.ceiling if slc_output is not None else None
|
||||
if slc_output is not None:
|
||||
planner_status = slc_output.message
|
||||
needs_clock = conditional_host is not None or traffic_owner is not None or force_stop_owner is not None or mode_owner is not None
|
||||
clock_pair = paired_clocks_ns() if needs_clock and 'REPLAY' not in os.environ else None
|
||||
controller_traffic_toggle = bool(wheel_coast.traffic_toggles % 2)
|
||||
@@ -600,7 +518,6 @@ def starpilot_main():
|
||||
lead_approach_key = lead_approach_for_frame(approach_preferences, sm, CP, now_ns)
|
||||
if curve_preferences is not None and curve_host is not None:
|
||||
curve_preferences.refresh(curve_host, now_ns)
|
||||
curve_event = current_cruise_event(pending_curve_events, now_ns)
|
||||
conditional_handoff = (conditional_handoff_for_frame(conditional_host, sm, CP, clock_pair[0], current_start_ns)
|
||||
if clock_pair is not None else None)
|
||||
wheel_coast.conditional_stop = bool(conditional_handoff is not None and conditional_host is not None and
|
||||
@@ -617,6 +534,7 @@ def starpilot_main():
|
||||
drive_id=current_start_ns, follow_seconds=follow_seconds,
|
||||
traffic_mode=traffic_mode, takeoff_enabled=faster_lead_takeoff)
|
||||
curve_result = update_curve_frame(longitudinal_planner, sm, CP, now_ns, host=curve_host,
|
||||
cruise_ceiling=cruise_ceiling,
|
||||
profile_tuning=profile_tuning,
|
||||
traffic_mode=traffic_mode, lane_change_policy=lane_change_policy,
|
||||
global_braking_response=global_braking_response, selected_profiles=selected_profiles,
|
||||
@@ -643,6 +561,7 @@ def starpilot_main():
|
||||
proposal, snapshot = conditional_host.sample(
|
||||
sm, CP, longitudinal_planner, now_mono_ns=conditional_now_ns, now_boot_ns=conditional_boot_ns,
|
||||
sample_skew_ns=conditional_skew_ns, drive_id=current_start_ns,
|
||||
cruise_ceiling_mps=cruise_ceiling.speed_mps if cruise_ceiling is not None else None,
|
||||
owner_context=ConditionalOwnerContext(
|
||||
traffic_mode=traffic_observation(traffic_verdict, model_ns=int(sm.logMonoTime['modelV2']),
|
||||
owner_present=traffic_owner is not None),
|
||||
@@ -677,6 +596,8 @@ def starpilot_main():
|
||||
mode_owner.sample('switchback', sm, CP, now_ns=now_ns), session=mode_session,
|
||||
sequence=mode_sequence, now_ns=now_ns, source_car_control_ns=int(sm.logMonoTime['carControl']))
|
||||
if planner_status is not None:
|
||||
if planner_status.slcState.wheelOverrideSince and wheel_override_blocked(force_stop_owner, curve_host):
|
||||
planner_status.slcState.wheelOverrideSince = 0
|
||||
pm.send('slcState', planner_status)
|
||||
if current_start_ns > 0:
|
||||
drive_start_ns = current_start_ns
|
||||
|
||||
@@ -9,6 +9,7 @@ import unittest
|
||||
from openpilot.cereal import messaging
|
||||
from openpilot.common.params import Params
|
||||
from openpilot.starpilot import feature_runtime
|
||||
from openpilot.starpilot.speed_limits import planner_host as slc_host
|
||||
from openpilot.starpilot.speed_limits.runtime_settings import read_params as read_slc_settings
|
||||
from openpilot.selfdrive.controls import plannerd
|
||||
from openpilot.starpilot.conditional_mode.tests import test_status_consumer as transport
|
||||
@@ -98,7 +99,7 @@ class TestCemInputAcquisitionOrder(unittest.TestCase):
|
||||
def read(params):
|
||||
reads.append(True)
|
||||
return read_slc_settings(params)
|
||||
with patch.object(plannerd, 'read_slc_settings', side_effect=read, create=True), \
|
||||
with patch.object(slc_host, 'read_slc_settings', side_effect=read), \
|
||||
patch.object(feature_runtime, 'read_slc_settings', side_effect=read):
|
||||
runtime, changed = owner.refresh(MONO)
|
||||
self.assertTrue(changed)
|
||||
|
||||
@@ -23,21 +23,21 @@ from openpilot.starpilot.conditional_mode.projection import ConditionalOwnerCont
|
||||
from openpilot.starpilot.conditional_mode.runtime_settings import DOCUMENT_KEY
|
||||
from openpilot.starpilot.conditional_mode.status import settings_fingerprint
|
||||
from openpilot.starpilot.conditional_mode.tests.test_projection import BOOT, MONO, FakeSubMaster, serialized_scene
|
||||
from openpilot.starpilot.speed_limits.runtime import Action as SlcAction, CruiseEvent as SlcCruiseEvent
|
||||
|
||||
|
||||
class TestConditionalRuntimeLoop(unittest.TestCase):
|
||||
def test_slc_unit_change_keeps_receipts_for_the_current_command(self):
|
||||
from openpilot.selfdrive.car.cruise import slc_target_kph
|
||||
def test_slc_unit_change_preserves_cap_and_queued_driver_event(self):
|
||||
from openpilot.starpilot.speed_limits import offset_document as od
|
||||
from openpilot.starpilot.speed_limits.runtime import Action, CruiseEvent
|
||||
from openpilot.starpilot.speed_limits.tests.test_runtime_replay import RuntimeReplayTests
|
||||
fixture = RuntimeReplayTests()
|
||||
from openpilot.starpilot.speed_limits.tests import test_runtime_replay as replay
|
||||
fixture = replay.RuntimeReplayTests()
|
||||
fixture.setUp()
|
||||
sm, cp = fixture.sm, fixture.cp
|
||||
with tempfile.TemporaryDirectory() as directory:
|
||||
params = Params(directory)
|
||||
params.put_bool('SpeedLimitController', True, block=True)
|
||||
params.put_bool('SLCConfirmation', True, block=True)
|
||||
params.put('SLCConfirmation', 1, block=True)
|
||||
params.put('SLCPriority1', 'Dashboard', block=True)
|
||||
params.put('SLCOffsetSchedule', od.to_value(od.default_document(False)), block=True)
|
||||
owner = plannerd.SavedSlcLifecycle(params, cp, {})
|
||||
@@ -46,20 +46,19 @@ class TestConditionalRuntimeLoop(unittest.TestCase):
|
||||
sm.advance(2_900_000_000)
|
||||
pending = runtime.step(sm, cp, now_ns=2_900_000_000).message.slcState
|
||||
sm.advance(2_950_000_000)
|
||||
issued = runtime.step(sm, cp, now_ns=2_950_000_000,
|
||||
accepted = runtime.step(sm, cp, now_ns=2_950_000_000,
|
||||
request=Action(runtime.session_id, 1, pending.decisionId, pending.presentationId, 'accept'))
|
||||
command = issued.command.slcCruiseCommand
|
||||
selected = slc_target_kph(command.targetMps, False)
|
||||
receipts = deque([CruiseEvent(1, 2_990_000_000, 60 / 3.6, selected / 3.6, '', 'card-producer',
|
||||
2_990_000_000, 'commandApplied', runtime.session_id, command.decisionId, command.presentationId, command.commandId)])
|
||||
self.assertEqual(accepted.result.ceiling.speed_mps, 25.)
|
||||
events = deque([CruiseEvent(1, 2_990_000_000, 60/3.6, 65/3.6, 'accel', 'card-producer', 2_990_000_000)])
|
||||
params.put_bool('IsMetric', True, block=True)
|
||||
metric, _ = owner.refresh(3_000_000_000, pending_cruise=receipts)
|
||||
self.assertEqual(len(receipts), 1)
|
||||
sm['carState'].vCruise = sm['carState'].vCruiseCluster = selected
|
||||
metric, _ = owner.refresh(3_000_000_000, pending_cruise=events)
|
||||
self.assertIs(metric, runtime)
|
||||
self.assertEqual(len(events), 1)
|
||||
sm['carState'].vCruise = sm['carState'].vCruiseCluster = 65.
|
||||
sm.advance(3_000_000_000)
|
||||
applied = metric.step(sm, cp, now_ns=3_000_000_000, cruise_event=receipts.popleft())
|
||||
self.assertEqual(applied.message.slcState.commandStatus, 'applied')
|
||||
self.assertIsNone(applied.command)
|
||||
output = metric.step(sm, cp, now_ns=3_000_000_000, cruise_event=events.popleft())
|
||||
self.assertEqual(output.result.ceiling.speed_mps, 25.)
|
||||
self.assertEqual(sm['carState'].vCruise, 65.)
|
||||
|
||||
def test_slc_unit_changes_preserve_set_speed_and_driver_choices(self):
|
||||
from openpilot.common.constants import CV
|
||||
@@ -68,14 +67,14 @@ class TestConditionalRuntimeLoop(unittest.TestCase):
|
||||
from openpilot.starpilot.speed_limits.runtime import Action, CruiseEvent
|
||||
from openpilot.starpilot.speed_limits.tests.test_runtime_replay import RuntimeReplayTests
|
||||
now = 2_000_000_000
|
||||
for confirmation, choice in ((False, 'applied'), (True, 'applied'), (False, 'override'), (True, 'decline')):
|
||||
for confirmation, choice in ((False, 'applied'), (True, 'applied'), (False, 'ceiling'), (True, 'decline')):
|
||||
with self.subTest(confirmation=confirmation, choice=choice), tempfile.TemporaryDirectory() as directory:
|
||||
fixture = RuntimeReplayTests()
|
||||
fixture.setUp()
|
||||
sm, cp = fixture.sm, fixture.cp
|
||||
params = Params(directory)
|
||||
params.put_bool('SpeedLimitController', True, block=True)
|
||||
params.put_bool('SLCConfirmation', confirmation, block=True)
|
||||
params.put('SLCConfirmation', int(confirmation), block=True)
|
||||
params.put('SLCPriority1', 'Dashboard', block=True)
|
||||
params.put('SLCOffsetSchedule', od.to_value(od.default_document(False)), block=True)
|
||||
owner = plannerd.SavedSlcLifecycle(params, cp, {})
|
||||
@@ -84,14 +83,14 @@ class TestConditionalRuntimeLoop(unittest.TestCase):
|
||||
baseline = slc_target_kph(65 * CV.MPH_TO_MS, False)
|
||||
sm['carState'].vCruise = sm['carState'].vCruiseCluster = baseline
|
||||
aligned = original.step(sm, cp, now_ns=now)
|
||||
self.assertIsNone(aligned.command)
|
||||
if choice == 'override':
|
||||
self.assertTrue(aligned.message.slcState.hasAccepted)
|
||||
if choice == 'ceiling':
|
||||
selected = slc_target_kph(70 * CV.MPH_TO_MS, False)
|
||||
sm['carState'].vCruise = sm['carState'].vCruiseCluster = selected
|
||||
sm.advance(now + 50_000_000)
|
||||
chosen = original.step(sm, cp, now_ns=now + 50_000_000, cruise_event=CruiseEvent(
|
||||
1, now + 50_000_000, baseline / 3.6, selected / 3.6, 'accel', 'card-producer', now + 50_000_000))
|
||||
self.assertTrue(chosen.message.slcState.driverOverrideActive)
|
||||
self.assertFalse(chosen.message.slcState.driverOverrideActive)
|
||||
elif choice == 'decline':
|
||||
sm['slcDashboardObservation'].speedMps = 55 * CV.MPH_TO_MS
|
||||
sm.advance(now + 50_000_000)
|
||||
@@ -105,11 +104,10 @@ class TestConditionalRuntimeLoop(unittest.TestCase):
|
||||
self.assertTrue(changed)
|
||||
sm.advance(now + 1_000_000_000)
|
||||
output = metric.step(sm, cp, now_ns=now + 1_000_000_000)
|
||||
self.assertIsNone(output.command)
|
||||
self.assertFalse(output.message.slcState.hasPending)
|
||||
self.assertTrue(output.message.slcState.hasAccepted)
|
||||
self.assertEqual(sm['carState'].vCruise, selected)
|
||||
self.assertEqual(output.message.slcState.driverOverrideActive, choice == 'override')
|
||||
self.assertFalse(output.message.slcState.driverOverrideActive)
|
||||
if choice == 'decline':
|
||||
self.assertEqual(output.result.acceptance.basis, 'rejected')
|
||||
|
||||
@@ -122,13 +120,11 @@ class TestConditionalRuntimeLoop(unittest.TestCase):
|
||||
owner = plannerd.SavedSlcLifecycle(params, cp, {})
|
||||
imperial, _ = owner.refresh(MONO)
|
||||
self.assertFalse(imperial.is_metric)
|
||||
imperial.set_speed_after_ns = MONO + 1_050_000_000
|
||||
actions, cruise = deque([object()]), deque([object()])
|
||||
params.put_bool('IsMetric', True, block=True)
|
||||
metric, changed = owner.refresh(MONO + 1_000_000_000, pending_actions=actions, pending_cruise=cruise)
|
||||
self.assertTrue(changed)
|
||||
self.assertTrue(metric.is_metric)
|
||||
self.assertEqual(metric.set_speed_after_ns, MONO + 1_050_000_000)
|
||||
self.assertIs(metric, imperial)
|
||||
self.assertFalse(actions)
|
||||
self.assertEqual(len(cruise), 1)
|
||||
@@ -168,7 +164,7 @@ class TestConditionalRuntimeLoop(unittest.TestCase):
|
||||
# Status and transport are reserved independently of saved SLC/CEM
|
||||
# admission, so a later saved change can attach without restarting.
|
||||
self.assertEqual(publisher.call_args.args[0], ['longitudinalPlan', 'starpilotLongitudinalPlan',
|
||||
'driverAssistance', 'slcState', 'slcCruiseCommand'])
|
||||
'driverAssistance', 'slcState'])
|
||||
self.assertEqual(conditional_ctor.call_count, int(enabled))
|
||||
if enabled:
|
||||
self.assertIs(conditional_ctor.call_args.args[0], params)
|
||||
@@ -200,8 +196,8 @@ class TestConditionalRuntimeLoop(unittest.TestCase):
|
||||
self.assertFalse(vision.vision_control_qualified)
|
||||
self.assertEqual(vision.settings.selection.slots[0], Source.VISION)
|
||||
session = vision.session_id
|
||||
pending_actions = deque([(3_000_000_000, plannerd.SlcAction(session, 1, 1, 1, 'accept'))])
|
||||
pending_cruise = deque([plannerd.SlcCruiseEvent(1, 3_000_000_000, 20., 21., 'set', session_id=session)])
|
||||
pending_actions = deque([(3_000_000_000, SlcAction(session, 1, 1, 1, 'accept'))])
|
||||
pending_cruise = deque([SlcCruiseEvent(1, 3_000_000_000, 20., 21., 'set', session_id=session)])
|
||||
self.assertEqual(owner.refresh(3_050_000_000, pending_actions=pending_actions,
|
||||
pending_cruise=pending_cruise), (vision, False))
|
||||
self.assertEqual(len(pending_actions), 1)
|
||||
@@ -217,8 +213,8 @@ class TestConditionalRuntimeLoop(unittest.TestCase):
|
||||
self.assertNotEqual(vision.session_id, session) # Retired session was reset.
|
||||
params.put_bool('ShowSpeedLimits', False, block=True)
|
||||
params.put_bool('SpeedLimitController', False, block=True)
|
||||
pending_actions.append((4_050_000_000, plannerd.SlcAction(control.session_id, 1, 1, 1, 'accept')))
|
||||
pending_cruise.append(plannerd.SlcCruiseEvent(2, 4_050_000_000, 20., 21., 'set', session_id=control.session_id))
|
||||
pending_actions.append((4_050_000_000, SlcAction(control.session_id, 1, 1, 1, 'accept')))
|
||||
pending_cruise.append(SlcCruiseEvent(2, 4_050_000_000, 20., 21., 'set', session_id=control.session_id))
|
||||
self.assertEqual(owner.refresh(5_000_000_000, pending_actions=pending_actions,
|
||||
pending_cruise=pending_cruise), (None, True))
|
||||
self.assertFalse(pending_actions)
|
||||
@@ -251,7 +247,7 @@ class TestConditionalRuntimeLoop(unittest.TestCase):
|
||||
patch.object(messaging, 'SubMaster', return_value=sm) as subscriber, patch.object(messaging, 'sub_sock'):
|
||||
with self.assertRaises(EndLoop):
|
||||
plannerd.starpilot_main()
|
||||
self.assertIn('slcCruiseCommand', publisher.call_args.args[0])
|
||||
self.assertNotIn('slcCruiseCommand', publisher.call_args.args[0])
|
||||
for service in ('slcDashboardObservation', 'slcVisionObservation'):
|
||||
self.assertIn(service, subscriber.call_args.args[0])
|
||||
for check in ('ignore_alive', 'ignore_valid', 'ignore_avg_freq'):
|
||||
|
||||
@@ -156,7 +156,7 @@ class CurveLoopTests(unittest.TestCase):
|
||||
stamp = NOW + n * 50_000_000
|
||||
sm = bus(stamp)
|
||||
result = update_curve_frame(planner, sm, cp, stamp, host=host, cruise_ceiling=slc)
|
||||
self.assertIsNotNone(result.ceiling_mps)
|
||||
self.assertIsNone(result.ceiling_mps)
|
||||
self.assertFalse(planner.last_curve_ceiling_applied)
|
||||
self.assertFalse(host.runtime.was_controlling)
|
||||
self.assertEqual(host.runtime.curve.revision, 0)
|
||||
|
||||
@@ -45,326 +45,326 @@ const static double MAHA_THRESH_31 = 3.8414588206941227;
|
||||
* *
|
||||
* This file is part of 'ekf' *
|
||||
******************************************************************************/
|
||||
void err_fun(double *nom_x, double *delta_x, double *out_999653426340036453) {
|
||||
out_999653426340036453[0] = delta_x[0] + nom_x[0];
|
||||
out_999653426340036453[1] = delta_x[1] + nom_x[1];
|
||||
out_999653426340036453[2] = delta_x[2] + nom_x[2];
|
||||
out_999653426340036453[3] = delta_x[3] + nom_x[3];
|
||||
out_999653426340036453[4] = delta_x[4] + nom_x[4];
|
||||
out_999653426340036453[5] = delta_x[5] + nom_x[5];
|
||||
out_999653426340036453[6] = delta_x[6] + nom_x[6];
|
||||
out_999653426340036453[7] = delta_x[7] + nom_x[7];
|
||||
out_999653426340036453[8] = delta_x[8] + nom_x[8];
|
||||
void err_fun(double *nom_x, double *delta_x, double *out_90543357710769815) {
|
||||
out_90543357710769815[0] = delta_x[0] + nom_x[0];
|
||||
out_90543357710769815[1] = delta_x[1] + nom_x[1];
|
||||
out_90543357710769815[2] = delta_x[2] + nom_x[2];
|
||||
out_90543357710769815[3] = delta_x[3] + nom_x[3];
|
||||
out_90543357710769815[4] = delta_x[4] + nom_x[4];
|
||||
out_90543357710769815[5] = delta_x[5] + nom_x[5];
|
||||
out_90543357710769815[6] = delta_x[6] + nom_x[6];
|
||||
out_90543357710769815[7] = delta_x[7] + nom_x[7];
|
||||
out_90543357710769815[8] = delta_x[8] + nom_x[8];
|
||||
}
|
||||
void inv_err_fun(double *nom_x, double *true_x, double *out_4610969769563887824) {
|
||||
out_4610969769563887824[0] = -nom_x[0] + true_x[0];
|
||||
out_4610969769563887824[1] = -nom_x[1] + true_x[1];
|
||||
out_4610969769563887824[2] = -nom_x[2] + true_x[2];
|
||||
out_4610969769563887824[3] = -nom_x[3] + true_x[3];
|
||||
out_4610969769563887824[4] = -nom_x[4] + true_x[4];
|
||||
out_4610969769563887824[5] = -nom_x[5] + true_x[5];
|
||||
out_4610969769563887824[6] = -nom_x[6] + true_x[6];
|
||||
out_4610969769563887824[7] = -nom_x[7] + true_x[7];
|
||||
out_4610969769563887824[8] = -nom_x[8] + true_x[8];
|
||||
void inv_err_fun(double *nom_x, double *true_x, double *out_17966199294720670) {
|
||||
out_17966199294720670[0] = -nom_x[0] + true_x[0];
|
||||
out_17966199294720670[1] = -nom_x[1] + true_x[1];
|
||||
out_17966199294720670[2] = -nom_x[2] + true_x[2];
|
||||
out_17966199294720670[3] = -nom_x[3] + true_x[3];
|
||||
out_17966199294720670[4] = -nom_x[4] + true_x[4];
|
||||
out_17966199294720670[5] = -nom_x[5] + true_x[5];
|
||||
out_17966199294720670[6] = -nom_x[6] + true_x[6];
|
||||
out_17966199294720670[7] = -nom_x[7] + true_x[7];
|
||||
out_17966199294720670[8] = -nom_x[8] + true_x[8];
|
||||
}
|
||||
void H_mod_fun(double *state, double *out_1018390387391438144) {
|
||||
out_1018390387391438144[0] = 1.0;
|
||||
out_1018390387391438144[1] = 0.0;
|
||||
out_1018390387391438144[2] = 0.0;
|
||||
out_1018390387391438144[3] = 0.0;
|
||||
out_1018390387391438144[4] = 0.0;
|
||||
out_1018390387391438144[5] = 0.0;
|
||||
out_1018390387391438144[6] = 0.0;
|
||||
out_1018390387391438144[7] = 0.0;
|
||||
out_1018390387391438144[8] = 0.0;
|
||||
out_1018390387391438144[9] = 0.0;
|
||||
out_1018390387391438144[10] = 1.0;
|
||||
out_1018390387391438144[11] = 0.0;
|
||||
out_1018390387391438144[12] = 0.0;
|
||||
out_1018390387391438144[13] = 0.0;
|
||||
out_1018390387391438144[14] = 0.0;
|
||||
out_1018390387391438144[15] = 0.0;
|
||||
out_1018390387391438144[16] = 0.0;
|
||||
out_1018390387391438144[17] = 0.0;
|
||||
out_1018390387391438144[18] = 0.0;
|
||||
out_1018390387391438144[19] = 0.0;
|
||||
out_1018390387391438144[20] = 1.0;
|
||||
out_1018390387391438144[21] = 0.0;
|
||||
out_1018390387391438144[22] = 0.0;
|
||||
out_1018390387391438144[23] = 0.0;
|
||||
out_1018390387391438144[24] = 0.0;
|
||||
out_1018390387391438144[25] = 0.0;
|
||||
out_1018390387391438144[26] = 0.0;
|
||||
out_1018390387391438144[27] = 0.0;
|
||||
out_1018390387391438144[28] = 0.0;
|
||||
out_1018390387391438144[29] = 0.0;
|
||||
out_1018390387391438144[30] = 1.0;
|
||||
out_1018390387391438144[31] = 0.0;
|
||||
out_1018390387391438144[32] = 0.0;
|
||||
out_1018390387391438144[33] = 0.0;
|
||||
out_1018390387391438144[34] = 0.0;
|
||||
out_1018390387391438144[35] = 0.0;
|
||||
out_1018390387391438144[36] = 0.0;
|
||||
out_1018390387391438144[37] = 0.0;
|
||||
out_1018390387391438144[38] = 0.0;
|
||||
out_1018390387391438144[39] = 0.0;
|
||||
out_1018390387391438144[40] = 1.0;
|
||||
out_1018390387391438144[41] = 0.0;
|
||||
out_1018390387391438144[42] = 0.0;
|
||||
out_1018390387391438144[43] = 0.0;
|
||||
out_1018390387391438144[44] = 0.0;
|
||||
out_1018390387391438144[45] = 0.0;
|
||||
out_1018390387391438144[46] = 0.0;
|
||||
out_1018390387391438144[47] = 0.0;
|
||||
out_1018390387391438144[48] = 0.0;
|
||||
out_1018390387391438144[49] = 0.0;
|
||||
out_1018390387391438144[50] = 1.0;
|
||||
out_1018390387391438144[51] = 0.0;
|
||||
out_1018390387391438144[52] = 0.0;
|
||||
out_1018390387391438144[53] = 0.0;
|
||||
out_1018390387391438144[54] = 0.0;
|
||||
out_1018390387391438144[55] = 0.0;
|
||||
out_1018390387391438144[56] = 0.0;
|
||||
out_1018390387391438144[57] = 0.0;
|
||||
out_1018390387391438144[58] = 0.0;
|
||||
out_1018390387391438144[59] = 0.0;
|
||||
out_1018390387391438144[60] = 1.0;
|
||||
out_1018390387391438144[61] = 0.0;
|
||||
out_1018390387391438144[62] = 0.0;
|
||||
out_1018390387391438144[63] = 0.0;
|
||||
out_1018390387391438144[64] = 0.0;
|
||||
out_1018390387391438144[65] = 0.0;
|
||||
out_1018390387391438144[66] = 0.0;
|
||||
out_1018390387391438144[67] = 0.0;
|
||||
out_1018390387391438144[68] = 0.0;
|
||||
out_1018390387391438144[69] = 0.0;
|
||||
out_1018390387391438144[70] = 1.0;
|
||||
out_1018390387391438144[71] = 0.0;
|
||||
out_1018390387391438144[72] = 0.0;
|
||||
out_1018390387391438144[73] = 0.0;
|
||||
out_1018390387391438144[74] = 0.0;
|
||||
out_1018390387391438144[75] = 0.0;
|
||||
out_1018390387391438144[76] = 0.0;
|
||||
out_1018390387391438144[77] = 0.0;
|
||||
out_1018390387391438144[78] = 0.0;
|
||||
out_1018390387391438144[79] = 0.0;
|
||||
out_1018390387391438144[80] = 1.0;
|
||||
void H_mod_fun(double *state, double *out_844907361945072597) {
|
||||
out_844907361945072597[0] = 1.0;
|
||||
out_844907361945072597[1] = 0.0;
|
||||
out_844907361945072597[2] = 0.0;
|
||||
out_844907361945072597[3] = 0.0;
|
||||
out_844907361945072597[4] = 0.0;
|
||||
out_844907361945072597[5] = 0.0;
|
||||
out_844907361945072597[6] = 0.0;
|
||||
out_844907361945072597[7] = 0.0;
|
||||
out_844907361945072597[8] = 0.0;
|
||||
out_844907361945072597[9] = 0.0;
|
||||
out_844907361945072597[10] = 1.0;
|
||||
out_844907361945072597[11] = 0.0;
|
||||
out_844907361945072597[12] = 0.0;
|
||||
out_844907361945072597[13] = 0.0;
|
||||
out_844907361945072597[14] = 0.0;
|
||||
out_844907361945072597[15] = 0.0;
|
||||
out_844907361945072597[16] = 0.0;
|
||||
out_844907361945072597[17] = 0.0;
|
||||
out_844907361945072597[18] = 0.0;
|
||||
out_844907361945072597[19] = 0.0;
|
||||
out_844907361945072597[20] = 1.0;
|
||||
out_844907361945072597[21] = 0.0;
|
||||
out_844907361945072597[22] = 0.0;
|
||||
out_844907361945072597[23] = 0.0;
|
||||
out_844907361945072597[24] = 0.0;
|
||||
out_844907361945072597[25] = 0.0;
|
||||
out_844907361945072597[26] = 0.0;
|
||||
out_844907361945072597[27] = 0.0;
|
||||
out_844907361945072597[28] = 0.0;
|
||||
out_844907361945072597[29] = 0.0;
|
||||
out_844907361945072597[30] = 1.0;
|
||||
out_844907361945072597[31] = 0.0;
|
||||
out_844907361945072597[32] = 0.0;
|
||||
out_844907361945072597[33] = 0.0;
|
||||
out_844907361945072597[34] = 0.0;
|
||||
out_844907361945072597[35] = 0.0;
|
||||
out_844907361945072597[36] = 0.0;
|
||||
out_844907361945072597[37] = 0.0;
|
||||
out_844907361945072597[38] = 0.0;
|
||||
out_844907361945072597[39] = 0.0;
|
||||
out_844907361945072597[40] = 1.0;
|
||||
out_844907361945072597[41] = 0.0;
|
||||
out_844907361945072597[42] = 0.0;
|
||||
out_844907361945072597[43] = 0.0;
|
||||
out_844907361945072597[44] = 0.0;
|
||||
out_844907361945072597[45] = 0.0;
|
||||
out_844907361945072597[46] = 0.0;
|
||||
out_844907361945072597[47] = 0.0;
|
||||
out_844907361945072597[48] = 0.0;
|
||||
out_844907361945072597[49] = 0.0;
|
||||
out_844907361945072597[50] = 1.0;
|
||||
out_844907361945072597[51] = 0.0;
|
||||
out_844907361945072597[52] = 0.0;
|
||||
out_844907361945072597[53] = 0.0;
|
||||
out_844907361945072597[54] = 0.0;
|
||||
out_844907361945072597[55] = 0.0;
|
||||
out_844907361945072597[56] = 0.0;
|
||||
out_844907361945072597[57] = 0.0;
|
||||
out_844907361945072597[58] = 0.0;
|
||||
out_844907361945072597[59] = 0.0;
|
||||
out_844907361945072597[60] = 1.0;
|
||||
out_844907361945072597[61] = 0.0;
|
||||
out_844907361945072597[62] = 0.0;
|
||||
out_844907361945072597[63] = 0.0;
|
||||
out_844907361945072597[64] = 0.0;
|
||||
out_844907361945072597[65] = 0.0;
|
||||
out_844907361945072597[66] = 0.0;
|
||||
out_844907361945072597[67] = 0.0;
|
||||
out_844907361945072597[68] = 0.0;
|
||||
out_844907361945072597[69] = 0.0;
|
||||
out_844907361945072597[70] = 1.0;
|
||||
out_844907361945072597[71] = 0.0;
|
||||
out_844907361945072597[72] = 0.0;
|
||||
out_844907361945072597[73] = 0.0;
|
||||
out_844907361945072597[74] = 0.0;
|
||||
out_844907361945072597[75] = 0.0;
|
||||
out_844907361945072597[76] = 0.0;
|
||||
out_844907361945072597[77] = 0.0;
|
||||
out_844907361945072597[78] = 0.0;
|
||||
out_844907361945072597[79] = 0.0;
|
||||
out_844907361945072597[80] = 1.0;
|
||||
}
|
||||
void f_fun(double *state, double dt, double *out_8437871470129484601) {
|
||||
out_8437871470129484601[0] = state[0];
|
||||
out_8437871470129484601[1] = state[1];
|
||||
out_8437871470129484601[2] = state[2];
|
||||
out_8437871470129484601[3] = state[3];
|
||||
out_8437871470129484601[4] = state[4];
|
||||
out_8437871470129484601[5] = dt*((-state[4] + (-center_to_front*stiffness_front*state[0] + center_to_rear*stiffness_rear*state[0])/(mass*state[4]))*state[6] - 9.8100000000000005*state[8] + stiffness_front*(-state[2] - state[3] + state[7])*state[0]/(mass*state[1]) + (-stiffness_front*state[0] - stiffness_rear*state[0])*state[5]/(mass*state[4])) + state[5];
|
||||
out_8437871470129484601[6] = dt*(center_to_front*stiffness_front*(-state[2] - state[3] + state[7])*state[0]/(rotational_inertia*state[1]) + (-center_to_front*stiffness_front*state[0] + center_to_rear*stiffness_rear*state[0])*state[5]/(rotational_inertia*state[4]) + (-pow(center_to_front, 2)*stiffness_front*state[0] - pow(center_to_rear, 2)*stiffness_rear*state[0])*state[6]/(rotational_inertia*state[4])) + state[6];
|
||||
out_8437871470129484601[7] = state[7];
|
||||
out_8437871470129484601[8] = state[8];
|
||||
void f_fun(double *state, double dt, double *out_7814100581081982154) {
|
||||
out_7814100581081982154[0] = state[0];
|
||||
out_7814100581081982154[1] = state[1];
|
||||
out_7814100581081982154[2] = state[2];
|
||||
out_7814100581081982154[3] = state[3];
|
||||
out_7814100581081982154[4] = state[4];
|
||||
out_7814100581081982154[5] = dt*((-state[4] + (-center_to_front*stiffness_front*state[0] + center_to_rear*stiffness_rear*state[0])/(mass*state[4]))*state[6] - 9.8100000000000005*state[8] + stiffness_front*(-state[2] - state[3] + state[7])*state[0]/(mass*state[1]) + (-stiffness_front*state[0] - stiffness_rear*state[0])*state[5]/(mass*state[4])) + state[5];
|
||||
out_7814100581081982154[6] = dt*(center_to_front*stiffness_front*(-state[2] - state[3] + state[7])*state[0]/(rotational_inertia*state[1]) + (-center_to_front*stiffness_front*state[0] + center_to_rear*stiffness_rear*state[0])*state[5]/(rotational_inertia*state[4]) + (-pow(center_to_front, 2)*stiffness_front*state[0] - pow(center_to_rear, 2)*stiffness_rear*state[0])*state[6]/(rotational_inertia*state[4])) + state[6];
|
||||
out_7814100581081982154[7] = state[7];
|
||||
out_7814100581081982154[8] = state[8];
|
||||
}
|
||||
void F_fun(double *state, double dt, double *out_7222922179546997313) {
|
||||
out_7222922179546997313[0] = 1;
|
||||
out_7222922179546997313[1] = 0;
|
||||
out_7222922179546997313[2] = 0;
|
||||
out_7222922179546997313[3] = 0;
|
||||
out_7222922179546997313[4] = 0;
|
||||
out_7222922179546997313[5] = 0;
|
||||
out_7222922179546997313[6] = 0;
|
||||
out_7222922179546997313[7] = 0;
|
||||
out_7222922179546997313[8] = 0;
|
||||
out_7222922179546997313[9] = 0;
|
||||
out_7222922179546997313[10] = 1;
|
||||
out_7222922179546997313[11] = 0;
|
||||
out_7222922179546997313[12] = 0;
|
||||
out_7222922179546997313[13] = 0;
|
||||
out_7222922179546997313[14] = 0;
|
||||
out_7222922179546997313[15] = 0;
|
||||
out_7222922179546997313[16] = 0;
|
||||
out_7222922179546997313[17] = 0;
|
||||
out_7222922179546997313[18] = 0;
|
||||
out_7222922179546997313[19] = 0;
|
||||
out_7222922179546997313[20] = 1;
|
||||
out_7222922179546997313[21] = 0;
|
||||
out_7222922179546997313[22] = 0;
|
||||
out_7222922179546997313[23] = 0;
|
||||
out_7222922179546997313[24] = 0;
|
||||
out_7222922179546997313[25] = 0;
|
||||
out_7222922179546997313[26] = 0;
|
||||
out_7222922179546997313[27] = 0;
|
||||
out_7222922179546997313[28] = 0;
|
||||
out_7222922179546997313[29] = 0;
|
||||
out_7222922179546997313[30] = 1;
|
||||
out_7222922179546997313[31] = 0;
|
||||
out_7222922179546997313[32] = 0;
|
||||
out_7222922179546997313[33] = 0;
|
||||
out_7222922179546997313[34] = 0;
|
||||
out_7222922179546997313[35] = 0;
|
||||
out_7222922179546997313[36] = 0;
|
||||
out_7222922179546997313[37] = 0;
|
||||
out_7222922179546997313[38] = 0;
|
||||
out_7222922179546997313[39] = 0;
|
||||
out_7222922179546997313[40] = 1;
|
||||
out_7222922179546997313[41] = 0;
|
||||
out_7222922179546997313[42] = 0;
|
||||
out_7222922179546997313[43] = 0;
|
||||
out_7222922179546997313[44] = 0;
|
||||
out_7222922179546997313[45] = dt*(stiffness_front*(-state[2] - state[3] + state[7])/(mass*state[1]) + (-stiffness_front - stiffness_rear)*state[5]/(mass*state[4]) + (-center_to_front*stiffness_front + center_to_rear*stiffness_rear)*state[6]/(mass*state[4]));
|
||||
out_7222922179546997313[46] = -dt*stiffness_front*(-state[2] - state[3] + state[7])*state[0]/(mass*pow(state[1], 2));
|
||||
out_7222922179546997313[47] = -dt*stiffness_front*state[0]/(mass*state[1]);
|
||||
out_7222922179546997313[48] = -dt*stiffness_front*state[0]/(mass*state[1]);
|
||||
out_7222922179546997313[49] = dt*((-1 - (-center_to_front*stiffness_front*state[0] + center_to_rear*stiffness_rear*state[0])/(mass*pow(state[4], 2)))*state[6] - (-stiffness_front*state[0] - stiffness_rear*state[0])*state[5]/(mass*pow(state[4], 2)));
|
||||
out_7222922179546997313[50] = dt*(-stiffness_front*state[0] - stiffness_rear*state[0])/(mass*state[4]) + 1;
|
||||
out_7222922179546997313[51] = dt*(-state[4] + (-center_to_front*stiffness_front*state[0] + center_to_rear*stiffness_rear*state[0])/(mass*state[4]));
|
||||
out_7222922179546997313[52] = dt*stiffness_front*state[0]/(mass*state[1]);
|
||||
out_7222922179546997313[53] = -9.8100000000000005*dt;
|
||||
out_7222922179546997313[54] = dt*(center_to_front*stiffness_front*(-state[2] - state[3] + state[7])/(rotational_inertia*state[1]) + (-center_to_front*stiffness_front + center_to_rear*stiffness_rear)*state[5]/(rotational_inertia*state[4]) + (-pow(center_to_front, 2)*stiffness_front - pow(center_to_rear, 2)*stiffness_rear)*state[6]/(rotational_inertia*state[4]));
|
||||
out_7222922179546997313[55] = -center_to_front*dt*stiffness_front*(-state[2] - state[3] + state[7])*state[0]/(rotational_inertia*pow(state[1], 2));
|
||||
out_7222922179546997313[56] = -center_to_front*dt*stiffness_front*state[0]/(rotational_inertia*state[1]);
|
||||
out_7222922179546997313[57] = -center_to_front*dt*stiffness_front*state[0]/(rotational_inertia*state[1]);
|
||||
out_7222922179546997313[58] = dt*(-(-center_to_front*stiffness_front*state[0] + center_to_rear*stiffness_rear*state[0])*state[5]/(rotational_inertia*pow(state[4], 2)) - (-pow(center_to_front, 2)*stiffness_front*state[0] - pow(center_to_rear, 2)*stiffness_rear*state[0])*state[6]/(rotational_inertia*pow(state[4], 2)));
|
||||
out_7222922179546997313[59] = dt*(-center_to_front*stiffness_front*state[0] + center_to_rear*stiffness_rear*state[0])/(rotational_inertia*state[4]);
|
||||
out_7222922179546997313[60] = dt*(-pow(center_to_front, 2)*stiffness_front*state[0] - pow(center_to_rear, 2)*stiffness_rear*state[0])/(rotational_inertia*state[4]) + 1;
|
||||
out_7222922179546997313[61] = center_to_front*dt*stiffness_front*state[0]/(rotational_inertia*state[1]);
|
||||
out_7222922179546997313[62] = 0;
|
||||
out_7222922179546997313[63] = 0;
|
||||
out_7222922179546997313[64] = 0;
|
||||
out_7222922179546997313[65] = 0;
|
||||
out_7222922179546997313[66] = 0;
|
||||
out_7222922179546997313[67] = 0;
|
||||
out_7222922179546997313[68] = 0;
|
||||
out_7222922179546997313[69] = 0;
|
||||
out_7222922179546997313[70] = 1;
|
||||
out_7222922179546997313[71] = 0;
|
||||
out_7222922179546997313[72] = 0;
|
||||
out_7222922179546997313[73] = 0;
|
||||
out_7222922179546997313[74] = 0;
|
||||
out_7222922179546997313[75] = 0;
|
||||
out_7222922179546997313[76] = 0;
|
||||
out_7222922179546997313[77] = 0;
|
||||
out_7222922179546997313[78] = 0;
|
||||
out_7222922179546997313[79] = 0;
|
||||
out_7222922179546997313[80] = 1;
|
||||
void F_fun(double *state, double dt, double *out_8005261171107713583) {
|
||||
out_8005261171107713583[0] = 1;
|
||||
out_8005261171107713583[1] = 0;
|
||||
out_8005261171107713583[2] = 0;
|
||||
out_8005261171107713583[3] = 0;
|
||||
out_8005261171107713583[4] = 0;
|
||||
out_8005261171107713583[5] = 0;
|
||||
out_8005261171107713583[6] = 0;
|
||||
out_8005261171107713583[7] = 0;
|
||||
out_8005261171107713583[8] = 0;
|
||||
out_8005261171107713583[9] = 0;
|
||||
out_8005261171107713583[10] = 1;
|
||||
out_8005261171107713583[11] = 0;
|
||||
out_8005261171107713583[12] = 0;
|
||||
out_8005261171107713583[13] = 0;
|
||||
out_8005261171107713583[14] = 0;
|
||||
out_8005261171107713583[15] = 0;
|
||||
out_8005261171107713583[16] = 0;
|
||||
out_8005261171107713583[17] = 0;
|
||||
out_8005261171107713583[18] = 0;
|
||||
out_8005261171107713583[19] = 0;
|
||||
out_8005261171107713583[20] = 1;
|
||||
out_8005261171107713583[21] = 0;
|
||||
out_8005261171107713583[22] = 0;
|
||||
out_8005261171107713583[23] = 0;
|
||||
out_8005261171107713583[24] = 0;
|
||||
out_8005261171107713583[25] = 0;
|
||||
out_8005261171107713583[26] = 0;
|
||||
out_8005261171107713583[27] = 0;
|
||||
out_8005261171107713583[28] = 0;
|
||||
out_8005261171107713583[29] = 0;
|
||||
out_8005261171107713583[30] = 1;
|
||||
out_8005261171107713583[31] = 0;
|
||||
out_8005261171107713583[32] = 0;
|
||||
out_8005261171107713583[33] = 0;
|
||||
out_8005261171107713583[34] = 0;
|
||||
out_8005261171107713583[35] = 0;
|
||||
out_8005261171107713583[36] = 0;
|
||||
out_8005261171107713583[37] = 0;
|
||||
out_8005261171107713583[38] = 0;
|
||||
out_8005261171107713583[39] = 0;
|
||||
out_8005261171107713583[40] = 1;
|
||||
out_8005261171107713583[41] = 0;
|
||||
out_8005261171107713583[42] = 0;
|
||||
out_8005261171107713583[43] = 0;
|
||||
out_8005261171107713583[44] = 0;
|
||||
out_8005261171107713583[45] = dt*(stiffness_front*(-state[2] - state[3] + state[7])/(mass*state[1]) + (-stiffness_front - stiffness_rear)*state[5]/(mass*state[4]) + (-center_to_front*stiffness_front + center_to_rear*stiffness_rear)*state[6]/(mass*state[4]));
|
||||
out_8005261171107713583[46] = -dt*stiffness_front*(-state[2] - state[3] + state[7])*state[0]/(mass*pow(state[1], 2));
|
||||
out_8005261171107713583[47] = -dt*stiffness_front*state[0]/(mass*state[1]);
|
||||
out_8005261171107713583[48] = -dt*stiffness_front*state[0]/(mass*state[1]);
|
||||
out_8005261171107713583[49] = dt*((-1 - (-center_to_front*stiffness_front*state[0] + center_to_rear*stiffness_rear*state[0])/(mass*pow(state[4], 2)))*state[6] - (-stiffness_front*state[0] - stiffness_rear*state[0])*state[5]/(mass*pow(state[4], 2)));
|
||||
out_8005261171107713583[50] = dt*(-stiffness_front*state[0] - stiffness_rear*state[0])/(mass*state[4]) + 1;
|
||||
out_8005261171107713583[51] = dt*(-state[4] + (-center_to_front*stiffness_front*state[0] + center_to_rear*stiffness_rear*state[0])/(mass*state[4]));
|
||||
out_8005261171107713583[52] = dt*stiffness_front*state[0]/(mass*state[1]);
|
||||
out_8005261171107713583[53] = -9.8100000000000005*dt;
|
||||
out_8005261171107713583[54] = dt*(center_to_front*stiffness_front*(-state[2] - state[3] + state[7])/(rotational_inertia*state[1]) + (-center_to_front*stiffness_front + center_to_rear*stiffness_rear)*state[5]/(rotational_inertia*state[4]) + (-pow(center_to_front, 2)*stiffness_front - pow(center_to_rear, 2)*stiffness_rear)*state[6]/(rotational_inertia*state[4]));
|
||||
out_8005261171107713583[55] = -center_to_front*dt*stiffness_front*(-state[2] - state[3] + state[7])*state[0]/(rotational_inertia*pow(state[1], 2));
|
||||
out_8005261171107713583[56] = -center_to_front*dt*stiffness_front*state[0]/(rotational_inertia*state[1]);
|
||||
out_8005261171107713583[57] = -center_to_front*dt*stiffness_front*state[0]/(rotational_inertia*state[1]);
|
||||
out_8005261171107713583[58] = dt*(-(-center_to_front*stiffness_front*state[0] + center_to_rear*stiffness_rear*state[0])*state[5]/(rotational_inertia*pow(state[4], 2)) - (-pow(center_to_front, 2)*stiffness_front*state[0] - pow(center_to_rear, 2)*stiffness_rear*state[0])*state[6]/(rotational_inertia*pow(state[4], 2)));
|
||||
out_8005261171107713583[59] = dt*(-center_to_front*stiffness_front*state[0] + center_to_rear*stiffness_rear*state[0])/(rotational_inertia*state[4]);
|
||||
out_8005261171107713583[60] = dt*(-pow(center_to_front, 2)*stiffness_front*state[0] - pow(center_to_rear, 2)*stiffness_rear*state[0])/(rotational_inertia*state[4]) + 1;
|
||||
out_8005261171107713583[61] = center_to_front*dt*stiffness_front*state[0]/(rotational_inertia*state[1]);
|
||||
out_8005261171107713583[62] = 0;
|
||||
out_8005261171107713583[63] = 0;
|
||||
out_8005261171107713583[64] = 0;
|
||||
out_8005261171107713583[65] = 0;
|
||||
out_8005261171107713583[66] = 0;
|
||||
out_8005261171107713583[67] = 0;
|
||||
out_8005261171107713583[68] = 0;
|
||||
out_8005261171107713583[69] = 0;
|
||||
out_8005261171107713583[70] = 1;
|
||||
out_8005261171107713583[71] = 0;
|
||||
out_8005261171107713583[72] = 0;
|
||||
out_8005261171107713583[73] = 0;
|
||||
out_8005261171107713583[74] = 0;
|
||||
out_8005261171107713583[75] = 0;
|
||||
out_8005261171107713583[76] = 0;
|
||||
out_8005261171107713583[77] = 0;
|
||||
out_8005261171107713583[78] = 0;
|
||||
out_8005261171107713583[79] = 0;
|
||||
out_8005261171107713583[80] = 1;
|
||||
}
|
||||
void h_25(double *state, double *unused, double *out_5845994727189855200) {
|
||||
out_5845994727189855200[0] = state[6];
|
||||
void h_25(double *state, double *unused, double *out_3451529468624779566) {
|
||||
out_3451529468624779566[0] = state[6];
|
||||
}
|
||||
void H_25(double *state, double *unused, double *out_1618166537623988050) {
|
||||
out_1618166537623988050[0] = 0;
|
||||
out_1618166537623988050[1] = 0;
|
||||
out_1618166537623988050[2] = 0;
|
||||
out_1618166537623988050[3] = 0;
|
||||
out_1618166537623988050[4] = 0;
|
||||
out_1618166537623988050[5] = 0;
|
||||
out_1618166537623988050[6] = 1;
|
||||
out_1618166537623988050[7] = 0;
|
||||
out_1618166537623988050[8] = 0;
|
||||
void H_25(double *state, double *unused, double *out_339572919744775885) {
|
||||
out_339572919744775885[0] = 0;
|
||||
out_339572919744775885[1] = 0;
|
||||
out_339572919744775885[2] = 0;
|
||||
out_339572919744775885[3] = 0;
|
||||
out_339572919744775885[4] = 0;
|
||||
out_339572919744775885[5] = 0;
|
||||
out_339572919744775885[6] = 1;
|
||||
out_339572919744775885[7] = 0;
|
||||
out_339572919744775885[8] = 0;
|
||||
}
|
||||
void h_24(double *state, double *unused, double *out_2733857150290724482) {
|
||||
out_2733857150290724482[0] = state[4];
|
||||
out_2733857150290724482[1] = state[5];
|
||||
void h_24(double *state, double *unused, double *out_3799606392279303524) {
|
||||
out_3799606392279303524[0] = state[4];
|
||||
out_3799606392279303524[1] = state[5];
|
||||
}
|
||||
void H_24(double *state, double *unused, double *out_3790816136629487616) {
|
||||
out_3790816136629487616[0] = 0;
|
||||
out_3790816136629487616[1] = 0;
|
||||
out_3790816136629487616[2] = 0;
|
||||
out_3790816136629487616[3] = 0;
|
||||
out_3790816136629487616[4] = 1;
|
||||
out_3790816136629487616[5] = 0;
|
||||
out_3790816136629487616[6] = 0;
|
||||
out_3790816136629487616[7] = 0;
|
||||
out_3790816136629487616[8] = 0;
|
||||
out_3790816136629487616[9] = 0;
|
||||
out_3790816136629487616[10] = 0;
|
||||
out_3790816136629487616[11] = 0;
|
||||
out_3790816136629487616[12] = 0;
|
||||
out_3790816136629487616[13] = 0;
|
||||
out_3790816136629487616[14] = 1;
|
||||
out_3790816136629487616[15] = 0;
|
||||
out_3790816136629487616[16] = 0;
|
||||
out_3790816136629487616[17] = 0;
|
||||
void H_24(double *state, double *unused, double *out_3383890129749124077) {
|
||||
out_3383890129749124077[0] = 0;
|
||||
out_3383890129749124077[1] = 0;
|
||||
out_3383890129749124077[2] = 0;
|
||||
out_3383890129749124077[3] = 0;
|
||||
out_3383890129749124077[4] = 1;
|
||||
out_3383890129749124077[5] = 0;
|
||||
out_3383890129749124077[6] = 0;
|
||||
out_3383890129749124077[7] = 0;
|
||||
out_3383890129749124077[8] = 0;
|
||||
out_3383890129749124077[9] = 0;
|
||||
out_3383890129749124077[10] = 0;
|
||||
out_3383890129749124077[11] = 0;
|
||||
out_3383890129749124077[12] = 0;
|
||||
out_3383890129749124077[13] = 0;
|
||||
out_3383890129749124077[14] = 1;
|
||||
out_3383890129749124077[15] = 0;
|
||||
out_3383890129749124077[16] = 0;
|
||||
out_3383890129749124077[17] = 0;
|
||||
}
|
||||
void h_30(double *state, double *unused, double *out_2208545198935832850) {
|
||||
out_2208545198935832850[0] = state[4];
|
||||
void h_30(double *state, double *unused, double *out_2912064509677036295) {
|
||||
out_2912064509677036295[0] = state[4];
|
||||
}
|
||||
void H_30(double *state, double *unused, double *out_6145862867751596248) {
|
||||
out_6145862867751596248[0] = 0;
|
||||
out_6145862867751596248[1] = 0;
|
||||
out_6145862867751596248[2] = 0;
|
||||
out_6145862867751596248[3] = 0;
|
||||
out_6145862867751596248[4] = 1;
|
||||
out_6145862867751596248[5] = 0;
|
||||
out_6145862867751596248[6] = 0;
|
||||
out_6145862867751596248[7] = 0;
|
||||
out_6145862867751596248[8] = 0;
|
||||
void H_30(double *state, double *unused, double *out_6577117421746840870) {
|
||||
out_6577117421746840870[0] = 0;
|
||||
out_6577117421746840870[1] = 0;
|
||||
out_6577117421746840870[2] = 0;
|
||||
out_6577117421746840870[3] = 0;
|
||||
out_6577117421746840870[4] = 1;
|
||||
out_6577117421746840870[5] = 0;
|
||||
out_6577117421746840870[6] = 0;
|
||||
out_6577117421746840870[7] = 0;
|
||||
out_6577117421746840870[8] = 0;
|
||||
}
|
||||
void h_26(double *state, double *unused, double *out_604006904334020533) {
|
||||
out_604006904334020533[0] = state[7];
|
||||
void h_26(double *state, double *unused, double *out_2089608757983809350) {
|
||||
out_2089608757983809350[0] = state[7];
|
||||
}
|
||||
void H_26(double *state, double *unused, double *out_5359669856498044274) {
|
||||
out_5359669856498044274[0] = 0;
|
||||
out_5359669856498044274[1] = 0;
|
||||
out_5359669856498044274[2] = 0;
|
||||
out_5359669856498044274[3] = 0;
|
||||
out_5359669856498044274[4] = 0;
|
||||
out_5359669856498044274[5] = 0;
|
||||
out_5359669856498044274[6] = 0;
|
||||
out_5359669856498044274[7] = 1;
|
||||
out_5359669856498044274[8] = 0;
|
||||
void H_26(double *state, double *unused, double *out_4081076238618832109) {
|
||||
out_4081076238618832109[0] = 0;
|
||||
out_4081076238618832109[1] = 0;
|
||||
out_4081076238618832109[2] = 0;
|
||||
out_4081076238618832109[3] = 0;
|
||||
out_4081076238618832109[4] = 0;
|
||||
out_4081076238618832109[5] = 0;
|
||||
out_4081076238618832109[6] = 0;
|
||||
out_4081076238618832109[7] = 1;
|
||||
out_4081076238618832109[8] = 0;
|
||||
}
|
||||
void h_27(double *state, double *unused, double *out_844321459529009555) {
|
||||
out_844321459529009555[0] = state[3];
|
||||
void h_27(double *state, double *unused, double *out_7992451901291706271) {
|
||||
out_7992451901291706271[0] = state[3];
|
||||
}
|
||||
void H_27(double *state, double *unused, double *out_8320626179552021159) {
|
||||
out_8320626179552021159[0] = 0;
|
||||
out_8320626179552021159[1] = 0;
|
||||
out_8320626179552021159[2] = 0;
|
||||
out_8320626179552021159[3] = 1;
|
||||
out_8320626179552021159[4] = 0;
|
||||
out_8320626179552021159[5] = 0;
|
||||
out_8320626179552021159[6] = 0;
|
||||
out_8320626179552021159[7] = 0;
|
||||
out_8320626179552021159[8] = 0;
|
||||
void H_27(double *state, double *unused, double *out_2643675178688440866) {
|
||||
out_2643675178688440866[0] = 0;
|
||||
out_2643675178688440866[1] = 0;
|
||||
out_2643675178688440866[2] = 0;
|
||||
out_2643675178688440866[3] = 1;
|
||||
out_2643675178688440866[4] = 0;
|
||||
out_2643675178688440866[5] = 0;
|
||||
out_2643675178688440866[6] = 0;
|
||||
out_2643675178688440866[7] = 0;
|
||||
out_2643675178688440866[8] = 0;
|
||||
}
|
||||
void h_29(double *state, double *unused, double *out_2793128068725012795) {
|
||||
out_2793128068725012795[0] = state[1];
|
||||
void h_29(double *state, double *unused, double *out_99512406407182912) {
|
||||
out_99512406407182912[0] = state[1];
|
||||
}
|
||||
void H_29(double *state, double *unused, double *out_5635631523437204064) {
|
||||
out_5635631523437204064[0] = 0;
|
||||
out_5635631523437204064[1] = 1;
|
||||
out_5635631523437204064[2] = 0;
|
||||
out_5635631523437204064[3] = 0;
|
||||
out_5635631523437204064[4] = 0;
|
||||
out_5635631523437204064[5] = 0;
|
||||
out_5635631523437204064[6] = 0;
|
||||
out_5635631523437204064[7] = 0;
|
||||
out_5635631523437204064[8] = 0;
|
||||
void H_29(double *state, double *unused, double *out_41319477426376229) {
|
||||
out_41319477426376229[0] = 0;
|
||||
out_41319477426376229[1] = 1;
|
||||
out_41319477426376229[2] = 0;
|
||||
out_41319477426376229[3] = 0;
|
||||
out_41319477426376229[4] = 0;
|
||||
out_41319477426376229[5] = 0;
|
||||
out_41319477426376229[6] = 0;
|
||||
out_41319477426376229[7] = 0;
|
||||
out_41319477426376229[8] = 0;
|
||||
}
|
||||
void h_28(double *state, double *unused, double *out_4484828697352558873) {
|
||||
out_4484828697352558873[0] = state[0];
|
||||
void h_28(double *state, double *unused, double *out_4705680922922981175) {
|
||||
out_4705680922922981175[0] = state[0];
|
||||
}
|
||||
void H_28(double *state, double *unused, double *out_3672001251871877813) {
|
||||
out_3672001251871877813[0] = 1;
|
||||
out_3672001251871877813[1] = 0;
|
||||
out_3672001251871877813[2] = 0;
|
||||
out_3672001251871877813[3] = 0;
|
||||
out_3672001251871877813[4] = 0;
|
||||
out_3672001251871877813[5] = 0;
|
||||
out_3672001251871877813[6] = 0;
|
||||
out_3672001251871877813[7] = 0;
|
||||
out_3672001251871877813[8] = 0;
|
||||
void H_28(double *state, double *unused, double *out_2004949748991702480) {
|
||||
out_2004949748991702480[0] = 1;
|
||||
out_2004949748991702480[1] = 0;
|
||||
out_2004949748991702480[2] = 0;
|
||||
out_2004949748991702480[3] = 0;
|
||||
out_2004949748991702480[4] = 0;
|
||||
out_2004949748991702480[5] = 0;
|
||||
out_2004949748991702480[6] = 0;
|
||||
out_2004949748991702480[7] = 0;
|
||||
out_2004949748991702480[8] = 0;
|
||||
}
|
||||
void h_31(double *state, double *unused, double *out_2046944767694668159) {
|
||||
out_2046944767694668159[0] = state[8];
|
||||
void h_31(double *state, double *unused, double *out_1000579080861491944) {
|
||||
out_1000579080861491944[0] = state[8];
|
||||
}
|
||||
void H_31(double *state, double *unused, double *out_5985877958731395750) {
|
||||
out_5985877958731395750[0] = 0;
|
||||
out_5985877958731395750[1] = 0;
|
||||
out_5985877958731395750[2] = 0;
|
||||
out_5985877958731395750[3] = 0;
|
||||
out_5985877958731395750[4] = 0;
|
||||
out_5985877958731395750[5] = 0;
|
||||
out_5985877958731395750[6] = 0;
|
||||
out_5985877958731395750[7] = 0;
|
||||
out_5985877958731395750[8] = 1;
|
||||
void H_31(double *state, double *unused, double *out_308926957867815457) {
|
||||
out_308926957867815457[0] = 0;
|
||||
out_308926957867815457[1] = 0;
|
||||
out_308926957867815457[2] = 0;
|
||||
out_308926957867815457[3] = 0;
|
||||
out_308926957867815457[4] = 0;
|
||||
out_308926957867815457[5] = 0;
|
||||
out_308926957867815457[6] = 0;
|
||||
out_308926957867815457[7] = 0;
|
||||
out_308926957867815457[8] = 1;
|
||||
}
|
||||
#include <eigen3/Eigen/Dense>
|
||||
#include <iostream>
|
||||
@@ -518,68 +518,68 @@ void car_update_28(double *in_x, double *in_P, double *in_z, double *in_R, doubl
|
||||
void car_update_31(double *in_x, double *in_P, double *in_z, double *in_R, double *in_ea) {
|
||||
update<1, 3, 0>(in_x, in_P, h_31, H_31, NULL, in_z, in_R, in_ea, MAHA_THRESH_31);
|
||||
}
|
||||
void car_err_fun(double *nom_x, double *delta_x, double *out_999653426340036453) {
|
||||
err_fun(nom_x, delta_x, out_999653426340036453);
|
||||
void car_err_fun(double *nom_x, double *delta_x, double *out_90543357710769815) {
|
||||
err_fun(nom_x, delta_x, out_90543357710769815);
|
||||
}
|
||||
void car_inv_err_fun(double *nom_x, double *true_x, double *out_4610969769563887824) {
|
||||
inv_err_fun(nom_x, true_x, out_4610969769563887824);
|
||||
void car_inv_err_fun(double *nom_x, double *true_x, double *out_17966199294720670) {
|
||||
inv_err_fun(nom_x, true_x, out_17966199294720670);
|
||||
}
|
||||
void car_H_mod_fun(double *state, double *out_1018390387391438144) {
|
||||
H_mod_fun(state, out_1018390387391438144);
|
||||
void car_H_mod_fun(double *state, double *out_844907361945072597) {
|
||||
H_mod_fun(state, out_844907361945072597);
|
||||
}
|
||||
void car_f_fun(double *state, double dt, double *out_8437871470129484601) {
|
||||
f_fun(state, dt, out_8437871470129484601);
|
||||
void car_f_fun(double *state, double dt, double *out_7814100581081982154) {
|
||||
f_fun(state, dt, out_7814100581081982154);
|
||||
}
|
||||
void car_F_fun(double *state, double dt, double *out_7222922179546997313) {
|
||||
F_fun(state, dt, out_7222922179546997313);
|
||||
void car_F_fun(double *state, double dt, double *out_8005261171107713583) {
|
||||
F_fun(state, dt, out_8005261171107713583);
|
||||
}
|
||||
void car_h_25(double *state, double *unused, double *out_5845994727189855200) {
|
||||
h_25(state, unused, out_5845994727189855200);
|
||||
void car_h_25(double *state, double *unused, double *out_3451529468624779566) {
|
||||
h_25(state, unused, out_3451529468624779566);
|
||||
}
|
||||
void car_H_25(double *state, double *unused, double *out_1618166537623988050) {
|
||||
H_25(state, unused, out_1618166537623988050);
|
||||
void car_H_25(double *state, double *unused, double *out_339572919744775885) {
|
||||
H_25(state, unused, out_339572919744775885);
|
||||
}
|
||||
void car_h_24(double *state, double *unused, double *out_2733857150290724482) {
|
||||
h_24(state, unused, out_2733857150290724482);
|
||||
void car_h_24(double *state, double *unused, double *out_3799606392279303524) {
|
||||
h_24(state, unused, out_3799606392279303524);
|
||||
}
|
||||
void car_H_24(double *state, double *unused, double *out_3790816136629487616) {
|
||||
H_24(state, unused, out_3790816136629487616);
|
||||
void car_H_24(double *state, double *unused, double *out_3383890129749124077) {
|
||||
H_24(state, unused, out_3383890129749124077);
|
||||
}
|
||||
void car_h_30(double *state, double *unused, double *out_2208545198935832850) {
|
||||
h_30(state, unused, out_2208545198935832850);
|
||||
void car_h_30(double *state, double *unused, double *out_2912064509677036295) {
|
||||
h_30(state, unused, out_2912064509677036295);
|
||||
}
|
||||
void car_H_30(double *state, double *unused, double *out_6145862867751596248) {
|
||||
H_30(state, unused, out_6145862867751596248);
|
||||
void car_H_30(double *state, double *unused, double *out_6577117421746840870) {
|
||||
H_30(state, unused, out_6577117421746840870);
|
||||
}
|
||||
void car_h_26(double *state, double *unused, double *out_604006904334020533) {
|
||||
h_26(state, unused, out_604006904334020533);
|
||||
void car_h_26(double *state, double *unused, double *out_2089608757983809350) {
|
||||
h_26(state, unused, out_2089608757983809350);
|
||||
}
|
||||
void car_H_26(double *state, double *unused, double *out_5359669856498044274) {
|
||||
H_26(state, unused, out_5359669856498044274);
|
||||
void car_H_26(double *state, double *unused, double *out_4081076238618832109) {
|
||||
H_26(state, unused, out_4081076238618832109);
|
||||
}
|
||||
void car_h_27(double *state, double *unused, double *out_844321459529009555) {
|
||||
h_27(state, unused, out_844321459529009555);
|
||||
void car_h_27(double *state, double *unused, double *out_7992451901291706271) {
|
||||
h_27(state, unused, out_7992451901291706271);
|
||||
}
|
||||
void car_H_27(double *state, double *unused, double *out_8320626179552021159) {
|
||||
H_27(state, unused, out_8320626179552021159);
|
||||
void car_H_27(double *state, double *unused, double *out_2643675178688440866) {
|
||||
H_27(state, unused, out_2643675178688440866);
|
||||
}
|
||||
void car_h_29(double *state, double *unused, double *out_2793128068725012795) {
|
||||
h_29(state, unused, out_2793128068725012795);
|
||||
void car_h_29(double *state, double *unused, double *out_99512406407182912) {
|
||||
h_29(state, unused, out_99512406407182912);
|
||||
}
|
||||
void car_H_29(double *state, double *unused, double *out_5635631523437204064) {
|
||||
H_29(state, unused, out_5635631523437204064);
|
||||
void car_H_29(double *state, double *unused, double *out_41319477426376229) {
|
||||
H_29(state, unused, out_41319477426376229);
|
||||
}
|
||||
void car_h_28(double *state, double *unused, double *out_4484828697352558873) {
|
||||
h_28(state, unused, out_4484828697352558873);
|
||||
void car_h_28(double *state, double *unused, double *out_4705680922922981175) {
|
||||
h_28(state, unused, out_4705680922922981175);
|
||||
}
|
||||
void car_H_28(double *state, double *unused, double *out_3672001251871877813) {
|
||||
H_28(state, unused, out_3672001251871877813);
|
||||
void car_H_28(double *state, double *unused, double *out_2004949748991702480) {
|
||||
H_28(state, unused, out_2004949748991702480);
|
||||
}
|
||||
void car_h_31(double *state, double *unused, double *out_2046944767694668159) {
|
||||
h_31(state, unused, out_2046944767694668159);
|
||||
void car_h_31(double *state, double *unused, double *out_1000579080861491944) {
|
||||
h_31(state, unused, out_1000579080861491944);
|
||||
}
|
||||
void car_H_31(double *state, double *unused, double *out_5985877958731395750) {
|
||||
H_31(state, unused, out_5985877958731395750);
|
||||
void car_H_31(double *state, double *unused, double *out_308926957867815457) {
|
||||
H_31(state, unused, out_308926957867815457);
|
||||
}
|
||||
void car_predict(double *in_x, double *in_P, double *in_Q, double dt) {
|
||||
predict(in_x, in_P, in_Q, dt);
|
||||
|
||||
@@ -9,27 +9,27 @@ void car_update_27(double *in_x, double *in_P, double *in_z, double *in_R, doubl
|
||||
void car_update_29(double *in_x, double *in_P, double *in_z, double *in_R, double *in_ea);
|
||||
void car_update_28(double *in_x, double *in_P, double *in_z, double *in_R, double *in_ea);
|
||||
void car_update_31(double *in_x, double *in_P, double *in_z, double *in_R, double *in_ea);
|
||||
void car_err_fun(double *nom_x, double *delta_x, double *out_999653426340036453);
|
||||
void car_inv_err_fun(double *nom_x, double *true_x, double *out_4610969769563887824);
|
||||
void car_H_mod_fun(double *state, double *out_1018390387391438144);
|
||||
void car_f_fun(double *state, double dt, double *out_8437871470129484601);
|
||||
void car_F_fun(double *state, double dt, double *out_7222922179546997313);
|
||||
void car_h_25(double *state, double *unused, double *out_5845994727189855200);
|
||||
void car_H_25(double *state, double *unused, double *out_1618166537623988050);
|
||||
void car_h_24(double *state, double *unused, double *out_2733857150290724482);
|
||||
void car_H_24(double *state, double *unused, double *out_3790816136629487616);
|
||||
void car_h_30(double *state, double *unused, double *out_2208545198935832850);
|
||||
void car_H_30(double *state, double *unused, double *out_6145862867751596248);
|
||||
void car_h_26(double *state, double *unused, double *out_604006904334020533);
|
||||
void car_H_26(double *state, double *unused, double *out_5359669856498044274);
|
||||
void car_h_27(double *state, double *unused, double *out_844321459529009555);
|
||||
void car_H_27(double *state, double *unused, double *out_8320626179552021159);
|
||||
void car_h_29(double *state, double *unused, double *out_2793128068725012795);
|
||||
void car_H_29(double *state, double *unused, double *out_5635631523437204064);
|
||||
void car_h_28(double *state, double *unused, double *out_4484828697352558873);
|
||||
void car_H_28(double *state, double *unused, double *out_3672001251871877813);
|
||||
void car_h_31(double *state, double *unused, double *out_2046944767694668159);
|
||||
void car_H_31(double *state, double *unused, double *out_5985877958731395750);
|
||||
void car_err_fun(double *nom_x, double *delta_x, double *out_90543357710769815);
|
||||
void car_inv_err_fun(double *nom_x, double *true_x, double *out_17966199294720670);
|
||||
void car_H_mod_fun(double *state, double *out_844907361945072597);
|
||||
void car_f_fun(double *state, double dt, double *out_7814100581081982154);
|
||||
void car_F_fun(double *state, double dt, double *out_8005261171107713583);
|
||||
void car_h_25(double *state, double *unused, double *out_3451529468624779566);
|
||||
void car_H_25(double *state, double *unused, double *out_339572919744775885);
|
||||
void car_h_24(double *state, double *unused, double *out_3799606392279303524);
|
||||
void car_H_24(double *state, double *unused, double *out_3383890129749124077);
|
||||
void car_h_30(double *state, double *unused, double *out_2912064509677036295);
|
||||
void car_H_30(double *state, double *unused, double *out_6577117421746840870);
|
||||
void car_h_26(double *state, double *unused, double *out_2089608757983809350);
|
||||
void car_H_26(double *state, double *unused, double *out_4081076238618832109);
|
||||
void car_h_27(double *state, double *unused, double *out_7992451901291706271);
|
||||
void car_H_27(double *state, double *unused, double *out_2643675178688440866);
|
||||
void car_h_29(double *state, double *unused, double *out_99512406407182912);
|
||||
void car_H_29(double *state, double *unused, double *out_41319477426376229);
|
||||
void car_h_28(double *state, double *unused, double *out_4705680922922981175);
|
||||
void car_H_28(double *state, double *unused, double *out_2004949748991702480);
|
||||
void car_h_31(double *state, double *unused, double *out_1000579080861491944);
|
||||
void car_H_31(double *state, double *unused, double *out_308926957867815457);
|
||||
void car_predict(double *in_x, double *in_P, double *in_Q, double dt);
|
||||
void car_set_mass(double x);
|
||||
void car_set_rotational_inertia(double x);
|
||||
|
||||
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large
Load Diff
@@ -5,18 +5,18 @@ void pose_update_4(double *in_x, double *in_P, double *in_z, double *in_R, doubl
|
||||
void pose_update_10(double *in_x, double *in_P, double *in_z, double *in_R, double *in_ea);
|
||||
void pose_update_13(double *in_x, double *in_P, double *in_z, double *in_R, double *in_ea);
|
||||
void pose_update_14(double *in_x, double *in_P, double *in_z, double *in_R, double *in_ea);
|
||||
void pose_err_fun(double *nom_x, double *delta_x, double *out_8942860553158829980);
|
||||
void pose_inv_err_fun(double *nom_x, double *true_x, double *out_2254641408306223903);
|
||||
void pose_H_mod_fun(double *state, double *out_76292315910108330);
|
||||
void pose_f_fun(double *state, double dt, double *out_1412378285720339068);
|
||||
void pose_F_fun(double *state, double dt, double *out_5059879879978958987);
|
||||
void pose_h_4(double *state, double *unused, double *out_8306941651867152905);
|
||||
void pose_H_4(double *state, double *unused, double *out_41214293034027689);
|
||||
void pose_h_10(double *state, double *unused, double *out_4127406154043783729);
|
||||
void pose_H_10(double *state, double *unused, double *out_7440694459650338948);
|
||||
void pose_h_13(double *state, double *unused, double *out_2904306600296367164);
|
||||
void pose_H_13(double *state, double *unused, double *out_3171059532298305112);
|
||||
void pose_h_14(double *state, double *unused, double *out_6520989930447577741);
|
||||
void pose_H_14(double *state, double *unused, double *out_3922026563305456840);
|
||||
void pose_err_fun(double *nom_x, double *delta_x, double *out_6057666269876821352);
|
||||
void pose_inv_err_fun(double *nom_x, double *true_x, double *out_5874123099794783116);
|
||||
void pose_H_mod_fun(double *state, double *out_3022146375872577553);
|
||||
void pose_f_fun(double *state, double dt, double *out_5302456385426177188);
|
||||
void pose_F_fun(double *state, double dt, double *out_1927640881032223950);
|
||||
void pose_h_4(double *state, double *unused, double *out_61275514649772420);
|
||||
void pose_H_4(double *state, double *unused, double *out_8870228182624536274);
|
||||
void pose_h_10(double *state, double *unused, double *out_3113540078229992172);
|
||||
void pose_H_10(double *state, double *unused, double *out_8133353293446248542);
|
||||
void pose_h_13(double *state, double *unused, double *out_9190534258819938312);
|
||||
void pose_H_13(double *state, double *unused, double *out_5657954357292203473);
|
||||
void pose_h_14(double *state, double *unused, double *out_4475366908773028757);
|
||||
void pose_H_14(double *state, double *unused, double *out_9141399364440131743);
|
||||
void pose_predict(double *in_x, double *in_P, double *in_Q, double dt);
|
||||
}
|
||||
@@ -41,9 +41,7 @@ from openpilot.system.camerad.cameras.nv12_info import get_nv12_info
|
||||
from openpilot.common.transformations.model import get_warp_matrix
|
||||
from openpilot.selfdrive.controls.lib.desire_helper import DesireHelper
|
||||
from openpilot.starpilot.lateral.lane_change_preferences import LiveLaneChangePolicy
|
||||
from openpilot.starpilot.lateral.auto_lane_change import (
|
||||
ClockEpochGuard, auto_evidence, auto_trigger_engaged, paired_clocks_ns, session_policy, steering_capable,
|
||||
)
|
||||
from openpilot.starpilot.lateral.auto_lane_change import ClockEpochGuard, auto_evidence, paired_clocks_ns, session_policy, steering_capable
|
||||
from openpilot.starpilot.lateral.lane_change_status_wire import (
|
||||
Direction as StatusDirection, LaneChangeStatus, Phase as StatusPhase, encode_optional as encode_lane_status,
|
||||
)
|
||||
@@ -85,7 +83,7 @@ def get_action_from_model(model_output: dict[str, np.ndarray], prev_action: log.
|
||||
lat_action_t)
|
||||
else:
|
||||
desired_accel = model_output['action'][0,1]
|
||||
desired_curvature = model_output['action'][0,0] / (max(2.5, v_ego))**2
|
||||
desired_curvature = model_output['action'][0,0] / (max(1.0, v_ego))**2
|
||||
stop = should_stop(v_ego, desired_accel)
|
||||
desired_accel = smooth_value(desired_accel, prev_action.desiredAcceleration, LONG_SMOOTH_SECONDS)
|
||||
if v_ego > MIN_LAT_CONTROL_SPEED:
|
||||
@@ -651,8 +649,7 @@ def main(demo=False):
|
||||
model_valid=bool(modelv2_send.valid), vehicle_capable=auto_vehicle_capable))
|
||||
engaged = bool(sm['carControl'].latActive)
|
||||
DH.update(car_state, sm['carControl'].latActive, lane_change_prob,
|
||||
auto_evidence=auto_ready, engaged=auto_trigger_engaged(sm, runtime_lane_policy,
|
||||
openpilot_longitudinal=bool(CP.openpilotLongitudinalControl), now_mono_ns=mono_now_ns),
|
||||
auto_evidence=auto_ready, engaged=engaged,
|
||||
navigation_turn=matching_turn_signal(sm, mono_now_ns, supported=turn_supported))
|
||||
modelv2_send.modelV2.meta.laneChangeState = DH.lane_change_state
|
||||
modelv2_send.modelV2.meta.laneChangeDirection = DH.lane_change_direction
|
||||
|
||||
@@ -25,7 +25,7 @@ def action_function():
|
||||
def test_action_curvature_low_speed_threshold(speed):
|
||||
previous = log.ModelDataV2.Action(desiredCurvature=0.01, desiredAcceleration=0.1)
|
||||
action = action_function()({"action": np.array([[0.25, 0.5]])}, previous, 0.2, 0.2, speed)
|
||||
expected = smooth_value(0.25 / max(2.5, speed) ** 2, 0.01, LAT_SMOOTH_SECONDS) if speed > 0.3 else 0.01
|
||||
expected = smooth_value(0.25 / max(1.0, speed) ** 2, 0.01, LAT_SMOOTH_SECONDS) if speed > 0.3 else 0.01
|
||||
assert action.desiredCurvature == pytest.approx(expected)
|
||||
assert action.desiredAcceleration == pytest.approx(smooth_value(0.5, 0.1, 0.3))
|
||||
assert not action.shouldStop
|
||||
|
||||
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.
@@ -215,8 +215,9 @@ class SelfdriveD:
|
||||
|
||||
car_recognized = self.CP.brand != 'mock'
|
||||
|
||||
# A stock-ACC startup can be temporary. Preserve saved driving choices;
|
||||
# experimental_requested still gates their use on current longitudinal capability.
|
||||
# Capability changes affect runtime authority, never the saved Alpha Long choice.
|
||||
if not self.CP.openpilotLongitudinalControl:
|
||||
self.params.remove("ExperimentalMode")
|
||||
|
||||
self.CS_prev = car.CarState.new_message()
|
||||
self.AM = AlertManager()
|
||||
@@ -1274,8 +1275,7 @@ class SelfdriveD:
|
||||
not 0 < intent.carStateLogMonoTime <= now_ns or now_ns - intent.carStateLogMonoTime > INTENT_MAX_AGE_NS or
|
||||
intent.carStateLogMonoTime <= anchor[2].carStateLogMonoTime or
|
||||
intent.producerSessionId != anchor[0][4] or companion is None or companion.integrity_epoch != anchor[0][5] or
|
||||
(self.events.contains(ET.USER_DISABLE) and not stock_lateral_role(self, CS, native, intent,
|
||||
allow_independent_brake=True, allow_independent_gas=True)) or
|
||||
(self.events.contains(ET.USER_DISABLE) and not stock_lateral_role(self, CS, native, intent, allow_independent_gas=True)) or
|
||||
self.events.contains(ET.IMMEDIATE_DISABLE) or
|
||||
native is None or native is not getattr(self.aol_transport_pause, 'observed_native', None) or
|
||||
native.pandaSerial != anchor[1].pandaSerial or native.observedMonoTime < anchor[1].observedMonoTime or
|
||||
@@ -1458,13 +1458,12 @@ class SelfdriveD:
|
||||
if user_disable:
|
||||
from opendbc.car.hyundai.canfd_angle_aol import qualified as angle_qualified
|
||||
from opendbc.car.hyundai.canfd_stock_aol import qualified as stock_canfd_qualified
|
||||
from opendbc.car.hyundai.classic_scc_aol import qualified as stock_classic_qualified
|
||||
from openpilot.starpilot.car.toyota.aol import qualified as toyota_qualified
|
||||
|
||||
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) or stock_classic_qualified(self.CP, marked_only=True)) or
|
||||
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,
|
||||
@@ -1781,13 +1780,12 @@ class SelfdriveD:
|
||||
|
||||
|
||||
def _decide_aol_axes(self, CS, intent, native, *, now_ns=0, ordinary_lag=False):
|
||||
from openpilot.starpilot.car.gm.aol import physical_cancel_lateral_allowed, stock_ascm_independent_lateral, independent_lateral_below_engage
|
||||
from openpilot.starpilot.car.gm.aol import physical_cancel_lateral_allowed, stock_ascm_independent_lateral, cc_independent_lateral_below_engage
|
||||
from openpilot.starpilot.car.hyundai.aol import physical_cancel_lateral_allowed as hkg_cancel_lateral_allowed
|
||||
from openpilot.starpilot.car.mazda.aol import physical_cancel_lateral_allowed as mazda_cancel_lateral_allowed
|
||||
from openpilot.starpilot.car.ford.aol import physical_cancel_lateral_allowed as ford_cancel_lateral_allowed
|
||||
from openpilot.starpilot.car.subaru.aol import sng_standstill_lateral_inhibit
|
||||
from openpilot.starpilot.car.volkswagen.aol import (lateral_output_inhibited as volkswagen_lateral_output_inhibited,
|
||||
below_engage_lateral_allowed as volkswagen_below_engage_lateral_allowed)
|
||||
from openpilot.starpilot.car.volkswagen.aol import lateral_output_inhibited as volkswagen_lateral_output_inhibited
|
||||
from openpilot.starpilot.aol.dm import lateral_event_context
|
||||
from openpilot.starpilot.car.toyota.legacy_lateral import below_engage_lateral_allowed as toyota_below_engage_lateral_allowed
|
||||
retained_dm_lateral, lateral_events = lateral_event_context(self, CS, intent, native, now_ns, ordinary_lag)
|
||||
@@ -1821,13 +1819,12 @@ class SelfdriveD:
|
||||
cruise_main_required=getattr(self, 'aol_cruise_main_required', True),
|
||||
allow_below_engage_speed=bool((is_volt_ascm_longitudinal(self.CP) or
|
||||
stock_ascm_independent_lateral(self.CP) or
|
||||
independent_lateral_below_engage(self.CP, intent,
|
||||
cc_independent_lateral_below_engage(self.CP, intent,
|
||||
enabled=self.enabled, active=self.active)) and
|
||||
not self.CP.flags & GMFlags.PEDAL_LONG) or
|
||||
toyota_below_engage_lateral_allowed(self.CP, CS, intent, native,
|
||||
source_ns=getattr(self, 'aol_car_state_log_ns', 0), now_ns=now_ns,
|
||||
axis_session_id=getattr(self, 'aol_session_id', '')) or
|
||||
volkswagen_below_engage_lateral_allowed(self.CP, CS, intent)),
|
||||
axis_session_id=getattr(self, 'aol_session_id', ''))),
|
||||
immediate_disable=self.events.contains(ET.IMMEDIATE_DISABLE),
|
||||
dm_lockout=bool(not self.sm.all_checks(['driverMonitoringState']) or
|
||||
not retained_dm_lateral and (self.sm['driverMonitoringState'].lockout or
|
||||
@@ -1883,9 +1880,7 @@ class SelfdriveD:
|
||||
ordinary_ford = bool(self.CP.brand == 'ford' and self.CP.openpilotLongitudinalControl and self.CP.safetyConfigs and
|
||||
ford_native_accepts_cp(self.CP, int(car.CarParams.SafetyModel.ford), self.CP.safetyConfigs[-1].safetyParam))
|
||||
# Driver steering does not invalidate a still-current native LAT grant.
|
||||
from opendbc.car.volkswagen.long_aol import qualified as volkswagen_long_qualified
|
||||
steering_override_allowed = (ordinary_ford or self.CP.brand == 'hyundai' or
|
||||
volkswagen_long_qualified(self.CP, marked_only=True))
|
||||
steering_override_allowed = ordinary_ford or self.CP.brand == 'hyundai'
|
||||
proof = getattr(self, 'aol_published_input_proof', None)
|
||||
prior = getattr(self, 'aol_published_input_native', None)
|
||||
anchor = getattr(self, 'aol_ordinary_source_anchor', None)
|
||||
|
||||
@@ -189,9 +189,11 @@ class TogglesLayout(Widget):
|
||||
self._toggles["ExperimentalMode"].set_description(e2e_description)
|
||||
self._long_personality_setting.action_item.set_enabled(True)
|
||||
else:
|
||||
# Unavailable now, but retain the saved choice for a qualified drive.
|
||||
# no long for now
|
||||
self._toggles["ExperimentalMode"].action_item.set_enabled(False)
|
||||
self._toggles["ExperimentalMode"].action_item.set_state(False)
|
||||
self._long_personality_setting.action_item.set_enabled(False)
|
||||
self._params.remove("ExperimentalMode")
|
||||
|
||||
unavailable = tr("Experimental mode is currently unavailable on this car since the car's stock ACC is used for longitudinal control.")
|
||||
|
||||
|
||||
@@ -136,9 +136,11 @@ class TogglesLayoutMici(NavScroller):
|
||||
self._experimental_btn.set_visible(True)
|
||||
self._personality_toggle.set_visible(True)
|
||||
else:
|
||||
# Unavailable now, but retain the saved choice for a qualified drive.
|
||||
# no long for now
|
||||
self._experimental_btn.set_visible(False)
|
||||
self._experimental_btn.set_checked(False)
|
||||
self._personality_toggle.set_visible(False)
|
||||
ui_state.params.remove("ExperimentalMode")
|
||||
|
||||
# Refresh toggles from params to mirror external changes
|
||||
for key, item in self._refresh_toggles:
|
||||
|
||||
@@ -16,6 +16,8 @@ GearShifter = car.CarState.GearShifter
|
||||
AOL_TOGGLE = 9
|
||||
PAUSE_LATERAL = 3
|
||||
PAUSE_LONGITUDINAL = 4
|
||||
# The only wheel action WheelPublisher delivers from cruise MAIN.
|
||||
ADOPT_SPEED_LIMIT = 10
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class AolSettings:
|
||||
@@ -28,6 +30,7 @@ class AolSettings:
|
||||
mode_actions: tuple[int, int, int] = (0, 0, 0)
|
||||
custom_actions: tuple[int, int, int] = (0, 0, 0)
|
||||
default_physical_buttons: bool = True
|
||||
main_wheel_action: bool = False
|
||||
|
||||
|
||||
def _button_value(params, key: str) -> int | None:
|
||||
@@ -64,6 +67,7 @@ def read_settings(params) -> AolSettings:
|
||||
enabled=params.get_bool('AlwaysOnLateral') and valid_pause, pause_brake_mps=pause_mps if valid_pause else 0.0,
|
||||
lkas_action=_axis_action(lkas), main_action=_axis_action(main),
|
||||
default_physical_buttons=lkas == 0 and main == 0,
|
||||
main_wheel_action=main == ADOPT_SPEED_LIMIT,
|
||||
distance_actions=(_button_action(params, 'DistanceButtonControl'),
|
||||
_button_action(params, 'LongDistanceButtonControl'),
|
||||
_button_action(params, 'VeryLongDistanceButtonControl')),
|
||||
@@ -141,11 +145,10 @@ def disarming_fault(events, CS, *, temporary_ui_process_failure: bool = False,
|
||||
|
||||
class AolCardIntent:
|
||||
def __init__(self, settings: AolSettings, *, explicit_latch: bool = False, lkas_button_supported: bool = True,
|
||||
independent_cruise_buttons: bool = False, preserve_unassigned_cancel: bool = False):
|
||||
independent_cruise_buttons: bool = False):
|
||||
self.settings = settings
|
||||
self.explicit_latch = explicit_latch
|
||||
self.independent_cruise_buttons = independent_cruise_buttons
|
||||
self.preserve_unassigned_cancel = preserve_unassigned_cancel
|
||||
self.lkas_button_supported = lkas_button_supported
|
||||
self.allowed_latch = False
|
||||
# Observational only: records the exact branch that changed True to False.
|
||||
@@ -320,7 +323,8 @@ class AolCardIntent:
|
||||
independent_buttons = self.independent_cruise_buttons and (main_managed or lkas_managed)
|
||||
# The opt-in is bound to existing HKG torque owners. Other physical
|
||||
# owners keep their current event and native contracts.
|
||||
main_toggle = not independent_buttons or main_managed
|
||||
main_toggle = (not independent_buttons or main_managed) and not (
|
||||
self.independent_cruise_buttons and self.settings.main_wheel_action)
|
||||
lkas_toggle = not independent_buttons or lkas_managed
|
||||
previously_held = (self._main_held and main_toggle) or (self._lkas_held and lkas_toggle)
|
||||
cancel_pressed = False
|
||||
@@ -342,12 +346,12 @@ class AolCardIntent:
|
||||
(not independent_buttons or int(ButtonType.mainCruise) not in consumed_buttons)) or
|
||||
(self._lkas_held and lkas_toggle and
|
||||
(not independent_buttons or int(ButtonType.lkas) not in consumed_buttons)))
|
||||
if not previously_held and gesture_held and (not (independent_buttons or self.preserve_unassigned_cancel) or not cancel_pressed):
|
||||
if not previously_held and gesture_held and (not independent_buttons or not cancel_pressed):
|
||||
self._last_latch_edge_ns = now_ns
|
||||
if cancel_pressed:
|
||||
# An unassigned cruise CANCEL cannot toggle, arm or renew independent
|
||||
# lateral. Keep the existing withdrawal for configured CANCEL actions.
|
||||
if not (self.independent_cruise_buttons or self.preserve_unassigned_cancel) or any(self.settings.cancel_actions):
|
||||
if not self.independent_cruise_buttons or any(self.settings.cancel_actions):
|
||||
self._record_latch_clear('cancel_press')
|
||||
self.allowed_latch = False
|
||||
elif not self._gesture_neutral_seen:
|
||||
|
||||
@@ -121,7 +121,7 @@ class IntentCompanion:
|
||||
verified = ((stamp, *raw_verified) if raw_current else self.verified_sm_projection)
|
||||
if (verified is not None and verified[0] == stamp and verified[2] == self.integrity_epoch and
|
||||
verified[3] == self.session and sm.valid.get(INTENT_SERVICE, False) and
|
||||
0 < stamp <= now_ns <= min(stamp + SAFETY_MAX_AGE_NS, verified[1].validUntilMonoTime) and
|
||||
sm.alive.get(INTENT_SERVICE, False) and 0 < stamp <= now_ns <= stamp + SAFETY_MAX_AGE_NS and
|
||||
(stamp < source_ns or (source_ns == 0 or source_ns == stamp) and
|
||||
0 < stamp <= now_ns <= stamp + SAFETY_MAX_AGE_NS) and
|
||||
now_ns - stamp > INTENT_MAX_AGE_NS and decode_intent(sm[INTENT_SERVICE]) == verified[1]):
|
||||
|
||||
@@ -15,10 +15,6 @@ AlertLevel = log.DriverMonitoringState.AlertLevel
|
||||
|
||||
def stock_lateral_role(self, CS, native, intent=None, *, allow_independent_cancel=False, allow_independent_brake=False, allow_independent_gas=False, now_ns=0):
|
||||
"""Current stock USER role while an established independent LAT owner remains."""
|
||||
from opendbc.car.hyundai.classic_scc_aol import qualified as classic_stock_qualified
|
||||
hkg_stock = bool(self.CP.brand == 'hyundai' and classic_stock_qualified(self.CP, marked_only=True))
|
||||
main_off_role = bool(hkg_stock and not getattr(self, 'aol_cruise_main_required', True) and
|
||||
not CS.cruiseState.available)
|
||||
cancel_role = False
|
||||
if allow_independent_cancel:
|
||||
from opendbc.car.hyundai.canfd_angle_aol import qualified as angle_qualified
|
||||
@@ -37,13 +33,11 @@ def stock_lateral_role(self, CS, native, intent=None, *, allow_independent_cance
|
||||
settings = getattr(self, 'aol_settings', None)
|
||||
previous = getattr(self, 'aol_axis_decision', None)
|
||||
brake_role = held_lateral_pedal(self, CS) or bool(settings is not None and settings.enabled and CS.brakePressed and not CS.regenBraking and
|
||||
previous is not None and (previous.lateral_active or hkg_stock and
|
||||
getattr(self, 'aol_fault_reason', 'none') == 'availabilityPause' and previous.desired_lateral) and
|
||||
previous.native_acknowledged and not previous.longitudinal_active and
|
||||
previous is not None and previous.lateral_active and previous.native_acknowledged and not previous.longitudinal_active and
|
||||
(toyota_qualified(self.CP, marked_only=True) and CS.standstill and not CS.cruiseState.enabled and
|
||||
CS.gearShifter == car.CarState.GearShifter.drive and isfinite(CS.vEgoRaw) and isfinite(CS.vEgo) or
|
||||
CS.vEgo >= settings.pause_brake_mps and command_allowed(self.CP, CS) or
|
||||
len(self.CP.safetyConfigs) == 1 and (stock_canfd_qualified(self.CP, marked_only=True) or hkg_stock) and
|
||||
len(self.CP.safetyConfigs) == 1 and stock_canfd_qualified(self.CP, marked_only=True) and
|
||||
CS.gearShifter == car.CarState.GearShifter.drive and isfinite(CS.vEgoRaw) and isfinite(CS.vEgo) and
|
||||
(settings.pause_brake_mps <= 0 or CS.vEgo >= settings.pause_brake_mps or CS.standstill)))
|
||||
gas_role = False
|
||||
@@ -56,11 +50,11 @@ def stock_lateral_role(self, CS, native, intent=None, *, allow_independent_cance
|
||||
from opendbc.car.subaru.stock_aol import qualified as subaru_qualified
|
||||
|
||||
gas_role = bool((self.CP.brand == 'hyundai' and len(self.CP.safetyConfigs) == 1 and
|
||||
(stock_canfd_qualified(self.CP, marked_only=True) or hkg_stock) or self.CP.brand == 'gm' and qualified_gm(self.CP) and self.CP.pcmCruise and
|
||||
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 or
|
||||
self.CP.brand == 'subaru' and subaru_qualified(self.CP, marked_only=True)) and settings is not None and settings.enabled and
|
||||
CS.gasPressed and not CS.brakePressed and not CS.regenBraking and
|
||||
previous is not None and (previous.lateral_active or (self.CP.brand in ('gm', 'subaru') or hkg_stock) and
|
||||
previous is not None and (previous.lateral_active or self.CP.brand in ('gm', 'subaru') and
|
||||
getattr(self, 'aol_fault_reason', 'none') == 'availabilityPause' and previous.desired_lateral) and
|
||||
previous.native_acknowledged and not previous.longitudinal_active and
|
||||
(CS.gearShifter == car.CarState.GearShifter.drive or self.CP.brand == 'gm' and
|
||||
@@ -69,7 +63,7 @@ def stock_lateral_role(self, CS, native, intent=None, *, allow_independent_cance
|
||||
stock_user = stock_lateral_user_event(self.CP)
|
||||
users = [name for name in self.events.names if ET.USER_DISABLE in rules.get(name, {})]
|
||||
if stock_user is None or not users or any((name != stock_user and not (cancel_role and name == EventName.buttonCancel) and
|
||||
not (brake_role and name == EventName.pedalPressed) and not (main_off_role and name == EventName.wrongCarMode)) or
|
||||
not (brake_role and name == EventName.pedalPressed)) or
|
||||
ET.SOFT_DISABLE in rules.get(name, {}) or ET.IMMEDIATE_DISABLE in rules.get(name, {}) for name in users):
|
||||
return False
|
||||
anchor = getattr(self, 'aol_ordinary_source_anchor', None)
|
||||
@@ -83,8 +77,7 @@ def stock_lateral_role(self, CS, native, intent=None, *, allow_independent_cance
|
||||
self.sm['driverMonitoringState'].lockout or self.sm['driverMonitoringState'].alwaysOnLockout or
|
||||
not stock_lateral_off(self.CP, CS, native, allow_independent_cancel=cancel_role,
|
||||
allow_independent_brake=brake_role,
|
||||
allow_independent_gas=gas_role or brake_role and self.CP.brand == 'toyota' and CS.gasPressed,
|
||||
allow_independent_main=main_off_role)):
|
||||
allow_independent_gas=gas_role or brake_role and self.CP.brand == 'toyota' and CS.gasPressed)):
|
||||
return False
|
||||
accepted = intent if intent is not None else anchor[2]
|
||||
companion = getattr(self, 'aol_intent_companion', None)
|
||||
@@ -141,11 +134,7 @@ def ordinary_source_availability(self, CS, now_ns, native=None, *, replay: bool,
|
||||
from openpilot.starpilot.car.gm.aol import held_gm_lateral_pedal
|
||||
from openpilot.starpilot.car.toyota.aol import held_lateral_pedal
|
||||
toyota_pedal = held_lateral_pedal(self, CS)
|
||||
hkg_brake = False
|
||||
if self.CP.brand == 'hyundai' and CS.brakePressed and anchor is not None and CS is anchor[4]:
|
||||
native = native or native_provider(self.sm, self.CP, now_ns=now_ns, axis_session_id=self.aol_session_id)
|
||||
hkg_brake = stock_lateral_role(self, CS, native, allow_independent_brake=True)
|
||||
held_pedal = held_gm_lateral_pedal(self, CS) or toyota_pedal or hkg_brake
|
||||
held_pedal = held_gm_lateral_pedal(self, CS) or toyota_pedal
|
||||
if held_pedal:
|
||||
events = [event for event in events if not (event.name == EventName.pedalPressed and
|
||||
event.userDisable and not event.softDisable and not event.immediateDisable)]
|
||||
@@ -158,9 +147,8 @@ def ordinary_source_availability(self, CS, now_ns, native=None, *, replay: bool,
|
||||
if stock_lateral_role(self, CS, native, allow_independent_brake=held_pedal, allow_independent_gas=True):
|
||||
stock_gas = bool(CS.gasPressed and anchor is not None and anchor[4].gasPressed)
|
||||
stock_user = stock_lateral_user_event(self.CP)
|
||||
events = [event for event in events if not ((event.name == stock_user or
|
||||
self.CP.brand == 'hyundai' and not CS.cruiseState.available and event.name == EventName.wrongCarMode) and
|
||||
event.userDisable and not event.softDisable and not event.immediateDisable)]
|
||||
events = [event for event in events if not (event.name == stock_user and event.userDisable and
|
||||
not event.softDisable and not event.immediateDisable)]
|
||||
if (replay or not self.aol_replay or not self.axis_transport_required or anchor is None or
|
||||
not self.initialized or self.aol_authority_lost or
|
||||
getattr(self, 'aol_availability_long_inhibit', False) and stock_user is None or
|
||||
|
||||
@@ -59,14 +59,10 @@ def temporary_steering_intent_current(self, sm, cp, cs, axis, stamp, now_ns, *,
|
||||
not axis.desiredLongitudinal and not axis.longitudinalActive and not cs.steeringDisengage and
|
||||
cs.cruiseState.available and not cs.cruiseState.enabled and stock_user is not None and
|
||||
any(event.name == stock_user and event.userDisable for event in sm['onroadEvents']))
|
||||
from openpilot.starpilot.car.subaru.aol import temporary_eps_brake_recovery
|
||||
subaru_brake = bool(stock_eps_user and temporary_eps_brake_recovery(cp, cs))
|
||||
# Seatbelt blocks normal LONG, not admitted LAT intent. Keep warnings/native/output checks.
|
||||
events = tuple(event for event in sm['onroadEvents'] if not (
|
||||
stock_eps_user and event.name == stock_user and event.userDisable and
|
||||
not event.softDisable and not event.immediateDisable) and not (
|
||||
subaru_brake and event.name == log.OnroadEvent.EventName.pedalPressed and event.userDisable and
|
||||
not event.softDisable and not event.immediateDisable) and not (
|
||||
self.lateral_only_baseline and cs.seatbeltUnlatched and not axis.desiredLongitudinal and
|
||||
not axis.longitudinalActive and event.name == log.OnroadEvent.EventName.seatbeltNotLatched and
|
||||
event.softDisable and event.noEntry and not event.immediateDisable and not event.userDisable))
|
||||
|
||||
@@ -1,98 +0,0 @@
|
||||
"""An aged conflating cache is metadata, never a new raw integrity violation."""
|
||||
from contextlib import ExitStack
|
||||
from dataclasses import replace
|
||||
from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
from opendbc.car.gm.tests.test_bolt_pedal import params
|
||||
from opendbc.car.gm.values import CAR
|
||||
from openpilot.cereal import messaging
|
||||
from openpilot.common.prefix import OpenpilotPrefix
|
||||
from openpilot.selfdrive.selfdrived.events import EventName
|
||||
from openpilot.starpilot.aol.intent_companion import IntentCompanion
|
||||
from openpilot.starpilot.aol.wire import decode_intent, encode_intent
|
||||
from openpilot.starpilot.aol.tests import test_ioniq_availability as fixture
|
||||
from openpilot.starpilot.aol.tests.test_input_deadline_continuity import intent
|
||||
|
||||
BASE = 10_000_000_000
|
||||
SERVICE = 'aolIntentWire'
|
||||
|
||||
|
||||
def drain(companion, message):
|
||||
with patch('openpilot.starpilot.aol.intent_companion.messaging.recv_one_or_none', side_effect=[message.as_reader(), None]):
|
||||
assert companion.drain()
|
||||
|
||||
|
||||
@pytest.mark.parametrize('bad', ['none', 'cached_invalid', 'cached_tampered', 'raw_invalid', 'expired', 'short_lease'])
|
||||
def test_aged_known_sm_does_not_poison_valid_raw_successor_or_grant_output(bad):
|
||||
with OpenpilotPrefix():
|
||||
sm = messaging.SubMaster([SERVICE])
|
||||
companion = IntentCompanion(sock=object())
|
||||
original = intent(BASE, 1)
|
||||
if bad == 'short_lease':
|
||||
original.aolIntentWire = encode_intent(replace(decode_intent(bytes(original.aolIntentWire)),
|
||||
validUntilMonoTime=BASE + 50_000_000))
|
||||
sm.update_msgs(BASE / 1e9, [original.as_reader()])
|
||||
drain(companion, original)
|
||||
assert companion.current(sm, source_ns=BASE, now_ns=BASE + 1_000_000, drain=False) is not None
|
||||
# A normal source wait prunes the output cache, retaining only its original metadata proof.
|
||||
assert companion.current(sm, source_ns=0, now_ns=BASE + 60_000_000, drain=False) is None
|
||||
assert BASE not in companion.samples
|
||||
epoch = companion.integrity_epoch
|
||||
now = BASE + (210_000_000 if bad == 'expired' else 110_000_000)
|
||||
successor = intent(now - 1_000_000, 2)
|
||||
if bad == 'raw_invalid':
|
||||
successor.valid = False
|
||||
drain(companion, successor)
|
||||
sm.alive[SERVICE] = False
|
||||
if bad == 'cached_invalid':
|
||||
sm.valid[SERVICE] = False
|
||||
elif bad == 'cached_tampered':
|
||||
changed = intent(BASE, 1, latched=False)
|
||||
sm.update_msgs(now / 1e9, [changed.as_reader()])
|
||||
sm.alive[SERVICE] = False
|
||||
assert companion.current(sm, source_ns=0, now_ns=now, drain=False) is None
|
||||
assert companion.current(sm, source_ns=successor.logMonoTime, now_ns=now, drain=False) is None
|
||||
if bad == 'none':
|
||||
assert companion.latest is not None
|
||||
assert companion.integrity_epoch == epoch and companion.latest[1]
|
||||
sm.update_msgs(now / 1e9, [successor.as_reader()])
|
||||
assert companion.current(sm, source_ns=successor.logMonoTime, now_ns=now, drain=False) is not None
|
||||
else:
|
||||
assert companion.integrity_epoch > epoch
|
||||
|
||||
|
||||
def test_actual_bolt_source_wait_survives_raw_successor_before_conflated_cache_recovers():
|
||||
with OpenpilotPrefix(), ExitStack() as cleanup:
|
||||
case = fixture.IoniqAvailabilityTests(methodName='runTest')
|
||||
cleanup.callback(case.doCleanups)
|
||||
cp = params(CAR.CHEVROLET_BOLT_CC_2018_2021, pedal=True, camera=True)
|
||||
sd, controls, step = case._authority_callers(False, cp=cp, genuine_events=True)
|
||||
for tick in (0, 1):
|
||||
axis, _, command = step(tick, native_request_long=False, panda_controls_allowed=False)
|
||||
assert axis.lateralActive and command.latActive and not command.longActive
|
||||
old = intent(BASE + 10_000_000, 2)
|
||||
for tick in (4, 7):
|
||||
axis, _, command = step(tick, offset_ns=1_000_000 if tick == 4 else 0, timeout=True, state_stamp=BASE + 10_000_000,
|
||||
intent_message=old, native_request_long=False, panda_controls_allowed=False)
|
||||
assert str(axis.faultReason) == 'availabilityPause'
|
||||
assert not axis.lateralActive and not command.latActive and not command.longActive
|
||||
epoch = sd.aol_intent_companion.integrity_epoch
|
||||
original_update = sd.sm.update_msgs
|
||||
|
||||
def cached_poll(now, messages):
|
||||
original_update(now, [m for m in messages if m.which() != SERVICE])
|
||||
sd.sm.alive[SERVICE] = False
|
||||
|
||||
successor = intent(BASE + 110_000_000, 12)
|
||||
with patch.object(sd.sm, 'update_msgs', side_effect=cached_poll):
|
||||
axis, state, command = step(11, timeout=True, state_stamp=BASE + 10_000_000,
|
||||
intent_message=successor, native_request_long=False, panda_controls_allowed=False)
|
||||
assert sd.aol_intent_companion.integrity_epoch == epoch
|
||||
assert str(axis.faultReason) == 'availabilityPause' and axis.desiredLateral and axis.nativeAcknowledged
|
||||
assert not axis.lateralActive and not axis.longitudinalActive and axis.sourceCarStateMonoTime == 0
|
||||
assert not command.latActive and not command.longActive and not state.enabled
|
||||
assert EventName.controlsMismatch not in sd.events.names and not sd.aol_authority_lost
|
||||
axis, _, command = step(12, native_request_long=False, panda_controls_allowed=False)
|
||||
assert axis.lateralActive and command.latActive and not command.longActive
|
||||
assert EventName.controlsMismatch not in sd.events.names
|
||||
@@ -21,59 +21,6 @@ IDENTITIES = (CAR.HYUNDAI_PALISADE, CAR.HYUNDAI_SONATA, CAR.HYUNDAI_SONATA_HYBRI
|
||||
CAR.HYUNDAI_ELANTRA_2021, CAR.HYUNDAI_KONA_EV, 'palisade_hdai', 'palisade_hdaii')
|
||||
|
||||
|
||||
def brake_health_before_permission_echo(card, cs, now):
|
||||
native = withdrawal(card, cs, now, controls=True)
|
||||
native = native_change(card.sm, native, longitudinalAllowed=True)
|
||||
change(card.sm, 'pandaStates', lambda pandas: setattr(pandas[0], 'controlsAllowed', False))
|
||||
card.sm.logMonoTime['pandaStates'] = now + 1_000_000
|
||||
return native
|
||||
|
||||
|
||||
@pytest.mark.parametrize('identity', IDENTITIES)
|
||||
def test_current_brake_health_precedes_old_full_permission_echo(identity):
|
||||
card, cs = armed_card(identity)
|
||||
now = NOW + 10_000_000
|
||||
cs.brakePressed = True
|
||||
native = brake_health_before_permission_echo(card, cs, now)
|
||||
fault = card.aol_disarming_fault(cs, now, now + 2_000_000)
|
||||
card.aol_card_intent.update(cs, fault_active=fault, now_ns=now + 2_000_000)
|
||||
assert not fault and card.aol_card_intent.allowed_latch, 'BRAKE_HEALTH_FIRST_ERASED_AOL'
|
||||
result = axes(card, cs, native, now + 2_000_000)
|
||||
assert result.lateral_active and not result.longitudinal_active
|
||||
|
||||
|
||||
@pytest.mark.parametrize('fault', ('equal_health', 'older_health', 'not_braking', 'gas', 'regen',
|
||||
'rx', 'heartbeat', 'native_fault', 'latest_fault', 'lat_denied',
|
||||
'expired', 'producer', 'epoch', 'source', 'unlatched'))
|
||||
def test_brake_health_first_never_admits_unproven_or_faulted_lateral(fault):
|
||||
card, cs = armed_card(CAR.HYUNDAI_ELANTRA_2021)
|
||||
now = NOW + 10_000_000
|
||||
cs.brakePressed = True
|
||||
native = brake_health_before_permission_echo(card, cs, now)
|
||||
if fault in ('equal_health', 'older_health'):
|
||||
card.sm.logMonoTime['pandaStates'] = now - int(fault == 'older_health')
|
||||
elif fault in ('not_braking', 'gas', 'regen'):
|
||||
setattr(cs, {'not_braking':'brakePressed', 'gas':'gasPressed', 'regen':'regenBraking'}[fault], fault != 'not_braking')
|
||||
elif fault in ('rx', 'heartbeat', 'native_fault'):
|
||||
field, value = {'rx': ('safetyRxChecksInvalid', True), 'heartbeat': ('heartbeatLost', True), 'native_fault': ('faults', 1)}[fault]
|
||||
native_change(card.sm, native, pandaInventory=(replace(native.pandaInventory[0], **{field: value}),))
|
||||
elif fault == 'latest_fault':
|
||||
change(card.sm, 'pandaStates', lambda pandas: setattr(pandas[0], 'faults', ['relayMalfunction']))
|
||||
elif fault == 'lat_denied':
|
||||
native_change(card.sm, native, lateralAllowed=False)
|
||||
elif fault == 'expired':
|
||||
native_change(card.sm, native, observedMonoTime=now - 200_000_001, validUntilMonoTime=now - 1)
|
||||
elif fault in ('producer', 'epoch', 'source'):
|
||||
field, value = {'producer': ('intentProducerSessionId', 'foreign'), 'epoch': ('intentIntegrityEpoch', 99),
|
||||
'source': ('sourceCarStateMonoTime', now - 30_000_001)}[fault]
|
||||
change(card.sm, 'aolAxisState', lambda axis: setattr(axis, field, value))
|
||||
else:
|
||||
card.aol_card_intent.allowed_latch = False
|
||||
rejected = card.aol_disarming_fault(cs, now, now + 2_000_000)
|
||||
card.aol_card_intent.update(cs, fault_active=rejected, now_ns=now + 2_000_000)
|
||||
assert not card.aol_card_intent.allowed_latch
|
||||
|
||||
|
||||
def profile(identity):
|
||||
if identity in ('palisade_hdai', 'palisade_hdaii'):
|
||||
from opendbc.car.hyundai.tests.test_palisade_2023 import params as mixed_params
|
||||
|
||||
@@ -1,60 +0,0 @@
|
||||
"""Cruise's engagement floor cannot clear independent GM steering intent."""
|
||||
from contextlib import ExitStack
|
||||
|
||||
import pytest
|
||||
from opendbc.car.gm.tests.test_ascm_intercept import params
|
||||
from opendbc.car.gm.values import CAR
|
||||
from openpilot.cereal import messaging
|
||||
from openpilot.common.params import Params
|
||||
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_gm as gm_fixture, test_ioniq_availability as fixture
|
||||
from openpilot.starpilot.aol.wire import IntentState, encode_intent
|
||||
from openpilot.starpilot.audio.tests.test_axis_alerts import AudioFixture, AudibleAlert
|
||||
|
||||
|
||||
@pytest.mark.parametrize('candidate', [CAR.CHEVROLET_BLAZER, CAR.CHEVROLET_TRAVERSE, CAR.CHEVROLET_MALIBU_ASCM])
|
||||
@pytest.mark.parametrize('braking', [False, True])
|
||||
def test_acc_cruise_floor_does_not_clear_main_intent_or_chime_on_pedals(candidate, braking):
|
||||
with OpenpilotPrefix(), ExitStack() as cleanup:
|
||||
saved = Params()
|
||||
saved.put_bool('AlwaysOnLateral', True, block=True)
|
||||
card = gm_fixture.TestGmAol.card(params(candidate, sascm=True, alpha=True), saved)
|
||||
cleanup.callback(card.vehicle_startup.close)
|
||||
card.slc_producer_session = 'authority-card'
|
||||
card.aol_card_intent.allowed_latch = True
|
||||
case = fixture.IoniqAvailabilityTests(methodName='runTest')
|
||||
cleanup.callback(case.doCleanups)
|
||||
sd, _, step = case._authority_callers(False, cp=card.CP.as_reader().as_builder(), genuine_events=True)
|
||||
sd.aol_settings = read_settings(sd.params)
|
||||
audio = AudioFixture()
|
||||
sounds = []
|
||||
for tick, speed in enumerate((5., 4., 3.2, 2., 1.5, 1.3, .5, 0., .5, 1.3, 2., 3.2, 4.)):
|
||||
now = 10_000_000_000 + tick * 10_000_000
|
||||
cs = fixture.car_state(brake=braking and tick < 8)
|
||||
cs.vEgo = cs.vEgoRaw = speed
|
||||
cs.standstill = speed == 0.
|
||||
cs.cruiseState.enabled = False
|
||||
axis, host, control = step(tick, observed_state=cs, gas=tick >= 8,
|
||||
latched=card.aol_card_intent.allowed_latch, native_request_long=False, panda_controls_allowed=False)
|
||||
assert not host.enabled and not control.longActive and not axis.longitudinalActive
|
||||
assert axis.desiredLateral and axis.lateralActive and axis.nativeAcknowledged
|
||||
assert bool(control.latActive) == (speed >= card.CP.minSteerSpeed)
|
||||
below_cruise_minimum = speed < card.CP.minEnableSpeed and not (cs.brakePressed and cs.standstill)
|
||||
assert (EventName.belowEngageSpeed in sd.events.names) == below_cruise_minimum
|
||||
frames = [sd.pm.messages[n] for n in ('aolAxisState', 'onroadEvents', 'selfdriveState')]
|
||||
for service in ('pandaStates', 'aolSafetyWire', 'deviceState'):
|
||||
msg = messaging.new_message(service, len(sd.sm[service]) if service in ('pandaStates', 'aolSafetyWire') else None,
|
||||
valid=sd.sm.valid[service], logMonoTime=sd.sm.logMonoTime[service])
|
||||
setattr(msg, service, sd.sm[service])
|
||||
frames.append(msg.as_reader())
|
||||
card.sm.update_msgs(now / 1e9, frames)
|
||||
rejected = card.aol_disarming_fault(sd.CS_prev, int(card.sm.logMonoTime['onroadEvents']), now)
|
||||
card.aol_card_intent.update(sd.CS_prev, fault_active=rejected, now_ns=now)
|
||||
assert not rejected and card.aol_card_intent.allowed_latch
|
||||
wire = messaging.new_message('aolIntentWire', 0, valid=True, logMonoTime=now)
|
||||
wire.aolIntentWire = encode_intent(IntentState('authority-card', tick+1, now, now, now+200_000_000,
|
||||
True, False, False, True, card.aol_card_intent.allowed_latch))
|
||||
sounds.append(audio.publish(intent_message=wire, enabled=False, native=host.alertSound))
|
||||
assert sounds == [AudibleAlert.none] * len(sounds)
|
||||
@@ -16,7 +16,7 @@ from openpilot.starpilot.aol.runtime import decide_axes
|
||||
from openpilot.starpilot.aol.tests.test_gm_parked_startup import _exercise_parked_main
|
||||
from openpilot.starpilot.aol.tests.test_gm_startup_main import cases
|
||||
from openpilot.starpilot.aol.wire import IntentState
|
||||
from openpilot.starpilot.car.gm.aol import independent_lateral_below_engage
|
||||
from openpilot.starpilot.car.gm.aol import cc_independent_lateral_below_engage
|
||||
from openpilot.starpilot.nostalgia import aol_no_entry
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ def test_actual_bolt_main_park_then_low_speed_drive_without_main_edge(main_actio
|
||||
@pytest.mark.parametrize('identity', [CAR.CHEVROLET_BOLT_CC_2017, CAR.CHEVROLET_BOLT_CC_2018_2021, CAR.CHEVROLET_BOLT_CC_2022_2023])
|
||||
def test_exact_cc_long_factory_owners(identity):
|
||||
cp, intent = setup(identity)
|
||||
assert independent_lateral_below_engage(cp, intent, enabled=False, active=False)
|
||||
assert cc_independent_lateral_below_engage(cp, intent, enabled=False, active=False)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('enabled,active', [(False, False), (True, False), (False, True), (True, True)])
|
||||
@@ -44,7 +44,7 @@ def test_minimum_exemption_cannot_engage_ordinary_long(enabled, active, long_gra
|
||||
cp, intent = setup()
|
||||
cs = car.CarState(canValid=True, gearShifter='drive', vEgo=4.8, standstill=False)
|
||||
cs.cruiseState.available = True
|
||||
allowed = independent_lateral_below_engage(cp, intent, enabled=enabled, active=active)
|
||||
allowed = cc_independent_lateral_below_engage(cp, intent, enabled=enabled, active=active)
|
||||
no_entry = aol_no_entry([EventName.belowEngageSpeed], cs, paddle_only_cancel=False, allow_below_engage_speed=allowed)
|
||||
native = SimpleNamespace(requestedLateral=True, lateralAllowed=True, requestedLongitudinal=long_grant, longitudinalAllowed=long_grant)
|
||||
decision = decide_axes(
|
||||
@@ -68,22 +68,22 @@ def test_minimum_exemption_cannot_engage_ordinary_long(enabled, active, long_gra
|
||||
@pytest.mark.parametrize('field', ['settingsQualified', 'allowedLatch', 'lateralArmed'])
|
||||
def test_current_armed_intent_required(field):
|
||||
cp, intent = setup()
|
||||
assert not independent_lateral_below_engage(cp, replace(intent, **{field: False}), enabled=False, active=False)
|
||||
assert not cc_independent_lateral_below_engage(cp, replace(intent, **{field: False}), enabled=False, active=False)
|
||||
|
||||
|
||||
def test_lateral_pause_and_missing_intent_remain_denied():
|
||||
cp, intent = setup()
|
||||
assert not independent_lateral_below_engage(cp, None, enabled=False, active=False)
|
||||
assert not independent_lateral_below_engage(cp, replace(intent, pauseLateral=True), enabled=False, active=False)
|
||||
assert independent_lateral_below_engage(cp, replace(intent, pauseLongitudinal=True), enabled=False, active=False)
|
||||
assert not cc_independent_lateral_below_engage(cp, None, enabled=False, active=False)
|
||||
assert not cc_independent_lateral_below_engage(cp, replace(intent, pauseLateral=True), enabled=False, active=False)
|
||||
assert cc_independent_lateral_below_engage(cp, replace(intent, pauseLongitudinal=True), enabled=False, active=False)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('index', [0, 1, 3, 4, 5, 6, 7])
|
||||
def test_factory_profiles_use_independent_steering_minimum(index):
|
||||
def test_other_factory_profiles_keep_existing_admission(index):
|
||||
_, intent = setup()
|
||||
cp = list(cases())[index]
|
||||
cp.alternativeExperience = 32
|
||||
assert independent_lateral_below_engage(cp, intent, enabled=False, active=False)
|
||||
assert not cc_independent_lateral_below_engage(cp, intent, enabled=False, active=False)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
@@ -93,7 +93,7 @@ def test_factory_profiles_use_independent_steering_minimum(index):
|
||||
def test_changed_configuration_is_not_a_cc_long_owner(field, value):
|
||||
cp, intent = setup()
|
||||
setattr(cp, field, value)
|
||||
assert not independent_lateral_below_engage(cp, intent, enabled=False, active=False)
|
||||
assert not cc_independent_lateral_below_engage(cp, intent, enabled=False, active=False)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('event', [EventName.buttonCancel, EventName.doorOpen, EventName.controlsMismatch, EventName.commIssue, EventName.steerUnavailable])
|
||||
@@ -101,5 +101,5 @@ def test_only_cruise_speed_event_is_removed(event):
|
||||
cp, intent = setup()
|
||||
cs = car.CarState(canValid=True, gearShifter='drive', vEgo=4.8)
|
||||
cs.cruiseState.available = True
|
||||
allowed = independent_lateral_below_engage(cp, intent, enabled=False, active=False)
|
||||
allowed = cc_independent_lateral_below_engage(cp, intent, enabled=False, active=False)
|
||||
assert aol_no_entry([EventName.belowEngageSpeed, event], cs, paddle_only_cancel=False, allow_below_engage_speed=allowed) == (event != EventName.doorOpen)
|
||||
|
||||
@@ -18,7 +18,7 @@ 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 independent_lateral_below_engage
|
||||
from openpilot.starpilot.car.gm.aol import cc_independent_lateral_below_engage
|
||||
|
||||
|
||||
def cc_params(candidate):
|
||||
@@ -31,16 +31,16 @@ def cc_params(candidate):
|
||||
def test_all_exact_ordinary_cc_owners_keep_independent_steering_minimum(candidate):
|
||||
cp = cc_params(candidate)
|
||||
_, intent = setup()
|
||||
assert independent_lateral_below_engage(cp, intent, enabled=False, active=False)
|
||||
assert cc_independent_lateral_below_engage(cp, intent, enabled=False, active=False)
|
||||
for enabled, active in ((True, True), (True, False), (False, True)):
|
||||
assert not independent_lateral_below_engage(cp, intent, enabled=enabled, active=active)
|
||||
assert not cc_independent_lateral_below_engage(cp, intent, enabled=enabled, active=active)
|
||||
for key in ('settingsQualified', 'allowedLatch', 'lateralArmed'):
|
||||
assert not independent_lateral_below_engage(cp, replace(intent, **{key: False}), enabled=False, active=False)
|
||||
assert not independent_lateral_below_engage(cp, replace(intent, pauseLateral=True), enabled=False, active=False)
|
||||
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 independent_lateral_below_engage(denied, intent, enabled=False, active=False)
|
||||
assert not cc_independent_lateral_below_engage(denied, intent, enabled=False, active=False)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('main_action', [0, 9])
|
||||
|
||||
@@ -1,74 +0,0 @@
|
||||
"""Classic stock ACC user roles do not revoke an independent lateral owner."""
|
||||
from contextlib import ExitStack
|
||||
|
||||
import pytest
|
||||
from openpilot.cereal import log, messaging
|
||||
from openpilot.common.prefix import OpenpilotPrefix
|
||||
from opendbc.car.hyundai.values import CAR
|
||||
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_classic_scc_profiles import params
|
||||
from openpilot.starpilot.aol.tests.test_ordinary_source_availability import settings
|
||||
|
||||
BASE = 10_000_000_000
|
||||
|
||||
|
||||
def sample(stamp, pedal):
|
||||
message = messaging.new_message('carState', valid=True, logMonoTime=stamp)
|
||||
state = fixture.car_state(brake=pedal == 'brake')
|
||||
state.gasPressed = pedal == 'gas'
|
||||
state.cruiseState.available = state.cruiseState.enabled = False
|
||||
message.carState = state
|
||||
return message
|
||||
|
||||
|
||||
@pytest.mark.parametrize('identity', [CAR.HYUNDAI_SONATA_HYBRID, CAR.HYUNDAI_SONATA, CAR.HYUNDAI_ELANTRA_2021])
|
||||
@pytest.mark.parametrize('pedal', ['brake', 'gas'])
|
||||
@pytest.mark.parametrize('fresh', [False, True])
|
||||
def test_stock_hkg_main_off_pedal_source_join_preserves_only_valid_lateral(identity, pedal, fresh):
|
||||
case = fixture.IoniqAvailabilityTests(methodName='runTest')
|
||||
with ExitStack() as cleanup:
|
||||
cleanup.callback(case.doCleanups)
|
||||
cleanup.enter_context(OpenpilotPrefix())
|
||||
sd, controls, drive, card, receive = case._ioniq_availability_callers(False, cp=params(identity, 0x391), cold_start=True)
|
||||
sd.params.put('PauseAOLOnBrake', 0.0, block=True)
|
||||
sd.aol_settings = read_settings(sd.params)
|
||||
for tick in (0, 1, 2):
|
||||
stamp = BASE + tick * 10_000_000
|
||||
state = sample(stamp, pedal)
|
||||
axis, _, command = drive(tick, car_state_message=state, offset_ns=5_000_000 if tick == 2 else 0,
|
||||
long_allowed=False, **settings('lateral_only', stamp, tick + 1))
|
||||
assert axis.lateralActive and command.latActive and not command.longActive
|
||||
receive(stamp + (5_000_000 if tick == 2 else 0), axis=sd.pm.messages['aolAxisState'],
|
||||
event=sd.pm.messages['onroadEvents'], state=state.carState, preserve_cruise_state=True)
|
||||
assert card.aol_card_intent.allowed_latch
|
||||
original = axis.to_dict()
|
||||
stamp = BASE + (37_000_000 if fresh else 20_000_000)
|
||||
state = sample(stamp, pedal)
|
||||
axis, _, command = drive(6, car_state_message=state, timeout=not fresh, offset_ns=5_000_000,
|
||||
long_allowed=False, **settings('lateral_only', stamp, 4 if fresh else 3))
|
||||
assert log.OnroadEvent.EventName.controlsMismatch not in sd.events.names, 'STOCK_HKG_HELD_PEDAL_MISMATCH'
|
||||
assert not sd.aol_authority_lost and not command.longActive
|
||||
if fresh:
|
||||
assert axis.lateralActive and command.latActive
|
||||
assert axis.sourceCarStateMonoTime == stamp
|
||||
assert axis.validUntilMonoTime != original['validUntilMonoTime']
|
||||
else:
|
||||
assert str(axis.faultReason) == 'availabilityPause' and axis.desiredLateral
|
||||
assert not axis.lateralActive and not command.latActive and axis.sourceCarStateMonoTime == 0
|
||||
receive(BASE + 65_000_000, axis=sd.pm.messages['aolAxisState'], event=sd.pm.messages['onroadEvents'], state=state.carState, preserve_cruise_state=True)
|
||||
assert card.aol_card_intent.allowed_latch, 'STOCK_HKG_HELD_PEDAL_ERASED_LATCH'
|
||||
if not fresh:
|
||||
axis, _, command = drive(7, car_state_message=state, timeout=True, long_allowed=False,
|
||||
**settings('lateral_only', stamp, 3))
|
||||
assert str(axis.faultReason) == 'availabilityPause' and axis.desiredLateral
|
||||
assert not axis.lateralActive and not command.latActive and not command.longActive
|
||||
assert not sd.aol_authority_lost and log.OnroadEvent.EventName.controlsMismatch not in sd.events.names
|
||||
receive(BASE + 70_000_000, axis=sd.pm.messages['aolAxisState'], event=sd.pm.messages['onroadEvents'],
|
||||
state=state.carState, preserve_cruise_state=True)
|
||||
assert card.aol_card_intent.allowed_latch
|
||||
stamp = BASE + 80_000_000
|
||||
state = sample(stamp, pedal)
|
||||
axis, _, command = drive(8, car_state_message=state, long_allowed=False,
|
||||
**settings('lateral_only', stamp, 5))
|
||||
assert axis.lateralActive and command.latActive and not command.longActive
|
||||
@@ -75,12 +75,12 @@ def cleanup(cp):
|
||||
|
||||
def test_actual_cleanup_preserves_request_without_enabling_long():
|
||||
cp = stock(True)
|
||||
assert cleanup(cp) == []
|
||||
assert cleanup(cp) == ['ExperimentalMode']
|
||||
cp.passive = True
|
||||
cp.dashcamOnly = True
|
||||
assert not qualified_ioniq6(cp) and not ioniq6_settings_capable(cp)
|
||||
assert cleanup(cp) == []
|
||||
assert cleanup(cp) == ['ExperimentalMode']
|
||||
cp.carFingerprint = CAR.HYUNDAI_IONIQ_5
|
||||
assert cleanup(cp) == []
|
||||
assert cleanup(cp) == ['ExperimentalMode']
|
||||
cp.openpilotLongitudinalControl = True
|
||||
assert cleanup(cp) == []
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
"""Cruise MAIN assigned to Adopt speed limit is not also the Hyundai physical lateral toggle."""
|
||||
import pytest
|
||||
from opendbc.car import structs
|
||||
from openpilot.starpilot.aol.intent import AolCardIntent, AolSettings, read_settings
|
||||
from openpilot.starpilot.aol.tests.test_ioniq6_stock_repair import stock
|
||||
from openpilot.starpilot.car.hyundai.aol import create_intent, qualified_ioniq6
|
||||
|
||||
|
||||
def long_cp():
|
||||
cp = stock(False)
|
||||
cp.pcmCruise = False
|
||||
cp.openpilotLongitudinalControl = True
|
||||
cp.safetyConfigs[0].safetyParam = 0x8815
|
||||
assert qualified_ioniq6(cp)
|
||||
return cp
|
||||
|
||||
|
||||
def press(owner, cs, button, now_ns, *, engaged=False):
|
||||
cs.buttonEvents = [structs.CarState.ButtonEvent(type=button, pressed=True)]
|
||||
owner.update(cs, now_ns=now_ns, fault_active=False, standard_enabled=engaged)
|
||||
cs.buttonEvents = [structs.CarState.ButtonEvent(type=button, pressed=False)]
|
||||
owner.update(cs, now_ns=now_ns + 1, fault_active=False, standard_enabled=engaged)
|
||||
|
||||
|
||||
def lateral(owner):
|
||||
return owner.allowed_latch and not owner.pause_lateral
|
||||
|
||||
|
||||
def armed(owner):
|
||||
cs = structs.CarState(canValid=True, gearShifter='drive')
|
||||
cs.cruiseState.available = True
|
||||
owner.update(cs, now_ns=1, fault_active=False)
|
||||
press(owner, cs, 'lkas', 2)
|
||||
assert lateral(owner)
|
||||
return cs
|
||||
|
||||
|
||||
@pytest.mark.parametrize('main_wheel_action', [False, True])
|
||||
def test_ioniq6_main_assigned_to_adopt_keeps_lkas_armed_lateral(main_wheel_action):
|
||||
owner = create_intent(long_cp(), AolSettings(True, 0., 0, 0, (0, 0, 0), (0, 0, 0), main_wheel_action=main_wheel_action))
|
||||
assert owner.independent_cruise_buttons
|
||||
cs = armed(owner)
|
||||
press(owner, cs, 'mainCruise', 10, engaged=True)
|
||||
assert lateral(owner) == main_wheel_action
|
||||
press(owner, cs, 'lkas', 20, engaged=True)
|
||||
assert lateral(owner) != main_wheel_action
|
||||
|
||||
|
||||
def test_main_toggle_is_unchanged_without_independent_cruise_buttons():
|
||||
owner = AolCardIntent(AolSettings(True, 0., 0, 0, (0, 0, 0), (0, 0, 0), main_wheel_action=True), explicit_latch=True)
|
||||
cs = armed(owner)
|
||||
press(owner, cs, 'mainCruise', 10, engaged=True)
|
||||
assert not lateral(owner)
|
||||
|
||||
|
||||
class SavedParams:
|
||||
def __init__(self, values):
|
||||
self.values = values
|
||||
|
||||
def get(self, key, return_default=False):
|
||||
return self.values.get(key)
|
||||
|
||||
def get_bool(self, key):
|
||||
return self.values.get(key) == b'1'
|
||||
|
||||
def get_param_path(self, key):
|
||||
return f'/nonexistent/{key}'
|
||||
|
||||
|
||||
@pytest.mark.parametrize('lkas,main,main_wheel', [(b'0', b'10', True), (b'0', b'9', False), (b'10', b'0', False), (b'0', b'8', False)])
|
||||
def test_only_main_adopt_is_marked_as_a_main_wheel_action(lkas, main, main_wheel):
|
||||
settings = read_settings(SavedParams({'AlwaysOnLateral': b'1', 'LKASButtonControl': lkas, 'MainCruiseButtonControl': main}))
|
||||
assert settings.main_wheel_action == main_wheel
|
||||
@@ -378,7 +378,7 @@ class IoniqAvailabilityTests(unittest.TestCase):
|
||||
cs.buttonEvents = []
|
||||
self.assertEqual(card.aol_card_intent.allowed_latch, initially_armed)
|
||||
|
||||
def receive(now, *, axis=None, event=None, state=None, preserve_cruise_state=False):
|
||||
def receive(now, *, axis=None, event=None, state=None):
|
||||
frames = []
|
||||
for service in ('pandaStates', 'aolSafetyWire', 'deviceState', 'carControl'):
|
||||
message = messaging.new_message(service, len(sd.sm[service]) if service == 'pandaStates' else
|
||||
@@ -391,8 +391,7 @@ class IoniqAvailabilityTests(unittest.TestCase):
|
||||
observed = state if state is not None else car_state()
|
||||
if stock_angle and state is None:
|
||||
observed.vEgoRaw = observed.vEgo
|
||||
if not preserve_cruise_state:
|
||||
observed.cruiseState.available = True
|
||||
observed.cruiseState.available = True
|
||||
card.CC_prev = card.sm['carControl']
|
||||
with mock.patch('openpilot.selfdrive.car.card.messaging.drain_sock_raw', return_value=[b'fixture']), \
|
||||
mock.patch('openpilot.selfdrive.car.card.can_capnp_to_list', return_value=[]), \
|
||||
|
||||
@@ -114,7 +114,7 @@ def test_meb_full_engagement_brake_reverse_drive_actual_callers(identity, stock,
|
||||
# Explicit ambient producers added since the original shared caller fixture.
|
||||
# Keep production SubMaster health/frequency checks; never ignore a required service.
|
||||
from openpilot.cereal.services import SERVICE_LIST
|
||||
ambient_services = ('laneChangeAssistWire',
|
||||
ambient_services = ('alertDebug', 'lateralManeuverPlan', 'laneChangeAssistWire',
|
||||
'starpilotLongitudinalPlan', 'slcState', 'gpsLocation')
|
||||
original_sd_update = sd.sm.update_msgs
|
||||
ambient_due = {}
|
||||
|
||||
@@ -31,7 +31,7 @@ from openpilot.starpilot.aol.tests.stock_lateral_test_helpers import authority_c
|
||||
@pytest.mark.parametrize('fixed', (False,))
|
||||
def test_mqb_full_engagement_brake_reverse_drive_actual_callers(gearbox, alpha, offset, fixed, engage_on_set,
|
||||
stop_and_resume=False, candidate=CAR.VOLKSWAGEN_GOLF_MK7,
|
||||
brake_held_engagement=False, creep_start=False, early_brake=False):
|
||||
brake_held_engagement=False):
|
||||
with ExitStack() as cleanup:
|
||||
cleanup.enter_context(OpenpilotPrefix())
|
||||
cleanup.enter_context(mock.patch.dict('os.environ', {'SIMULATION': '0', 'AOL_REPLAY_RUNTIME': '1'}))
|
||||
@@ -116,7 +116,7 @@ def test_mqb_full_engagement_brake_reverse_drive_actual_callers(gearbox, alpha,
|
||||
# Explicit ambient producers added since the original shared caller fixture.
|
||||
# Keep production SubMaster health/frequency checks; never ignore a required service.
|
||||
from openpilot.cereal.services import SERVICE_LIST
|
||||
ambient_services = ('laneChangeAssistWire',
|
||||
ambient_services = ('alertDebug', 'lateralManeuverPlan', 'laneChangeAssistWire',
|
||||
'starpilotLongitudinalPlan', 'slcState', 'gpsLocation')
|
||||
original_sd_update = sd.sm.update_msgs
|
||||
ambient_due = {}
|
||||
@@ -156,8 +156,6 @@ def test_mqb_full_engagement_brake_reverse_drive_actual_callers(gearbox, alpha,
|
||||
for tick in range(120):
|
||||
now = 10_000_000_000 + tick * 10_000_000
|
||||
native.set_timer(1_000_000 + tick * 10_000)
|
||||
if creep_start and tick >= 12:
|
||||
native.set_aol_test_heartbeat(bool(card.aol_card_intent.allowed_latch or request))
|
||||
if quiet is not None:
|
||||
quiet.set_timer(1_000_000 + tick * 10_000)
|
||||
quiet.safety_tick()
|
||||
@@ -165,12 +163,12 @@ def test_mqb_full_engagement_brake_reverse_drive_actual_callers(gearbox, alpha,
|
||||
assert not quiet.get_controls_allowed()
|
||||
assert native.get_current_safety_mode() != quiet.get_current_safety_mode()
|
||||
|
||||
brake = (60 if early_brake else 65) <= tick < 75 or (brake_held_engagement and tick < 29)
|
||||
brake = 65 <= tick < 75 or (brake_held_engagement and tick < 29)
|
||||
reverse = 85 <= tick < 95
|
||||
ordinary = engage_on_set and 21 <= tick < 65
|
||||
overrides = {
|
||||
'TSK_06': {'TSK_Status': 3 if ordinary and not alpha else 2},
|
||||
'LH_EPS_03': {'EPS_HCA_Status': 3, 'EPS_Lenkmoment': 100 if early_brake and 52 <= tick <= 61 else 0},
|
||||
'LH_EPS_03': {'EPS_HCA_Status': 3, 'EPS_Lenkmoment': 0},
|
||||
'Motor_20': {'MO_Fahrpedalrohwert_01': 0},
|
||||
'Motor_14': {'MO_Fahrer_bremst': int(brake), 'MO_Kuppl_schalter': 0},
|
||||
'ESP_05': {'ESP_Fahrer_bremst': int(brake)},
|
||||
@@ -178,10 +176,9 @@ def test_mqb_full_engagement_brake_reverse_drive_actual_callers(gearbox, alpha,
|
||||
'Gateway_72': {'BCM1_Rueckfahrlicht_Schalter': int(reverse)},
|
||||
'Motor_EV_01': {'MO_Waehlpos': 3 if reverse else 5},
|
||||
'ESP_19': dict.fromkeys(('ESP_VL_Radgeschw_02', 'ESP_VR_Radgeschw_02', 'ESP_HL_Radgeschw_02', 'ESP_HR_Radgeschw_02'),
|
||||
(72 if tick < 12 else 0 if tick < 20 else 1 if tick < 24 else 2 if tick < 46 else 72) if creep_start else
|
||||
0 if (stop_and_resume and 45 <= tick < 56) or (brake_held_engagement and tick < 30) else 72),
|
||||
'Airbag_02': {'AB_Gurtschloss_FA': 0 if 105 <= tick < 111 else 3},
|
||||
'GRA_ACC_01': {'GRA_Hauptschalter': 1, 'GRA_Tip_Setzen': int(engage_on_set and (tick in (20, 50) if creep_start else tick == 20))},
|
||||
'GRA_ACC_01': {'GRA_Hauptschalter': 1, 'GRA_Tip_Setzen': int(engage_on_set and tick == 20)},
|
||||
'ACC_02': {'ACC_Gesetzte_Zeitluecke': 1},
|
||||
}
|
||||
incoming = []
|
||||
@@ -199,9 +196,6 @@ def test_mqb_full_engagement_brake_reverse_drive_actual_callers(gearbox, alpha,
|
||||
native.safety_tick()
|
||||
native.aol_set_host_request(request)
|
||||
permission = native.aol_get_permission_mask()
|
||||
if early_brake and tick == 60:
|
||||
incoming = [(address, data, bus) for address, data, bus in incoming if address not in (0x106, 0x3BE)]
|
||||
assert not native.get_controls_allowed() and not permission & 2
|
||||
original_message = messaging.new_message
|
||||
def clocked_card_message(*args, original_message=original_message, now=now, **kwargs):
|
||||
message = original_message(*args, **kwargs)
|
||||
@@ -224,11 +218,6 @@ def test_mqb_full_engagement_brake_reverse_drive_actual_callers(gearbox, alpha,
|
||||
allowed=bool(permission & 1), long_allowed=bool(permission & 2),
|
||||
native_request_lat=bool(request & 1), native_request_long=bool(request & 2),
|
||||
panda_controls_allowed=native.get_controls_allowed())
|
||||
if early_brake and tick == 60:
|
||||
assert not state.brakePressed and state.steeringPressed
|
||||
assert log.OnroadEvent.EventName.controlsMismatch not in sd.events.names, 'VW_EARLY_BRAKE_STEERING_MISMATCH'
|
||||
assert not command.longActive
|
||||
assert card.aol_card_intent.allowed_latch
|
||||
if brake_held_engagement and 22 <= tick < 29:
|
||||
assert state.standstill and state.brakePressed
|
||||
assert selfdrive.state == log.SelfdriveState.OpenpilotState.preEnabled
|
||||
@@ -273,9 +262,6 @@ def test_mqb_full_engagement_brake_reverse_drive_actual_callers(gearbox, alpha,
|
||||
emitted.extend(sent)
|
||||
lat_commands.append(command.latActive)
|
||||
long_commands.append(command.longActive)
|
||||
if creep_start:
|
||||
assert any(lat_commands[35:45]), 'AOL must request and obtain native steering below the manual long-enable speed'
|
||||
assert not any(long_commands[:46]), 'Creep AOL must not enable longitudinal below its independent minimum'
|
||||
assert any(lat_commands[35:60])
|
||||
assert any(lat_commands[76:84]) # braking released long, not qualified lateral intent
|
||||
assert not any(lat_commands[86:95])
|
||||
@@ -284,7 +270,7 @@ def test_mqb_full_engagement_brake_reverse_drive_actual_callers(gearbox, alpha,
|
||||
assert not any(long_commands[66:]) # no automatic longitudinal restoration
|
||||
assert emitted
|
||||
if alpha and engage_on_set:
|
||||
assert any(long_commands[50:65] if creep_start else long_commands[35:60])
|
||||
assert any(long_commands[35:60])
|
||||
else:
|
||||
assert not any(long_commands)
|
||||
|
||||
@@ -301,50 +287,3 @@ def test_mqb_brake_held_engagement_waits_for_release_actual_callers(gearbox):
|
||||
test_mqb_full_engagement_brake_reverse_drive_actual_callers(
|
||||
gearbox, True, 0, False, True, brake_held_engagement=True,
|
||||
candidate=CAR.SEAT_ATECA_MK1 if gearbox == 'automatic' else CAR.VOLKSWAGEN_GOLF_MK7)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('offset,fixed', [(0, False), (0, True), (4, False), (4, True)])
|
||||
def test_mqb_manual_alpha_stopped_creep_preserves_native_arm_and_fresh_speed_engagement(offset, fixed):
|
||||
test_mqb_full_engagement_brake_reverse_drive_actual_callers(
|
||||
'manual', True, offset, fixed, True, creep_start=True)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('veto', ('none', 'can', 'timeout', 'temporary', 'permanent', 'main', 'reverse', 'latch', 'pause', 'automatic', 'stock'))
|
||||
def test_manual_cruise_minimum_exception_retains_physical_and_intent_vetoes(veto):
|
||||
from types import SimpleNamespace
|
||||
from openpilot.starpilot.car.volkswagen.aol import below_engage_lateral_allowed
|
||||
from openpilot.starpilot.aol.vehicle import policy_for
|
||||
fp = {bus: {} for bus in range(8)}
|
||||
fp[1][0xFD] = 8
|
||||
cp = CarInterface.get_params(CAR.VOLKSWAGEN_GOLF_MK7, fp, [], True, False, False)
|
||||
policy = policy_for(cp)
|
||||
cp.safetyConfigs[-1].safetyParam |= policy.safety_param_addition
|
||||
cp.alternativeExperience |= policy.alternative_experience_addition
|
||||
state = structs.CarState(canValid=True, gearShifter=structs.CarState.GearShifter.drive)
|
||||
state.cruiseState.available = True
|
||||
intent = SimpleNamespace(allowedLatch=True, pauseLateral=False)
|
||||
if veto in ('can', 'timeout', 'temporary', 'permanent'):
|
||||
setattr(state, {'can': 'canValid', 'timeout': 'canTimeout', 'temporary': 'steerFaultTemporary',
|
||||
'permanent': 'steerFaultPermanent'}[veto], veto != 'can')
|
||||
elif veto == 'main':
|
||||
state.cruiseState.available = False
|
||||
elif veto == 'reverse':
|
||||
state.gearShifter = structs.CarState.GearShifter.reverse
|
||||
elif veto == 'latch':
|
||||
intent.allowedLatch = False
|
||||
elif veto == 'pause':
|
||||
intent.pauseLateral = True
|
||||
elif veto == 'automatic':
|
||||
cp.transmissionType = structs.CarParams.TransmissionType.automatic
|
||||
cp.safetyConfigs[-1].safetyParam = 9
|
||||
elif veto == 'stock':
|
||||
cp.openpilotLongitudinalControl, cp.pcmCruise = False, True
|
||||
cp.safetyConfigs[-1].safetyParam = 24
|
||||
assert below_engage_lateral_allowed(cp, state, intent) is (veto == 'none')
|
||||
|
||||
|
||||
@pytest.mark.parametrize('gearbox', ('automatic', 'manual', 'direct'))
|
||||
def test_native_brake_precedes_parsed_pedal_during_driver_steering(gearbox):
|
||||
test_mqb_full_engagement_brake_reverse_drive_actual_callers(
|
||||
gearbox, True, 0, False, True, early_brake=True,
|
||||
candidate=CAR.SEAT_ATECA_MK1 if gearbox == 'automatic' else CAR.VOLKSWAGEN_GOLF_MK7)
|
||||
|
||||
@@ -1,214 +0,0 @@
|
||||
"""Mixed Palisade Pause/Resume retains only existing, current lateral authority."""
|
||||
from dataclasses import replace
|
||||
from types import SimpleNamespace
|
||||
from typing import cast
|
||||
import pytest
|
||||
from opendbc.car import structs
|
||||
from openpilot.cereal import log
|
||||
from openpilot.selfdrive.selfdrived.events import Events
|
||||
from openpilot.selfdrive.selfdrived.selfdrived import SelfdriveD
|
||||
from openpilot.starpilot.aol.intent import AolSettings, AOL_TOGGLE
|
||||
from openpilot.starpilot.aol.runtime import AxisDecision
|
||||
from openpilot.starpilot.aol.wire import IntentState, SafetyState, PandaSlot
|
||||
from openpilot.starpilot.aol.tests.test_classic_brake_withdrawal import profile
|
||||
from openpilot.starpilot.car.hyundai.aol import create_intent
|
||||
|
||||
|
||||
class Messages(dict):
|
||||
ready = True
|
||||
def all_checks(self, services=None):
|
||||
return self.ready
|
||||
|
||||
|
||||
@pytest.mark.parametrize('identity', ('palisade_hdai', 'palisade_hdaii'))
|
||||
@pytest.mark.parametrize('case', ['retain', 'prior_off', 'settings_off', 'configured_cancel', 'intent_off', 'stale_source',
|
||||
'native_denied', 'native_expired', 'wrong_session', 'wrong_inventory', 'hard_event',
|
||||
'model', 'dm', 'reverse', 'steer_fault'])
|
||||
def test_actual_selfdrived_cancel_keeps_only_current_owned_lateral(identity, case):
|
||||
now = 4_000_000_000
|
||||
cp = profile(identity)
|
||||
config = cp.safetyConfigs[0]
|
||||
model, word = int(config.safetyModel.raw), int(config.safetyParam)
|
||||
intent = IntentState('card', 1, now, now, now + 200_000_000, True, False, False, True, True)
|
||||
slot = PandaSlot(0, 'panda', model, word, cp.alternativeExperience)
|
||||
inventory = (slot,)
|
||||
native = SafetyState(1, True, now, now + 200_000_000, model, word, True, False, True, False,
|
||||
'panda', 'axis', now, inventory)
|
||||
cs = structs.CarState(canValid=True, gearShifter='drive', vEgo=10.)
|
||||
cs.cruiseState.available = True
|
||||
cs.buttonEvents = [structs.CarState.ButtonEvent(type='cancel', pressed=True)]
|
||||
sm = Messages(driverMonitoringState=SimpleNamespace(alertLevel=0, lockout=False, alwaysOnLockout=False),
|
||||
extrinsicsCalibration=SimpleNamespace(calStatus=log.ExtrinsicsCalibration.Status.calibrated))
|
||||
sd = SimpleNamespace(CP=cp, active=False, enabled=False, initialized=True, sm=sm,
|
||||
aol_settings=AolSettings(True, 0., AOL_TOGGLE, 0, (0,0,0), (0,0,0)),
|
||||
aol_axis_decision=AxisDecision(lateral_active=True, native_acknowledged=True),
|
||||
aol_car_state_log_ns=now, aol_session_id='axis', aol_dm_lateral_inhibit=False,
|
||||
nostalgia_paddle_cancel=False, events=Events())
|
||||
sd.events.add(log.OnroadEvent.EventName.buttonCancel)
|
||||
if case == 'prior_off':
|
||||
sd.aol_axis_decision = AxisDecision()
|
||||
elif case == 'settings_off':
|
||||
sd.aol_settings = replace(sd.aol_settings, enabled=False)
|
||||
elif case == 'configured_cancel':
|
||||
sd.aol_settings = replace(sd.aol_settings, cancel_actions=(3,0,0))
|
||||
elif case == 'intent_off':
|
||||
intent = replace(intent, allowedLatch=False)
|
||||
elif case == 'stale_source':
|
||||
sd.aol_car_state_log_ns = now - 30_000_001
|
||||
intent = replace(intent, carStateLogMonoTime=sd.aol_car_state_log_ns)
|
||||
elif case == 'native_denied':
|
||||
native = replace(native, lateralAllowed=False)
|
||||
elif case == 'native_expired':
|
||||
native = replace(native, validUntilMonoTime=now-1)
|
||||
elif case == 'wrong_session':
|
||||
native = replace(native, axisSessionId='other')
|
||||
elif case == 'wrong_inventory':
|
||||
native = replace(native, pandaInventory=(*inventory[:-1], replace(slot, safetyParam=word ^ 0x80)))
|
||||
elif case == 'hard_event':
|
||||
sd.events.add(log.OnroadEvent.EventName.controlsMismatch)
|
||||
elif case == 'model':
|
||||
sm.ready = False
|
||||
elif case == 'dm':
|
||||
sm['driverMonitoringState'].lockout = True
|
||||
elif case == 'reverse':
|
||||
cs.gearShifter = 'reverse'
|
||||
elif case == 'steer_fault':
|
||||
cs.steerFaultPermanent = True
|
||||
result = SelfdriveD._decide_aol_axes(cast(SelfdriveD, sd), cs, intent, native, now_ns=now)
|
||||
assert result.lateral_active == (case == 'retain')
|
||||
assert not result.longitudinal_active
|
||||
assert log.OnroadEvent.EventName.buttonCancel in sd.events.names
|
||||
|
||||
|
||||
@pytest.mark.parametrize('identity', ('palisade_hdai', 'palisade_hdaii'))
|
||||
@pytest.mark.parametrize('duration', (61_047_724, 221_004_624))
|
||||
@pytest.mark.parametrize('cancel_actions', ((0, 0, 0), (3, 0, 0), (0, 3, 0), (0, 0, 3)))
|
||||
def test_host_cancel_hold_keeps_only_unassigned_preexisting_latch(identity, duration, cancel_actions):
|
||||
cp = profile(identity)
|
||||
settings = AolSettings(True, 0., AOL_TOGGLE, 0, (0, 0, 0), cancel_actions)
|
||||
owner = create_intent(cp, settings)
|
||||
cs = structs.CarState(canValid=True, gearShifter='drive')
|
||||
cs.cruiseState.available = True
|
||||
owner.update(cs, now_ns=1_000_000_000)
|
||||
cs.buttonEvents = [structs.CarState.ButtonEvent(type='lkas', pressed=True)]
|
||||
owner.update(cs, now_ns=1_010_000_000)
|
||||
assert owner.allowed_latch
|
||||
edge, arm = owner._last_latch_edge_ns, owner.physical_arm_ns
|
||||
cs.buttonEvents = [structs.CarState.ButtonEvent(type='lkas', pressed=False)]
|
||||
owner.update(cs, now_ns=1_020_000_000)
|
||||
for stamp, events in ((1_030_000_000, [('cancel', True)]),
|
||||
(1_040_000_000, []), (1_030_000_000 + duration, [('cancel', False)])):
|
||||
cs.buttonEvents = [structs.CarState.ButtonEvent(type=kind, pressed=pressed) for kind, pressed in events]
|
||||
owner.update(cs, now_ns=stamp)
|
||||
assert owner.allowed_latch == (not any(cancel_actions))
|
||||
assert (owner._last_latch_edge_ns, owner.physical_arm_ns) == (edge, arm)
|
||||
assert not owner.independent_cruise_buttons # MAIN/LKAS semantics are not changed.
|
||||
|
||||
|
||||
@pytest.mark.parametrize('identity', ('palisade_hdai', 'palisade_hdaii'))
|
||||
@pytest.mark.parametrize('simultaneous', (None, 'lkas', 'mainCruise'))
|
||||
def test_cold_cancel_never_arms_or_renews_gesture(identity, simultaneous):
|
||||
owner = create_intent(profile(identity), AolSettings(True, 0., AOL_TOGGLE, 0, (0,0,0), (0,0,0)))
|
||||
cs = structs.CarState(canValid=True, gearShifter='drive')
|
||||
cs.cruiseState.available = True
|
||||
owner.update(cs, now_ns=1_000_000_000)
|
||||
buttons = [structs.CarState.ButtonEvent(type='cancel', pressed=True)]
|
||||
if simultaneous:
|
||||
buttons.append(structs.CarState.ButtonEvent(type=simultaneous, pressed=True))
|
||||
cs.buttonEvents = buttons
|
||||
owner.update(cs, now_ns=1_010_000_000)
|
||||
assert not owner.allowed_latch
|
||||
assert owner._last_latch_edge_ns == owner.physical_arm_ns == 0
|
||||
|
||||
|
||||
@pytest.mark.parametrize('identity', ('palisade_hdai', 'palisade_hdaii'))
|
||||
def test_card_retains_original_cancel_receipt_until_lateral_only_ack(identity):
|
||||
"""Real Card caller, synthetic transport; parser receipt is tested separately."""
|
||||
from unittest.mock import Mock, patch
|
||||
from opendbc.car.hyundai.blended_longitudinal import BlendedStartup, candidate_from_stock
|
||||
from opendbc.car.hyundai.tests.test_palisade_2023 import params as mixed_params
|
||||
from openpilot.common.params import Params
|
||||
from openpilot.common.prefix import OpenpilotPrefix
|
||||
from openpilot.starpilot.aol.tests.test_ioniq_availability import IoniqAvailabilityTests, car_state
|
||||
|
||||
case = IoniqAvailabilityTests(methodName='runTest')
|
||||
with OpenpilotPrefix():
|
||||
try:
|
||||
stock = mixed_params('hdaii' if identity == 'palisade_hdaii' else 'hdai')
|
||||
cp = candidate_from_stock(stock, alpha_requested=True, native_qualified=True)
|
||||
startup = BlendedStartup(stock, cp.as_reader().as_builder(), (Mock(), Mock()))
|
||||
startup.prepare(admission=lambda: True)
|
||||
case.addCleanup(startup.close)
|
||||
Params().put('LKASButtonControl', AOL_TOGGLE, block=True)
|
||||
sd, _, drive, card, receive = case._ioniq_availability_callers(True, cp=cp, startup_owner=startup)
|
||||
sd.aol_settings = card.aol_settings
|
||||
source = 10_020_000_000
|
||||
cs = car_state()
|
||||
cs.cruiseState.available = True
|
||||
cs.buttonEvents = [structs.CarState.ButtonEvent(type='cancel', pressed=True)]
|
||||
# Native withdrawal arrives before the next SelfdriveD publication.
|
||||
drive(2, native_request_lat=True, native_request_long=True, long_allowed=False,
|
||||
panda_controls_allowed=False)
|
||||
old_axis = sd.pm.messages['aolAxisState'].as_builder()
|
||||
old_axis.logMonoTime = old_axis.aolAxisState.observedMonoTime = source - 1_000_000
|
||||
old_axis.aolAxisState.sourceCarStateMonoTime = source - 1_000_000
|
||||
old_axis.aolAxisState.validUntilMonoTime = source + 29_000_000
|
||||
old_axis.aolAxisState.mode = 'combined'
|
||||
old_axis.aolAxisState.desiredLongitudinal = old_axis.aolAxisState.longitudinalActive = True
|
||||
with patch.object(card, 'aol_cancel_source', return_value=source):
|
||||
assert receive(source, axis=old_axis.as_reader(), state=cs)
|
||||
assert card.aol_transport_feedback.longitudinal_cancel_ns == source
|
||||
# No second physical press, no newer axis yet, now the cancellation event.
|
||||
event = log.Event.new_message(logMonoTime=source)
|
||||
event.init('onroadEvents', 1)
|
||||
event.onroadEvents[0].name = 'buttonCancel'
|
||||
event.onroadEvents[0].userDisable = True
|
||||
event.onroadEvents[0].noEntry = True
|
||||
cs.buttonEvents = []
|
||||
with patch.object(card, 'aol_cancel_source', return_value=0):
|
||||
assert receive(source + 10_000_000, event=event.as_reader(), state=cs)
|
||||
assert card.aol_transport_feedback.longitudinal_cancel_ns == source
|
||||
assert not card.aol_transport_feedback.failed
|
||||
# SelfdriveD receives the same physical press; its actual LAT-only output
|
||||
# and the native LAT-only request close the bounded withdrawal handshake.
|
||||
cancel = car_state()
|
||||
cancel.buttonEvents = [structs.CarState.ButtonEvent(type='cancel', pressed=True)]
|
||||
axis, state, command = drive(4, observed_state=cancel, native_request_lat=True,
|
||||
native_request_long=False, long_allowed=False, panda_controls_allowed=False)
|
||||
assert not state.enabled and axis.lateralActive and command.latActive
|
||||
assert not axis.longitudinalActive and not command.longActive
|
||||
with patch.object(card, 'aol_cancel_source', return_value=0):
|
||||
assert receive(source + 20_000_000, axis=sd.pm.messages['aolAxisState'],
|
||||
event=sd.pm.messages['onroadEvents'], state=cs)
|
||||
assert card.aol_card_intent.allowed_latch
|
||||
assert card.aol_transport_feedback.longitudinal_cancel_ns == 0
|
||||
finally:
|
||||
case.doCleanups()
|
||||
|
||||
|
||||
@pytest.mark.parametrize('identity', ('palisade_hdai', 'palisade_hdaii'))
|
||||
@pytest.mark.parametrize('fault', ('none', 'wrong_bus', 'stale', 'unmarked', 'stock'))
|
||||
def test_cancel_source_is_bound_to_real_mixed_parser_receipt(identity, fault):
|
||||
from opendbc.can import CANPacker
|
||||
from opendbc.car import Bus
|
||||
from opendbc.car.hyundai.interface import CarInterface
|
||||
from openpilot.selfdrive.car.card import Car
|
||||
cp = profile(identity)
|
||||
if fault == 'unmarked':
|
||||
cp.alternativeExperience = 0
|
||||
elif fault == 'stock':
|
||||
cp.openpilotLongitudinalControl, cp.pcmCruise = False, True
|
||||
cp.safetyConfigs[0].safetyParam &= ~4
|
||||
ci = CarInterface(cp)
|
||||
parser = ci.can_parsers[Bus.pt]
|
||||
packer = CANPacker(parser.dbc.name)
|
||||
for tick in range(15):
|
||||
now = 2_000_000_000 + tick * 20_000_000
|
||||
packet = packer.make_can_msg('CLU11', 2 if fault == 'wrong_bus' else parser.bus,
|
||||
{'CF_Clu_CruiseSwState': 4 if tick == 14 else 0, 'CF_Clu_AliveCnt1': tick})
|
||||
cs = ci.update([(now, [packet])])
|
||||
if fault == 'wrong_bus':
|
||||
cs.buttonEvents = [structs.CarState.ButtonEvent(type='cancel', pressed=True)]
|
||||
received = now - 30_000_001 if fault == 'stale' else now
|
||||
caller = SimpleNamespace(CI=ci, _aol_can_observation=(cs, received, now, now))
|
||||
assert Car.aol_cancel_source(cast(Car, caller), cs, now) == (now if fault == 'none' else 0)
|
||||
@@ -63,18 +63,11 @@ def test_native_eps_denial_retains_intent_without_renewing_permission(gas, withd
|
||||
'door', 'sensors', 'disengage', 'reverse', 'acc_fault', 'native_invalid', 'native_fault', 'native_heartbeat',
|
||||
'wrong_session', 'wrong_producer', 'wrong_epoch', 'expired', 'warning_stale', 'native_stale',
|
||||
'native_missing', 'request_off', 'long_request', 'hard_event', 'other_user'))
|
||||
@pytest.mark.parametrize('brake', (False, True))
|
||||
def test_eps_retention_does_not_mask_independent_vetoes(fault, brake):
|
||||
def test_eps_retention_does_not_mask_independent_vetoes(fault):
|
||||
cp, state, feedback = baseline()
|
||||
state.steerFaultTemporary = True
|
||||
state.brakePressed = brake
|
||||
now = NOW + 10_000_000
|
||||
sm, native = sample(cp, now, now, 2, allowed=False)
|
||||
if brake:
|
||||
event = sm.messages['onroadEvents'].as_builder()
|
||||
event.onroadEvents = [{'name': 'pcmDisable', 'userDisable': True},
|
||||
{'name': 'pedalPressed', 'userDisable': True}]
|
||||
sm.messages['onroadEvents'] = event.as_reader()
|
||||
physical = {'no_eps': ('steerFaultTemporary', False), 'permanent_eps': ('steerFaultPermanent', True),
|
||||
'invalid_can': ('canValid', False), 'timeout': ('canTimeout', True), 'door': ('doorOpen', True),
|
||||
'sensors': ('vehicleSensorsInvalid', True), 'disengage': ('steeringDisengage', True), 'acc_fault': ('accFaulted', True)}
|
||||
@@ -209,28 +202,3 @@ def test_continuous_warning_keeps_intent_but_gap_or_stale_source_cannot_renew_it
|
||||
if stale_source:
|
||||
change(sm, 'aolAxisState', lambda axis: setattr(axis, 'sourceCarStateMonoTime', NOW))
|
||||
assert feedback.observe(sm, cp, state, now, latched=True, intent_producer_session=PRODUCER)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('axis_first', (False, True))
|
||||
def test_brake_warning_event_change_preserves_existing_eps_intent(axis_first):
|
||||
cp, state, feedback = baseline()
|
||||
state.steerFaultTemporary = state.brakePressed = True
|
||||
before = leases(feedback)
|
||||
warning = NOW + 10_000_000
|
||||
for sequence in range(2, 10):
|
||||
now = NOW + (sequence - 1) * 10_000_000
|
||||
event_stamp = warning if sequence < 6 else NOW + 50_000_000
|
||||
axis_stamp = warning if sequence < 6 or sequence == 6 and not axis_first else NOW + 50_000_000
|
||||
sm, native = sample(cp, now, now, sequence, active=False, requested=sequence == 2, allowed=False, event_stamp=event_stamp)
|
||||
change(sm, 'aolAxisState', lambda axis, stamp=axis_stamp: setattr(axis, 'faultEventMonoTime', stamp))
|
||||
event = sm.messages['onroadEvents'].as_builder()
|
||||
event.onroadEvents = [{'name': 'pcmDisable', 'userDisable': True},
|
||||
{'name': 'pedalPressed', 'userDisable': True},
|
||||
{'name': 'steerTempUnavailableSilent', 'warning': True}]
|
||||
if sequence == 6 and axis_first:
|
||||
event.logMonoTime = warning
|
||||
sm.logMonoTime['onroadEvents'] = warning
|
||||
sm.messages['onroadEvents'] = event.as_reader()
|
||||
assert not feedback.observe(sm, cp, state, now, latched=True, intent_producer_session=PRODUCER)
|
||||
assert not feedback.failed and leases(feedback) == before
|
||||
assert not native.lateralAllowed and not native.longitudinalAllowed
|
||||
|
||||
@@ -122,19 +122,6 @@ class TestHighlanderAol(unittest.TestCase):
|
||||
self._assert_card_full_axis_transport(car, True, prepared_cp=cp,
|
||||
filter_disabled=disabled, late_disable=late)
|
||||
|
||||
def test_continental_radar_metadata_keeps_exact_stock_owner(self):
|
||||
for car in (CAR.TOYOTA_CHR_TSS2, CAR.TOYOTA_RAV4_TSS2_2022):
|
||||
for alpha in (False, True):
|
||||
with self.subTest(car=car, alpha=alpha):
|
||||
cp = CarInterface.get_params(car, gen_empty_fingerprint(), [], alpha, False, False)
|
||||
cp.flags |= int(ToyotaFlags.ARS510_RADAR)
|
||||
expected = car == CAR.TOYOTA_RAV4_TSS2_2022 and not alpha
|
||||
self.assertEqual(qualified(cp), expected)
|
||||
cp.alternativeExperience = 32
|
||||
self.assertEqual(qualified(cp, marked_only=True), expected)
|
||||
cp.flags |= int(ToyotaFlags.ARS510_FUSED)
|
||||
self.assertFalse(qualified(cp))
|
||||
|
||||
def test_radar_torque_factory_stock_and_alpha_transport(self):
|
||||
from opendbc.car.toyota.carcontroller import CarController
|
||||
from opendbc.car.toyota.carstate import CarState
|
||||
|
||||
@@ -9,7 +9,7 @@ from openpilot.cereal import log
|
||||
from openpilot.selfdrive.car.car_events import CarEvents
|
||||
from openpilot.starpilot.aol.tests.test_gm_cc_lateral_minimum import setup
|
||||
from openpilot.starpilot.aol.tests.test_gm_parked_startup import _exercise_parked_main
|
||||
from openpilot.starpilot.car.gm.aol import independent_lateral_below_engage
|
||||
from openpilot.starpilot.car.gm.aol import cc_independent_lateral_below_engage
|
||||
|
||||
|
||||
def params(f1):
|
||||
@@ -24,9 +24,9 @@ def test_volt_cc_steering_minimum_keeps_cruise_minimum(f1):
|
||||
_, intent = setup()
|
||||
assert cp.minSteerSpeed == pytest.approx(7 * CV.MPH_TO_MS)
|
||||
assert cp.minEnableSpeed == pytest.approx(24 * CV.MPH_TO_MS)
|
||||
assert independent_lateral_below_engage(cp, intent, enabled=False, active=False)
|
||||
assert not independent_lateral_below_engage(cp, replace(intent, allowedLatch=False), enabled=False, active=False)
|
||||
assert not independent_lateral_below_engage(cp, intent, enabled=True, active=True)
|
||||
assert cc_independent_lateral_below_engage(cp, intent, enabled=False, active=False)
|
||||
assert not cc_independent_lateral_below_engage(cp, replace(intent, allowedLatch=False), enabled=False, active=False)
|
||||
assert not cc_independent_lateral_below_engage(cp, intent, enabled=True, active=True)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('f1', [False, True])
|
||||
|
||||
@@ -114,7 +114,7 @@ def stock_lateral_user_event(cp):
|
||||
|
||||
|
||||
def stock_lateral_off(cp, cs, native, *, allow_independent_cancel=False, allow_independent_brake=False,
|
||||
allow_independent_gas=False, allow_independent_main=False):
|
||||
allow_independent_gas=False):
|
||||
from openpilot.starpilot.nostalgia import physical_cancel
|
||||
from opendbc.car.hyundai.canfd_angle_aol import qualified as angle_qualified
|
||||
|
||||
@@ -127,20 +127,18 @@ def stock_lateral_off(cp, cs, native, *, allow_independent_cancel=False, allow_i
|
||||
|
||||
stock_gas = bool(cp.brand == 'gm' and qualified_gm(cp) and cp.pcmCruise and not cp.openpilotLongitudinalControl or
|
||||
cp.brand == 'subaru' and subaru_qualified(cp, marked_only=True))
|
||||
from opendbc.car.hyundai.classic_scc_aol import qualified as classic_stock_qualified
|
||||
classic_stock = classic_stock_qualified(cp, marked_only=True)
|
||||
independent_angle = angle_qualified(cp, marked_only=True)
|
||||
independent_brake = independent_angle or (allow_independent_brake and (
|
||||
toyota_qualified(cp, marked_only=True) and not cs.cruiseState.enabled or
|
||||
cp.brand == 'hyundai' and len(cp.safetyConfigs) == 1 and (stock_canfd_qualified(cp, marked_only=True) or classic_stock)))
|
||||
return bool(cp.pcmCruise and (cs.cruiseState.available or allow_independent_main and classic_stock) and
|
||||
cp.brand == 'hyundai' and len(cp.safetyConfigs) == 1 and stock_canfd_qualified(cp, marked_only=True)))
|
||||
return bool(cp.pcmCruise and cs.cruiseState.available and
|
||||
(not cs.cruiseState.enabled or independent_brake and allow_independent_brake and cs.brakePressed) and
|
||||
cs.canValid and not cs.canTimeout and not cs.steerFaultPermanent and not cs.steerFaultTemporary and
|
||||
not cs.accFaulted and not cs.doorOpen and not cs.seatbeltUnlatched and
|
||||
(not cs.brakePressed or independent_brake and allow_independent_brake) and
|
||||
not cs.regenBraking and (not cs.gasPressed or independent_angle or
|
||||
allow_independent_gas and (stock_gas or toyota_qualified(cp, marked_only=True) or
|
||||
cp.brand == 'hyundai' and (stock_canfd_qualified(cp, marked_only=True) or classic_stock))) and
|
||||
cp.brand == 'hyundai' and stock_canfd_qualified(cp, marked_only=True))) and
|
||||
(not physical_cancel(cs, cp) or independent_angle and allow_independent_cancel) and
|
||||
native is not None and native.requestedLateral and native.lateralAllowed and not native.requestedLongitudinal)
|
||||
|
||||
@@ -364,7 +362,6 @@ class TransportPauseFeedback:
|
||||
early_long = gm or cp.brand == 'hyundai' and early_native_long_withdrawal_supported(cp) or ev9 or ford_withdrawal or ford_long
|
||||
from openpilot.starpilot.car.gm.aol import f1_conventional_lateral_owner, gm_physical_pedal_withdrawal
|
||||
gm_pedal = gm_physical_pedal_withdrawal(cp, cs) and not f1_conventional_lateral_owner(cp)
|
||||
classic_brake = classic_brake_withdrawal(cp, cs)
|
||||
event_ns = int(sm.logMonoTime['onroadEvents'])
|
||||
main_off_recovery = bool(0 < event_ns <= self.ccnc_main_off_source_ns <= now_ns and
|
||||
now_ns - self.ccnc_main_off_source_ns <= AXIS_MAX_AGE_NS)
|
||||
@@ -389,7 +386,7 @@ class TransportPauseFeedback:
|
||||
now_ns - int(sm.logMonoTime['onroadEvents']) > 1_500_000_000 or
|
||||
any(event.immediateDisable or event.softDisable or
|
||||
event.userDisable and not (ev9_main_off and event.name == log.OnroadEvent.EventName.wrongCarMode or
|
||||
(gm_pedal or classic_brake) and event.name == log.OnroadEvent.EventName.pedalPressed or
|
||||
(gm_pedal or classic_brake_withdrawal(cp, cs)) and event.name == log.OnroadEvent.EventName.pedalPressed or
|
||||
ford_withdrawal and event.name in (log.OnroadEvent.EventName.pedalPressed, log.OnroadEvent.EventName.pcmDisable,
|
||||
log.OnroadEvent.EventName.buttonCancel))
|
||||
for event in sm['onroadEvents'])):
|
||||
@@ -397,13 +394,13 @@ class TransportPauseFeedback:
|
||||
native = independent_lateral_health(sm, cp, cs, self.session, now_ns)
|
||||
# Separate health may report brake withdrawal before the matching axis status.
|
||||
# Retain only the existing LAT intent; LONG still follows its physical pedal.
|
||||
brake_health = bool((ford_withdrawal or classic_brake) and cs.brakePressed and not cs.gasPressed and
|
||||
ford_brake_health = bool(ford_withdrawal and cs.brakePressed and not cs.gasPressed and
|
||||
native is not None and native.sourcePandaStatesMonoTime > 0 and
|
||||
native.sourcePandaStatesMonoTime < int(sm.logMonoTime['pandaStates']) and
|
||||
all(not slot.controlsAllowed for slot, config in zip(sm['pandaStates'], cp.safetyConfigs, strict=True)
|
||||
if str(config.safetyModel) not in ('silent', 'noOutput')))
|
||||
if (native is None or not native.pandaInventory or not native.requestedLongitudinal or
|
||||
native.longitudinalAllowed and not brake_health or
|
||||
native.longitudinalAllowed and not ford_brake_health or
|
||||
native.observedMonoTime < self.lateral_native_ns or
|
||||
(native.pandaSerial, tuple(slot.hardwareSerial for slot in native.pandaInventory)) != self.lateral_inventory):
|
||||
return None
|
||||
@@ -575,11 +572,6 @@ class TransportPauseFeedback:
|
||||
not axis.desiredLongitudinal and not axis.longitudinalActive and
|
||||
all(event.name in (stock_user, log.OnroadEvent.EventName.pedalPressed) and
|
||||
not event.softDisable and not event.immediateDisable for event in users))
|
||||
from openpilot.starpilot.car.subaru.aol import temporary_eps_brake_recovery as subaru_eps_brake_recovery
|
||||
stock_temporary_user |= bool(eps_recovery and self.lateral_only_baseline and subaru_eps_brake_recovery(cp, cs) and users and
|
||||
not axis.desiredLongitudinal and not axis.longitudinalActive and
|
||||
all(event.name in (stock_user, log.OnroadEvent.EventName.pedalPressed) and
|
||||
not event.softDisable and not event.immediateDisable for event in users))
|
||||
from openpilot.starpilot.car.gm.aol import f1_conventional_lateral_owner, gm_physical_pedal_withdrawal
|
||||
# Receipt-bound GM owns F1 physical withdrawal.
|
||||
pedal = bool(not f1_conventional_lateral_owner(cp) and
|
||||
@@ -813,12 +805,11 @@ class TransportPauseFeedback:
|
||||
def _longitudinal_cancel_current(self, sm, cp, cs, axis, now_ns, *, latched, cancel_source_ns, withdrawal_required=True) -> SafetyState | None:
|
||||
from opendbc.car.hyundai.canfd_stock_aol import qualified_long as qualified
|
||||
from openpilot.starpilot.car.hyundai.aol import qualified_ccnc_long_aol as ev9_qualified
|
||||
from opendbc.car.hyundai.blended_stock_aol import qualified_alpha as mixed_qualified
|
||||
from opendbc.car.structs import car
|
||||
|
||||
if (not latched or self.failed or self.paused or self.pending_event or
|
||||
not self.session or str(axis.sessionId) != self.session or
|
||||
not (qualified(cp, marked_only=True) or ev9_qualified(cp, marked_only=True) or mixed_qualified(cp, marked_only=True)) or
|
||||
not (qualified(cp, marked_only=True) or ev9_qualified(cp, marked_only=True)) or
|
||||
not self.lateral_baseline_ns or not 0 <= now_ns - self.lateral_baseline_ns <= AXIS_MAX_AGE_NS or
|
||||
str(axis.faultReason) != 'none' or not axis.nativeAcknowledged or
|
||||
not axis.desiredLateral or not axis.lateralActive or cs.accFaulted or
|
||||
|
||||
Binary file not shown.
@@ -210,23 +210,11 @@ class FordBluePilotController(BluePilotLateral):
|
||||
if self.starpilot_selected:
|
||||
angle = cast("StarPilotFordController", self.angle)
|
||||
angle.native_curvature_bounds = None
|
||||
if self.measured_seen and CS.out.vEgoRaw > 10.:
|
||||
if self.native_pinion_model is not None:
|
||||
# Pinion native limits use 150 CAN curvature units, without learned geometry.
|
||||
error = 150 * .00002
|
||||
low = -max(self.measured_history) * .00002 - error
|
||||
high = -min(self.measured_history) * .00002 + error
|
||||
else:
|
||||
# An old yaw extreme can expire before the next steering packet consumes its metadata.
|
||||
history = list(self.measured_history)
|
||||
sample_step = max((abs(b - a) for a, b in zip(history[:-1], history[1:], strict=True)), default=0)
|
||||
# Native RX may advance one sample before the host receives the same batch.
|
||||
error = max(0., CarControllerParams.CURVATURE_ERROR - (sample_step + 1) * .00002)
|
||||
current_native = -self.measured_history[-1] * .00002
|
||||
low = current_native - error
|
||||
high = current_native + error
|
||||
low = max(low, measured - CarControllerParams.CURVATURE_ERROR)
|
||||
high = min(high, measured + CarControllerParams.CURVATURE_ERROR)
|
||||
if self.native_pinion_model is not None and self.measured_seen and CS.out.vEgoRaw > 10.:
|
||||
# Pinion native limits use 150 CAN curvature units, without learned geometry.
|
||||
error = 150 * .00002
|
||||
low = max(-max(self.measured_history) * .00002 - error, measured - CarControllerParams.CURVATURE_ERROR)
|
||||
high = min(-min(self.measured_history) * .00002 + error, measured + CarControllerParams.CURVATURE_ERROR)
|
||||
# Tighten the host envelope only where both measurements agree on a command.
|
||||
if low <= high:
|
||||
angle.native_curvature_bounds = (low, high)
|
||||
|
||||
@@ -57,7 +57,6 @@ _STALL_HOLD_S = 0.5 # accumulated clip-binding time before a pulse fire
|
||||
_STALL_BLIP_FRAMES = 6 # mode-0 pulse length (6 frames @ 20 Hz = 300 ms)
|
||||
_STALL_COOLDOWN_S = 2.0 # re-arm delay after a pulse (release ramp + PSCM response time)
|
||||
_STALL_MAX_BLIPS = 3 # give up on a stuck episode; devLim telemetry keeps recording the stall
|
||||
_STALL_PROGRESS_EPS = 0.00002 # one native CAN curvature unit
|
||||
# The pulse releases steering for 300 ms; never fire it in a curve.
|
||||
_BLIP_MAX_PATH_ANGLE = 0.10 # rad
|
||||
|
||||
@@ -139,7 +138,6 @@ class StarPilotFordController:
|
||||
self.stall_blip_frames_left = 0 # remaining pulse frames; > 0 -> mode 0 on the wire
|
||||
self.stall_blip_cooldown_s = 0.0 # re-arm delay after a pulse
|
||||
self.stall_blip_count = 0 # pulses fired this stall episode
|
||||
self.stall_measured_last: float | None = None
|
||||
self.angle_stall_blip_active = False
|
||||
|
||||
def update_angle_params(self, params):
|
||||
@@ -205,7 +203,6 @@ class StarPilotFordController:
|
||||
self.stall_blip_frames_left = 0
|
||||
self.stall_blip_cooldown_s = 0.0
|
||||
self.stall_blip_count = 0
|
||||
self.stall_measured_last = None
|
||||
self.angle_stall_blip_active = False
|
||||
self.shared.precision_type = 1
|
||||
return LateralResult(
|
||||
@@ -244,7 +241,6 @@ class StarPilotFordController:
|
||||
self.stall_blip_frames_left = 0
|
||||
self.stall_blip_cooldown_s = 0.0
|
||||
self.stall_blip_count = 0
|
||||
self.stall_measured_last = None
|
||||
self.angle_stall_blip_active = False
|
||||
self.shared.precision_type = 1
|
||||
return LateralResult(
|
||||
@@ -257,17 +253,6 @@ class StarPilotFordController:
|
||||
lateralUncertainty=0.0,
|
||||
)
|
||||
|
||||
progress = 0.0 if self.stall_measured_last is None else measured - self.stall_measured_last
|
||||
steering_responding = (abs(progress) > _STALL_PROGRESS_EPS and
|
||||
progress * (float(actuators.curvature) - measured) > 0.0)
|
||||
self.stall_measured_last = measured
|
||||
if steering_responding:
|
||||
# A clipped tracking correction is not a stall while the car is responding.
|
||||
self.stall_blip_hold_s = 0.0
|
||||
if self.stall_blip_frames_left > 0 and not self.angle_stall_blip_active:
|
||||
self.stall_blip_frames_left = 0
|
||||
self.stall_blip_count = max(0, self.stall_blip_count - 1)
|
||||
|
||||
# Stall-blip pulse in progress: hold lateral inactive (mode 0, all-zero signals -- the same
|
||||
# wire pattern as the human-turn override, no ford.h involvement) for _STALL_BLIP_FRAMES so the
|
||||
# PSCM drops its post-override attenuation, then release; path_angle ramps back in from zero
|
||||
@@ -454,8 +439,7 @@ class StarPilotFordController:
|
||||
and abs(_stall_gap) > _STALL_GAP_MIN
|
||||
and abs(desired_curvature) > abs(current_curvature))
|
||||
if _stalled:
|
||||
if (self.shared.bp_curvature_deviation_limited and self.stall_blip_cooldown_s <= 0.0 and
|
||||
not steering_responding):
|
||||
if self.shared.bp_curvature_deviation_limited and self.stall_blip_cooldown_s <= 0.0:
|
||||
self.stall_blip_hold_s += _STEER_DT
|
||||
if (self.stall_blip_hold_s >= _STALL_HOLD_S and self.stall_blip_count < _STALL_MAX_BLIPS
|
||||
and abs(self.shared.path_angle_last) < _BLIP_MAX_PATH_ANGLE):
|
||||
|
||||
@@ -28,10 +28,11 @@ def native_accepts_cp(cp, model: int, param: int) -> bool:
|
||||
native_profile_supported(model, param))
|
||||
|
||||
|
||||
def independent_lateral_below_engage(cp, intent, *, enabled, active):
|
||||
"""An armed independent GM LAT request uses its steering speed gate."""
|
||||
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, is_ordinary_cc_profile
|
||||
return bool(not enabled and not active and qualified_gm(cp) and aol_enabled(cp) and
|
||||
not cp.flags & GMFlags.PEDAL_LONG 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)
|
||||
|
||||
|
||||
@@ -388,8 +388,7 @@ def physical_cancel_lateral_allowed(CP, CS, intent, native, *, settings, previou
|
||||
not any(e.type == car.CarState.ButtonEvent.Type.cancel for e in CS.buttonEvents)):
|
||||
return False
|
||||
ev6_long = CP.carFingerprint == HyundaiCAR.KIA_EV6 and qualified_canfd_long(CP, marked_only=True)
|
||||
return bool((ev6_long or qualified_blended_alpha(CP, marked_only=True) or
|
||||
independent_cruise_buttons_supported(CP, marked_only=True)) and
|
||||
return bool((ev6_long or independent_cruise_buttons_supported(CP, marked_only=True)) and
|
||||
previous is not None and previous.lateral_active and previous.native_acknowledged and
|
||||
intent is not None and intent.settingsQualified and intent.allowedLatch and intent.lateralArmed and
|
||||
not intent.pauseLateral and intent.producerSessionId and intent.carStateLogMonoTime == source_ns and
|
||||
@@ -428,10 +427,9 @@ def retained_shared_long_cancel_intent(CP, CS, sm, feedback, owner, *, cancel_so
|
||||
from openpilot.starpilot.car.hyundai.ev6_intent import Ev6CardIntent
|
||||
ev6_long = bool(isinstance(owner, Ev6CardIntent) and owner._buttons_current and
|
||||
owner.physical.authorized and owner._last_latch_edge_ns > 0)
|
||||
mixed_long = qualified_blended_alpha(CP, marked_only=True)
|
||||
if (not (ev6_long or mixed_long or qualified_sonata_independent(CP) or qualified_ccnc_long_aol(CP, marked_only=True) or
|
||||
if (not (ev6_long or qualified_sonata_independent(CP) or qualified_ccnc_long_aol(CP, marked_only=True) or
|
||||
CP.carFingerprint in INDEPENDENT_LONG_BUTTON_CARS and qualified_canfd_long(CP, marked_only=True)) or
|
||||
owner is None or not owner.explicit_latch or not (owner.independent_cruise_buttons or ev6_long or mixed_long) or
|
||||
owner is None or not owner.explicit_latch or not (owner.independent_cruise_buttons or ev6_long) or
|
||||
not owner.allowed_latch or owner.pause_lateral or owner.pause_longitudinal or owner._fault_inhibit):
|
||||
return False
|
||||
settings = owner.settings
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
from opendbc.car.hyundai.classic_long_aol import independent_buttons_qualified
|
||||
from opendbc.car.hyundai.blended_stock_aol import qualified_alpha
|
||||
from openpilot.starpilot.aol.intent import AolCardIntent
|
||||
|
||||
|
||||
@@ -8,8 +7,7 @@ class ClassicLongCardIntent(AolCardIntent):
|
||||
|
||||
def __init__(self, cp, settings):
|
||||
super().__init__(settings, explicit_latch=True, independent_cruise_buttons=(
|
||||
independent_buttons_qualified(cp, marked_only=False)),
|
||||
preserve_unassigned_cancel=qualified_alpha(cp))
|
||||
independent_buttons_qualified(cp, marked_only=False)))
|
||||
self.requires_fault_observation = True
|
||||
self.observe_stock_engagement = True
|
||||
|
||||
|
||||
@@ -97,12 +97,3 @@ def sng_standstill_lateral_inhibit(cp, state):
|
||||
# All stock Subaru native profiles require movement. Withdraw the request before its expected denial,
|
||||
# preserving ordinary stock ACC without granting either active axis.
|
||||
return bool(qualified(cp, marked_only=True) and state.standstill)
|
||||
|
||||
|
||||
def temporary_eps_brake_recovery(cp, state):
|
||||
return bool(qualified(cp, marked_only=True) and state.canValid and not state.canTimeout and
|
||||
not state.steerFaultPermanent and not state.accFaulted and not state.doorOpen and
|
||||
not state.vehicleSensorsInvalid and not state.steeringDisengage and not state.buttonEvents and
|
||||
state.cruiseState.available and not state.cruiseState.enabled and
|
||||
state.gearShifter == structs.CarState.GearShifter.drive and
|
||||
state.brakePressed and not state.regenBraking and not state.gasPressed)
|
||||
|
||||
@@ -25,7 +25,7 @@ from openpilot.starpilot.car.subaru.tests.native_fixture import subaru_native_pa
|
||||
def test_actual_full_engage_brake_reverse_temporary_recovery_mainoff(candidate, subaru_native_pair, tmp_path, aol=True, manual=False, stop_resume=False,
|
||||
hard_stop_native_first=False, main_only=False, preferences=None, firmware=None,
|
||||
deadline_gap=False, fixed_angle_handoff=False, host_first_stop=False,
|
||||
source_outage=False, vm_angle_handoff=0, crawl=False, eps_brake_join=False):
|
||||
source_outage=False, vm_angle_handoff=0):
|
||||
angle_handoff = fixed_angle_handoff or vm_angle_handoff
|
||||
fw = firmware_records(candidate, FW_VERSIONS, 'subaru') if firmware is None else firmware
|
||||
rows = []
|
||||
@@ -68,11 +68,10 @@ def test_actual_full_engage_brake_reverse_temporary_recovery_mainoff(candidate,
|
||||
gap_tick = 75 if main_only else 210
|
||||
last_card_published = {}
|
||||
pending_host_can = []
|
||||
pending_eps_event = None
|
||||
for elapsed, tick in enumerate(ticks):
|
||||
now = base + elapsed * 10_000_000 + (22_504_479 if deadline_gap and elapsed >= gap_tick else 0)
|
||||
cruise = not main_only and (30 <= tick < 110 or tick >= 410)
|
||||
brake = 90 <= tick < 110 or eps_brake_join and 225 <= tick < 270
|
||||
brake = 90 <= tick < 110
|
||||
reverse = 150 <= tick < 185
|
||||
temporary = 225 <= tick < 260
|
||||
main = not 310 <= tick < 350 and (tick >= 30 or not main_only)
|
||||
@@ -84,11 +83,10 @@ def test_actual_full_engage_brake_reverse_temporary_recovery_mainoff(candidate,
|
||||
'Steering_Torque': {'Steer_Warning': int(temporary),
|
||||
'Steer_Torque_Sensor': (250 if 70 <= tick < 76 else
|
||||
80 if vm_angle_handoff and 76 <= tick < 90 else
|
||||
143 if 76 <= tick < 90 else 0) if angle_handoff else 94 if eps_brake_join and 235 <= tick < 237 else 0},
|
||||
143 if 76 <= tick < 90 else 0) if angle_handoff else 0},
|
||||
'Steering_2': {'Steering_Angle': (vm_angle_handoff * (5.65 if tick < 78 else 5.60) if vm_angle_handoff else
|
||||
(5.65 if tick < 78 else 5.61) if fixed_angle_handoff else 0)},
|
||||
'Wheel_Speeds': dict.fromkeys(('FL', 'FR', 'RL', 'RR'),
|
||||
0 if stopped else 98.2 if vm_angle_handoff else 121.5 if fixed_angle_handoff else 7.2 if crawl else 54),
|
||||
'Wheel_Speeds': dict.fromkeys(('FL', 'FR', 'RL', 'RR'), 0 if stopped else 98.2 if vm_angle_handoff else 121.5 if fixed_angle_handoff else 54),
|
||||
'Brake_Status': {'Brake': int(brake)},
|
||||
'CruiseControl': {'Cruise_On': int(main), 'Cruise_Activated': int(cruise)},
|
||||
'ES_Status': {'Cruise_Activated': int(cruise)},
|
||||
@@ -122,17 +120,7 @@ def test_actual_full_engage_brake_reverse_temporary_recovery_mainoff(candidate,
|
||||
peers.append(panda.as_reader())
|
||||
if 'carControl' in latest_controls:
|
||||
peers.append(latest_controls['carControl'])
|
||||
if pending_eps_event is not None:
|
||||
peers.append(pending_eps_event)
|
||||
pending_eps_event = None
|
||||
for service in ('onroadEvents', 'selfdriveState', 'aolAxisState'):
|
||||
if service not in driven_new or service not in card.sm.data:
|
||||
continue
|
||||
if eps_brake_join and service == 'onroadEvents' and tick == 236:
|
||||
# Deliver the existing event one Card cycle after its axis update.
|
||||
pending_eps_event = driven_new[service]
|
||||
else:
|
||||
peers.append(driven_new[service])
|
||||
peers.extend(driven_new[s] for s in ('onroadEvents', 'selfdriveState', 'aolAxisState') if s in driven_new and s in card.sm.data)
|
||||
if receipt is not None and (tick % 10 == 1 or hard_stop_receipt and not host_first_stop or host_first_stop and elapsed == 127):
|
||||
peers.append(receipt.as_reader())
|
||||
card.sm.update_msgs((now + 1000) / 1e9, peers)
|
||||
@@ -231,9 +219,6 @@ def test_actual_full_engage_brake_reverse_temporary_recovery_mainoff(candidate,
|
||||
request = bool(decoder.vl[steering_name]['LKAS_Request'])
|
||||
output = decoder.vl[steering_name]['LKAS_Output']
|
||||
decoded_steering.append({'request': request, 'output': output, 'nativeAccepted': accepted})
|
||||
if crawl and 95 <= tick < 105:
|
||||
assert aol and main_only and cs.brakePressed and not command.enabled
|
||||
assert request and accepted, ('CRAWL_AOL_BRAKE_WITHDREW', tick, decoded_steering)
|
||||
if not stopped and (65 <= tick < 90 and not (source_outage and 75 <= tick < 84) or
|
||||
aol and (first_resume or 205 <= tick < 220 or 280 <= tick < 300)):
|
||||
if angle_handoff and 72 <= tick < 78:
|
||||
@@ -444,13 +429,3 @@ def test_actual_held_gas_source_outage_withdraws_and_recovers_subaru(candidate,
|
||||
def test_actual_vm_angle_driver_handoff_keeps_native_rate_limit(candidate, direction, subaru_native_pair, tmp_path):
|
||||
test_actual_full_engage_brake_reverse_temporary_recovery_mainoff(
|
||||
candidate, subaru_native_pair, tmp_path, vm_angle_handoff=direction)
|
||||
|
||||
|
||||
def test_actual_crawl_lateral_only_tracks_small_targets(subaru_native_pair, tmp_path):
|
||||
test_actual_full_engage_brake_reverse_temporary_recovery_mainoff(
|
||||
CAR.SUBARU_CROSSTREK_2025, subaru_native_pair, tmp_path, main_only=True, crawl=True)
|
||||
|
||||
|
||||
def test_actual_eps_brake_event_join_preserves_armed_intent(subaru_native_pair, tmp_path):
|
||||
test_actual_full_engage_brake_reverse_temporary_recovery_mainoff(
|
||||
CAR.SUBARU_IMPREZA_2020, subaru_native_pair, tmp_path, main_only=True, eps_brake_join=True)
|
||||
|
||||
@@ -21,9 +21,7 @@ class TeslaScreenCardIntent(AolCardIntent):
|
||||
super().update(filtered.as_reader(), **kwargs)
|
||||
engaged = bool(CS.cruiseState.enabled)
|
||||
available = bool(CS.cruiseState.available)
|
||||
# Stock cruise availability does not revoke an explicit screen selection.
|
||||
if (engaged and not self.previous_enabled) or (self.previous_available and not available and not CS.brakePressed and
|
||||
self.screen_override is not True):
|
||||
if (engaged and not self.previous_enabled) or (self.previous_available and not available and not CS.brakePressed):
|
||||
self.screen_override = None if engaged and not self.previous_enabled else False
|
||||
self.brake_blocked = False
|
||||
self.pause_lateral = False
|
||||
|
||||
+157
-1
@@ -101,7 +101,7 @@
|
||||
"discriminantOffset": 0
|
||||
},
|
||||
"new_storage": {
|
||||
"dataWordCount": 18,
|
||||
"dataWordCount": 21,
|
||||
"pointerCount": 22,
|
||||
"preferredListEncoding": "inlineComposite",
|
||||
"isGroup": false,
|
||||
@@ -252,6 +252,78 @@
|
||||
"ordinal": {
|
||||
"explicit": 58
|
||||
}
|
||||
},
|
||||
{
|
||||
"slot": {
|
||||
"offset": 141,
|
||||
"type": {
|
||||
"bool": null
|
||||
},
|
||||
"defaultValue": {
|
||||
"bool": false
|
||||
},
|
||||
"hadExplicitDefault": false
|
||||
},
|
||||
"name": "acceptedIsPreview",
|
||||
"codeOrder": 59,
|
||||
"discriminantValue": 65535,
|
||||
"ordinal": {
|
||||
"explicit": 59
|
||||
}
|
||||
},
|
||||
{
|
||||
"slot": {
|
||||
"offset": 18,
|
||||
"type": {
|
||||
"uint64": null
|
||||
},
|
||||
"defaultValue": {
|
||||
"uint64": 0
|
||||
},
|
||||
"hadExplicitDefault": false
|
||||
},
|
||||
"name": "wheelOverrideSince",
|
||||
"codeOrder": 60,
|
||||
"discriminantValue": 65535,
|
||||
"ordinal": {
|
||||
"explicit": 60
|
||||
}
|
||||
},
|
||||
{
|
||||
"slot": {
|
||||
"offset": 19,
|
||||
"type": {
|
||||
"uint64": null
|
||||
},
|
||||
"defaultValue": {
|
||||
"uint64": 0
|
||||
},
|
||||
"hadExplicitDefault": false
|
||||
},
|
||||
"name": "wheelAdoptMonoTime",
|
||||
"codeOrder": 61,
|
||||
"discriminantValue": 65535,
|
||||
"ordinal": {
|
||||
"explicit": 61
|
||||
}
|
||||
},
|
||||
{
|
||||
"slot": {
|
||||
"offset": 40,
|
||||
"type": {
|
||||
"float32": null
|
||||
},
|
||||
"defaultValue": {
|
||||
"float32": 0.0
|
||||
},
|
||||
"hadExplicitDefault": false
|
||||
},
|
||||
"name": "wheelAdoptTargetMps",
|
||||
"codeOrder": 62,
|
||||
"discriminantValue": 65535,
|
||||
"ordinal": {
|
||||
"explicit": 62
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -764,6 +836,90 @@
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"0x9ccdc8676701b412": {
|
||||
"kind": "struct",
|
||||
"before_name": "SlcAction",
|
||||
"after_name": "SlcAction",
|
||||
"old_field_count": 8,
|
||||
"old_storage": {
|
||||
"dataWordCount": 4,
|
||||
"pointerCount": 4,
|
||||
"preferredListEncoding": "inlineComposite",
|
||||
"isGroup": false,
|
||||
"discriminantCount": 0,
|
||||
"discriminantOffset": 0
|
||||
},
|
||||
"new_storage": {
|
||||
"dataWordCount": 5,
|
||||
"pointerCount": 4,
|
||||
"preferredListEncoding": "inlineComposite",
|
||||
"isGroup": false,
|
||||
"discriminantCount": 0,
|
||||
"discriminantOffset": 0
|
||||
},
|
||||
"added_fields": [
|
||||
{
|
||||
"slot": {
|
||||
"offset": 4,
|
||||
"type": {
|
||||
"uint64": null
|
||||
},
|
||||
"defaultValue": {
|
||||
"uint64": 0
|
||||
},
|
||||
"hadExplicitDefault": false
|
||||
},
|
||||
"name": "wheelAdoptMonoTime",
|
||||
"codeOrder": 8,
|
||||
"discriminantValue": 65535,
|
||||
"ordinal": {
|
||||
"explicit": 8
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"0xcd96dafb67a082d0": {
|
||||
"kind": "struct",
|
||||
"before_name": "SlcCruiseEvent",
|
||||
"after_name": "SlcCruiseEvent",
|
||||
"old_field_count": 16,
|
||||
"old_storage": {
|
||||
"dataWordCount": 7,
|
||||
"pointerCount": 6,
|
||||
"preferredListEncoding": "inlineComposite",
|
||||
"isGroup": false,
|
||||
"discriminantCount": 0,
|
||||
"discriminantOffset": 0
|
||||
},
|
||||
"new_storage": {
|
||||
"dataWordCount": 8,
|
||||
"pointerCount": 6,
|
||||
"preferredListEncoding": "inlineComposite",
|
||||
"isGroup": false,
|
||||
"discriminantCount": 0,
|
||||
"discriminantOffset": 0
|
||||
},
|
||||
"added_fields": [
|
||||
{
|
||||
"slot": {
|
||||
"offset": 7,
|
||||
"type": {
|
||||
"uint64": null
|
||||
},
|
||||
"defaultValue": {
|
||||
"uint64": 0
|
||||
},
|
||||
"hadExplicitDefault": false
|
||||
},
|
||||
"name": "wheelOverrideSince",
|
||||
"codeOrder": 16,
|
||||
"discriminantValue": 65535,
|
||||
"ordinal": {
|
||||
"explicit": 16
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"published_addition_commits": {
|
||||
|
||||
@@ -181,52 +181,3 @@ def test_availability_off_still_allows_a_new_ordinary_cruise_engagement():
|
||||
assert not owner.allowed_latch
|
||||
owner.update(state(enabled=True).as_reader(), now_ns=50)
|
||||
assert owner.allowed_latch and owner.screen_override is None
|
||||
|
||||
|
||||
@pytest.mark.parametrize('brake_disengage', [False, True])
|
||||
def test_stock_cruise_availability_cannot_erase_existing_screen_arm(brake_disengage):
|
||||
owner = intent(brake_disengage)
|
||||
owner.update(state(available=False, press=False).as_reader(), now_ns=10)
|
||||
owner.update(state(available=False, press=True).as_reader(), now_ns=20)
|
||||
owner.update(state(available=True, press=False).as_reader(), now_ns=30)
|
||||
assert owner.screen_override is True
|
||||
for index, available in enumerate((False, True, False, True)):
|
||||
cs = state(available=available)
|
||||
cs.gasPressed = bool(index % 2)
|
||||
owner.update(cs.as_reader(), now_ns=40 + index)
|
||||
assert owner.allowed_latch and owner.screen_override is True
|
||||
owner.update(state(press=True).as_reader(), now_ns=50)
|
||||
assert not owner.allowed_latch and owner.pause_lateral
|
||||
owner.update(state(available=False, press=False).as_reader(), now_ns=60)
|
||||
owner.update(state(available=True).as_reader(), now_ns=70)
|
||||
assert not owner.allowed_latch
|
||||
|
||||
|
||||
def test_cruise_baseline_availability_loss_does_not_create_screen_arm():
|
||||
owner = intent()
|
||||
owner.update(state().as_reader(), now_ns=10)
|
||||
assert owner.allowed_latch and owner.screen_override is None
|
||||
owner.update(state(available=False).as_reader(), now_ns=20)
|
||||
assert not owner.allowed_latch and owner.screen_override is False
|
||||
owner.update(state().as_reader(), now_ns=30)
|
||||
assert not owner.allowed_latch
|
||||
|
||||
|
||||
@pytest.mark.parametrize('fault', ['canTimeout', 'doorOpen', 'steerFaultPermanent', 'steeringDisengage', 'accFaulted', 'cancel', 'brake'])
|
||||
def test_availability_loss_cannot_hide_fault_cancel_or_configured_brake(fault):
|
||||
owner = intent(brake=fault == 'brake')
|
||||
owner.update(state(available=False, press=False).as_reader(), now_ns=10)
|
||||
owner.update(state(available=False, press=True).as_reader(), now_ns=20)
|
||||
owner.update(state(press=False).as_reader(), now_ns=30)
|
||||
cs = state(available=False)
|
||||
if fault == 'cancel':
|
||||
cs.buttonEvents = [structs.CarState.ButtonEvent(type=ButtonType.cancel, pressed=True)]
|
||||
elif fault == 'brake':
|
||||
cs.brakePressed = True
|
||||
else:
|
||||
setattr(cs, fault, True)
|
||||
owner.update(cs.as_reader(), now_ns=40)
|
||||
assert not owner.allowed_latch
|
||||
for index, available in enumerate((False, True, False, True)):
|
||||
owner.update(state(available=available).as_reader(), now_ns=50 + index)
|
||||
assert not owner.allowed_latch
|
||||
|
||||
@@ -1,101 +0,0 @@
|
||||
"""Independent screen arming through real parser, intent, native and axis callers."""
|
||||
from types import SimpleNamespace
|
||||
from typing import cast
|
||||
|
||||
import pytest
|
||||
|
||||
from opendbc.can import CANPacker
|
||||
from opendbc.car.tesla.interface import CarInterface
|
||||
from opendbc.car.tesla.screen_button import apply_screen_button
|
||||
from opendbc.car.tesla.tests.test_screen_button import screen_params
|
||||
from opendbc.safety.tests.libsafety import libsafety_py
|
||||
from openpilot.cereal import log
|
||||
from openpilot.selfdrive.selfdrived.events import Events
|
||||
from openpilot.selfdrive.selfdrived.selfdrived import SelfdriveD
|
||||
from openpilot.starpilot.aol.intent import AolSettings
|
||||
from openpilot.starpilot.aol.runtime import AxisDecision
|
||||
from openpilot.starpilot.aol.wire import IntentState, PandaSlot, SafetyState
|
||||
from openpilot.starpilot.car.tesla.screen_intent import TeslaScreenCardIntent
|
||||
|
||||
|
||||
class HealthyPeers(dict):
|
||||
def all_checks(self, services):
|
||||
return all(service in self for service in services)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('cooperative', [False, True])
|
||||
@pytest.mark.parametrize('brake_disengage', [False, True])
|
||||
def test_explicit_screen_axis_survives_sparse_touch_and_stock_availability(cooperative, brake_disengage):
|
||||
cp = screen_params()
|
||||
apply_screen_button(cp, True, brake_disengage)
|
||||
cp.safetyConfigs[0].safetyParam |= 256 * cooperative
|
||||
cp.alternativeExperience = 32
|
||||
ci = CarInterface(cp)
|
||||
owner = TeslaScreenCardIntent(cp, AolSettings(True, 0., 0, 0, (0, 0, 0), (0, 0, 0)))
|
||||
packer = CANPacker('tesla_model3_party')
|
||||
peers = HealthyPeers(modelV2=SimpleNamespace(),
|
||||
extrinsicsCalibration=SimpleNamespace(calStatus=log.ExtrinsicsCalibration.Status.calibrated),
|
||||
driverMonitoringState=SimpleNamespace(lockout=False, alwaysOnLockout=False, alertLevel=0))
|
||||
sd = SimpleNamespace(CP=cp, active=False, enabled=False, initialized=True, sm=peers, aol_settings=owner.settings,
|
||||
aol_axis_decision=AxisDecision(), aol_car_state_log_ns=0, aol_session_id='screen-test',
|
||||
aol_dm_lateral_inhibit=False, nostalgia_paddle_cancel=False, aol_cruise_main_required=False,
|
||||
events=Events())
|
||||
native = libsafety_py.libsafety
|
||||
native.init_tests()
|
||||
native.set_alternative_experience(32)
|
||||
native.set_safety_hooks(10, cp.safetyConfigs[0].safetyParam)
|
||||
native.set_aol_test_heartbeat(True)
|
||||
try:
|
||||
for tick in range(250):
|
||||
now = 1_000_000_000 + tick * 10_000_000
|
||||
native.set_timer(now // 1000)
|
||||
stock_state = 1 if 40 <= tick < 80 or 140 <= tick < 180 else 0
|
||||
messages: tuple[tuple[str, int, dict[str, float]], ...] = (
|
||||
('DAS_control', 2, {}), ('DAS_steeringControl', 2, {}),
|
||||
('SCCM_steeringAngleSensor', 2, {}), ('DAS_status', 2, {}), ('DAS_settings', 2, {}),
|
||||
('DI_speed', 0, {'DI_vehicleSpeed': 0}),
|
||||
('ESP_B', 0, {'ESP_vehicleSpeed': 0, 'ESP_wheelSpeedsQF': 1, 'ESP_vehicleStandstillSts': 1}),
|
||||
('EPAS3S_sysStatus', 0, {'EPAS3S_internalSAS': 0, 'EPAS3S_eacStatus': 1,
|
||||
'EPAS3S_handsOnLevel': 3 if tick >= 240 else 0, 'EPAS3S_torsionBarTorque': 0}),
|
||||
('DI_systemStatus', 0, {'DI_gear': 4, 'DI_accelPedalPos': 20 if tick % 30 < 15 else 0}),
|
||||
('ESP_status', 0, {'ESP_driverBrakeApply': 2 if not brake_disengage and 100 <= tick < 120 else 1}),
|
||||
('UI_warning', 0, {'buckleStatus': 1}),
|
||||
('DI_state', 0, {'DI_cruiseState': stock_state}),
|
||||
)
|
||||
frames = [packer.make_can_msg(name, bus, values) for name, bus, values in messages]
|
||||
if tick in (20, 21):
|
||||
touch = bytearray(8)
|
||||
touch[3] = 3 if tick == 21 else 0
|
||||
frames.append((0x3DF, bytes(touch), 1))
|
||||
for address, data, bus in frames:
|
||||
assert native.safety_rx_hook(libsafety_py.make_CANPacket(address, bus, data))
|
||||
native.safety_tick()
|
||||
cs = ci.update([(now, frames)]).as_reader()
|
||||
if tick > 1:
|
||||
assert cs.canValid and not cs.canTimeout
|
||||
owner.update(cs, now_ns=now)
|
||||
allowed, pause, long_pause = owner.output(cs)
|
||||
intention = IntentState('screen-card', tick + 1, now, now, now + 30_000_000, allowed, pause, long_pause, True, allowed)
|
||||
permission = native.aol_get_permission_mask()
|
||||
request = native.aol_get_request_mask()
|
||||
safety = SafetyState(1, True, now, now + 200_000_000, 10, cp.safetyConfigs[0].safetyParam,
|
||||
bool(permission & 1), bool(permission & 2), bool(request & 1), bool(request & 2),
|
||||
'test-panda', 'screen-test', now,
|
||||
(PandaSlot(0, 'test-panda', 10, cp.safetyConfigs[0].safetyParam, 32, bool(native.get_controls_allowed())),))
|
||||
sd.aol_car_state_log_ns = now
|
||||
sd.events = Events()
|
||||
sd.events.add(log.OnroadEvent.EventName.pcmDisable)
|
||||
if not cs.cruiseState.available:
|
||||
sd.events.add(log.OnroadEvent.EventName.wrongCarMode)
|
||||
decision = SelfdriveD._decide_aol_axes(cast(SelfdriveD, sd), cs, intention, safety, now_ns=now)
|
||||
sd.aol_axis_decision = decision
|
||||
native.aol_set_host_request(int(decision.desired_lateral))
|
||||
assert not decision.longitudinal_active
|
||||
if 22 <= tick < 240:
|
||||
assert decision.lateral_active
|
||||
elif tick >= 240:
|
||||
assert not decision.lateral_active
|
||||
finally:
|
||||
native.init_tests()
|
||||
native.set_alternative_experience(0)
|
||||
native.set_safety_hooks(0, 0)
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user