From 1a3d0286ce2b45ec07977ce5dcb1f5c3e62a5586 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Mon, 23 Oct 2023 16:10:31 -0700 Subject: [PATCH 1/8] hmm this works --- board/safety/safety_hyundai.h | 4 +- tests/safety/common.py | 74 +++++++++++++++++------------------ tests/safety/test_subaru.py | 4 +- 3 files changed, 42 insertions(+), 40 deletions(-) diff --git a/board/safety/safety_hyundai.h b/board/safety/safety_hyundai.h index c3d2576144..e284a5d8cf 100644 --- a/board/safety/safety_hyundai.h +++ b/board/safety/safety_hyundai.h @@ -178,7 +178,9 @@ static int hyundai_rx_hook(CANPacket_t *to_push) { if (valid && (bus == 0)) { if (addr == 0x251) { - int torque_driver_new = ((GET_BYTES(to_push, 0, 4) & 0x7ffU) * 0.79) - 808; // scale down new driver torque signal to match previous one +// int torque_driver_new = ((GET_BYTES(to_push, 0, 2) & 0x7ffU) * 0.79) - 808; // scale down new driver torque signal to match previous one + int torque_driver_new = (GET_BYTES(to_push, 0, 2) & 0x7ffU); // scale down new driver torque signal to match previous one + torque_driver_new -= 1024; // update array of samples update_sample(&torque_driver, torque_driver_new); } diff --git a/tests/safety/common.py b/tests/safety/common.py index f954875937..2e4b4de563 100644 --- a/tests/safety/common.py +++ b/tests/safety/common.py @@ -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""" + 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): @@ -490,11 +506,16 @@ def test_realtime_limits(self): self.assertTrue(self._tx(self._torque_cmd_msg(sign * (self.MAX_RT_DELTA - 1)))) self.assertTrue(self._tx(self._torque_cmd_msg(sign * (self.MAX_RT_DELTA + 1)))) + def test_soimething(self): + self._common_measurement_test(self._torque_driver_msg, -self.MAX_TORQUE, self.MAX_TORQUE, 1, + self.safety.get_torque_driver_min, self.safety.get_torque_driver_max) + def test_reset_driver_torque_measurements(self): # Tests that the driver torque measurement sample_t is reset on safety mode init for t in np.linspace(-self.MAX_TORQUE, self.MAX_TORQUE, MAX_SAMPLE_VALS): self.assertTrue(self._rx(self._torque_driver_msg(t))) + print(self.safety.get_torque_driver_max(), self.safety.get_torque_driver_min()) self.assertNotEqual(self.safety.get_torque_driver_min(), 0) self.assertNotEqual(self.safety.get_torque_driver_max(), 0) @@ -620,45 +641,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 - - 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(PandaSafetyTestBase): -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 @@ -669,10 +655,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) @@ -685,6 +679,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.] diff --git a/tests/safety/test_subaru.py b/tests/safety/test_subaru.py index 96731c51ff..bedb3cd0ae 100755 --- a/tests/safety/test_subaru.py +++ b/tests/safety/test_subaru.py @@ -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): @@ -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 From a5f6713c14a72ee97c9f146b4b187dcc2efa4787 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Mon, 23 Oct 2023 19:22:34 -0700 Subject: [PATCH 2/8] hyundai fix in another pr --- board/safety/safety_hyundai.h | 4 +--- tests/safety/common.py | 4 ---- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/board/safety/safety_hyundai.h b/board/safety/safety_hyundai.h index e284a5d8cf..c3d2576144 100644 --- a/board/safety/safety_hyundai.h +++ b/board/safety/safety_hyundai.h @@ -178,9 +178,7 @@ static int hyundai_rx_hook(CANPacket_t *to_push) { if (valid && (bus == 0)) { if (addr == 0x251) { -// int torque_driver_new = ((GET_BYTES(to_push, 0, 2) & 0x7ffU) * 0.79) - 808; // scale down new driver torque signal to match previous one - int torque_driver_new = (GET_BYTES(to_push, 0, 2) & 0x7ffU); // scale down new driver torque signal to match previous one - torque_driver_new -= 1024; + int torque_driver_new = ((GET_BYTES(to_push, 0, 4) & 0x7ffU) * 0.79) - 808; // scale down new driver torque signal to match previous one // update array of samples update_sample(&torque_driver, torque_driver_new); } diff --git a/tests/safety/common.py b/tests/safety/common.py index 2e4b4de563..da66dc7bef 100644 --- a/tests/safety/common.py +++ b/tests/safety/common.py @@ -506,10 +506,6 @@ def test_realtime_limits(self): self.assertTrue(self._tx(self._torque_cmd_msg(sign * (self.MAX_RT_DELTA - 1)))) self.assertTrue(self._tx(self._torque_cmd_msg(sign * (self.MAX_RT_DELTA + 1)))) - def test_soimething(self): - self._common_measurement_test(self._torque_driver_msg, -self.MAX_TORQUE, self.MAX_TORQUE, 1, - self.safety.get_torque_driver_min, self.safety.get_torque_driver_max) - def test_reset_driver_torque_measurements(self): # Tests that the driver torque measurement sample_t is reset on safety mode init for t in np.linspace(-self.MAX_TORQUE, self.MAX_TORQUE, MAX_SAMPLE_VALS): From 2c381077d50178c28a9757285dd773629df253aa Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Mon, 23 Oct 2023 19:41:11 -0700 Subject: [PATCH 3/8] support testing integer torque cmt --- tests/safety/common.py | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/tests/safety/common.py b/tests/safety/common.py index da66dc7bef..489c772990 100644 --- a/tests/safety/common.py +++ b/tests/safety/common.py @@ -99,16 +99,21 @@ 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]): + def _common_measurement_test(self, msg_func: Callable[[int | float], ...], min_value: float, max_value: float, + factor: int, meas_min_func: Callable[[], int], meas_max_func: Callable[[], int], + increment: float = 1): """Tests accurate measurement parsing, and that it's reset on safety mode init""" - for val in np.arange(min_value, max_value, 0.5): + + # Support testing float and int measurement structs using increment argument + incremented_val = increment * (MAX_SAMPLE_VALS - 1) + + for val in np.arange(min_value, max_value, incremented_val): for i in range(MAX_SAMPLE_VALS): - self.assertTrue(self._rx(msg_func(val + i * 0.1))) + self.assertTrue(self._rx(msg_func(val + i * increment))) - # assert close by one decimal place + # assert close by one decimal place if float self.assertAlmostEqual(meas_min_func() / factor, val, delta=0.1) - self.assertAlmostEqual(meas_max_func() / factor - 0.5, val, delta=0.1) + self.assertAlmostEqual(meas_max_func() / factor - incremented_val, val, delta=0.1) # ensure sample_t is reset on safety init self._reset_safety_hooks() @@ -511,7 +516,6 @@ def test_reset_driver_torque_measurements(self): for t in np.linspace(-self.MAX_TORQUE, self.MAX_TORQUE, MAX_SAMPLE_VALS): self.assertTrue(self._rx(self._torque_driver_msg(t))) - print(self.safety.get_torque_driver_max(), self.safety.get_torque_driver_min()) self.assertNotEqual(self.safety.get_torque_driver_min(), 0) self.assertNotEqual(self.safety.get_torque_driver_max(), 0) @@ -676,10 +680,12 @@ def _reset_speed_measurement(self, speed): 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) + self._common_measurement_test(self._speed_msg, 0, 80, VEHICLE_SPEED_FACTOR, + self.safety.get_vehicle_speed_min, self.safety.get_vehicle_speed_max, increment=0.1) 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) + 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, increment=0.1) def test_angle_cmd_when_enabled(self): # when controls are allowed, angle cmd rate limit is enforced From 6c5a19aad82d1413ba409825e7ec2d574fe60162 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Mon, 23 Oct 2023 19:51:38 -0700 Subject: [PATCH 4/8] there we go --- tests/safety/common.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/safety/common.py b/tests/safety/common.py index 489c772990..888bcd08d3 100644 --- a/tests/safety/common.py +++ b/tests/safety/common.py @@ -99,7 +99,7 @@ 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[[int | float], ...], min_value: float, max_value: float, + def _common_measurement_test(self, msg_func: MessageFunction, min_value: float, max_value: float, factor: int, meas_min_func: Callable[[], int], meas_max_func: Callable[[], int], increment: float = 1): """Tests accurate measurement parsing, and that it's reset on safety mode init""" From 411c6e1f62fb3865c6c1b85c19da547aaa1718a7 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Mon, 23 Oct 2023 19:52:18 -0700 Subject: [PATCH 5/8] Revert "there we go" This reverts commit 6c5a19aad82d1413ba409825e7ec2d574fe60162. --- tests/safety/common.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/safety/common.py b/tests/safety/common.py index 888bcd08d3..489c772990 100644 --- a/tests/safety/common.py +++ b/tests/safety/common.py @@ -99,7 +99,7 @@ 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: MessageFunction, min_value: float, max_value: float, + def _common_measurement_test(self, msg_func: Callable[[int | float], ...], min_value: float, max_value: float, factor: int, meas_min_func: Callable[[], int], meas_max_func: Callable[[], int], increment: float = 1): """Tests accurate measurement parsing, and that it's reset on safety mode init""" From 19dc061d13ba2eef6751f80d53ffd49f1c9b1e98 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Mon, 23 Oct 2023 19:52:20 -0700 Subject: [PATCH 6/8] Revert "support testing integer torque" This reverts commit 2c381077d50178c28a9757285dd773629df253aa. --- tests/safety/common.py | 24 +++++++++--------------- 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/tests/safety/common.py b/tests/safety/common.py index 489c772990..da66dc7bef 100644 --- a/tests/safety/common.py +++ b/tests/safety/common.py @@ -99,21 +99,16 @@ 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[[int | float], ...], min_value: float, max_value: float, - factor: int, meas_min_func: Callable[[], int], meas_max_func: Callable[[], int], - increment: float = 1): + 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""" - - # Support testing float and int measurement structs using increment argument - incremented_val = increment * (MAX_SAMPLE_VALS - 1) - - for val in np.arange(min_value, max_value, incremented_val): + 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 * increment))) + self.assertTrue(self._rx(msg_func(val + i * 0.1))) - # assert close by one decimal place if float + # assert close by one decimal place self.assertAlmostEqual(meas_min_func() / factor, val, delta=0.1) - self.assertAlmostEqual(meas_max_func() / factor - incremented_val, 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() @@ -516,6 +511,7 @@ def test_reset_driver_torque_measurements(self): for t in np.linspace(-self.MAX_TORQUE, self.MAX_TORQUE, MAX_SAMPLE_VALS): self.assertTrue(self._rx(self._torque_driver_msg(t))) + print(self.safety.get_torque_driver_max(), self.safety.get_torque_driver_min()) self.assertNotEqual(self.safety.get_torque_driver_min(), 0) self.assertNotEqual(self.safety.get_torque_driver_max(), 0) @@ -680,12 +676,10 @@ def _reset_speed_measurement(self, speed): 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, increment=0.1) + 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, increment=0.1) + 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 From a309da917881fa78896cfc6a9327b9cd4f0b2135 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Mon, 23 Oct 2023 19:52:52 -0700 Subject: [PATCH 7/8] clean up --- tests/safety/common.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/safety/common.py b/tests/safety/common.py index da66dc7bef..ee59d3ecf2 100644 --- a/tests/safety/common.py +++ b/tests/safety/common.py @@ -511,7 +511,6 @@ def test_reset_driver_torque_measurements(self): for t in np.linspace(-self.MAX_TORQUE, self.MAX_TORQUE, MAX_SAMPLE_VALS): self.assertTrue(self._rx(self._torque_driver_msg(t))) - print(self.safety.get_torque_driver_max(), self.safety.get_torque_driver_min()) self.assertNotEqual(self.safety.get_torque_driver_min(), 0) self.assertNotEqual(self.safety.get_torque_driver_max(), 0) From 56f4193849b3533036b3fd722b4b484fe4602b28 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Mon, 23 Oct 2023 19:59:30 -0700 Subject: [PATCH 8/8] Update tests/safety/common.py --- tests/safety/common.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/safety/common.py b/tests/safety/common.py index ee59d3ecf2..3077df2be2 100644 --- a/tests/safety/common.py +++ b/tests/safety/common.py @@ -101,7 +101,7 @@ def _generic_limit_safety_check(self, msg_function: MessageFunction, min_allowed 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""" + """Tests accurate measurement parsing, and that the struct is reset on safety mode init""" 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)))