From ce98b31cbfd095db00e4e72083d44d3ca92c9d3b Mon Sep 17 00:00:00 2001 From: firestar5683 <168790843+firestar5683@users.noreply.github.com> Date: Fri, 19 Jun 2026 15:48:26 -0500 Subject: [PATCH] Tyger Tyger Burning Bright --- opendbc_repo/opendbc/car/gm/carcontroller.py | 85 +++++++++++++++++ .../car/gm/tests/test_carcontroller.py | 91 +++++++++++++++++++ opendbc_repo/opendbc/car/gm/tests/test_gm.py | 1 + opendbc_repo/opendbc/safety/modes/gm.h | 26 +++++- opendbc_repo/opendbc/safety/tests/common.py | 4 +- opendbc_repo/opendbc/safety/tests/test_gm.py | 65 +++++++++++++ 6 files changed, 268 insertions(+), 4 deletions(-) diff --git a/opendbc_repo/opendbc/car/gm/carcontroller.py b/opendbc_repo/opendbc/car/gm/carcontroller.py index 71de792a2..9c5a50c62 100644 --- a/opendbc_repo/opendbc/car/gm/carcontroller.py +++ b/opendbc_repo/opendbc/car/gm/carcontroller.py @@ -40,6 +40,9 @@ AUTO_HOLD_MAX_BRAKE = 240 AUTO_HOLD_MIN_DRIVE_TIME_S = 3.0 AUTO_HOLD_STOPPED_SPEED = 0.02 AUTO_HOLD_2019_MIN_BRAKE = 100 +BOLT_ACC_PEDAL_FRICTION_RELEASE_FRAMES = 5 +BOLT_PEDAL_LONG_ACCEL_LIMIT_BP = [0.0, 1.5, 4.0, 8.0, 15.0, 30.0] +BOLT_PEDAL_LONG_ACCEL_LIMIT_V = [-0.93, -1.28, -1.98, -2.58, -2.86, -2.95] VOLT_ONE_PEDAL_DECEL_BP = [0.5 * CV.MPH_TO_MS, 6.0 * CV.MPH_TO_MS] VOLT_ONE_PEDAL_DECEL_V = [-1.0, -1.1] VOLT_ONE_PEDAL_REGEN_PADDLE_DECEL_V = [-1.5, -1.6] @@ -285,6 +288,51 @@ def get_friction_brake_bus(CP): return CanBus.CHASSIS +def supports_bolt_acc_pedal_friction_experiment(CP) -> bool: + return ( + CP.carFingerprint == CAR.CHEVROLET_BOLT_ACC_2022_2023_PEDAL and + CP.openpilotLongitudinalControl and + CP.enableGasInterceptorDEPRECATED and + bool(CP.flags & GMFlags.PEDAL_LONG.value) + ) + + +def get_bolt_acc_pedal_friction_brake(apply_brake, full_brake_accel, v_ego, params) -> int: + if apply_brake <= 0: + return 0 + + full_brake_accel = min(full_brake_accel, -0.1) + legacy_full_scale = max(-params.ACCEL_MIN, 0.1) + corrected_scale = legacy_full_scale / max(-full_brake_accel, 0.1) + speed_gain = float(np.interp(v_ego, [0.0, 10.0, 25.0], [1.0, 1.15, 1.3])) + + return int(round(np.clip(apply_brake * corrected_scale * speed_gain, 0, params.MAX_BRAKE))) + + +def get_bolt_pedal_long_accel_limit(v_ego: float) -> float: + return float(np.interp(v_ego, BOLT_PEDAL_LONG_ACCEL_LIMIT_BP, BOLT_PEDAL_LONG_ACCEL_LIMIT_V)) + + +def get_bolt_acc_pedal_planner_brake_switch(v_ego: float, params, tire_radius: float, mass: float, + coeff_drag: float, frontal_area: float, air_density: float) -> int: + planner_accel_limit = get_bolt_pedal_long_accel_limit(v_ego) + aero_drag_force = 0.5 * coeff_drag * frontal_area * air_density * v_ego ** 2 + planner_torque = tire_radius * ((mass * planner_accel_limit) + aero_drag_force) + return int(round(planner_torque + params.ZERO_GAS)) + + +def get_bolt_acc_pedal_friction_command_state(apply_brake: int, cruise_main_on: bool, release_frames: int): + command_brake = apply_brake if cruise_main_on else 0 + + if command_brake > 0: + release_frames = BOLT_ACC_PEDAL_FRICTION_RELEASE_FRAMES + elif release_frames > 0: + release_frames -= 1 + + should_send = cruise_main_on or release_frames > 0 + return command_brake, release_frames, should_send + + class CarController(CarControllerBase): def __init__(self, dbc_names, CP): super().__init__(dbc_names, CP) @@ -351,6 +399,7 @@ class CarController(CarControllerBase): self.gm_auto_hold_enabled = self.params_.get_bool("GMAutoHold") except UnknownKeyName: self.gm_auto_hold_enabled = False + self.bolt_acc_pedal_friction_release_frames = 0 def _reset_volt_one_pedal(self): self.volt_one_pedal_pid.reset() @@ -678,6 +727,8 @@ class CarController(CarControllerBase): CS.out.vEgo, get_auto_hold_stop_threshold(self.CP, CS.auto_hold_engaged), ) + bolt_acc_pedal_friction_experiment = supports_bolt_acc_pedal_friction_experiment(self.CP) + bolt_acc_pedal_friction_main_on = bolt_acc_pedal_friction_experiment and CS.out.cruiseState.available volt_one_pedal_braking = volt_one_pedal_active and self.volt_one_pedal_brake > 0 volt_one_pedal_hold_active = ( volt_one_pedal_braking and @@ -806,9 +857,23 @@ class CarController(CarControllerBase): if testing_ground.use_1: brake_switch_bias = get_testing_ground_1_brake_switch_bias(CS.out.vEgo) brake_switch = min(self.params.ZERO_GAS, brake_switch + brake_switch_bias) + if bolt_acc_pedal_friction_main_on: + planner_brake_switch = get_bolt_acc_pedal_planner_brake_switch( + CS.out.vEgo, self.params, self.tireRadius, self.mass, self.coeffDrag, self.frontalArea, self.airDensity, + ) + brake_switch = min(brake_switch, planner_brake_switch) brake_accel = min((scaled_torque - brake_switch) / (self.tireRadius * self.mass), 0) self.apply_gas = int(round(apply_gas_torque)) self.apply_brake = int(round(np.interp(brake_accel, self.params.BRAKE_LOOKUP_BP, self.params.BRAKE_LOOKUP_V))) + if bolt_acc_pedal_friction_main_on and self.apply_brake > 0: + full_brake_accel = min( + self.params.ACCEL_MIN + (0.5 * self.coeffDrag * self.frontalArea * self.airDensity * CS.out.vEgo ** 2) / self.mass + + (self.params.ZERO_GAS - brake_switch) / (self.tireRadius * self.mass), + -0.1, + ) + self.apply_brake = get_bolt_acc_pedal_friction_brake( + self.apply_brake, full_brake_accel, CS.out.vEgo, self.params, + ) if self.apply_brake > 0: self.apply_gas = self.params.INACTIVE_REGEN @@ -865,6 +930,26 @@ class CarController(CarControllerBase): can_sends.append(gmcan.create_buttons(self.packer_pt, CanBus.POWERTRAIN, (CS.buttons_counter + 1) % 4, CruiseButtons.DECEL_SET)) if self.CP.enableGasInterceptorDEPRECATED: can_sends.append(create_gas_interceptor_command(self.packer_pt, interceptor_gas_cmd, idx)) + if bolt_acc_pedal_friction_experiment: + friction_brake_bus = get_friction_brake_bus(self.CP) + if self.CP.networkLocation == NetworkLocation.fwdCamera: + at_full_stop = at_full_stop and stopping + + experiment_brake, self.bolt_acc_pedal_friction_release_frames, should_send_bolt_acc_pedal_friction = \ + get_bolt_acc_pedal_friction_command_state( + self.apply_brake, + bolt_acc_pedal_friction_main_on, + self.bolt_acc_pedal_friction_release_frames, + ) + + # This fingerprint is routed through the CC-only pedal path, so it + # does not fall through to the normal friction-brake sender below. + # Never apply stock friction with cruise main off, but do send a short + # explicit zero-brake unwind so the last nonzero stock-EBCM command + # cannot linger after a disengage or main-off event. + if should_send_bolt_acc_pedal_friction: + can_sends.append(gmcan.create_friction_brake_command( + self.packer_ch, friction_brake_bus, experiment_brake, idx, False, near_stop, at_full_stop, self.CP)) if self.CP.carFingerprint not in CC_ONLY_CAR: friction_brake_bus = get_friction_brake_bus(self.CP) # GM Camera exceptions diff --git a/opendbc_repo/opendbc/car/gm/tests/test_carcontroller.py b/opendbc_repo/opendbc/car/gm/tests/test_carcontroller.py index aa9bfad09..c11a6b742 100644 --- a/opendbc_repo/opendbc/car/gm/tests/test_carcontroller.py +++ b/opendbc_repo/opendbc/car/gm/tests/test_carcontroller.py @@ -1,6 +1,8 @@ import sys import types from types import SimpleNamespace +import numpy as np +import pytest from opendbc.car import structs @@ -40,6 +42,10 @@ from opendbc.car.gm.carcontroller import ( estimate_auto_hold_brake, get_adas_keepalive_step, get_auto_hold_stop_threshold, + get_bolt_acc_pedal_friction_brake, + get_bolt_acc_pedal_friction_command_state, + get_bolt_acc_pedal_planner_brake_switch, + get_bolt_pedal_long_accel_limit, get_lka_steering_cmd_counter, get_volt_one_pedal_target_decel, get_testing_ground_1_brake_switch_bias, @@ -51,6 +57,7 @@ from opendbc.car.gm.carcontroller import ( should_send_stock_long_cancel, should_spoof_dash_speed, should_spoof_ecm_cruise_status, + supports_bolt_acc_pedal_friction_experiment, supports_volt_auto_hold, supports_volt_one_pedal, use_interceptor_sng_launch, @@ -107,6 +114,90 @@ def test_gen2_bolt_acc_pedal_cancel_uses_enabled_only(): assert not get_stock_cc_active_for_cancel(CP, _cs(False, AccState.ACTIVE)) +def test_bolt_acc_pedal_friction_experiment_is_single_fingerprint_only(): + assert supports_bolt_acc_pedal_friction_experiment(SimpleNamespace( + carFingerprint=CAR.CHEVROLET_BOLT_ACC_2022_2023_PEDAL, + openpilotLongitudinalControl=True, + enableGasInterceptorDEPRECATED=True, + flags=GMFlags.PEDAL_LONG.value, + )) + assert not supports_bolt_acc_pedal_friction_experiment(SimpleNamespace( + carFingerprint=CAR.CHEVROLET_MALIBU_HYBRID_CC, + openpilotLongitudinalControl=True, + enableGasInterceptorDEPRECATED=True, + flags=GMFlags.PEDAL_LONG.value, + )) + assert not supports_bolt_acc_pedal_friction_experiment(SimpleNamespace( + carFingerprint=CAR.CHEVROLET_BOLT_ACC_2022_2023_PEDAL, + openpilotLongitudinalControl=False, + enableGasInterceptorDEPRECATED=True, + flags=GMFlags.PEDAL_LONG.value, + )) + + +def test_bolt_acc_pedal_friction_blend_preserves_zero_before_crossover(): + params = SimpleNamespace(ACCEL_MIN=-4.0, MAX_BRAKE=400) + + assert get_bolt_acc_pedal_friction_brake(0, -2.8, 20.0, params) == 0 + + +def test_bolt_acc_pedal_friction_blend_uses_full_brake_range_after_regen(): + params = SimpleNamespace(ACCEL_MIN=-4.0, MAX_BRAKE=400) + + # Legacy mapping tops out early once regen has already consumed part of the + # decel request. The experiment remaps that reduced span back to full scale. + assert get_bolt_acc_pedal_friction_brake(286, -2.86, 20.0, params) == 400 + + +def test_bolt_acc_pedal_friction_blend_biases_small_commands_upward_at_speed(): + params = SimpleNamespace(ACCEL_MIN=-4.0, MAX_BRAKE=400) + + low_speed = get_bolt_acc_pedal_friction_brake(31, -2.86, 0.0, params) + high_speed = get_bolt_acc_pedal_friction_brake(31, -2.86, 20.0, params) + + assert low_speed > 31 + assert high_speed > low_speed + + +def test_bolt_pedal_long_accel_limit_matches_planner_regen_envelope(): + assert get_bolt_pedal_long_accel_limit(6.66) == pytest.approx(-2.379, abs=1e-3) + assert get_bolt_pedal_long_accel_limit(3.0) == pytest.approx(-1.70, abs=1e-3) + + +def test_bolt_acc_pedal_planner_brake_switch_delays_friction_vs_stock_switch(): + params = SimpleNamespace(ZERO_GAS=6150, BRAKE_SWITCH_LOOKUP_BP=[0.5, 10.0], BRAKE_SWITCH_LOOKUP_V=[6150, 5500]) + v_ego = 6.66 + + stock_switch = int(round(np.interp(v_ego, params.BRAKE_SWITCH_LOOKUP_BP, params.BRAKE_SWITCH_LOOKUP_V))) + planner_switch = get_bolt_acc_pedal_planner_brake_switch( + v_ego, params, tire_radius=0.336, mass=1832.0, coeff_drag=0.30, frontal_area=2.35, air_density=1.225, + ) + + assert planner_switch < stock_switch + + +def test_bolt_acc_pedal_friction_command_state_requires_cruise_main_for_positive_brake(): + command_brake, release_frames, should_send = get_bolt_acc_pedal_friction_command_state(120, False, 0) + + assert command_brake == 0 + assert release_frames == 0 + assert not should_send + + +def test_bolt_acc_pedal_friction_command_state_sends_zero_unwind_after_main_off(): + command_brake, release_frames, should_send = get_bolt_acc_pedal_friction_command_state(120, True, 0) + + assert command_brake == 120 + assert release_frames > 0 + assert should_send + + command_brake, release_frames, should_send = get_bolt_acc_pedal_friction_command_state(0, False, release_frames) + + assert command_brake == 0 + assert release_frames >= 0 + assert should_send + + def test_stock_cancel_is_suppressed_when_acc_is_faulted(): CP = SimpleNamespace(carFingerprint=CAR.CHEVROLET_VOLT_CAMERA) cs = _cs(True, AccState.FAULTED) diff --git a/opendbc_repo/opendbc/car/gm/tests/test_gm.py b/opendbc_repo/opendbc/car/gm/tests/test_gm.py index bb1b70e22..711a38ed6 100644 --- a/opendbc_repo/opendbc/car/gm/tests/test_gm.py +++ b/opendbc_repo/opendbc/car/gm/tests/test_gm.py @@ -261,6 +261,7 @@ class TestGMInterface: assert car_params.alternativeExperience & ALTERNATIVE_EXPERIENCE.GM_REMAP_CANCEL_TO_DISTANCE assert car_params.safetyConfigs[0].safetyParam & GMSafetyFlags.FLAG_GM_BOLT_2022_PEDAL.value + assert car_params.safetyConfigs[0].safetyParam & GMSafetyFlags.FLAG_GM_PANDA_PADDLE_SCHED.value def test_cadillac_xt5_sdgm_sascm_gates_alpha_long(self): CarInterface = interfaces[CAR.CADILLAC_XT5] diff --git a/opendbc_repo/opendbc/safety/modes/gm.h b/opendbc_repo/opendbc/safety/modes/gm.h index 5976fb6fc..e5b6d525b 100644 --- a/opendbc_repo/opendbc/safety/modes/gm.h +++ b/opendbc_repo/opendbc/safety/modes/gm.h @@ -614,6 +614,11 @@ static safety_config gm_init(uint16_t param) { {0x200, 0, 6, .check_relay = false}, {0x1E1, 0, 7, .check_relay = false}, {0xBD, 0, 7, .check_relay = false}, {0x1F5, 0, 8, .check_relay = false}}; // pt bus + static const CanMsg GM_CAM_BOLT_2022_PEDAL_FRICTION_TX_MSGS[] = {{0x180, 0, 4, .check_relay = true}, {0x370, 0, 6, .check_relay = false}, {0x3D1, 0, 8, .check_relay = false}, {0x315, 0, 5, .check_relay = true}, // pt bus + {0x1E1, 2, 7, .check_relay = false}, {0x184, 2, 8, .check_relay = true}, // camera bus + {0x200, 0, 6, .check_relay = false}, + {0x1E1, 0, 7, .check_relay = false}, + {0xBD, 0, 7, .check_relay = false}, {0x1F5, 0, 8, .check_relay = false}}; // pt bus static const CanMsg GM_CAM_VOLT_AUTO_HOLD_TX_MSGS[] = {{0x180, 0, 4, .check_relay = true}, {0x370, 0, 6, .check_relay = false}, {0x3D1, 0, 8, .check_relay = false}, {0x315, 0, 5, .check_relay = true}, // pt bus {0x1E1, 2, 7, .check_relay = false}, {0x184, 2, 8, .check_relay = true}, // camera bus {0x200, 0, 6, .check_relay = false}, @@ -637,6 +642,12 @@ static safety_config gm_init(uint16_t param) { {0x200, 0, 6, .check_relay = false}, {0x1E1, 0, 7, .check_relay = false}, {0xBD, 0, 7, .check_relay = false}, {0x1F5, 0, 8, .check_relay = false}}; // pt bus + static const CanMsg GM_CAM_NO_CAMERA_BOLT_2022_PEDAL_FRICTION_TX_MSGS[] = {{0x180, 0, 4, .check_relay = false}, {0x370, 0, 6, .check_relay = false}, {0x3D1, 0, 8, .check_relay = false}, {0x315, 0, 5, .check_relay = false}, // pt bus + {0x409, 0, 7, .check_relay = false}, {0x40A, 0, 7, .check_relay = false}, + {0x1E1, 2, 7, .check_relay = false}, {0x184, 2, 8, .check_relay = false}, // camera bus + {0x200, 0, 6, .check_relay = false}, + {0x1E1, 0, 7, .check_relay = false}, + {0xBD, 0, 7, .check_relay = false}, {0x1F5, 0, 8, .check_relay = false}}; // pt bus static const CanMsg GM_CAM_NO_CAMERA_VOLT_AUTO_HOLD_TX_MSGS[] = {{0x180, 0, 4, .check_relay = false}, {0x370, 0, 6, .check_relay = false}, {0x3D1, 0, 8, .check_relay = false}, {0x315, 0, 5, .check_relay = false}, // pt bus {0x409, 0, 7, .check_relay = false}, {0x40A, 0, 7, .check_relay = false}, {0x1E1, 2, 7, .check_relay = false}, {0x184, 2, 8, .check_relay = false}, // camera bus @@ -736,6 +747,11 @@ static safety_config gm_init(uint16_t param) { gm_pcm_cruise = (gm_hw == GM_CAM || gm_sdgm) && !gm_cam_long && !gm_force_ascm && !gm_pedal_long; const bool gm_ascm_int_stock_cam = gm_ascm_int && (gm_hw == GM_CAM) && gm_pcm_cruise && !gm_cam_long && !gm_pedal_long && !gm_cc_long; const bool gm_ascm_int_no_accel_pos = gm_ascm_int && (gm_hw == GM_CAM) && gm_force_brake_c9; + // FLAG_GM_BOLT_2022_PEDAL is shared with Malibu Hybrid pedal-long. Requiring + // the paddle scheduler bit narrows this whitelist to the Gen2 Bolt pedal-long + // experiment, which is the only path that should probe chassis friction brake + // while stock ACC remains canceled. + const bool gm_bolt_2022_pedal_friction = gm_bolt_2022_pedal && gm_panda_paddle_sched && !gm_has_acc; gm_steer_limits = GET_FLAG(param, GM_PARAM_BOLT_2017) ? &GM_BOLT_2017_STEERING_LIMITS : &GM_STEERING_LIMITS; if ((gm_hw == GM_ASCM && !gm_sdgm) || gm_ascm_int || gm_force_ascm) { @@ -774,13 +790,19 @@ static safety_config gm_init(uint16_t param) { if (gm_volt_stock_brake && gm_sdgm) { ret = BUILD_SAFETY_CFG(gm_rx_checks, GM_SDGM_VOLT_AUTO_HOLD_TX_MSGS); } else if (gm_no_camera) { - if (gm_volt_stock_brake) { + if (gm_bolt_2022_pedal_friction) { + ret = BUILD_SAFETY_CFG(gm_rx_checks, GM_CAM_NO_CAMERA_BOLT_2022_PEDAL_FRICTION_TX_MSGS); + } else if (gm_volt_stock_brake) { ret = BUILD_SAFETY_CFG(gm_rx_checks, GM_CAM_NO_CAMERA_VOLT_AUTO_HOLD_TX_MSGS); } else { ret = BUILD_SAFETY_CFG(gm_rx_checks, GM_CAM_NO_CAMERA_TX_MSGS); } } else { - if (gm_volt_stock_brake) { + if (gm_bolt_2022_pedal_friction) { + // Experimental Gen2 Bolt pedal-long path: keep stock ACC canceled but + // allow OP to probe chassis friction-brake acceptance through panda. + ret = BUILD_SAFETY_CFG(gm_rx_checks, GM_CAM_BOLT_2022_PEDAL_FRICTION_TX_MSGS); + } else if (gm_volt_stock_brake) { ret = BUILD_SAFETY_CFG(gm_rx_checks, GM_CAM_VOLT_AUTO_HOLD_TX_MSGS); } else { ret = BUILD_SAFETY_CFG(gm_rx_checks, GM_CAM_TX_MSGS); diff --git a/opendbc_repo/opendbc/safety/tests/common.py b/opendbc_repo/opendbc/safety/tests/common.py index c5086c734..1f26fe4a7 100644 --- a/opendbc_repo/opendbc/safety/tests/common.py +++ b/opendbc_repo/opendbc/safety/tests/common.py @@ -973,8 +973,8 @@ class SafetyTest(SafetyTestBase): continue if {attr, current_test}.issubset({'TestGmCameraSafety', 'TestGmCameraLongitudinalSafety', 'TestGmAscmSafety', 'TestGmCameraEVSafety', 'TestGmCameraLongitudinalEVSafety', 'TestGmAscmEVSafety', - 'TestGmInterceptorSafety', 'TestGmCcLongitudinalSafety', - 'TestGmCcLongitudinalPandaSchedSafety'}): + 'TestGmInterceptorSafety', 'TestGmBolt2022PedalFrictionSafety', + 'TestGmCcLongitudinalSafety', 'TestGmCcLongitudinalPandaSchedSafety'}): continue if attr.startswith('TestFord') and current_test.startswith('TestFord'): continue diff --git a/opendbc_repo/opendbc/safety/tests/test_gm.py b/opendbc_repo/opendbc/safety/tests/test_gm.py index 3cfcee19e..12bb9d22e 100755 --- a/opendbc_repo/opendbc/safety/tests/test_gm.py +++ b/opendbc_repo/opendbc/safety/tests/test_gm.py @@ -469,6 +469,71 @@ class TestGmInterceptorSafety(common.GasInterceptorSafetyTest, TestGmCameraSafet return to_send +class TestGmBolt2022PedalFrictionSafety(TestGmInterceptorSafety): + TX_MSGS = TestGmInterceptorSafety.TX_MSGS + [[0x315, 0]] + FWD_BLACKLISTED_ADDRS = {2: [0x180, 0x370, 0x315], 0: [0x184, 0x3D1]} + RELAY_MALFUNCTION_ADDRS = {0: (0x180, 0x315), 2: (0x184,)} + EXTRA_SAFETY_PARAM = GMSafetyFlags.FLAG_GM_BOLT_2022_PEDAL + + def setUp(self): + self.packer = CANPackerPanda("gm_global_a_powertrain_generated") + self.packer_chassis = CANPackerPanda("gm_global_a_chassis") + self.safety = libsafety_py.libsafety + self.safety.set_safety_hooks( + CarParams.SafetyModel.gm, + GMSafetyFlags.HW_CAM | + GMSafetyFlags.FLAG_GM_NO_ACC | + GMSafetyFlags.FLAG_GM_PEDAL_LONG | + GMSafetyFlags.FLAG_GM_GAS_INTERCEPTOR | + GMSafetyFlags.FLAG_GM_PANDA_PADDLE_SCHED | + self.EXTRA_SAFETY_PARAM) + self.safety.init_tests() + + def _send_brake_msg(self, brake): + values = {"FrictionBrakeCmd": -brake} + return self.packer_chassis.make_can_msg_safety("EBCMFrictionBrakeCmd", 0, values) + + def _engage_longitudinal(self): + self._rx(self.packer.make_can_msg_panda("ASCMSteeringButton", 0, {"ACCButtons": Buttons.DECEL_SET})) + self._rx(self.packer.make_can_msg_panda("ASCMSteeringButton", 0, {"ACCButtons": Buttons.UNPRESS})) + self.assertTrue(self.safety.get_controls_allowed()) + self.assertTrue(self.safety.get_longitudinal_allowed()) + + def test_tx_hook_on_wrong_safety_mode(self): + self.skipTest("Gen2 Bolt pedal friction experiment intentionally shares the interceptor TX set plus 0x315") + + def test_buttons(self): + for controls_allowed in (False, True): + self.safety.set_controls_allowed(controls_allowed) + for btn in range(8): + self.assertEqual(btn == Buttons.CANCEL, self._tx(self._button_msg(btn))) + + def test_brake_allowed_when_controls_allowed(self): + self._engage_longitudinal() + self.assertTrue(self._tx(self._send_brake_msg(100))) + + def test_brake_blocked_without_controls(self): + self.safety.set_controls_allowed(False) + self.assertFalse(self._tx(self._send_brake_msg(100))) + + def test_brake_limit_enforced(self): + self._engage_longitudinal() + self.assertTrue(self._tx(self._send_brake_msg(400))) + self.assertFalse(self._tx(self._send_brake_msg(401))) + + def test_brake_whitelist_requires_paddle_scheduler_selector(self): + self.safety.set_safety_hooks( + CarParams.SafetyModel.gm, + GMSafetyFlags.HW_CAM | + GMSafetyFlags.FLAG_GM_NO_ACC | + GMSafetyFlags.FLAG_GM_PEDAL_LONG | + GMSafetyFlags.FLAG_GM_GAS_INTERCEPTOR | + self.EXTRA_SAFETY_PARAM) + self.safety.init_tests() + self._engage_longitudinal() + self.assertFalse(self._tx(self._send_brake_msg(100))) + + class TestGmCcLongitudinalSafety(TestGmCameraSafety): TX_MSGS = [[0x180, 0], [0x370, 0], [0x1E1, 0], [0x3D1, 0], [0xBD, 0], [0x1F5, 0], [0x184, 2], [0x1E1, 2]] FWD_BLACKLISTED_ADDRS = {2: [0x180], 0: [0x184, 0x3D1]} # block LKAS, PSCMStatus, and stock cruise status