mirror of
https://github.com/firestar5683/StarPilot.git
synced 2026-11-05 03:54:04 +08:00
Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 68c0b55a00 | |||
| 446801ca74 | |||
| 7fe8dd5e83 | |||
| 72d886cef9 | |||
| b161b33ae8 | |||
| b0c03bdbda | |||
| 08cfff03af | |||
| af4d7e5a0d |
@@ -343,8 +343,6 @@ jobs:
|
||||
run: scons
|
||||
- name: Gate every assigned lifecycle module serially
|
||||
timeout-minutes: 10
|
||||
env:
|
||||
RAYLIB_BACKEND: headless
|
||||
run: python3 tools/ci/run_vehicle_tests.py --suite aol-lifecycle --shard-index "${{ matrix.shard }}" --shard-count 16 --output "ci-results/aol-lifecycle-${{ matrix.shard }}"
|
||||
- name: Save every lifecycle outcome and native build
|
||||
if: always()
|
||||
@@ -445,7 +443,7 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
shard: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
|
||||
shard: [0, 1, 2, 3]
|
||||
env:
|
||||
FUZZ_SEED: '0'
|
||||
steps:
|
||||
@@ -455,9 +453,7 @@ jobs:
|
||||
run: scons
|
||||
- name: Run every assigned RELEASE target serially
|
||||
timeout-minutes: 20
|
||||
env:
|
||||
RAYLIB_BACKEND: headless
|
||||
run: python3 tools/ci/run_vehicle_tests.py --suite safety-release --shard-index "${{ matrix.shard }}" --shard-count 16 --output "ci-results/safety-release-${{ matrix.shard }}"
|
||||
run: python3 tools/ci/run_vehicle_tests.py --suite safety-release --shard-index "${{ matrix.shard }}" --shard-count 4 --output "ci-results/safety-release-${{ matrix.shard }}"
|
||||
- name: Save every RELEASE outcome and native build
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v4
|
||||
@@ -480,7 +476,7 @@ jobs:
|
||||
pattern: safety-release-*
|
||||
path: ci-results/safety-release-shards
|
||||
- name: Require every RELEASE case exactly once and passed
|
||||
run: python3 tools/ci/lifecycle_shards.py ci-results/safety-release-shards --suite safety-release --count 16 --output ci-results/safety-release-complete.json
|
||||
run: python3 tools/ci/lifecycle_shards.py ci-results/safety-release-shards --suite safety-release --count 4 --output ci-results/safety-release-complete.json
|
||||
- name: Save complete RELEASE verdict
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v4
|
||||
|
||||
+4
-37
@@ -57,37 +57,6 @@ EOF
|
||||
fi
|
||||
}
|
||||
|
||||
function update_is_ready {
|
||||
[ -f "$1" ] && [ ! -L "$1" ]
|
||||
}
|
||||
|
||||
# A validated legacy return records the OS it was staged against. Check it on
|
||||
# every boot before swapping trees, including manual reboots.
|
||||
# Keep this in sync with TRUSTED_LEGACY_RELEASES in updated.py.
|
||||
TRUSTED_LEGACY_STARPILOT_COMMIT="4a553da9ac22a14e34513c011aed04c1e93b4619"
|
||||
|
||||
function staged_os_is_compatible {
|
||||
local staged="$1" marker="$1/.git/starpilot-retain-agnos" commit
|
||||
commit="$(git -C "$staged" rev-parse --verify 'HEAD^{commit}')" || return 1
|
||||
if [ "$commit" = "$TRUSTED_LEGACY_STARPILOT_COMMIT" ]; then
|
||||
[ -f "$marker" ] && [ -s "$marker" ] && [ ! -L "$marker" ] && [ -r /VERSION ] && [ "$(cat "$marker")" = "$(< /VERSION)" ]
|
||||
else
|
||||
[ ! -e "$marker" ] && [ ! -L "$marker" ]
|
||||
fi
|
||||
}
|
||||
|
||||
function reset_update_os_environment {
|
||||
unset AGNOS_VERSION AGNOS_ACCEPTED_VERSIONS
|
||||
}
|
||||
|
||||
function maybe_activate_legacy_agnos {
|
||||
local repo="$1" verifier="$2" manifest="$3" commit
|
||||
commit="$(git -C "$repo" rev-parse --verify 'HEAD^{commit}')" || return 1
|
||||
if [ "$commit" != "$TRUSTED_LEGACY_STARPILOT_COMMIT" ] && [ ! -L "$manifest" ]; then
|
||||
"$verifier" --verify "$manifest" && sudo reboot
|
||||
fi
|
||||
}
|
||||
|
||||
function launch {
|
||||
# Remove orphaned git lock if it exists on boot
|
||||
[ -f "$DIR/.git/index.lock" ] && rm -f "$DIR/.git/index.lock"
|
||||
@@ -106,10 +75,8 @@ function launch {
|
||||
if [ $? -eq 0 ]; then
|
||||
echo "${DIR} has been modified, skipping overlay update installation"
|
||||
else
|
||||
if update_is_ready "${STAGING_ROOT}/finalized/.overlay_consistent"; then
|
||||
if ! staged_os_is_compatible "${STAGING_ROOT}/finalized"; then
|
||||
echo "Staged update requires a different or unverified AGNOS version; skipping installation."
|
||||
elif [ ! -d /data/safe_staging/old_openpilot ]; then
|
||||
if [ -f "${STAGING_ROOT}/finalized/.overlay_consistent" ]; then
|
||||
if [ ! -d /data/safe_staging/old_openpilot ]; then
|
||||
echo "Valid overlay update found, installing"
|
||||
LAUNCHER_LOCATION="${BASH_SOURCE[0]}"
|
||||
|
||||
@@ -117,10 +84,10 @@ function launch {
|
||||
mv "${STAGING_ROOT}/finalized" "$DIR"
|
||||
cd "$DIR"
|
||||
# releases before the openpilot/ move can't swap AGNOS on newer AGNOS, use ours
|
||||
maybe_activate_legacy_agnos "$DIR" /data/safe_staging/old_openpilot/openpilot/common/hardware/comma/agnos.py system/hardware/tici/agnos.json
|
||||
[ ! -L system/hardware/tici/agnos.json ] && /data/safe_staging/old_openpilot/openpilot/common/hardware/comma/agnos.py --verify system/hardware/tici/agnos.json && sudo reboot
|
||||
|
||||
echo "Restarting launch script ${LAUNCHER_LOCATION}"
|
||||
reset_update_os_environment
|
||||
unset AGNOS_VERSION
|
||||
exec "${LAUNCHER_LOCATION}"
|
||||
else
|
||||
echo "openpilot backup found, not updating"
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -360,6 +360,7 @@ class CarController(CarControllerBase):
|
||||
self.volt_cc_cancel_pending = False
|
||||
self.volt_cc_last_cancel_frame = 0
|
||||
self.volt_cc_metric = False
|
||||
self.volt_cc_speed_ceiling_mps = None
|
||||
self.volt_cc_last_button_frame = 0
|
||||
self.volt_cc_last_direction_frame = None
|
||||
self.volt_cc_consumed_source_ns = 0
|
||||
@@ -1396,7 +1397,10 @@ class CarController(CarControllerBase):
|
||||
self.volt_cc_consumed_source_ns = source_ns
|
||||
can_sends.append(gmcan.create_buttons(self.packer_pt, CanBus.POWERTRAIN, (CS.buttons_counter + 1) % 4, button))
|
||||
elif self.frame % 4 == 0:
|
||||
button, rate = button_request(CS.out.vEgo, CS.out.cruiseState.speed, actuators.accel, CS.out.vCruise,
|
||||
cruise_kph = CS.out.vCruise
|
||||
if self.volt_cc_speed_ceiling_mps is not None:
|
||||
cruise_kph = min(cruise_kph, self.volt_cc_speed_ceiling_mps * CV.MS_TO_KPH)
|
||||
button, rate = button_request(CS.out.vEgo, CS.out.cruiseState.speed, actuators.accel, cruise_kph,
|
||||
bool(CC.hudControl.leadVisible), self.volt_cc_metric)
|
||||
source_ns = physical.button_credit_ns
|
||||
if ((self.frame - self.volt_cc_last_button_frame) * DT_CTRL > rate and
|
||||
|
||||
@@ -335,8 +335,8 @@ def test_actual_card_active_observation_requires_fresh_selfdrive_state(monkeypat
|
||||
host.observe_ioniq6_long_authority = lambda *args: None
|
||||
host.update_vehicle_state_context = lambda *args: None
|
||||
|
||||
def update_cruise_speed(_state, _pending, *, approved=None):
|
||||
assert approved is None
|
||||
def update_cruise_speed(_state, _pending, *, wheel_context=None):
|
||||
assert wheel_context is None
|
||||
host.update_cruise_speed = update_cruise_speed
|
||||
host.v_cruise_helper = SimpleNamespace(slc_consumed_button=None, toyota=None)
|
||||
host.startup_panda_configured = lambda: True
|
||||
|
||||
@@ -244,14 +244,14 @@ class TestIoniq6PrepublicationHandoff(unittest.TestCase):
|
||||
cp = CarInterface.get_params(model, fp, [], False, False, False)
|
||||
ci = CarInterface(cp)
|
||||
packer = CANPacker(DBC[model][Bus.pt])
|
||||
ci.update([(1_000_000_000, [])])
|
||||
ci.update((1_000_000_000, []))
|
||||
sources = [(parser, state) for parser in ci.can_parsers.values()
|
||||
for state in parser.message_states.values() if not state.ignore_alive and state.address != 0x2E0]
|
||||
for frame in range(120):
|
||||
packets = [packer.make_can_msg(state.name, parser.bus,
|
||||
{"COUNTER": frame % 256} if any(sig.name == "COUNTER" for sig in state.signals) else {})
|
||||
for parser, state in sources]
|
||||
state = ci.update([(1_000_000_000 + frame * 100_000_000, packets)])
|
||||
state = ci.update((1_000_000_000 + frame * 100_000_000, packets))
|
||||
self.assertTrue(state.canValid)
|
||||
self.assertFalse(cp.openpilotLongitudinalControl)
|
||||
self.assertTrue(cp.pcmCruise)
|
||||
@@ -261,7 +261,7 @@ class TestIoniq6PrepublicationHandoff(unittest.TestCase):
|
||||
packets = [packer.make_can_msg(msg.name, parser.bus,
|
||||
{"COUNTER": frame % 256} if any(sig.name == "COUNTER" for sig in msg.signals) else {})
|
||||
for parser, msg in sources if msg.name != "MDPS"]
|
||||
state = ci.update([(1_000_000_000 + frame * 100_000_000, packets)])
|
||||
state = ci.update((1_000_000_000 + frame * 100_000_000, packets))
|
||||
self.assertFalse(state.canValid)
|
||||
|
||||
def test_card_tags_aol_candidate_before_cp_publication_without_disabling_ecu(self):
|
||||
@@ -870,7 +870,7 @@ class TestIoniq6PrepublicationHandoff(unittest.TestCase):
|
||||
card_owner.is_metric = True
|
||||
card_owner.experimental_mode = False
|
||||
card_owner.initial_cruise_speed = 40
|
||||
card_owner.v_cruise_helper = SimpleNamespace(update_v_cruise=lambda *_, slc_enabled=False, slc_approved=None: None,
|
||||
card_owner.v_cruise_helper = SimpleNamespace(update_v_cruise=lambda *_, slc_enabled=False, wheel_context=None: None,
|
||||
initialize_v_cruise=lambda *_, **kwargs: None, slc_consumed_button=None, toyota=None,
|
||||
slc_cruise_change=None, v_cruise_kph=60.0, v_cruise_cluster_kph=60.0)
|
||||
card_owner.RI = SimpleNamespace(update=lambda _: None)
|
||||
|
||||
Regular → Executable
+1
-6
@@ -77,12 +77,7 @@ class RadarInterface(RadarInterfaceBase):
|
||||
if not self.rcp.can_valid:
|
||||
ret.errors.canError = True
|
||||
|
||||
status = self.rcp.vl['STATUS_MSG']
|
||||
pre_fault = status['RADAR_PRE_FAULT']
|
||||
# Legacy pre-warning can rise on an empty road before the documented dirty-radar threshold.
|
||||
legacy_pre_warning = (not self.CP.flags & ToyotaFlags.TSS2 and pre_fault == 3 and
|
||||
status['INVALID_COUNTER'] < 2659)
|
||||
if status['RADAR_STATUS'] != 1 or pre_fault != 0 and not legacy_pre_warning:
|
||||
if self.rcp.vl['STATUS_MSG']['RADAR_STATUS'] != 1 or self.rcp.vl['STATUS_MSG']['RADAR_PRE_FAULT'] != 0:
|
||||
ret.errors.radarUnavailableTemporary = True
|
||||
|
||||
for ii in sorted(updated_messages):
|
||||
|
||||
@@ -659,9 +659,7 @@ static void hyundai_canfd_rx_hook(const CANPacket_t *msg) {
|
||||
aol_optional_release_neutral = false;
|
||||
aol_optional_release_previous = 0U;
|
||||
aol_optional_release_pending = false;
|
||||
if ((elapsed == 0U) || (elapsed > 100000U)) {
|
||||
aol_optional_release_latch = false;
|
||||
}
|
||||
aol_optional_release_latch = false;
|
||||
}
|
||||
const bool raw_neutral = fresh && !main_button &&
|
||||
!(hyundai_canfd_alt_buttons ? GET_BIT(msg, 39U) : GET_BIT(msg, 23U)) && (cruise_button == HYUNDAI_BTN_NONE);
|
||||
|
||||
@@ -68,9 +68,9 @@ class TestHyundaiOptionalRelease(unittest.TestCase):
|
||||
self.buttons(button)
|
||||
self.buttons()
|
||||
|
||||
def claimed(self, button=2):
|
||||
def claimed(self):
|
||||
self.safety.aol_set_host_request(5)
|
||||
self.release_gesture(button)
|
||||
self.release_gesture()
|
||||
self.assert_rx_profile()
|
||||
self.safety.aol_set_host_request(7)
|
||||
self.buttons(4)
|
||||
@@ -200,35 +200,3 @@ class TestHyundaiOptionalRelease(unittest.TestCase):
|
||||
self.assertEqual(self.safety.aol_get_request_mask(), 0, self.context)
|
||||
self.assertEqual(self.safety.aol_get_permission_mask(), 0, self.context)
|
||||
self.assertFalse(self.tx(1, 1), self.context)
|
||||
|
||||
def test_claimed_lateral_survives_timely_button_counter_interruption(self):
|
||||
for raw in (0x815, 0x895):
|
||||
for button in (1, 2):
|
||||
for defect in ("replay", "gap"):
|
||||
with self.subTest(raw=raw, button=button, defect=defect):
|
||||
self.mode(raw)
|
||||
self.claimed(button)
|
||||
self.now += 20_000
|
||||
self.safety.set_timer(self.now)
|
||||
self.healthy()
|
||||
counter = self.counters["CRUISE_BUTTONS"] % 16
|
||||
self.buttons(counter=counter if defect == "replay" else (counter + 2) % 16)
|
||||
self.assert_rx_profile()
|
||||
self.assertEqual(self.safety.aol_get_permission_mask(), 0 if self.release else 1, self.context)
|
||||
self.assertEqual(bool(self.tx(1, 1)), not self.release, self.context)
|
||||
self.buttons(counter=(counter + 2) % 16)
|
||||
self.safety.aol_set_host_request(5)
|
||||
self.assertFalse(self.safety.get_controls_allowed(), self.context)
|
||||
self.assertEqual(self.safety.aol_get_permission_mask(), 0 if self.release else 1, self.context)
|
||||
self.safety.aol_set_host_request(0)
|
||||
self.safety.aol_set_host_request(5)
|
||||
self.assert_denied()
|
||||
|
||||
def test_claimed_lateral_still_requires_bounded_button_source_time(self):
|
||||
for raw in (0x815, 0x895):
|
||||
for elapsed in (0, 100_001):
|
||||
with self.subTest(raw=raw, elapsed=elapsed):
|
||||
self.mode(raw)
|
||||
self.claimed()
|
||||
self.buttons(counter=self.counters["CRUISE_BUTTONS"] % 16, advance=elapsed)
|
||||
self.assert_denied()
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
"""Malformed and contradictory physical sources withdraw receiver credits."""
|
||||
import unittest
|
||||
from opendbc.car.structs import CarParams
|
||||
from opendbc.safety.tests import test_subaru_software_cruise as software
|
||||
from opendbc.safety.tests import test_subaru_sng as sng
|
||||
from opendbc.safety.tests import test_subaru_stock_aol as stock
|
||||
@@ -41,36 +40,12 @@ class TestSubaruReceiveGuards(unittest.TestCase):
|
||||
|
||||
def test_stock_aol_truncated_optional_gear_withdraws(self):
|
||||
case = stock.TestSubaruStockAol()
|
||||
self.addCleanup(case.tearDown)
|
||||
case.configure()
|
||||
case.arm()
|
||||
gear = case.frame('Transmission', {'Gear': 121})
|
||||
case.safety.safety_rx_hook(libsafety_py.make_CANPacket(gear.addr, gear.bus, bytes(gear.data[0:7])))
|
||||
self.assertEqual(case.safety.aol_get_permission_mask(), 0)
|
||||
|
||||
def test_stock_aol_cleanup_runs_on_success_failure_and_error(self):
|
||||
"""The manually owned helper must not leave its native AOL profile for the next test."""
|
||||
safety = libsafety_py.libsafety
|
||||
for outcome in ('success', 'failure', 'error'):
|
||||
with self.subTest(outcome=outcome):
|
||||
class OwnedCase(TestSubaruReceiveGuards):
|
||||
def runTest(self, expected_outcome=outcome):
|
||||
self.test_stock_aol_truncated_optional_gear_withdraws()
|
||||
if expected_outcome == 'failure':
|
||||
self.fail('deliberate failure after the real receiver assertions')
|
||||
if expected_outcome == 'error':
|
||||
raise RuntimeError('deliberate error after the real receiver assertions')
|
||||
|
||||
result = unittest.TestResult()
|
||||
OwnedCase().run(result)
|
||||
self.assertEqual(result.testsRun, 1)
|
||||
self.assertEqual(len(result.failures), int(outcome == 'failure'))
|
||||
self.assertEqual(len(result.errors), int(outcome == 'error'))
|
||||
self.assertEqual(safety.get_current_safety_mode(), CarParams.SafetyModel.noOutput)
|
||||
self.assertFalse(safety.get_controls_allowed())
|
||||
self.assertEqual(safety.aol_get_request_mask(), 0)
|
||||
self.assertEqual(safety.aol_get_permission_mask(), 0)
|
||||
|
||||
def test_sng_conflicting_duplicate_withdraws_source_and_forwarding_lease(self):
|
||||
case = sng.TestSubaruSng()
|
||||
case.setup()
|
||||
|
||||
@@ -333,7 +333,7 @@ struct SlcState @0xa1680744031fdb2d {
|
||||
hasPendingAdjustedLimit @45 :Bool;
|
||||
presentationAdjustedLimit @46 :Float32;
|
||||
hasPresentationAdjustedLimit @47 :Bool;
|
||||
overrideBasis @48 :Text; # none/pedal/persistent; qualified contribution only
|
||||
overrideBasis @48 :Text; # none/pedal/persistent/wheel; qualified contribution only
|
||||
retainedOverride @49 :Bool;
|
||||
commandActionSequenceId @50 :UInt64; # UI sequence that issued this command
|
||||
automaticAdoptionPending @51 :Bool;
|
||||
@@ -346,6 +346,10 @@ struct SlcState @0xa1680744031fdb2d {
|
||||
# Qualified adoption proposal; the current displayed observation stays above.
|
||||
upcomingSpeedLimit @57 :Float32;
|
||||
upcomingBoundaryId @58 :Text;
|
||||
acceptedIsPreview @59 :Bool; # a temporary ceiling, ineligible for initializing SET
|
||||
wheelOverrideSince @60 :UInt64; # nonzero while a wheel tap may cross the current adjusted limit
|
||||
wheelAdoptMonoTime @61 :UInt64; # Card's original wheel event timestamp, only on the approval frame
|
||||
wheelAdoptTargetMps @62 :Float32; # exact adjusted SET approved for that event
|
||||
struct SourceReading {
|
||||
source @0 :Text;
|
||||
enabled @1 :Bool;
|
||||
@@ -447,6 +451,7 @@ struct SlcAction @0x9ccdc8676701b412 {
|
||||
conditionalManual @5 :ConditionalManualAction;
|
||||
controllerCruise @6 :ControllerCruiseAction;
|
||||
controllerMode @7 :ControllerCruiseAction;
|
||||
wheelAdoptMonoTime @8 :UInt64; # original timestamp of a qualified Adopt Speed Limit wheel event
|
||||
|
||||
struct ControllerCruiseAction {
|
||||
version @0 :UInt16;
|
||||
@@ -514,6 +519,7 @@ struct SlcCruiseEvent @0xcd96dafb67a082d0 {
|
||||
trafficMode @13 :TrafficModeGesture;
|
||||
wheelAction @14 :SlcAction.ControllerCruiseAction;
|
||||
teslaStockCruise @15 :TeslaStockCruise;
|
||||
wheelOverrideSince @16 :UInt64; # SLC eligibility captured by a physical + tap; zero for ordinary SET changes
|
||||
|
||||
struct TeslaStockCruise {
|
||||
version @0 :UInt16;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -730,7 +730,7 @@ struct SlcState {
|
||||
struct ConditionalModeState;
|
||||
|
||||
struct _capnpPrivate {
|
||||
CAPNP_DECLARE_STRUCT_HEADER(a1680744031fdb2d, 18, 22)
|
||||
CAPNP_DECLARE_STRUCT_HEADER(a1680744031fdb2d, 21, 22)
|
||||
#if !CAPNP_LITE
|
||||
static constexpr ::capnp::_::RawBrandedSchema const* brand() { return &schema->defaultBrand; }
|
||||
#endif // !CAPNP_LITE
|
||||
@@ -826,7 +826,7 @@ struct SlcAction {
|
||||
|
||||
|
||||
struct _capnpPrivate {
|
||||
CAPNP_DECLARE_STRUCT_HEADER(9ccdc8676701b412, 4, 4)
|
||||
CAPNP_DECLARE_STRUCT_HEADER(9ccdc8676701b412, 5, 4)
|
||||
#if !CAPNP_LITE
|
||||
static constexpr ::capnp::_::RawBrandedSchema const* brand() { return &schema->defaultBrand; }
|
||||
#endif // !CAPNP_LITE
|
||||
@@ -878,7 +878,7 @@ struct SlcCruiseEvent {
|
||||
struct ManualModeGesture;
|
||||
|
||||
struct _capnpPrivate {
|
||||
CAPNP_DECLARE_STRUCT_HEADER(cd96dafb67a082d0, 7, 6)
|
||||
CAPNP_DECLARE_STRUCT_HEADER(cd96dafb67a082d0, 8, 6)
|
||||
#if !CAPNP_LITE
|
||||
static constexpr ::capnp::_::RawBrandedSchema const* brand() { return &schema->defaultBrand; }
|
||||
#endif // !CAPNP_LITE
|
||||
@@ -4366,6 +4366,14 @@ public:
|
||||
inline bool hasUpcomingBoundaryId() const;
|
||||
inline ::capnp::Text::Reader getUpcomingBoundaryId() const;
|
||||
|
||||
inline bool getAcceptedIsPreview() const;
|
||||
|
||||
inline ::uint64_t getWheelOverrideSince() const;
|
||||
|
||||
inline ::uint64_t getWheelAdoptMonoTime() const;
|
||||
|
||||
inline float getWheelAdoptTargetMps() const;
|
||||
|
||||
private:
|
||||
::capnp::_::StructReader _reader;
|
||||
template <typename, ::capnp::Kind>
|
||||
@@ -4659,6 +4667,18 @@ public:
|
||||
inline void adoptUpcomingBoundaryId(::capnp::Orphan< ::capnp::Text>&& value);
|
||||
inline ::capnp::Orphan< ::capnp::Text> disownUpcomingBoundaryId();
|
||||
|
||||
inline bool getAcceptedIsPreview();
|
||||
inline void setAcceptedIsPreview(bool value);
|
||||
|
||||
inline ::uint64_t getWheelOverrideSince();
|
||||
inline void setWheelOverrideSince( ::uint64_t value);
|
||||
|
||||
inline ::uint64_t getWheelAdoptMonoTime();
|
||||
inline void setWheelAdoptMonoTime( ::uint64_t value);
|
||||
|
||||
inline float getWheelAdoptTargetMps();
|
||||
inline void setWheelAdoptTargetMps(float value);
|
||||
|
||||
private:
|
||||
::capnp::_::StructBuilder _builder;
|
||||
template <typename, ::capnp::Kind>
|
||||
@@ -5511,6 +5531,8 @@ public:
|
||||
inline bool hasControllerMode() const;
|
||||
inline ::cereal::SlcAction::ControllerCruiseAction::Reader getControllerMode() const;
|
||||
|
||||
inline ::uint64_t getWheelAdoptMonoTime() const;
|
||||
|
||||
private:
|
||||
::capnp::_::StructReader _reader;
|
||||
template <typename, ::capnp::Kind>
|
||||
@@ -5579,6 +5601,9 @@ public:
|
||||
inline void adoptControllerMode(::capnp::Orphan< ::cereal::SlcAction::ControllerCruiseAction>&& value);
|
||||
inline ::capnp::Orphan< ::cereal::SlcAction::ControllerCruiseAction> disownControllerMode();
|
||||
|
||||
inline ::uint64_t getWheelAdoptMonoTime();
|
||||
inline void setWheelAdoptMonoTime( ::uint64_t value);
|
||||
|
||||
private:
|
||||
::capnp::_::StructBuilder _builder;
|
||||
template <typename, ::capnp::Kind>
|
||||
@@ -5970,6 +5995,8 @@ public:
|
||||
inline bool hasTeslaStockCruise() const;
|
||||
inline ::cereal::SlcCruiseEvent::TeslaStockCruise::Reader getTeslaStockCruise() const;
|
||||
|
||||
inline ::uint64_t getWheelOverrideSince() const;
|
||||
|
||||
private:
|
||||
::capnp::_::StructReader _reader;
|
||||
template <typename, ::capnp::Kind>
|
||||
@@ -6070,6 +6097,9 @@ public:
|
||||
inline void adoptTeslaStockCruise(::capnp::Orphan< ::cereal::SlcCruiseEvent::TeslaStockCruise>&& value);
|
||||
inline ::capnp::Orphan< ::cereal::SlcCruiseEvent::TeslaStockCruise> disownTeslaStockCruise();
|
||||
|
||||
inline ::uint64_t getWheelOverrideSince();
|
||||
inline void setWheelOverrideSince( ::uint64_t value);
|
||||
|
||||
private:
|
||||
::capnp::_::StructBuilder _builder;
|
||||
template <typename, ::capnp::Kind>
|
||||
@@ -19713,6 +19743,62 @@ inline ::capnp::Orphan< ::capnp::Text> SlcState::Builder::disownUpcomingBoundary
|
||||
::capnp::bounded<21>() * ::capnp::POINTERS));
|
||||
}
|
||||
|
||||
inline bool SlcState::Reader::getAcceptedIsPreview() const {
|
||||
return _reader.getDataField<bool>(
|
||||
::capnp::bounded<141>() * ::capnp::ELEMENTS);
|
||||
}
|
||||
|
||||
inline bool SlcState::Builder::getAcceptedIsPreview() {
|
||||
return _builder.getDataField<bool>(
|
||||
::capnp::bounded<141>() * ::capnp::ELEMENTS);
|
||||
}
|
||||
inline void SlcState::Builder::setAcceptedIsPreview(bool value) {
|
||||
_builder.setDataField<bool>(
|
||||
::capnp::bounded<141>() * ::capnp::ELEMENTS, value);
|
||||
}
|
||||
|
||||
inline ::uint64_t SlcState::Reader::getWheelOverrideSince() const {
|
||||
return _reader.getDataField< ::uint64_t>(
|
||||
::capnp::bounded<18>() * ::capnp::ELEMENTS);
|
||||
}
|
||||
|
||||
inline ::uint64_t SlcState::Builder::getWheelOverrideSince() {
|
||||
return _builder.getDataField< ::uint64_t>(
|
||||
::capnp::bounded<18>() * ::capnp::ELEMENTS);
|
||||
}
|
||||
inline void SlcState::Builder::setWheelOverrideSince( ::uint64_t value) {
|
||||
_builder.setDataField< ::uint64_t>(
|
||||
::capnp::bounded<18>() * ::capnp::ELEMENTS, value);
|
||||
}
|
||||
|
||||
inline ::uint64_t SlcState::Reader::getWheelAdoptMonoTime() const {
|
||||
return _reader.getDataField< ::uint64_t>(
|
||||
::capnp::bounded<19>() * ::capnp::ELEMENTS);
|
||||
}
|
||||
|
||||
inline ::uint64_t SlcState::Builder::getWheelAdoptMonoTime() {
|
||||
return _builder.getDataField< ::uint64_t>(
|
||||
::capnp::bounded<19>() * ::capnp::ELEMENTS);
|
||||
}
|
||||
inline void SlcState::Builder::setWheelAdoptMonoTime( ::uint64_t value) {
|
||||
_builder.setDataField< ::uint64_t>(
|
||||
::capnp::bounded<19>() * ::capnp::ELEMENTS, value);
|
||||
}
|
||||
|
||||
inline float SlcState::Reader::getWheelAdoptTargetMps() const {
|
||||
return _reader.getDataField<float>(
|
||||
::capnp::bounded<40>() * ::capnp::ELEMENTS);
|
||||
}
|
||||
|
||||
inline float SlcState::Builder::getWheelAdoptTargetMps() {
|
||||
return _builder.getDataField<float>(
|
||||
::capnp::bounded<40>() * ::capnp::ELEMENTS);
|
||||
}
|
||||
inline void SlcState::Builder::setWheelAdoptTargetMps(float value) {
|
||||
_builder.setDataField<float>(
|
||||
::capnp::bounded<40>() * ::capnp::ELEMENTS, value);
|
||||
}
|
||||
|
||||
inline bool SlcState::SourceReading::Reader::hasSource() const {
|
||||
return !_reader.getPointerField(
|
||||
::capnp::bounded<0>() * ::capnp::POINTERS).isNull();
|
||||
@@ -21220,6 +21306,20 @@ inline ::capnp::Orphan< ::cereal::SlcAction::ControllerCruiseAction> SlcAction::
|
||||
::capnp::bounded<3>() * ::capnp::POINTERS));
|
||||
}
|
||||
|
||||
inline ::uint64_t SlcAction::Reader::getWheelAdoptMonoTime() const {
|
||||
return _reader.getDataField< ::uint64_t>(
|
||||
::capnp::bounded<4>() * ::capnp::ELEMENTS);
|
||||
}
|
||||
|
||||
inline ::uint64_t SlcAction::Builder::getWheelAdoptMonoTime() {
|
||||
return _builder.getDataField< ::uint64_t>(
|
||||
::capnp::bounded<4>() * ::capnp::ELEMENTS);
|
||||
}
|
||||
inline void SlcAction::Builder::setWheelAdoptMonoTime( ::uint64_t value) {
|
||||
_builder.setDataField< ::uint64_t>(
|
||||
::capnp::bounded<4>() * ::capnp::ELEMENTS, value);
|
||||
}
|
||||
|
||||
inline ::uint16_t SlcAction::ControllerCruiseAction::Reader::getVersion() const {
|
||||
return _reader.getDataField< ::uint16_t>(
|
||||
::capnp::bounded<0>() * ::capnp::ELEMENTS);
|
||||
@@ -22088,6 +22188,20 @@ inline ::capnp::Orphan< ::cereal::SlcCruiseEvent::TeslaStockCruise> SlcCruiseEve
|
||||
::capnp::bounded<5>() * ::capnp::POINTERS));
|
||||
}
|
||||
|
||||
inline ::uint64_t SlcCruiseEvent::Reader::getWheelOverrideSince() const {
|
||||
return _reader.getDataField< ::uint64_t>(
|
||||
::capnp::bounded<7>() * ::capnp::ELEMENTS);
|
||||
}
|
||||
|
||||
inline ::uint64_t SlcCruiseEvent::Builder::getWheelOverrideSince() {
|
||||
return _builder.getDataField< ::uint64_t>(
|
||||
::capnp::bounded<7>() * ::capnp::ELEMENTS);
|
||||
}
|
||||
inline void SlcCruiseEvent::Builder::setWheelOverrideSince( ::uint64_t value) {
|
||||
_builder.setDataField< ::uint64_t>(
|
||||
::capnp::bounded<7>() * ::capnp::ELEMENTS, value);
|
||||
}
|
||||
|
||||
inline ::uint16_t SlcCruiseEvent::TeslaStockCruise::Reader::getVersion() const {
|
||||
return _reader.getDataField< ::uint16_t>(
|
||||
::capnp::bounded<0>() * ::capnp::ELEMENTS);
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -327,12 +327,12 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
|
||||
{"HideDMIcon", {PERSISTENT, BOOL, "0"}},
|
||||
{"HidePersonalityAlerts", {PERSISTENT, BOOL, "0"}},
|
||||
{"ShowSpeedLimits", {PERSISTENT, BOOL, "1"}},
|
||||
{"SLCConfirmation", {PERSISTENT, BOOL, "0"}},
|
||||
{"SLCAdvanceSeconds", {PERSISTENT, INT, "0"}},
|
||||
{"SLCConfirmationHigher", {PERSISTENT, BOOL, "0"}},
|
||||
{"SLCConfirmationLower", {PERSISTENT, BOOL, "0"}},
|
||||
{"SLCFallback", {PERSISTENT, INT}}, // Retired; preserve saved bytes for older versions.
|
||||
{"SLCOverride", {PERSISTENT, INT, "1"}},
|
||||
{"SLCOnEngage", {PERSISTENT, BOOL, "0"}},
|
||||
{"SLCConfirmation", {PERSISTENT, INT, "0"}},
|
||||
{"SLCAdvanceHigherSeconds", {PERSISTENT, INT, "0"}},
|
||||
{"SLCAdvanceLowerSeconds", {PERSISTENT, INT, "0"}},
|
||||
{"SLCOverrideMode", {PERSISTENT, INT, "1"}},
|
||||
{"SLCWheelOverride", {PERSISTENT, BOOL, "0"}},
|
||||
{"SLCQualifiedHistory", {PERSISTENT, JSON}},
|
||||
{"ConditionalModeConfig", {PERSISTENT | DONT_LOG, JSON}},
|
||||
{"ConditionalManualState", {PERSISTENT | DONT_LOG, JSON}},
|
||||
@@ -361,7 +361,8 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
|
||||
{"VASMPreferences", {PERSISTENT | DONT_LOG, JSON}},
|
||||
{"LaneChangePreferences", {PERSISTENT | DONT_LOG, JSON}},
|
||||
{"SLCPriority1", {PERSISTENT, STRING, "Vision"}},
|
||||
{"SLCPriority2", {PERSISTENT, STRING, "Dashboard"}},
|
||||
{"SLCPriority2", {PERSISTENT, STRING, "Map Data"}},
|
||||
{"SLCPriority3", {PERSISTENT, STRING, "Off"}},
|
||||
{"SpeedLimitController", {PERSISTENT, BOOL, "0"}},
|
||||
{"RouteCount", {PERSISTENT, INT, "0"}},
|
||||
{"SnoozeUpdate", {CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION, BOOL}},
|
||||
|
||||
@@ -4,7 +4,6 @@ import os
|
||||
import time
|
||||
import threading
|
||||
import uuid
|
||||
from collections import deque
|
||||
from typing import cast
|
||||
|
||||
import openpilot.cereal.messaging as messaging
|
||||
@@ -32,7 +31,7 @@ from opendbc.car.hyundai.ioniq6_media import Ioniq6MediaButtons
|
||||
from opendbc.car.hyundai.values import CAR as HYUNDAI_CAR
|
||||
from opendbc.car.interfaces import CarInterfaceBase, RadarInterfaceBase
|
||||
from openpilot.selfdrive.pandad import can_capnp_to_list, can_list_to_can_capnp
|
||||
from openpilot.selfdrive.car.cruise import VCruiseHelper, SlcPendingConfirmation
|
||||
from openpilot.selfdrive.car.cruise import SlcWheelContext, VCruiseHelper
|
||||
from openpilot.starpilot.speed_limits import physical_actions as slc_physical
|
||||
from openpilot.starpilot.aol.intent import AolProcessFaultContext, independent_axis_requested, read_settings
|
||||
from openpilot.starpilot.aol.transport_pause import TransportPauseFeedback, cp_identity
|
||||
@@ -93,7 +92,7 @@ from openpilot.starpilot.vehicle_selection import startup_candidate
|
||||
from openpilot.starpilot.vehicle_preferences import VehicleStartupPreferences
|
||||
from openpilot.starpilot.controller_extensions import configure_controller, observe_controller_can
|
||||
from openpilot.starpilot.vehicle_startup import VehicleStartupOwner
|
||||
from openpilot.starpilot.controllers.wheel_actions import WheelPublisher, key_for
|
||||
from openpilot.starpilot.controllers.wheel_actions import LIFETIME_NS as WHEEL_ACTION_MAX_AGE_NS, WheelPublisher, key_for
|
||||
from openpilot.starpilot.controllers.cruise_action import CruiseActionConsumer, eligible as cruise_action_eligible
|
||||
from openpilot.starpilot.schema_cache import get_cache, prewarm_cache_contracts, put_cache
|
||||
|
||||
@@ -139,15 +138,15 @@ def can_comm_callbacks(logcan: messaging.SubSocket, sendcan: messaging.PubSocket
|
||||
|
||||
def curve_press_receipt(CS: car.CarState, helper: VCruiseHelper, sm, *, curve_replay: bool,
|
||||
slc_replay: bool, host_long_active: bool, observed_ns: int):
|
||||
"""Reserve SLC presentation presses even when their command is stale or rejected."""
|
||||
"""A confirmation or native standstill RES must not also override Curve control."""
|
||||
accelerate = frozenset((car.CarState.ButtonEvent.Type.accelCruise,
|
||||
car.CarState.ButtonEvent.Type.resumeCruise))
|
||||
pressed = {b.type.raw for b in CS.buttonEvents if b.pressed and b.type.raw in accelerate}
|
||||
if (not curve_replay or not host_long_active or not CS.canValid or CS.canTimeout or not CS.cruiseState.available or
|
||||
not pressed):
|
||||
CS.standstill or CS.cruiseState.standstill or not pressed):
|
||||
return None
|
||||
if slc_replay and (helper.slc_consumed_button is not None or bool(pressed & helper.slc_suppressed_buttons) or
|
||||
bool(sm['slcState'].hasPending)):
|
||||
if (helper.slc_consumed_button is not None or bool(pressed & helper.slc_suppressed_buttons) or
|
||||
slc_replay and bool(sm['slcState'].hasPending)):
|
||||
return None
|
||||
return ('curveAccelPress', None, float(helper.v_cruise_kph_last / 3.6),
|
||||
float(helper.v_cruise_kph / 3.6), observed_ns)
|
||||
@@ -187,10 +186,6 @@ class Car:
|
||||
ignore_valid=['autoSteeringLearning'], ignore_avg_freq=['autoSteeringLearning'])
|
||||
self.slc_cruise_event_id = 0
|
||||
self.slc_producer_session = uuid.uuid4().hex
|
||||
self.slc_command_sock = messaging.sub_sock('slcCruiseCommand', conflate=False) if self.slc_replay else None
|
||||
self.slc_commands = deque(maxlen=8)
|
||||
self.slc_last_command_id = 0
|
||||
self.slc_command_session = ''
|
||||
self.slc_receipts: list[tuple[str, object, float, float, int]] = []
|
||||
self.manual_receipt = None
|
||||
self.manual_event_sequence = 0
|
||||
@@ -427,14 +422,13 @@ class Car:
|
||||
for ignored in (self.sm.ignore_alive, self.sm.ignore_valid, self.sm.ignore_average_freq):
|
||||
if 'slcState' not in ignored:
|
||||
ignored.append('slcState')
|
||||
if self.slc_command_sock is None:
|
||||
self.slc_command_sock = messaging.sub_sock('slcCruiseCommand', conflate=False)
|
||||
if 'slcDashboardObservation' not in self.pm.sock:
|
||||
self.pm.sock['slcDashboardObservation'] = messaging.pub_sock('slcDashboardObservation')
|
||||
self.slc_requested_configuration = self.read_slc_configuration()
|
||||
self.slc_configuration = self.slc_requested_configuration
|
||||
self.slc_replay = self.slc_configuration[0]
|
||||
self.slc_control_enabled = self.slc_replay and self.slc_configuration[1].enabled
|
||||
self.slc_match_on_engage = self.slc_transport_available and self.read_slc_match_on_engage()
|
||||
self.slc_source_floor_ns = 0
|
||||
|
||||
# Write previous route's CarParams
|
||||
@@ -475,6 +469,9 @@ class Car:
|
||||
feature_enabled(self.params, self.CP, 'slc', os.environ))
|
||||
return bool(admitted), settings
|
||||
|
||||
def read_slc_match_on_engage(self) -> bool:
|
||||
return read_saved(self.params, 'SLCOnEngage', 1) == (b'1', True)
|
||||
|
||||
def refresh_slc_configuration(self, now_ns: int) -> None:
|
||||
configuration = getattr(self, 'slc_requested_configuration', None)
|
||||
if configuration is None or configuration == getattr(self, 'slc_configuration', None):
|
||||
@@ -482,12 +479,8 @@ class Car:
|
||||
self.slc_configuration = configuration
|
||||
self.slc_replay = configuration[0]
|
||||
self.slc_control_enabled = self.slc_replay and configuration[1].enabled
|
||||
# Invalidate old SLC choices while keeping consumed buttons latched until release.
|
||||
self.slc_source_floor_ns = max(getattr(self, 'slc_source_floor_ns', 0), now_ns)
|
||||
self.slc_commands.clear()
|
||||
self.slc_command_session = ''
|
||||
self.slc_last_command_id = 0
|
||||
# Keep the shared event producer and held suppression until physical release.
|
||||
# Only SLC's sourced/issued work predating this admission is retired.
|
||||
|
||||
def observe_distance_personality(self, CS: car.CarState, now_ns: int) -> None:
|
||||
tracker = getattr(self, 'distance_personality_tracker', None)
|
||||
@@ -573,18 +566,22 @@ class Car:
|
||||
0 < control_log_ns <= now_ns and now_ns - control_log_ns <= slc_physical.STATE_MAX_AGE_NS and
|
||||
self.sm['carControl'].enabled)
|
||||
host_control_enabled = bool(self.CP.openpilotLongitudinalControl and not self.CP.pcmCruise and host_enabled)
|
||||
host_long_active = host_control_enabled and self.sm['carControl'].longActive
|
||||
host_long_active = bool(self.CP.openpilotLongitudinalControl and host_enabled and self.sm['carControl'].longActive)
|
||||
slc_long_active = bool(getattr(self, 'slc_control_enabled', self.slc_replay) and self.CP.openpilotLongitudinalControl and
|
||||
host_enabled and (self.sm['carControl'].longActive or CS.gasPressed))
|
||||
pending = None
|
||||
if self.slc_replay:
|
||||
pending = slc_physical.pending_confirmation(
|
||||
self.sm, now_ns, long_active=slc_long_active, after_ns=floor_ns)
|
||||
approved = (slc_physical.state_current(self.sm, observation, self.slc_producer_session, now_ns)
|
||||
if self.v_cruise_helper.toyota is not None and self.slc_control_enabled else None)
|
||||
if approved is not None and int(approved.frameMonoTime) <= floor_ns:
|
||||
approved = None
|
||||
self.update_cruise_speed(CS, pending, approved=approved)
|
||||
wheel_context = None
|
||||
slc_settings = getattr(self, 'slc_configuration', (False, None))[1]
|
||||
cruise_helper = self.v_cruise_helper
|
||||
if (self.slc_replay and getattr(slc_settings, 'wheel_override', False) and host_long_active and can_rcv_valid and
|
||||
not self.CP.pcmCruise and pending is None and (cruise_helper.slc_wheel_press is not None or len(CS.buttonEvents) > 0) and
|
||||
not (CS.brakePressed or CS.gasPressed or CS.standstill or CS.cruiseState.standstill)):
|
||||
wheel_context = slc_physical.wheel_override_context(self.sm, now_ns, after_ns=floor_ns,
|
||||
selected_kph=cruise_helper.v_cruise_kph, is_metric=self.is_metric)
|
||||
self.update_cruise_speed(CS, pending, wheel_context=wheel_context)
|
||||
if self.controller_cruise_sock is not None:
|
||||
for _ in range(8):
|
||||
request = messaging.recv_one_or_none(self.controller_cruise_sock)
|
||||
@@ -593,6 +590,8 @@ class Car:
|
||||
self.controller_cruise_consumer.apply(request, self.CP, CS, self.sm, self.v_cruise_helper,
|
||||
now_ns=now_ns, is_metric=self.is_metric,
|
||||
enabled=bool(host_control_enabled and can_rcv_valid and not self.params.get_bool("SafeMode")))
|
||||
if self.slc_replay:
|
||||
self.apply_slc_adopt(now_ns, host_control_enabled=host_control_enabled, after_ns=floor_ns)
|
||||
consumed = self.v_cruise_helper.slc_consumed_button
|
||||
if self.aol_card_intent is not None:
|
||||
if self.aol_process_fault_context is None:
|
||||
@@ -784,52 +783,6 @@ class Car:
|
||||
slc_replay=self.slc_replay, host_long_active=host_long_active, observed_ns=now_ns)
|
||||
if curve_receipt is not None:
|
||||
self.slc_receipts.append(curve_receipt)
|
||||
if self.slc_command_sock is not None:
|
||||
for _ in range(8):
|
||||
command_message = messaging.recv_one_or_none(self.slc_command_sock)
|
||||
if command_message is None:
|
||||
break
|
||||
if self.slc_replay and command_message.valid:
|
||||
self.slc_commands.append(command_message)
|
||||
for _ in range(len(self.slc_commands)):
|
||||
command_message = self.slc_commands.popleft()
|
||||
command = command_message.slcCruiseCommand
|
||||
if int(command.issuedMonoTime) <= floor_ns:
|
||||
continue
|
||||
if now_ns < int(command.issuedMonoTime):
|
||||
if now_ns <= int(command.expiresMonoTime):
|
||||
self.slc_commands.append(command_message)
|
||||
continue
|
||||
state = slc_physical.state_current(self.sm, observation, self.slc_producer_session, now_ns)
|
||||
if (state is None or int(self.sm.logMonoTime['slcState']) < int(command.issuedMonoTime)) and now_ns < int(command.expiresMonoTime):
|
||||
self.slc_commands.append(command_message)
|
||||
continue
|
||||
if state is not None and str(state.sessionId) != self.slc_command_session:
|
||||
self.slc_command_session = str(state.sessionId)
|
||||
self.slc_last_command_id = 0
|
||||
if not self.slc_command_session:
|
||||
self.slc_command_session = str(command.sessionId)
|
||||
if str(command.sessionId) != self.slc_command_session or int(command.commandId) <= self.slc_last_command_id:
|
||||
continue
|
||||
self.slc_last_command_id = int(command.commandId)
|
||||
previous_mps = float(self.v_cruise_helper.v_cruise_kph / 3.6)
|
||||
origin = SlcPendingConfirmation(str(command.sessionId), int(command.decisionId), int(command.presentationId))
|
||||
owners = {**self.v_cruise_helper.slc_suppression_owner, **self.v_cruise_helper.slc_released_owner}
|
||||
allowed_buttons = {button for button, owner in owners.items() if owner == origin} if int(command.decisionId) > 0 else set()
|
||||
intervening_button = (bool(self.v_cruise_helper.slc_cruise_change) or
|
||||
any(b.type.raw not in allowed_buttons for b in CS.buttonEvents) or
|
||||
any(timer > 0 and button not in allowed_buttons
|
||||
for button, timer in self.v_cruise_helper.button_timers.items()))
|
||||
applicable = slc_physical.command_applicable(
|
||||
command, state, observation, self.slc_producer_session, now_ns, previous_mps,
|
||||
long_active=slc_long_active,
|
||||
button_event=intervening_button)
|
||||
if self.v_cruise_helper.toyota is not None:
|
||||
applicable = applicable and self.v_cruise_helper.toyota.can_apply(float(command.targetMps))
|
||||
if applicable:
|
||||
self.v_cruise_helper.apply_slc_target(float(command.targetMps), self.is_metric)
|
||||
self.slc_receipts.append(('commandApplied' if applicable else 'commandRejected', command,
|
||||
previous_mps, float(self.v_cruise_helper.v_cruise_kph / 3.6), now_ns))
|
||||
blocked_keys = set()
|
||||
if self.manual_receipt is not None:
|
||||
blocked_keys.add(key_for(self.manual_receipt[0]))
|
||||
@@ -845,28 +798,49 @@ class Car:
|
||||
|
||||
return CS, RD
|
||||
|
||||
def update_cruise_speed(self, CS, pending=None, *, approved=None):
|
||||
def apply_slc_adopt(self, now_ns: int, *, host_control_enabled: bool, after_ns: int = 0) -> None:
|
||||
pending = getattr(self, 'slc_wheel_adopt', None)
|
||||
if pending is None:
|
||||
return
|
||||
wheel_ns, selected_kph = pending
|
||||
helper = self.v_cruise_helper
|
||||
if (not after_ns < wheel_ns <= now_ns <= wheel_ns + WHEEL_ACTION_MAX_AGE_NS or
|
||||
not host_control_enabled or not self.slc_control_enabled or
|
||||
not helper.v_cruise_initialized or helper.v_cruise_kph != selected_kph or any(helper.button_timers.values())):
|
||||
self.slc_wheel_adopt = None
|
||||
return
|
||||
target = slc_physical.adopted_target(self.sm, now_ns, wheel_ns=wheel_ns)
|
||||
if target is not None:
|
||||
self.slc_wheel_adopt = None
|
||||
helper.apply_slc_target(target, self.is_metric)
|
||||
|
||||
def update_cruise_speed(self, CS, pending=None, *, wheel_context: SlcWheelContext | None = None):
|
||||
self.v_cruise_helper.update_v_cruise(CS, self.sm['carControl'].enabled, self.is_metric, pending,
|
||||
slc_enabled=getattr(self, 'slc_control_enabled', getattr(self, 'slc_replay', False)),
|
||||
slc_approved=approved)
|
||||
wheel_context=wheel_context)
|
||||
if self.sm['carControl'].enabled and not self.CC_prev.enabled:
|
||||
initial = None
|
||||
target, initial = None, None
|
||||
now_ns = int(self.can_log_mono_time) if REPLAY and hasattr(self, 'can_log_mono_time') else time.monotonic_ns()
|
||||
if (getattr(self, 'slc_control_enabled', False) and self.CP.openpilotLongitudinalControl and
|
||||
if (getattr(self, 'slc_match_on_engage', False) and getattr(self, 'slc_control_enabled', False) and
|
||||
not self.CP.pcmCruise and self.CP.openpilotLongitudinalControl and
|
||||
CS.canValid and not CS.canTimeout and CS.cruiseState.available and
|
||||
self.sm.valid['carControl'] and self.sm.alive['carControl'] and
|
||||
0 < int(self.sm.logMonoTime['carControl']) <= now_ns and
|
||||
now_ns - int(self.sm.logMonoTime['carControl']) <= slc_physical.STATE_MAX_AGE_NS and
|
||||
(self.sm['carControl'].longActive or CS.gasPressed)):
|
||||
evidence = getattr(self.CI.CS, 'dashboard_limit', None)
|
||||
initial = slc_physical.engagement_state(
|
||||
self.sm, evidence.observation if evidence is not None else None, self.slc_producer_session,
|
||||
now_ns, after_ns=getattr(self, 'slc_source_floor_ns', 0))
|
||||
# Initialization owns this edge; evaluate commands against its final speed.
|
||||
now_ns - int(self.sm.logMonoTime['carControl']) <= slc_physical.STATE_MAX_AGE_NS):
|
||||
long_requested = self.sm['carControl'].longActive or CS.gasPressed
|
||||
if not long_requested and 'aolAxisState' in self.sm.logMonoTime:
|
||||
# AOL can acknowledge the request before longitudinal actuators become active.
|
||||
axis = current_axis(self.sm, now_ns=now_ns)
|
||||
long_requested = axis is not None and axis.nativeAcknowledged and axis.desiredLongitudinal
|
||||
if long_requested:
|
||||
evidence = getattr(self.CI.CS, 'dashboard_limit', None)
|
||||
target, initial = slc_physical.engagement_target(
|
||||
self.sm, evidence.observation if evidence is not None else None, self.slc_producer_session,
|
||||
now_ns, after_ns=getattr(self, 'slc_source_floor_ns', 0))
|
||||
adopted = self.v_cruise_helper.initialize_v_cruise(
|
||||
self.CS_prev, self.experimental_mode, resume=self.vehicle_startup.consume_cruise_resume(), initial_speed=self.initial_cruise_speed,
|
||||
slc_target_mps=float(initial.presentationAdjustedLimit) if initial is not None else None, is_metric=self.is_metric)
|
||||
if adopted:
|
||||
slc_target_mps=target, is_metric=self.is_metric)
|
||||
if adopted and initial is not None:
|
||||
self.slc_receipts.append(('engagementAdopt', initial, 0.0, float(self.v_cruise_helper.v_cruise_kph / 3.6), now_ns))
|
||||
|
||||
def aol_cancel_source(self, CS, now_ns: int) -> int:
|
||||
@@ -1135,11 +1109,6 @@ class Car:
|
||||
elif self.CP.brand == 'hyundai':
|
||||
from openpilot.starpilot.car.hyundai.dashboard import compose as hyundai_compose
|
||||
companion = hyundai_compose(self.CP, self.CI.CS, CS, companion, car_state_stamp=int(cs_send.logMonoTime))
|
||||
if self.v_cruise_helper.toyota is not None:
|
||||
if companion is None:
|
||||
companion = messaging.new_message('starpilotCarState', valid=bool(cs_send.valid), logMonoTime=int(cs_send.logMonoTime))
|
||||
companion.starpilotCarState.sourceCarStateMonoTime = int(cs_send.logMonoTime)
|
||||
self.v_cruise_helper.toyota.publish(companion.starpilotCarState.toyotaCruise)
|
||||
self.car_gps_publisher.update(self.CI.CS, self.pm, companion)
|
||||
self.pm.send('carState', cs_send)
|
||||
if getattr(self, 'learning_stamp_observer', None) is not None:
|
||||
@@ -1147,6 +1116,9 @@ class Car:
|
||||
|
||||
wheel = getattr(self, 'wheel_publisher', None)
|
||||
if wheel is not None:
|
||||
if (getattr(self, 'slc_control_enabled', False) and self.CC_prev.enabled and self.sm['carControl'].enabled and
|
||||
any(action == 10 for _, action in self.wheel_commands)):
|
||||
self.slc_wheel_adopt = (int(cs_send.logMonoTime), self.v_cruise_helper.v_cruise_kph)
|
||||
wheel.publish(self.wheel_commands, self.CP, self.pm, now_ns=int(cs_send.logMonoTime),
|
||||
drive_id=int(self.sm['deviceState'].startedMonoTime), source_car_ns=int(cs_send.logMonoTime),
|
||||
source_control_ns=int(self.sm.logMonoTime['carControl']))
|
||||
@@ -1186,6 +1158,8 @@ class Car:
|
||||
if kind == 'driverChange':
|
||||
assert isinstance(detail, tuple) and len(detail) == 4
|
||||
record.button, record.longPress = detail[2], detail[3]
|
||||
if self.v_cruise_helper.slc_wheel_tap is not None:
|
||||
record.sessionId, record.wheelOverrideSince = self.v_cruise_helper.slc_wheel_tap
|
||||
elif kind in ('confirmationAccept', 'confirmationReject'):
|
||||
record.button = detail.button
|
||||
record.sessionId = detail.pending.session_id
|
||||
@@ -1193,13 +1167,11 @@ class Car:
|
||||
record.presentationId = detail.pending.presentation_id
|
||||
elif kind == 'curveAccelPress':
|
||||
record.button = 'accel'
|
||||
else:
|
||||
elif kind == 'engagementAdopt':
|
||||
record.sessionId = str(detail.sessionId)
|
||||
record.commandId = 0 if kind == 'engagementAdopt' else int(detail.commandId)
|
||||
record.decisionId = int(detail.decisionId)
|
||||
record.presentationId = int(detail.presentationId)
|
||||
if kind == 'engagementAdopt':
|
||||
record.button = 'decel'
|
||||
record.button = 'decel'
|
||||
self.pm.send('slcCruiseEvent', event)
|
||||
|
||||
if getattr(self, 'conditional_replay', False) and self.manual_receipt is not None:
|
||||
@@ -1619,6 +1591,10 @@ class Car:
|
||||
if physical.button_credit_ns - self.volt_cc_boot_offset_ns <= max(self.volt_cc_drive_id, self.volt_cc_source_floor_ns):
|
||||
self.CI.CC.volt_cc_consumed_source_ns = physical.button_credit_ns
|
||||
self.CI.CC.volt_cc_metric = self.is_metric
|
||||
self.CI.CC.volt_cc_speed_ceiling_mps = (slc_physical.active_ceiling(
|
||||
self.sm, self.volt_cc_now_mono_ns,
|
||||
after_ns=max(self.volt_cc_drive_id, self.volt_cc_source_floor_ns, getattr(self, 'slc_source_floor_ns', 0)))
|
||||
if getattr(self, 'slc_control_enabled', False) else None)
|
||||
|
||||
if getattr(self.CI.CC, "bolt_cc_profile", False):
|
||||
self.CI.CC.bolt_cc_metric = self.is_metric
|
||||
@@ -1871,6 +1847,8 @@ class Car:
|
||||
now = time.monotonic()
|
||||
if now >= next_cruise_read:
|
||||
self.slc_requested_configuration = self.read_slc_configuration()
|
||||
if getattr(self, 'slc_transport_available', False):
|
||||
self.slc_match_on_engage = self.read_slc_match_on_engage()
|
||||
self.v_cruise_helper.intervals = read_cruise_intervals(self.params, self.CP)
|
||||
pitch_input = getattr(self.CI.CC, 'long_pitch_input', None)
|
||||
if pitch_input is not None:
|
||||
|
||||
@@ -43,6 +43,9 @@ class SlcConsumedButton:
|
||||
button: str
|
||||
|
||||
|
||||
SlcWheelContext = tuple[str, int]
|
||||
|
||||
|
||||
def validated_initial_cruise_speed(value: object) -> int:
|
||||
if type(value) is int and V_CRUISE_INITIAL <= value <= V_CRUISE_MAX:
|
||||
return value
|
||||
@@ -66,35 +69,27 @@ class VCruiseHelper:
|
||||
self.button_change_states = {btn: {"standstill": False, "enabled": False} for btn in self.button_timers}
|
||||
self.pcm_slc_sample: tuple[float, int] | None = None
|
||||
self.pcm_slc_button = ButtonType.unknown
|
||||
# Set by software cruise steps or a checked Toyota PCM physical +/- change.
|
||||
self.slc_cruise_change: tuple[float, float, str, bool] | None = None
|
||||
self.slc_consumed_button: SlcConsumedButton | None = None
|
||||
self.slc_suppressed_buttons: set[ButtonType] = set()
|
||||
self.slc_suppression_owner: dict[ButtonType, SlcPendingConfirmation] = {}
|
||||
self.slc_released_owner: dict[ButtonType, SlcPendingConfirmation] = {}
|
||||
self.slc_released_suppressed = False
|
||||
self.slc_pcm = False
|
||||
from openpilot.starpilot.speed_limits.toyota import eligible, ToyotaCruise
|
||||
self.toyota = ToyotaCruise() if eligible(CP) else None
|
||||
self.slc_wheel_press: SlcWheelContext | None = None
|
||||
self.slc_wheel_tap: SlcWheelContext | None = None
|
||||
|
||||
@property
|
||||
def v_cruise_initialized(self):
|
||||
return self.v_cruise_kph != V_CRUISE_UNSET
|
||||
|
||||
def update_v_cruise(self, CS, enabled, is_metric, slc_pending: SlcPendingConfirmation | None = None, *, slc_enabled: bool = False,
|
||||
slc_approved=None):
|
||||
self.slc_pcm = bool(slc_enabled and self.CP.pcmCruise and self.CP.openpilotLongitudinalControl)
|
||||
def update_v_cruise(self, CS, enabled, is_metric, slc_pending: SlcPendingConfirmation | None = None, *,
|
||||
slc_enabled: bool = False, wheel_context: SlcWheelContext | None = None):
|
||||
from opendbc.car.subaru.software_cruise import selected as subaru_software_selected
|
||||
subaru_software = subaru_software_selected(self.CP)
|
||||
software_cruise = not self.CP.pcmCruise or self.slc_pcm or subaru_software
|
||||
software_cruise = not self.CP.pcmCruise or subaru_software
|
||||
self.slc_cruise_change = None
|
||||
self.slc_consumed_button = None
|
||||
self.slc_released_suppressed = False
|
||||
self.slc_released_owner = {}
|
||||
self.slc_wheel_tap = None
|
||||
self.v_cruise_kph_last = self.v_cruise_kph
|
||||
if self.toyota is not None:
|
||||
self.slc_pcm = self.toyota.update(self, CS, enabled, slc_enabled, slc_approved, is_metric)
|
||||
software_cruise = self.slc_pcm
|
||||
now_ns = monotonic_ns()
|
||||
if not CS.cruiseState.available or now_ns - self.pending_resume_ns > 500_000_000:
|
||||
self.pending_resume_ns = 0
|
||||
@@ -106,21 +101,26 @@ class VCruiseHelper:
|
||||
button.type in (ButtonType.accelCruise, ButtonType.resumeCruise) and self.v_cruise_initialized):
|
||||
self.pending_resume_ns = now_ns
|
||||
|
||||
if software_cruise or self.slc_suppressed_buttons:
|
||||
if software_cruise or slc_enabled and self.CP.openpilotLongitudinalControl or self.slc_suppressed_buttons:
|
||||
self._update_slc_button_latches(CS, enabled, slc_pending)
|
||||
|
||||
if (not slc_enabled or not enabled or self.CP.pcmCruise or slc_pending is not None or
|
||||
not CS.cruiseState.available or ButtonType.accelCruise in self.slc_suppressed_buttons):
|
||||
wheel_context = None
|
||||
if wheel_context != self.slc_wheel_press:
|
||||
self.slc_wheel_press = None
|
||||
if wheel_context is not None and self.button_timers[ButtonType.accelCruise] == 0:
|
||||
pressed = {b.type.raw for b in CS.buttonEvents if b.pressed}
|
||||
if ButtonType.accelCruise in pressed and ButtonType.resumeCruise not in pressed:
|
||||
self.slc_wheel_press = wheel_context
|
||||
|
||||
if CS.cruiseState.available:
|
||||
if software_cruise:
|
||||
if self.toyota is None and (self.slc_pcm or subaru_software) and (not enabled or not self.v_cruise_initialized):
|
||||
# PCM still owns engagement; SLC owns the software set speed while system long is selected.
|
||||
if subaru_software and (not enabled or not self.v_cruise_initialized):
|
||||
stock_kph = CS.cruiseState.speed * CV.MS_TO_KPH
|
||||
self.v_cruise_kph = stock_kph if 0 < stock_kph < V_CRUISE_UNSET else V_CRUISE_UNSET
|
||||
# if stock cruise is completely disabled, then we can use our own set speed logic
|
||||
self._update_v_cruise_non_pcm(CS, enabled, is_metric)
|
||||
if self.toyota is None:
|
||||
self.v_cruise_cluster_kph = self.v_cruise_kph
|
||||
if self.toyota is not None:
|
||||
self.toyota.constrain(self)
|
||||
self.v_cruise_cluster_kph = self.v_cruise_kph
|
||||
self.update_button_timers(CS, enabled)
|
||||
else:
|
||||
self.v_cruise_kph = CS.cruiseState.speed * CV.MS_TO_KPH
|
||||
@@ -139,6 +139,8 @@ class VCruiseHelper:
|
||||
self.pcm_slc_button = ButtonType.unknown
|
||||
if software_cruise:
|
||||
self.update_button_timers(CS, enabled)
|
||||
if self.button_timers[ButtonType.accelCruise] == 0:
|
||||
self.slc_wheel_press = None
|
||||
|
||||
def _update_toyota_pcm_slc_change(self, CS, enabled: bool, now_ns: int) -> None:
|
||||
"""Observe a physical PCM speed change without writing stock selected speed."""
|
||||
@@ -165,8 +167,9 @@ class VCruiseHelper:
|
||||
held = ButtonType.unknown
|
||||
if continuous:
|
||||
delta = self.v_cruise_kph - previous[0]
|
||||
if ((button == ButtonType.accelCruise and delta > 0.) or
|
||||
(button == ButtonType.decelCruise and delta < 0.)):
|
||||
if (not self.slc_suppressed_buttons and not self.slc_released_suppressed and
|
||||
((button == ButtonType.accelCruise and delta > 0.) or
|
||||
(button == ButtonType.decelCruise and delta < 0.))):
|
||||
self.slc_cruise_change = (previous[0] / 3.6, self.v_cruise_kph / 3.6,
|
||||
'accel' if delta > 0. else 'decel', False)
|
||||
self.pcm_slc_button = held
|
||||
@@ -179,17 +182,13 @@ class VCruiseHelper:
|
||||
continue
|
||||
if not b.pressed and button in self.slc_suppressed_buttons:
|
||||
self.slc_suppressed_buttons.remove(button)
|
||||
prior_owner = self.slc_suppression_owner.pop(button, None)
|
||||
if prior_owner is not None:
|
||||
self.slc_released_owner[button] = prior_owner
|
||||
self.slc_released_suppressed = True
|
||||
self.button_timers[button] = 0
|
||||
elif (b.pressed and enabled and CS.cruiseState.available and slc_pending is not None and
|
||||
not (self.toyota is not None and button == ButtonType.accelCruise and
|
||||
not (self.CP.pcmCruise and button == ButtonType.accelCruise and
|
||||
(CS.standstill or CS.cruiseState.standstill or not CS.cruiseState.enabled)) and
|
||||
button not in self.slc_suppressed_buttons):
|
||||
self.slc_suppressed_buttons.add(button)
|
||||
self.slc_suppression_owner[button] = slc_pending
|
||||
self.slc_consumed_button = SlcConsumedButton(slc_pending, 'accel' if button == ButtonType.accelCruise else 'decel')
|
||||
|
||||
def _update_v_cruise_non_pcm(self, CS, enabled, is_metric):
|
||||
@@ -231,26 +230,20 @@ class VCruiseHelper:
|
||||
if not self.button_change_states[button_type]["enabled"]:
|
||||
return
|
||||
|
||||
self.adjust_v_cruise(button_type, CS, is_metric, long_press=long_press)
|
||||
changed = self.adjust_v_cruise(button_type, CS, is_metric, long_press=long_press)
|
||||
if changed and button_type == ButtonType.accelCruise and not long_press and self.button_timers[button_type] > 0:
|
||||
self.slc_wheel_tap = self.slc_wheel_press
|
||||
|
||||
def adjust_v_cruise(self, button_type, CS, is_metric: bool, *, long_press: bool = False) -> bool:
|
||||
"""Apply the normal software cruise step without synthesizing button events."""
|
||||
from opendbc.car.subaru.software_cruise import selected as subaru_software_selected
|
||||
subaru_software = subaru_software_selected(self.CP)
|
||||
if ((self.CP.pcmCruise and not self.slc_pcm and not subaru_software) or button_type not in (ButtonType.accelCruise, ButtonType.decelCruise) or
|
||||
if ((self.CP.pcmCruise and not subaru_software) or button_type not in (ButtonType.accelCruise, ButtonType.decelCruise) or
|
||||
not 0 < self.v_cruise_kph < V_CRUISE_UNSET or
|
||||
(button_type == ButtonType.accelCruise and CS.cruiseState.standstill)):
|
||||
return False
|
||||
v_cruise_delta = 1. if is_metric else IMPERIAL_INCREMENT
|
||||
previous_kph = self.v_cruise_kph
|
||||
toyota = self.toyota if self.slc_pcm else None
|
||||
previous_cluster_kph = self.v_cruise_cluster_kph
|
||||
if toyota is not None:
|
||||
if toyota.requested is None:
|
||||
return False
|
||||
if button_type == ButtonType.decelCruise:
|
||||
previous_kph = toyota.requested[0]
|
||||
self.v_cruise_kph = toyota.requested[1]
|
||||
interval = self.intervals.held if long_press else self.intervals.short
|
||||
v_cruise_delta = v_cruise_delta * interval
|
||||
display_speed = round(self.v_cruise_kph if is_metric else self.v_cruise_kph * CV.KPH_TO_MPH)
|
||||
@@ -266,11 +259,6 @@ class VCruiseHelper:
|
||||
|
||||
self.v_cruise_kph = np.clip(round(self.v_cruise_kph, 1), V_CRUISE_MIN, V_CRUISE_MAX)
|
||||
self.v_cruise_cluster_kph = self.v_cruise_kph
|
||||
if toyota is not None:
|
||||
toyota.requested = (float(self.v_cruise_kph), float(self.v_cruise_cluster_kph))
|
||||
toyota.manual_step = (previous_kph, previous_cluster_kph, 'accel' if button_type == ButtonType.accelCruise else 'decel', long_press)
|
||||
toyota.constrain(self)
|
||||
return self.v_cruise_kph != previous_kph
|
||||
if 0 < previous_kph < V_CRUISE_UNSET and self.v_cruise_kph != previous_kph:
|
||||
button = "accel" if button_type == ButtonType.accelCruise else "decel"
|
||||
self.slc_cruise_change = (float(previous_kph / 3.6), float(self.v_cruise_kph / 3.6), button, long_press)
|
||||
@@ -291,23 +279,19 @@ class VCruiseHelper:
|
||||
self.button_change_states[b.type.raw] = {"standstill": CS.cruiseState.standstill, "enabled": enabled}
|
||||
|
||||
def apply_slc_target(self, target_mps: float, is_metric: bool) -> None:
|
||||
"""Apply a qualified planner command to the card-owned software set speed."""
|
||||
"""Set the software cruise ceiling for engagement or a qualified driver request."""
|
||||
# Match ordinary button precision so stepping away and back restores this target.
|
||||
self.v_cruise_kph = slc_target_kph(target_mps, is_metric)
|
||||
self.v_cruise_cluster_kph = self.v_cruise_kph
|
||||
if self.toyota is not None:
|
||||
self.toyota.manual_step = None
|
||||
self.toyota.constrain(self)
|
||||
self.toyota.requested = (self.v_cruise_kph, self.v_cruise_cluster_kph)
|
||||
|
||||
def initialize_v_cruise(self, CS, experimental_mode: bool, *, resume: bool | None = None,
|
||||
initial_speed: object = V_CRUISE_INITIAL, slc_target_mps: float | None = None, is_metric: bool = False) -> bool:
|
||||
"""Return whether engagement adopts the qualified current SLC limit."""
|
||||
"""Return whether engagement used the supplied SLC target."""
|
||||
if (not self.CP.openpilotLongitudinalControl or slc_target_mps is None or type(slc_target_mps) not in (int, float) or
|
||||
not math.isfinite(slc_target_mps) or not V_CRUISE_MIN / 3.6 - 1e-5 <= slc_target_mps <= V_CRUISE_MAX / 3.6 + 1e-5):
|
||||
slc_target_mps = None
|
||||
# PCM initializes when SLC does not own SET.
|
||||
if self.CP.pcmCruise and (not self.slc_pcm or slc_target_mps is None):
|
||||
# Native cruise owns PCM SET, including when SLC supplies a planner cap.
|
||||
if self.CP.pcmCruise:
|
||||
return False
|
||||
# A car-owned acknowledgment can outlive the physical button event.
|
||||
if resume is None:
|
||||
@@ -315,15 +299,12 @@ class VCruiseHelper:
|
||||
resume = (0 < self.pending_resume_ns and 0 <= age <= 500_000_000) or any(
|
||||
b.type in (ButtonType.accelCruise, ButtonType.resumeCruise) for b in CS.buttonEvents)
|
||||
self.pending_resume_ns = 0
|
||||
if slc_target_mps is None and resume and self.v_cruise_initialized:
|
||||
if not self.CP.pcmCruise:
|
||||
self.v_cruise_kph = self.v_cruise_kph_last
|
||||
self.v_cruise_cluster_kph = self.v_cruise_kph
|
||||
if resume and self.v_cruise_initialized:
|
||||
self.v_cruise_kph = self.v_cruise_kph_last
|
||||
self.v_cruise_cluster_kph = self.v_cruise_kph
|
||||
return False
|
||||
elif slc_target_mps is not None:
|
||||
# Toyota still needs its native capacity/release approval before application.
|
||||
if self.toyota is None:
|
||||
self.apply_slc_target(slc_target_mps, is_metric)
|
||||
self.apply_slc_target(slc_target_mps, is_metric)
|
||||
return True
|
||||
else:
|
||||
self.v_cruise_kph = int(round(np.clip(max(CS.vEgo * CV.MS_TO_KPH, validated_initial_cruise_speed(initial_speed)), V_CRUISE_INITIAL, V_CRUISE_MAX)))
|
||||
|
||||
@@ -52,7 +52,7 @@ class TestCruiseIntervals(unittest.TestCase):
|
||||
Path(self.params.get_param_path("CustomCruiseLong")).write_bytes(b"200")
|
||||
self.assertEqual(read_cruise_intervals(self.params, self.cp), CruiseIntervals())
|
||||
|
||||
def test_slc_uses_saved_intervals_for_every_other_make_and_pcm_mode(self):
|
||||
def test_slc_preserves_software_intervals_and_native_pcm_set(self):
|
||||
brands = [brand.__module__.split('.')[-2] for brand in BRANDS if brand.__module__.split('.')[-2] != 'toyota'] + ['future']
|
||||
self.params.put("CustomCruise", 3.0, block=True)
|
||||
self.params.put("CustomCruiseLong", 7.0, block=True)
|
||||
@@ -67,6 +67,11 @@ class TestCruiseIntervals(unittest.TestCase):
|
||||
helper.v_cruise_kph = helper.v_cruise_cluster_kph = initial
|
||||
helper.intervals = read_cruise_intervals(self.params, cp)
|
||||
helper.update_v_cruise(state(button, True, native=140.), True, metric, slc_enabled=True)
|
||||
if pcm:
|
||||
helper.update_v_cruise(state(button, False, native=140.), True, metric, slc_enabled=True)
|
||||
self.assertAlmostEqual(helper.v_cruise_kph, 140., places=4)
|
||||
self.assertIsNone(helper.slc_cruise_change)
|
||||
continue
|
||||
if held:
|
||||
for _ in range(CRUISE_LONG_PRESS):
|
||||
helper.update_v_cruise(state(native=140.), True, metric, slc_enabled=True)
|
||||
|
||||
@@ -373,12 +373,11 @@ class LongitudinalPlanner:
|
||||
cruise_profile = replace(cruise_profile, cruise_brake_magnitude=global_magnitude)
|
||||
if sm['controlsState'].forceDecel:
|
||||
cruise_profile = None
|
||||
elif (ceiling.effective_mps is not None or curve.effective_mps is not None) and cruise_profile is not None:
|
||||
# An eligible lower ceiling must retain at least native approach
|
||||
# braking even when the saved profile requests a softer cruise response.
|
||||
elif curve.effective_mps is not None and cruise_profile is not None:
|
||||
# Curve approach retains native braking when the saved cruise response is softer.
|
||||
cruise_profile = replace(cruise_profile, cruise_brake_magnitude=max(cruise_profile.cruise_brake_magnitude,
|
||||
abs(A_CRUISE_MIN)))
|
||||
if ceiling.effective_mps is not None or curve.effective_mps is not None:
|
||||
if curve.effective_mps is not None:
|
||||
global_floor = min(global_floor, A_CRUISE_MIN)
|
||||
if selected_acceleration is not None and cruise_profile is not None:
|
||||
# The selected curve owns acceleration; following/jerks retain their owner.
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
#!/usr/bin/env python3
|
||||
import os
|
||||
import json
|
||||
from collections import deque
|
||||
from dataclasses import replace
|
||||
|
||||
from opendbc.car.structs import car
|
||||
from openpilot.common.params import Params
|
||||
@@ -12,8 +10,9 @@ from openpilot.selfdrive.controls.lib.ldw import LaneDepartureWarning
|
||||
from openpilot.selfdrive.controls.lib.longitudinal_planner import LongitudinalPlanner, NATIVE_PLAN_INPUTS
|
||||
import openpilot.cereal.messaging as messaging
|
||||
from openpilot.cereal.services import SERVICE_LIST
|
||||
from openpilot.starpilot.speed_limits import history as slc_history
|
||||
from openpilot.starpilot.speed_limits.runtime import Action as SlcAction, CruiseEvent as SlcCruiseEvent, Runtime as SlcRuntime
|
||||
from openpilot.starpilot.speed_limits.planner_host import (
|
||||
SLC_ACTIONS, SavedSlcLifecycle, action_from_message, cruise_event_from_message, current_cruise_event, receive_slc_events,
|
||||
)
|
||||
from openpilot.starpilot.longitudinal.profile_runtime import ProfileHost
|
||||
from openpilot.starpilot.longitudinal.lane_change_gap import LaneGapPreferences
|
||||
from openpilot.starpilot.longitudinal.experimental_release import ConditionalHandoff
|
||||
@@ -40,62 +39,8 @@ from openpilot.starpilot.feature_capabilities import longitudinal_supported
|
||||
from openpilot.starpilot.conditional_mode.projection import paired_clocks_ns
|
||||
from openpilot.starpilot.conditional_mode.ui_action import observation as ui_manual_observation
|
||||
from openpilot.starpilot.longitudinal.cruise_ceiling import CurveCeiling
|
||||
from openpilot.starpilot.feature_runtime import (enabled as feature_enabled, requested as feature_requested,
|
||||
slc_runtime_settings, slc_transport_capable, vision_control_enabled)
|
||||
from openpilot.starpilot.feature_runtime import enabled as feature_enabled, requested as feature_requested, slc_transport_capable
|
||||
import time
|
||||
from openpilot.starpilot.speed_limits.runtime_settings import read_params as read_slc_settings
|
||||
|
||||
|
||||
class SavedSlcLifecycle:
|
||||
"""Refresh saved admission; offsets and display units preserve driver choices."""
|
||||
|
||||
def __init__(self, params, cp, environment):
|
||||
self.params, self.cp, self.environment = params, cp, dict(environment)
|
||||
self.runtime = None
|
||||
self.configuration = None
|
||||
self.last_refresh_ns = None
|
||||
|
||||
def refresh(self, now_ns: int, *, pending_actions=None, pending_cruise=None):
|
||||
if self.last_refresh_ns is not None and 0 <= now_ns - self.last_refresh_ns < 1_000_000_000:
|
||||
return self.runtime, False
|
||||
self.last_refresh_ns = now_ns
|
||||
# One immutable parsed snapshot owns this refresh. Repeated qualification
|
||||
# used to reopen the same saved keys five times in a due model iteration.
|
||||
saved_slc = read_slc_settings(self.params)
|
||||
admitted = feature_enabled(self.params, self.cp, 'slc', self.environment, saved_slc=saved_slc)
|
||||
settings = slc_runtime_settings(self.params, self.cp, self.environment, saved_slc=saved_slc)
|
||||
vision = admitted and feature_enabled(self.params, self.cp, 'vision', self.environment, saved_slc=saved_slc)
|
||||
configuration = (admitted and not settings.errors, settings, vision,
|
||||
vision_control_enabled(self.params, self.cp, saved_slc=saved_slc),
|
||||
feature_enabled(self.params, self.cp, 'vision', {}, saved_slc=saved_slc),
|
||||
self.params.get_bool('IsMetric'))
|
||||
if configuration == self.configuration:
|
||||
return self.runtime, False
|
||||
previous_runtime = self.runtime
|
||||
if (self.runtime is not None and self.configuration is not None and
|
||||
configuration[:-1] == (self.configuration[0], replace(self.runtime.settings, offsets=settings.offsets,
|
||||
advance_seconds=settings.advance_seconds),
|
||||
*self.configuration[2:-1])):
|
||||
if settings.offsets != self.runtime.settings.offsets or configuration[5] != self.configuration[5]:
|
||||
self.runtime.update_offsets(settings.offsets, is_metric=configuration[5])
|
||||
if settings.advance_seconds != self.runtime.settings.advance_seconds:
|
||||
self.runtime.update_advance(settings.advance_seconds)
|
||||
else:
|
||||
set_speed_after_ns = self.runtime.set_speed_after_ns if self.runtime is not None else 0
|
||||
if self.runtime is not None:
|
||||
self.runtime.reset()
|
||||
self.runtime = (SlcRuntime(settings, vision_enabled=vision,
|
||||
vision_control_qualified=configuration[3],
|
||||
vision_display_qualified=configuration[4], is_metric=configuration[5])
|
||||
if configuration[0] else None)
|
||||
if self.runtime is not None:
|
||||
self.runtime.set_speed_after_ns = set_speed_after_ns
|
||||
self.configuration = configuration
|
||||
if pending_actions is not None:
|
||||
pending_actions.clear()
|
||||
if pending_cruise is not None and (self.runtime is None or self.runtime is not previous_runtime):
|
||||
pending_cruise.clear()
|
||||
return self.runtime, True
|
||||
|
||||
|
||||
def profile_for_frame(host: ProfileHost | None, sm, CP, now_ns: int, *, traffic_mode: bool | None = False):
|
||||
@@ -159,11 +104,13 @@ def traffic_profile_status(mode: bool | None, target, applied, settings) -> tupl
|
||||
|
||||
|
||||
def curve_for_frame(host: CurveHost | None, sm, CP, now_ns: int, *, drive_id: int = 0,
|
||||
event: CurveDriverEvent | None = None, follow_time_s: float | None = None):
|
||||
event: CurveDriverEvent | None = None, follow_time_s: float | None = None,
|
||||
cruise_ceiling_mps: float | None = None):
|
||||
"""A diagnostic runtime result is not planner authority by itself."""
|
||||
if host is None:
|
||||
return None, None
|
||||
result = host.sample(sm, CP, now_ns, drive_id=drive_id, event=event, follow_time_s=follow_time_s)
|
||||
result = host.sample(sm, CP, now_ns, drive_id=drive_id, event=event, follow_time_s=follow_time_s,
|
||||
cruise_ceiling_mps=cruise_ceiling_mps)
|
||||
if result.ceiling_mps is None:
|
||||
return None, result
|
||||
model_ns = sm.logMonoTime.get('modelV2', 0)
|
||||
@@ -177,6 +124,12 @@ def confirm_curve_frame(host: CurveHost | None, planner: LongitudinalPlanner, no
|
||||
host.runtime.confirm_applied(now_ns, applied=bool(planner.last_curve_ceiling_applied))
|
||||
|
||||
|
||||
def wheel_override_blocked(force_stop: ForceStopRuntime | None, curve_host: CurveHost | None, *, curve_backlog: bool = False) -> bool:
|
||||
# Force Stop can consume RES on the tick it commits, before a forcing plan is published.
|
||||
return ((force_stop is not None and (force_stop.policy.can_commit_next_frame or force_stop.plan.speed_ceiling_mps is not None)) or
|
||||
(curve_host is not None and (curve_backlog or curve_host.runtime.was_controlling)))
|
||||
|
||||
|
||||
def conditional_handoff_for_frame(host: ConditionalPlannerHost | None, sm, CP, now_ns: int,
|
||||
drive_id: int) -> ConditionalHandoff | None:
|
||||
if host is None or not CP.openpilotLongitudinalControl or CP.passive or CP.dashcamOnly:
|
||||
@@ -290,7 +243,8 @@ def update_curve_frame(planner: LongitudinalPlanner, sm, CP, now_ns: int, *, hos
|
||||
results = []
|
||||
def provider(follow_time_s: float | None):
|
||||
ceiling, result = curve_for_frame(host, sm, CP, now_ns, drive_id=drive_id,
|
||||
event=event, follow_time_s=follow_time_s)
|
||||
event=event, follow_time_s=follow_time_s,
|
||||
cruise_ceiling_mps=cruise_ceiling.speed_mps if cruise_ceiling is not None else None)
|
||||
results.append(result)
|
||||
return ceiling
|
||||
planner.update(sm, cruise_ceiling=cruise_ceiling, profile_tuning=profile_tuning,
|
||||
@@ -320,18 +274,7 @@ def queue_cruise_event(event, slc_events, curve_events, conditional_events=None,
|
||||
curve_events.append(CurveDriverEvent(str(cruise.producerSessionId), int(cruise.eventId),
|
||||
int(cruise.observedMonoTime), kind, str(cruise.button), int(event.logMonoTime)))
|
||||
else:
|
||||
slc_events.append(SlcCruiseEvent(int(cruise.eventId), int(cruise.observedMonoTime),
|
||||
float(cruise.previousMps), float(cruise.selectedMps), str(cruise.button),
|
||||
str(cruise.producerSessionId), int(event.logMonoTime), kind,
|
||||
str(cruise.sessionId), int(cruise.decisionId),
|
||||
int(cruise.presentationId), int(cruise.commandId)))
|
||||
|
||||
|
||||
def current_cruise_event(events, now_ns: int):
|
||||
max_event_age_ns = int(2e9 / SERVICE_LIST['modelV2'].frequency)
|
||||
while events and now_ns - events[0].observed_ns > max_event_age_ns:
|
||||
events.popleft()
|
||||
return events.popleft() if events and events[0].observed_ns <= now_ns else None
|
||||
slc_events.append(cruise_event_from_message(event))
|
||||
|
||||
|
||||
def queue_ui_action(event, slc_actions, conditional_actions, mode_actions=None) -> None:
|
||||
@@ -343,9 +286,8 @@ def queue_ui_action(event, slc_actions, conditional_actions, mode_actions=None)
|
||||
mode_actions.append(event)
|
||||
elif kind == 'conditionalModeCycle':
|
||||
conditional_actions.append(event)
|
||||
elif kind in ('accept', 'reject', 'adopt'):
|
||||
slc_actions.append((int(event.logMonoTime), SlcAction(str(request.sessionId), int(request.sequenceId),
|
||||
int(request.decisionId), int(request.presentationId), kind)))
|
||||
elif kind in SLC_ACTIONS:
|
||||
slc_actions.append(action_from_message(event))
|
||||
|
||||
|
||||
def current_ui_manual_event(events, now_ns: int):
|
||||
@@ -417,7 +359,7 @@ def starpilot_main():
|
||||
pending_switchback = deque(maxlen=8)
|
||||
mode_settings = ConditionalSettingsOwner(params) if mode_owner is not None else None
|
||||
slc_lifecycle = SavedSlcLifecycle(params, CP, os.environ)
|
||||
slc_runtime, _ = slc_lifecycle.refresh(0)
|
||||
slc_lifecycle.refresh(0)
|
||||
slc_action_sock = (messaging.sub_sock("slcAction", conflate=False)
|
||||
if slc_available or conditional_host is not None or mode_owner is not None else None)
|
||||
slc_cruise_sock = messaging.sub_sock("slcCruiseEvent", conflate=False)
|
||||
@@ -431,18 +373,14 @@ def starpilot_main():
|
||||
publish_services = ['longitudinalPlan', 'starpilotLongitudinalPlan', 'driverAssistance']
|
||||
if slc_available or curve_host is not None or conditional_host is not None or traffic_owner is not None or mode_owner is not None:
|
||||
publish_services.append('slcState')
|
||||
if slc_available:
|
||||
publish_services.append('slcCruiseCommand')
|
||||
pm = messaging.PubMaster(publish_services)
|
||||
native_plan_checks = list(NATIVE_PLAN_INPUTS)
|
||||
subscribed_services = list(NATIVE_PLAN_INPUTS)
|
||||
subscribed_services.append('deviceState')
|
||||
if conditional_host is not None:
|
||||
subscribed_services.append('starpilotRadarState')
|
||||
optional_slc = ['slcDashboardObservation', 'slcVisionObservation', 'mapdOut', 'starpilotCarState'] if slc_available else []
|
||||
if slc_available and feature_enabled(params, CP, 'aol', os.environ):
|
||||
optional_slc.append('aolAxisState')
|
||||
if force_stop_owner is not None and 'starpilotCarState' not in optional_slc:
|
||||
optional_slc = slc_lifecycle.source_services() if slc_available else []
|
||||
if force_stop_owner is not None:
|
||||
optional_slc.append('starpilotCarState')
|
||||
subscribed_services.extend(optional_slc)
|
||||
subscribed_services.append('starpilotNavigation')
|
||||
@@ -459,16 +397,14 @@ def starpilot_main():
|
||||
# Due saved-setting verification can block on Params. Complete it
|
||||
# before acquiring the model and fast-source batch, not after that
|
||||
# batch has already begun aging toward the CEM freshness deadline.
|
||||
slc_runtime, _ = slc_lifecycle.refresh(time.monotonic_ns(), pending_actions=pending_slc_actions,
|
||||
pending_cruise=pending_slc_cruise)
|
||||
slc_lifecycle.refresh(time.monotonic_ns(), pending_actions=pending_slc_actions, pending_cruise=pending_slc_cruise)
|
||||
sm.update()
|
||||
if sm.updated['modelV2']:
|
||||
# Only explicit replay uses the recorded model timeline. In a live host,
|
||||
# modelV2 may lag newer inputs in the same SubMaster update.
|
||||
now_ns = int(sm.logMonoTime['modelV2']) if 'REPLAY' in os.environ else time.monotonic_ns()
|
||||
if 'REPLAY' in os.environ:
|
||||
slc_runtime, _ = slc_lifecycle.refresh(now_ns, pending_actions=pending_slc_actions,
|
||||
pending_cruise=pending_slc_cruise)
|
||||
slc_lifecycle.refresh(now_ns, pending_actions=pending_slc_actions, pending_cruise=pending_slc_cruise)
|
||||
current_start_ns = (int(sm['deviceState'].startedMonoTime)
|
||||
if 'deviceState' in subscribed_services and sm.valid['deviceState'] and
|
||||
sm.alive['deviceState'] and 0 < sm.logMonoTime['deviceState'] <= now_ns else 0)
|
||||
@@ -479,10 +415,7 @@ def starpilot_main():
|
||||
planner_status = None
|
||||
drive_changed = current_start_ns > 0 and drive_start_ns > 0 and current_start_ns != drive_start_ns
|
||||
if slc_cruise_sock is not None:
|
||||
for _ in range(8):
|
||||
event = messaging.recv_one_or_none(slc_cruise_sock)
|
||||
if event is None:
|
||||
break
|
||||
for event in receive_slc_events(slc_cruise_sock):
|
||||
if str(event.slcCruiseEvent.kind) == 'wheelAction' and event.valid:
|
||||
pending_wheel.append(event)
|
||||
continue
|
||||
@@ -505,10 +438,7 @@ def starpilot_main():
|
||||
if traffic_owner is not None:
|
||||
traffic_owner.reset()
|
||||
if slc_action_sock is not None:
|
||||
for _ in range(8):
|
||||
event = messaging.recv_one_or_none(slc_action_sock)
|
||||
if event is None:
|
||||
break
|
||||
for event in receive_slc_events(slc_action_sock):
|
||||
queue_ui_action(event, pending_slc_actions, pending_conditional_ui, pending_modes)
|
||||
waiting_wheel = deque(maxlen=32)
|
||||
while pending_wheel:
|
||||
@@ -520,25 +450,13 @@ def starpilot_main():
|
||||
else:
|
||||
wheel_coast.receive(event, params, CP, sm, now_ns=now_ns)
|
||||
pending_wheel = waiting_wheel
|
||||
if slc_runtime is not None:
|
||||
if drive_changed or slc_runtime.state.reset_required or slc_runtime.ledger.reset_required:
|
||||
slc_runtime.reset()
|
||||
pending_slc_actions.clear()
|
||||
pending_slc_cruise.clear()
|
||||
max_event_age_ns = int(2e9 / SERVICE_LIST['modelV2'].frequency)
|
||||
while pending_slc_actions and now_ns - pending_slc_actions[0][0] > max_event_age_ns:
|
||||
pending_slc_actions.popleft()
|
||||
cruise_event = current_cruise_event(pending_slc_cruise, now_ns)
|
||||
request = (pending_slc_actions.popleft()[1] if cruise_event is None and pending_slc_actions and
|
||||
pending_slc_actions[0][0] <= now_ns else None)
|
||||
output = slc_runtime.step(sm, CP, now_ns=now_ns, request=request, cruise_event=cruise_event)
|
||||
planner_status = output.message
|
||||
if output.command is not None:
|
||||
pm.send('slcCruiseCommand', output.command)
|
||||
history_write = output.result.acceptance.history_write if output.result.acceptance is not None else None
|
||||
# A camera episode has no road-location identity for a later drive.
|
||||
if history_write is not None and history_write.candidate.source != 'vision':
|
||||
params.put("SLCQualifiedHistory", json.loads(slc_history.encode(history_write, time.time_ns())))
|
||||
# Curve observes the press before SLC sees the release. A queued Curve press can still own the tap.
|
||||
curve_event = current_cruise_event(pending_curve_events, now_ns)
|
||||
slc_output = slc_lifecycle.step(sm, now_ns, pending_slc_actions, pending_slc_cruise, drive_changed=drive_changed,
|
||||
wheel_override_blocked=wheel_override_blocked(force_stop_owner, curve_host, curve_backlog=bool(pending_curve_events)))
|
||||
cruise_ceiling = slc_output.result.ceiling if slc_output is not None else None
|
||||
if slc_output is not None:
|
||||
planner_status = slc_output.message
|
||||
needs_clock = conditional_host is not None or traffic_owner is not None or force_stop_owner is not None or mode_owner is not None
|
||||
clock_pair = paired_clocks_ns() if needs_clock and 'REPLAY' not in os.environ else None
|
||||
controller_traffic_toggle = bool(wheel_coast.traffic_toggles % 2)
|
||||
@@ -598,7 +516,6 @@ def starpilot_main():
|
||||
lead_approach_key = lead_approach_for_frame(approach_preferences, sm, CP, now_ns)
|
||||
if curve_preferences is not None and curve_host is not None:
|
||||
curve_preferences.refresh(curve_host, now_ns)
|
||||
curve_event = current_cruise_event(pending_curve_events, now_ns)
|
||||
conditional_handoff = (conditional_handoff_for_frame(conditional_host, sm, CP, clock_pair[0], current_start_ns)
|
||||
if clock_pair is not None else None)
|
||||
wheel_coast.conditional_stop = bool(conditional_handoff is not None and conditional_host is not None and
|
||||
@@ -615,6 +532,7 @@ def starpilot_main():
|
||||
drive_id=current_start_ns, follow_seconds=follow_seconds,
|
||||
traffic_mode=traffic_mode, takeoff_enabled=faster_lead_takeoff)
|
||||
curve_result = update_curve_frame(longitudinal_planner, sm, CP, now_ns, host=curve_host,
|
||||
cruise_ceiling=cruise_ceiling,
|
||||
profile_tuning=profile_tuning,
|
||||
traffic_mode=traffic_mode, lane_change_policy=lane_change_policy,
|
||||
global_braking_response=global_braking_response, selected_profiles=selected_profiles,
|
||||
@@ -641,6 +559,7 @@ def starpilot_main():
|
||||
proposal, snapshot = conditional_host.sample(
|
||||
sm, CP, longitudinal_planner, now_mono_ns=conditional_now_ns, now_boot_ns=conditional_boot_ns,
|
||||
sample_skew_ns=conditional_skew_ns, drive_id=current_start_ns,
|
||||
cruise_ceiling_mps=cruise_ceiling.speed_mps if cruise_ceiling is not None else None,
|
||||
owner_context=ConditionalOwnerContext(
|
||||
traffic_mode=traffic_observation(traffic_verdict, model_ns=int(sm.logMonoTime['modelV2']),
|
||||
owner_present=traffic_owner is not None),
|
||||
@@ -675,6 +594,8 @@ def starpilot_main():
|
||||
mode_owner.sample('switchback', sm, CP, now_ns=now_ns), session=mode_session,
|
||||
sequence=mode_sequence, now_ns=now_ns, source_car_control_ns=int(sm.logMonoTime['carControl']))
|
||||
if planner_status is not None:
|
||||
if planner_status.slcState.wheelOverrideSince and wheel_override_blocked(force_stop_owner, curve_host):
|
||||
planner_status.slcState.wheelOverrideSince = 0
|
||||
pm.send('slcState', planner_status)
|
||||
if current_start_ns > 0:
|
||||
drive_start_ns = current_start_ns
|
||||
|
||||
@@ -9,6 +9,7 @@ import unittest
|
||||
from openpilot.cereal import messaging
|
||||
from openpilot.common.params import Params
|
||||
from openpilot.starpilot import feature_runtime
|
||||
from openpilot.starpilot.speed_limits import planner_host as slc_host
|
||||
from openpilot.starpilot.speed_limits.runtime_settings import read_params as read_slc_settings
|
||||
from openpilot.selfdrive.controls import plannerd
|
||||
from openpilot.starpilot.conditional_mode.tests import test_status_consumer as transport
|
||||
@@ -98,7 +99,7 @@ class TestCemInputAcquisitionOrder(unittest.TestCase):
|
||||
def read(params):
|
||||
reads.append(True)
|
||||
return read_slc_settings(params)
|
||||
with patch.object(plannerd, 'read_slc_settings', side_effect=read, create=True), \
|
||||
with patch.object(slc_host, 'read_slc_settings', side_effect=read), \
|
||||
patch.object(feature_runtime, 'read_slc_settings', side_effect=read):
|
||||
runtime, changed = owner.refresh(MONO)
|
||||
self.assertTrue(changed)
|
||||
|
||||
@@ -23,21 +23,21 @@ from openpilot.starpilot.conditional_mode.projection import ConditionalOwnerCont
|
||||
from openpilot.starpilot.conditional_mode.runtime_settings import DOCUMENT_KEY
|
||||
from openpilot.starpilot.conditional_mode.status import settings_fingerprint
|
||||
from openpilot.starpilot.conditional_mode.tests.test_projection import BOOT, MONO, FakeSubMaster, serialized_scene
|
||||
from openpilot.starpilot.speed_limits.runtime import Action as SlcAction, CruiseEvent as SlcCruiseEvent
|
||||
|
||||
|
||||
class TestConditionalRuntimeLoop(unittest.TestCase):
|
||||
def test_slc_unit_change_keeps_receipts_for_the_current_command(self):
|
||||
from openpilot.selfdrive.car.cruise import slc_target_kph
|
||||
def test_slc_unit_change_preserves_cap_and_queued_driver_event(self):
|
||||
from openpilot.starpilot.speed_limits import offset_document as od
|
||||
from openpilot.starpilot.speed_limits.runtime import Action, CruiseEvent
|
||||
from openpilot.starpilot.speed_limits.tests.test_runtime_replay import RuntimeReplayTests
|
||||
fixture = RuntimeReplayTests()
|
||||
from openpilot.starpilot.speed_limits.tests import test_runtime_replay as replay
|
||||
fixture = replay.RuntimeReplayTests()
|
||||
fixture.setUp()
|
||||
sm, cp = fixture.sm, fixture.cp
|
||||
with tempfile.TemporaryDirectory() as directory:
|
||||
params = Params(directory)
|
||||
params.put_bool('SpeedLimitController', True, block=True)
|
||||
params.put_bool('SLCConfirmation', True, block=True)
|
||||
params.put('SLCConfirmation', 1, block=True)
|
||||
params.put('SLCPriority1', 'Dashboard', block=True)
|
||||
params.put('SLCOffsetSchedule', od.to_value(od.default_document(False)), block=True)
|
||||
owner = plannerd.SavedSlcLifecycle(params, cp, {})
|
||||
@@ -46,20 +46,19 @@ class TestConditionalRuntimeLoop(unittest.TestCase):
|
||||
sm.advance(2_900_000_000)
|
||||
pending = runtime.step(sm, cp, now_ns=2_900_000_000).message.slcState
|
||||
sm.advance(2_950_000_000)
|
||||
issued = runtime.step(sm, cp, now_ns=2_950_000_000,
|
||||
accepted = runtime.step(sm, cp, now_ns=2_950_000_000,
|
||||
request=Action(runtime.session_id, 1, pending.decisionId, pending.presentationId, 'accept'))
|
||||
command = issued.command.slcCruiseCommand
|
||||
selected = slc_target_kph(command.targetMps, False)
|
||||
receipts = deque([CruiseEvent(1, 2_990_000_000, 60 / 3.6, selected / 3.6, '', 'card-producer',
|
||||
2_990_000_000, 'commandApplied', runtime.session_id, command.decisionId, command.presentationId, command.commandId)])
|
||||
self.assertEqual(accepted.result.ceiling.speed_mps, 25.)
|
||||
events = deque([CruiseEvent(1, 2_990_000_000, 60/3.6, 65/3.6, 'accel', 'card-producer', 2_990_000_000)])
|
||||
params.put_bool('IsMetric', True, block=True)
|
||||
metric, _ = owner.refresh(3_000_000_000, pending_cruise=receipts)
|
||||
self.assertEqual(len(receipts), 1)
|
||||
sm['carState'].vCruise = sm['carState'].vCruiseCluster = selected
|
||||
metric, _ = owner.refresh(3_000_000_000, pending_cruise=events)
|
||||
self.assertIs(metric, runtime)
|
||||
self.assertEqual(len(events), 1)
|
||||
sm['carState'].vCruise = sm['carState'].vCruiseCluster = 65.
|
||||
sm.advance(3_000_000_000)
|
||||
applied = metric.step(sm, cp, now_ns=3_000_000_000, cruise_event=receipts.popleft())
|
||||
self.assertEqual(applied.message.slcState.commandStatus, 'applied')
|
||||
self.assertIsNone(applied.command)
|
||||
output = metric.step(sm, cp, now_ns=3_000_000_000, cruise_event=events.popleft())
|
||||
self.assertEqual(output.result.ceiling.speed_mps, 25.)
|
||||
self.assertEqual(sm['carState'].vCruise, 65.)
|
||||
|
||||
def test_slc_unit_changes_preserve_set_speed_and_driver_choices(self):
|
||||
from openpilot.common.constants import CV
|
||||
@@ -68,14 +67,14 @@ class TestConditionalRuntimeLoop(unittest.TestCase):
|
||||
from openpilot.starpilot.speed_limits.runtime import Action, CruiseEvent
|
||||
from openpilot.starpilot.speed_limits.tests.test_runtime_replay import RuntimeReplayTests
|
||||
now = 2_000_000_000
|
||||
for confirmation, choice in ((False, 'applied'), (True, 'applied'), (False, 'override'), (True, 'decline')):
|
||||
for confirmation, choice in ((False, 'applied'), (True, 'applied'), (False, 'ceiling'), (True, 'decline')):
|
||||
with self.subTest(confirmation=confirmation, choice=choice), tempfile.TemporaryDirectory() as directory:
|
||||
fixture = RuntimeReplayTests()
|
||||
fixture.setUp()
|
||||
sm, cp = fixture.sm, fixture.cp
|
||||
params = Params(directory)
|
||||
params.put_bool('SpeedLimitController', True, block=True)
|
||||
params.put_bool('SLCConfirmation', confirmation, block=True)
|
||||
params.put('SLCConfirmation', int(confirmation), block=True)
|
||||
params.put('SLCPriority1', 'Dashboard', block=True)
|
||||
params.put('SLCOffsetSchedule', od.to_value(od.default_document(False)), block=True)
|
||||
owner = plannerd.SavedSlcLifecycle(params, cp, {})
|
||||
@@ -84,14 +83,14 @@ class TestConditionalRuntimeLoop(unittest.TestCase):
|
||||
baseline = slc_target_kph(65 * CV.MPH_TO_MS, False)
|
||||
sm['carState'].vCruise = sm['carState'].vCruiseCluster = baseline
|
||||
aligned = original.step(sm, cp, now_ns=now)
|
||||
self.assertIsNone(aligned.command)
|
||||
if choice == 'override':
|
||||
self.assertTrue(aligned.message.slcState.hasAccepted)
|
||||
if choice == 'ceiling':
|
||||
selected = slc_target_kph(70 * CV.MPH_TO_MS, False)
|
||||
sm['carState'].vCruise = sm['carState'].vCruiseCluster = selected
|
||||
sm.advance(now + 50_000_000)
|
||||
chosen = original.step(sm, cp, now_ns=now + 50_000_000, cruise_event=CruiseEvent(
|
||||
1, now + 50_000_000, baseline / 3.6, selected / 3.6, 'accel', 'card-producer', now + 50_000_000))
|
||||
self.assertTrue(chosen.message.slcState.driverOverrideActive)
|
||||
self.assertFalse(chosen.message.slcState.driverOverrideActive)
|
||||
elif choice == 'decline':
|
||||
sm['slcDashboardObservation'].speedMps = 55 * CV.MPH_TO_MS
|
||||
sm.advance(now + 50_000_000)
|
||||
@@ -105,11 +104,10 @@ class TestConditionalRuntimeLoop(unittest.TestCase):
|
||||
self.assertTrue(changed)
|
||||
sm.advance(now + 1_000_000_000)
|
||||
output = metric.step(sm, cp, now_ns=now + 1_000_000_000)
|
||||
self.assertIsNone(output.command)
|
||||
self.assertFalse(output.message.slcState.hasPending)
|
||||
self.assertTrue(output.message.slcState.hasAccepted)
|
||||
self.assertEqual(sm['carState'].vCruise, selected)
|
||||
self.assertEqual(output.message.slcState.driverOverrideActive, choice == 'override')
|
||||
self.assertFalse(output.message.slcState.driverOverrideActive)
|
||||
if choice == 'decline':
|
||||
self.assertEqual(output.result.acceptance.basis, 'rejected')
|
||||
|
||||
@@ -122,13 +120,11 @@ class TestConditionalRuntimeLoop(unittest.TestCase):
|
||||
owner = plannerd.SavedSlcLifecycle(params, cp, {})
|
||||
imperial, _ = owner.refresh(MONO)
|
||||
self.assertFalse(imperial.is_metric)
|
||||
imperial.set_speed_after_ns = MONO + 1_050_000_000
|
||||
actions, cruise = deque([object()]), deque([object()])
|
||||
params.put_bool('IsMetric', True, block=True)
|
||||
metric, changed = owner.refresh(MONO + 1_000_000_000, pending_actions=actions, pending_cruise=cruise)
|
||||
self.assertTrue(changed)
|
||||
self.assertTrue(metric.is_metric)
|
||||
self.assertEqual(metric.set_speed_after_ns, MONO + 1_050_000_000)
|
||||
self.assertIs(metric, imperial)
|
||||
self.assertFalse(actions)
|
||||
self.assertEqual(len(cruise), 1)
|
||||
@@ -168,7 +164,7 @@ class TestConditionalRuntimeLoop(unittest.TestCase):
|
||||
# Status and transport are reserved independently of saved SLC/CEM
|
||||
# admission, so a later saved change can attach without restarting.
|
||||
self.assertEqual(publisher.call_args.args[0], ['longitudinalPlan', 'starpilotLongitudinalPlan',
|
||||
'driverAssistance', 'slcState', 'slcCruiseCommand'])
|
||||
'driverAssistance', 'slcState'])
|
||||
self.assertEqual(conditional_ctor.call_count, int(enabled))
|
||||
if enabled:
|
||||
self.assertIs(conditional_ctor.call_args.args[0], params)
|
||||
@@ -200,8 +196,8 @@ class TestConditionalRuntimeLoop(unittest.TestCase):
|
||||
self.assertFalse(vision.vision_control_qualified)
|
||||
self.assertEqual(vision.settings.selection.slots[0], Source.VISION)
|
||||
session = vision.session_id
|
||||
pending_actions = deque([(3_000_000_000, plannerd.SlcAction(session, 1, 1, 1, 'accept'))])
|
||||
pending_cruise = deque([plannerd.SlcCruiseEvent(1, 3_000_000_000, 20., 21., 'set', session_id=session)])
|
||||
pending_actions = deque([(3_000_000_000, SlcAction(session, 1, 1, 1, 'accept'))])
|
||||
pending_cruise = deque([SlcCruiseEvent(1, 3_000_000_000, 20., 21., 'set', session_id=session)])
|
||||
self.assertEqual(owner.refresh(3_050_000_000, pending_actions=pending_actions,
|
||||
pending_cruise=pending_cruise), (vision, False))
|
||||
self.assertEqual(len(pending_actions), 1)
|
||||
@@ -217,8 +213,8 @@ class TestConditionalRuntimeLoop(unittest.TestCase):
|
||||
self.assertNotEqual(vision.session_id, session) # Retired session was reset.
|
||||
params.put_bool('ShowSpeedLimits', False, block=True)
|
||||
params.put_bool('SpeedLimitController', False, block=True)
|
||||
pending_actions.append((4_050_000_000, plannerd.SlcAction(control.session_id, 1, 1, 1, 'accept')))
|
||||
pending_cruise.append(plannerd.SlcCruiseEvent(2, 4_050_000_000, 20., 21., 'set', session_id=control.session_id))
|
||||
pending_actions.append((4_050_000_000, SlcAction(control.session_id, 1, 1, 1, 'accept')))
|
||||
pending_cruise.append(SlcCruiseEvent(2, 4_050_000_000, 20., 21., 'set', session_id=control.session_id))
|
||||
self.assertEqual(owner.refresh(5_000_000_000, pending_actions=pending_actions,
|
||||
pending_cruise=pending_cruise), (None, True))
|
||||
self.assertFalse(pending_actions)
|
||||
@@ -251,7 +247,7 @@ class TestConditionalRuntimeLoop(unittest.TestCase):
|
||||
patch.object(messaging, 'SubMaster', return_value=sm) as subscriber, patch.object(messaging, 'sub_sock'):
|
||||
with self.assertRaises(EndLoop):
|
||||
plannerd.starpilot_main()
|
||||
self.assertIn('slcCruiseCommand', publisher.call_args.args[0])
|
||||
self.assertNotIn('slcCruiseCommand', publisher.call_args.args[0])
|
||||
for service in ('slcDashboardObservation', 'slcVisionObservation'):
|
||||
self.assertIn(service, subscriber.call_args.args[0])
|
||||
for check in ('ignore_alive', 'ignore_valid', 'ignore_avg_freq'):
|
||||
|
||||
@@ -156,7 +156,7 @@ class CurveLoopTests(unittest.TestCase):
|
||||
stamp = NOW + n * 50_000_000
|
||||
sm = bus(stamp)
|
||||
result = update_curve_frame(planner, sm, cp, stamp, host=host, cruise_ceiling=slc)
|
||||
self.assertIsNotNone(result.ceiling_mps)
|
||||
self.assertIsNone(result.ceiling_mps)
|
||||
self.assertFalse(planner.last_curve_ceiling_applied)
|
||||
self.assertFalse(host.runtime.was_controlling)
|
||||
self.assertEqual(host.runtime.curve.revision, 0)
|
||||
|
||||
@@ -45,326 +45,326 @@ const static double MAHA_THRESH_31 = 3.8414588206941227;
|
||||
* *
|
||||
* This file is part of 'ekf' *
|
||||
******************************************************************************/
|
||||
void err_fun(double *nom_x, double *delta_x, double *out_7353202191454035649) {
|
||||
out_7353202191454035649[0] = delta_x[0] + nom_x[0];
|
||||
out_7353202191454035649[1] = delta_x[1] + nom_x[1];
|
||||
out_7353202191454035649[2] = delta_x[2] + nom_x[2];
|
||||
out_7353202191454035649[3] = delta_x[3] + nom_x[3];
|
||||
out_7353202191454035649[4] = delta_x[4] + nom_x[4];
|
||||
out_7353202191454035649[5] = delta_x[5] + nom_x[5];
|
||||
out_7353202191454035649[6] = delta_x[6] + nom_x[6];
|
||||
out_7353202191454035649[7] = delta_x[7] + nom_x[7];
|
||||
out_7353202191454035649[8] = delta_x[8] + nom_x[8];
|
||||
void err_fun(double *nom_x, double *delta_x, double *out_7567495437456270106) {
|
||||
out_7567495437456270106[0] = delta_x[0] + nom_x[0];
|
||||
out_7567495437456270106[1] = delta_x[1] + nom_x[1];
|
||||
out_7567495437456270106[2] = delta_x[2] + nom_x[2];
|
||||
out_7567495437456270106[3] = delta_x[3] + nom_x[3];
|
||||
out_7567495437456270106[4] = delta_x[4] + nom_x[4];
|
||||
out_7567495437456270106[5] = delta_x[5] + nom_x[5];
|
||||
out_7567495437456270106[6] = delta_x[6] + nom_x[6];
|
||||
out_7567495437456270106[7] = delta_x[7] + nom_x[7];
|
||||
out_7567495437456270106[8] = delta_x[8] + nom_x[8];
|
||||
}
|
||||
void inv_err_fun(double *nom_x, double *true_x, double *out_5273149315250963980) {
|
||||
out_5273149315250963980[0] = -nom_x[0] + true_x[0];
|
||||
out_5273149315250963980[1] = -nom_x[1] + true_x[1];
|
||||
out_5273149315250963980[2] = -nom_x[2] + true_x[2];
|
||||
out_5273149315250963980[3] = -nom_x[3] + true_x[3];
|
||||
out_5273149315250963980[4] = -nom_x[4] + true_x[4];
|
||||
out_5273149315250963980[5] = -nom_x[5] + true_x[5];
|
||||
out_5273149315250963980[6] = -nom_x[6] + true_x[6];
|
||||
out_5273149315250963980[7] = -nom_x[7] + true_x[7];
|
||||
out_5273149315250963980[8] = -nom_x[8] + true_x[8];
|
||||
void inv_err_fun(double *nom_x, double *true_x, double *out_6277676899296321355) {
|
||||
out_6277676899296321355[0] = -nom_x[0] + true_x[0];
|
||||
out_6277676899296321355[1] = -nom_x[1] + true_x[1];
|
||||
out_6277676899296321355[2] = -nom_x[2] + true_x[2];
|
||||
out_6277676899296321355[3] = -nom_x[3] + true_x[3];
|
||||
out_6277676899296321355[4] = -nom_x[4] + true_x[4];
|
||||
out_6277676899296321355[5] = -nom_x[5] + true_x[5];
|
||||
out_6277676899296321355[6] = -nom_x[6] + true_x[6];
|
||||
out_6277676899296321355[7] = -nom_x[7] + true_x[7];
|
||||
out_6277676899296321355[8] = -nom_x[8] + true_x[8];
|
||||
}
|
||||
void H_mod_fun(double *state, double *out_5463203357507594110) {
|
||||
out_5463203357507594110[0] = 1.0;
|
||||
out_5463203357507594110[1] = 0.0;
|
||||
out_5463203357507594110[2] = 0.0;
|
||||
out_5463203357507594110[3] = 0.0;
|
||||
out_5463203357507594110[4] = 0.0;
|
||||
out_5463203357507594110[5] = 0.0;
|
||||
out_5463203357507594110[6] = 0.0;
|
||||
out_5463203357507594110[7] = 0.0;
|
||||
out_5463203357507594110[8] = 0.0;
|
||||
out_5463203357507594110[9] = 0.0;
|
||||
out_5463203357507594110[10] = 1.0;
|
||||
out_5463203357507594110[11] = 0.0;
|
||||
out_5463203357507594110[12] = 0.0;
|
||||
out_5463203357507594110[13] = 0.0;
|
||||
out_5463203357507594110[14] = 0.0;
|
||||
out_5463203357507594110[15] = 0.0;
|
||||
out_5463203357507594110[16] = 0.0;
|
||||
out_5463203357507594110[17] = 0.0;
|
||||
out_5463203357507594110[18] = 0.0;
|
||||
out_5463203357507594110[19] = 0.0;
|
||||
out_5463203357507594110[20] = 1.0;
|
||||
out_5463203357507594110[21] = 0.0;
|
||||
out_5463203357507594110[22] = 0.0;
|
||||
out_5463203357507594110[23] = 0.0;
|
||||
out_5463203357507594110[24] = 0.0;
|
||||
out_5463203357507594110[25] = 0.0;
|
||||
out_5463203357507594110[26] = 0.0;
|
||||
out_5463203357507594110[27] = 0.0;
|
||||
out_5463203357507594110[28] = 0.0;
|
||||
out_5463203357507594110[29] = 0.0;
|
||||
out_5463203357507594110[30] = 1.0;
|
||||
out_5463203357507594110[31] = 0.0;
|
||||
out_5463203357507594110[32] = 0.0;
|
||||
out_5463203357507594110[33] = 0.0;
|
||||
out_5463203357507594110[34] = 0.0;
|
||||
out_5463203357507594110[35] = 0.0;
|
||||
out_5463203357507594110[36] = 0.0;
|
||||
out_5463203357507594110[37] = 0.0;
|
||||
out_5463203357507594110[38] = 0.0;
|
||||
out_5463203357507594110[39] = 0.0;
|
||||
out_5463203357507594110[40] = 1.0;
|
||||
out_5463203357507594110[41] = 0.0;
|
||||
out_5463203357507594110[42] = 0.0;
|
||||
out_5463203357507594110[43] = 0.0;
|
||||
out_5463203357507594110[44] = 0.0;
|
||||
out_5463203357507594110[45] = 0.0;
|
||||
out_5463203357507594110[46] = 0.0;
|
||||
out_5463203357507594110[47] = 0.0;
|
||||
out_5463203357507594110[48] = 0.0;
|
||||
out_5463203357507594110[49] = 0.0;
|
||||
out_5463203357507594110[50] = 1.0;
|
||||
out_5463203357507594110[51] = 0.0;
|
||||
out_5463203357507594110[52] = 0.0;
|
||||
out_5463203357507594110[53] = 0.0;
|
||||
out_5463203357507594110[54] = 0.0;
|
||||
out_5463203357507594110[55] = 0.0;
|
||||
out_5463203357507594110[56] = 0.0;
|
||||
out_5463203357507594110[57] = 0.0;
|
||||
out_5463203357507594110[58] = 0.0;
|
||||
out_5463203357507594110[59] = 0.0;
|
||||
out_5463203357507594110[60] = 1.0;
|
||||
out_5463203357507594110[61] = 0.0;
|
||||
out_5463203357507594110[62] = 0.0;
|
||||
out_5463203357507594110[63] = 0.0;
|
||||
out_5463203357507594110[64] = 0.0;
|
||||
out_5463203357507594110[65] = 0.0;
|
||||
out_5463203357507594110[66] = 0.0;
|
||||
out_5463203357507594110[67] = 0.0;
|
||||
out_5463203357507594110[68] = 0.0;
|
||||
out_5463203357507594110[69] = 0.0;
|
||||
out_5463203357507594110[70] = 1.0;
|
||||
out_5463203357507594110[71] = 0.0;
|
||||
out_5463203357507594110[72] = 0.0;
|
||||
out_5463203357507594110[73] = 0.0;
|
||||
out_5463203357507594110[74] = 0.0;
|
||||
out_5463203357507594110[75] = 0.0;
|
||||
out_5463203357507594110[76] = 0.0;
|
||||
out_5463203357507594110[77] = 0.0;
|
||||
out_5463203357507594110[78] = 0.0;
|
||||
out_5463203357507594110[79] = 0.0;
|
||||
out_5463203357507594110[80] = 1.0;
|
||||
void H_mod_fun(double *state, double *out_7420380263460044385) {
|
||||
out_7420380263460044385[0] = 1.0;
|
||||
out_7420380263460044385[1] = 0.0;
|
||||
out_7420380263460044385[2] = 0.0;
|
||||
out_7420380263460044385[3] = 0.0;
|
||||
out_7420380263460044385[4] = 0.0;
|
||||
out_7420380263460044385[5] = 0.0;
|
||||
out_7420380263460044385[6] = 0.0;
|
||||
out_7420380263460044385[7] = 0.0;
|
||||
out_7420380263460044385[8] = 0.0;
|
||||
out_7420380263460044385[9] = 0.0;
|
||||
out_7420380263460044385[10] = 1.0;
|
||||
out_7420380263460044385[11] = 0.0;
|
||||
out_7420380263460044385[12] = 0.0;
|
||||
out_7420380263460044385[13] = 0.0;
|
||||
out_7420380263460044385[14] = 0.0;
|
||||
out_7420380263460044385[15] = 0.0;
|
||||
out_7420380263460044385[16] = 0.0;
|
||||
out_7420380263460044385[17] = 0.0;
|
||||
out_7420380263460044385[18] = 0.0;
|
||||
out_7420380263460044385[19] = 0.0;
|
||||
out_7420380263460044385[20] = 1.0;
|
||||
out_7420380263460044385[21] = 0.0;
|
||||
out_7420380263460044385[22] = 0.0;
|
||||
out_7420380263460044385[23] = 0.0;
|
||||
out_7420380263460044385[24] = 0.0;
|
||||
out_7420380263460044385[25] = 0.0;
|
||||
out_7420380263460044385[26] = 0.0;
|
||||
out_7420380263460044385[27] = 0.0;
|
||||
out_7420380263460044385[28] = 0.0;
|
||||
out_7420380263460044385[29] = 0.0;
|
||||
out_7420380263460044385[30] = 1.0;
|
||||
out_7420380263460044385[31] = 0.0;
|
||||
out_7420380263460044385[32] = 0.0;
|
||||
out_7420380263460044385[33] = 0.0;
|
||||
out_7420380263460044385[34] = 0.0;
|
||||
out_7420380263460044385[35] = 0.0;
|
||||
out_7420380263460044385[36] = 0.0;
|
||||
out_7420380263460044385[37] = 0.0;
|
||||
out_7420380263460044385[38] = 0.0;
|
||||
out_7420380263460044385[39] = 0.0;
|
||||
out_7420380263460044385[40] = 1.0;
|
||||
out_7420380263460044385[41] = 0.0;
|
||||
out_7420380263460044385[42] = 0.0;
|
||||
out_7420380263460044385[43] = 0.0;
|
||||
out_7420380263460044385[44] = 0.0;
|
||||
out_7420380263460044385[45] = 0.0;
|
||||
out_7420380263460044385[46] = 0.0;
|
||||
out_7420380263460044385[47] = 0.0;
|
||||
out_7420380263460044385[48] = 0.0;
|
||||
out_7420380263460044385[49] = 0.0;
|
||||
out_7420380263460044385[50] = 1.0;
|
||||
out_7420380263460044385[51] = 0.0;
|
||||
out_7420380263460044385[52] = 0.0;
|
||||
out_7420380263460044385[53] = 0.0;
|
||||
out_7420380263460044385[54] = 0.0;
|
||||
out_7420380263460044385[55] = 0.0;
|
||||
out_7420380263460044385[56] = 0.0;
|
||||
out_7420380263460044385[57] = 0.0;
|
||||
out_7420380263460044385[58] = 0.0;
|
||||
out_7420380263460044385[59] = 0.0;
|
||||
out_7420380263460044385[60] = 1.0;
|
||||
out_7420380263460044385[61] = 0.0;
|
||||
out_7420380263460044385[62] = 0.0;
|
||||
out_7420380263460044385[63] = 0.0;
|
||||
out_7420380263460044385[64] = 0.0;
|
||||
out_7420380263460044385[65] = 0.0;
|
||||
out_7420380263460044385[66] = 0.0;
|
||||
out_7420380263460044385[67] = 0.0;
|
||||
out_7420380263460044385[68] = 0.0;
|
||||
out_7420380263460044385[69] = 0.0;
|
||||
out_7420380263460044385[70] = 1.0;
|
||||
out_7420380263460044385[71] = 0.0;
|
||||
out_7420380263460044385[72] = 0.0;
|
||||
out_7420380263460044385[73] = 0.0;
|
||||
out_7420380263460044385[74] = 0.0;
|
||||
out_7420380263460044385[75] = 0.0;
|
||||
out_7420380263460044385[76] = 0.0;
|
||||
out_7420380263460044385[77] = 0.0;
|
||||
out_7420380263460044385[78] = 0.0;
|
||||
out_7420380263460044385[79] = 0.0;
|
||||
out_7420380263460044385[80] = 1.0;
|
||||
}
|
||||
void f_fun(double *state, double dt, double *out_6125045598488451197) {
|
||||
out_6125045598488451197[0] = state[0];
|
||||
out_6125045598488451197[1] = state[1];
|
||||
out_6125045598488451197[2] = state[2];
|
||||
out_6125045598488451197[3] = state[3];
|
||||
out_6125045598488451197[4] = state[4];
|
||||
out_6125045598488451197[5] = dt*((-state[4] + (-center_to_front*stiffness_front*state[0] + center_to_rear*stiffness_rear*state[0])/(mass*state[4]))*state[6] - 9.8100000000000005*state[8] + stiffness_front*(-state[2] - state[3] + state[7])*state[0]/(mass*state[1]) + (-stiffness_front*state[0] - stiffness_rear*state[0])*state[5]/(mass*state[4])) + state[5];
|
||||
out_6125045598488451197[6] = dt*(center_to_front*stiffness_front*(-state[2] - state[3] + state[7])*state[0]/(rotational_inertia*state[1]) + (-center_to_front*stiffness_front*state[0] + center_to_rear*stiffness_rear*state[0])*state[5]/(rotational_inertia*state[4]) + (-pow(center_to_front, 2)*stiffness_front*state[0] - pow(center_to_rear, 2)*stiffness_rear*state[0])*state[6]/(rotational_inertia*state[4])) + state[6];
|
||||
out_6125045598488451197[7] = state[7];
|
||||
out_6125045598488451197[8] = state[8];
|
||||
void f_fun(double *state, double dt, double *out_3045596088569131513) {
|
||||
out_3045596088569131513[0] = state[0];
|
||||
out_3045596088569131513[1] = state[1];
|
||||
out_3045596088569131513[2] = state[2];
|
||||
out_3045596088569131513[3] = state[3];
|
||||
out_3045596088569131513[4] = state[4];
|
||||
out_3045596088569131513[5] = dt*((-state[4] + (-center_to_front*stiffness_front*state[0] + center_to_rear*stiffness_rear*state[0])/(mass*state[4]))*state[6] - 9.8100000000000005*state[8] + stiffness_front*(-state[2] - state[3] + state[7])*state[0]/(mass*state[1]) + (-stiffness_front*state[0] - stiffness_rear*state[0])*state[5]/(mass*state[4])) + state[5];
|
||||
out_3045596088569131513[6] = dt*(center_to_front*stiffness_front*(-state[2] - state[3] + state[7])*state[0]/(rotational_inertia*state[1]) + (-center_to_front*stiffness_front*state[0] + center_to_rear*stiffness_rear*state[0])*state[5]/(rotational_inertia*state[4]) + (-pow(center_to_front, 2)*stiffness_front*state[0] - pow(center_to_rear, 2)*stiffness_rear*state[0])*state[6]/(rotational_inertia*state[4])) + state[6];
|
||||
out_3045596088569131513[7] = state[7];
|
||||
out_3045596088569131513[8] = state[8];
|
||||
}
|
||||
void F_fun(double *state, double dt, double *out_548031277288491442) {
|
||||
out_548031277288491442[0] = 1;
|
||||
out_548031277288491442[1] = 0;
|
||||
out_548031277288491442[2] = 0;
|
||||
out_548031277288491442[3] = 0;
|
||||
out_548031277288491442[4] = 0;
|
||||
out_548031277288491442[5] = 0;
|
||||
out_548031277288491442[6] = 0;
|
||||
out_548031277288491442[7] = 0;
|
||||
out_548031277288491442[8] = 0;
|
||||
out_548031277288491442[9] = 0;
|
||||
out_548031277288491442[10] = 1;
|
||||
out_548031277288491442[11] = 0;
|
||||
out_548031277288491442[12] = 0;
|
||||
out_548031277288491442[13] = 0;
|
||||
out_548031277288491442[14] = 0;
|
||||
out_548031277288491442[15] = 0;
|
||||
out_548031277288491442[16] = 0;
|
||||
out_548031277288491442[17] = 0;
|
||||
out_548031277288491442[18] = 0;
|
||||
out_548031277288491442[19] = 0;
|
||||
out_548031277288491442[20] = 1;
|
||||
out_548031277288491442[21] = 0;
|
||||
out_548031277288491442[22] = 0;
|
||||
out_548031277288491442[23] = 0;
|
||||
out_548031277288491442[24] = 0;
|
||||
out_548031277288491442[25] = 0;
|
||||
out_548031277288491442[26] = 0;
|
||||
out_548031277288491442[27] = 0;
|
||||
out_548031277288491442[28] = 0;
|
||||
out_548031277288491442[29] = 0;
|
||||
out_548031277288491442[30] = 1;
|
||||
out_548031277288491442[31] = 0;
|
||||
out_548031277288491442[32] = 0;
|
||||
out_548031277288491442[33] = 0;
|
||||
out_548031277288491442[34] = 0;
|
||||
out_548031277288491442[35] = 0;
|
||||
out_548031277288491442[36] = 0;
|
||||
out_548031277288491442[37] = 0;
|
||||
out_548031277288491442[38] = 0;
|
||||
out_548031277288491442[39] = 0;
|
||||
out_548031277288491442[40] = 1;
|
||||
out_548031277288491442[41] = 0;
|
||||
out_548031277288491442[42] = 0;
|
||||
out_548031277288491442[43] = 0;
|
||||
out_548031277288491442[44] = 0;
|
||||
out_548031277288491442[45] = dt*(stiffness_front*(-state[2] - state[3] + state[7])/(mass*state[1]) + (-stiffness_front - stiffness_rear)*state[5]/(mass*state[4]) + (-center_to_front*stiffness_front + center_to_rear*stiffness_rear)*state[6]/(mass*state[4]));
|
||||
out_548031277288491442[46] = -dt*stiffness_front*(-state[2] - state[3] + state[7])*state[0]/(mass*pow(state[1], 2));
|
||||
out_548031277288491442[47] = -dt*stiffness_front*state[0]/(mass*state[1]);
|
||||
out_548031277288491442[48] = -dt*stiffness_front*state[0]/(mass*state[1]);
|
||||
out_548031277288491442[49] = dt*((-1 - (-center_to_front*stiffness_front*state[0] + center_to_rear*stiffness_rear*state[0])/(mass*pow(state[4], 2)))*state[6] - (-stiffness_front*state[0] - stiffness_rear*state[0])*state[5]/(mass*pow(state[4], 2)));
|
||||
out_548031277288491442[50] = dt*(-stiffness_front*state[0] - stiffness_rear*state[0])/(mass*state[4]) + 1;
|
||||
out_548031277288491442[51] = dt*(-state[4] + (-center_to_front*stiffness_front*state[0] + center_to_rear*stiffness_rear*state[0])/(mass*state[4]));
|
||||
out_548031277288491442[52] = dt*stiffness_front*state[0]/(mass*state[1]);
|
||||
out_548031277288491442[53] = -9.8100000000000005*dt;
|
||||
out_548031277288491442[54] = dt*(center_to_front*stiffness_front*(-state[2] - state[3] + state[7])/(rotational_inertia*state[1]) + (-center_to_front*stiffness_front + center_to_rear*stiffness_rear)*state[5]/(rotational_inertia*state[4]) + (-pow(center_to_front, 2)*stiffness_front - pow(center_to_rear, 2)*stiffness_rear)*state[6]/(rotational_inertia*state[4]));
|
||||
out_548031277288491442[55] = -center_to_front*dt*stiffness_front*(-state[2] - state[3] + state[7])*state[0]/(rotational_inertia*pow(state[1], 2));
|
||||
out_548031277288491442[56] = -center_to_front*dt*stiffness_front*state[0]/(rotational_inertia*state[1]);
|
||||
out_548031277288491442[57] = -center_to_front*dt*stiffness_front*state[0]/(rotational_inertia*state[1]);
|
||||
out_548031277288491442[58] = dt*(-(-center_to_front*stiffness_front*state[0] + center_to_rear*stiffness_rear*state[0])*state[5]/(rotational_inertia*pow(state[4], 2)) - (-pow(center_to_front, 2)*stiffness_front*state[0] - pow(center_to_rear, 2)*stiffness_rear*state[0])*state[6]/(rotational_inertia*pow(state[4], 2)));
|
||||
out_548031277288491442[59] = dt*(-center_to_front*stiffness_front*state[0] + center_to_rear*stiffness_rear*state[0])/(rotational_inertia*state[4]);
|
||||
out_548031277288491442[60] = dt*(-pow(center_to_front, 2)*stiffness_front*state[0] - pow(center_to_rear, 2)*stiffness_rear*state[0])/(rotational_inertia*state[4]) + 1;
|
||||
out_548031277288491442[61] = center_to_front*dt*stiffness_front*state[0]/(rotational_inertia*state[1]);
|
||||
out_548031277288491442[62] = 0;
|
||||
out_548031277288491442[63] = 0;
|
||||
out_548031277288491442[64] = 0;
|
||||
out_548031277288491442[65] = 0;
|
||||
out_548031277288491442[66] = 0;
|
||||
out_548031277288491442[67] = 0;
|
||||
out_548031277288491442[68] = 0;
|
||||
out_548031277288491442[69] = 0;
|
||||
out_548031277288491442[70] = 1;
|
||||
out_548031277288491442[71] = 0;
|
||||
out_548031277288491442[72] = 0;
|
||||
out_548031277288491442[73] = 0;
|
||||
out_548031277288491442[74] = 0;
|
||||
out_548031277288491442[75] = 0;
|
||||
out_548031277288491442[76] = 0;
|
||||
out_548031277288491442[77] = 0;
|
||||
out_548031277288491442[78] = 0;
|
||||
out_548031277288491442[79] = 0;
|
||||
out_548031277288491442[80] = 1;
|
||||
void F_fun(double *state, double dt, double *out_8700545601531255316) {
|
||||
out_8700545601531255316[0] = 1;
|
||||
out_8700545601531255316[1] = 0;
|
||||
out_8700545601531255316[2] = 0;
|
||||
out_8700545601531255316[3] = 0;
|
||||
out_8700545601531255316[4] = 0;
|
||||
out_8700545601531255316[5] = 0;
|
||||
out_8700545601531255316[6] = 0;
|
||||
out_8700545601531255316[7] = 0;
|
||||
out_8700545601531255316[8] = 0;
|
||||
out_8700545601531255316[9] = 0;
|
||||
out_8700545601531255316[10] = 1;
|
||||
out_8700545601531255316[11] = 0;
|
||||
out_8700545601531255316[12] = 0;
|
||||
out_8700545601531255316[13] = 0;
|
||||
out_8700545601531255316[14] = 0;
|
||||
out_8700545601531255316[15] = 0;
|
||||
out_8700545601531255316[16] = 0;
|
||||
out_8700545601531255316[17] = 0;
|
||||
out_8700545601531255316[18] = 0;
|
||||
out_8700545601531255316[19] = 0;
|
||||
out_8700545601531255316[20] = 1;
|
||||
out_8700545601531255316[21] = 0;
|
||||
out_8700545601531255316[22] = 0;
|
||||
out_8700545601531255316[23] = 0;
|
||||
out_8700545601531255316[24] = 0;
|
||||
out_8700545601531255316[25] = 0;
|
||||
out_8700545601531255316[26] = 0;
|
||||
out_8700545601531255316[27] = 0;
|
||||
out_8700545601531255316[28] = 0;
|
||||
out_8700545601531255316[29] = 0;
|
||||
out_8700545601531255316[30] = 1;
|
||||
out_8700545601531255316[31] = 0;
|
||||
out_8700545601531255316[32] = 0;
|
||||
out_8700545601531255316[33] = 0;
|
||||
out_8700545601531255316[34] = 0;
|
||||
out_8700545601531255316[35] = 0;
|
||||
out_8700545601531255316[36] = 0;
|
||||
out_8700545601531255316[37] = 0;
|
||||
out_8700545601531255316[38] = 0;
|
||||
out_8700545601531255316[39] = 0;
|
||||
out_8700545601531255316[40] = 1;
|
||||
out_8700545601531255316[41] = 0;
|
||||
out_8700545601531255316[42] = 0;
|
||||
out_8700545601531255316[43] = 0;
|
||||
out_8700545601531255316[44] = 0;
|
||||
out_8700545601531255316[45] = dt*(stiffness_front*(-state[2] - state[3] + state[7])/(mass*state[1]) + (-stiffness_front - stiffness_rear)*state[5]/(mass*state[4]) + (-center_to_front*stiffness_front + center_to_rear*stiffness_rear)*state[6]/(mass*state[4]));
|
||||
out_8700545601531255316[46] = -dt*stiffness_front*(-state[2] - state[3] + state[7])*state[0]/(mass*pow(state[1], 2));
|
||||
out_8700545601531255316[47] = -dt*stiffness_front*state[0]/(mass*state[1]);
|
||||
out_8700545601531255316[48] = -dt*stiffness_front*state[0]/(mass*state[1]);
|
||||
out_8700545601531255316[49] = dt*((-1 - (-center_to_front*stiffness_front*state[0] + center_to_rear*stiffness_rear*state[0])/(mass*pow(state[4], 2)))*state[6] - (-stiffness_front*state[0] - stiffness_rear*state[0])*state[5]/(mass*pow(state[4], 2)));
|
||||
out_8700545601531255316[50] = dt*(-stiffness_front*state[0] - stiffness_rear*state[0])/(mass*state[4]) + 1;
|
||||
out_8700545601531255316[51] = dt*(-state[4] + (-center_to_front*stiffness_front*state[0] + center_to_rear*stiffness_rear*state[0])/(mass*state[4]));
|
||||
out_8700545601531255316[52] = dt*stiffness_front*state[0]/(mass*state[1]);
|
||||
out_8700545601531255316[53] = -9.8100000000000005*dt;
|
||||
out_8700545601531255316[54] = dt*(center_to_front*stiffness_front*(-state[2] - state[3] + state[7])/(rotational_inertia*state[1]) + (-center_to_front*stiffness_front + center_to_rear*stiffness_rear)*state[5]/(rotational_inertia*state[4]) + (-pow(center_to_front, 2)*stiffness_front - pow(center_to_rear, 2)*stiffness_rear)*state[6]/(rotational_inertia*state[4]));
|
||||
out_8700545601531255316[55] = -center_to_front*dt*stiffness_front*(-state[2] - state[3] + state[7])*state[0]/(rotational_inertia*pow(state[1], 2));
|
||||
out_8700545601531255316[56] = -center_to_front*dt*stiffness_front*state[0]/(rotational_inertia*state[1]);
|
||||
out_8700545601531255316[57] = -center_to_front*dt*stiffness_front*state[0]/(rotational_inertia*state[1]);
|
||||
out_8700545601531255316[58] = dt*(-(-center_to_front*stiffness_front*state[0] + center_to_rear*stiffness_rear*state[0])*state[5]/(rotational_inertia*pow(state[4], 2)) - (-pow(center_to_front, 2)*stiffness_front*state[0] - pow(center_to_rear, 2)*stiffness_rear*state[0])*state[6]/(rotational_inertia*pow(state[4], 2)));
|
||||
out_8700545601531255316[59] = dt*(-center_to_front*stiffness_front*state[0] + center_to_rear*stiffness_rear*state[0])/(rotational_inertia*state[4]);
|
||||
out_8700545601531255316[60] = dt*(-pow(center_to_front, 2)*stiffness_front*state[0] - pow(center_to_rear, 2)*stiffness_rear*state[0])/(rotational_inertia*state[4]) + 1;
|
||||
out_8700545601531255316[61] = center_to_front*dt*stiffness_front*state[0]/(rotational_inertia*state[1]);
|
||||
out_8700545601531255316[62] = 0;
|
||||
out_8700545601531255316[63] = 0;
|
||||
out_8700545601531255316[64] = 0;
|
||||
out_8700545601531255316[65] = 0;
|
||||
out_8700545601531255316[66] = 0;
|
||||
out_8700545601531255316[67] = 0;
|
||||
out_8700545601531255316[68] = 0;
|
||||
out_8700545601531255316[69] = 0;
|
||||
out_8700545601531255316[70] = 1;
|
||||
out_8700545601531255316[71] = 0;
|
||||
out_8700545601531255316[72] = 0;
|
||||
out_8700545601531255316[73] = 0;
|
||||
out_8700545601531255316[74] = 0;
|
||||
out_8700545601531255316[75] = 0;
|
||||
out_8700545601531255316[76] = 0;
|
||||
out_8700545601531255316[77] = 0;
|
||||
out_8700545601531255316[78] = 0;
|
||||
out_8700545601531255316[79] = 0;
|
||||
out_8700545601531255316[80] = 1;
|
||||
}
|
||||
void h_25(double *state, double *unused, double *out_7825793012049214991) {
|
||||
out_7825793012049214991[0] = state[6];
|
||||
void h_25(double *state, double *unused, double *out_2824946100795669147) {
|
||||
out_2824946100795669147[0] = state[6];
|
||||
}
|
||||
void H_25(double *state, double *unused, double *out_8986251996152959258) {
|
||||
out_8986251996152959258[0] = 0;
|
||||
out_8986251996152959258[1] = 0;
|
||||
out_8986251996152959258[2] = 0;
|
||||
out_8986251996152959258[3] = 0;
|
||||
out_8986251996152959258[4] = 0;
|
||||
out_8986251996152959258[5] = 0;
|
||||
out_8986251996152959258[6] = 1;
|
||||
out_8986251996152959258[7] = 0;
|
||||
out_8986251996152959258[8] = 0;
|
||||
void H_25(double *state, double *unused, double *out_2516688438275379545) {
|
||||
out_2516688438275379545[0] = 0;
|
||||
out_2516688438275379545[1] = 0;
|
||||
out_2516688438275379545[2] = 0;
|
||||
out_2516688438275379545[3] = 0;
|
||||
out_2516688438275379545[4] = 0;
|
||||
out_2516688438275379545[5] = 0;
|
||||
out_2516688438275379545[6] = 1;
|
||||
out_2516688438275379545[7] = 0;
|
||||
out_2516688438275379545[8] = 0;
|
||||
}
|
||||
void h_24(double *state, double *unused, double *out_3863850671382452828) {
|
||||
out_3863850671382452828[0] = state[4];
|
||||
out_3863850671382452828[1] = state[5];
|
||||
void h_24(double *state, double *unused, double *out_3049085865514909722) {
|
||||
out_3049085865514909722[0] = state[4];
|
||||
out_3049085865514909722[1] = state[5];
|
||||
}
|
||||
void H_24(double *state, double *unused, double *out_5428326397350636122) {
|
||||
out_5428326397350636122[0] = 0;
|
||||
out_5428326397350636122[1] = 0;
|
||||
out_5428326397350636122[2] = 0;
|
||||
out_5428326397350636122[3] = 0;
|
||||
out_5428326397350636122[4] = 1;
|
||||
out_5428326397350636122[5] = 0;
|
||||
out_5428326397350636122[6] = 0;
|
||||
out_5428326397350636122[7] = 0;
|
||||
out_5428326397350636122[8] = 0;
|
||||
out_5428326397350636122[9] = 0;
|
||||
out_5428326397350636122[10] = 0;
|
||||
out_5428326397350636122[11] = 0;
|
||||
out_5428326397350636122[12] = 0;
|
||||
out_5428326397350636122[13] = 0;
|
||||
out_5428326397350636122[14] = 1;
|
||||
out_5428326397350636122[15] = 0;
|
||||
out_5428326397350636122[16] = 0;
|
||||
out_5428326397350636122[17] = 0;
|
||||
void H_24(double *state, double *unused, double *out_339474014668229572) {
|
||||
out_339474014668229572[0] = 0;
|
||||
out_339474014668229572[1] = 0;
|
||||
out_339474014668229572[2] = 0;
|
||||
out_339474014668229572[3] = 0;
|
||||
out_339474014668229572[4] = 1;
|
||||
out_339474014668229572[5] = 0;
|
||||
out_339474014668229572[6] = 0;
|
||||
out_339474014668229572[7] = 0;
|
||||
out_339474014668229572[8] = 0;
|
||||
out_339474014668229572[9] = 0;
|
||||
out_339474014668229572[10] = 0;
|
||||
out_339474014668229572[11] = 0;
|
||||
out_339474014668229572[12] = 0;
|
||||
out_339474014668229572[13] = 0;
|
||||
out_339474014668229572[14] = 1;
|
||||
out_339474014668229572[15] = 0;
|
||||
out_339474014668229572[16] = 0;
|
||||
out_339474014668229572[17] = 0;
|
||||
}
|
||||
void h_30(double *state, double *unused, double *out_3653635896979304131) {
|
||||
out_3653635896979304131[0] = state[4];
|
||||
void h_30(double *state, double *unused, double *out_6312771183888182035) {
|
||||
out_6312771183888182035[0] = state[4];
|
||||
}
|
||||
void H_30(double *state, double *unused, double *out_6467919037645710631) {
|
||||
out_6467919037645710631[0] = 0;
|
||||
out_6467919037645710631[1] = 0;
|
||||
out_6467919037645710631[2] = 0;
|
||||
out_6467919037645710631[3] = 0;
|
||||
out_6467919037645710631[4] = 1;
|
||||
out_6467919037645710631[5] = 0;
|
||||
out_6467919037645710631[6] = 0;
|
||||
out_6467919037645710631[7] = 0;
|
||||
out_6467919037645710631[8] = 0;
|
||||
void H_30(double *state, double *unused, double *out_1644520231869082) {
|
||||
out_1644520231869082[0] = 0;
|
||||
out_1644520231869082[1] = 0;
|
||||
out_1644520231869082[2] = 0;
|
||||
out_1644520231869082[3] = 0;
|
||||
out_1644520231869082[4] = 1;
|
||||
out_1644520231869082[5] = 0;
|
||||
out_1644520231869082[6] = 0;
|
||||
out_1644520231869082[7] = 0;
|
||||
out_1644520231869082[8] = 0;
|
||||
}
|
||||
void h_26(double *state, double *unused, double *out_8655309164640149776) {
|
||||
out_8655309164640149776[0] = state[7];
|
||||
void h_26(double *state, double *unused, double *out_6908065673986044634) {
|
||||
out_6908065673986044634[0] = state[7];
|
||||
}
|
||||
void H_26(double *state, double *unused, double *out_5718988758682536134) {
|
||||
out_5718988758682536134[0] = 0;
|
||||
out_5718988758682536134[1] = 0;
|
||||
out_5718988758682536134[2] = 0;
|
||||
out_5718988758682536134[3] = 0;
|
||||
out_5718988758682536134[4] = 0;
|
||||
out_5718988758682536134[5] = 0;
|
||||
out_5718988758682536134[6] = 0;
|
||||
out_5718988758682536134[7] = 1;
|
||||
out_5718988758682536134[8] = 0;
|
||||
void H_26(double *state, double *unused, double *out_6258191757149435769) {
|
||||
out_6258191757149435769[0] = 0;
|
||||
out_6258191757149435769[1] = 0;
|
||||
out_6258191757149435769[2] = 0;
|
||||
out_6258191757149435769[3] = 0;
|
||||
out_6258191757149435769[4] = 0;
|
||||
out_6258191757149435769[5] = 0;
|
||||
out_6258191757149435769[6] = 0;
|
||||
out_6258191757149435769[7] = 1;
|
||||
out_6258191757149435769[8] = 0;
|
||||
}
|
||||
void h_27(double *state, double *unused, double *out_5968325673074535865) {
|
||||
out_5968325673074535865[0] = state[3];
|
||||
void h_27(double *state, double *unused, double *out_709679758613972836) {
|
||||
out_709679758613972836[0] = state[3];
|
||||
}
|
||||
void H_27(double *state, double *unused, double *out_4244324966461767414) {
|
||||
out_4244324966461767414[0] = 0;
|
||||
out_4244324966461767414[1] = 0;
|
||||
out_4244324966461767414[2] = 0;
|
||||
out_4244324966461767414[3] = 1;
|
||||
out_4244324966461767414[4] = 0;
|
||||
out_4244324966461767414[5] = 0;
|
||||
out_4244324966461767414[6] = 0;
|
||||
out_4244324966461767414[7] = 0;
|
||||
out_4244324966461767414[8] = 0;
|
||||
void H_27(double *state, double *unused, double *out_2173118791568555829) {
|
||||
out_2173118791568555829[0] = 0;
|
||||
out_2173118791568555829[1] = 0;
|
||||
out_2173118791568555829[2] = 0;
|
||||
out_2173118791568555829[3] = 1;
|
||||
out_2173118791568555829[4] = 0;
|
||||
out_2173118791568555829[5] = 0;
|
||||
out_2173118791568555829[6] = 0;
|
||||
out_2173118791568555829[7] = 0;
|
||||
out_2173118791568555829[8] = 0;
|
||||
}
|
||||
void h_29(double *state, double *unused, double *out_2671551803644006019) {
|
||||
out_2671551803644006019[0] = state[1];
|
||||
void h_29(double *state, double *unused, double *out_6982675176999014474) {
|
||||
out_6982675176999014474[0] = state[1];
|
||||
}
|
||||
void H_29(double *state, double *unused, double *out_5957687693331318447) {
|
||||
out_5957687693331318447[0] = 0;
|
||||
out_5957687693331318447[1] = 1;
|
||||
out_5957687693331318447[2] = 0;
|
||||
out_5957687693331318447[3] = 0;
|
||||
out_5957687693331318447[4] = 0;
|
||||
out_5957687693331318447[5] = 0;
|
||||
out_5957687693331318447[6] = 0;
|
||||
out_5957687693331318447[7] = 0;
|
||||
out_5957687693331318447[8] = 0;
|
||||
void H_29(double *state, double *unused, double *out_511875864546261266) {
|
||||
out_511875864546261266[0] = 0;
|
||||
out_511875864546261266[1] = 1;
|
||||
out_511875864546261266[2] = 0;
|
||||
out_511875864546261266[3] = 0;
|
||||
out_511875864546261266[4] = 0;
|
||||
out_511875864546261266[5] = 0;
|
||||
out_511875864546261266[6] = 0;
|
||||
out_511875864546261266[7] = 0;
|
||||
out_511875864546261266[8] = 0;
|
||||
}
|
||||
void h_28(double *state, double *unused, double *out_3845497107703219871) {
|
||||
out_3845497107703219871[0] = state[0];
|
||||
void h_28(double *state, double *unused, double *out_2400127593349653796) {
|
||||
out_2400127593349653796[0] = state[0];
|
||||
}
|
||||
void H_28(double *state, double *unused, double *out_7406657363308702595) {
|
||||
out_7406657363308702595[0] = 1;
|
||||
out_7406657363308702595[1] = 0;
|
||||
out_7406657363308702595[2] = 0;
|
||||
out_7406657363308702595[3] = 0;
|
||||
out_7406657363308702595[4] = 0;
|
||||
out_7406657363308702595[5] = 0;
|
||||
out_7406657363308702595[6] = 0;
|
||||
out_7406657363308702595[7] = 0;
|
||||
out_7406657363308702595[8] = 0;
|
||||
void H_28(double *state, double *unused, double *out_4570523152523269308) {
|
||||
out_4570523152523269308[0] = 1;
|
||||
out_4570523152523269308[1] = 0;
|
||||
out_4570523152523269308[2] = 0;
|
||||
out_4570523152523269308[3] = 0;
|
||||
out_4570523152523269308[4] = 0;
|
||||
out_4570523152523269308[5] = 0;
|
||||
out_4570523152523269308[6] = 0;
|
||||
out_4570523152523269308[7] = 0;
|
||||
out_4570523152523269308[8] = 0;
|
||||
}
|
||||
void h_31(double *state, double *unused, double *out_1981073584941794741) {
|
||||
out_1981073584941794741[0] = state[8];
|
||||
void h_31(double *state, double *unused, double *out_1304974248711835435) {
|
||||
out_1304974248711835435[0] = state[8];
|
||||
}
|
||||
void H_31(double *state, double *unused, double *out_8955606034275998830) {
|
||||
out_8955606034275998830[0] = 0;
|
||||
out_8955606034275998830[1] = 0;
|
||||
out_8955606034275998830[2] = 0;
|
||||
out_8955606034275998830[3] = 0;
|
||||
out_8955606034275998830[4] = 0;
|
||||
out_8955606034275998830[5] = 0;
|
||||
out_8955606034275998830[6] = 0;
|
||||
out_8955606034275998830[7] = 0;
|
||||
out_8955606034275998830[8] = 1;
|
||||
void H_31(double *state, double *unused, double *out_6884399859382787245) {
|
||||
out_6884399859382787245[0] = 0;
|
||||
out_6884399859382787245[1] = 0;
|
||||
out_6884399859382787245[2] = 0;
|
||||
out_6884399859382787245[3] = 0;
|
||||
out_6884399859382787245[4] = 0;
|
||||
out_6884399859382787245[5] = 0;
|
||||
out_6884399859382787245[6] = 0;
|
||||
out_6884399859382787245[7] = 0;
|
||||
out_6884399859382787245[8] = 1;
|
||||
}
|
||||
#include <eigen3/Eigen/Dense>
|
||||
#include <iostream>
|
||||
@@ -518,68 +518,68 @@ void car_update_28(double *in_x, double *in_P, double *in_z, double *in_R, doubl
|
||||
void car_update_31(double *in_x, double *in_P, double *in_z, double *in_R, double *in_ea) {
|
||||
update<1, 3, 0>(in_x, in_P, h_31, H_31, NULL, in_z, in_R, in_ea, MAHA_THRESH_31);
|
||||
}
|
||||
void car_err_fun(double *nom_x, double *delta_x, double *out_7353202191454035649) {
|
||||
err_fun(nom_x, delta_x, out_7353202191454035649);
|
||||
void car_err_fun(double *nom_x, double *delta_x, double *out_7567495437456270106) {
|
||||
err_fun(nom_x, delta_x, out_7567495437456270106);
|
||||
}
|
||||
void car_inv_err_fun(double *nom_x, double *true_x, double *out_5273149315250963980) {
|
||||
inv_err_fun(nom_x, true_x, out_5273149315250963980);
|
||||
void car_inv_err_fun(double *nom_x, double *true_x, double *out_6277676899296321355) {
|
||||
inv_err_fun(nom_x, true_x, out_6277676899296321355);
|
||||
}
|
||||
void car_H_mod_fun(double *state, double *out_5463203357507594110) {
|
||||
H_mod_fun(state, out_5463203357507594110);
|
||||
void car_H_mod_fun(double *state, double *out_7420380263460044385) {
|
||||
H_mod_fun(state, out_7420380263460044385);
|
||||
}
|
||||
void car_f_fun(double *state, double dt, double *out_6125045598488451197) {
|
||||
f_fun(state, dt, out_6125045598488451197);
|
||||
void car_f_fun(double *state, double dt, double *out_3045596088569131513) {
|
||||
f_fun(state, dt, out_3045596088569131513);
|
||||
}
|
||||
void car_F_fun(double *state, double dt, double *out_548031277288491442) {
|
||||
F_fun(state, dt, out_548031277288491442);
|
||||
void car_F_fun(double *state, double dt, double *out_8700545601531255316) {
|
||||
F_fun(state, dt, out_8700545601531255316);
|
||||
}
|
||||
void car_h_25(double *state, double *unused, double *out_7825793012049214991) {
|
||||
h_25(state, unused, out_7825793012049214991);
|
||||
void car_h_25(double *state, double *unused, double *out_2824946100795669147) {
|
||||
h_25(state, unused, out_2824946100795669147);
|
||||
}
|
||||
void car_H_25(double *state, double *unused, double *out_8986251996152959258) {
|
||||
H_25(state, unused, out_8986251996152959258);
|
||||
void car_H_25(double *state, double *unused, double *out_2516688438275379545) {
|
||||
H_25(state, unused, out_2516688438275379545);
|
||||
}
|
||||
void car_h_24(double *state, double *unused, double *out_3863850671382452828) {
|
||||
h_24(state, unused, out_3863850671382452828);
|
||||
void car_h_24(double *state, double *unused, double *out_3049085865514909722) {
|
||||
h_24(state, unused, out_3049085865514909722);
|
||||
}
|
||||
void car_H_24(double *state, double *unused, double *out_5428326397350636122) {
|
||||
H_24(state, unused, out_5428326397350636122);
|
||||
void car_H_24(double *state, double *unused, double *out_339474014668229572) {
|
||||
H_24(state, unused, out_339474014668229572);
|
||||
}
|
||||
void car_h_30(double *state, double *unused, double *out_3653635896979304131) {
|
||||
h_30(state, unused, out_3653635896979304131);
|
||||
void car_h_30(double *state, double *unused, double *out_6312771183888182035) {
|
||||
h_30(state, unused, out_6312771183888182035);
|
||||
}
|
||||
void car_H_30(double *state, double *unused, double *out_6467919037645710631) {
|
||||
H_30(state, unused, out_6467919037645710631);
|
||||
void car_H_30(double *state, double *unused, double *out_1644520231869082) {
|
||||
H_30(state, unused, out_1644520231869082);
|
||||
}
|
||||
void car_h_26(double *state, double *unused, double *out_8655309164640149776) {
|
||||
h_26(state, unused, out_8655309164640149776);
|
||||
void car_h_26(double *state, double *unused, double *out_6908065673986044634) {
|
||||
h_26(state, unused, out_6908065673986044634);
|
||||
}
|
||||
void car_H_26(double *state, double *unused, double *out_5718988758682536134) {
|
||||
H_26(state, unused, out_5718988758682536134);
|
||||
void car_H_26(double *state, double *unused, double *out_6258191757149435769) {
|
||||
H_26(state, unused, out_6258191757149435769);
|
||||
}
|
||||
void car_h_27(double *state, double *unused, double *out_5968325673074535865) {
|
||||
h_27(state, unused, out_5968325673074535865);
|
||||
void car_h_27(double *state, double *unused, double *out_709679758613972836) {
|
||||
h_27(state, unused, out_709679758613972836);
|
||||
}
|
||||
void car_H_27(double *state, double *unused, double *out_4244324966461767414) {
|
||||
H_27(state, unused, out_4244324966461767414);
|
||||
void car_H_27(double *state, double *unused, double *out_2173118791568555829) {
|
||||
H_27(state, unused, out_2173118791568555829);
|
||||
}
|
||||
void car_h_29(double *state, double *unused, double *out_2671551803644006019) {
|
||||
h_29(state, unused, out_2671551803644006019);
|
||||
void car_h_29(double *state, double *unused, double *out_6982675176999014474) {
|
||||
h_29(state, unused, out_6982675176999014474);
|
||||
}
|
||||
void car_H_29(double *state, double *unused, double *out_5957687693331318447) {
|
||||
H_29(state, unused, out_5957687693331318447);
|
||||
void car_H_29(double *state, double *unused, double *out_511875864546261266) {
|
||||
H_29(state, unused, out_511875864546261266);
|
||||
}
|
||||
void car_h_28(double *state, double *unused, double *out_3845497107703219871) {
|
||||
h_28(state, unused, out_3845497107703219871);
|
||||
void car_h_28(double *state, double *unused, double *out_2400127593349653796) {
|
||||
h_28(state, unused, out_2400127593349653796);
|
||||
}
|
||||
void car_H_28(double *state, double *unused, double *out_7406657363308702595) {
|
||||
H_28(state, unused, out_7406657363308702595);
|
||||
void car_H_28(double *state, double *unused, double *out_4570523152523269308) {
|
||||
H_28(state, unused, out_4570523152523269308);
|
||||
}
|
||||
void car_h_31(double *state, double *unused, double *out_1981073584941794741) {
|
||||
h_31(state, unused, out_1981073584941794741);
|
||||
void car_h_31(double *state, double *unused, double *out_1304974248711835435) {
|
||||
h_31(state, unused, out_1304974248711835435);
|
||||
}
|
||||
void car_H_31(double *state, double *unused, double *out_8955606034275998830) {
|
||||
H_31(state, unused, out_8955606034275998830);
|
||||
void car_H_31(double *state, double *unused, double *out_6884399859382787245) {
|
||||
H_31(state, unused, out_6884399859382787245);
|
||||
}
|
||||
void car_predict(double *in_x, double *in_P, double *in_Q, double dt) {
|
||||
predict(in_x, in_P, in_Q, dt);
|
||||
|
||||
@@ -9,27 +9,27 @@ void car_update_27(double *in_x, double *in_P, double *in_z, double *in_R, doubl
|
||||
void car_update_29(double *in_x, double *in_P, double *in_z, double *in_R, double *in_ea);
|
||||
void car_update_28(double *in_x, double *in_P, double *in_z, double *in_R, double *in_ea);
|
||||
void car_update_31(double *in_x, double *in_P, double *in_z, double *in_R, double *in_ea);
|
||||
void car_err_fun(double *nom_x, double *delta_x, double *out_7353202191454035649);
|
||||
void car_inv_err_fun(double *nom_x, double *true_x, double *out_5273149315250963980);
|
||||
void car_H_mod_fun(double *state, double *out_5463203357507594110);
|
||||
void car_f_fun(double *state, double dt, double *out_6125045598488451197);
|
||||
void car_F_fun(double *state, double dt, double *out_548031277288491442);
|
||||
void car_h_25(double *state, double *unused, double *out_7825793012049214991);
|
||||
void car_H_25(double *state, double *unused, double *out_8986251996152959258);
|
||||
void car_h_24(double *state, double *unused, double *out_3863850671382452828);
|
||||
void car_H_24(double *state, double *unused, double *out_5428326397350636122);
|
||||
void car_h_30(double *state, double *unused, double *out_3653635896979304131);
|
||||
void car_H_30(double *state, double *unused, double *out_6467919037645710631);
|
||||
void car_h_26(double *state, double *unused, double *out_8655309164640149776);
|
||||
void car_H_26(double *state, double *unused, double *out_5718988758682536134);
|
||||
void car_h_27(double *state, double *unused, double *out_5968325673074535865);
|
||||
void car_H_27(double *state, double *unused, double *out_4244324966461767414);
|
||||
void car_h_29(double *state, double *unused, double *out_2671551803644006019);
|
||||
void car_H_29(double *state, double *unused, double *out_5957687693331318447);
|
||||
void car_h_28(double *state, double *unused, double *out_3845497107703219871);
|
||||
void car_H_28(double *state, double *unused, double *out_7406657363308702595);
|
||||
void car_h_31(double *state, double *unused, double *out_1981073584941794741);
|
||||
void car_H_31(double *state, double *unused, double *out_8955606034275998830);
|
||||
void car_err_fun(double *nom_x, double *delta_x, double *out_7567495437456270106);
|
||||
void car_inv_err_fun(double *nom_x, double *true_x, double *out_6277676899296321355);
|
||||
void car_H_mod_fun(double *state, double *out_7420380263460044385);
|
||||
void car_f_fun(double *state, double dt, double *out_3045596088569131513);
|
||||
void car_F_fun(double *state, double dt, double *out_8700545601531255316);
|
||||
void car_h_25(double *state, double *unused, double *out_2824946100795669147);
|
||||
void car_H_25(double *state, double *unused, double *out_2516688438275379545);
|
||||
void car_h_24(double *state, double *unused, double *out_3049085865514909722);
|
||||
void car_H_24(double *state, double *unused, double *out_339474014668229572);
|
||||
void car_h_30(double *state, double *unused, double *out_6312771183888182035);
|
||||
void car_H_30(double *state, double *unused, double *out_1644520231869082);
|
||||
void car_h_26(double *state, double *unused, double *out_6908065673986044634);
|
||||
void car_H_26(double *state, double *unused, double *out_6258191757149435769);
|
||||
void car_h_27(double *state, double *unused, double *out_709679758613972836);
|
||||
void car_H_27(double *state, double *unused, double *out_2173118791568555829);
|
||||
void car_h_29(double *state, double *unused, double *out_6982675176999014474);
|
||||
void car_H_29(double *state, double *unused, double *out_511875864546261266);
|
||||
void car_h_28(double *state, double *unused, double *out_2400127593349653796);
|
||||
void car_H_28(double *state, double *unused, double *out_4570523152523269308);
|
||||
void car_h_31(double *state, double *unused, double *out_1304974248711835435);
|
||||
void car_H_31(double *state, double *unused, double *out_6884399859382787245);
|
||||
void car_predict(double *in_x, double *in_P, double *in_Q, double dt);
|
||||
void car_set_mass(double x);
|
||||
void car_set_rotational_inertia(double x);
|
||||
|
||||
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large
Load Diff
@@ -5,18 +5,18 @@ void pose_update_4(double *in_x, double *in_P, double *in_z, double *in_R, doubl
|
||||
void pose_update_10(double *in_x, double *in_P, double *in_z, double *in_R, double *in_ea);
|
||||
void pose_update_13(double *in_x, double *in_P, double *in_z, double *in_R, double *in_ea);
|
||||
void pose_update_14(double *in_x, double *in_P, double *in_z, double *in_R, double *in_ea);
|
||||
void pose_err_fun(double *nom_x, double *delta_x, double *out_2134324718959906218);
|
||||
void pose_inv_err_fun(double *nom_x, double *true_x, double *out_7874553077816062449);
|
||||
void pose_H_mod_fun(double *state, double *out_8129875391940694982);
|
||||
void pose_f_fun(double *state, double dt, double *out_5920058179701921818);
|
||||
void pose_F_fun(double *state, double dt, double *out_534012189001621552);
|
||||
void pose_h_4(double *state, double *unused, double *out_6191552396506608484);
|
||||
void pose_H_4(double *state, double *unused, double *out_4468786875016897913);
|
||||
void pose_h_10(double *state, double *unused, double *out_8347226084972414573);
|
||||
void pose_H_10(double *state, double *unused, double *out_4111169058602364742);
|
||||
void pose_h_13(double *state, double *unused, double *out_2707578914264125574);
|
||||
void pose_H_13(double *state, double *unused, double *out_7681060700349230714);
|
||||
void pose_h_14(double *state, double *unused, double *out_4697260034120461682);
|
||||
void pose_H_14(double *state, double *unused, double *out_8432027731356382442);
|
||||
void pose_err_fun(double *nom_x, double *delta_x, double *out_4830772321857990181);
|
||||
void pose_inv_err_fun(double *nom_x, double *true_x, double *out_5200017052910787602);
|
||||
void pose_H_mod_fun(double *state, double *out_1712341249416054868);
|
||||
void pose_f_fun(double *state, double dt, double *out_3100560613383761185);
|
||||
void pose_F_fun(double *state, double dt, double *out_3350602915939425859);
|
||||
void pose_h_4(double *state, double *unused, double *out_2074712223981570498);
|
||||
void pose_H_4(double *state, double *unused, double *out_4135740557335903853);
|
||||
void pose_h_10(double *state, double *unused, double *out_5949398724887603410);
|
||||
void pose_H_10(double *state, double *unused, double *out_3347508424845693314);
|
||||
void pose_h_13(double *state, double *unused, double *out_9220727019356339608);
|
||||
void pose_H_13(double *state, double *unused, double *out_923466732003571052);
|
||||
void pose_h_14(double *state, double *unused, double *out_8529780658774629922);
|
||||
void pose_H_14(double *state, double *unused, double *out_172499700996419324);
|
||||
void pose_predict(double *in_x, double *in_P, double *in_Q, double dt);
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1352,27 +1352,6 @@ class SelfdriveD:
|
||||
self.aol_fault_reason = 'availabilityPause'
|
||||
self.aol_publication_retained_intent = intent
|
||||
return
|
||||
from openpilot.starpilot.car.toyota.aol import qualified as toyota_aol_qualified
|
||||
companion = getattr(self, 'aol_intent_companion', None)
|
||||
if (healthy_expiry and toyota_aol_qualified(self.CP, marked_only=True) and
|
||||
not was_enabled and not self.active and not getattr(self, 'aol_ever_active', False) and
|
||||
not any((previous.desired_lateral, previous.desired_longitudinal, previous.lateral_active,
|
||||
previous.longitudinal_active, previous.native_acknowledged)) and
|
||||
not self.aol_authority_lost and getattr(self, 'aol_fault_reason', 'none') == 'availabilityPause' and
|
||||
availability_only(self.events.to_msg()) and not any(
|
||||
event.userDisable and event.name != EventName.pcmDisable for event in self.events.to_msg()) and
|
||||
intent is not None and intent is self.aol_last_intent and
|
||||
intent.settingsQualified and intent.allowedLatch and intent.lateralArmed and
|
||||
intent.carStateLogMonoTime == source_ns and
|
||||
0 < intent.observedMonoTime <= now_ns <= intent.validUntilMonoTime and
|
||||
companion is not None and companion.last_drain_ok and companion.session == intent.producerSessionId):
|
||||
from openpilot.starpilot.aol.transport_pause import _healthy_transport_native
|
||||
native = _healthy_transport_native(self.sm, self.CP, CS, self.aol_session_id, now_ns)
|
||||
if native is not None and native.pandaInventory and not any((native.requestedLateral,
|
||||
native.lateralAllowed, native.requestedLongitudinal, native.longitudinalAllowed)):
|
||||
# Cold readiness has no granted command to lose; the expired source still withdraws all output.
|
||||
self.aol_publication_retained_intent = intent
|
||||
return
|
||||
pause = getattr(self, 'aol_availability_pause', None)
|
||||
if (healthy_expiry and pause is not None and intent is not None and
|
||||
getattr(self, 'aol_fault_reason', 'none') in ('none', 'availabilityPause') and
|
||||
|
||||
@@ -98,7 +98,6 @@ class Plant:
|
||||
# always start at current speed
|
||||
model.modelV2.velocity.x = [float(self.speed)] + [float(self.speed + 0.5)] * (len(T_IDXS) - 1)
|
||||
model.modelV2.acceleration.x = [0.0] * len(T_IDXS)
|
||||
model.modelV2.meta.disengagePredictions.gasPressProbs = [float(prob_throttle)] * 6
|
||||
|
||||
control.controlsState.longControlState = LongCtrlState.pid if self.enabled else LongCtrlState.off
|
||||
ss.selfdriveState.experimentalMode = self.e2e
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
@@ -34,18 +34,10 @@ def update_frame(preview=None, controllers=None, star=None, game_mode=None, arab
|
||||
|
||||
|
||||
def main():
|
||||
device_type = HARDWARE.get_device_type() if COMMA_HARDWARE else ""
|
||||
camera_core_ui = device_type in ("mici", "tici", "tizi")
|
||||
background_ui = device_type == "mici"
|
||||
cores = {0, 1, 2, 7} if background_ui else {6} if camera_core_ui else {5}
|
||||
display_nice = 5 if background_ui else 0
|
||||
camera_core_ui = COMMA_HARDWARE and HARDWARE.get_device_type() in ("mici", "tici", "tizi")
|
||||
cores = {6} if camera_core_ui else {5}
|
||||
main_thread_configured = False
|
||||
if background_ui:
|
||||
drop_realtime()
|
||||
os.setpriority(os.PRIO_PROCESS, 0, display_nice)
|
||||
set_core_affinity(list(cores))
|
||||
else:
|
||||
config_realtime_process(0, Priority.UI)
|
||||
config_realtime_process(0, Priority.UI)
|
||||
|
||||
selection = select_ui(Profile.LARGE if BIG_UI else Profile.COMPACT, os.environ)
|
||||
print(f"UI: {'StarPilot' if selection.custom else 'upstream'} selected ({selection.reason})", file=sys.stderr, flush=True)
|
||||
@@ -84,10 +76,11 @@ def main():
|
||||
for should_render, frame_time, cpu_time in gui_app.render(before_frame=lambda: update_frame(preview, controllers, star, poll_game_mode, poll_arabesque)):
|
||||
if should_render:
|
||||
if camera_core_ui and not main_thread_configured:
|
||||
# Display work stays below real-time driving and audio playback.
|
||||
# Share the camera core with normal scheduling, without FIFO display
|
||||
# work preempting camera work or waiting on the driving core.
|
||||
drop_realtime()
|
||||
os.setpriority(os.PRIO_PROCESS, 0, display_nice)
|
||||
if os.sched_getscheduler(0) != os.SCHED_OTHER or os.getpriority(os.PRIO_PROCESS, 0) != display_nice:
|
||||
os.setpriority(os.PRIO_PROCESS, 0, 0)
|
||||
if os.sched_getscheduler(0) != os.SCHED_OTHER or os.getpriority(os.PRIO_PROCESS, 0) != 0:
|
||||
raise RuntimeError("Could not configure display scheduling")
|
||||
main_thread_configured = True
|
||||
# reaffine after power save offlines our core
|
||||
|
||||
@@ -16,6 +16,8 @@ GearShifter = car.CarState.GearShifter
|
||||
AOL_TOGGLE = 9
|
||||
PAUSE_LATERAL = 3
|
||||
PAUSE_LONGITUDINAL = 4
|
||||
# The only wheel action WheelPublisher delivers from cruise MAIN.
|
||||
ADOPT_SPEED_LIMIT = 10
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class AolSettings:
|
||||
@@ -28,6 +30,7 @@ class AolSettings:
|
||||
mode_actions: tuple[int, int, int] = (0, 0, 0)
|
||||
custom_actions: tuple[int, int, int] = (0, 0, 0)
|
||||
default_physical_buttons: bool = True
|
||||
main_wheel_action: bool = False
|
||||
|
||||
|
||||
def _button_value(params, key: str) -> int | None:
|
||||
@@ -64,6 +67,7 @@ def read_settings(params) -> AolSettings:
|
||||
enabled=params.get_bool('AlwaysOnLateral') and valid_pause, pause_brake_mps=pause_mps if valid_pause else 0.0,
|
||||
lkas_action=_axis_action(lkas), main_action=_axis_action(main),
|
||||
default_physical_buttons=lkas == 0 and main == 0,
|
||||
main_wheel_action=main == ADOPT_SPEED_LIMIT,
|
||||
distance_actions=(_button_action(params, 'DistanceButtonControl'),
|
||||
_button_action(params, 'LongDistanceButtonControl'),
|
||||
_button_action(params, 'VeryLongDistanceButtonControl')),
|
||||
@@ -241,10 +245,6 @@ class AolCardIntent:
|
||||
return
|
||||
if self._engagement_consumed or not active and not blocked:
|
||||
return
|
||||
if (not blocked and getattr(self, 'defer_full_engagement_on_gas', False) and CS.gasPressed and
|
||||
CS.cruiseState.available and
|
||||
CS.gearShifter not in (GearShifter.park, GearShifter.neutral, GearShifter.reverse, GearShifter.unknown)):
|
||||
return
|
||||
self._engagement_consumed = True
|
||||
if (blocked or not active or not self.settings.enabled or not CS.cruiseState.available or
|
||||
CS.gearShifter in (GearShifter.park, GearShifter.neutral, GearShifter.reverse, GearShifter.unknown)):
|
||||
@@ -319,7 +319,8 @@ class AolCardIntent:
|
||||
independent_buttons = self.independent_cruise_buttons and (main_managed or lkas_managed)
|
||||
# The opt-in is bound to existing HKG torque owners. Other physical
|
||||
# owners keep their current event and native contracts.
|
||||
main_toggle = not independent_buttons or main_managed
|
||||
main_toggle = (not independent_buttons or main_managed) and not (
|
||||
self.independent_cruise_buttons and self.settings.main_wheel_action)
|
||||
lkas_toggle = not independent_buttons or lkas_managed
|
||||
previously_held = (self._main_held and main_toggle) or (self._lkas_held and lkas_toggle)
|
||||
cancel_pressed = False
|
||||
|
||||
@@ -21,6 +21,15 @@ def _diagnostic_mask(cp):
|
||||
int(cp.flags) & int(HondaFlags.BOSCH) and qualified(cp, marked_only=True)) else 0
|
||||
if cp.brand == 'hyundai':
|
||||
from opendbc.car.hyundai.values import CAR, HyundaiFlags
|
||||
if cp.carFingerprint == CAR.HYUNDAI_ELANTRA_2021:
|
||||
from opendbc.car.hyundai.classic_scc_aol import qualified
|
||||
# Firmware latches this IRQ diagnostic without revoking native authority.
|
||||
# Limit compatibility to the recorded stock-SCC profile; keep raw faults;
|
||||
# native permission, receive health, relay and heartbeat remain required.
|
||||
recorded_flags = HyundaiFlags.SEND_LFA | HyundaiFlags.USE_FCA | HyundaiFlags.CHECKSUM_CRC8 | HyundaiFlags.HAS_LDA_BUTTON
|
||||
return (1 << 2) if (str(cp.fingerprintSource) == 'fw' and int(cp.flags) == int(recorded_flags) and
|
||||
len(cp.safetyConfigs) == 1 and int(cp.safetyConfigs[0].safetyParam) == 0xC00 and
|
||||
cp.alternativeExperience == 32 and qualified(cp, marked_only=True)) else 0
|
||||
if cp.carFingerprint != CAR.HYUNDAI_SONATA_HYBRID:
|
||||
return 0
|
||||
from opendbc.car.hyundai.classic_scc_aol import qualified
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
"""Elantra CAN interrupt faults block steering regardless of otherwise valid authority."""
|
||||
"""Exact stock-SCC Elantra IRQ diagnostics retain native authority vetoes."""
|
||||
from contextlib import ExitStack
|
||||
from dataclasses import replace
|
||||
from unittest import mock
|
||||
@@ -32,8 +32,7 @@ def recorded_cp():
|
||||
@pytest.mark.parametrize('fault_source', ['none', 'panda', 'native', 'both'])
|
||||
@pytest.mark.parametrize('full', [False, True])
|
||||
@pytest.mark.parametrize('fault', ['none', 'rx', 'relay', 'denied', 'request', 'expired-wire'])
|
||||
def test_recorded_irq_through_selfdrived_and_controls_blocks_faulted_steering(full, fault, fault_source):
|
||||
irq = fault_source != 'none'
|
||||
def test_recorded_irq_through_selfdrived_and_controls_keeps_native_vetoes(full, fault, fault_source):
|
||||
cp = recorded_cp()
|
||||
before = cp.to_dict()
|
||||
case = fixture.IoniqAvailabilityTests(methodName='runTest')
|
||||
@@ -70,20 +69,16 @@ def test_recorded_irq_through_selfdrived_and_controls_blocks_faulted_steering(fu
|
||||
if fault == 'request':
|
||||
options.update(native_request_lat=False, native_request_long=False)
|
||||
axis, state, command = drive(tick, **options)
|
||||
if fault == 'none' and not irq:
|
||||
if fault == 'none':
|
||||
assert axis.lateralActive and command.latActive, 'HEALTHY_ELANTRA_AUTHORITY_REJECTED'
|
||||
assert log.OnroadEvent.EventName.controlsMismatch not in sd.events.names
|
||||
native = current_native(sd.sm, sd.CP, now_ns=stamp, axis_session_id=sd.aol_session_id)
|
||||
assert native is not None and native.pandaInventory[0].faults == 0
|
||||
assert list(sd.sm['pandaStates'][0].faults) == []
|
||||
assert native is not None and native.pandaInventory[0].faults == (4 if fault_source in ('native', 'both') else 0)
|
||||
assert list(sd.sm['pandaStates'][0].faults) == ([log.PandaState.FaultType.interruptRateCan1] if fault_source in ('panda', 'both') else [])
|
||||
assert not axis.longitudinalActive and not command.longActive
|
||||
assert state.enabled == full
|
||||
else:
|
||||
assert not axis.lateralActive and not command.latActive, 'ELANTRA_HARDWARE_FAULT_MUST_BLOCK'
|
||||
if irq and fault == 'none':
|
||||
assert log.OnroadEvent.EventName.controlsMismatch in sd.events.names
|
||||
expected_faults = [log.PandaState.FaultType.interruptRateCan1] if fault_source in ('panda', 'both') else []
|
||||
assert list(sd.sm['pandaStates'][0].faults) == expected_faults
|
||||
assert not axis.longitudinalActive and not command.longActive
|
||||
assert cp.to_dict() == before
|
||||
|
||||
@@ -91,7 +86,7 @@ def test_recorded_irq_through_selfdrived_and_controls_blocks_faulted_steering(fu
|
||||
@pytest.mark.parametrize('name,value', tuple(log.PandaState.FaultType.schema.enumerants.items()))
|
||||
def test_other_and_mixed_faults_still_block(name, value):
|
||||
cp = recorded_cp()
|
||||
expected = True
|
||||
expected = name != 'interruptRateCan1'
|
||||
assert blocking_fault_mask(1 << value, cp) == expected
|
||||
assert blocking_fault_mask((1 << value) | 4, cp) == expected
|
||||
assert blocking_fault_list([value], cp) == expected
|
||||
|
||||
@@ -100,48 +100,3 @@ def test_ev6_card_cancel_join_requires_its_dedicated_physical_owner(restriction)
|
||||
owner._last_latch_edge_ns = now - 10_000_000
|
||||
assert retained_shared_long_cancel_intent(cp, cs, sm, feedback, owner,
|
||||
cancel_source_ns=now, now_ns=now, producer='authority-card') == (restriction == 'healthy')
|
||||
|
||||
|
||||
@pytest.mark.parametrize('ev6', (False, True))
|
||||
@pytest.mark.parametrize('restriction', ('healthy', 'no_source', 'old_source', 'lat_denied', 'rx',
|
||||
'heartbeat', 'same_health', 'wrong_session'))
|
||||
def test_cancel_can_join_new_raw_long_off_health_on_its_first_physical_sample(ev6, restriction):
|
||||
from openpilot.starpilot.aol.wire import encode_safety
|
||||
|
||||
cp, cs, sm, feedback, _, native, now = captured_context()
|
||||
if ev6:
|
||||
cp = ev6_params()
|
||||
cp.safetyConfigs[0].safetyParam |= 0x800
|
||||
cp.alternativeExperience = 32
|
||||
feedback.cp = cp_identity(cp)
|
||||
slot = replace(native.pandaInventory[0], controlsAllowed=True)
|
||||
native = replace(native, longitudinalAllowed=True, pandaInventory=(slot,),
|
||||
sourcePandaStatesMonoTime=now - 10_000_000)
|
||||
source = now
|
||||
if restriction == 'no_source':
|
||||
source = 0
|
||||
elif restriction == 'old_source':
|
||||
source = now - 30_000_001
|
||||
elif restriction == 'lat_denied':
|
||||
native = replace(native, lateralAllowed=False)
|
||||
elif restriction == 'same_health':
|
||||
native = replace(native, sourcePandaStatesMonoTime=now)
|
||||
elif restriction == 'wrong_session':
|
||||
native = replace(native, axisSessionId='foreign')
|
||||
from openpilot.cereal import messaging
|
||||
pandas = messaging.new_message('pandaStates', 1)
|
||||
pandas.pandaStates[0] = sm['pandaStates'][0]
|
||||
pandas.pandaStates[0].safetyRxChecksInvalid = restriction == 'rx'
|
||||
pandas.pandaStates[0].heartbeatLost = restriction == 'heartbeat'
|
||||
sm['pandaStates'] = pandas.as_reader().pandaStates
|
||||
sm['aolSafetyWire'] = encode_safety(native)
|
||||
assert feedback.longitudinal_cancel_ns == 0
|
||||
cleared = feedback.observe(sm, cp, cs, now, latched=True, cancel_source_ns=source,
|
||||
intent_producer_session='authority-card')
|
||||
assert cleared == (restriction != 'healthy')
|
||||
if restriction == 'healthy':
|
||||
assert feedback.longitudinal_cancel_ns == now
|
||||
cs.buttonEvents = []
|
||||
assert not feedback.observe(sm, cp, cs, now + 10_000_000, latched=True,
|
||||
intent_producer_session='authority-card')
|
||||
assert feedback.longitudinal_cancel_ns == now
|
||||
|
||||
@@ -194,49 +194,3 @@ def test_auxiliary_off_after_pending_observation_cannot_be_undone_by_activation(
|
||||
assert not owner.allowed_latch
|
||||
observe(owner, cs, 3)
|
||||
assert not owner.allowed_latch
|
||||
|
||||
|
||||
@pytest.mark.parametrize('factory', (ClassicLongCardIntent, ClassicSccCardIntent))
|
||||
@pytest.mark.parametrize('main', (0, AOL_TOGGLE))
|
||||
@pytest.mark.parametrize('lkas', (0, AOL_TOGGLE))
|
||||
def test_classic_hkg_gas_override_defers_arming_until_release(factory, main, lkas):
|
||||
owner, cs = factory(CP, replace(SETTINGS, main_action=main, lkas_action=lkas)), state()
|
||||
observe(owner, cs, 1, enabled=False, active=False)
|
||||
cs.gasPressed = True
|
||||
observe(owner, cs, 2)
|
||||
observe(owner, cs, 3)
|
||||
assert not owner.allowed_latch
|
||||
cs.gasPressed = False
|
||||
observe(owner, cs, 4)
|
||||
assert owner.allowed_latch and owner._last_latch_edge_ns == 4
|
||||
cs.brakePressed = True
|
||||
owner.update(cs, now_ns=5, fault_active=False)
|
||||
observe(owner, cs, 5, enabled=False, active=False)
|
||||
assert owner.allowed_latch and owner.output(cs)[0]
|
||||
|
||||
|
||||
@pytest.mark.parametrize('factory', (ClassicLongCardIntent, ClassicSccCardIntent))
|
||||
@pytest.mark.parametrize('veto', ('cancel', 'lkas', 'fault', 'rejection', 'invalid', 'main_off', 'gear', 'disabled', 'calibration'))
|
||||
def test_classic_hkg_pending_gas_engagement_cannot_replay_after_withdrawal(factory, veto):
|
||||
owner, cs = factory(CP, SETTINGS), state()
|
||||
observe(owner, cs, 1, enabled=False, active=False)
|
||||
cs.gasPressed = True
|
||||
observe(owner, cs, 2)
|
||||
if veto in ('cancel', 'lkas'):
|
||||
cs.buttonEvents = [car.CarState.ButtonEvent(type=veto, pressed=True)]
|
||||
elif veto == 'invalid':
|
||||
cs.canValid = False
|
||||
elif veto == 'main_off':
|
||||
cs.cruiseState.available = False
|
||||
elif veto == 'gear':
|
||||
cs.gearShifter = 'park'
|
||||
elif veto == 'disabled':
|
||||
owner.settings = replace(SETTINGS, enabled=False)
|
||||
elif veto == 'calibration':
|
||||
owner._calibration_rearm_required = True
|
||||
observe(owner, cs, 3, fault=veto == 'fault', rejection=3 if veto == 'rejection' else 0)
|
||||
owner.settings, owner._calibration_rearm_required = SETTINGS, False
|
||||
cs.gasPressed, cs.canValid = False, True
|
||||
cs.gearShifter, cs.cruiseState.available, cs.buttonEvents = 'drive', True, []
|
||||
observe(owner, cs, 4)
|
||||
assert not owner.allowed_latch
|
||||
|
||||
@@ -256,54 +256,3 @@ def test_simultaneous_cancel_keeps_default_timestamp_side_effects(opted_in, mapp
|
||||
owner.update(cs, now_ns=2, fault_active=False)
|
||||
assert not owner.allowed_latch
|
||||
assert owner._last_latch_edge_ns == (0 if opted_in and mapped else 2)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('identity', (CAR.GENESIS_GV60_EV_1ST_GEN, CAR.HYUNDAI_IONIQ_5,
|
||||
CAR.HYUNDAI_KONA_EV_2ND_GEN))
|
||||
@pytest.mark.parametrize('fault', ('none', 'no_cancel', 'lat_denied', 'rx', 'heartbeat', 'expired', 'same_health'))
|
||||
def test_shared_long_cancel_health_can_precede_native_status(identity, fault):
|
||||
from opendbc.car import gen_empty_fingerprint
|
||||
from opendbc.car.hyundai.canfd_stock_aol import qualified_long
|
||||
from openpilot.starpilot.aol.transport_pause import cp_identity
|
||||
|
||||
cp, cs, sm, feedback, owner, native, now = captured_context()
|
||||
if identity == CAR.GENESIS_GV60_EV_1ST_GEN:
|
||||
fingerprint = gen_empty_fingerprint()
|
||||
fingerprint[0][0x1cf] = 8
|
||||
cp = CarInterface.get_params(identity, fingerprint, [], True, False, False)
|
||||
cp.safetyConfigs[0].safetyParam |= 0x800
|
||||
cp.alternativeExperience = 32
|
||||
else:
|
||||
cp.carFingerprint = identity
|
||||
assert qualified_long(cp, marked_only=True)
|
||||
feedback.cp = cp_identity(cp)
|
||||
config = cp.safetyConfigs[0]
|
||||
slot = replace(native.pandaInventory[0], safetyModel=int(config.safetyModel.raw), safetyParam=config.safetyParam,
|
||||
controlsAllowed=True)
|
||||
native = replace(native, safetyModel=int(config.safetyModel.raw), safetyParam=config.safetyParam,
|
||||
longitudinalAllowed=True, pandaInventory=(slot,), sourcePandaStatesMonoTime=now)
|
||||
sm['aolSafetyWire'] = encode_safety(native)
|
||||
pandas = messaging.new_message('pandaStates', 1, valid=True, logMonoTime=now)
|
||||
panda = pandas.pandaStates[0]
|
||||
panda.safetyModel, panda.safetyParam, panda.alternativeExperience = config.safetyModel, config.safetyParam, 32
|
||||
panda.controlsAllowed = True
|
||||
sm['pandaStates'] = pandas.as_reader().pandaStates
|
||||
if fault == 'no_cancel':
|
||||
cs.buttonEvents = []
|
||||
assert not feedback.observe(sm, cp, cs, now, latched=True, cancel_source_ns=now,
|
||||
intent_producer_session='authority-card')
|
||||
assert bool(feedback.longitudinal_cancel_ns) == (fault != 'no_cancel')
|
||||
original_receipt = feedback.longitudinal_cancel_ns
|
||||
cs.buttonEvents = []
|
||||
pandas.pandaStates[0].controlsAllowed = False
|
||||
pandas.pandaStates[0].safetyRxChecksInvalid = fault == 'rx'
|
||||
pandas.pandaStates[0].heartbeatLost = fault == 'heartbeat'
|
||||
sm['pandaStates'] = pandas.as_reader().pandaStates
|
||||
sm.logMonoTime['pandaStates'] = now + (0 if fault == 'same_health' else 10_000_000)
|
||||
if fault == 'lat_denied':
|
||||
sm['aolSafetyWire'] = encode_safety(replace(native, lateralAllowed=False))
|
||||
later = now + (201_000_000 if fault == 'expired' else 10_000_000)
|
||||
retained = feedback._longitudinal_cancel_current(sm, cp, cs, sm['aolAxisState'], later,
|
||||
latched=True, cancel_source_ns=0)
|
||||
assert (retained is not None) == (fault == 'none')
|
||||
assert feedback.longitudinal_cancel_ns == original_receipt
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
"""Cruise MAIN assigned to Adopt speed limit is not also the Hyundai physical lateral toggle."""
|
||||
import pytest
|
||||
from opendbc.car import structs
|
||||
from openpilot.starpilot.aol.intent import AolCardIntent, AolSettings, read_settings
|
||||
from openpilot.starpilot.aol.tests.test_ioniq6_stock_repair import stock
|
||||
from openpilot.starpilot.car.hyundai.aol import create_intent, qualified_ioniq6
|
||||
|
||||
|
||||
def long_cp():
|
||||
cp = stock(False)
|
||||
cp.pcmCruise = False
|
||||
cp.openpilotLongitudinalControl = True
|
||||
cp.safetyConfigs[0].safetyParam = 0x8815
|
||||
assert qualified_ioniq6(cp)
|
||||
return cp
|
||||
|
||||
|
||||
def press(owner, cs, button, now_ns, *, engaged=False):
|
||||
cs.buttonEvents = [structs.CarState.ButtonEvent(type=button, pressed=True)]
|
||||
owner.update(cs, now_ns=now_ns, fault_active=False, standard_enabled=engaged)
|
||||
cs.buttonEvents = [structs.CarState.ButtonEvent(type=button, pressed=False)]
|
||||
owner.update(cs, now_ns=now_ns + 1, fault_active=False, standard_enabled=engaged)
|
||||
|
||||
|
||||
def lateral(owner):
|
||||
return owner.allowed_latch and not owner.pause_lateral
|
||||
|
||||
|
||||
def armed(owner):
|
||||
cs = structs.CarState(canValid=True, gearShifter='drive')
|
||||
cs.cruiseState.available = True
|
||||
owner.update(cs, now_ns=1, fault_active=False)
|
||||
press(owner, cs, 'lkas', 2)
|
||||
assert lateral(owner)
|
||||
return cs
|
||||
|
||||
|
||||
@pytest.mark.parametrize('main_wheel_action', [False, True])
|
||||
def test_ioniq6_main_assigned_to_adopt_keeps_lkas_armed_lateral(main_wheel_action):
|
||||
owner = create_intent(long_cp(), AolSettings(True, 0., 0, 0, (0, 0, 0), (0, 0, 0), main_wheel_action=main_wheel_action))
|
||||
assert owner.independent_cruise_buttons
|
||||
cs = armed(owner)
|
||||
press(owner, cs, 'mainCruise', 10, engaged=True)
|
||||
assert lateral(owner) == main_wheel_action
|
||||
press(owner, cs, 'lkas', 20, engaged=True)
|
||||
assert lateral(owner) != main_wheel_action
|
||||
|
||||
|
||||
def test_main_toggle_is_unchanged_without_independent_cruise_buttons():
|
||||
owner = AolCardIntent(AolSettings(True, 0., 0, 0, (0, 0, 0), (0, 0, 0), main_wheel_action=True), explicit_latch=True)
|
||||
cs = armed(owner)
|
||||
press(owner, cs, 'mainCruise', 10, engaged=True)
|
||||
assert not lateral(owner)
|
||||
|
||||
|
||||
class SavedParams:
|
||||
def __init__(self, values):
|
||||
self.values = values
|
||||
|
||||
def get(self, key, return_default=False):
|
||||
return self.values.get(key)
|
||||
|
||||
def get_bool(self, key):
|
||||
return self.values.get(key) == b'1'
|
||||
|
||||
def get_param_path(self, key):
|
||||
return f'/nonexistent/{key}'
|
||||
|
||||
|
||||
@pytest.mark.parametrize('lkas,main,main_wheel', [(b'0', b'10', True), (b'0', b'9', False), (b'10', b'0', False), (b'0', b'8', False)])
|
||||
def test_only_main_adopt_is_marked_as_a_main_wheel_action(lkas, main, main_wheel):
|
||||
settings = read_settings(SavedParams({'AlwaysOnLateral': b'1', 'LKASButtonControl': lkas, 'MainCruiseButtonControl': main}))
|
||||
assert settings.main_wheel_action == main_wheel
|
||||
@@ -1,114 +0,0 @@
|
||||
"""Cold Toyota readiness is not a previously granted steering command."""
|
||||
from contextlib import ExitStack
|
||||
from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
from openpilot.cereal import log, messaging
|
||||
from openpilot.common.prefix import OpenpilotPrefix
|
||||
from openpilot.starpilot.aol.transport_pause import TransportPauseFeedback
|
||||
from openpilot.starpilot.aol.tests import test_ioniq_availability as fixture
|
||||
from openpilot.starpilot.aol.tests.test_toyota_moving_pcm_off_deadline import cp_for, moving
|
||||
from openpilot.starpilot.aol.tests.test_toyota_parking_brake_deadline import refresh_saved_settings
|
||||
|
||||
|
||||
BASE = 10_000_000_000
|
||||
|
||||
|
||||
@pytest.mark.parametrize('profile', ('prius_long', 'prius_stock', 'camera_long', 'camera_stock'))
|
||||
@pytest.mark.parametrize('gas', (False, True))
|
||||
def test_cold_source_expiry_waits_without_inventing_authority(profile, gas):
|
||||
with ExitStack() as cleanup:
|
||||
cleanup.enter_context(OpenpilotPrefix())
|
||||
cleanup.enter_context(patch.dict('os.environ', {'SIMULATION': '0', 'REPLAY': '0'}))
|
||||
case = fixture.IoniqAvailabilityTests(methodName='runTest')
|
||||
cleanup.callback(case.doCleanups)
|
||||
sd, _, drive = case._authority_callers(False, cp=cp_for(profile), genuine_events=True)
|
||||
refresh_saved_settings(sd)
|
||||
feedback = TransportPauseFeedback(sd.CP)
|
||||
feedback_sm = messaging.SubMaster(['aolAxisState', 'onroadEvents', 'aolSafetyWire', 'pandaStates'])
|
||||
|
||||
def card_feedback(now):
|
||||
samples = [sd.pm.messages[name] for name in ('aolAxisState', 'onroadEvents')]
|
||||
for name in ('aolSafetyWire', 'pandaStates'):
|
||||
sample = messaging.new_message(name, len(sd.sm[name]), valid=sd.sm.valid[name], logMonoTime=sd.sm.logMonoTime[name])
|
||||
setattr(sample, name, sd.sm[name])
|
||||
samples.append(sample.as_reader())
|
||||
feedback_sm.update_msgs(now / 1e9, samples)
|
||||
assert not feedback.observe(feedback_sm, sd.CP, moving(), now, latched=True,
|
||||
intent_producer_session='authority-card')
|
||||
|
||||
update_events, publish = sd.update_events, sd.publish_axis_proof
|
||||
|
||||
def startup_events(cs):
|
||||
sd.sm.valid['longitudinalPlan'] = False
|
||||
return update_events(cs)
|
||||
|
||||
def delayed_publication(cs, **kwargs):
|
||||
with patch('openpilot.selfdrive.selfdrived.selfdrived.time.monotonic_ns', return_value=BASE + 31_148_802):
|
||||
return publish(cs, **kwargs)
|
||||
|
||||
with patch.object(sd, 'update_events', side_effect=startup_events), \
|
||||
patch.object(sd, 'publish_axis_proof', side_effect=delayed_publication):
|
||||
axis, host, command = drive(0, observed_state=moving(), gas=gas, allowed=False,
|
||||
native_request_lat=False, native_request_long=False)
|
||||
assert log.OnroadEvent.EventName.commIssue in sd.events.names
|
||||
assert log.OnroadEvent.EventName.controlsMismatch not in sd.events.names, 'TOYOTA_COLD_SOURCE_EXPIRY'
|
||||
assert axis.sourceCarStateMonoTime == 0 and str(axis.faultReason) == 'availabilityPause'
|
||||
assert not axis.nativeAcknowledged and not axis.desiredLateral and not axis.desiredLongitudinal
|
||||
assert not axis.lateralActive and not axis.longitudinalActive and not host.enabled
|
||||
assert not command.latActive and not command.longActive and not sd.aol_authority_lost
|
||||
assert sd.aol_publication_retained_intent.validUntilMonoTime == BASE + 200_000_000
|
||||
card_feedback(BASE + 31_148_802)
|
||||
# A fresh source is required; requesting LAT alone still cannot produce it.
|
||||
axis, host, command = drive(4, observed_state=moving(), gas=gas, allowed=False,
|
||||
native_request_lat=False, native_request_long=False)
|
||||
assert axis.sourceCarStateMonoTime == BASE + 40_000_000
|
||||
assert not axis.lateralActive and not command.latActive and not host.enabled
|
||||
card_feedback(BASE + 40_000_000)
|
||||
axis, host, command = drive(5, observed_state=moving(), gas=gas, allowed=True,
|
||||
native_request_lat=True, native_request_long=False)
|
||||
assert axis.lateralActive and command.latActive and not host.enabled and not command.longActive
|
||||
assert log.OnroadEvent.EventName.controlsMismatch not in sd.events.names
|
||||
card_feedback(BASE + 50_000_000)
|
||||
assert feedback.ever_lateral_active
|
||||
|
||||
|
||||
@pytest.mark.parametrize('restriction', ('previously_active', 'native_requested', 'native_granted', 'native_invalid',
|
||||
'rx_fault', 'relay_fault', 'expired_source', 'can_invalid', 'critical'))
|
||||
def test_cold_wait_never_waives_authority_or_health_failures(restriction):
|
||||
with ExitStack() as cleanup:
|
||||
cleanup.enter_context(OpenpilotPrefix())
|
||||
cleanup.enter_context(patch.dict('os.environ', {'SIMULATION': '0', 'REPLAY': '0'}))
|
||||
case = fixture.IoniqAvailabilityTests(methodName='runTest')
|
||||
cleanup.callback(case.doCleanups)
|
||||
sd, _, drive = case._authority_callers(False, cp=cp_for('prius_long'), genuine_events=True)
|
||||
refresh_saved_settings(sd)
|
||||
if restriction == 'previously_active':
|
||||
axis, _, command = drive(0, observed_state=moving(), native_request_long=False)
|
||||
assert axis.lateralActive and command.latActive
|
||||
original_events, publish = sd.update_events, sd.publish_axis_proof
|
||||
|
||||
def startup_events(cs):
|
||||
sd.sm.valid['longitudinalPlan'] = False
|
||||
return original_events(cs)
|
||||
|
||||
def delayed_publication(cs, **kwargs):
|
||||
delay = 201_000_000 if restriction == 'expired_source' else 31_148_802
|
||||
with patch('openpilot.selfdrive.selfdrived.selfdrived.time.monotonic_ns', return_value=BASE + 10_000_000 + delay):
|
||||
return publish(cs, **kwargs)
|
||||
|
||||
state = moving()
|
||||
if restriction == 'can_invalid':
|
||||
state.canValid = False
|
||||
options = {'allowed': restriction == 'native_granted', 'native_request_lat': restriction == 'native_requested',
|
||||
'native_request_long': False, 'native_valid': restriction != 'native_invalid',
|
||||
'panda_fault': restriction == 'rx_fault', 'relay_fault': restriction == 'relay_fault'}
|
||||
if restriction == 'critical':
|
||||
options['critical_event'] = log.OnroadEvent.EventName.canError
|
||||
with patch.object(sd, 'update_events', side_effect=startup_events), \
|
||||
patch.object(sd, 'publish_axis_proof', side_effect=delayed_publication):
|
||||
axis, host, command = drive(1, observed_state=state, **options)
|
||||
assert log.OnroadEvent.EventName.controlsMismatch in sd.events.names
|
||||
assert not axis.lateralActive and not axis.longitudinalActive
|
||||
assert not host.enabled and not command.latActive and not command.longActive
|
||||
assert str(axis.faultReason) == 'critical'
|
||||
@@ -805,7 +805,7 @@ class TransportPauseFeedback:
|
||||
return native
|
||||
|
||||
def _longitudinal_cancel_current(self, sm, cp, cs, axis, now_ns, *, latched, cancel_source_ns, withdrawal_required=True) -> SafetyState | None:
|
||||
from opendbc.car.hyundai.canfd_stock_aol import qualified_long as qualified
|
||||
from opendbc.car.hyundai.ev6_aol import qualified
|
||||
from openpilot.starpilot.car.hyundai.aol import qualified_ccnc_long_aol as ev9_qualified
|
||||
from opendbc.car.structs import car
|
||||
|
||||
@@ -833,7 +833,7 @@ class TransportPauseFeedback:
|
||||
return None
|
||||
native = current_native(sm, cp, now_ns=now_ns, axis_session_id=self.session)
|
||||
if (native is None or not native.pandaInventory or not native.requestedLateral or not native.lateralAllowed or
|
||||
not native.requestedLongitudinal or
|
||||
not native.requestedLongitudinal or withdrawal_required and native.longitudinalAllowed or
|
||||
native.observedMonoTime < self.lateral_native_ns or
|
||||
not native_inventory_matches_cp(native, cp) or
|
||||
(native.pandaSerial, tuple(p.hardwareSerial for p in native.pandaInventory)) != self.lateral_inventory):
|
||||
@@ -849,12 +849,6 @@ class TransportPauseFeedback:
|
||||
not p.heartbeatLost and not blocking_fault_list(p.faults, cp) and
|
||||
(str(c.safetyModel) not in ('silent', 'noOutput') or not p.controlsAllowed)
|
||||
for p, c in zip(pandas, cp.safetyConfigs, strict=True))
|
||||
raw_withdrawal = bool((self.longitudinal_cancel_ns or fresh_cancel) and
|
||||
0 < native.sourcePandaStatesMonoTime < stamp and
|
||||
all(not p.controlsAllowed for p, c in zip(pandas, cp.safetyConfigs, strict=True)
|
||||
if str(c.safetyModel) not in ('silent', 'noOutput')))
|
||||
if withdrawal_required and native.longitudinalAllowed and not raw_withdrawal:
|
||||
return None
|
||||
return native if current_pandas else None
|
||||
|
||||
|
||||
|
||||
@@ -3,8 +3,6 @@ from openpilot.starpilot.aol.intent import AolCardIntent
|
||||
|
||||
|
||||
class ClassicLongCardIntent(AolCardIntent):
|
||||
defer_full_engagement_on_gas = True
|
||||
|
||||
def __init__(self, cp, settings):
|
||||
super().__init__(settings, explicit_latch=True, independent_cruise_buttons=(
|
||||
independent_buttons_qualified(cp, marked_only=False)))
|
||||
|
||||
@@ -4,8 +4,6 @@ from openpilot.starpilot.car.hyundai.forte_intent import ForteCardIntent
|
||||
|
||||
|
||||
class ClassicSccCardIntent(ForteCardIntent):
|
||||
defer_full_engagement_on_gas = True
|
||||
|
||||
observe_stock_engagement = True
|
||||
|
||||
def __init__(self, cp, settings):
|
||||
|
||||
+121
-28
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"reviewed_schema_commit": "63beafc6af294d0759b47dc3bc546f24082a2a5d",
|
||||
"reviewed_schema_sha256": "ca01ef4f6d2b15c5fac59ce237ec6e49f1167e2408903c9088ee322ca490de31",
|
||||
"reviewed_schema_commit": "cd0f46b9d65a9c5e47002f57e5ad6625c9757c63",
|
||||
"reviewed_schema_sha256": "5effa2eb1fbbada3d1bbaae0acd3d31d780d8ec0e6ee1e214fedbc38ae400182",
|
||||
"incumbent_fixture_sha256": "a36ab54c00b5bbe61d520873d24aadfb8f33babd70c31a07046516d5545838d9",
|
||||
"types": {
|
||||
"0xf35cc4560bbf6ec2": {
|
||||
@@ -18,7 +18,7 @@
|
||||
},
|
||||
"new_storage": {
|
||||
"dataWordCount": 3,
|
||||
"pointerCount": 7,
|
||||
"pointerCount": 6,
|
||||
"preferredListEncoding": "inlineComposite",
|
||||
"isGroup": false,
|
||||
"discriminantCount": 0,
|
||||
@@ -59,30 +59,10 @@
|
||||
"hadExplicitDefault": false
|
||||
},
|
||||
"name": "toyotaCruise",
|
||||
"codeOrder": 39,
|
||||
"discriminantValue": 65535,
|
||||
"ordinal": {
|
||||
"explicit": 38
|
||||
}
|
||||
},
|
||||
{
|
||||
"slot": {
|
||||
"offset": 6,
|
||||
"type": {
|
||||
"struct": {
|
||||
"typeId": 15172299076723568810
|
||||
}
|
||||
},
|
||||
"defaultValue": {
|
||||
"schemaValueHex": "00000000040000000000000002000100100000000000000000000000000000000000000000000000"
|
||||
},
|
||||
"hadExplicitDefault": false
|
||||
},
|
||||
"name": "steeringLimitInfo",
|
||||
"codeOrder": 38,
|
||||
"discriminantValue": 65535,
|
||||
"ordinal": {
|
||||
"explicit": 39
|
||||
"explicit": 38
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -101,7 +81,7 @@
|
||||
"discriminantOffset": 0
|
||||
},
|
||||
"new_storage": {
|
||||
"dataWordCount": 18,
|
||||
"dataWordCount": 21,
|
||||
"pointerCount": 22,
|
||||
"preferredListEncoding": "inlineComposite",
|
||||
"isGroup": false,
|
||||
@@ -252,6 +232,78 @@
|
||||
"ordinal": {
|
||||
"explicit": 58
|
||||
}
|
||||
},
|
||||
{
|
||||
"slot": {
|
||||
"offset": 141,
|
||||
"type": {
|
||||
"bool": null
|
||||
},
|
||||
"defaultValue": {
|
||||
"bool": false
|
||||
},
|
||||
"hadExplicitDefault": false
|
||||
},
|
||||
"name": "acceptedIsPreview",
|
||||
"codeOrder": 59,
|
||||
"discriminantValue": 65535,
|
||||
"ordinal": {
|
||||
"explicit": 59
|
||||
}
|
||||
},
|
||||
{
|
||||
"slot": {
|
||||
"offset": 18,
|
||||
"type": {
|
||||
"uint64": null
|
||||
},
|
||||
"defaultValue": {
|
||||
"uint64": 0
|
||||
},
|
||||
"hadExplicitDefault": false
|
||||
},
|
||||
"name": "wheelOverrideSince",
|
||||
"codeOrder": 60,
|
||||
"discriminantValue": 65535,
|
||||
"ordinal": {
|
||||
"explicit": 60
|
||||
}
|
||||
},
|
||||
{
|
||||
"slot": {
|
||||
"offset": 19,
|
||||
"type": {
|
||||
"uint64": null
|
||||
},
|
||||
"defaultValue": {
|
||||
"uint64": 0
|
||||
},
|
||||
"hadExplicitDefault": false
|
||||
},
|
||||
"name": "wheelAdoptMonoTime",
|
||||
"codeOrder": 61,
|
||||
"discriminantValue": 65535,
|
||||
"ordinal": {
|
||||
"explicit": 61
|
||||
}
|
||||
},
|
||||
{
|
||||
"slot": {
|
||||
"offset": 40,
|
||||
"type": {
|
||||
"float32": null
|
||||
},
|
||||
"defaultValue": {
|
||||
"float32": 0.0
|
||||
},
|
||||
"hadExplicitDefault": false
|
||||
},
|
||||
"name": "wheelAdoptTargetMps",
|
||||
"codeOrder": 62,
|
||||
"discriminantValue": 65535,
|
||||
"ordinal": {
|
||||
"explicit": 62
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -672,12 +724,53 @@
|
||||
"codeOrder": 12
|
||||
}
|
||||
]
|
||||
},
|
||||
"0x9ccdc8676701b412": {
|
||||
"kind": "struct",
|
||||
"before_name": "SlcAction",
|
||||
"after_name": "SlcAction",
|
||||
"old_field_count": 8,
|
||||
"old_storage": {
|
||||
"dataWordCount": 4,
|
||||
"pointerCount": 4,
|
||||
"preferredListEncoding": "inlineComposite",
|
||||
"isGroup": false,
|
||||
"discriminantCount": 0,
|
||||
"discriminantOffset": 0
|
||||
},
|
||||
"new_storage": {
|
||||
"dataWordCount": 5,
|
||||
"pointerCount": 4,
|
||||
"preferredListEncoding": "inlineComposite",
|
||||
"isGroup": false,
|
||||
"discriminantCount": 0,
|
||||
"discriminantOffset": 0
|
||||
},
|
||||
"added_fields": [
|
||||
{
|
||||
"slot": {
|
||||
"offset": 4,
|
||||
"type": {
|
||||
"uint64": null
|
||||
},
|
||||
"defaultValue": {
|
||||
"uint64": 0
|
||||
},
|
||||
"hadExplicitDefault": false
|
||||
},
|
||||
"name": "wheelAdoptMonoTime",
|
||||
"codeOrder": 8,
|
||||
"discriminantValue": 65535,
|
||||
"ordinal": {
|
||||
"explicit": 8
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"published_addition_commits": {
|
||||
"slc": "ab8368f287b78b42f8590722878c7d5834609e19",
|
||||
"flm": "b151ca11cb4d88f32d7adfdbcff50baf29f4fbbc",
|
||||
"tesla_cooperative_steering": "63beafc6af294d0759b47dc3bc546f24082a2a5d"
|
||||
"flm": "b151ca11cb4d88f32d7adfdbcff50baf29f4fbbc"
|
||||
},
|
||||
"review_basis": "Published SLC, FLM, and Tesla cooperative-steering additions. Every incumbent wire field descriptor and enum prefix was independently checked unchanged; only the exact typed deltas listed here are permitted. Tesla adds steeringLimitInfo @39 at pointer slot 6 using the existing SteeringLimitInfo type. ToyotaCruise @38 retains pointer slot 5, type, and default; only its declaration codeOrder changes from 38 to 39."
|
||||
"review_basis": "Published SLC and FLM append-only changes. Every incumbent compiled field descriptor and enum prefix was independently checked unchanged; only the exact seven typed deltas below are permitted."
|
||||
}
|
||||
|
||||
@@ -115,47 +115,6 @@ def test_additive_feedback_survives_incumbent_reader_relay(historical):
|
||||
assert recovered.feedbackPublication.to_dict() == expected_feedback
|
||||
|
||||
|
||||
def test_additive_steering_limits_preserve_defaults_and_survive_incumbent_relay(historical):
|
||||
original = historical.StarPilotCarState.new_message(
|
||||
pedalMaxRegen=True, pedalLongActive=True, sourceCarStateMonoTime=123456,
|
||||
gps={'latitude': 41.25, 'longitude': -87.5, 'hasFix': True})
|
||||
incumbent = original.to_dict()
|
||||
with custom.StarPilotCarState.from_bytes(original.to_bytes()) as decoded:
|
||||
assert not decoded.steeringLimitInfo.valid
|
||||
assert decoded.steeringLimitInfo.monoTime == 0
|
||||
current = decoded.as_builder()
|
||||
current.feedbackPublication = {'version': 1, 'publicationSequence': 27, 'reportedTorque': 0.25}
|
||||
current.toyotaCruise = {'version': 1, 'status': 'active', 'targetKph': 72., 'sessionId': 'incumbent'}
|
||||
current.steeringLimitInfo = {
|
||||
'valid': True, 'modelLimitErrorDeg': 1.25, 'resumeLimitErrorDeg': 2.5,
|
||||
'cooperativeLimitErrorDeg': 3.75, 'cooperativeOffsetDeg': -0.5,
|
||||
'monoTime': 123456789, 'combinedLimitErrorDeg': 4.0}
|
||||
expected = current.to_dict()
|
||||
with historical.StarPilotCarState.from_bytes(current.to_bytes()) as old:
|
||||
assert old.to_dict() == incumbent
|
||||
relayed = old.as_builder().to_bytes()
|
||||
with custom.StarPilotCarState.from_bytes(relayed) as recovered:
|
||||
assert recovered.to_dict() == expected
|
||||
|
||||
|
||||
@pytest.mark.parametrize(('before', 'after'), [
|
||||
('dashboardSpeedLimit @3 :Float32;', 'dashboardSpeedLimit @3 :Float64;'),
|
||||
('toyotaCruise @38 :ToyotaCruise;', 'toyotaCruise @38 :StarPilotCarControl.SteeringLimitInfo;'),
|
||||
('steeringLimitInfo @39 :StarPilotCarControl.SteeringLimitInfo;', 'steeringLimitInfo @39 :StarPilotCarState.Gps;'),
|
||||
('steeringLimitInfo @39 :StarPilotCarControl.SteeringLimitInfo;',
|
||||
'steeringLimitInfo @39 :StarPilotCarControl.SteeringLimitInfo; unreviewed @40 :Bool;'),
|
||||
])
|
||||
def test_reviewed_steering_addition_does_not_allow_other_schema_changes(historical, tmp_path, before, after):
|
||||
source = Path(CEREAL_PATH, 'custom.capnp').read_text()
|
||||
assert source.count(before) == 1
|
||||
path = tmp_path / 'unreviewed.capnp'
|
||||
path.write_text(source.replace(before, after))
|
||||
parser = capnp.SchemaParser()
|
||||
changed = parser.load(str(path), imports=[CEREAL_PATH, str(files('opendbc') / 'car')])
|
||||
with pytest.raises(AssertionError):
|
||||
compare_node(historical.StarPilotCarState, changed.StarPilotCarState)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(('fields', 'compatible'), [
|
||||
('value @0 :UInt32; label @1 :Text;', True),
|
||||
('value @0 :UInt32; label @1 :Text; extra @2 :Bool;', False),
|
||||
|
||||
@@ -1,138 +0,0 @@
|
||||
"""Legacy Toyota pre-warning is distinct from dirty radar and CAN failure."""
|
||||
from contextlib import ExitStack
|
||||
from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
from opendbc.can import CANPacker
|
||||
from opendbc.car import Bus, gen_empty_fingerprint
|
||||
from opendbc.car.toyota.interface import CarInterface
|
||||
from opendbc.car.toyota.radar_interface import RadarInterface
|
||||
from opendbc.car.toyota.values import CAR, DBC
|
||||
from openpilot.cereal import messaging
|
||||
from openpilot.common.prefix import OpenpilotPrefix
|
||||
from openpilot.starpilot.aol.tests import test_ioniq_availability as fixture
|
||||
from openpilot.starpilot.aol.tests.test_toyota_moving_pcm_off_deadline import cp_for
|
||||
|
||||
|
||||
LEGACY = (CAR.TOYOTA_PRIUS, CAR.TOYOTA_PRIUS_RETROFIT, CAR.TOYOTA_RAV4H, CAR.LEXUS_RX)
|
||||
|
||||
|
||||
def params(candidate, alpha):
|
||||
if candidate == CAR.TOYOTA_PRIUS_RETROFIT:
|
||||
return cp_for('prius_long' if alpha else 'prius_stock')
|
||||
return CarInterface.get_params(candidate, gen_empty_fingerprint(), [], alpha, False, False)
|
||||
|
||||
|
||||
def batch(radar, packer, tick, *, status=1, pre=3, counter=1358, target=False, omit=None):
|
||||
frames = []
|
||||
for address in radar.RADAR_A_MSGS + radar.RADAR_B_MSGS:
|
||||
if address == omit:
|
||||
continue
|
||||
first = address == radar.RADAR_A_MSGS[0]
|
||||
fields = ({'LONG_DIST': 30. if target and first else 255., 'VALID': int(target and first),
|
||||
'LAT_DIST': 0., 'REL_SPEED': -1.} if address in radar.RADAR_A_MSGS else {'SCORE': 100})
|
||||
frames.append(packer.make_can_msg(address, 1, fields))
|
||||
if omit != 'status':
|
||||
frames.append(packer.make_can_msg('STATUS_MSG', 1, {
|
||||
'RADAR_STATUS': status, 'RADAR_PRE_FAULT': pre, 'INVALID_COUNTER': counter}))
|
||||
return [(1_000_000_000 + tick * 50_000_000, frames)]
|
||||
|
||||
|
||||
def feed(radar, packer, **kwargs):
|
||||
result = None
|
||||
for tick in range(30):
|
||||
result = radar.update(batch(radar, packer, tick, **kwargs))
|
||||
assert result is not None
|
||||
return result
|
||||
|
||||
|
||||
@pytest.mark.parametrize('candidate', LEGACY)
|
||||
@pytest.mark.parametrize('alpha', (False, True))
|
||||
@pytest.mark.parametrize('counter', (1358, 1872, 2643))
|
||||
@pytest.mark.parametrize('target', (False, True))
|
||||
def test_legacy_ready_radar_pre_warning_keeps_live_measurements(candidate, alpha, counter, target):
|
||||
cp = params(candidate, alpha)
|
||||
radar = RadarInterface(cp)
|
||||
packer = CANPacker(DBC[cp.carFingerprint][Bus.radar])
|
||||
result = feed(radar, packer, counter=counter, target=target)
|
||||
assert radar.rcp.can_valid
|
||||
assert not result.errors.radarUnavailableTemporary, 'LEGACY_RADAR_PRE_WARNING'
|
||||
assert not result.errors.canError
|
||||
assert len(result.points) == int(target)
|
||||
if target:
|
||||
assert result.points[0].dRel == 30. and result.points[0].vRel == -1.
|
||||
|
||||
|
||||
@pytest.mark.parametrize('profile', ('prius_long', 'camera_long'))
|
||||
@pytest.mark.parametrize('condition', ('initializing', 'fault', 'unknown_status', 'unknown_pre', 'dirty_threshold',
|
||||
'saturated_counter', 'missing_status', 'missing_track'))
|
||||
def test_real_radar_faults_and_receive_checks_remain(profile, condition):
|
||||
cp = cp_for(profile)
|
||||
radar = RadarInterface(cp)
|
||||
packer = CANPacker(DBC[cp.carFingerprint][Bus.radar])
|
||||
options = {'target': True}
|
||||
if condition == 'initializing':
|
||||
options['status'] = 0
|
||||
elif condition == 'fault':
|
||||
options['status'] = 3
|
||||
elif condition == 'unknown_status':
|
||||
options['status'] = 2
|
||||
elif condition == 'unknown_pre':
|
||||
options['pre'] = 1
|
||||
elif condition == 'dirty_threshold':
|
||||
options['counter'] = 2659
|
||||
elif condition == 'saturated_counter':
|
||||
options['counter'] = 4095
|
||||
elif condition == 'missing_status':
|
||||
options['omit'] = 'status'
|
||||
else:
|
||||
options['omit'] = radar.RADAR_A_MSGS[0]
|
||||
result = feed(radar, packer, **options)
|
||||
assert result.errors.canError or result.errors.radarUnavailableTemporary
|
||||
if condition.startswith('missing'):
|
||||
assert result.errors.canError and not radar.rcp.can_valid
|
||||
else:
|
||||
assert result.errors.radarUnavailableTemporary
|
||||
|
||||
|
||||
def test_tss2_pre_fault_behavior_is_unchanged():
|
||||
cp = cp_for('camera_long')
|
||||
radar = RadarInterface(cp)
|
||||
result = feed(radar, CANPacker(DBC[cp.carFingerprint][Bus.radar]))
|
||||
assert result.errors.radarUnavailableTemporary
|
||||
|
||||
|
||||
@pytest.mark.parametrize('profile', ('prius_long', 'prius_stock'))
|
||||
def test_actual_radar_to_selfdrived_keeps_pre_warning_distinct_from_fault(profile):
|
||||
with ExitStack() as cleanup:
|
||||
cleanup.enter_context(OpenpilotPrefix())
|
||||
cleanup.enter_context(patch.dict('os.environ', {'SIMULATION': '0', 'AOL_REPLAY_RUNTIME': '1'}))
|
||||
cp = cp_for(profile)
|
||||
case = fixture.IoniqAvailabilityTests(methodName='runTest')
|
||||
cleanup.callback(case.doCleanups)
|
||||
sd, _, drive = case._authority_callers(True, cp=cp, genuine_events=True)
|
||||
radar = RadarInterface(cp)
|
||||
packer = CANPacker(DBC[cp.carFingerprint][Bus.radar])
|
||||
observed = fixture.car_state()
|
||||
observed.cruiseState.available = observed.cruiseState.enabled = True
|
||||
original = sd.update_events
|
||||
errors = [feed(radar, packer).errors]
|
||||
|
||||
def events(cs):
|
||||
message = messaging.new_message('radarState', valid=True)
|
||||
message.radarState.radarErrors = errors[0].as_reader()
|
||||
sd.sm.data['radarState'] = message.radarState.as_reader()
|
||||
return original(cs)
|
||||
|
||||
with patch.object(sd, 'update_events', side_effect=events):
|
||||
for tick in range(5):
|
||||
axis, host, command = drive(tick, observed_state=observed)
|
||||
assert host.enabled and axis.lateralActive and command.latActive
|
||||
assert not sd.sm['radarState'].radarErrors.radarUnavailableTemporary, 'LEGACY_RADAR_PRE_WARNING'
|
||||
if cp.openpilotLongitudinalControl:
|
||||
assert axis.longitudinalActive and command.longActive
|
||||
errors[0] = feed(radar, packer, status=3, counter=2659).errors
|
||||
axis, host, command = drive(5, observed_state=observed, native_request_long=False, long_allowed=False)
|
||||
if cp.openpilotLongitudinalControl:
|
||||
assert not host.enabled and not axis.longitudinalActive and not command.longActive
|
||||
assert axis.lateralActive and command.latActive
|
||||
@@ -6,9 +6,8 @@ CEM selects Experimental mode for configured speed, road, signal, curve, lead, s
|
||||
|
||||
`ConditionalPlannerHost` joins the current native MPC result, current model/car/radar observations, saved preferences, and qualified wheel-button events. It produces a short-lived proposal on `slcState.conditionalMode`; it does not own the effective driving mode. `selfdrived` independently checks the current vehicle capability, longitudinal axis, drive, saved configuration, and input freshness before accepting that proposal. Its ordinary `ExperimentalMode` request remains the fallback. The UI displays an active conditional reason only from a fresh selfdrived acknowledgment matching the exact current `selfdriveState` frame and active longitudinal axis; the saved family is displayed separately.
|
||||
|
||||
Speed-limit availability is independent of mode selection. The retired `SLCFallback=1`
|
||||
preference cannot request Experimental or veto Chill, including when Maps or Vision
|
||||
becomes unknown or stale. Other scene triggers, manual mode choices and committed
|
||||
Speed-limit availability is independent of mode selection. A missing or stale Maps or Vision limit does
|
||||
not request Experimental or veto Chill. Other scene triggers, manual mode choices and committed
|
||||
stop handling retain their existing behavior. Historical speed-limit reason labels
|
||||
remain readable in recorded routes.
|
||||
|
||||
@@ -49,6 +48,6 @@ Traffic assignment does not suppress ordinary profile tuning.
|
||||
|
||||
Scene inputs distinguish false from unknown. Model filters advance once per original model tick; the oldest required source bounds freshness separately, so a slower fresh radar cannot stall a model filter or renew stale data. Startup, clock discontinuities, and drive changes require fresh observations. Conditional Chill requires every applicable veto to be known false. Disabled build features can be known absent; an enabled producer without a qualified observation remains unknown.
|
||||
|
||||
Current producers cover native MPC follow time and stop/throttle intent, model lane geometry, qualified adjacent radar, SLC attribution, pedal override, and the effective mode reported by selfdrived. The exact Ioniq media path supplies source-stamped Traffic Mode to CEM and a bounded Traffic follow/jerk, acceleration and cruise-braking profile. The shared committed-turn evaluator feeds both the stop detector and slower-lead retuning from original car/model stamps. Traffic retains the existing 0.75-second effective headway floor; saved values below it remain unsupported and intact. The native planner shapes the cruise-only floor for a qualified SLC cap, a validated named Eco/Sport braking personality, or the explicit global Braking response. A saved Traffic braking category reverts to its 0.42 m/s² cruise baseline for a present radar lead or standstill, then recovers through the existing profile smoother; MPC and model braking retain priority. The saved lane-change close-gap preference and current lateral minimum-speed policy are separate from conditional scene selection. Frozen weather and other hazard-context braking inputs remain absent. Stop-sign, forced-stop, and dashboard stop-sign producers remain absent. The historical red-light name is already represented by the current stop-light observation, so it does not need a duplicate owner.
|
||||
Current producers cover native MPC follow time and stop/throttle intent, model lane geometry, qualified adjacent radar, SLC attribution, pedal override, and the effective mode reported by selfdrived. The exact Ioniq media path supplies source-stamped Traffic Mode to CEM and a bounded Traffic follow/jerk, acceleration and cruise-braking profile. The shared committed-turn evaluator feeds both the stop detector and slower-lead retuning from original car/model stamps. Traffic retains the existing 0.75-second effective headway floor; saved values below it remain unsupported and intact. The native planner retains the selected cruise-braking response for an SLC cap. Conditional speed comparisons and curve control receive the same-cycle SLC-capped cruise target while the driver's SET remains unchanged. A saved Traffic braking category reverts to its 0.42 m/s² cruise baseline for a present radar lead or standstill, then recovers through the existing profile smoother; MPC and model braking retain priority. The saved lane-change close-gap preference and current lateral minimum-speed policy are separate from conditional scene selection. Frozen weather and other hazard-context braking inputs remain absent. Stop-sign, forced-stop, and dashboard stop-sign producers remain absent. The historical red-light name is already represented by the current stop-light observation, so it does not need a duplicate owner.
|
||||
|
||||
The source contract includes frozen `starpilot/controls/lib/conditional_experimental_mode.py`, `conditional_chill_mode.py`, `starpilot/common/experimental_state.py`, and the planner's mode selection. Tests compare executable frozen policy/detector traces and exercise current serialized messages, native MPC/radar joins, lifecycle boundaries, manual controls, and settings concurrency. These are integration evidence, not vehicle qualification. Normal availability is limited to the exact tagged Ioniq 6 LONG profile; the separate ECU handoff and native safety acknowledgment must succeed first. Missing scene producers and road behavior still require evaluation.
|
||||
|
||||
@@ -59,6 +59,7 @@ class ConditionalModeHost:
|
||||
selected_t_follow_s: float | None = None,
|
||||
selected_t_follow_observed_mono_ns: int | None = None,
|
||||
settings_owner: ConditionalSettingsOwner | None = None,
|
||||
cruise_ceiling_mps: float | None = None,
|
||||
) -> HostProposal:
|
||||
signal_options = None
|
||||
settings_revision = None
|
||||
@@ -120,6 +121,7 @@ class ConditionalModeHost:
|
||||
selected_t_follow_observed_mono_ns=selected_t_follow_observed_mono_ns,
|
||||
owner_context=owner_context,
|
||||
signal_options=signal_options,
|
||||
cruise_ceiling_mps=cruise_ceiling_mps,
|
||||
)
|
||||
authority = projected.authority
|
||||
if authority is not None and authority.fresh and (not authority.system_long_capable or authority.safe_mode or
|
||||
|
||||
@@ -180,6 +180,7 @@ class ConditionalPlannerHost:
|
||||
drive_id: int, manual: ManualState | None = None,
|
||||
owner_context: ConditionalOwnerContext | None = None,
|
||||
native_plan_valid: bool = False,
|
||||
cruise_ceiling_mps: float | None = None,
|
||||
manual_event=None,
|
||||
ui_event=None,
|
||||
) -> tuple[HostProposal, SettingsSnapshot | None]:
|
||||
@@ -289,6 +290,7 @@ class ConditionalPlannerHost:
|
||||
manual=self.manual_state if manual is None else manual, owner_context=context,
|
||||
selected_t_follow_s=selected_follow_time(planner),
|
||||
selected_t_follow_observed_mono_ns=sm.logMonoTime.get('modelV2'),
|
||||
cruise_ceiling_mps=cruise_ceiling_mps,
|
||||
settings_owner=self.settings,
|
||||
)
|
||||
snapshot = self.settings.current
|
||||
|
||||
@@ -406,6 +406,7 @@ class SceneProjector:
|
||||
selected_t_follow_observed_mono_ns: int | None = None,
|
||||
owner_context: ConditionalOwnerContext | None = None,
|
||||
signal_options: CEMOptions | None = None,
|
||||
cruise_ceiling_mps: float | None = None,
|
||||
) -> ProjectedScene:
|
||||
if (
|
||||
type(now_mono_ns) is not int
|
||||
@@ -441,6 +442,9 @@ class SceneProjector:
|
||||
speed = _ego_speed(car) if car is not None and _field(car, 'canValid') is True and _field(car, 'canTimeout') is False else None
|
||||
cruise_kph = _number(_field(car, 'vCruise'), high=V_CRUISE_UNSET_KPH) if speed is not None else None
|
||||
cruise = cruise_kph * KPH_TO_MPS if cruise_kph is not None and 0 < cruise_kph < V_CRUISE_UNSET_KPH else None
|
||||
if cruise_ceiling_mps is not None:
|
||||
cap = _number(cruise_ceiling_mps, high=V_CRUISE_UNSET_KPH * KPH_TO_MPS)
|
||||
cruise = min(cruise, cap) if cruise is not None and cap is not None and cap > 0 else None
|
||||
curvature = _number(_field(controls, 'curvature'), low=-1.0, high=1.0) if controls is not None else None
|
||||
raw_curve = abs(speed * speed * curvature) >= FROZEN_CURVE_ACCEL_MPS2 if speed is not None and curvature is not None else None
|
||||
horizon = None
|
||||
|
||||
@@ -114,6 +114,20 @@ def owner_context(stamp: int, *, stop_time=8.0, slower=True, stopped=False):
|
||||
|
||||
|
||||
class TestSceneProjector(unittest.TestCase):
|
||||
def test_conditional_speed_comparisons_use_the_slc_cap_without_mutating_set(self):
|
||||
for cap, expected in ((None, 20.), (15., 15.), (25., 20.), (float('nan'), None)):
|
||||
with self.subTest(cap=cap):
|
||||
projector = SceneProjector()
|
||||
sm = FakeSubMaster(serialized_scene(), MONO-5_000_000)
|
||||
projector.project(sm, CP, now_mono_ns=MONO, now_boot_ns=BOOT, sample_skew_ns=1000)
|
||||
now = MONO+50_000_000
|
||||
sm.stamp(now)
|
||||
sm.payloads.update(serialized_scene(stamp=BOOT+50_000_000))
|
||||
result = projector.project(sm, CP, now_mono_ns=now, now_boot_ns=BOOT+50_000_000,
|
||||
sample_skew_ns=1000, safe_mode=False, owner_context=owner_context(now), cruise_ceiling_mps=cap)
|
||||
self.assertEqual(result.scene.set_speed_mps, expected)
|
||||
self.assertEqual(sm['carState'].vCruise, 72.)
|
||||
|
||||
def test_standstill_filter_undershoot_requires_fresh_physical_zero_speed(self):
|
||||
cases = ((-1.401298464324817e-45, True, 0., True),
|
||||
(-0.0008391447481699288, True, 0., True),
|
||||
|
||||
@@ -80,8 +80,14 @@ class CurveHost:
|
||||
self.previous_long_active = False
|
||||
self.previous_active_headway = None
|
||||
|
||||
def sample(self, sm, CP, now_ns: int, *, drive_id: int = 0, event=None, follow_time_s: float | None = None):
|
||||
def sample(self, sm, CP, now_ns: int, *, drive_id: int = 0, event=None, follow_time_s: float | None = None,
|
||||
cruise_ceiling_mps: float | None = None):
|
||||
frame = frame_from_submaster(sm, CP, now_ns, drive_id=drive_id)
|
||||
if cruise_ceiling_mps is not None:
|
||||
if not math.isfinite(cruise_ceiling_mps) or cruise_ceiling_mps <= 0:
|
||||
frame = replace(frame, inputs_fresh=False)
|
||||
else:
|
||||
frame = replace(frame, cruise_mps=min(frame.cruise_mps, cruise_ceiling_mps))
|
||||
if drive_id and self.lead_drive_id and drive_id != self.lead_drive_id:
|
||||
self._reset_leads()
|
||||
if drive_id:
|
||||
|
||||
@@ -44,6 +44,12 @@ class TestCurveHost(unittest.TestCase):
|
||||
self.sm.logMonoTime['radarState'] = NOW - 1_000_000_000
|
||||
self.assertIsNone(self.host.sample(self.sm, self.cp, NOW + 50_000_000).ceiling_mps)
|
||||
|
||||
def test_curve_uses_same_cycle_slc_capped_cruise_without_mutating_set(self):
|
||||
with patch.object(self.host.runtime, 'step', wraps=self.host.runtime.step) as step:
|
||||
self.host.sample(self.sm, self.cp, NOW, follow_time_s=1.45, cruise_ceiling_mps=20.)
|
||||
self.assertEqual(step.call_args.args[0].cruise_mps, 20.)
|
||||
self.assertEqual(self.sm['carState'].vCruise, 108.)
|
||||
|
||||
def test_stock_owner_and_malformed_field_fail_closed(self):
|
||||
self.cp.openpilotLongitudinalControl = False
|
||||
self.assertIsNone(self.host.sample(self.sm, self.cp, NOW).ceiling_mps)
|
||||
|
||||
@@ -21,7 +21,7 @@ CYCLE_PERSONALITY = "__starpilot_controller_action__:cycle_driving_personality"
|
||||
|
||||
# Saved preference shortcuts retain the current owner's authority and dependencies.
|
||||
FEATURE_KEYS = {
|
||||
"slc": ("SpeedLimitController", "ShowSpeedLimits", "SLCConfirmation", "SLCConfirmationHigher", "SLCConfirmationLower"),
|
||||
"slc": ("SpeedLimitController", "ShowSpeedLimits", "SLCConfirmation"),
|
||||
"lane": ("LaneCentering", "LaneCenteringPauseOnSignal"),
|
||||
"curve": ("CurveSpeedController", "CurveSpeedControllerNoLead", "ShowCSCStatus"),
|
||||
"aol": ("AlwaysOnLateral", "NostalgiaMode"),
|
||||
|
||||
@@ -61,12 +61,21 @@ DESCRIPTIONS = {
|
||||
'CurveSpeedControllerNoLead': 'Pauses curve-speed adjustments while following a lead vehicle.',
|
||||
'ShowCSCStatus': 'Shows when Curve Speed Controller is adjusting speed on the driving screen.',
|
||||
'curve_reset': 'Deletes saved cornering learning so Curve Speed Controller can learn your preferences again.',
|
||||
'SpeedLimitController': 'Adjusts the set speed to accepted speed limits using your source priority and offsets.',
|
||||
'SpeedLimitController': 'Follows accepted limits plus offsets up to SET speed. Missing data uses the previous accepted limit.',
|
||||
'SLCOnEngage': 'Starts software cruise SET at the SLC limit plus offset, including a saved limit when live data is missing. RES restores the previous SET.',
|
||||
'SLCOverrideMode': ('Cruise uses this target when you lift off the accelerator. ' +
|
||||
'Holding your speed uses the highest speed you reached, up to SET.'),
|
||||
'SLCWheelOverride': 'Press + to raise SET past the adjusted limit and use your new SET speed.',
|
||||
'ShowSpeedLimits': 'Shows recognized speed-limit signs on the driving screen.',
|
||||
'SLCConfirmation': 'Auto adopts new limits immediately. With Automatic adoption off, + accepts and − declines within 10 seconds.',
|
||||
'SLCAdvanceSeconds': 'Offers mapped limits early using road distance and current speed. Existing adoption and confirmation settings still apply.',
|
||||
'SLCConfirmation': ('Off adopts new limits automatically. Both, Higher only, and Lower only prompt relative to the last accepted limit. ' +
|
||||
'+ accepts and − declines within 30 seconds.'),
|
||||
'SLCAdvanceHigherSeconds': ('Offers higher mapped limits early using road distance and current speed. ' +
|
||||
'Existing adoption and confirmation settings still apply.'),
|
||||
'SLCAdvanceLowerSeconds': ('Offers lower mapped limits early using road distance and current speed. ' +
|
||||
'Existing adoption and confirmation settings still apply.'),
|
||||
'SLCPriority1': 'Chooses the first source checked for a fresh speed limit.',
|
||||
'SLCPriority2': 'Chooses the source used when the primary source has no fresh speed limit.',
|
||||
'SLCPriority3': 'Chooses an optional third source when neither earlier source has a fresh speed limit.',
|
||||
'slc_reset': 'Resets saved speed-limit offsets while retaining valid configured speed ranges.',
|
||||
'reset_calibration': 'Clears saved camera and steering calibration. The device recalibrates on the next drive.',
|
||||
'reset_driver_monitoring': 'Clear saved wheel-side detection. Automatic detection restarts on the next drive.',
|
||||
@@ -100,7 +109,7 @@ DESCRIPTIONS = {
|
||||
'DeveloperSidebar': 'Shows live developer metrics on the right of the Big driving screen. Off gives that space back to the road view.',
|
||||
'DriverCamera': 'Shows the driver camera while reversing.',
|
||||
'StockConfidenceBallWidget': 'Uses StarPilot driving widgets in place of the stock confidence ball.',
|
||||
'HidePersonalityAlerts': 'Hides the alert shown when the driving personality changes on the comma and Android Auto.',
|
||||
'HidePersonalityAlerts': 'Hides the alert shown when the driving personality changes.',
|
||||
'ShowBrakeStatus': 'Colors the steering wheel to show braking and acceleration.',
|
||||
'HideLeadMarker': 'Shows the lead-vehicle indicator on the driving screen.',
|
||||
'LeadInfo': 'Shows the lead vehicle distance or speed beside its indicator.',
|
||||
|
||||
@@ -77,10 +77,15 @@
|
||||
"RelaxedJerkSpeedDecrease",
|
||||
"RemoteStartBootsComma",
|
||||
"ReverseCruise",
|
||||
"SLCAdvanceSeconds",
|
||||
"SLCAdvanceHigherSeconds",
|
||||
"SLCAdvanceLowerSeconds",
|
||||
"SLCConfirmation",
|
||||
"SLCOnEngage",
|
||||
"SLCOverrideMode",
|
||||
"SLCPriority1",
|
||||
"SLCPriority2",
|
||||
"SLCPriority3",
|
||||
"SLCWheelOverride",
|
||||
"ScreenBrightness",
|
||||
"ScreenBrightnessOnroad",
|
||||
"ScreenTimeout",
|
||||
|
||||
@@ -63,40 +63,23 @@ class TestCanonicalRecovery(TestCase):
|
||||
self.assertTrue(recovery.confirm(pending['intent'], self.session, self.generation)['saved'])
|
||||
self.assertEqual(recovery.snapshot(self.session, self.generation)['count'], 0)
|
||||
|
||||
def test_legacy_confirmation_flags_do_not_create_hidden_customization(self):
|
||||
self.params.put_bool('SLCConfirmation', False, block=True)
|
||||
self.params.put_bool('SLCConfirmationLower', True, block=True)
|
||||
self.params.put_bool('SLCConfirmationHigher', True, block=True)
|
||||
def test_directional_confirmation_resets_to_off(self):
|
||||
self.params.put('SLCConfirmation', 4, block=True)
|
||||
recovery = self.recovery()
|
||||
view = recovery.snapshot(self.session, self.generation)
|
||||
children = [row for row in view['rows'] if row['label'] in ('Confirm lower limits', 'Confirm higher limits')]
|
||||
self.assertEqual(children, [])
|
||||
self.assertFalse(self.params.get_bool('SLCConfirmation'))
|
||||
self.assertTrue(self.params.get_bool('SLCConfirmationLower'))
|
||||
self.assertTrue(self.params.get_bool('SLCConfirmationHigher'))
|
||||
|
||||
def test_reset_confirmation_master_preserves_ignored_legacy_bytes(self):
|
||||
for key in ('SLCConfirmation', 'SLCConfirmationLower', 'SLCConfirmationHigher'):
|
||||
self.params.put_bool(key, True, block=True)
|
||||
recovery = self.recovery()
|
||||
view = recovery.snapshot(self.session, self.generation)
|
||||
selected = [row['id'] for row in view['rows'] if row['label'] in
|
||||
('Automatic adoption', 'Confirm lower limits', 'Confirm higher limits')]
|
||||
self.assertEqual(len(selected), 1)
|
||||
pending = recovery.preview(view['view'], selected, self.session, self.generation)
|
||||
row = next(row for row in view['rows'] if row['label'] == 'Confirm limits')
|
||||
pending = recovery.preview(view['view'], [row['id']], self.session, self.generation)
|
||||
self.assertTrue(recovery.confirm(pending['intent'], self.session, self.generation)['saved'])
|
||||
self.assertFalse(self.params.get_bool('SLCConfirmation'))
|
||||
for key in ('SLCConfirmationLower', 'SLCConfirmationHigher'):
|
||||
self.assertTrue(self.params.get_bool(key))
|
||||
self.assertFalse(self.params.get('SLCConfirmation'))
|
||||
|
||||
def test_unsupported_rows_remain_visible_but_cannot_block_available_reset_all(self):
|
||||
from dataclasses import replace
|
||||
self.params.put_bool('SLCConfirmation', True, block=True)
|
||||
self.params.put('SLCConfirmation', 1, block=True)
|
||||
self.params.put_bool('ShowBrakeStatus', True, block=True)
|
||||
self.context.value = replace(self.context.value, cp=None, cp_raw=None)
|
||||
recovery = self.recovery()
|
||||
view = recovery.snapshot(self.session, self.generation)
|
||||
unsupported = next(row for row in view['rows'] if row['label'] == 'Automatic adoption')
|
||||
unsupported = next(row for row in view['rows'] if row['label'] == 'Confirm limits')
|
||||
self.assertFalse(unsupported['resetAvailable'])
|
||||
with self.assertRaises(SettingsChanged):
|
||||
recovery.preview(view['view'], [unsupported['id']], self.session, self.generation)
|
||||
@@ -104,7 +87,7 @@ class TestCanonicalRecovery(TestCase):
|
||||
self.assertTrue(selected)
|
||||
pending = recovery.preview(view['view'], selected, self.session, self.generation)
|
||||
self.assertTrue(recovery.confirm(pending['intent'], self.session, self.generation)['saved'])
|
||||
self.assertTrue(self.params.get_bool('SLCConfirmation'))
|
||||
self.assertTrue(self.params.get('SLCConfirmation'))
|
||||
self.assertFalse(self.params.get_bool('ShowBrakeStatus'))
|
||||
|
||||
def test_other_canonical_inactive_children_reset_without_turning_their_masters_on(self):
|
||||
|
||||
@@ -44,20 +44,17 @@ class TestRecoveryHttp(TestCase):
|
||||
rows = self.request('/api/settings/non-defaults', cookie=cookie)[1]['rows']
|
||||
self.assertNotIn('Wheel brake and acceleration colors', [row['label'] for row in rows])
|
||||
|
||||
def test_confirmation_reset_uses_only_master_and_preserves_legacy_flags(self):
|
||||
for key in ('SLCConfirmation', 'SLCConfirmationLower', 'SLCConfirmationHigher'):
|
||||
self.params.put_bool(key, True, block=True)
|
||||
def test_confirmation_reset_restores_off(self):
|
||||
self.params.put('SLCConfirmation', 1, block=True)
|
||||
cookie = self.login()
|
||||
payload = self.selection(cookie, ['Automatic adoption'])
|
||||
payload = self.selection(cookie, ['Confirm limits'])
|
||||
status, pending, _ = self.request('/api/settings/recovery/preview', cookie=cookie, payload=payload)
|
||||
self.assertEqual(status, 200)
|
||||
result = self.request('/api/settings/recovery/confirm', cookie=cookie,
|
||||
payload={'intent': pending['intent'], 'confirmed': True})
|
||||
self.assertEqual(result[0], 200)
|
||||
self.assertTrue(result[1]['saved'])
|
||||
self.assertFalse(self.params.get_bool('SLCConfirmation'))
|
||||
for key in ('SLCConfirmationLower', 'SLCConfirmationHigher'):
|
||||
self.assertTrue(self.params.get_bool(key))
|
||||
self.assertFalse(self.params.get('SLCConfirmation'))
|
||||
|
||||
def test_wrong_selection_and_unconfirmed_requests_are_rejected(self):
|
||||
self.params.put_bool('ShowBrakeStatus', True, block=True)
|
||||
@@ -81,13 +78,13 @@ class TestRecoveryHttp(TestCase):
|
||||
self.assertTrue(self.params.get_bool('ShowBrakeStatus'))
|
||||
|
||||
def test_unsupported_rows_are_reported_and_available_reset_all_does_not_touch_them(self):
|
||||
self.params.put_bool('SLCConfirmation', True, block=True)
|
||||
self.params.put('SLCConfirmation', 1, block=True)
|
||||
self.params.put_bool('ShowBrakeStatus', True, block=True)
|
||||
self.context.value = replace(self.context.value, cp=None, cp_raw=None)
|
||||
cookie = self.login()
|
||||
status, view, _ = self.request('/api/settings/non-defaults', cookie=cookie)
|
||||
self.assertEqual(status, 200)
|
||||
unsupported = next(row for row in view['rows'] if row['label'] == 'Automatic adoption')
|
||||
unsupported = next(row for row in view['rows'] if row['label'] == 'Confirm limits')
|
||||
self.assertFalse(unsupported['resetAvailable'])
|
||||
self.assertEqual(self.request('/api/settings/recovery/preview', cookie=cookie,
|
||||
payload={'view': view['view'], 'selected': [unsupported['id']]})[0], 409)
|
||||
@@ -99,4 +96,4 @@ class TestRecoveryHttp(TestCase):
|
||||
self.assertEqual(result[0], 200)
|
||||
self.assertTrue(result[1]['saved'])
|
||||
self.assertFalse(self.params.get_bool('ShowBrakeStatus'))
|
||||
self.assertTrue(self.params.get_bool('SLCConfirmation'))
|
||||
self.assertTrue(self.params.get('SLCConfirmation'))
|
||||
|
||||
@@ -107,7 +107,7 @@ class TestOffroadConfigurationPages(unittest.TestCase):
|
||||
'conditional': ('Saved driving mode',),
|
||||
'conditional/cem': ('Speed threshold', 'Speed with lead', 'Stop lights'),
|
||||
'lane_change': ('Allow lane changes', 'Minimum lane-change speed', 'Automatic Lane Changes', 'Lane Change Speed'),
|
||||
'slc': ('Speed Limit Controller', 'Automatic adoption'),
|
||||
'slc': ('Speed Limit Controller', 'Confirm limits'),
|
||||
'profiles': ('Force Stop', 'Selected Acceleration Profile', 'Selected Deceleration Profile'),
|
||||
'torque': ('Steering Controller', 'Turn Assist', 'Lat Accel', 'Friction'),
|
||||
'wheel': ('Short press', 'Hold', 'Distance long press', 'MODE press', 'Star button'),
|
||||
|
||||
@@ -138,8 +138,6 @@ OnroadLayoutPage.methods.setProjectionLeadInfo.call(leadVm, "Both")
|
||||
assert.equal(leadVm.state.draft.leadInfo, "Speed")
|
||||
assert.equal(leadChanges.length, 2)
|
||||
assert.equal(validDocument({ ...native.document, signalBorder: true }, native.metadata), false, "the comma screen keeps its C4 settings")
|
||||
assert.match(OnroadLayoutPage.template, /Personality alerts follow the comma screen setting\./)
|
||||
assert.doesNotMatch(OnroadLayoutPage.template, /setProjectionDisplay\('hidePersonalityAlerts', value\)/)
|
||||
assert.match(OnroadLayoutPage.template, /Red Blind-Spot Border/)
|
||||
assert.match(OnroadLayoutPage.template, /Amber Signal Border/)
|
||||
const borderChanges = []
|
||||
|
||||
@@ -76,7 +76,7 @@ class TestSavedTorquePage(unittest.TestCase):
|
||||
self.assertTrue(self.gateway._context('torque').editing_saved_vehicle)
|
||||
self.assertEqual(self.context.sample().cp.carFingerprint, 'HYUNDAI_IONIQ_6')
|
||||
self.assertEqual(self.gateway._context('slc').cp.carFingerprint, 'HYUNDAI_IONIQ_6')
|
||||
slc = next(row for row in self.page('slc')['rows'] if row['label'] == 'Automatic adoption')
|
||||
slc = next(row for row in self.page('slc')['rows'] if row['label'] == 'Confirm limits')
|
||||
self.assertTrue(slc['available'])
|
||||
self.offroad.return_value = False
|
||||
with patch.object(self.context, 'configuration_allowed', return_value=True):
|
||||
|
||||
@@ -461,9 +461,9 @@ class SettingsGatewayTest(unittest.TestCase):
|
||||
self.assertIn("Vision", primary["choices"])
|
||||
self.assertIn("Vision", secondary["choices"])
|
||||
self.assertNotIn("require your confirmation", primary["reason"])
|
||||
confirmation = next(row for row in rows if row["label"] == "Automatic adoption")
|
||||
self.assertEqual(confirmation["value"], "On")
|
||||
self.assertIn("immediately", confirmation["reason"])
|
||||
confirmation = next(row for row in rows if row["label"] == "Confirm limits")
|
||||
self.assertEqual(confirmation["value"], "Off")
|
||||
self.assertIn("automatically", confirmation["reason"])
|
||||
self.assertIn("accepts", confirmation["reason"])
|
||||
|
||||
with mock.patch.dict(os.environ, {"SLC_REPLAY_RUNTIME": "0", "SLC_VISION_DEVELOPMENT": "0"}):
|
||||
|
||||
@@ -119,34 +119,18 @@ class SenderJobTest(unittest.TestCase):
|
||||
|
||||
def test_sends_in_the_background_and_drops_the_snapshot_when_too_big(self):
|
||||
bundles, sent = [], []
|
||||
sending, release = threading.Event(), threading.Event()
|
||||
|
||||
def bundle(system_snapshot=True):
|
||||
bundles.append(system_snapshot)
|
||||
return "logs.zip", b"x" * (DISCORD_FILE_LIMIT + 1 if system_snapshot else 10)
|
||||
|
||||
def sender(*args):
|
||||
sending.set()
|
||||
if not release.wait(2):
|
||||
raise TimeoutError("The test did not release the send")
|
||||
sent.append(args)
|
||||
|
||||
job = StarPortLogSender(bundle, sender=sender, summary=lambda: SUMMARY, url=lambda: URL)
|
||||
try:
|
||||
self.assertEqual(job.start("hello")["state"], "sending")
|
||||
self.assertTrue(sending.wait(2))
|
||||
self.assertEqual(job.status()["state"], "sending")
|
||||
self.assertEqual(sent, [])
|
||||
finally:
|
||||
release.set()
|
||||
if job._thread is not None:
|
||||
job._thread.join(2)
|
||||
self.assertFalse(job._thread.is_alive())
|
||||
job = StarPortLogSender(bundle, sender=lambda *args: sent.append(args), summary=lambda: SUMMARY, url=lambda: URL)
|
||||
self.assertEqual(job.start("hello")["state"], "sending")
|
||||
status = self.wait(job)
|
||||
self.assertEqual(status["state"], "sent")
|
||||
self.assertIn("snapshot was left out", status["message"])
|
||||
self.assertEqual(bundles, [True, False])
|
||||
self.assertEqual(sent, [(b"x" * 10, "logs.zip", "hello", SUMMARY)])
|
||||
self.assertEqual(sent[0][2], "hello")
|
||||
|
||||
def test_one_send_at_a_time_and_failures_are_reported(self):
|
||||
release = threading.Event()
|
||||
|
||||
@@ -355,7 +355,8 @@ export const ONROAD_LAYOUT_TEMPLATE = `
|
||||
<div class="gx-layout__subhead"><h4>StarPort screen options</h4></div>
|
||||
<p class="gx-note">Only the StarPort screen uses these. The comma screen has its own options on the Comma tab. Save to apply.</p>
|
||||
<h5 class="gx-layout__display-group">Alerts and borders</h5>
|
||||
<p class="gx-note">Personality alerts follow the comma screen setting.</p>
|
||||
<GalaxySettingRow feedback="draft" :row="switchRow('Hide Personality Alerts', state.draft.hidePersonalityAlerts === true)" :index="0"
|
||||
:disabled="!editable || !!state.drag || !!state.layerDrag" :save-value="(index, value) => setProjectionDisplay('hidePersonalityAlerts', value)" />
|
||||
<GalaxySettingRow feedback="draft" :row="switchRow('Red Blind-Spot Border', state.draft.blindSpotBorder === true)" :index="1"
|
||||
:disabled="!editable || !!state.drag || !!state.layerDrag" :save-value="(index, value) => setProjectionDisplay('blindSpotBorder', value)" />
|
||||
<GalaxySettingRow feedback="draft" :row="switchRow('Amber Signal Border', state.draft.signalBorder === true)" :index="2"
|
||||
|
||||
@@ -156,8 +156,7 @@ class HKGVehicleTorquePolicy(HKGShapedTorquePolicy):
|
||||
if self.profile == 'kia_stinger_2022':
|
||||
return value * shaping.get_kia_stinger_2022_center_taper_scale(setpoint, speed)
|
||||
if self.profile == 'kia_ev6':
|
||||
return (value * shaping.get_kia_ev6_low_speed_center_taper_scale(setpoint, speed) *
|
||||
shaping.get_kia_ev6_center_output_scale(setpoint, speed, self.future_lateral_accel))
|
||||
return value * shaping.get_kia_ev6_low_speed_center_taper_scale(setpoint, speed) * shaping.get_kia_ev6_center_output_scale(setpoint, speed)
|
||||
if self.profile == 'kia_carnival':
|
||||
return (
|
||||
value
|
||||
|
||||
@@ -544,8 +544,6 @@ KIA_EV6_LOW_SPEED_CENTER_TAPER_SPEED_WIDTH = 1.4
|
||||
|
||||
KIA_EV6_CENTER_OUTPUT_TAPER_MAX = 0.14
|
||||
|
||||
KIA_EV6_HIGHWAY_CENTER_DAMPING = 0.08
|
||||
|
||||
KIA_EV6_CENTER_OUTPUT_TAPER_LAT = 0.30
|
||||
|
||||
KIA_EV6_CENTER_OUTPUT_TAPER_LAT_WIDTH = 0.08
|
||||
@@ -1300,17 +1298,12 @@ def get_kia_ev6_low_speed_center_taper_scale(desired_lateral_accel: float, v_ego
|
||||
reduction = KIA_EV6_LOW_SPEED_CENTER_TAPER_MAX * speed_weight * center_weight
|
||||
return 1.0 - reduction
|
||||
|
||||
def get_kia_ev6_center_output_scale(desired_lateral_accel: float, v_ego: float,
|
||||
future_lateral_accel: float | None = None) -> float:
|
||||
def get_kia_ev6_center_output_scale(desired_lateral_accel: float, v_ego: float) -> float:
|
||||
speed_weight = _kia_ev6_sigmoid((v_ego - KIA_EV6_CENTER_OUTPUT_TAPER_SPEED) /
|
||||
KIA_EV6_CENTER_OUTPUT_TAPER_SPEED_WIDTH)
|
||||
center_weight = _kia_ev6_sigmoid((KIA_EV6_CENTER_OUTPUT_TAPER_LAT - abs(desired_lateral_accel)) /
|
||||
KIA_EV6_CENTER_OUTPUT_TAPER_LAT_WIDTH)
|
||||
reduction = _flm_vehicle_knob("hyundai_kia_ev6.center_output_taper_max", KIA_EV6_CENTER_OUTPUT_TAPER_MAX) * speed_weight * center_weight
|
||||
future_accel = abs(desired_lateral_accel) if future_lateral_accel is None else max(abs(desired_lateral_accel), abs(future_lateral_accel))
|
||||
future_center_weight = _kia_ev6_sigmoid((KIA_EV6_CENTER_OUTPUT_TAPER_LAT - future_accel) /
|
||||
KIA_EV6_CENTER_OUTPUT_TAPER_LAT_WIDTH)
|
||||
reduction += KIA_EV6_HIGHWAY_CENTER_DAMPING * speed_weight * future_center_weight
|
||||
return 1.0 - reduction
|
||||
|
||||
|
||||
|
||||
@@ -30,7 +30,6 @@ MAX_LANE_WIDTH = 4.8
|
||||
MAX_OFFSET = 0.3
|
||||
MIN_CENTER_TO_LINE = 1.1
|
||||
MAX_CORRECTION = 0.004 * 0.30
|
||||
MAX_CORRECTION_JERK = 1.0
|
||||
CENTER_ERROR_DEADBAND = 0.08
|
||||
SINGLE_LINE_DEADBAND_MIN = 0.04
|
||||
E2E_MAX_PATH_STD = 0.35
|
||||
@@ -297,17 +296,6 @@ class LaneCenteringController:
|
||||
self._correction = 0.0
|
||||
return self._result(base, reason)
|
||||
|
||||
def _tracking_correction(self, base: float, correction: float, measured: float, speed: float, delay: float) -> float:
|
||||
if self._tracker.path is None:
|
||||
return correction
|
||||
x, y = self._tracker.path
|
||||
source = self._clearance_observation
|
||||
offset = source.settings.offset_m if source is not None else 0.
|
||||
if self._tracker.side is None and self._tracker.width is not None:
|
||||
bound = min(MAX_OFFSET, max(0., self._tracker.width * .5 - MIN_CENTER_TO_LINE))
|
||||
offset = clip_scalar(offset, -bound, bound)
|
||||
return limit_tracking((x, y + offset), base, correction, measured, speed, delay)
|
||||
|
||||
def guard_applied(self, model, base: float, candidate: float, measured: float, speed: float, delay) -> float:
|
||||
if self._body_required and (self._body is None or not self._body.valid):
|
||||
self.reset()
|
||||
@@ -318,7 +306,14 @@ class LaneCenteringController:
|
||||
delta = candidate - base
|
||||
allowed = 0. if checked_delay is None else delta
|
||||
if checked_delay is not None:
|
||||
allowed = self._tracking_correction(base, allowed, measured, speed, checked_delay)
|
||||
if self._tracker.path is not None:
|
||||
x, y = self._tracker.path
|
||||
source = self._clearance_observation
|
||||
offset = source.settings.offset_m if source is not None else 0.
|
||||
if self._tracker.side is None and self._tracker.width is not None:
|
||||
bound = min(MAX_OFFSET, max(0., self._tracker.width * .5 - MIN_CENTER_TO_LINE))
|
||||
offset = clip_scalar(offset, -bound, bound)
|
||||
allowed = limit_tracking((x, y + offset), base, allowed, measured, speed, checked_delay)
|
||||
if self._body is not None:
|
||||
allowed = limit_correction(model, base, allowed, measured, speed, checked_delay, self._body)
|
||||
if allowed != delta:
|
||||
@@ -391,12 +386,6 @@ class LaneCenteringController:
|
||||
target = clip_scalar(raw, -0.004, 0.004) * 0.30
|
||||
if strength != 1.0:
|
||||
target = clip_scalar(target * strength, -MAX_CORRECTION, MAX_CORRECTION)
|
||||
measured = _finite_number(observation.measured_curvature)
|
||||
delay = _finite_number(observation.lateral_delay_s)
|
||||
if measured is not None and delay is not None:
|
||||
target = self._tracking_correction(base, target, measured, speed, delay)
|
||||
step = (target - self._correction) * (1.0 - math.exp(-dt / ACQUIRE_TAU))
|
||||
max_step = MAX_CORRECTION_JERK * dt / speed / speed
|
||||
self._correction += clip_scalar(step, -max_step, max_step)
|
||||
self._correction += (target - self._correction) * (1.0 - math.exp(-dt / ACQUIRE_TAU))
|
||||
self._correction = clip_scalar(self._correction, -MAX_CORRECTION, MAX_CORRECTION)
|
||||
return self._result(base, "qualified")
|
||||
|
||||
@@ -136,61 +136,3 @@ def test_kona_2022_friction_does_not_change_sibling_policies(monkeypatch, vehicl
|
||||
controller = LatControlTorque(cp.as_reader(), interfaces[vehicle](cp), .01, controller_mode=ControllerMode.STARPILOT)
|
||||
threshold, scale = controller.starpilot_extension.policy.friction_context(.1, 0., 0., 120. / 3.6)
|
||||
assert threshold > 0. and scale > 0.
|
||||
|
||||
|
||||
@pytest.mark.parametrize('alpha', (False, True))
|
||||
@pytest.mark.parametrize('direction', (-1., 1.))
|
||||
def test_ev6_highway_center_damping_preserves_controller_and_curve_response(monkeypatch, alpha, direction):
|
||||
from types import SimpleNamespace
|
||||
import math
|
||||
from openpilot.cereal import log
|
||||
from opendbc.car.vehicle_model import VehicleModel
|
||||
from openpilot.starpilot.lateral import hkg_vehicle_shaping as shaping
|
||||
|
||||
cp = interfaces[CAR.KIA_EV6].get_non_essential_params(CAR.KIA_EV6)
|
||||
cp.openpilotLongitudinalControl, cp.pcmCruise = alpha, not alpha
|
||||
cs = SimpleNamespace(vEgo=26., steeringAngleDeg=0., steeringPressed=False)
|
||||
params = log.VehicleParameters.new_message(angleOffsetDeg=0., roll=0.)
|
||||
selected = shaping.KIA_EV6_HIGHWAY_CENTER_DAMPING
|
||||
assert selected == .08
|
||||
|
||||
def run(taper, turn, mode=ControllerMode.STARPILOT):
|
||||
monkeypatch.setattr(shaping, 'KIA_EV6_HIGHWAY_CENTER_DAMPING', taper)
|
||||
controller = LatControlTorque(cp.as_reader(), interfaces[cp.carFingerprint](cp), .01, controller_mode=mode)
|
||||
vm = VehicleModel(cp)
|
||||
outputs, stages = [], []
|
||||
for tick in range(300):
|
||||
acceleration = direction * (1.5 if turn else .12 * math.sin(2 * math.pi * .4 * tick * .01))
|
||||
value, _, state = controller.update(True, cs, vm, params, False, acceleration / cs.vEgo**2, False, .386)
|
||||
assert math.isfinite(value) and abs(value) <= controller.steer_max + 1e-6
|
||||
outputs.append(value)
|
||||
stages.append((state.p, state.i, state.d, state.f))
|
||||
off, _, state = controller.update(False, cs, vm, params, False, 0., False, .386)
|
||||
assert off == 0. and not state.active
|
||||
return outputs, stages, controller.torque_params.to_dict(), (controller.pid.pos_limit, controller.pid.neg_limit, controller.steer_max)
|
||||
|
||||
actual, baseline = run(selected, False), run(0., False)
|
||||
assert actual[1:] == baseline[1:]
|
||||
assert sum(x*x for x in actual[0]) < .88 * sum(x*x for x in baseline[0])
|
||||
assert all(a*b >= -1e-12 for a, b in zip(actual[0], baseline[0], strict=True))
|
||||
actual_turn, baseline_turn = run(selected, True), run(0., True)
|
||||
assert actual_turn[1:] == baseline_turn[1:]
|
||||
assert actual_turn[0] == pytest.approx(baseline_turn[0], rel=1e-6, abs=1e-8)
|
||||
assert run(selected, False, ControllerMode.STANDARD) == run(0., False, ControllerMode.STANDARD)
|
||||
|
||||
|
||||
def test_ev6_taper_keeps_low_speed_and_substantial_curve_effect_small():
|
||||
from openpilot.starpilot.lateral import hkg_vehicle_shaping as shaping
|
||||
for speed in (0., 3., 26., 36.):
|
||||
for acceleration in (-2., -1., 0., 1., 2.):
|
||||
actual = shaping.get_kia_ev6_center_output_scale(acceleration, speed)
|
||||
speed_weight = shaping._kia_ev6_sigmoid((speed - shaping.KIA_EV6_CENTER_OUTPUT_TAPER_SPEED) /
|
||||
shaping.KIA_EV6_CENTER_OUTPUT_TAPER_SPEED_WIDTH)
|
||||
center_weight = shaping._kia_ev6_sigmoid((shaping.KIA_EV6_CENTER_OUTPUT_TAPER_LAT - abs(acceleration)) /
|
||||
shaping.KIA_EV6_CENTER_OUTPUT_TAPER_LAT_WIDTH)
|
||||
old = 1. - .14 * speed_weight * center_weight
|
||||
assert .78 <= actual <= old <= 1.
|
||||
if speed <= 3.:
|
||||
assert old - actual < .002
|
||||
if abs(acceleration) >= 1.:
|
||||
assert old - actual < .00002
|
||||
|
||||
@@ -483,70 +483,11 @@ class TestSingleLineTracking(unittest.TestCase):
|
||||
controller = LaneCenteringController()
|
||||
source = observation(m, base_curvature=0., measured_curvature=0., lateral_delay_s=.1)
|
||||
result = controller.update(source)
|
||||
self.assertEqual(result.correction, 0.)
|
||||
self.assertGreater(-direction * result.correction, 0.)
|
||||
assert result.candidate_curvature is not None
|
||||
self.assertEqual(controller.guard_applied(m, 0., result.candidate_curvature, 0., 20., .1), 0.)
|
||||
self.assertEqual(controller._correction, 0.)
|
||||
|
||||
def test_centered_planned_curves_do_not_build_an_opposing_nudge(self):
|
||||
from openpilot.starpilot.lateral.lane_clearance import _center_motion
|
||||
for curvature in (-.006, -.002, .002, .006):
|
||||
x, y, _ = _center_motion(curvature, curvature, 25., .1)
|
||||
x, y = np.r_[0., x, 50.], np.r_[0., y, y[-1]]
|
||||
m = model()
|
||||
for index, shift in ((1, -1.8), (2, 1.8)):
|
||||
m.laneLines[index].x, m.laneLines[index].y = x.tolist(), (y + shift).tolist()
|
||||
m.position.x, m.position.y = x.tolist(), (y - math.copysign(.6, curvature)).tolist()
|
||||
m.position.yStd = [.1] * len(x)
|
||||
controller = LaneCenteringController()
|
||||
source = observation(m, base_curvature=curvature, measured_curvature=curvature,
|
||||
speed_mps=25., lateral_delay_s=.1)
|
||||
self.assertAlmostEqual(controller.update(source).correction, 0., delta=1e-8)
|
||||
|
||||
def test_curve_spacing_limit_shapes_the_target_before_it_ramps(self):
|
||||
from openpilot.starpilot.lateral.lane_clearance import _center_motion, limit_tracking
|
||||
from openpilot.starpilot.lateral.lane_centering import ACQUIRE_TAU
|
||||
for sign in (-1., 1.):
|
||||
base, delta, speed = sign * .004, sign * .00004, 25.
|
||||
x, y, _ = _center_motion(base + delta, base, speed, .1)
|
||||
x, y = np.r_[0., x, 50.], np.r_[0., y, y[-1]]
|
||||
m = model()
|
||||
for index, shift in ((1, -1.8), (2, 1.8)):
|
||||
m.laneLines[index].x, m.laneLines[index].y = x.tolist(), (y + shift).tolist()
|
||||
m.position.x, m.position.y = x.tolist(), (y - sign * .7).tolist()
|
||||
m.position.yStd = [.1] * len(x)
|
||||
source = observation(m, base_curvature=base, measured_curvature=base, speed_mps=speed, lateral_delay_s=.1)
|
||||
controller = LaneCenteringController()
|
||||
first = controller.update(source)
|
||||
self.assertGreater(sign * first.correction, 0.)
|
||||
self.assertLess(abs(first.correction), abs(delta) * .04)
|
||||
raw = raw_correction(m, speed, 0., 0.)
|
||||
assert raw is not None
|
||||
path = controller._tracker.path
|
||||
assert path is not None
|
||||
target = limit_tracking(path, base, raw * .3 * 1.5, base, speed, .1)
|
||||
self.assertAlmostEqual(first.correction, target * (1. - math.exp(-.01 / ACQUIRE_TAU)), delta=1e-12)
|
||||
assert first.candidate_curvature is not None
|
||||
applied = controller.guard_applied(m, base, first.candidate_curvature, base, speed, .1) - base
|
||||
self.assertAlmostEqual(applied, first.correction, delta=1e-12)
|
||||
|
||||
def test_nudge_build_and_reversal_respect_lateral_acceleration_rate(self):
|
||||
for speed in (5., 20., 35.):
|
||||
controller = LaneCenteringController()
|
||||
previous = 0.
|
||||
for tick in range(160):
|
||||
sign = 1. if tick < 80 else -1.
|
||||
dt = (.01, .02, .05)[tick % 3]
|
||||
source = observation(model(left=-1.8 + sign * 1.4, right=1.8 + sign * 1.4),
|
||||
speed_mps=speed, elapsed_seconds=dt, model_sample_id=tick,
|
||||
settings=LaneCenteringSettings(True, e2e_authority=0., strength=3.))
|
||||
result = controller.update(source)
|
||||
self.assertLessEqual(abs(result.correction - previous) * speed * speed / dt, 1. + 1e-12)
|
||||
previous = result.correction
|
||||
self.assertLess(previous, 0.)
|
||||
self.assertEqual(controller.update(replace(source, driver_override=True)).correction, 0.)
|
||||
self.assertEqual(controller.update(replace(source, lateral_active=False)).correction, 0.)
|
||||
|
||||
def test_two_line_tracking_interpolates_shared_coordinates_and_bounds_offset(self):
|
||||
m = model(left=-1.31, right=1.31)
|
||||
m.laneLines[2].x = [float(x) + .25 for x in XS]
|
||||
|
||||
@@ -162,8 +162,6 @@ class LaneRuntimeTests(unittest.TestCase):
|
||||
feed(controls, 1_000_000_000 + tick * 10_000_000, tick)
|
||||
controls.state_control()
|
||||
self.assertIsNotNone(controls.last_lane_centering_result)
|
||||
assert controls.last_lane_centering_result is not None
|
||||
self.assertEqual(controls.last_lane_centering_result.correction, 0.)
|
||||
self.assertEqual(controls.lane_centering_applied, 0.)
|
||||
self.assertAlmostEqual(controls.desired_curvature, .0002)
|
||||
|
||||
|
||||
@@ -12,15 +12,14 @@ Policy order matters. Target shaping precedes PID preparation; vehicle output sh
|
||||
|
||||
`LongitudinalPlanner.update(sm, *, cruise_ceiling=None, profile_tuning=None)` accepts an optional
|
||||
`CruiseCeiling(speed_mps, authority)` in addition to the existing driver cruise
|
||||
message. The live caller supplies no ceiling. The planner still reads
|
||||
`carState.vCruise` for the driver's selected ceiling and leaves every supplied
|
||||
Cap'n Proto message unchanged. [SLC](../speed_limits/README.md) adopts accepted
|
||||
limits into Card's ordinary cruise SET; it supplies no additional planner ceiling.
|
||||
The optional input remains available to explicit callers and replay tests.
|
||||
message. The live [SLC](../speed_limits/README.md) caller supplies a qualified cap.
|
||||
The planner still reads `carState.vCruise` as the driver's selected ceiling and
|
||||
leaves supplied messages unchanged. Accepted limits affect the planner cap, not SET.
|
||||
|
||||
The `CruiseCeiling` dataclass validates no source or acceptance history by
|
||||
construction. Its caller must first establish a fresh, accepted, normalized
|
||||
m/s ceiling in the correct speed coordinate. The planner validates its numeric
|
||||
construction. Its caller must establish an accepted, normalized m/s ceiling in
|
||||
the correct speed coordinate, including the fixed previous-accepted-limit fallback.
|
||||
Vehicle coordinates and authority remain fresh. The planner validates its numeric
|
||||
shape and the shared four-mode `Authority` contract. It applies the lower of
|
||||
the supplied ceiling and the existing driver ceiling only when native system
|
||||
longitudinal control is available, the supplied authority grants active system
|
||||
@@ -33,7 +32,7 @@ publish that diagnostic or alter the longitudinal plan schema.
|
||||
|
||||
This optional input changes only the cruise candidate before the planner's
|
||||
existing jerk limiting. SLC uses the ordinary cruise braking response and selected
|
||||
profile after its SET adoption. Lead-relevance checks still prevent cruise comfort
|
||||
profile for its capped target; a cap does not strengthen a softer cruise profile. Lead-relevance checks still prevent cruise comfort
|
||||
tuning from weakening braking for a close, slow or braking lead; malformed lead
|
||||
evidence also vetoes that tuning. The native MPC, lead, e2e, stop and acceleration
|
||||
arbitration remain in place. An inactive planner may still compute a nonzero
|
||||
@@ -111,7 +110,7 @@ different historical scales:
|
||||
| Sport (`sport`) | 2.4 m/s² | 2.0 m/s² |
|
||||
|
||||
These are positive deceleration magnitudes for ordinary cruise braking,
|
||||
including an SLC change to cruise SET. Global values preserve the historical
|
||||
including a changed SLC cap. Global values preserve the historical
|
||||
1.2 m/s² baseline with half/normal/double responses; explicit personality
|
||||
presets retain their original physical values. The distinction preserves
|
||||
existing tuning and saved-document compatibility.
|
||||
|
||||
@@ -7,6 +7,8 @@ from openpilot.common.realtime import DT_MDL
|
||||
|
||||
STANDSTILL_HOLD_NS = 2_000_000_000
|
||||
STANDSTILL_CLEAR_NS = 750_000_000
|
||||
STOP_COMMIT_TIME_S = .5
|
||||
SIGN_CONFIRM_STEP_S = 2 * DT_MDL
|
||||
SIGN_MAX_SPEED_MPS = 17.88
|
||||
SIGN_MODEL_AGREE_M = 50.0
|
||||
SIGN_SEED_M = 27.0
|
||||
@@ -102,6 +104,10 @@ class ForceStop:
|
||||
self.standstill_clear_ns = 0
|
||||
self.standstill_reason = None
|
||||
|
||||
@property
|
||||
def can_commit_next_frame(self) -> bool:
|
||||
return self.timer + SIGN_CONFIRM_STEP_S >= STOP_COMMIT_TIME_S
|
||||
|
||||
def step(self, frame: StopFrame, tune: StopTune, offset_ft: int = 0) -> StopPlan:
|
||||
now = frame.model_ns
|
||||
if (type(now) is not int or now <= 0 or now <= self.last_model_ns or
|
||||
@@ -177,16 +183,16 @@ class ForceStop:
|
||||
if not available and not self.forcing:
|
||||
self.timer = 0
|
||||
elif active and not frame.standstill:
|
||||
self.timer = min(2.0, self.timer + DT_MDL * (2 if frame.sign else 1))
|
||||
self.timer = min(2.0, self.timer + (SIGN_CONFIRM_STEP_S if frame.sign else DT_MDL))
|
||||
elif turn and not self.forcing and not frame.standstill:
|
||||
self.timer = 0
|
||||
elif held:
|
||||
self.timer = max(self.timer, .5)
|
||||
self.timer = max(self.timer, STOP_COMMIT_TIME_S)
|
||||
elif self.forcing and frame.standstill and not frame.sign and not frame.light and not frame.raw_model_stopped:
|
||||
self.timer = 0
|
||||
else:
|
||||
self.timer = max(0, self.timer - DT_MDL * .25)
|
||||
force = self.timer >= .5 or self.forcing and not frame.standstill or held
|
||||
force = self.timer >= STOP_COMMIT_TIME_S or self.forcing and not frame.standstill or held
|
||||
light_cleared = (not held and self.forcing and self.from_light and not frame.standstill and
|
||||
available and not frame.light and not frame.sign and go_scene)
|
||||
low_speed_hold = (light_cleared and tune.low_speed_hold_mps is not None and self.entry_speed is not None and
|
||||
|
||||
@@ -7,11 +7,12 @@ from opendbc.car.honda.interface import CarInterface
|
||||
from opendbc.car.honda.values import CAR
|
||||
from openpilot.common.params import Params
|
||||
from openpilot.selfdrive.controls.lib.longitudinal_planner import LongitudinalPlanner
|
||||
from openpilot.selfdrive.controls.plannerd import update_curve_frame
|
||||
from openpilot.selfdrive.controls.plannerd import update_curve_frame, wheel_override_blocked
|
||||
from openpilot.starpilot.curve_speed.host import CurveHost
|
||||
from openpilot.starpilot.longitudinal.force_stop import StopPlan
|
||||
from openpilot.starpilot.longitudinal.force_stop import StopPlan, vehicle_tune
|
||||
from openpilot.starpilot.longitudinal.force_stop_runtime import ForceStopRuntime, dashboard_stop_sign
|
||||
from openpilot.starpilot.longitudinal.tests.test_cruise_ceiling import messages, snapshot
|
||||
from openpilot.starpilot.longitudinal.tests.test_force_stop import frame as stop_frame
|
||||
from openpilot.starpilot.longitudinal.tests.test_stop_resume import event as button_event
|
||||
|
||||
BASE = 100_000_000_000
|
||||
@@ -61,6 +62,31 @@ class ForceStopRuntimeTests(unittest.TestCase):
|
||||
self.assertTrue(output.forcing)
|
||||
return output
|
||||
|
||||
def test_wheel_priority_requires_a_stop_cap_or_possible_commit_next_frame(self):
|
||||
tune = vehicle_tune(str(self.cp.carFingerprint))
|
||||
sign = {'light': False, 'sign': True, 'horizon_m': 40.}
|
||||
self.owner.plan = self.owner.policy.step(stop_frame(0, **sign), tune)
|
||||
self.assertIsNone(self.owner.plan.speed_ceiling_mps)
|
||||
self.assertFalse(wheel_override_blocked(self.owner, None))
|
||||
self.owner.plan = self.owner.policy.step(stop_frame(1, light=False, resume_requested=True), tune)
|
||||
self.assertGreater(self.owner.policy.timer, 0.)
|
||||
self.assertEqual(self.owner.policy.override_until_ns, 0)
|
||||
self.assertFalse(wheel_override_blocked(self.owner, None))
|
||||
|
||||
self.owner.reset()
|
||||
for tick in range(4):
|
||||
self.owner.plan = self.owner.policy.step(stop_frame(tick, **sign), tune)
|
||||
self.assertIsNone(self.owner.plan.speed_ceiling_mps)
|
||||
self.assertTrue(wheel_override_blocked(self.owner, None))
|
||||
self.owner.policy.step(stop_frame(4, resume_requested=True, **sign), tune)
|
||||
self.assertGreater(self.owner.policy.override_until_ns, 0)
|
||||
|
||||
self.owner.reset()
|
||||
self.owner.plan = self.owner.policy.step(stop_frame(0), tune)
|
||||
self.assertFalse(self.owner.policy.can_commit_next_frame)
|
||||
self.assertIsNotNone(self.owner.plan.speed_ceiling_mps)
|
||||
self.assertTrue(wheel_override_blocked(self.owner, None))
|
||||
|
||||
def test_committed_stop_survives_valid_mode_settings_and_traffic_changes(self):
|
||||
import json
|
||||
from openpilot.starpilot.conditional_mode.policy import ModeChoice
|
||||
|
||||
@@ -245,6 +245,48 @@ class TestVoltCc(unittest.TestCase):
|
||||
self.assertFalse(any(msg[0] in (0x2cb, 0x315, 0x200, 0x3d1) for msg in sent[-1][0]))
|
||||
|
||||
|
||||
def test_slc_cap_reaches_cruise_down_without_changing_driver_set(self):
|
||||
for metric in (False, True):
|
||||
with self.subTest(metric=metric):
|
||||
controls, card, ci, packer, sent, base, offset = fixture()
|
||||
card.is_metric, card.slc_control_enabled, card.slc_source_floor_ns = metric, True, 0
|
||||
state = messaging.new_message('slcState').slcState
|
||||
state.enabled, state.sessionId, state.hasCeiling, state.effectiveCap = True, 'drive', True, 45 * .44704
|
||||
controls.sm.data['slcState'] = state
|
||||
for flags in (controls.sm.seen, controls.sm.valid, controls.sm.alive):
|
||||
flags['slcState'] = True
|
||||
flags['carControl'] = True
|
||||
command = structs.CarControl(enabled=True, latActive=True, longActive=True)
|
||||
command.actuators.accel = -1.
|
||||
command.actuators.longControlState = structs.CarControl.Actuators.LongControlState.pid
|
||||
command.hudControl.setSpeed = 70 * .44704
|
||||
controls.sm.data['carControl'] = command
|
||||
controls.sm.logMonoTime['carControl'] = base
|
||||
by_phase = []
|
||||
for phase in ('active', 'stale'):
|
||||
directions = []
|
||||
for tick in range(100):
|
||||
now = base + (len(by_phase) * 100 + tick) * 10_000_000
|
||||
out = ci.update([(now + offset - 2_000_000,
|
||||
physical_frames(packer, speed=55 * .44704, target_speed=70 * .44704, counter=(tick // 3) % 4))])
|
||||
self.assertTrue(out.canValid)
|
||||
out.vCruise = out.vCruiseCluster = 70 * .44704 * 3.6
|
||||
for name in controls.sm.logMonoTime:
|
||||
controls.sm.logMonoTime[name] = now - 2_000_000
|
||||
controls.sm.recv_time[name] = (now - 1_000_000) / 1e9
|
||||
controls.sm.logMonoTime['slcState'] = state.frameMonoTime = now - (2_000_000 if phase == 'active' else 200_000_000)
|
||||
with patch('openpilot.selfdrive.car.card.clock_pair_ns', return_value=(now, now + offset)), \
|
||||
patch('openpilot.selfdrive.car.card.time.monotonic_ns', return_value=now), \
|
||||
patch('openpilot.selfdrive.car.card.REPLAY', False):
|
||||
card.controls_update(out, command.as_reader())
|
||||
directions.extend((data[5] >> 4) & 7 for address, data, _ in sent[-1][0] if address == 0x1e1)
|
||||
self.assertAlmostEqual(out.vCruise, 70 * .44704 * 3.6, places=4)
|
||||
self.assertAlmostEqual(command.hudControl.setSpeed, 70 * .44704, places=4)
|
||||
by_phase.append(directions)
|
||||
self.assertIn(3, by_phase[0])
|
||||
self.assertEqual(by_phase[1], [])
|
||||
self.assertIsNone(ci.CC.volt_cc_speed_ceiling_mps)
|
||||
|
||||
def test_actual_card_constructor_subscribes_selected_consumer(self):
|
||||
class Registered(Exception):
|
||||
pass
|
||||
|
||||
@@ -86,8 +86,9 @@ class TestPlannerSelection(unittest.TestCase):
|
||||
imports = [node.module for node in planner.body if isinstance(node, ast.ImportFrom)]
|
||||
self.assertIn('openpilot.starpilot.longitudinal.upstream.mpc', imports)
|
||||
self.assertNotIn('openpilot.selfdrive.controls.lib.longitudinal_mpc_lib.long_mpc', imports)
|
||||
self.assertFalse(any(value and value.startswith('openpilot.starpilot.') and value !=
|
||||
'openpilot.starpilot.longitudinal.upstream.mpc' for value in imports))
|
||||
self.assertEqual({value for value in imports if value and value.startswith('openpilot.starpilot.')}, {
|
||||
'openpilot.starpilot.longitudinal.upstream.mpc', 'openpilot.starpilot.longitudinal.cruise_ceiling',
|
||||
})
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
@@ -60,7 +60,7 @@ class TrafficAccelerationPlannerTests(unittest.TestCase):
|
||||
self.assertLessEqual(traffic.output_a_target, 0.44 + 1e-8)
|
||||
self.assertLess(traffic.a_cruise, baseline.a_cruise)
|
||||
|
||||
def test_soft_saved_traffic_keeps_slc_and_force_decel_braking(self):
|
||||
def test_slc_cap_preserves_soft_traffic_response_and_native_force_decel(self):
|
||||
target = self.tuning(acceleration=0.6, braking=0.35)
|
||||
self.assertAlmostEqual(target.acceleration_max, 0.6)
|
||||
self.assertAlmostEqual(target.cruise_brake_magnitude, 0.35)
|
||||
@@ -72,14 +72,15 @@ class TrafficAccelerationPlannerTests(unittest.TestCase):
|
||||
for _ in range(60):
|
||||
sm, _ = messages(force=force)
|
||||
sm['carControl'].longActive = True
|
||||
baseline.update(sm, cruise_ceiling=ceiling)
|
||||
traffic.update(sm, cruise_ceiling=ceiling, profile_tuning=target, traffic_mode=True)
|
||||
sm['carState'].vCruise = 15.0 * 3.6
|
||||
baseline.update(sm, profile_tuning=target, traffic_mode=True)
|
||||
self.assertEqual(traffic.mpc.solution_status, 0)
|
||||
self.assertLessEqual(traffic.a_cruise, baseline.a_cruise + 1e-8)
|
||||
self.assertEqual(traffic.last_cruise_ceiling_status, 'force_decel' if force else 'applied')
|
||||
self.assertIsNotNone(traffic.last_profile)
|
||||
self.assertAlmostEqual(traffic.last_profile.cruise_brake_magnitude, 0.35)
|
||||
self.assertLessEqual(traffic.a_cruise, -0.5 + 1e-8)
|
||||
self.assertLessEqual(traffic.a_cruise, (-0.5 if force else -0.35) + 1e-8)
|
||||
|
||||
def test_soft_saved_traffic_preserves_lead_and_model_stop_candidates(self):
|
||||
target = self.tuning(acceleration=0.6, braking=0.5)
|
||||
@@ -215,8 +216,10 @@ class TrafficAccelerationPlannerTests(unittest.TestCase):
|
||||
cruise_ceiling=cruise_ceiling, curve_ceiling=curve_ceiling)
|
||||
self.assertEqual(planner.mpc.solution_status, 0)
|
||||
self.assertAlmostEqual(planner.output_a_target, baseline.output_a_target, places=5)
|
||||
if case in ('slc', 'curve'):
|
||||
if case == 'curve':
|
||||
self.assertLessEqual(planner.a_cruise, -0.5 + 1e-8)
|
||||
elif case == 'slc':
|
||||
self.assertLessEqual(planner.a_cruise, -0.42 + 1e-8)
|
||||
elif case == 'force_decel':
|
||||
self.assertLessEqual(planner.a_cruise, -0.5 + 1e-8)
|
||||
elif case == 'model_stop':
|
||||
|
||||
@@ -23,7 +23,7 @@ class EndLoop(Exception):
|
||||
|
||||
|
||||
class TestUpstreamConditional(unittest.TestCase):
|
||||
def run_loop(self, *, safe=False, stale=False, clocks=True, replay=False):
|
||||
def run_loop(self, *, safe=False, stale=False, clocks=True, replay=False, show_speed_limits=False):
|
||||
directory = self.enterContext(tempfile.TemporaryDirectory())
|
||||
params = Params(directory)
|
||||
cp = CarInterface.get_non_essential_params(CAR.HONDA_CIVIC_BOSCH)
|
||||
@@ -32,6 +32,9 @@ class TestUpstreamConditional(unittest.TestCase):
|
||||
params.put_bool('UseStarPilotLongitudinalPlanner', False, block=True)
|
||||
params.put_bool('SafeMode', safe, block=True)
|
||||
params.put_bool('ExperimentalMode', False, block=True)
|
||||
params.put_bool('ShowSpeedLimits', show_speed_limits, block=True)
|
||||
if show_speed_limits:
|
||||
params.put('SLCPriority1', 'Dashboard', block=True)
|
||||
params.put('ConditionalModeConfig', json.loads(encode_preferences(SavedPreferences(
|
||||
mode=ModeChoice.CEM, cem=CEMOptions(speed_mps=20., stop_lights=False, lead=False, slower_lead=False)))), block=True)
|
||||
settings = ConditionalSettingsOwner(params)
|
||||
@@ -82,6 +85,15 @@ class TestUpstreamConditional(unittest.TestCase):
|
||||
self.payloads[name] = getattr(messaging.log_from_bytes(event.to_bytes()), name)
|
||||
if stale:
|
||||
self.logMonoTime['modelV2'] -= 1_000_000_000
|
||||
if show_speed_limits:
|
||||
dashboard = messaging.new_message('slcDashboardObservation', valid=True).slcDashboardObservation
|
||||
dashboard.carStateLogMonoTime = dashboard.observedMonoTime = stamp
|
||||
dashboard.validUntilMonoTime = stamp + 100_000_000
|
||||
dashboard.status, dashboard.speedMps, dashboard.episode = 'valid', 15., 1
|
||||
dashboard.producerSessionId = 'card'
|
||||
self.payloads['slcDashboardObservation'] = dashboard
|
||||
self.logMonoTime['slcDashboardObservation'] = stamp
|
||||
self.valid['slcDashboardObservation'] = self.alive['slcDashboardObservation'] = True
|
||||
|
||||
sm = Scene()
|
||||
|
||||
@@ -133,10 +145,19 @@ class TestUpstreamConditional(unittest.TestCase):
|
||||
self.assertFalse(any(result.accepted for _, result in results))
|
||||
self.assertFalse(any(str(plan.longitudinalPlanSource) == 'e2e' for plan in plans))
|
||||
|
||||
def test_cem_and_speed_limit_share_one_status_per_model_frame(self):
|
||||
results, statuses, plans, subscriptions = self.run_loop(show_speed_limits=True)
|
||||
self.assertEqual(subscriptions.count('deviceState'), 1)
|
||||
self.assertEqual(len(statuses), len(plans))
|
||||
self.assertTrue(any(result.accepted and result.experimental for _, result in results))
|
||||
self.assertTrue(all(status.slcState.displayOnly and status.slcState.speedLimit == 15. and
|
||||
status.slcState.conditionalMode.version == 1 for status in statuses))
|
||||
|
||||
def test_safe_mode_retains_upstream_without_conditional_transport(self):
|
||||
results, statuses, plans, subscriptions = self.run_loop(safe=True)
|
||||
self.assertFalse(results or statuses)
|
||||
self.assertNotIn('deviceState', subscriptions)
|
||||
self.assertNotIn('starpilotRadarState', subscriptions)
|
||||
self.assertNotIn('starpilotNavigation', subscriptions)
|
||||
self.assertEqual(len(plans), 100)
|
||||
|
||||
def test_missing_live_clock_does_not_publish_conditional_proposals(self):
|
||||
|
||||
@@ -2,11 +2,15 @@
|
||||
from pathlib import Path
|
||||
from types import SimpleNamespace
|
||||
import unittest
|
||||
from unittest.mock import patch
|
||||
from unittest.mock import Mock, patch
|
||||
|
||||
import numpy as np
|
||||
from openpilot.cereal import log
|
||||
import pytest
|
||||
from openpilot.cereal import log, messaging
|
||||
from openpilot.common.params import Params
|
||||
from openpilot.selfdrive.controls import plannerd as starpilot_plannerd
|
||||
from openpilot.starpilot.longitudinal.upstream import mpc, planner, plannerd
|
||||
from openpilot.starpilot.speed_limits.tests import test_runtime_replay as replay
|
||||
|
||||
|
||||
class Solver:
|
||||
@@ -51,9 +55,9 @@ class TestUpstreamPlanner(unittest.TestCase):
|
||||
self.assertEqual(vars(cp), before)
|
||||
self.assertFalse(hasattr(stock, 'profile_tuning'))
|
||||
|
||||
def test_upstream_process_constructs_only_native_publishers_and_planner(self):
|
||||
def test_upstream_process_without_slc_transport_uses_only_native_services(self):
|
||||
calls = []
|
||||
cp = SimpleNamespace(brand='honda', openpilotLongitudinalControl=True)
|
||||
cp = SimpleNamespace(brand='honda', openpilotLongitudinalControl=True, passive=True)
|
||||
class SM:
|
||||
def update(self):
|
||||
raise InterruptedError('bounded end of mocked loop')
|
||||
@@ -61,7 +65,8 @@ class TestUpstreamPlanner(unittest.TestCase):
|
||||
calls.append(('subscribe', services, kwargs))
|
||||
return SM()
|
||||
fake_params = SimpleNamespace(get=lambda key, **kwargs: b'CP')
|
||||
with patch.object(plannerd, 'config_realtime_process'), patch.object(plannerd, 'Params', return_value=fake_params), \
|
||||
with patch.dict('os.environ', {}, clear=True), patch.object(plannerd, 'config_realtime_process'), \
|
||||
patch.object(plannerd, 'Params', return_value=fake_params), \
|
||||
patch.object(plannerd.messaging, 'log_from_bytes', return_value=cp), \
|
||||
patch.object(plannerd, 'LongitudinalPlanner', side_effect=lambda cp: calls.append(('planner', cp))), \
|
||||
patch.object(plannerd, 'LaneDepartureWarning', return_value=object()), \
|
||||
@@ -77,5 +82,87 @@ class TestUpstreamPlanner(unittest.TestCase):
|
||||
self.assertEqual(Path(plannerd.__file__).resolve(), (root / 'plannerd.py').resolve())
|
||||
|
||||
|
||||
@pytest.mark.parametrize('choice', ['wheel', 'screen', 'stock_cruise'])
|
||||
def test_upstream_planner_applies_confirmed_slc_with_op_long(tmp_path, monkeypatch, choice):
|
||||
fixture = replay.RuntimeReplayTests()
|
||||
fixture.setUp()
|
||||
cp, sm = fixture.cp, fixture.sm
|
||||
cp.openpilotLongitudinalControl = choice != 'stock_cruise'
|
||||
sm['slcDashboardObservation'].speedMps = 15.
|
||||
sm.data['deviceState'] = messaging.new_message('deviceState').deviceState
|
||||
sm.valid['deviceState'] = sm.alive['deviceState'] = False
|
||||
params = Params(str(tmp_path))
|
||||
params.put('CarParams', cp.to_bytes(), block=True)
|
||||
params.put_bool('UseStarPilotLongitudinalPlanner', False, block=True)
|
||||
params.put_bool('SpeedLimitController', True, block=True)
|
||||
params.put('SLCPriority1', 'Dashboard', block=True)
|
||||
params.put('SLCConfirmation', 1, block=True)
|
||||
|
||||
publisher = Mock()
|
||||
subscriber = Mock(return_value=sm)
|
||||
outputs = []
|
||||
events = {}
|
||||
start = 2_000_000_000
|
||||
|
||||
class EndLoop(Exception):
|
||||
pass
|
||||
|
||||
def update():
|
||||
sm.frame = len(outputs) + 1
|
||||
if sm.frame == 3:
|
||||
raise EndLoop
|
||||
sm.advance(start + (sm.frame - 1) * 50_000_000)
|
||||
if sm.frame != 2 or choice == 'stock_cruise':
|
||||
return
|
||||
shown = outputs[0].slcState
|
||||
assert shown.hasPending and not shown.hasCeiling
|
||||
service = 'slcCruiseEvent' if choice == 'wheel' else 'slcAction'
|
||||
event = messaging.new_message(service, valid=True)
|
||||
event.logMonoTime = sm.logMonoTime['modelV2']
|
||||
action = getattr(event, service)
|
||||
action.sessionId, action.decisionId, action.presentationId = shown.sessionId, shown.decisionId, shown.presentationId
|
||||
if choice == 'wheel':
|
||||
action.kind, action.button = 'confirmationAccept', 'accel'
|
||||
action.eventId, action.producerSessionId, action.observedMonoTime = 1, 'card-producer', event.logMonoTime
|
||||
else:
|
||||
action.kind, action.sequenceId = 'accept', 1
|
||||
events[service] = event
|
||||
|
||||
def send(service, message):
|
||||
if service == 'slcState':
|
||||
outputs.append(message)
|
||||
|
||||
sm.all_checks = lambda services=None: True
|
||||
monkeypatch.setattr(sm, 'update', update)
|
||||
monkeypatch.setenv('REPLAY', '1')
|
||||
monkeypatch.delenv('SLC_REPLAY_RUNTIME', raising=False)
|
||||
monkeypatch.setattr(starpilot_plannerd, 'Params', lambda: params)
|
||||
monkeypatch.setattr(plannerd, 'Params', lambda: params)
|
||||
monkeypatch.setattr(plannerd, 'config_realtime_process', Mock())
|
||||
monkeypatch.setattr(plannerd, 'LaneDepartureWarning', Mock(return_value=Mock(left=False, right=False)))
|
||||
monkeypatch.setattr(messaging, 'SubMaster', subscriber)
|
||||
monkeypatch.setattr(messaging, 'PubMaster', Mock(return_value=publisher))
|
||||
monkeypatch.setattr(messaging, 'sub_sock', lambda service, **kwargs: service)
|
||||
monkeypatch.setattr(messaging, 'recv_one_or_none', lambda service: events.pop(service, None))
|
||||
publisher.send.side_effect = send
|
||||
cruise = Mock(wraps=planner.get_cruise_accel)
|
||||
monkeypatch.setattr(planner, 'get_cruise_accel', cruise)
|
||||
|
||||
with pytest.raises(EndLoop):
|
||||
starpilot_plannerd.main()
|
||||
|
||||
assert 'slcDashboardObservation' in subscriber.call_args.kwargs['ignore_valid']
|
||||
assert cruise.call_args_list[0].args[1] == pytest.approx(100. / 3.6)
|
||||
assert sm['carState'].vCruise == 100.
|
||||
state = outputs[1].slcState
|
||||
if choice == 'stock_cruise':
|
||||
assert state.displayOnly and not state.hasPending and not state.hasCeiling
|
||||
assert cruise.call_args.args[1] == pytest.approx(100. / 3.6)
|
||||
else:
|
||||
assert state.hasCeiling and state.actionStatus == 'accept'
|
||||
assert cruise.call_args.args[1] == 15.
|
||||
assert params.get('SLCQualifiedHistory')['speed_mps'] == 15.
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
@@ -14,6 +14,7 @@ from openpilot.starpilot.longitudinal.upstream.mpc import T_IDXS as T_IDXS_MPC
|
||||
from openpilot.selfdrive.controls.lib.drive_helpers import CONTROL_N, get_accel_from_plan, should_stop
|
||||
from openpilot.selfdrive.car.cruise import V_CRUISE_MAX, V_CRUISE_UNSET
|
||||
from openpilot.common.swaglog import cloudlog
|
||||
from openpilot.starpilot.longitudinal.cruise_ceiling import select_cruise_ceiling
|
||||
|
||||
A_CRUISE_MAX_VALS = [1.6, 1.2, 0.8, 0.6]
|
||||
A_CRUISE_MAX_BP = [0., 10.0, 25., 40.]
|
||||
@@ -70,7 +71,7 @@ class LongitudinalPlanner:
|
||||
self.a_desired_trajectory = np.zeros(CONTROL_N)
|
||||
self.j_desired_trajectory = np.zeros(CONTROL_N)
|
||||
|
||||
def update(self, sm):
|
||||
def update(self, sm, *, cruise_ceiling=None):
|
||||
if len(sm['carControl'].orientationNED) == 3:
|
||||
accel_coast = get_coast_accel(sm['carControl'].orientationNED[1])
|
||||
else:
|
||||
@@ -79,11 +80,17 @@ class LongitudinalPlanner:
|
||||
v_ego = sm['carState'].vEgo
|
||||
v_cruise_kph = min(sm['carState'].vCruise, V_CRUISE_MAX)
|
||||
v_cruise = v_cruise_kph * CV.KPH_TO_MS
|
||||
long_control_off = sm['controlsState'].longControlState == LongCtrlState.off
|
||||
ceiling = select_cruise_ceiling(
|
||||
cruise_ceiling, driver_v_cruise_kph=sm['carState'].vCruise, driver_cruise_mps=v_cruise,
|
||||
system_long_available=self.CP.openpilotLongitudinalControl, long_control_active=not long_control_off,
|
||||
selfdrive_enabled=sm['selfdriveState'].enabled, force_decel=sm['controlsState'].forceDecel,
|
||||
)
|
||||
if ceiling.effective_mps is not None:
|
||||
v_cruise = ceiling.effective_mps
|
||||
if sm['controlsState'].forceDecel:
|
||||
v_cruise = 0.0
|
||||
|
||||
long_control_off = sm['controlsState'].longControlState == LongCtrlState.off
|
||||
|
||||
# Reset current state when not engaged, or user is controlling the speed
|
||||
reset_state = long_control_off if self.CP.openpilotLongitudinalControl else not sm['selfdriveState'].enabled
|
||||
# PCM cruise speed may be updated a few cycles later, check if initialized
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user