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 tests: clean up measurement tests #1694

Merged
merged 8 commits into from
Oct 24, 2023
Merged
Show file tree
Hide file tree
Changes from 7 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
69 changes: 32 additions & 37 deletions tests/safety/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,22 @@ def _generic_limit_safety_check(self, msg_function: MessageFunction, min_allowed
should_tx = (should_tx or v == inactive_value) and msg_allowed
self.assertEqual(self._tx(msg_function(v)), should_tx, (controls_allowed, should_tx, v))

def _common_measurement_test(self, msg_func: Callable, min_value: float, max_value: float, factor: int,
meas_min_func: Callable[[], int], meas_max_func: Callable[[], int]):
"""Tests accurate measurement parsing, and that it's reset on safety mode init"""
sshane marked this conversation as resolved.
Show resolved Hide resolved
for val in np.arange(min_value, max_value, 0.5):
for i in range(MAX_SAMPLE_VALS):
self.assertTrue(self._rx(msg_func(val + i * 0.1)))

# assert close by one decimal place
self.assertAlmostEqual(meas_min_func() / factor, val, delta=0.1)
self.assertAlmostEqual(meas_max_func() / factor - 0.5, val, delta=0.1)

# ensure sample_t is reset on safety init
self._reset_safety_hooks()
self.assertEqual(meas_min_func(), 0)
self.assertEqual(meas_max_func(), 0)


class InterceptorSafetyTest(PandaSafetyTestBase):

Expand Down Expand Up @@ -620,45 +636,10 @@ def test_reset_torque_measurements(self):
self.assertEqual(self.safety.get_torque_meas_min(), 0)
self.assertEqual(self.safety.get_torque_meas_max(), 0)

class MeasurementSafetyTest(PandaSafetyTestBase):
DEG_TO_CAN: float = 1

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

@abc.abstractmethod
def _angle_meas_msg(self, angle: float):
pass

@abc.abstractmethod
def _speed_msg(self, speed):
pass
class AngleSteeringSafetyTest(PandaSafetyTestBase):

def common_measurement_test(self, msg_func, min_value, max_value, factor, get_min_func, get_max_func):
for val in np.arange(min_value, max_value, 0.5):
for i in range(MAX_SAMPLE_VALS):
self.assertTrue(self._rx(msg_func(val + i * 0.1)))

# assert close by one decimal place
self.assertAlmostEqual(get_min_func() / factor, val, delta=0.1)
self.assertAlmostEqual(get_max_func() / factor - 0.5, val, delta=0.1)

# ensure sample_t is reset on safety init
self._reset_safety_hooks()
self.assertEqual(get_min_func(), 0)
self.assertEqual(get_max_func(), 0)

def test_vehicle_speed_measurements(self):
self.common_measurement_test(self._speed_msg, 0, 80, VEHICLE_SPEED_FACTOR, self.safety.get_vehicle_speed_min, self.safety.get_vehicle_speed_max)

def test_steering_angle_measurements(self):
self.common_measurement_test(self._angle_meas_msg, -180, 180, self.DEG_TO_CAN, self.safety.get_angle_meas_min, self.safety.get_angle_meas_max)


class AngleSteeringSafetyTest(MeasurementSafetyTest):
DEG_TO_CAN: int
ANGLE_RATE_BP: List[float]
ANGLE_RATE_UP: List[float] # windup limit
ANGLE_RATE_DOWN: List[float] # unwind limit
Expand All @@ -669,10 +650,18 @@ def setUpClass(cls):
cls.safety = None
raise unittest.SkipTest

@abc.abstractmethod
def _speed_msg(self, speed):
pass

@abc.abstractmethod
def _angle_cmd_msg(self, angle: float, enabled: bool):
pass

@abc.abstractmethod
def _angle_meas_msg(self, angle: float):
pass

def _set_prev_desired_angle(self, t):
t = int(t * self.DEG_TO_CAN)
self.safety.set_desired_angle_last(t)
Expand All @@ -685,6 +674,12 @@ def _reset_speed_measurement(self, speed):
for _ in range(MAX_SAMPLE_VALS):
self._rx(self._speed_msg(speed))

def test_vehicle_speed_measurements(self):
self._common_measurement_test(self._speed_msg, 0, 80, VEHICLE_SPEED_FACTOR, self.safety.get_vehicle_speed_min, self.safety.get_vehicle_speed_max)

def test_steering_angle_measurements(self):
self._common_measurement_test(self._angle_meas_msg, -180, 180, self.DEG_TO_CAN, self.safety.get_angle_meas_min, self.safety.get_angle_meas_max)

def test_angle_cmd_when_enabled(self):
# when controls are allowed, angle cmd rate limit is enforced
speeds = [0., 1., 5., 10., 15., 50.]
Expand Down
4 changes: 2 additions & 2 deletions tests/safety/test_subaru.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from panda import Panda
from panda.tests.libpanda import libpanda_py
import panda.tests.safety.common as common
from panda.tests.safety.common import CANPackerPanda, MeasurementSafetyTest
from panda.tests.safety.common import CANPackerPanda
from functools import partial

class SubaruMsg(enum.IntEnum):
Expand Down Expand Up @@ -52,7 +52,7 @@ def gen2_long_additional_tx_msgs():
def fwd_blacklisted_addr(lkas_msg=SubaruMsg.ES_LKAS):
return {SUBARU_CAM_BUS: [lkas_msg, SubaruMsg.ES_DashStatus, SubaruMsg.ES_LKAS_State, SubaruMsg.ES_Infotainment]}

class TestSubaruSafetyBase(common.PandaSafetyTest, MeasurementSafetyTest):
class TestSubaruSafetyBase(common.PandaSafetyTest):
FLAGS = 0
STANDSTILL_THRESHOLD = 0 # kph
RELAY_MALFUNCTION_ADDR = SubaruMsg.ES_LKAS
Expand Down
Loading