mirror of
https://github.com/firestar5683/StarPilot.git
synced 2026-11-06 20:43:46 +08:00
7f3b68f0a8
Update Toyota ADAS messages and control interfaces with platform-specific safety tests. Co-authored-by: Dom <168790843+firestar5683@users.noreply.github.com> Co-authored-by: pharmacomaniac <blittle65@gmail.com>
104 lines
5.7 KiB
Python
104 lines
5.7 KiB
Python
"""Actual source transitions must not silently alter vehicle startup limits."""
|
|
|
|
from dataclasses import replace
|
|
from unittest import mock
|
|
import unittest
|
|
|
|
from opendbc.car.car_helpers import interfaces
|
|
from opendbc.car.hyundai.values import CAR as HYUNDAI
|
|
from opendbc.car.toyota.values import CAR as TOYOTA
|
|
from openpilot.common.params import Params
|
|
from openpilot.common.prefix import OpenpilotPrefix
|
|
from openpilot.common.realtime import DT_CTRL
|
|
from openpilot.selfdrive.controls.lib.latcontrol_torque import LatControlTorque
|
|
from openpilot.starpilot.lateral.torque_runtime import TorqueHost
|
|
from openpilot.starpilot.lateral.torque_tuning import TorqueSource
|
|
|
|
|
|
class TestTorqueApplication(unittest.TestCase):
|
|
def controller(self, vehicle):
|
|
cp = interfaces[vehicle].get_non_essential_params(vehicle)
|
|
controller = LatControlTorque(cp.as_reader(), interfaces[vehicle](cp), DT_CTRL)
|
|
return TorqueHost(Params(), cp), controller
|
|
|
|
def test_initial_vehicle_preserves_ioniq_startup_and_explicit_equal_source_updates(self):
|
|
with OpenpilotPrefix():
|
|
for source in (TorqueSource.LEARNED, TorqueSource.USER):
|
|
with self.subTest(source=source):
|
|
host, controller = self.controller(HYUNDAI.HYUNDAI_IONIQ_6)
|
|
with mock.patch.object(controller, 'update_torque_parameters', wraps=controller.update_torque_parameters) as update:
|
|
initial_limit = controller.pid.pos_limit
|
|
for _ in range(3):
|
|
self.assertFalse(host.apply(controller, host.vehicle))
|
|
update.assert_not_called()
|
|
self.assertEqual(controller.pid.pos_limit, initial_limit)
|
|
self.assertAlmostEqual(initial_limit, 3.0 * 1.22, places=6)
|
|
selected = replace(host.vehicle, source=source)
|
|
self.assertTrue(host.apply(controller, selected))
|
|
self.assertAlmostEqual(controller.pid.pos_limit, 3.0 * 1.22, places=6)
|
|
self.assertFalse(host.apply(controller, selected))
|
|
self.assertEqual(update.call_count, 1)
|
|
self.assertAlmostEqual(controller.torque_params.latAccelFactor, 3.0 * 1.22, places=6)
|
|
|
|
def test_changed_values_fallback_and_new_controller_do_not_inherit_application(self):
|
|
with OpenpilotPrefix():
|
|
host, controller = self.controller(HYUNDAI.HYUNDAI_IONIQ_6)
|
|
custom = replace(host.vehicle, source=TorqueSource.USER, lat_accel_factor=3.3, friction=0.12)
|
|
self.assertTrue(host.apply(controller, custom))
|
|
self.assertAlmostEqual(controller.torque_params.latAccelFactor, 3.3 * 1.22, places=6)
|
|
self.assertTrue(host.apply(controller, host.vehicle))
|
|
self.assertAlmostEqual(controller.torque_params.latAccelFactor, 3.0 * 1.22, places=6)
|
|
self.assertAlmostEqual(controller.torque_params.friction, 0.09, places=6)
|
|
# A fallback restores values through the normal update API. It does not
|
|
# rewind the controller's history to its pre-update initialization state.
|
|
self.assertAlmostEqual(controller.pid.pos_limit, 3.0 * 1.22, places=6)
|
|
self.assertFalse(host.apply(controller, host.vehicle))
|
|
_, other = self.controller(HYUNDAI.HYUNDAI_IONIQ_6)
|
|
self.assertFalse(host.apply(other, host.vehicle))
|
|
self.assertAlmostEqual(other.pid.pos_limit, 3.0 * 1.22, places=6)
|
|
self.assertTrue(host.apply(other, custom))
|
|
|
|
def test_vehicle_only_samples_do_not_unlock_limit(self):
|
|
with OpenpilotPrefix():
|
|
host, controller = self.controller(HYUNDAI.HYUNDAI_IONIQ_6)
|
|
# Inactive or malformed saved settings return the exact vehicle source.
|
|
sm = mock.Mock()
|
|
sm.all_checks.return_value = False
|
|
host.params.put_bool('AdvancedLateralTune', True, block=True)
|
|
host.params.put('TorqueOverrideDocument', {'invalid': True}, block=True)
|
|
for now, active in ((1_000_000_000, False), (1_010_000_000, True), (2_000_000_000, True)):
|
|
tune = host.sample(sm, now_ns=now, lat_active=active)
|
|
self.assertEqual(tune, host.vehicle)
|
|
self.assertFalse(host.apply(controller, tune))
|
|
self.assertAlmostEqual(controller.pid.pos_limit, 3.0 * 1.22, places=6)
|
|
|
|
def test_inactive_fallback_and_failed_application_are_not_cached_as_success(self):
|
|
with OpenpilotPrefix():
|
|
host, controller = self.controller(HYUNDAI.HYUNDAI_IONIQ_6)
|
|
custom = replace(host.vehicle, source=TorqueSource.USER, lat_accel_factor=3.3, friction=0.12)
|
|
with mock.patch.object(controller, 'update_torque_parameters', side_effect=ValueError('rejected')):
|
|
with self.assertRaises(ValueError):
|
|
host.apply(controller, custom)
|
|
self.assertTrue(host.apply(controller, custom))
|
|
self.assertAlmostEqual(controller.torque_params.latAccelFactor, 3.3 * 1.22, places=6)
|
|
fallback = host.sample(mock.Mock(), now_ns=1_000_000_000, lat_active=False)
|
|
self.assertEqual(fallback, host.vehicle)
|
|
self.assertTrue(host.apply(controller, fallback))
|
|
self.assertAlmostEqual(controller.torque_params.latAccelFactor, 3.0 * 1.22, places=6)
|
|
self.assertAlmostEqual(controller.torque_params.friction, 0.09, places=6)
|
|
|
|
def test_non_ioniq_controller_and_cross_vehicle_rejection(self):
|
|
with OpenpilotPrefix():
|
|
host, controller = self.controller(TOYOTA.TOYOTA_COROLLA_TSS2)
|
|
original = (controller.pid.pos_limit, controller.pid.neg_limit)
|
|
self.assertFalse(host.apply(controller, host.vehicle))
|
|
self.assertEqual((controller.pid.pos_limit, controller.pid.neg_limit), original)
|
|
self.assertTrue(host.apply(controller, replace(host.vehicle, source=TorqueSource.USER)))
|
|
self.assertEqual((controller.pid.pos_limit, controller.pid.neg_limit), original)
|
|
with self.assertRaises(ValueError):
|
|
host.apply(controller, replace(host.vehicle, vehicle='HYUNDAI_IONIQ_6'))
|
|
|
|
|
|
if __name__ == '__main__':
|
|
unittest.main()
|