Files
StarPilot/openpilot/starpilot/tests/test_gm_stock_caller.py
T
firestar5683 80fbb4f6db Feature registration
Register vehicle controls, planner modes, UI actions and Galaxy services after their providers exist. Wire the shared runtime entrypoints and cover their combined behavior.

Co-authored-by: Adeeb Shihadeh <adeebshihadeh@gmail.com>
Co-authored-by: Daniel Koepping <elkoled@gmail.com>
Co-authored-by: Danny <dannyirwin31@yahoo.com>
Co-authored-by: DimitriChrysafis <Dimitri.chrysafis@gmail.com>
Co-authored-by: Dom <168790843+firestar5683@users.noreply.github.com>
Co-authored-by: Harald Schäfer <harald.the.engineer@gmail.com>
Co-authored-by: Kacper Rączy <gfw.kra@gmail.com>
Co-authored-by: Prabhaav Pillai <prabhaav@msu.edu>
Co-authored-by: dirwin31 <83434411+dirwin31@users.noreply.github.com>
Co-authored-by: firestarsdog <229254897+firestarsdog@users.noreply.github.com>
Co-authored-by: stef <19478336+stefpi@users.noreply.github.com>
Co-authored-by: whoisdomi <dcantu83@gmail.com>

Co-authored-by: Intelli <contact@intelli.software>
Co-authored-by: haraschax <6804392+haraschax@users.noreply.github.com>
Co-authored-by: prabhaavp <prabhaav@msu.edu>
2026-10-14 20:02:39 -05:00

81 lines
4.4 KiB
Python

"""GM pedal events through the stock control state and native TX checks."""
import ast
from pathlib import Path
from types import SimpleNamespace as NS
import unittest
from openpilot.cereal import log
from opendbc.car.structs import car
from openpilot.selfdrive.selfdrived.events import Events
from openpilot.selfdrive.selfdrived.state import StateMachine
from openpilot.starpilot.aol.runtime import ordinary_lateral_requested
from opendbc.car.gm import gmcan
from opendbc.car.gm.values import CC_GATEWAY_STOCK_CAR, is_ordinary_cc_profile
from opendbc.safety.tests import test_gm_cc_gateway_stock as stock
def pedal_event_expression():
# Compile the reached producer predicate rather than inventing pedal semantics.
import openpilot.selfdrive.selfdrived.selfdrived as module
tree = ast.parse(Path(module.__file__).read_text())
matches = [node.test for node in ast.walk(tree) if isinstance(node, ast.If)
and any(isinstance(call, ast.Call) and isinstance(call.func, ast.Attribute)
and call.func.attr == "add" and call.args
and isinstance(call.args[0], ast.Attribute)
and call.args[0].attr == "pedalPressed"
for statement in node.body if isinstance(statement, ast.Expr)
for call in (statement.value,))]
if len(matches) != 1:
raise AssertionError("SelfdriveD pedal predicate changed; review its caller")
return compile(ast.Expression(matches[0]), "SelfdriveD.pedalPressed", "eval")
class TestGMStockCaller(unittest.TestCase):
def test_decoded_pedals_normal_axis_and_adversarial_native_boundary(self):
predicate = pedal_event_expression()
fixture = stock.TestGmCcGatewayStock()
fixture.setUp()
self.addCleanup(fixture.safety.set_safety_hooks, car.CarParams.SafetyModel.noOutput, 0)
self.addCleanup(fixture.safety.set_alternative_experience, 0)
for identity in sorted(CC_GATEWAY_STOCK_CAR):
for brake, gas, accelerator_disengage in ((False, False, False), (True, False, False),
(False, True, False), (False, True, True)):
with self.subTest(identity=identity, brake=brake, gas=gas, accelerator_disengage=accelerator_disengage):
# Capture the real decoded CS and CP via the actual CI/controller fixture.
packer, frames, _ = fixture.joined(identity, brake=brake, gas=gas, active=False, cancel=False)
cp, ci, cs = fixture.joined_cp, fixture.joined_interface, fixture.joined_state
self.assertEqual(cs.brakePressed, brake)
self.assertEqual(cs.gasPressed, gas)
self.assertFalse(cs.standstill)
previous = car.CarState.new_message()
events = Events()
producer = NS(CS_prev=previous, disengage_on_accelerator=accelerator_disengage)
if eval(predicate, {"CS": cs, "self": producer, "previous": previous}):
events.add(log.OnroadEvent.EventName.pedalPressed)
machine = StateMachine()
machine.state = log.SelfdriveState.OpenpilotState.enabled
enabled, active = machine.update(events)
self.assertEqual(active, not (brake or (gas and accelerator_disengage)))
requested = ordinary_lateral_requested(active, cs, cp)
control = car.CarControl.new_message()
control.enabled = enabled
control.latActive = requested
control.actuators.torque = .03
# Disabled steering is 10 Hz. Advance ten real 100-Hz input frames,
# keeping physical sources fresh before the next eligible TX slot.
for tick in range(1, 11):
stamp = 1_050_000_000 + tick * 10_000_000
current_frames = [frame for frame in frames if frame[0] != 0x1E1]
if tick % 3 == 0:
current_frames.append(gmcan.create_buttons(packer, 0, (2 + tick // 3) % 4, 1))
cs = ci.update([(stamp, current_frames)])
self.assertTrue(cs.canValid)
fixture.safety.set_timer(stamp // 1000)
fixture.feed(current_frames)
ci.CC.frame = 30
_, commands = ci.CC.update(control.as_reader(), ci.CS, stamp)
steering = next(msg for msg in commands if msg[0] == 0x180)
torque = ((steering[1][0] & 7) << 8) | steering[1][1]
self.assertEqual(torque > 0, requested and (not gas or is_ordinary_cc_profile(cp)))
self.assertTrue(fixture.safety.safety_tx_hook(fixture.packet(steering)))