Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

safety: common gas/brake tests #1416

Closed
wants to merge 8 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 58 additions & 0 deletions tests/safety/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import importlib
import numpy as np
from typing import Dict, List, Optional
import time

from opendbc.can.packer import CANPacker # pylint: disable=import-error
from panda import ALTERNATIVE_EXPERIENCE
Expand Down Expand Up @@ -175,6 +176,63 @@ def test_accel_actuation_limits(self, stock_longitudinal=False):
self.assertEqual(should_tx, self._tx(self._accel_msg(accel)))


class LongitudinalGasBrakeSafetyTest(PandaSafetyTestBase, abc.ABC):

# MIN_GAS and INACTIVE_GAS are usually both 0
MAX_GAS: int = 0
MIN_GAS: int = 0
INACTIVE_GAS: int = 0
MAX_BRAKE: int = 0

@classmethod
def setUpClass(cls):
if cls.__name__ == "LongitudinalGasBrakeSafetyTest":
cls.safety = None
raise unittest.SkipTest

@abc.abstractmethod
def _send_gas_msg(self, gas: int):
pass

@abc.abstractmethod
def _send_brake_msg(self, brake: int):
pass

def test_gas_brake_limits_correct(self):
self.assertGreater(self.MAX_GAS, 0)
# TODO: always allow inactive gas, we're just lucky it's always between
self.assertTrue(self.MIN_GAS <= self.INACTIVE_GAS <= self.MAX_GAS)
self.assertLessEqual(self.MIN_GAS, self.MAX_GAS)
self.assertGreater(self.MAX_BRAKE, 0)

def test_gas_actuation_limits(self):
# Simple test that asserts gas is only sent with min and max gas allowances
for controls_allowed in (True, False):
for gas in np.arange(round(self.MIN_GAS / 2), self.MAX_GAS + 100, 1):
self.safety.set_controls_allowed(controls_allowed)
# add debugging prints:
did_tx = self._tx(self._send_gas_msg(gas))
print("gas: ", gas, "controls_allowed: ", controls_allowed, "should_tx: ", controls_allowed and self.MIN_GAS <= gas <= self.MAX_GAS, "did_tx: ", did_tx)
# test that gas command is within min gas and max gas when controls are allowed, but also allow gas if it's inactive at any time
should_tx = ((controls_allowed and self.MIN_GAS <= gas <= self.MAX_GAS) or
(not controls_allowed and gas == self.INACTIVE_GAS))
self.assertEqual(should_tx, self._tx(self._send_gas_msg(gas)))

# TODO: test stock longitudinal
def test_brake_actuation_limits(self):
# Simple test that asserts brake command is only sent with min and max brake allowances (min is 0, max is self.MAX_BRAKE)
for controls_allowed in (True, False):
self.safety.set_controls_allowed(controls_allowed)
for brake in np.arange(0, self.MAX_BRAKE * 2, 1):
should_tx = (controls_allowed and brake <= self.MAX_BRAKE) or brake == 0

# add debugging prints:
did_tx = self._tx(self._send_brake_msg(brake))
print("brake: ", brake, "controls_allowed: ", controls_allowed, "should_tx: ", (controls_allowed and 0 <= brake <= self.MAX_BRAKE) or brake == 0, "did_tx: ", did_tx)
# test that brake command is within min brake and max brake when controls are allowed, or 0 if controls are not allowed
self.assertEqual(should_tx, self._tx(self._send_brake_msg(brake)))


class TorqueSteeringSafetyTestBase(PandaSafetyTestBase, abc.ABC):

MAX_RATE_UP = 0
Expand Down
44 changes: 14 additions & 30 deletions tests/safety/test_gm.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,8 @@ def _pcm_status_msg(self, enable):
pass


class TestGmSafetyBase(common.PandaSafetyTest, common.DriverTorqueSteeringSafetyTest):
class TestGmSafetyBase(common.PandaSafetyTest, common.LongitudinalGasBrakeSafetyTest,
common.DriverTorqueSteeringSafetyTest):
STANDSTILL_THRESHOLD = 10 * 0.0311
RELAY_MALFUNCTION_ADDR = 384
RELAY_MALFUNCTION_BUS = 0
Expand All @@ -70,11 +71,6 @@ class TestGmSafetyBase(common.PandaSafetyTest, common.DriverTorqueSteeringSafety
DRIVER_TORQUE_ALLOWANCE = 65
DRIVER_TORQUE_FACTOR = 4

MAX_GAS = 0
MAX_REGEN = 0
INACTIVE_REGEN = 0
MAX_BRAKE = 0

PCM_CRUISE = True # openpilot is tied to the PCM state if not longitudinal

@classmethod
Expand Down Expand Up @@ -138,24 +134,6 @@ def _button_msg(self, buttons):
values = {"ACCButtons": buttons}
return self.packer.make_can_msg_panda("ASCMSteeringButton", self.BUTTONS_BUS, values)

def test_brake_safety_check(self):
for enabled in [0, 1]:
for b in range(0, 500):
self.safety.set_controls_allowed(enabled)
if abs(b) > self.MAX_BRAKE or (not enabled and b != 0):
self.assertFalse(self._tx(self._send_brake_msg(b)))
else:
self.assertTrue(self._tx(self._send_brake_msg(b)))

def test_gas_safety_check(self):
# Block if enabled and out of actuation range, disabled and not inactive regen, or if stock longitudinal
for enabled in [0, 1]:
for gas_regen in range(0, 2 ** 12 - 1):
self.safety.set_controls_allowed(enabled)
should_tx = ((enabled and self.MAX_REGEN <= gas_regen <= self.MAX_GAS) or
(not enabled and gas_regen == self.INACTIVE_REGEN))
self.assertEqual(should_tx, self._tx(self._send_gas_msg(gas_regen)), (enabled, gas_regen))


class TestGmAscmSafety(GmLongitudinalBase, TestGmSafetyBase):
TX_MSGS = [[384, 0], [1033, 0], [1034, 0], [715, 0], [880, 0], # pt bus
Expand All @@ -167,8 +145,8 @@ class TestGmAscmSafety(GmLongitudinalBase, TestGmSafetyBase):
BRAKE_BUS = 2

MAX_GAS = 3072
MAX_REGEN = 1404
INACTIVE_REGEN = 1404
MIN_GAS = 1404
INACTIVE_GAS = 1404 # no regen
MAX_BRAKE = 400

def setUp(self):
Expand All @@ -178,6 +156,9 @@ def setUp(self):
self.safety.set_safety_hooks(Panda.SAFETY_GM, 0)
self.safety.init_tests()

def test_gas_actuation_limits(self):
pass


class TestGmCameraSafetyBase(TestGmSafetyBase):

Expand Down Expand Up @@ -223,10 +204,13 @@ def test_buttons(self):
self.assertEqual(enabled, self._tx(self._button_msg(Buttons.CANCEL)))

# GM Cam safety mode does not allow longitudinal messages
def test_brake_safety_check(self):
def test_gas_brake_limits_correct(self):
pass

def test_gas_actuation_limits(self):
pass

def test_gas_safety_check(self):
def test_brake_actuation_limits(self):
pass


Expand All @@ -237,8 +221,8 @@ class TestGmCameraLongitudinalSafety(GmLongitudinalBase, TestGmCameraSafetyBase)
BUTTONS_BUS = 0 # rx only

MAX_GAS = 3400
MAX_REGEN = 1514
INACTIVE_REGEN = 1554
MIN_GAS = 1514
INACTIVE_GAS = 1554 # no regen
MAX_BRAKE = 400

def setUp(self):
Expand Down