Files
StarPilot/openpilot/starpilot/longitudinal/tests/test_g90_forecast_stop.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

79 lines
3.6 KiB
Python

"""G90 forecast stop intent; current acceleration and stop arbitration remain intact."""
from unittest.mock import patch
import unittest
from types import SimpleNamespace
import numpy as np
from opendbc.car import gen_empty_fingerprint
from opendbc.car.hyundai.interface import CarInterface
from opendbc.car.hyundai.values import CAR
from openpilot.selfdrive.controls.lib import longitudinal_planner as planner_module
from openpilot.starpilot.longitudinal.force_stop import StopPlan
from openpilot.starpilot.longitudinal.vehicle_policy import forecast_should_stop
from openpilot.starpilot.longitudinal.tests.test_cruise_ceiling import messages
class TestG90ForecastStop(unittest.TestCase):
def test_both_forecasts_strict_boundary_and_delay(self):
cp = SimpleNamespace(carFingerprint=CAR.GENESIS_G90, openpilotLongitudinalControl=True)
times = (0., .5, 1., 1.5, 2.)
threshold = float(np.float32(.8))
for speeds, action, expected in (
((.7,) * 5, .5, True), ((.8,) * 5, .5, True),
((threshold,) * 5, .5, False),
((np.nextafter(threshold, -np.inf),) * 5, .5, True),
((np.nextafter(threshold, np.inf),) * 5, .5, False),
((.7, .7, .7, threshold, .7), .5, False),
((.7, threshold, .7, .7, .7), .5, False),
((.5, .7, .9, .7, .5), .25, True),
((.5, .7, .9, .7, .5), .5, True),
((), .5, True),
):
with self.subTest(speeds=speeds, action=action):
self.assertEqual(forecast_should_stop(cp, speeds, times, action, False), expected)
def test_stock_and_other_cars_retain_actual_fallback(self):
for car, owned in ((CAR.GENESIS_G90, False), (CAR.GENESIS_G80, True)):
cp = SimpleNamespace(carFingerprint=car, openpilotLongitudinalControl=owned)
for fallback in (False, True):
self.assertEqual(forecast_should_stop(cp, (.7, .7), (0., 2.), .5, fallback), fallback)
def planner(self, speed):
cp = CarInterface.get_params(CAR.GENESIS_G90, gen_empty_fingerprint(), [], True, False, False)
self.assertTrue(cp.openpilotLongitudinalControl)
planner = planner_module.LongitudinalPlanner(cp, init_v=1.)
# Replace only the solver's trajectory production, leaving the real planner
# acceleration calculation, smoothing and stop candidate arbitration reached.
def trajectory(*args, **kwargs):
planner.mpc.v_solution[:] = speed
planner.mpc.a_solution[:] = 0.
planner.mpc.j_solution[:] = 0.
self.enterContext(patch.object(planner.mpc, 'update', trajectory, create=True))
return planner
def test_actual_planner_forecast_changes_only_stop_intent(self):
sm, _ = messages()
sm['carState'].vEgo = 1.
actual, baseline = self.planner(.7), self.planner(.7)
actual.update(sm)
with patch.object(planner_module, 'forecast_should_stop', side_effect=lambda *args, **kw: kw['fallback']):
baseline.update(sm)
self.assertTrue(actual.output_should_stop)
self.assertFalse(baseline.output_should_stop)
self.assertEqual(actual.output_a_target, baseline.output_a_target)
self.assertEqual(actual.a_cruise, baseline.a_cruise)
np.testing.assert_array_equal(actual.v_desired_trajectory, baseline.v_desired_trajectory)
def test_actual_planner_preserves_e2e_and_force_stop(self):
for e2e, force in ((True, False), (False, True)):
sm, _ = messages(e2e=e2e)
class CurrentMessages(dict):
logMonoTime = {'modelV2': 100}
sm = CurrentMessages(sm)
sm['carState'].vEgo = 1.
sm['carControl'].longActive = True
planner = self.planner(.9)
planner.update(sm, force_stop_provider=(lambda _: StopPlan(model_ns=100, should_stop=True)) if force else None)
self.assertTrue(planner.output_should_stop)