Compare commits

..

1564 Commits

Author SHA1 Message Date
firestar5683 665bbc8b35 Qualify split HKG angle EPS publications 2026-11-06 22:15:29 -06:00
firestar5683 287da30f08 Qualify split HKG angle EPS publications 2026-11-06 22:03:21 -06:00
firestar5683 24bebd4dde Qualify split HKG angle EPS publications 2026-11-06 21:43:16 -06:00
firestar5683 de42ba5a6a Qualify split HKG angle EPS publications 2026-11-06 21:38:23 -06:00
firestar5683 328cf79d00 Qualify split HKG angle EPS publications 2026-11-06 21:30:57 -06:00
firestar5683 49e6905975 Qualify split HKG angle EPS publications 2026-11-06 21:30:07 -06:00
firestar5683 d5d1b47c32 Qualify split HKG angle EPS publications 2026-11-06 21:18:55 -06:00
firestar5683 6392df973b Qualify split HKG angle EPS publications 2026-11-06 21:13:02 -06:00
firestar5683 ebf322777a Qualify split HKG angle EPS publications 2026-11-06 21:09:14 -06:00
firestar5683 88f139bd51 Qualify split HKG angle EPS publications 2026-11-06 21:04:32 -06:00
firestar5683 29ba47dc3c Qualify split HKG angle EPS publications 2026-11-06 20:59:18 -06:00
firestar5683 d776acdd97 Qualify split HKG angle EPS publications 2026-11-06 20:46:16 -06:00
firestar5683 68434cf39c Build matching Outback state-zero stop-start firmware 2026-11-06 19:55:36 -06:00
firestar5683 5d6e6afc3e Allow bounded Outback stop-start OFF requests from state zero 2026-11-06 19:48:56 -06:00
Dom 453aa6e0f7 Show C3 recording-drive capacity and available storage in Galaxy
Keep internal storage and the separately mounted recording drive distinct.
Gate added-drive telemetry and both views to C3, recheck filesystem identity,
and report available bytes without counting reserved space as free.

Cover absent drives, mount transitions, read failures, zero free space,
C3X/C4 exclusion, and actual Vue rendering. Refresh the inventory receipt
for JavaScript test-only protected changes; native artifacts are unchanged.
2026-11-06 19:33:47 -06:00
firestar5683 e959f52e25 Display bounded recorded steering graphs without rejecting the report 2026-11-06 19:29:42 +00:00
Dom af342611ef Merge pull request #281 from dirwin31/Starport-Log-Refusal-Bytes
Add Logging for Screen Failure Response
2026-11-06 13:20:23 -06:00
Danny c9dd2b7339 Add Logging for Screen Failure Response 2026-11-06 10:57:58 -08:00
firestar5683 be0f2cf7ba Use original metadata identities for independent route profiles 2026-11-06 18:56:19 +00:00
firestar5683 9fcecdec2a Restore installed commit date on C4 home screen 2026-11-06 12:31:03 -06:00
firestar5683 9af9f09071 Keep completed-drive windows distinct and retain measured results on failure 2026-11-06 17:39:43 +00:00
firestar5683 359cb9fda6 Add offroad alert volume previews to Galaxy 2026-11-06 11:26:19 -06:00
firestar5683 4f5cf21e9e Keep STOP vision and blind-spot inference on separate cores 2026-11-06 11:16:35 -06:00
firestar5683 dae68ce494 Explain FLM slider effects in Galaxy 2026-11-06 11:04:08 -06:00
firestar5683 52852af602 Preserve stock lateral intent during inhibited longitudinal recovery 2026-11-06 10:47:54 -06:00
firestar5683 7816b003b5 Refresh parked evidence before resuming route analysis 2026-11-06 16:16:53 +00:00
firestar5683 6e0baac46b Keep parked sessions from consuming untested steering tunes 2026-11-06 15:25:57 +00:00
Dom 9a55221ab5 Fix authenticated monitoring test fixtures and CI coverage 2026-11-06 08:38:57 -06:00
firestar5683 b4c3c5d656 Validate independent-drive Recommended profiles before automatic application 2026-11-06 14:34:42 +00:00
firestar5683 d64fb74c31 Preserve authenticated driver monitoring artifacts during builds 2026-11-06 07:46:10 -06:00
firestar5683 a2c96c47d6 Fix route-profile selection, replay verification and recommendation reporting 2026-11-06 12:45:38 +00:00
firestar5683 4e40bff081 Reject conflicting speed reads from expanded crops 2026-11-06 06:03:57 -06:00
firestar5683 97a5e15cc1 Validate offroad tune strength against an independent drive 2026-11-06 06:46:12 +00:00
firestar5683 b29a2ea4f8 Fit offroad tune strength and show completed-drive measurements 2026-11-06 05:19:36 +00:00
firestar5683 7f4e2ca589 Fix completed-drive aggregation and fresh parked result presentation 2026-11-06 03:44:15 +00:00
firestar5683 5d498ccdb9 Restore full-drive tuning recommendations and expose offroad decisions 2026-11-06 02:44:40 +00:00
Dom 7117b85680 Merge pull request #280 from zephleggett/jetlink-v0.9.0
Jetlink v0.9.0: API 4 and Jetlink's own model index
2026-11-05 20:41:37 -06:00
Dom 376d0f5df3 Qualify exact offroad candidates in cloud without temporary branches 2026-11-05 20:16:58 -06:00
firestar5683 3637b23b1c Avoid manual resume alerts for automatic Toyota launches 2026-11-05 20:10:51 -06:00
firestar5683 dc73f431a4 Preserve independent GM steering across speed and input transitions 2026-11-05 19:50:48 -06:00
firestar5683 3160182986 Use received car state for blindspot alert freshness 2026-11-05 19:44:40 -06:00
firestar5683 c0b6b82497 Restore Tesla AP1 Alpha Long cruise engagement 2026-11-05 19:40:53 -06:00
Zeph Leggett 37024b2f3d Jetlink: API 4, choices sized by Jetlink's model index
Jetlink v0.9.0 keeps its own index of comma's big models and the ONNX
each was built from, checked hourly. Active Big's choice, by the ONNX's
hash, is sized from it rather than from comma's LFS, so JetlinkPointers
is no longer read and is dropped.

JetlinkPointers leaves params_keys.h: the native params libraries need
their usual rebuild.
2026-11-05 19:37:27 -06:00
Zeph Leggett 76cb6f414c Jetlink v0.9.0 2026-11-05 19:37:27 -06:00
firestar5683 2b83873371 Add AGNOS 20.2.2 east mirror 2026-11-05 15:34:45 -06:00
firestar5683 e2a417ab1f AGNOS 20.2.2 2026-11-05 15:33:17 -06:00
firestar5683 83ecf50c3b Improve STOP recall while preserving speed detector features 2026-11-05 14:57:38 -06:00
firestar5683 790d9d2a5b Publish matching Dom precompiled artifacts 2026-11-05 14:51:20 -06:00
firestar5683 479da9e7d5 Restore automatic stopped launch for qualified Toyota SDSU profiles 2026-11-05 14:39:49 -06:00
Dom 63137021a1 Align schema and capability fixtures and StarPort types 2026-11-05 14:29:57 -06:00
firestar5683 8aa299daf8 Improve shared traffic-sign features while preserving speed reads 2026-11-05 14:27:57 -06:00
firestar5683 505495a936 Scope offroad checks to all learner and integrated controller source 2026-11-05 14:16:44 -06:00
firestar5683 fa0fd7b57e Add optional onroad weather widget 2026-11-05 14:12:15 -06:00
firestar5683 37df49d937 Check offroad learner contracts independently of unrelated pushes 2026-11-05 14:11:32 -06:00
firestar5683 a7b683d362 Preserve GM ACC startup and routing with blind-spot monitoring 2026-11-05 14:04:50 -06:00
firestar5683 1383968390 Default vehicle-specific tunes to the StarPilot controller 2026-11-05 13:58:13 -06:00
Dom 708dfa8ad5 Restore selfdrived executable mode 2026-11-05 13:50:03 -06:00
firestar5683 4b9e2ada7d Fix offroad learner report types and hosted static checks 2026-11-05 13:46:52 -06:00
prabhaavp 8b1e3066f4 Merge pull request #279 from dirwin31:MavlouPhoneIdentity
Star Port Dongle Compatibility
2026-11-05 14:42:56 -05:00
Prabhaav Pillai 55eae09ad4 Refactor handshake logic and improve comments for clarity 2026-11-05 14:41:46 -05:00
firestar5683 1301d6a566 Build Panda firmware for MQB brake-held engagement 2026-11-05 13:40:31 -06:00
firestar5683 9d3579cbbb Preserve stopped MQB brake-held engagement until brake release 2026-11-05 13:33:58 -06:00
firestar5683 34e23d945c Analyze completed drives and apply bounded next-drive steering tunes 2026-11-05 13:28:41 -06:00
firestar5683 52dcc22a1c Wrap MQB stop regression diagnostics 2026-11-05 13:23:04 -06:00
firestar5683 2e614b456b Pause MQB steering requests below the actuation speed limit 2026-11-05 13:22:11 -06:00
firestar5683 83e86fe022 Restore Subaru steering override reporting 2026-11-05 12:55:17 -06:00
firestar5683 ced757efe2 Distinguish Crosstrek wheel load from steering override 2026-11-05 12:38:13 -06:00
Danny b9d6539aa7 StarPort: Reset handshake forgets the remembered reply 2026-11-05 10:35:00 -08:00
dirwin31 64bc4e9ca2 StarPort: Setup ladder keeps what worked and skips rejected replies 2026-11-05 10:35:00 -08:00
Danny ad47e77a93 AAwireless: Account for Failed DHCP 2026-11-05 10:34:59 -08:00
Danny e96ab43d4c StarPort: Add a phone identity handshake step for unofficial dongles 2026-11-05 10:34:44 -08:00
prabhaavp 0cc9ddddfe Merge pull request #278 from dirwin31:Dom-UI-Maps-Fixes
UI/Maps: lane guidance, map polish. Hotspot and Wi-Fi fixes
2026-11-05 13:16:20 -05:00
Prabhaav Pillai aa631c8e9f backoff logic and improve widget art handling 2026-11-05 13:15:42 -05:00
firestar5683 5c9eeb707a Queue model selections for the next onroad cycle 2026-11-05 12:03:41 -06:00
firestar5683 faf81bb461 Separate stacked speed-limit panels before reading numbers 2026-11-05 11:53:09 -06:00
firestar5683 ac2b90145f Select Continental radar automatically on supported RAV4 profiles 2026-11-05 11:41:20 -06:00
Dom fb8d2c73c6 Align vision and Sierra CI fixtures with existing contracts 2026-11-05 11:05:18 -06:00
firestar5683 19943c7080 Enable vision STOP detection independently of SLC 2026-11-05 10:34:35 -06:00
firestar5683 90ee6b3686 Restore Sierra no-ACC conventional cruise layout 2026-11-05 10:03:48 -06:00
firestar5683 8d9bc972d3 Restore the default Carnival torque controller 2026-11-05 08:51:12 -06:00
firestar5683 d96d023746 Improve night stop detection and reject advisory speed signs 2026-11-05 00:51:32 -06:00
firestar5683 3b6abac814 Keep vision stop updates independent of speed-limit readings 2026-11-04 23:57:40 -06:00
firestar5683 18dc7949aa Register Ford ordering regression in complete CI inventory 2026-11-04 23:01:48 -06:00
firestar5683 6d5624f564 Order StarPilot Ford metadata before same-cycle steering 2026-11-04 22:45:29 -06:00
Dom c08b3249b6 Align Subaru engagement test with transmitted steering history 2026-11-04 19:15:52 -06:00
Dom cb707259be Refresh inventory receipt for schema qualification tests 2026-11-04 18:07:28 -06:00
Dom 49ed51a48f Qualify reviewed vision stop-sign schema addition 2026-11-04 17:53:54 -06:00
Dom e30016c008 Restore familiar torque arc colors and label 2026-11-04 17:25:53 -06:00
firestar5683 c8296b3bd8 Keep Turn Assist helping through braking turns 2026-11-04 17:17:54 -06:00
firestar5683 0571890b8e Integrate confirmed vision stop signs with Force Stop 2026-11-04 17:07:50 -06:00
firestar5683 064a3afb6d Correct steering demand display and add bounded Card timing 2026-11-04 16:49:57 -06:00
firestar5683 95bf946844 Keep Subaru VM steering handoff within native rate limits 2026-11-04 16:47:02 -06:00
Dom 761a47bc69 Provide pinned OpenCV DNN for unit vision contracts 2026-11-04 15:45:44 -06:00
firestar5683 3eb7f3fe29 Preserve Ford lateral intent through initial steering readiness 2026-11-04 15:33:07 -06:00
firestar5683 88566f1887 Add a STOP-capable joint traffic sign model 2026-11-04 14:40:28 -06:00
firestar5683 4b8ca5c379 Match StarPilot Ford demand to native pinion bounds 2026-11-04 14:20:17 -06:00
firestar5683 db1aaa67a2 Show supported years for the Elantra Hybrid refresh 2026-11-04 14:18:18 -06:00
firestar5683 a15b58fc2e Block unresolved Elantra CAN interrupt faults 2026-11-04 13:43:55 -06:00
Dom 3cb38bb9e0 Safely stage the audited legacy StarPilot return 2026-11-04 13:39:59 -06:00
firestar5683 a43e74d7f6 Preserve lateral on the first CANCEL health handoff 2026-11-04 11:28:20 -06:00
firestar5683 29bf269ccc Arm classic HKG lateral after accelerator override clears 2026-11-04 11:17:39 -06:00
Danny aab624393e Cleanup: Fix lint, trim comments, narrow error handling 2026-11-04 08:41:19 -08:00
firestar5683 73d805fafe Coordinate lane-centering nudges with planned turns 2026-11-04 10:26:26 -06:00
dirwin31 0cf1f4f3bb StarPort: rebuild aa0 after NetworkManager restarts, hold home Wi-Fi while projecting
- A NetworkManager restart left the dedicated lease on stale device paths,
  and the rest of the drive fell back to wlan0. A non-timeout aa0 failure
  now rebuilds aa0 once before falling back.
- DedicatedLease lacked `dedicated`, so SharedRadioWatch (and the ACK grace
  for home Wi-Fi changes) never ran.
- When wlan0 loses its network mid-projection, pause its autoconnect (and
  stop a join under way) until the session ends. Each retry took the shared
  radio off the car's channel and stalled video past the ACK timeout.
2026-11-04 08:07:17 -08:00
dirwin31 1bb9e9fc0e Galaxy hotspot: ship hostapd and keep NetworkManager off galaxy0
AGNOS 20.x no longer includes hostapd (agnos-builder 0cf5062), so every
start failed and the worker created and deleted galaxy0 every 15 s. On
2026-11-04 that churn crashed NetworkManager mid-drive and dropped StarPort.

- Ship Ubuntu 24.04's hostapd 2.10 and hostapd_cli (arm64, checksums in
  bin/hostapd.sha256) and run those.
- Check for hostapd before galaxy0 exists.
- Back off repeated failures from 15 s up to 5 min (DFS refusals exempt).
- Write a runtime udev rule (NM_UNMANAGED) before creating galaxy0, and wait
  for wpa_supplicant to let go of it before starting hostapd. Its release
  stopped the AP that hostapd had just started.
2026-11-04 08:07:17 -08:00
Danny 9b377b16f6 Cleanup: Trim comments, drop swallowed errors, fix glow test 2026-11-04 08:07:17 -08:00
dirwin31 f001d80378 Theme maker: allow up to 10 widget art sprites
The compact profile renders 8 sprites and the browser rejected anything over 7
as invalid metadata, leaving only the sketch outlines.
2026-11-04 08:07:17 -08:00
dirwin31 3edf46a991 UI: curve-speed glow, gas-override blink and Theme maker preview
- Steady the curve-speed glow and the gas-override blink on the on-road screen
- Stop the Theme maker preview flapping to "unavailable" and allow for Galaxy tunnel latency
- Carry the widget sprite sheet in a header so it survives the tunnel
2026-11-04 08:07:17 -08:00
dirwin31 54939bf311 Spotify: retry a saved sign-in that failed at startup
- Retry a saved sign-in when the first attempt at boot fails, for example before the clock has synced
- Let the supervisor restart a player that never logged in
- Add connect tests for the retry
2026-11-04 08:07:17 -08:00
dirwin31 11a65c1010 Maps: lane guidance, calmer rotation and steadier navigation layout
- Add lane guidance to the on-road navigation banner, fed by new route engine fields in the custom cereal schema
- Calm map rotation and steady the navigation layout, including the Galaxy layout preview
- Add route runtime, on-road map and navigation tests
2026-11-04 08:07:17 -08:00
dirwin31 1334ce24a5 StarPort: draw the default layer order when none is saved
- Use StarPort's default widget layer order in the car projection when no widgetOrder is saved, so the map no longer draws over the PiP
- Make the layout editor and the car screen agree on that order
- Cover the default order in the projection layout and widget layer tests
2026-11-04 08:07:17 -08:00
Dom 46a872b02f Synchronize sender fixture and narrow startup test callable
Hold the mock background upload until in-flight assertions finish and join it on every exit. Preserve strict completion and payload checks.

Type the dynamically populated startup fixture namespace as object and assert its main entry is callable before invocation. Preserve startup ordering assertions and refresh the canonical protected inventory receipt.
2026-11-04 09:32:22 -06:00
firestar5683 d907c22ac0 Bind each Android Auto startup fixture to its device 2026-11-04 09:12:53 -06:00
Dom 13930a89e5 Share personality alert preference with Android Auto 2026-11-04 09:10:06 -06:00
firestar5683 7c0d1d505a Prioritize driving and audio over Android Auto on every comma 2026-11-04 09:07:25 -06:00
firestar5683 ea27a69c20 Keep Android Auto transport below driving and audio on comma 4 2026-11-04 09:06:20 -06:00
firestar5683 f45fd8ed79 Keep comma 4 display work below driving and audio 2026-11-04 08:43:09 -06:00
firestar5683 1509b5e6b6 Distinguish legacy Toyota radar pre-warning from unavailability 2026-11-04 08:18:37 -06:00
firestar5683 a17e2a963d Preserve HKG lateral through CANCEL status handoffs 2026-11-04 08:01:00 -06:00
Dom c6484123d5 Keep replay provenance manifest within existing size gate
Reduce leading JSON indentation only, preserving every token, value, key and list order, reviewed reference binary and provenance pin. Keep the unchanged 120 KiB lint limit and all validation gates.

Refresh the canonical protected inventory receipt for the formatting-only file change.
2026-11-04 03:23:25 -06:00
Dom aa6207cf90 Qualify replay references and complete hosted CI coverage
Pin seven independently reviewed replay outputs to the unchanged recorded inputs and baseline provenance. Correct Subaru test helper cleanup without changing native admission rules. Declare the exact generated pandad test executable in the build-output inventory.

Run the unchanged whole-target RELEASE workload in sixteen mandatory shards, retaining all cases, assertions, skip and provenance gates, and existing deadlines. Refresh the canonical protected inventory receipt.

Native qualification completed 10,211 passed and 115 existing skips across all sixteen bins; fresh source-bound hosted CI remains required.
2026-11-04 02:23:57 -06:00
Dom 9eab5400a4 Repair hosted display selection and migrated test fixtures
Select the real headless backend for hosted AOL and RELEASE tests. Isolate SpacePilot scripted clocks from unrelated UI readers. Update camera and replay fixtures for existing native APIs, pin the reviewed schema addition, and supply declared CAN batch and throttle inputs.

Preserve production guards, acceptance assertions, selected cases, capability skips and receipt gating. Refresh the canonical protected inventory receipt.
2026-11-04 00:50:22 -06:00
Dom baf883200c Restore selfdrived executable mode after Toyota source update
Preserve the exact published Toyota source blob and restore its prior100755 executable mode. Refresh the canonical protected inventory receipt. No source semantics or test expectations change.
2026-11-03 23:25:25 -06:00
firestar5683 512742ca42 Keep cold Toyota AOL source waits distinct from lost control 2026-11-03 23:08:10 -06:00
firestar5683 1e4fe7c8a4 Damp EV6 near-center steering while preserving turn-in 2026-11-03 22:28:41 -06:00
firestar5683 9b7e8fa5fa Preserve armed HKG lateral through button counter noise 2026-11-03 22:20:04 -06:00
firestar5683 2496b40f84 Preserve armed HKG lateral through button counter noise 2026-11-03 22:02:51 -06:00
Dom 67fde8c695 Refresh audited graphics fixture identities after native binding migration
Re-audit only the GPU/map source and shared fixture fingerprints following the native binding migration. Preserve the exact capability guard, reason, selected nodes and unavailable disposition. Add a real-source fingerprint regression so mocked gate fixtures cannot hide stale source pins.
2026-11-03 21:38:16 -06:00
firestar5683 14534287c1 Detect C-HR CAN filters alongside native radar traffic 2026-11-03 20:22:32 -06:00
Dom 2929a25afe Add parked manual editing for thirty steering coordinates 2026-11-04 02:14:49 +00:00
firestar5683 465db1bc61 Preserve Ford lateral intent across separate brake status receipts 2026-11-03 19:55:37 -06:00
firestar5683 89af8a6569 Preserve Subaru intent through stop and source handoffs 2026-11-03 18:30:19 -06:00
Dom dd4214f8f5 Restore direct LAN DNS entry to Galaxy safely
Discover exact OS/DHCP/PTR identities independently of request Host. Redirect arbitrary aliases only at the direct-local inert landing document, preserving Host, Origin, session and remote boundaries. Refresh the protected-inventory receipt for the documentation-only protected change; native inputs are unchanged.
2026-11-03 17:03:01 -06:00
firestar5683 180f94dc35 Recover firmware reads interrupted by unrelated diagnostics 2026-11-03 16:02:27 -06:00
prabhaavp e997847db0 Merge pull request #275 from dirwin31/CarViewFontFix
StarPort: Missing Fonts
2026-11-03 16:44:04 -05:00
Danny 228534e0cf StarPort: Load fonts in the car screen and log full process commands 2026-11-03 13:37:46 -08:00
firestarsdog 5e02c2387a Arabesque time harden 2026-11-03 16:20:25 -05:00
Dom e6f1ad93ee Exercise native timer and interrupt health in siren harness 2026-11-03 14:43:02 -06:00
Dom 5f2caf8249 Align loader affinity fixture with captured camera dimensions 2026-11-03 14:24:38 -06:00
firestar5683 7b16c71824 Recheck live tuning against the accepted baseline 2026-11-03 13:21:03 -06:00
firestarsdog e381bf6a44 Check offline Firefox against AGNOS 2026-11-03 14:07:29 -05:00
firestarsdog c7b81a5768 More offline Arabesque stuff 2026-11-03 13:41:15 -05:00
firestar5683 7920339796 Prevent same-bus HKG advisory packet retransmission 2026-11-03 12:33:24 -06:00
Dom fc45eb6862 Style Polaris model cards by series 2026-11-03 12:06:50 -06:00
firestar5683 b302c2ef8f Avoid repeated CCNC longitudinal configuration clones 2026-11-03 11:53:35 -06:00
firestar5683 4d804768ab Keep conditional experimental mode independent of planner policy 2026-11-03 11:49:49 -06:00
firestarsdog 9b7f4c6646 Arabesque, include offline depends for install 2026-11-03 12:33:55 -05:00
firestar5683 14205db69d Reset HKG angle recovery gain and rejected command history 2026-11-03 11:33:09 -06:00
firestar5683 4cf5c9e098 Keep Subaru fixed-angle handoff within native rate limits 2026-11-03 11:25:13 -06:00
firestar5683 bf4e27fca5 Build matching Carnival independent lateral and longitudinal firmware 2026-11-03 11:12:56 -06:00
firestar5683 d42ddc3ac5 Restore Carnival ICE independent lateral and longitudinal startup 2026-11-03 11:01:08 -06:00
firestar5683 55d6fb1778 Continue learning after restoring a saved tune 2026-11-03 10:53:50 -06:00
prabhaavp 91dad8b66d Merge pull request #273 from dirwin31:StarPort-AutoSetup-20.2
StarPort: guided setup, a setup ladder, and wireless adapter fixes
2026-11-03 11:22:05 -05:00
Prabhaav Pillai 047294a1d0 Merge Dom into StarPort 2026-11-03 11:19:55 -05:00
firestar5683 a336e65dd8 Improve Tuning and Analysis controls and plot tabs 2026-11-03 10:15:16 -06:00
firestar5683 dd9608eecd Preserve Subaru lateral on fresh input after output expiry 2026-11-03 10:09:06 -06:00
Prabhaav Pillai 123e3c20a0 StarPort setup, reconnect handling, and UI consistency 2026-11-03 10:58:57 -05:00
whoisdomi 80975369ee Force Stop: add dashboard signal back 2026-11-03 09:18:24 -06:00
whoisdomi 7ec8cc721d Hold CEM for cross traffic leads 2026-11-03 09:18:24 -06:00
whoisdomi 1d2d18fa2a Force Stop Temp Hold 2026-11-03 09:18:23 -06:00
firestar5683 42a1244e1b Show retained Auto Learn study progress 2026-11-03 08:28:22 -06:00
Danny f148770788 Build 2026-11-03 05:40:06 -08:00
Danny 755d82a7e6 UI Unification 2026-11-03 05:40:06 -08:00
Danny 7d7bf43fac Account for Starport on Dev Page 2026-11-03 05:40:06 -08:00
Danny 63b05a9294 Fix test for AA wireless 2026-11-03 05:40:06 -08:00
Danny 80f707aa3d StarPort: Review fixes for the stall notice, car forget and car adoption
Reuse atomic_write and the sr-only class, drop dead code, update stale tests
2026-11-03 05:40:06 -08:00
Danny 6d99039382 Test Fixes 2026-11-03 05:40:06 -08:00
Danny cfdcd14ca8 Show car vs app in pairing 2026-11-03 05:40:06 -08:00
Danny 5be95f48df AAwireless: Stop tearing the Session down every 60s 2026-11-03 05:40:06 -08:00
Danny fb9b3516b1 Allow Disconnect from Galaxy for Starport Projecting Car 2026-11-03 05:40:06 -08:00
Danny 15b2612d7f Hold RFComm for a moment, Better Traces. 2026-11-03 05:40:06 -08:00
dirwin31 e99e552f21 StarPort: wait out the comma's own Wi-Fi switching networks, and say so on the car screen
- When the comma's Wi-Fi joins or roams while the car is on aa0, wait up to 8 s for video acks instead of 2.5 s; reconnect at once if the car takes its screen back.
- After a drop it causes, the car screen shows "Reconnected: comma Wi-Fi Joined <network>" (the network's SSID) for 8 s; Galaxy and bug reports name it too.
- Logs ack_grace, ack_grace_recovered and other_wifi_changed events.
2026-11-03 05:40:06 -08:00
dirwin31 f0c5a666c5 Galaxy Bluetooth setup: one systemd reload, after the radio has started
- Link the bridge and discovery units with --no-reload and reload once (was four reloads, 1-3 s each on a comma four).
- Wait (up to 45 s after boot) while starpilot-bluetooth-radio is still activating, so the reload can't stall btattach or bluetoothd.
- Failure handling unchanged; on a comma four this removed the ~20 s Bluetooth boots (now 16.4-16.6 s with stock AGNOS).
2026-11-03 05:40:06 -08:00
dirwin31 3b8d201839 Hide other devices once selected one 2026-11-03 05:40:06 -08:00
dirwin31 067f30dfa4 Bring the purple back to the guided setup's selections
The selected adapter or app segment is solid purple with a soft glow instead
of grey, the selected connection option gets a purple-tinted row and a filled
icon tile, the current step and the primary button glow, and a faint purple
wash sits behind the step track.
2026-11-03 05:40:05 -08:00
dirwin31 1d0525fe41 Keep the guided card open after choosing how the car connects
Choosing a USB cable or a tablet app switches the connection at once, which
counts as setup done, so the card closed under the person's finger and left
no way to change the choice. A choice made in the card now holds it open
until Done. In the guided setup the home card's button reads Change how your
car connects, so the way back is visible.
2026-11-03 05:40:05 -08:00
dirwin31 363d942ec9 Theme the guided setup with Galaxy's own colors, font and icons
Done steps and checks use the theme's success color, the current step and
selection use the primary purple, text uses Galaxy's Inter and weight tokens,
and the connection options use the outline icons the rest of the page uses
(car-front, usb-symbol, usb-c, tablet) on a single primary-tinted tile
instead of Apple's multicolour gradients.
2026-11-03 05:40:05 -08:00
dirwin31 b21952a392 Reuse the existing StarPort list, row and disclosure styles in the guided card
The guided card's lists, paired-car rows, checklist rows and handshake
disclosure now use the page's own gx-starport-list, gx-starport-row,
gx-starport-row__text and gx-starport-wiki classes, restyled once for the
card, instead of parallel guided-only copies. Radii, spacing and motion use
Galaxy's tokens. Only the step track, icon tiles, radio rows, segmented
control and checklist marks remain new, and the block is about a fifth
shorter.
2026-11-03 05:40:05 -08:00
dirwin31 2678e29cb3 Redesign the guided StarPort setup; keep the dropdowns to the classic screens
The guided card is rebuilt in an Apple-style: a three-step track (support,
car, connect) that fills as setup moves along, a large title, inset grouped
lists with coloured icon tiles for how the car connects, a segmented control
for the adapter model and the tablet app, iOS-style checklist rows for the
connection, and one full-width primary button. It uses Galaxy's theme colors
so light and dark both work, and reads cleanly down to a phone.

The classic step 2 and the home card's dropdowns now render only in the
classic setup. The guided card replaces them and hosts the page's own
pairing, cable and app instructions and the Connect button, so nothing is
lost. A finished setup can be reopened from Setup and Pairing, and Done
closes it. Paired cars the page's own list does not show yet are offered in
the card; the rest stay in the pairing step.
2026-11-03 05:40:05 -08:00
dirwin31 75a29d3e71 Put the full report in the logs bundle; turn the Bluetooth-first refusal into a step
The logs bundle, and so the Discord send, now carries report.json: the same
tags and per-session detail a Bugsink report has (setup, offered modes, reach
class, ladder, SDP, strategy) for the newest five sessions, sanitized and
size-capped. It is built best-effort: if it cannot be, the bundle is
unaffected.

The adapter setup's 'Connect your paired car in Galaxy Bluetooth first'
refusal (27 Bugsink reports) now shows a step with a link to Bluetooth, next
to the usual question while setup is unfinished and alone once it is done.
2026-11-03 05:40:05 -08:00
dirwin31 e5a981b5ad Ladder passes are per ignition; car adoption leaves the owner's audio setup alone
The comma can stay powered between drives, so the three passes belong to an
ignition (an onroad start), not to a boot; a reboot still resets them. When a
learned strategy is dropped after two handshake failures the ladder carries
on from the next rung instead of starting at the one that just failed, which
the dongle flip also avoided.

Choosing the only paired Android Auto car from housekeeping runs with nobody
at the screen, and selecting a receiver unselects that device as the
Bluetooth audio output. It now skips a car that is the selected audio device,
or when that cannot be read, leaves it to the setup screen's Use button. It
reports problems once instead of every 20 s and gives the projection role
back, keeping only the standby gateway auto-connect needs.
2026-11-03 05:40:05 -08:00
dirwin31 54eab0df8d Replay the real Bugsink failure texts through the reachability classifier; tag the reply style
The 686 pre-video failures Bugsink showed (54 distinct texts) are kept as a
fixture. The test checks the intended class for each: Host is down and page
timeouts are a sleeping car, refused and unanswered handshakes are not ready,
and anything after the link is up is not a reachability problem. Reports tag
the reply style and Wi-Fi band the working strategy used.
2026-11-03 05:40:05 -08:00
dirwin31 99a9025afe Report how the channel was found and what the ladder tried; wait longer for a slow car
Reports carry the SDP outcome (where the RFCOMM channel came from and how
many retries it took). The Discord post names the handshake options tried
and how each ended. A car that once needed 70 s to be ready now gets that
many stage timeouts to answer the Wi-Fi handshake, never fewer than the
standard wait and capped at nine.
2026-11-03 05:40:05 -08:00
dirwin31 0783710c9a Add the guided StarPort setup behind the New pairing experience switch
A card above the classic steps asks one question (how does the car connect:
built-in wireless, adapter, USB cable or app), shows only the instructions
for that answer, and lists paired cars that offer Android Auto to choose
between when there is more than one. While connecting it shows a live
checklist of the stages (Bluetooth, the car's Wi-Fi, securing the connection,
the dashboard), a list of the handshake options being tried once one has
failed or one is remembered, and a notice that the first connection can take
a few minutes or more than one drive. Every action is one the page already had.

The card is on by default; Use classic setup turns the switch off, and Try
the guided setup turns it back on. Returning drivers see the card only while
a connection is in progress. The state function is pure and tested without a
browser.
2026-11-03 05:40:05 -08:00
dirwin31 3d26454122 Let Galaxy read and change the new pairing experience switch
The setup response carries newSetup, and a new_setup control action turns the
guided setup off or on. It is handled before the StarPort-enabled check and
never calls the daemon, so it works as the way back to the classic screens
when the service is off or broken. It needs a signed-in session and park.
2026-11-03 05:40:05 -08:00
dirwin31 57900f3d73 Add the setup ladder: try handshakes in order, remember the one that worked
One module now decides how to answer a car's handshake, and it is also the
learned profile: the strategy that last worked is the one tried first.

The first pass is today's behavior. Later rungs are knobs that already
existed: the other version reply (the flip the dongle work added, now owned
here), the phone-style reply with both bands and with 2.4 GHz only, a longer
wait for a car that keeps saying not ready, and, for adapters only, the
handshake variations.

A failure advances the ladder only when the car answered the version
exchange, then went silent or refused the Wi-Fi setup, before any video was
acknowledged in the session. A sleeping car, this comma's own Bluetooth
trouble and anything after video (a car shutting off) never move it. Three
passes per boot at most, resuming at the first untried rung on the next
ignition; a learned strategy is dropped after two handshake failures; a
session that streams for a minute makes its strategy the learned one.

State is a small per-car file beside the config (setup_state.json, newest 16
cars, damaged or unreadable means a fresh start, never blocks a connection).
The status exposes the ladder for the setup screen, and reports carry what
was tried, how each rung ended and how long the car took to be ready.
2026-11-03 05:40:05 -08:00
dirwin31 2af7bdd1dd Add opt-in H.265 for a head unit that offers no H.264 mode
Allow H.265 video (Developer > StarPort, off by default) lets a car that
offers only H.265 get H.265. A car with an H.264 mode is chosen exactly as
before whatever the setting; this is replay-tested over several offers.

The hardware encoder library is ABI 4: an HEVC option selects H.265 Main
profile at high-tier level 5 and allows up to 1920x1080 (H.264 stays at
1280x720). The Python side keeps H.265 apart from the H.264 code: its own
codec-config and wire-frame functions for the two-byte NAL header, its own
access unit check, and an encoder subclass. If the car is told H.265 and the
hardware encoder cannot open, the session fails with the reason; there is no
fallback to another codec.

Measured on a parked comma with a scratch copy of the library: H.265 1080p
costs about 13 ms per frame on the NV12 path (30 ms from RGBA), mid-stream
keyframes carry VPS/SPS/PPS, and the H.264 session is unchanged. The
encoder splits every H.265 picture into two slices, which the check allows.
2026-11-03 05:40:05 -08:00
dirwin31 5dbcbb9618 Choose the car automatically when exactly one paired device offers Android Auto
A car that is paired and offers wireless Android Auto but was never chosen
(the pairing window closed first, or it was paired in Bluetooth settings)
is adopted by housekeeping when it is the only candidate. This reads the
cached BlueZ service list, so it never pages a device, and it does not run
during a pairing window or session, for adapter setups, or once a car is
chosen. With several candidates nothing is guessed; they are listed in the
status as car_candidates for the setup screen to offer.
2026-11-03 05:40:05 -08:00
dirwin31 d5f6d6ee72 Only wait on a Wi-Fi interface NetworkManager reports as unmanaged or unavailable
The first version waited on any state below disconnected, which also caught
the unknown state and broke the lease tests. Wait on states 10 and 20 only.
2026-11-03 05:40:05 -08:00
dirwin31 cad0022d3e Wait for NetworkManager to manage a shared Wi-Fi interface and tell stream losses apart
Nineteen Bugsink joins were refused with 'device is strictly unmanaged'.
On a shared interface the join now waits up to 8 s for NetworkManager to
manage it (dedicated mode already waited) and otherwise reports that the
interface is unmanaged and for how long, which the diagnosis already maps
to wifi_not_ready.

Stream losses keep the projection_interrupted category but now carry a kind
(stalled, silent, disconnected) with matching next steps, reported as
aa.loss_kind, so a car shutting off is no longer read as link loss.
2026-11-03 05:40:05 -08:00
dirwin31 dbb1fe6547 Add a StarPort section to the Developer page with the new pairing switch and an H.265 setting
The Developer page gets a heading row, StarPort, that groups the StarPort
settings: the New pairing experience switch (on unless turned off), Stay on
Wi-Fi (moved under the heading, same key, label and default) and Allow
H.265 video (off by default, daemon setting allow_hevc).

The heading is a row whose key starts with heading:, projected with
heading=true. It has no choices, is never available and cannot be changed,
and Galaxy draws it as a label. The new pairing switch is a plain file next
to the other StarPort data rather than a Params key, so it reads and changes
with the StarPort service down and needs no device rebuild; absent or
unreadable means on.
2026-11-03 05:40:05 -08:00
dirwin31 6854118aa2 Wait out an unreachable car by why it was unreachable
Most failed drives in Bugsink never reached video and stopped at Bluetooth,
but one backoff treated a sleeping radio, a service that was not up yet and
this comma's own busy stack alike.

reachability.classify() separates a car that never answered a page (Host is
down, page timeout), a car that answered but refused or reset, and a local
Bluetooth failure. A sleeping car or busy controller now waits at least as
long as before and often longer; other failures wait exactly as before. A
sleeping car no longer makes the comma forget the cached RFCOMM channel.
The class is logged on attempt_failed and reported as aa.reach_class.

The 141 'unknown' failures at the discovering stage now get a diagnosis:
car_asleep, car_not_ready or bluetooth_stack_busy, with next steps.
2026-11-03 05:40:05 -08:00
dirwin31 8e0073f110 Show the chosen setup in the StarPort log send
The Discord post now names the setup type, profile and whether it was
chosen or detected, from the newest session log that recorded one, next to
the car and head unit. The zip already carries the same data in each
session log's session_start and discovery events.
2026-11-03 05:40:05 -08:00
dirwin31 fd660ef6f7 Report the chosen setup, the car's offered video modes and the working strategy
Reports could not tell a built-in unit from a dongle, and offers and
handshakes were only visible in failure text, so there was nothing to
compare working pairings against.

session_start now records the setup type (builtin/adapter/usb/app), the
resolved profile and whether it was chosen or detected. Session reports
carry every video mode the head unit offered, whether the comma's address
reached the car in the pairing request, and, for streaming sessions, the
strategy that worked (window, reply style, Wi-Fi channel type). The drive
report gains low-cardinality tags (aa.setup_type, aa.adapter_model,
aa.profile_source, aa.phone_address_sent); long values stay in contexts.
Old logs without these fields still report. aa.schema is now 3.
2026-11-03 05:40:05 -08:00
dirwin31 745b1e94de Send the comma's Bluetooth address in the head unit pairing request
LeaseAwarePhone (used for every wireless session) had no adapter() method,
so Supervisor._adapter_address() hit AttributeError, swallowed it, and
returned "". The Bluetooth-service pairing request was therefore never sent
(bluetooth_opened always logged phone_address: "").

A 2020 myChevrolet (head unit 2.46.1.6) through an AAWireless Two+ then
accepted and acknowledged video but never showed it. A real phone sends the
pairing request about 1 s into the session; the car shows Android Auto once
its hands-free link to that address is up.

Pass adapter() through to the wrapped BluezPhone. It is a read-only property
lookup, so it needs no phone-role lease, like devices() and device().
_adapter_address() now logs adapter_address_unavailable instead of failing
silently.

Tested on the car (session-000052): phone_address sent, pairing response
received, picture displayed, touch input worked, 20-29 fps fully acked.

Co-authored-by: droobah <34214975+droobah@users.noreply.github.com>
2026-11-03 05:40:04 -08:00
dirwin31 085aa2a173 Send StarPort logs from Logs & Diagnostics
Adds a Send logs card to the StarPort Logs page so a user can report a
problem with all of their reportable logs: an optional note of up to 60
characters, and the same zip as Download all logs, sent from the device to
the developer's log inbox. Each device keeps its own thread, titled with the
car fingerprint and a hashed device tag. Manual sends only; no automatic
failure reports. If the zip is over the 10 MB size limit, it is resent
without the device snapshot.
2026-11-03 05:37:41 -08:00
Danny b8f3bcebcb StarPort: Remember which version reply each v4 head unit started with 2026-11-03 05:37:41 -08:00
Danny 29f498d77e StarPort: Unofficial dongle option, v4 head unit version reply fallback, patience for not-ready cars 2026-11-03 05:37:41 -08:00
firestar5683 31d5868e90 Narrow validated tracking history before indexing 2026-11-03 05:27:45 -06:00
firestar5683 0f57b620ef Keep learner confirmation pending through acknowledgement 2026-11-03 05:13:06 -06:00
firestar5683 bd917d43d8 Continue unfinished tuning studies across maneuvers 2026-11-03 04:07:11 -06:00
firestar5683 be17536f2b Validate only the causal tuning pose interval 2026-11-03 02:35:13 -06:00
firestar5683 ef974ecc66 Keep consumed tuning evidence through publication 2026-11-03 01:48:50 -06:00
firestar5683 10d170e799 Report live steering trials without legacy counter errors 2026-11-03 01:07:00 -06:00
firestar5683 3924bd2e39 Give Auto Learn a clear Galaxy tuner and saved-state status 2026-11-03 00:21:38 -06:00
firestar5683 208dc09bca Build matching HKG coffee-break advisory firmware 2026-11-02 23:17:28 -06:00
firestar5683 ff9b397393 Update AGNOS release receipt 2026-11-02 23:01:34 -06:00
firestar5683 de7c4c30ad Activate C3 firmware chain and AGNOS 20.2.1 2026-11-02 23:01:34 -06:00
firestar5683 507373cb7d Restore HKG coffee-break advisory replacements 2026-11-02 22:57:08 -06:00
firestar5683 58fc0bb4e7 Preserve HKG angle AOL intent through temporary EPS refusals 2026-11-02 22:19:54 -06:00
Dom 112c775a2c Restore executable mode for controlsd entrypoint 2026-11-02 22:11:25 -06:00
Dom a5622e8933 Rebuild Panda firmware for low-speed Ioniq 6 indication policy 2026-11-02 21:44:12 -06:00
Dom fb7592f834 Keep Ioniq 6 injected blind-spot indications neutral below 5 mph 2026-11-02 21:37:07 -06:00
Dom 5f960fba75 Retain Subaru lateral intent across qualified temporary EPS refusal 2026-11-02 18:28:50 -06:00
Dom 4a46f59825 Block incompatible AGNOS bootloader updates on comma 3 2026-11-02 18:16:58 -06:00
Dom 6df20cbb76 Keep Traffic border stable during snapshot settings reads 2026-11-02 17:54:14 -06:00
Dom f5c84b4871 Use repository parameterized helper in modem tests 2026-11-02 17:26:31 -06:00
firestar5683 35f3058ea1 Finish upstream compatibility 2026-11-02 16:38:36 -06:00
firestar5683 8defcc42b5 Build upstream compatibility artifacts 2026-11-02 16:38:36 -06:00
firestar5683 b044e891e9 Use unsigned flash self-test size arithmetic 2026-11-02 16:38:35 -06:00
firestar5683 6bcdbd0e81 Initialize RAM self-test alias storage 2026-11-02 16:38:34 -06:00
firestar5683 7d062147d3 Complete native safety type checks 2026-11-02 16:38:34 -06:00
firestar5683 1ecde54bb0 Complete synchronization test fixtures 2026-11-02 16:38:34 -06:00
firestar5683 f00fc85847 Pin the retained upstream schema contract 2026-11-02 16:38:34 -06:00
firestar5683 1650f86d22 Qualify upstream compatibility 2026-11-02 16:38:34 -06:00
Adeeb Shihadeh 788e655999 up: reset the UP in pandad loopback and can_replay (#39192) 2026-11-02 16:38:34 -06:00
Adeeb Shihadeh 61bc147001 Move HITL tests to UP (#39138) 2026-11-02 16:38:34 -06:00
Adeeb Shihadeh 8e828c2c70 Improve HITL test robustness (#39153) 2026-11-02 16:38:34 -06:00
firestar5683 2e39fc2162 Finish native transport dependency cleanup 2026-11-02 16:38:33 -06:00
Adeeb Shihadeh a61a0873cc replace bridge with webrtc (#39158) 2026-11-02 16:38:33 -06:00
Adeeb Shihadeh e2b90a34a6 remove zmq (#65) 2026-11-02 16:38:33 -06:00
Adeeb Shihadeh 2e6845146b raise max message size (#25) 2026-11-02 16:38:33 -06:00
firestar5683 242e2ac0a0 Warn for sustained high-angle HKG steering requests 2026-11-02 16:37:56 -06:00
firestarsdog f34746452a Add Arabesque clock for offline time recovery 2026-11-02 17:25:51 -05:00
firestar5683 97b5237e94 Build corrected Panda flash self-tests 2026-11-02 16:14:48 -06:00
firestar5683 20da579f92 Check H7 firmware CRC within its actual application flash region 2026-11-02 15:55:53 -06:00
firestar5683 789b5365c0 Preserve HKG lateral through brake receipt ordering during steering override 2026-11-02 15:54:48 -06:00
firestar5683 a931a94f79 Build matching CCNC takeover and resume artifacts 2026-11-02 15:29:25 -06:00
firestar5683 9576fd0c02 Restore CCNC driver takeover, resume and dashboard behavior 2026-11-02 15:23:58 -06:00
firestar5683 ccd9bfc4b8 Build Panda diagnostics 2026-11-02 15:05:15 -06:00
firestar5683 c802cb51a3 Build Panda diagnostic firmware 2026-11-02 15:05:14 -06:00
firestar5683 7125680cd3 Qualify Panda diagnostic requests 2026-11-02 15:03:26 -06:00
Daniel Koepping dea0ca1a91 cereal: name the new panda faults (#39133) 2026-11-02 14:50:47 -06:00
firestar5683 4975f7c387 Build Tesla cooperative steering host and Panda firmware 2026-11-02 14:48:21 -06:00
AngusBell97 63beafc6af Restore Tesla Model 3 cooperative steering 2026-11-02 14:45:21 -06:00
firestar5683 aa397cad54 Refresh safety RX build receipt 2026-11-02 14:20:21 -06:00
firestar5683 962d13da89 Build safety RX firmware 2026-11-02 14:19:45 -06:00
firestar5683 26b75e83fa Preserve fork safety RX configurations 2026-11-02 14:16:48 -06:00
Adeeb Shihadeh ba4d173709 safety: compare speed mismatch in integer units (#3877) 2026-11-02 14:16:45 -06:00
Adeeb Shihadeh 1e91930094 safety: pack RX descriptors (#3872) 2026-11-02 14:16:44 -06:00
Adeeb Shihadeh c7d78e5462 safety: make the RX check tables const (#3870) 2026-11-02 14:16:44 -06:00
Adeeb Shihadeh 2645933f91 safety: keep RX check state only for the active safety config (#3864) 2026-11-02 14:16:41 -06:00
Adeeb Shihadeh 9640b6e049 safety: const CRC lookup tables (#3867) 2026-11-02 14:16:40 -06:00
Adeeb Shihadeh 63944ec1c3 safety: pack CanMsg and CanMsgCheck (#3868) 2026-11-02 14:16:39 -06:00
Adeeb Shihadeh ccf112fb9c safety: integer math for the RX check lag threshold (#3869) 2026-11-02 14:16:39 -06:00
Adeeb Shihadeh fb72658c89 safety: make the remaining TX message lists const (#3863) 2026-11-02 14:16:39 -06:00
firestar5683 bc57fa8eb8 Preserve GM stock-cruise lateral intent through accelerator-held source gaps 2026-11-02 14:13:25 -06:00
firestar5683 d2680ea3db Keep Tinygrad vendor tree exact 2026-11-02 13:44:48 -06:00
firestar5683 a0283d271b Refresh model runtime source receipt 2026-11-02 13:38:46 -06:00
YassineYousfi 55e4f4289a bump tinygrad usb wrap fix (#39204) 2026-11-02 13:38:46 -06:00
YassineYousfi 70f3c8897d modeld: tinygrad jit copies and update tinygrad (#39187) 2026-11-02 13:38:46 -06:00
firestar5683 cfefb493aa Use planner-primary angle demand with bounded symmetric smoothing 2026-11-02 13:33:20 -06:00
firestar5683 05d60a67ea Refresh dependency documentation receipt 2026-11-02 13:03:41 -06:00
Adeeb Shihadeh 9f15200211 slash is the default (#3879) 2026-11-02 13:03:40 -06:00
Shane Smiskol e07299dd9a Car porting checklist (#3803) 2026-11-02 13:03:40 -06:00
firestar5683 ba40d9fe5a Refresh cleanup source receipt 2026-11-02 12:56:09 -06:00
Adeeb Shihadeh f2bac62f71 remove more dead code (#39196) 2026-11-02 12:56:09 -06:00
firestarsdog f27f1f22c2 Arabesque time sync to prevent Firefox issues 2026-11-02 13:55:41 -05:00
firestar5683 d1f4a06744 Refresh shared UI source receipt 2026-11-02 12:54:13 -06:00
Adeeb Shihadeh a4cf46600d ui: share ModelRenderer between tizi and mici (#39198) 2026-11-02 12:52:14 -06:00
Adeeb Shihadeh bfca6d7f3d ui: share CameraView between tizi and mici (#39197) 2026-11-02 12:52:13 -06:00
firestar5683 e094c3b624 Build fleet driver monitoring lock 2026-11-02 12:49:33 -06:00
Harald Schäfer 4f4525e59c Fleet: force always-on driver monitoring (#39169) 2026-11-02 12:46:12 -06:00
firestar5683 6140bd54a6 Update low-speed model action receipt 2026-11-02 12:37:31 -06:00
Harald Schäfer 07606c8f9e modeld: raise low-speed curvature thresholds (#39182) 2026-11-02 12:36:18 -06:00
firestar5683 b409ef52cb Build native message queue bindings 2026-11-02 12:30:26 -06:00
Adeeb Shihadeh fa3138c929 bump msgq: nanobind (#39200) 2026-11-02 12:28:46 -06:00
firestar5683 fa02d93a01 Recover Galaxy updates after gateway disconnects 2026-11-02 11:53:41 -06:00
firestar5683 aa3266824c Refresh modem update receipt 2026-11-02 11:47:13 -06:00
firestarsdog d7b5b8aef2 AGNOS Modem Fix 2026-11-02 12:31:45 -05:00
firestar5683 ad67ae64b4 Verify native UI build receipt 2026-11-02 11:22:15 -06:00
firestar5683 969ab9083c Verify native UI runtime artifacts 2026-11-02 11:20:21 -06:00
firestar5683 3fdd0d122b Build StarPilot native Raylib support 2026-11-02 11:14:49 -06:00
Adeeb Shihadeh 3c9a722083 Switch to nanobind raylib bindings (#39193) 2026-11-02 11:13:02 -06:00
Dom cbd6e1eb52 Use typed Card owners in radar publication tests 2026-11-02 09:57:40 -06:00
firestar5683 f1e5ee7312 Connect large Home dashboard to cached drive history 2026-11-02 09:40:11 -06:00
firestar5683 91c1fee59a Qualify Upstream Python Cleanup 2026-11-02 09:28:07 -06:00
Adeeb Shihadeh c08eb2fc0b remove dead code (#39195) 2026-11-02 09:26:06 -06:00
firestar5683 295374ecec Qualify Faster Unit Tests 2026-11-02 09:24:41 -06:00
Adeeb Shihadeh c710aea5ce faster unit tests (#39202) 2026-11-02 09:24:41 -06:00
firestar5683 e5931a7321 Qualify Radar Publication 2026-11-02 09:22:32 -06:00
firestar5683 25247f09ba Test Radar Publication Fault Semantics 2026-11-02 09:17:59 -06:00
Shane Smiskol 393a6139cf card: only radar CAN errors invalidate radarTracks (#39185) 2026-11-02 09:17:59 -06:00
firestar5683 9ef9ef54b2 Qualify Unix Datagram Logging 2026-11-02 08:57:34 -06:00
firestar5683 3705334acc Build Unix Datagram Logging 2026-11-02 08:57:34 -06:00
Adeeb Shihadeh 63ef69c04b swaglog: replace zmq with unix datagram socket (#39157) 2026-11-02 08:57:32 -06:00
firestar5683 5bbfafa3e7 Qualify AGNOS 20.2 Release 2026-11-02 08:46:49 -06:00
Adeeb Shihadeh ca5cbb342b AGNOS 20.2 (#39178) 2026-11-02 08:44:29 -06:00
firestar5683 8f59bfc56c Restore pre-rebase Prius torque shaping 2026-11-02 08:16:52 -06:00
firestar5683 7bcbee716a Refresh saved UI display flags once per second 2026-11-02 08:14:45 -06:00
firestar5683 8ef661eb59 Build matching Tesla screen lateral safety firmware 2026-11-02 08:13:18 -06:00
firestar5683 2ccc5fa9db Keep Tesla screen lateral off until fresh engagement 2026-11-02 08:11:23 -06:00
firestar5683 97c6040a9f Avoid takeover for expired idle GM cruise-main intent 2026-11-02 08:01:29 -06:00
firestar5683 1c035f3e6d Verify disabled Auto Learn without a runtime 2026-11-02 07:58:56 -06:00
firestar5683 8ff493640d Preserve HKG AOL through temporary EPS during driver override 2026-11-02 07:57:38 -06:00
firestar5683 ffc1aec50b Use planner-primary angle demand with bounded symmetric smoothing 2026-11-02 07:50:07 -06:00
firestar5683 d55e0b01f6 Qualify Integration Test Cleanup 2026-11-02 01:06:38 -06:00
firestar5683 dc6c5868f4 Clean Up Upstream Integration Tests 2026-11-02 01:05:32 -06:00
firestar5683 4d8df1f754 Qualify Camera Client Reuse 2026-11-02 00:55:15 -06:00
firestar5683 1e5f5c6791 Test Reused Camera Client Lifecycle 2026-11-02 00:50:20 -06:00
Adeeb Shihadeh 5808a49a52 UI: cache camera clients (#39166) 2026-11-02 00:48:19 -06:00
firestar5683 77cf37119c Qualify Upstream CAN Updates 2026-11-02 00:48:03 -06:00
Adeeb Shihadeh f7d3dfd037 Rate limit CAN timeout logs (#3898) 2026-11-02 00:46:06 -06:00
Shane Smiskol dea30ba5e2 Tesla: add Model Y EPS firmware (92),Y4003.10.2 (#3891) 2026-11-02 00:46:06 -06:00
Shane Smiskol 69711eb73f VW MEB: add pre-2024 HMS_Status (#3862) 2026-11-02 00:46:06 -06:00
Keenin Krehbiel 50514ff9ec Model 3 fingerprint for 2026.32.7 software (#3839) 2026-11-02 00:46:06 -06:00
Amy Jeanes 88eb1f00af Tesla: add Model Y EPS fingerprint (#3845) 2026-11-02 00:46:06 -06:00
firestar5683 993726afba Qualify Athena Input Validation 2026-11-02 00:43:01 -06:00
firestar5683 30d4b9e23f Preserve Internal Athena Validation 2026-11-02 00:40:42 -06:00
Adeeb Shihadeh 15e63f04f2 athena: validate all rpc params (#39190) 2026-11-02 00:40:42 -06:00
firestar5683 281098b7ab Qualify Upstream Calibration Updates 2026-11-02 00:38:41 -06:00
firestar5683 2c9dced9a8 Test Calibration Identity Changes 2026-11-02 00:36:43 -06:00
firestar5683 849c3715d9 Preserve Calibration Cache Validation 2026-11-02 00:36:14 -06:00
Daniel Koepping 8ef2ce2a0e docs: update safety wording (#39130) 2026-11-02 00:35:35 -06:00
Shane Smiskol fd1eb1662f calibrationd: don't reset calibration on a mock fingerprint (#39172) 2026-11-02 00:35:35 -06:00
Shane Smiskol 939b4889b2 calibrationd: use first block to only seed warp (#39170) 2026-11-02 00:35:35 -06:00
Shane Smiskol 7d6cf4e387 calibrationd: reset calibration when the car changes (#39171) 2026-11-02 00:35:35 -06:00
Adeeb Shihadeh 25830237de pack.py: include opendbc and check zipapps are self-contained (#39137) 2026-11-02 00:35:35 -06:00
firestar5683 b90a0d485c Keep Ford path angle within sign-correct wire bounds 2026-11-02 00:17:54 -06:00
firestar5683 0b7df146f8 Keep Ford controller comments technical and generic 2026-11-02 00:02:19 -06:00
firestar5683 7f630d2358 Qualify Upstream Touch Fixes 2026-11-01 23:29:45 -06:00
firestar5683 4c49768b24 Preserve Keyboard Gradient Compatibility 2026-11-02 05:27:52 +00:00
Shane Smiskol e20a02d930 ui: ignore additional fingers in last_mouse_event (#39144) 2026-11-02 05:27:52 +00:00
Shane Smiskol 04441d6049 ui: decel check on px moved per touch sample, not velocity (#39143) 2026-11-02 05:27:52 +00:00
Shane Smiskol ce9d9492c2 ui: mici keyboard multitouch (#39140) 2026-11-02 05:27:52 +00:00
Shane Smiskol 1376cf9d00 ui: fit fling velocity over the last 6 touch samples (#39142) 2026-11-02 05:27:52 +00:00
Shane Smiskol 9c4be6a6c1 ui: ignore additional fingers in scroll panel (#39141) 2026-11-02 05:27:52 +00:00
Harald Schäfer 7c3bf67791 SCons: avoid premature dependency scans in progress counter (#39131) 2026-11-02 05:27:51 +00:00
firestar5683 f353afa400 Skip disabled Auto Learn work in Controls 2026-11-01 23:19:52 -06:00
Dom 9e2d7594eb Model native receipt observation in selfdrived fixtures 2026-11-01 23:17:30 -06:00
firestar5683 5f741d9887 Keep Mazda source handoffs within the original control lease 2026-11-01 23:04:00 -06:00
firestar5683 865948a00e Preserve mixed HKG AOL after stock cruise disengages 2026-11-01 23:00:18 -06:00
firestar5683 5e92608ab7 Keep first AOL arming available during native startup 2026-11-01 22:41:34 -06:00
firestar5683 f429155f0f Keep StarPilot steering active after ordinary driver release 2026-11-01 22:34:13 -06:00
firestar5683 4e14f14461 Skip disabled Auto Learn CAN observations 2026-11-01 22:26:13 -06:00
firestar5683 2ee94f3643 Use saved vehicle settings in learner recovery tests 2026-11-01 21:57:07 -06:00
firestar5683 9e4cba8695 Test angle demand conditioning in StarPilot Controller 2026-11-01 21:28:45 -06:00
firestar5683 7cb8ea919f Isolate preference and replay clock test fixtures 2026-11-01 21:10:04 -06:00
firestar5683 f2f8cfffa8 Stop Auto Learn when learning is switched off 2026-11-01 21:01:08 -06:00
firestar5683 45c081b181 Balance independent Ford lifecycle cases across CI shards 2026-11-01 19:46:33 -06:00
firestar5683 736580721a Match Heatdeath safety builds and stream source verification 2026-11-01 18:59:11 -06:00
firestar5683 374a4134f6 Complete Honda active observation Card fixture 2026-11-01 18:36:08 -06:00
firestar5683 b16fc29ed5 Match radar isolation fixture to process health API 2026-11-01 18:17:07 -06:00
Dom c1e2b5f181 Complete Card fixtures and make SLC test timing deterministic 2026-11-01 17:44:39 -06:00
firestar5683 2c7f252feb Align developer command tests with JotPluggler alias 2026-11-01 17:25:38 -06:00
firestar5683 e522e5c6a3 Share immutable FLM evidence to bound learning memory 2026-11-01 17:14:56 -06:00
firestar5683 55f23c7fd5 Restore separate StarPilot Ford controller choices 2026-11-01 17:12:54 -06:00
firestar5683 61ad53c61f Keep automatic Tesla AP1 discovery on normal wiring 2026-11-01 16:55:04 -06:00
firestar5683 36e0e37086 Keep driving independent of UI restarts 2026-11-01 16:54:44 -06:00
firestar5683 407288852f Preserve Ford AOL through startup and brake transitions 2026-11-01 16:52:24 -06:00
firestar5683 0384be1253 Keep AOL through Nostalgia paddle handoff 2026-11-01 16:50:51 -06:00
firestar5683 a13b6395d4 Build stable traffic mode icon 2026-11-01 16:46:05 -06:00
firestar5683 e7cca0a136 Keep traffic mode icon stable on C4 2026-11-01 16:44:22 -06:00
firestar5683 c0bbed7762 Launch JotPluggler with juggle shortcut 2026-11-01 16:33:44 -06:00
firestar5683 6df04fa8e0 Detect Prius TSS2 steering rack retrofits automatically 2026-11-01 16:23:55 -06:00
firestar5683 40cd8ca264 Keep selected Tesla discovery on its declared CAN bus 2026-11-01 16:15:36 -06:00
firestar5683 d9b60fddc1 Balance measured Ford lifecycle work 2026-11-01 13:45:31 -06:00
firestar5683 153b95a88f Keep Auto Learn evidence current 2026-11-01 13:13:40 -06:00
firestar5683 5b6bdbe8ef Use typed gradient test fixtures 2026-11-01 12:39:39 -06:00
firestar5683 b70b7e16c4 Batch path gradient interpolation 2026-11-01 12:17:17 -06:00
firestar5683 195b7000aa Keep CI shard capacity and reviewed replay metadata consistent 2026-11-01 00:23:15 -05:00
firestar5683 1c7e21c43e Repair controller observation and current regression contracts 2026-11-01 00:08:07 -05:00
Dom e61c7993af Complete Card test interface fixtures for CAN observation 2026-10-31 23:59:05 -05:00
firestar5683 b158bc378a Preserve Ford expiry guards alongside native-first AOL braking 2026-10-31 23:15:31 -05:00
firestar5683 72f16053b5 Keep Ford curvature handoffs within native steering limits 2026-10-31 22:57:13 -05:00
Dom 1fe202b904 Narrow optional Bolt test intent before latch assignment 2026-10-31 22:40:12 -05:00
firestar5683 33bc40d061 Avoid invisible onroad texture draws 2026-10-31 22:35:49 -05:00
firestar5683 0102ad2a35 Review model producer after lane correction removal 2026-10-31 22:08:42 -05:00
firestar5683 5b0f81caa1 Reconcile queued Mach-E inputs before declaring a source fault 2026-10-31 21:56:17 -05:00
firestar5683 77ff308c76 Preserve GM lateral intent across brief Card source gaps 2026-10-31 21:48:35 -05:00
Dom cd585f55c2 Repair alert fixture and document weather epoch clock 2026-10-31 21:06:07 -05:00
firestar5683 feb1f442c2 Repair Ford BluePilot startup fixtures and native test loader identity 2026-10-31 20:44:19 -05:00
firestar5683 a7f4ba8254 Simplify Ford steering settings and preserve independent lateral intent 2026-10-31 20:36:04 -05:00
firestar5683 0f71f4669f Reduce FLM evidence allocations in Controls 2026-10-31 19:27:54 -05:00
firestar5683 2bc4f59d41 Report current FLM admission blockers 2026-10-31 19:25:18 -05:00
firestar5683 e8256ee5b2 Avoid repeated schema misses in the onroad display 2026-10-31 19:11:51 -05:00
firestar5683 1738b3798d Build Galaxy dashboard weather 2026-10-31 18:21:15 -05:00
firestar5683 d69ba777b5 Show outside temperature on Galaxy dashboard 2026-10-31 18:19:40 -05:00
firestar5683 0206e5dfc1 Default lane-centering model breakaway to fifty percent 2026-10-31 18:18:13 -05:00
Dom ff15fc4fae ci: restore selfdrived executable file mode
Restore the existing shebang script's executable mode without changing its source blob.
2026-10-31 17:57:44 -05:00
firestar5683 83e6132b6d Prefer another FLM coordinate after rejected oscillation 2026-10-31 17:56:27 -05:00
firestar5683 31b34665ce Preserve Ford AOL across gas override and lag recovery 2026-10-31 17:55:34 -05:00
firestar5683 8df7979373 Build matching GV60 HDA1 AOL artifacts 2026-10-31 17:43:02 -05:00
firestar5683 6f88e79025 Support captured GV60 HDA1 Alpha Long AOL 2026-10-31 17:32:21 -05:00
firestar5683 23123aefb4 Keep radar failures from disabling independent lateral steering 2026-10-31 17:25:44 -05:00
firestar5683 ddd67e58bc Reuse exact GM configuration checks in driving hot paths 2026-10-31 17:00:37 -05:00
firestar5683 b6e25bfa6d Match CAN-FD longitudinal parsers to fuel and camera layout 2026-10-31 16:55:25 -05:00
firestar5683 f10a7e229b Preserve stock CAN-FD AOL intent through held-gas recovery 2026-10-31 16:45:21 -05:00
firestar5683 ac318aa31e Fix Prius stop-and-go settings and stopped resume 2026-10-31 16:40:36 -05:00
firestar5683 15134f1ec2 Build Panda host binaries for comma hardware 2026-10-31 16:12:56 -05:00
Dom cb0c1507de test: complete default metadata lookup in remaining preference fixtures
Delegate the isolated Ioniq5PE, Toyota shell and GM pitch mock default lookups to real Params metadata. Preserve existing assertions, no-setting-write checks and runtime behavior.
2026-10-31 15:48:59 -05:00
firestar5683 3926f11699 Avoid unnecessary AOL recovery profile validation 2026-10-31 15:46:58 -05:00
firestar5683 8a760a3381 Scope ordinary Ford native lookup to configured Ford cars 2026-10-31 14:54:27 -05:00
firestar5683 58c38a4834 Honor default AOL buttons on classic HKG 2026-10-31 14:52:46 -05:00
Dom eb56047d57 test: supply real defaults and fresh onroad state in isolated fixtures
Delegate mock default lookup to Params metadata and supply the existing healthy UI fixtures with their post-start receive frame and onroad state. Preserve all runtime code, assertions and negative authority coverage.
2026-10-31 14:45:06 -05:00
alan-polk 67ad8ff05b Merge BluePilot Ford lateral library v0.2.1 2026-10-31 13:51:21 -05:00
firestar5683 fdbce123c8 Build matching BluePilot Ford firmware and host binaries 2026-10-31 13:40:02 -05:00
firestar5683 5235f58e07 Keep FLM trial input tapes current between motion deliveries 2026-10-31 13:39:45 -05:00
firestar5683 f26ab5e7f1 Integrate pinned BluePilot defaults and qualify Ford AOL lifecycle 2026-10-31 13:29:29 -05:00
alan-polk 14acf1ed31 Merge BluePilot Ford lateral library v0.2.1 2026-10-31 13:29:27 -05:00
firestar5683 6d2e382e92 Renew FLM from already received motion during FIFO ingest 2026-10-31 13:06:52 -05:00
firestar5683 fcb59fa1da Validate each FLM binding once per witness call 2026-10-31 12:54:03 -05:00
firestar5683 8c404b3b8a Align AOL reset and favorites fixtures with independent controls 2026-10-31 12:23:09 -05:00
firestar5683 3498e84770 Reuse validated immutable FLM source identity content 2026-10-31 12:15:20 -05:00
firestar5683 72f04816c3 Add metric-aware extra stopped-lead spacing 2026-10-31 12:04:00 -05:00
firestar5683 9068ec74f7 Keep quiet-camera HKG discovery on normal wiring 2026-10-31 11:58:04 -05:00
firestar5683 2df9c98963 Keep bookmark shortcuts working while alerts are displayed 2026-10-31 11:49:51 -05:00
firestar5683 70f523edc5 Renew owned FLM trials when admitted queued input becomes ready 2026-10-31 10:46:02 -05:00
firestar5683 c1f37c8314 Renew owned FLM trials when admitted queued input becomes ready 2026-10-31 10:26:39 -05:00
firestar5683 15ed9799cb Renew owned FLM trials when admitted queued input becomes ready 2026-10-31 09:43:22 -05:00
Dom f9dccd0c67 test(ford): return native transmit outcomes to joined angle fixture
Feed exact emitted steering payloads and their accepted/rejected host-bus receipts on the next parser cycle. Preserve all assertions and runtime behavior.
2026-10-31 09:36:31 -05:00
Dom 6a972ae87e test(gm): feed actual steering transmit receipts in healthy fixtures
Keep existing assertions and runtime behavior unchanged. Model healthy transport by returning only actually emitted steering commands; retain missing, stale, wrong-bus and rejected receipt coverage.
2026-10-31 09:25:04 -05:00
firestar5683 5197fe4568 Fix live network label typing and generated catalog lint policy 2026-10-31 08:51:50 -05:00
firestar5683 642d9d657d Record additive engagement receipt enum in schema compatibility review 2026-10-31 08:42:32 -05:00
firestar5683 cd0f46b9d6 Prevent stale lead handoff from reacquiring a clear stopped hold 2026-10-31 08:38:43 -05:00
firestar5683 a6030fa25d Feed native shadow receipts through Ford angle recovery tests 2026-10-31 08:27:09 -05:00
firestar5683 b8200e7bab Revisit pending FLM motion before another evidence drain 2026-10-31 08:19:02 -05:00
firestar5683 f9b678a7c3 Restore HKG AOL with auxiliary Pandas and default buttons 2026-10-31 08:14:06 -05:00
firestar5683 68fca49aab Avoid repeated static Bolt and empty-fault classification in Card 2026-10-31 08:11:52 -05:00
firestar5683 731a9e6c79 Keep Ford angle handovers quiet and preserve gas-override AOL 2026-10-31 08:09:22 -05:00
firestar5683 f506c8f299 Wait for Bolt steering transmit receipts before advancing counters 2026-10-31 08:04:17 -05:00
firestarsdog 6c38692ff7 Preserve SLC decisions to prevent source churn 2026-10-31 06:13:47 -04:00
firestarsdog 5668bafc4c Fix Vision units to prevent inflated metric speed limits 2026-10-31 04:13:19 -04:00
firestarsdog 859145dd8a Use SLC on RES to match SET 2026-10-31 03:41:55 -04:00
Dom 37341d2bca Fix CI type narrowing, fixtures and executable metadata
Preserve SLC validation behavior with explicit None narrowing, use existing Mock test doubles, and restore controlsd executable metadata without content changes. Keep exact torque gain expectations while matching the existing list representation.

Validated 1095 related tests, targeted ty and Ruff; prior baseline comparison and 4236 guard-equivalence cases preserve existing rejection semantics. Refresh the inventory receipt only for these validated changes.
2026-10-31 00:00:28 -05:00
firestarsdog 23a7b93df0 Rebuild Params and cereal for SLC changes 2026-10-31 00:53:56 -04:00
firestarsdog 7bce4121e5 Drop legacy SLC offsets for simpler setup
Use one saved schedule with editable zero defaults. Preserve current schedules and round displayed offsets and edits to whole units without conversion drift.
2026-10-31 00:24:31 -04:00
alan-polk 5c32d95d7b BluePilot lateral library: v0.2.1
Re-vendored at the v0.2.1 release (2b9dc6c), which adds StarPilot as a host.
Since the last copy: the matrix tests only re-key SAFETY_PARAM when the host's
stock base defines it (IQ.Pilot's iqdbc doesn't; StarPilot's does, so no change
here), and a FORKS.md note.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 22:50:58 -05:00
alan-polk a0d80165df Ford AOL: a rejected host actuator frame doesn't hand the bus to the camera
The previous commit let the camera's actuator through again as soon as one host frame was rejected. Mid-steer
that put the camera's inactive frame (its own counter) on the bus in place of the blocked one, and
the F-150's PSCM ramped out and reported lateral control faulty for several seconds, from a single
blocked frame. Without AOL, static blocking makes a rejected frame a missed one, which the PSCM rides
through.

Block the camera's copy while the host is sending, accepted or not; only a host that has stopped
sending for the AOL host timeout gives the actuator back to the camera.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 22:50:58 -05:00
alan-polk 67f4250d7e Ford AOL: one steering actuator sender while the host's frames are accepted
With AOL, the camera's actuator (0x3D3/0x3D6/0x3CA) is forwarded unless AOL owns steering, but the
host sends its own every frame, active or not. When AOL isn't steering, the PSCM hears both, with
unrelated counters. On a 2021 F-150 this flipped LatCtlSte_D_Stat between Unavailable and Available
every 50-100 ms for seconds at a time, worst when the two counters ran 1-2 apart: steerFaultTemporary
alerts, the panda's LKA-ready check denying lateral, and AOL dropping its latch on re-engagement.
BluePilot's angle-mode release during a manual turn made it easy to hit.

Block the camera's actuator while the host's last frame was accepted and is within the AOL host
timeout, as static blocking does without AOL. A rejected or stale host frame lets the camera's
copy through again. Permission and display ownership are unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 22:49:32 -05:00
alan-polk ed3b1c4c73 Settings: BluePilot lateral for Fords
On the Steering page, for every Ford that can run BluePilot lateral (not the Transit), in Galaxy and the
native settings alike:
- BluePilot Lateral (off by default), Primary Control (curvature/angle) and Use Pinion Yaw Sensor (platforms
  with a pinion geometry row) change while parked; the switch and the pinion sensor apply at the next vehicle
  startup, and Safe Mode keeps BluePilot lateral off.
- the tuning rows of the current mode change any time, since the library reads them every frame; rows that
  depend on Lane Positioning or the custom profile wait for it.
Ranges come from the library's manifest and defaults from params_keys.h, so the rows can't drift from what the
controller reads. A change is checked against the row it was made on, so a request from before its switch
changed is refused.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 22:49:32 -05:00
alan-polk 49b651af70 openpilot: BluePilot lateral startup bits, per-frame inputs and settings registry
- params_keys.h: every setting in the library's manifest (python -m opendbc.bluepilot_lateral.params.manifest),
  without BACKUP, which StarPilot's params don't have. disable_BP_lat_UI defaults to "1": BluePilot lateral
  is off until the user turns it on.
- car init (VehicleStartupPreferences.prepare): init_ford_safety_param_starpilot ORs the library's startup
  bits into the Ford param. They stay off without the openpilot toggle, in Safe Mode, or when a startup
  setting can't be read.
- card (configure_controller): the Ford CarController gets a BluePilotInputs, which fills the library's
  LateralInputs from modelV2, vehicleParameters and lateralDelay each frame and re-reads the per-frame
  settings every 3 s.
- test_bluepilot_lateral.py: the init path's bits, the real CarController and libsafety with the same
  param, for a CAN and a CAN FD Ford, off/on/on+pinion/pinion-only, curvature and angle mode: every
  steering and LKA frame passes, and only angle mode with BluePilot on steers through the path angle.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 22:49:32 -05:00
alan-polk 40147f9f74 BluePilot lateral library: Mondeo pinion geometry, platforms without a pinion row
Re-vendored from the library (stamped copy, unedited).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 22:49:32 -05:00
alan-polk bcb0abe576 Ford: hook the BluePilot lateral library into opendbc
- safety/modes/ford.h: includes the library's safety with StarPilot's lateral permission
  (lateral_controls_allowed(), which includes AOL) as its lateral binding. ford_init hands the library
  bits 9-14 of the Ford param (never on the Transit); with the bit set, LateralMotionControl/2 go through
  the library's 4-signal checks instead of the stock ones, Lane_Assist_Data1 carries the angle-mode state,
  and the pinion source replaces yaw and joins the selected RX list (stock, stock-switch or AOL).
- car/ford/carcontroller.py: one BluePilotLateral per Ford; while it's active it sends the steering and
  LKA messages, otherwise the stock path runs unchanged. StarPilot's openpilot side attaches the per-frame
  inputs and settings as bluepilot_inputs.
- safety tests: test.sh runs the library's safety tests by directory and gates the library headers'
  coverage in a run of their own; test_ford_bluepilot_hooks.py covers where the bits ride, the Transit,
  the pinion RX check on every list and the full-list guard. The qualification and mutation runners
  include the library's safety tests.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 22:49:32 -05:00
alan-polk 7b36024f6d Ford safety tests: reset the alternative experience after AOL fixtures
The CAN FD display fixtures and the stock-switch class left alternative_experience at 32. Now that the
stock words are AOL words, a later Ford test could run with AOL configured; they reset it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 22:49:32 -05:00
alan-polk c5ce014573 BluePilot lateral library (stamped copy)
opendbc_repo/opendbc/bluepilot_lateral is BluePilotDev/bluepilot-lateral as released, made by the
library's tools/vendor.sh and checked by its tools/check_vendored.sh (VERSION names the release and
commit). Nothing inside the folder is edited here: changes go to the library, get released, and are
vendored again.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 22:49:32 -05:00
alan-polk 8da0c9d916 Ford: remove the interim BluePilot port; AOL on the stock profiles
StarPilot's angle-only bluepilot_lateral adaptation and the BluePilot-derived curvature strategies (Mach-E,
classic, Explorer, generic CAN FD, new-port curvature, manual-turn latch, curvature preview, pinion
measurement) are removed, with their extended safety profiles 16/32/64 and the interim angle word 128. Ford
lateral is upstream's stock curvature path until the BluePilot lateral library is mounted.

StarPilot's own Ford features stay, re-keyed from the extended words to the stock ones:
- AOL: classic 32 -> 0, Mach-E 18 and the CAN FD trucks 66 -> 2 (+1 with openpilot long); the CAN FD
  retained-display rules apply to word 2. Edge/Mondeo/Transit and the manual bit (256) are unchanged.
- release CAN FD long: word 3 (was 19/67); 19 and 67 are now retired and denied.
- stock cruise switch, CAN FD camera-lead radar handling, hands-free cluster: keyed on the Ford family.
- classic stock ACC (0) keeps the no-ACCDATA TX list the extended classic profile had.

Bits 9-14 of the Ford param are reserved for the BluePilot lateral library's startup bits
(safety/modes/ford_param.h); AOL and pandad mask them out of the profile word.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 22:49:32 -05:00
firestarsdog 2fd0741f56 fix(slc): prevent confirmation prompts when longitudinal control is disengaged 2026-10-30 23:31:59 -04:00
firestar5683 c3d5939ffe Keep Polestar discovery on normal harness wiring 2026-10-30 22:09:43 -05:00
firestar5683 7f8aa3dceb Restore stable effective gain across StarPilot torque controllers 2026-10-30 22:07:21 -05:00
firestarsdog a1fccf70b4 Draw unset SET dash to avoid font fallback 2026-10-30 23:01:32 -04:00
firestarsdog aac227a103 Bind SET engagement to the selected speed limit 2026-10-30 22:53:13 -04:00
firestar5683 0d8c81e294 Renew an owned FLM trial before draining queued evidence 2026-10-30 21:49:47 -05:00
firestarsdog b9744f1cab Cycle shared personality widget on tap 2026-10-30 22:30:42 -04:00
firestarsdog 70b705782c Unify Onroad Long Indicator 2026-10-30 22:28:58 -04:00
firestar5683 efc8527820 Restore measured FLM receiver after queue scheduling regressions 2026-10-30 21:27:10 -05:00
firestar5683 d421abeee1 Yield FLM queues only when actual motion needs service 2026-10-30 21:05:45 -05:00
firestar5683 7da9cea0f6 Yield FLM evidence queues before fresh pose and trial delivery 2026-10-30 20:41:23 -05:00
firestar5683 24b3d17c58 Load live FLM trial validation before the driving loop 2026-10-30 19:51:36 -05:00
firestar5683 64eba05e62 Capture genuine FLM checkpoints before the reconstruction age ceiling 2026-10-30 19:27:50 -05:00
Dom 87736cf206 Use the established sound-pack mock in audio callback test 2026-10-30 19:10:31 -05:00
firestar5683 fb20651274 Retain native-visible FLM experiments through missing-evidence pauses 2026-10-30 19:03:46 -05:00
Dom 84bc481dd0 Align Galaxy test fixtures with current developer controls 2026-10-30 18:58:02 -05:00
firestar5683 e438a1a374 Play one temporary steering warning per AOL fault episode 2026-10-30 18:01:31 -05:00
firestar5683 f7c38a24a8 Show vehicle battery voltage on the Galaxy dashboard 2026-10-30 17:56:44 -05:00
Dom d56266b63b Restore selfdrived executable permission 2026-10-30 17:53:32 -05:00
firestar5683 6984f1da29 Add calibration reset beside driver monitoring in Galaxy 2026-10-30 17:43:17 -05:00
firestar5683 ce6545467b Keep AOL steering active with doors open 2026-10-30 17:36:45 -05:00
firestar5683 1a89d5577a Cover HKG torque steering independence from Alpha Long 2026-10-30 17:26:45 -05:00
firestar5683 39298d97be Remove optional Volt longitudinal tune 2026-10-30 17:21:28 -05:00
firestar5683 d84abc611d Keep door-open AOL pauses free of takeover alerts 2026-10-30 17:19:22 -05:00
firestar5683 60b0f1e826 Reuse exact native primitive validation during FLM input packing 2026-10-30 17:04:36 -05:00
firestar5683 2487154a1e Restore flashed Volt LKAS button mapping 2026-10-30 16:53:57 -05:00
firestar5683 5bedae0794 Refresh FLM producer clock pin for current modeld 2026-10-30 16:44:27 -05:00
Dom 1ab130e4a5 Align updater test fixtures with metadata checks 2026-10-30 16:41:09 -05:00
firestar5683 320e399f98 Keep alternate HKG CAN-FD discovery on normal wiring 2026-10-30 16:29:48 -05:00
firestar5683 1d7e01f9f1 Preserve Mach-E AOL across angle and pinion handovers 2026-10-30 16:26:07 -05:00
firestar5683 37bbd2711d Add toggle defaults reset to Software 2026-10-30 16:12:04 -05:00
Dom d513137550 Repair CI test collection and fixtures 2026-10-30 16:07:17 -05:00
firestar5683 0d32c47b56 Preserve FLM discovery against preselection backlog 2026-10-30 16:05:31 -05:00
firestar5683 6bc374c7d4 Refresh the release inventory after updater source changes 2026-10-30 16:04:39 -05:00
mtawata 867ed373d6 fix fast updater hang; improve version selection list; schedule update install 2026-10-30 13:58:52 -07:00
firestar5683 0fd2353c55 Publish the intended common archive as an ordinary Git blob 2026-10-30 15:37:32 -05:00
firestar5683 21f675eae0 Validate each FLM control graph once during probe preparation 2026-10-30 15:32:49 -05:00
firestar5683 f4dde57839 Keep FLM input reception responsive and numerical work off Controls 2026-10-30 15:09:04 -05:00
firestar5683 b2604f4b8a Ship the executable Params library 2026-10-30 13:23:44 -05:00
firestar5683 ebe7f28aae Update from the live installed Git revision 2026-10-30 13:18:48 -05:00
firestar5683 bf0243f41b Build matching Outback MAIN lateral firmware 2026-10-30 13:15:36 -05:00
firestar5683 da0d0d204b Restore Toyota stop-and-go assistance setting 2026-10-30 13:09:55 -05:00
firestar5683 e9e7481416 Offer both controllers on every torque-tuned vehicle 2026-10-30 13:08:26 -05:00
firestar5683 0fcf127704 Arm Outback lateral on physical cruise MAIN 2026-10-30 13:07:30 -05:00
Dom f7b85976a5 Align lane-centering test fixtures 2026-10-30 13:00:19 -05:00
James Vecellio-Grant 2b3e186822 controls: Blendful lateral policy (alpha) (#2073) 2026-10-30 12:40:57 -05:00
firestar5683 8daabf71f9 Keep live plots refresh cadence and layout steady 2026-10-30 12:02:00 -05:00
firestar5683 1fcfefc977 Show classic HKG AOL icons before cruise engagement 2026-10-30 11:59:48 -05:00
firestar5683 01529d935d Keep FLM numerical preparation within its input budget 2026-10-30 11:27:24 -05:00
firestar5683 ba0e051293 Fix language font loading on device 2026-10-30 11:10:30 -05:00
firestar5683 bfca5476e6 Preserve manager executable permission 2026-10-30 11:07:21 -05:00
firestar5683 bfa9427dbd Fix typed lane centering default enrollment 2026-10-30 11:06:09 -05:00
Dom 02588a19a2 Use typed localization test resources and fixtures 2026-10-30 10:52:25 -05:00
firestar5683 f4ec1c187e Share corroborated CEM lead departure across vehicle configurations 2026-10-30 10:48:59 -05:00
Dom bc494bd7d5 Restore manager executable mode 2026-10-30 10:43:58 -05:00
firestar5683 0b94e10fd9 Preserve Toyota stop engagement while the brake is held 2026-10-30 10:39:14 -05:00
firestar5683 f09bf1fc28 Reuse validated immutable FLM input metadata 2026-10-30 10:34:03 -05:00
firestar5683 de00bb2f11 Preserve missing lane centering preferences on upgrades 2026-10-30 10:28:21 -05:00
firestar5683 7acb954a6d Translate Galaxy and native UI through the shared language setting 2026-10-30 10:27:37 -05:00
firestar5683 5e057b9848 Share display translations across Galaxy and native UI 2026-10-30 10:27:37 -05:00
firestar5683 4fcf11f07a Enable lane centering by default on new installs 2026-10-30 10:22:47 -05:00
Dom 6120b36217 Register existing blindspot setting descriptions 2026-10-30 09:59:21 -05:00
Dom 5c662d9e43 Keep sound selection tests stable with blindspot settings 2026-10-30 09:53:20 -05:00
Dom b64d7cf9e9 Share banked Hyundai CAN-FD RX workspace to recover Panda RAM 2026-10-30 09:07:01 -05:00
firestar5683 18432adb3d Build blindspot alert 2026-10-30 08:41:03 -05:00
firestar5683 fae3f5ccfc Blindspot alert 2026-10-30 08:37:21 -05:00
firestar5683 8fdee1d44e Remove redundant wheel control default buttons 2026-10-30 08:19:47 -05:00
firestar5683 b3b96adecc Preserve Palisade AOL through camera echo races and braking 2026-10-30 07:48:14 -05:00
firestar5683 df9898673e Stabilize GM startup test collection across CI shards 2026-10-30 03:34:54 -05:00
firestar5683 66beca6de7 Avoid overlapping subscriptions for the flashed Volt parser 2026-10-30 02:40:34 -05:00
Dom 717a82245f Account for Galaxy-only accelerator preference in page parity test 2026-10-30 02:23:15 -05:00
firestar5683 d955ebd7a2 Keep schema and caller checks aligned with reviewed source contracts 2026-10-30 02:22:25 -05:00
firestar5683 2cca20624c Finalize the admitted Sportage long AOL startup profile 2026-10-30 02:12:43 -05:00
firestar5683 7db42dd5d6 Distinguish native-rejected Ford UI from transmitted display frames 2026-10-30 01:49:30 -05:00
firestarsdog eff809d839 Add DisengageOnAccelerator to Galaxy 2026-10-30 02:41:22 -04:00
Dom b3a4b964c2 Use typed Quick Select test fixtures
Retain real composition and input-routing methods with typed drawing boundaries.
Preserve existing assertions and validate optional settings requests explicitly.
2026-10-30 01:40:41 -05:00
firestarsdog 0114eb04bb Add compact ui DisengageOnAccelerator 2026-10-30 02:17:36 -04:00
firestarsdog 890c30c3cb Rebuild Params and cereal for SLC lookahead 2026-10-30 02:13:07 -04:00
firestar5683 7e00ee80c4 Align regression and settings coverage with current AOL contracts 2026-10-30 01:05:49 -05:00
firestarsdog e00fb66bc8 Firefox Arabesque Updater 2026-10-30 01:49:51 -04:00
firestarsdog 0d4422552b Large UI: replace radial Quick Select with a stable overlay 2026-10-30 01:43:43 -04:00
firestar5683 c7f3de2416 Clarify speed-limit type validation and settings test contracts 2026-10-30 00:28:07 -05:00
firestar5683 4ea9adbdb9 Order Ford standby display after inactive steering proof 2026-10-30 00:21:06 -05:00
firestar5683 9e7e4dde52 Keep factory wheel roles explicit and restore CI source hygiene 2026-10-30 00:16:33 -05:00
firestarsdog a8bc9d1f24 Merge remote-tracking branch 'origin/Dom' into Dom 2026-10-30 01:10:22 -04:00
firestarsdog 63395a56f1 SLC: add timed adoption of upcoming limits 2026-10-30 01:03:30 -04:00
firestar5683 0a786d37e3 Show factory AOL roles and fix wheel controls safety qualification 2026-10-30 00:01:17 -05:00
firestarsdog 6fe76b728b Mapd: add lookahead for early speed-limit adoption 2026-10-30 00:41:31 -04:00
Dom c65296f6df Initialize native settings localization test fixture
Share isolated real Params with the localization owner and initialize the RHD row.
Preserve existing saved-state and parked-state assertions.
2026-10-29 23:05:03 -05:00
firestar5683 008687eb87 Build Sportage AOL and shared wheel controls fixes 2026-10-29 23:03:49 -05:00
firestar5683 dc7ff90070 Restore Sportage AOL and show effective wheel controls 2026-10-29 22:46:29 -05:00
firestar5683 c6ef6b138a Avoid duplicate native packet validation in FLM 2026-10-29 22:39:28 -05:00
Dom 56c997a284 Restore controlsd executable mode
Preserve the source blob while restoring the script's executable bit.
Refresh the source inventory receipt for the mode-only correction.
2026-10-29 22:22:17 -05:00
firestar5683 f1f8f5696e Use Metric 2026-10-29 22:14:25 -05:00
firestar5683 16f31abd75 RHD Fix 2026-10-29 22:13:23 -05:00
firestar5683 77babc4c85 Release an existing Force Stop hold with RES 2026-10-29 22:10:01 -05:00
firestar5683 d1c2bc14a4 Localize steering command publication stalls 2026-10-29 21:57:08 -05:00
firestar5683 86315e76cb Restore flashed Volt OBD alpha longitudinal profile 2026-10-29 21:55:21 -05:00
firestar5683 52347a3593 Preserve AOL intent through temporary steering overrides 2026-10-29 21:40:46 -05:00
firestar5683 0933e58afc Keep AOL border lit while its latch is armed 2026-10-29 21:30:07 -05:00
firestar5683 cc125cf1eb Preserve Toyota cruise during accelerator override 2026-10-29 21:15:56 -05:00
firestar5683 c073497d44 Recognize short deliberate bookmark swipes 2026-10-29 21:14:56 -05:00
Dom b817c3c26e Fix Subaru init parameter MISRA compliance
Normalize a same-type local copy instead of modifying the input parameter.
Existing safety logic and all 34 paired firmware artifacts remain byte-identical.
Validated 67 debug and 67 release native cases; refresh source inventory receipt.
2026-10-29 20:40:23 -05:00
Dom 075d5c0ffe Cover Impreza MAIN words in the exhaustive AOL protocol contract 2026-10-29 19:41:11 -05:00
Dom 1f1b1d5db9 Match persisted language and bounded proof wait test fixtures 2026-10-29 19:24:19 -05:00
firestar5683 4dd24beabc Separate Volt CC steering minimum from cruise engagement 2026-10-29 19:19:20 -05:00
firestar5683 ad7bfa4899 Build matching 2020 Impreza MAIN lateral artifacts 2026-10-29 19:11:18 -05:00
firestar5683 5910ddb754 Arm 2020 Impreza lateral on physical cruise MAIN 2026-10-29 19:09:50 -05:00
firestar5683 21a3921b16 Keep GM steering active through axis proof delivery races 2026-10-29 19:07:31 -05:00
firestar5683 33e87cea5a Recognize standard HDA2 wiring before diagnostic queries 2026-10-29 18:59:39 -05:00
firestar5683 e3fbbdddb9 Add language selection to Galaxy localization 2026-10-29 18:42:16 -05:00
Dom 76d33aaa81 Keep native test fixtures aligned with current lifecycle contracts 2026-10-29 18:26:08 -05:00
Dom ff62737ac4 Align GM interface fixtures with registered identities and Gen2 Drive 2026-10-29 17:43:51 -05:00
Dom 87b8b2ada5 Cover Polestar receiver counter wraps and fail-closed faults 2026-10-29 17:19:31 -05:00
Dom b43db836a5 Label the non-pedal Bolt camera profile as ACC 2026-10-29 17:17:33 -05:00
firestar5683 1b9bb0cd51 Keep FLM snapshot preparation below live capture priority 2026-10-29 17:03:56 -05:00
Dom c3de3defc3 Keep parked power tests in their own collected module 2026-10-29 17:03:45 -05:00
Dom 268370640d Type Jetlink test parameters for model selections 2026-10-29 17:00:01 -05:00
firestar5683 0d795930ff Keep the FLM live receiver at normal CPU priority 2026-10-29 16:58:34 -05:00
firestar5683 ec5739441e Expose parked shutdown settings in Galaxy 2026-10-29 16:58:07 -05:00
Dom e94198f98e Clarify Subaru MAIN observer for MISRA without changing output 2026-10-29 16:41:26 -05:00
Zeph Leggett 5f8638e894 Jetlink: Active Big chooses Jetlink's model by its ONNX 2026-10-29 16:38:54 -05:00
firestar5683 98d2865a29 Publish the Controls scheduling repair for FLM 2026-10-29 16:28:51 -05:00
firestar5683 0e55c02b28 Prioritize Controls CPU time during FLM learning 2026-10-29 16:28:50 -05:00
Dom 3d7df63099 Normalize continuation indentation within source size limits 2026-10-29 16:26:33 -05:00
firestar5683 47e4855580 Build Polestar counter correction firmware 2026-10-29 16:18:37 -05:00
firestar5683 b33e4a071b Correct Polestar LCA counters at modulo-15 wraps 2026-10-29 16:17:07 -05:00
firestar5683 bc3dce2c0c Keep manager shutdown nonblocking and preserve paused AOL intent 2026-10-29 16:12:56 -05:00
Dom ed5e9e56f2 Assert registered Trax layout without changing stock control contracts 2026-10-29 16:07:54 -05:00
dirwin31 30625319b3 Improve Android Auto reconnection and report screen resolution to Bugsink 2026-10-29 16:06:11 -05:00
Zeph Leggett 51e866a038 Jetlink: Active Big chooses Jetlink's model by its ONNX
With Jetlink running the big model (on, supported, no Chestnut), Active Big
in Galaxy and on the device is Jetlink's choice: the model manager marks a
big model selectable when its catalog entry names the ONNX it was built from
(source_sha256), downloaded or not, since Jetlink's host fetches the ONNX
itself. The choice is the new JetlinkModel param, {sha256, displayName}, the
adapter's big-model key; Chestnut's own Active Big is left as it was. None
leaves the model to the Jetlink app, and the manager words it.

A newer choice made in the Jetlink app is written back into JetlinkModel by
jetlink, so Active Big shows the model that drives; one StarPilot does not
list is named as the app's.

JetlinkModel is a new key in params_keys.h: the native params libraries need
their usual rebuild.
2026-10-29 16:03:03 -05:00
Zeph Leggett 21c34c8f4d Jetlink v0.8.12 2026-10-29 16:03:03 -05:00
firestar5683 2800e5b452 Keep unarmed startup steering restrictions from displaying takeover warnings 2026-10-29 15:49:53 -05:00
firestar5683 85997f719f Quiet incomplete drive history and recover full logs 2026-10-29 15:42:32 -05:00
firestar5683 138da851ee Guard two-line centering against conflicting corrections 2026-10-29 15:37:16 -05:00
dirwin31 556f90458e Merge branch 'Dom' into AA-AutoConnectFix-Dom 2026-10-29 13:11:06 -07:00
firestar5683 e2fdc2a4dc Remember unchanged model verification across restarts 2026-10-29 14:59:31 -05:00
firestar5683 312eb9e94d Recover model verification after a worker failure 2026-10-29 14:51:14 -05:00
firestar5683 37fc53c48a Explain community Polaris models in Galaxy 2026-10-29 14:48:32 -05:00
firestar5683 efbe7a776f Materialize matching Panda host object files 2026-10-29 14:48:03 -05:00
firestar5683 7c5e70fd13 Build matching Ford standby-handover firmware 2026-10-29 14:37:09 -05:00
firestar5683 06a74465c0 Keep Ford standby display through the final steering packet 2026-10-29 14:34:35 -05:00
firestar5683 42057a3f8f Limit low-speed steering advisories to engaged or armed steering 2026-10-29 14:33:29 -05:00
firestar5683 d85372232f Animate Polaris highlights and preserve emoji colors 2026-10-29 14:30:06 -05:00
firestar5683 a952a8f8a8 Keep Mach-E native LONG withdrawal separate from manual steering 2026-10-29 14:27:37 -05:00
firestar5683 0826f3b22d Highlight custom Polaris models 2026-10-29 14:25:17 -05:00
firestar5683 58fb8e820b Identify the 2025 Trax from its CAN layout and VIN 2026-10-29 14:21:21 -05:00
firestar5683 38e14008b9 Build Impreza physical MAIN lateral firmware 2026-10-29 13:41:17 -05:00
firestar5683 4c015f6f49 Arm Impreza lateral on physical MAIN and show stock ACC engagement 2026-10-29 13:39:31 -05:00
firestar5683 256628e50b Keep Ford AOL through Cancel during accelerator override 2026-10-29 13:07:02 -05:00
firestar5683 fa25ea0567 Publish native permission for the bypassed-camera Volt configuration 2026-10-29 12:51:00 -05:00
Dom 99f52a7048 Align Bolt fixtures with Drive feeds and explicit ACC faults 2026-10-29 12:44:53 -05:00
firestar5683 eb2c90b80e Refresh queued GM permission during combined control 2026-10-29 12:37:07 -05:00
firestar5683 53b82a1726 Put Cruise Intervals In Wheel Controls 2026-10-29 12:31:54 -05:00
firestar5683 3b04ff3cc4 Keep downloaded models selectable offline 2026-10-29 12:27:56 -05:00
firestar5683 6e11c50dfc Build jacobw Subaru angle adapter firmware 2026-10-29 12:08:10 -05:00
firestar5683 34ae63cbf6 Adapt jacobw Subaru angle control to StarPilot 2026-10-29 12:06:38 -05:00
firestar5683 1f79ca865f Recognize Outback Wilderness camera firmware and credit jacobw Subaru support 2026-10-29 11:54:05 -05:00
Dom 87cb8f9a63 Assert exact four-byte Toyota button capability in variant parity 2026-10-29 11:52:04 -05:00
firestar5683 21b9fd8136 Build Gen2 Bolt pedal firmware 2026-10-29 11:49:58 -05:00
firestar5683 d317185c00 Allow Drive for Gen2 Bolt pedal 2026-10-29 11:48:24 -05:00
firestar5683 f47452c4b1 Restore radar-filter Toyota distance buttons and wheel mapping 2026-10-29 11:23:58 -05:00
firestar5683 6e124d1e45 Keep inactive ACC traffic flowing for every qualified GM owner 2026-10-29 11:17:06 -05:00
firestar5683 50b4da94ba Maintain inactive ACC streams during ordinary SDGM startup 2026-10-29 11:00:17 -05:00
firestar5683 66bf42ac1e Keep Bolt pedal control available after secondary brake loss 2026-10-29 10:55:11 -05:00
firestar5683 8aa35ed00f Report verified models in usage statistics 2026-10-29 10:51:59 -05:00
firestar5683 6639a95904 Keep lane centering responsive with fresh inputs and continuous tracking 2026-10-29 10:22:45 -05:00
dirwin31 2b069cffc2 Build 2026-10-29 06:43:30 -07:00
dirwin31 ac97f5f447 Add supported Message 2026-10-29 06:31:46 -07:00
dirwin31 e7b53f3099 Bugsink Screen Resolutions 2026-10-29 06:21:31 -07:00
dirwin31 6418bfd5b7 Let the car connect first and clear stuck Android Auto Bluetooth links
At ignition the head unit pages the comma for hands-free at the same moment
the supervisor paged the car. The crossed connections can leave the
controller holding a link the 4.9 kernel no longer knows ("ACL packet for
unknown connection handle 7"), after which every SDP/RFCOMM connect to the
car fails with EALREADY and the session retries the same sequence until the
car gives up.

- Ignition-triggered sessions wait up to 8 s for the car's own hands-free
  link before paging it.
- EALREADY on SDP or the cached RFCOMM channel fails the attempt at once,
  keeps the cached channel, drops the stray handle with a raw HCI Disconnect
  (only when BlueZ reports the car disconnected and the kernel does not list
  that handle), and lets the car connect first on the retry.
2026-10-29 06:21:10 -07:00
Dom 6d87ba090c Assert retired SLC fallback stays inactive after source loss 2026-10-29 03:12:15 -05:00
Dom 3fb884b5ba Follow renamed conditional planner integration fixture 2026-10-29 02:27:25 -05:00
firestarsdog aa99e3b6ca Build for SLC Refinement 2026-10-29 03:04:02 -04:00
firestarsdog ad397f89cc Retire SLC fallback to unify behavior 2026-10-29 02:57:30 -04:00
Dom 4e3775fa06 Refresh source inventory after speed-limit UI cleanup 2026-10-29 01:36:13 -05:00
firestarsdog 01416612a1 Remove "Use X Speed" 2026-10-29 02:16:51 -04:00
firestar5683 8fcd77c953 Recover FLM learning windows and deliver current trial evidence 2026-10-29 00:44:04 -05:00
firestar5683 9e002886f9 Build matching Ford cluster-handover firmware 2026-10-29 00:15:33 -05:00
firestar5683 1f9825e594 Keep Ford cluster standby through driver handover 2026-10-29 00:11:34 -05:00
firestar5683 4a82e92c73 Avoid repeated single-line lane-centering acquisition stalls 2026-10-29 00:09:26 -05:00
firestarsdog 507e464c59 Arabesque Firefox Character Fix 2026-10-29 00:32:05 -04:00
firestar5683 b6b548fc9a Admit fresh Toyota lateral input with cruise already off 2026-10-28 23:17:50 -05:00
firestar5683 434c89ce71 Normalize positive model-origin roundoff for stopped release 2026-10-28 22:57:27 -05:00
firestar5683 50781a9736 Fit StarPort torque widgets to square head-unit layouts 2026-10-28 22:37:17 -05:00
firestar5683 31b4abe4c6 Keep camera ACC messages flowing throughout GM startup 2026-10-28 22:28:52 -05:00
firestar5683 cdc97b58df Recognize HKG HDA1 wiring before diagnostic queries 2026-10-28 22:10:42 -05:00
Dom 4a3a989b3c Publish exact Panda bootstub bytes as ordinary Git blobs 2026-10-28 21:48:37 -05:00
firestar5683 b75d9f8baf Build repaired Panda CAN power-save firmware 2026-10-28 21:37:45 -05:00
firestar5683 b12ed577c0 Preserve stock Hyundai AOL across fresh input transitions 2026-10-28 21:34:28 -05:00
firestar5683 2567a32f70 Preserve Panda power-save state across CAN reinitialization 2026-10-28 21:29:24 -05:00
firestar5683 639866e6f7 Keep Hyundai brake withdrawal guard within source budget 2026-10-28 21:08:08 -05:00
firestar5683 ccdddea522 Preserve classic Hyundai AOL through brake withdrawal 2026-10-28 21:02:30 -05:00
Dom b5f1b8a8e3 Complete ready-state fixture for CAN clock contract 2026-10-28 20:45:58 -05:00
firestar5683 14835e1b42 Ignore logging-only Honda EPS diagnostic responses 2026-10-28 20:21:19 -05:00
firestar5683 f1bc3a8c5b Restore legacy USB Panda bootstub identification 2026-10-28 20:20:40 -05:00
Dom cbeaffba9a Refresh audited GPU capability fixture fingerprints 2026-10-28 20:11:42 -05:00
firestar5683 6537a58bd6 Avoid repeated Toyota parent admission projections 2026-10-28 19:52:55 -05:00
firestar5683 5a12f032ed Reuse exact GM steering profile qualification 2026-10-28 19:48:36 -05:00
firestar5683 094c9ae84e Reuse MEB static eligibility within each longitudinal update 2026-10-28 19:47:36 -05:00
firestar5683 83d4bf30a8 Reuse constant Chrysler AOL qualification masks 2026-10-28 19:46:32 -05:00
firestar5683 fdaaae86b0 Share exact static configuration cache across vehicle makes 2026-10-28 19:44:05 -05:00
firestar5683 8995ddb0df Build Toyota configuration overhead fix 2026-10-28 19:27:04 -05:00
firestar5683 390438432e Reduce Toyota pedal runtime configuration overhead 2026-10-28 19:25:58 -05:00
Dom 26ab9745af Align startup fixtures with GM neutral continuity contract 2026-10-28 19:03:46 -05:00
firestar5683 d8ccb7fd68 Honor StarPort head unit display insets 2026-10-28 18:59:25 -05:00
Dom 03feefed84 Preserve Toyota behavior with single-exit safety helpers 2026-10-28 18:20:31 -05:00
firestar5683 1fa29f6c33 Reverse Proxy 2026-10-28 18:07:26 -05:00
firestar5683 34dfc2e1b5 Build Toyota and Lexus pedal camera-copy parity 2026-10-28 18:04:12 -05:00
firestar5683 5309a0d6bc Preserve Toyota and Lexus primary pedal ownership with camera copies 2026-10-28 18:03:06 -05:00
firestar5683 3c770dc283 Build legacy Prius DSU and pedal receiver port 2026-10-28 17:52:38 -05:00
firestar5683 265ca6c17d Merge current Dom checks for legacy Prius receiver 2026-10-28 17:51:29 -05:00
firestar5683 04b5dd0ad5 Port legacy Prius retrofit DSU and pedal receiver from StarPilot 2026-10-28 17:51:23 -05:00
firestar5683 cdef286dbf Cover HKG stop permissions across native policy families 2026-10-28 17:37:03 -05:00
firestar5683 41692084c9 Build Prius retrofit native DSU mirror fix 2026-10-28 17:31:22 -05:00
firestar5683 dbba95d7fb Keep Prius retrofit stock control when native DSU traffic is mirrored 2026-10-28 17:30:07 -05:00
Dom fee87a4576 Keep transport pause comments within source size budget 2026-10-28 17:22:40 -05:00
firestar5683 df8e234ff6 Preserve CCNC AOL intent through full-engagement stop pauses 2026-10-28 17:05:42 -05:00
firestar5683 ba2275363f Maintain neutral steering throughout GM startup 2026-10-28 16:42:50 -05:00
Dom a4e552c7a3 Classify the compressed Ford regression fixture as binary 2026-10-28 15:36:55 -05:00
firestar5683 65ac3bccf6 Restore Dom predictive stopping across HKG 2026-10-28 15:35:36 -05:00
firestar5683 3e7a3dded8 Release corroborated Mach-E lead departure from CEM standstill hold 2026-10-28 15:25:31 -05:00
Dom 62dd77d6bb Keep transport intent documentation within the text size limit 2026-10-28 15:02:57 -05:00
firestar5683 7a7b6d2208 Identify blocking work inside Controls overruns 2026-10-28 14:58:41 -05:00
firestar5683 b8aa3b9eca Arm Ford Alpha AOL on the first physical MAIN edge 2026-10-28 14:41:17 -05:00
firestar5683 b0e93e304f Retain Ford AOL across first gas-held native grant 2026-10-28 14:29:26 -05:00
firestar5683 3baf4f7c82 Skip unrelated GM steering checks on other vehicles 2026-10-28 14:23:29 -05:00
firestar5683 621c8d31e3 Restore Stinger forecast stop intent with the smooth planner 2026-10-28 14:20:00 -05:00
firestar5683 e095ab6b71 Retain real FLM source packets for bounded frequency accounting 2026-10-28 14:01:27 -05:00
firestar5683 e0fc923875 Keep audio callbacks off protected driving cores 2026-10-28 13:56:22 -05:00
firestar5683 4848960966 Finish GM discovery after an early Ford signature rejection 2026-10-28 13:54:55 -05:00
firestar5683 2e70c76990 Patch native audio fixture attributes without type conflicts 2026-10-28 13:49:31 -05:00
firestar5683 b6bfd182dd Run shared GM discovery regressions in CI 2026-10-28 13:45:13 -05:00
firestar5683 c0ed5f309a Prioritize native audio playback below driving control loops 2026-10-28 13:42:32 -05:00
firestar5683 f2294f575e Keep shared GM discovery on normal CAN wiring 2026-10-28 13:19:53 -05:00
firestar5683 0fcc393380 Pace optional FLM receiver on motion without relaxing evidence guards 2026-10-28 12:33:01 -05:00
Dom 2af7ef756b Refresh audited graphics fixture identities after StarPort rename 2026-10-28 12:01:29 -05:00
firestar5683 db651809df Cover device Params startup state shared with native readers 2026-10-28 11:38:17 -05:00
Dom 39392c37a6 Preserve typed radar projection test overrides 2026-10-28 11:21:11 -05:00
firestar5683 d594c6772a Keep device settings in persistent Params storage 2026-10-28 11:18:25 -05:00
firestar5683 1fcd8b3353 Reduce Controls and Telemetry Parsing Work 2026-10-28 11:14:43 -05:00
Dom 7d9cb9b37b Align discovery and independent button fixtures with current contracts 2026-10-28 11:04:52 -05:00
Dom c55ef78274 Keep settings test documentation within the text size limit 2026-10-28 10:32:21 -05:00
firestar5683 e9b29f139c Reduce Onroad Torque Drawing Overhead 2026-10-28 10:28:03 -05:00
firestar5683 7d3ba7adf9 Avoid Unused Radar Signal Decoding 2026-10-28 10:19:32 -05:00
firestar5683 d2199ddc20 Speed Up Native CAN Signal Extraction 2026-10-28 10:10:39 -05:00
firestar5683 afd33370f6 Measure smaller FLM source experiments within the existing slew limit 2026-10-28 09:59:49 -05:00
firestar5683 b0417bbf61 Keep classic HKG LKAS and MAIN mappings independent 2026-10-28 09:47:49 -05:00
Dom 6617a1675d Refresh settings fixtures and audited StarPort capability identity 2026-10-28 09:38:58 -05:00
firestar5683 9f4fb7dbb4 Preserve FLM evidence during overload and remove unused wakeups 2026-10-28 09:11:23 -05:00
ervteng 316ed6b8b2 kia/stinger: decode 2022+ radar tracks 2026-10-28 09:06:57 -05:00
firestar5683 dbd72abae6 Add Experimental Speed Nudge and Group Driving Personalities 2026-10-28 08:57:26 -05:00
firestar5683 9ade200473 Build Camry filter radar admission fix 2026-10-28 08:56:35 -05:00
firestar5683 c1a00e3ef5 Merge current Dom checks for Camry filter fix 2026-10-28 08:55:20 -05:00
firestar5683 c03f84cf61 Recognize Camry filter with independent cluster radar traffic 2026-10-28 08:54:56 -05:00
Dom 5ce3f02b99 Align Bolt lifecycle fixtures with raw camera payload contract 2026-10-28 08:54:18 -05:00
firestar5683 63243d7e2d Preserve Encouragement Through Acceleration Noise 2026-10-28 08:39:55 -05:00
Dom 7b6d9cb64b Fix StarPort and Firefox static checks 2026-10-28 08:27:36 -05:00
firestar5683 d61cfcb04f Enable stopped-lead CEM by default 2026-10-28 08:23:23 -05:00
firestar5683 c3c7ee985a Identify Bolt variants from VIN and live cruise capability 2026-10-28 08:21:29 -05:00
Dom 51af702b2c Run CI on the Dom development branch 2026-10-28 08:19:35 -05:00
firestar5683 3621ccf9c2 Add Localization Settings and Repair Global Unit Switching 2026-10-28 08:10:00 -05:00
firestarsdog af48674fae Arabesque Firefox v1 2026-10-28 05:08:17 -04:00
Prabhaav Pillai 08fa2bfde2 Build 2026-10-28 02:56:01 -04:00
Prabhaav Pillai 5b2e2dae0f Refactor Android Auto references to StarPort across the codebase 2026-10-28 01:46:55 -04:00
firestarsdog cc184af2b5 Explain braking preset differences 2026-10-28 01:40:54 -04:00
firestarsdog 13a76dfb8e Keep SLC state and confirmations current 2026-10-28 01:09:47 -04:00
ervteng 9736d887b3 kia/stinger: decode 2022+ radar tracks
The 2022+ Kia Stinger has no radar DBC: the radar's object list is sent on
bus 1 as 10 slots x 3 frames (0x238..0x255, 8 bytes, 50 Hz). Decode it by
hand and publish the objects as radar tracks, gated to KIA_STINGER_2022.

- Stinger2022ObjectDecoder pairs the three frames of a slot (same cycle
  counter and object id in all three, completed within 20 ms, the last
  frame consumed so it cannot be reused) and decode_stinger_2022_triplet
  extracts distance, lateral offset, absolute speed and type.
- Ego speed is the WHL_SPD11 4-wheel average on bus 0 times
  wheelSpeedFactor; relative speed is the radar's absolute speed minus it.
- yRel = -lateral_raw (left positive in the car frame). Tracks not seen in
  a cycle are dropped. canError is reported while the parser is invalid or
  no wheel speed has been seen, and objects decoded in such a cycle are
  discarded. The cycle is emitted on the last address, 0x255.
- interface.py: radarUnavailable is False only when all 30 addresses are
  present in the bus 1 fingerprint with length 8.

Two measured corrections to the raw speed:
- A lead braking to a stop is reported at about -0.3 to -0.6 m/s for 1-3 s
  before settling at 0, while the stock SCC11 lead stays near -0.1 (median
  -0.13 over 13 stock-ACC stops, versus -0.45 from the object frames).
  radard's KF turns the recovery into a positive aLeadK for a parked car,
  which the MPC extrapolates as the lead pulling away. Negative speeds
  above -0.7 m/s are reported as 0 for objects within 2 m of centre;
  faster reversals, adjacent-lane and oncoming traffic stay as measured.
- Absolute speed minus the wheel-speed average reads ~0.14 m/s more closing
  than the stock SCC11 lead (ACC_ObjRelSpd) whenever the car is moving, and
  matches it at standstill: the radar's own ego speed runs below the wheel
  speeds. The 0.14 m/s is subtracted from the ego speed, ramped in over the
  first 1 m/s so standstill is unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-27 21:41:04 -07:00
Dom efe875ffd6 Retire completed GM cold-MAIN recovery before a new lateral request
Clear prior recovery wait state only after the existing healthy unlatched baseline is accepted. Preserve native acknowledgment, fault, identity and expiry gates; cover actual Card and native permission transitions.
2026-10-27 22:49:52 -05:00
Dom e53ec2605f Refresh measured custom pytest workload estimates
Preserve every node, grouping rule, shard count and timeout. Calibrate material module estimates from complete hosted outcomes with conservative scheduling margin.
2026-10-27 22:48:33 -05:00
Dom 65660a4d5c Restore selfdrived executable mode
Keep source bytes unchanged and refresh the canonical prebuilt inventory receipt.
2026-10-27 21:56:44 -05:00
Dom c64e7a8852 Bound Mach-E path assistance by the native speed sample window
Keep exact Mach-E19 independent-lateral profile within the existing six-sample native path limit. Preserve ordinary curvature limits, other modes, fault checks and firmware.
2026-10-27 21:37:48 -05:00
Dom f11f2357d8 Preserve bounded Mach-E lateral proof across held-brake source gaps
Reuse the existing original proof deadline for qualified Mach-E safety19 only. Require fresh independent authority for successors and retain fault, cancel, identity and expiry checks.
2026-10-27 21:28:40 -05:00
Dom b57687cda8 Keep ordinary Mach-E pedal withdrawal with its existing mismatch owner
Withdraw native-denied LONG immediately while retaining only an already proven LAT grant. Preserve fault checks, original proof deadlines and ordinary mismatch counter.
2026-10-27 21:24:22 -05:00
Dom 65d02d27a1 Restore qualified stock-SCC Elantra IRQ diagnostic parity
Classify only the recorded firmware-identified Elantra2021 CAN1 IRQ
bit as diagnostic. Preserve raw reports and native permission, request,
receive health, relay, heartbeat and freshness checks. Alpha-long and
all other configurations remain excluded.

Retain expanded caller/mixed-fault/configuration regression coverage.
Validated 257 focused Elantra and GM/Bolt tests; Ruff clean.
2026-10-27 20:56:10 -05:00
Dom 2dfa4b6fe1 Restore qualified Bolt IRQ diagnostic compatibility
Revert the Bolt-specific admission change from 4b754298 while retaining
native authority, receive-state, relay, heartbeat and actuator checks.
Keep raw diagnostic reports and all other profile policies unchanged.

Add actual Card coverage and restore exact diagnostic policy regressions.
2026-10-27 20:36:13 -05:00
firestarsdog d436c06f99 slc 2026-10-27 21:19:38 -04:00
Dom 05d14fc86e Complete Ford test identities and pinion setting description 2026-10-27 18:50:26 -05:00
Dom e4ad4faf9f Rebuild Panda firmware for qualified Ford CI source 5f0bda11
Refresh source-stamped debug artifacts and canonical prebuilt inventory.
2026-10-27 18:47:34 -05:00
Dom 5f0bda110a Fix Ford qualification coverage, MISRA declarations and test fixtures
Preserve geometry, steering limits, defaults and fault checks.
2026-10-27 18:30:15 -05:00
Dom a1d8204b25 CI: align FLM nested wire typing and paired drain fixtures 2026-10-27 18:06:31 -05:00
Dom 3e0a3e3d7a Ford: ship source-stamped pinion measurement firmware
Rebuild all distributed Panda variants and bootstubs from 4cbffe53 with verified Arm GNU12.3.Rel1; preserve development signing convention. Native debug and release builds passed. Refresh canonical protected source/binary inventory receipt. Default-off Ford option is init-only; no vehicle or road execution claimed.
2026-10-27 17:29:42 -05:00
Dom 4cbffe53b3 Ford: add opt-in pinion curvature measurement
Adapt BluePilot PR #145 by jrneulight (fe5b019568c8c13fb894dc6d8d779d60985051a8) to the existing exact native profile protocol. Default off; qualified parked setting applies at next startup. Preserve existing larger Mach-E envelope, all yaw/heartbeat/relay health, command limits and shadow freshness checks.

Cloud qualification: 171 host tests and 170 subtests; 80 new native/geometry tests; 153 existing native tests and 9131 subtests; release native tests, pandad protocol inventory and full ARM debug/release builds passed. Rebuilt source-stamped firmware follows before branch publication.
2026-10-27 17:14:17 -05:00
Dom 4b94f415ad FLM: construct evidence fields without recursive dict conversion
Build nested typed evidence rows directly while retaining every original field and validation. Complete serialized bytes match the prior writer on recorded d8 evidence and regression fixtures.

Fresh-head combined qualification: 459 tests passed. Cloud performance measurements do not claim device loop-rate recovery or a successful learning trial.
2026-10-27 17:01:45 -05:00
Dom 487dfc4bf4 FLM: preserve fresh evidence across paired queue drains
Merge bounded control/output FIFO heads using original publisher headers. Preserve original age, native, source, overflow and malformed-input checks; retain producer order after joins.

Validated with actual pinned cloud dependencies and fresh-head regression tests.
2026-10-27 17:01:24 -05:00
Dom baddf7a59d Align GM fast-path test oracle with restored Bolt fault veto 2026-10-27 16:42:56 -05:00
Dom a2160580a4 Signal test worker readiness after installing termination handler 2026-10-27 16:08:27 -05:00
Dom 4b754298e7 Reject Bolt CAN interrupt faults in AOL admission 2026-10-27 16:05:51 -05:00
Dom cc5b8b672c Hyundai: keep qualified Elantra AOL steering icons visible
Restore the existing Dom lateral-active dashboard icon behavior only for
qualified Elantra 2021 classic-long AOL profiles. Preserve stock-profile
handling, disengagement blinking, warning fields and all actuator/SCC/FCA
packets. Add native-permission and packet-equivalence regression coverage.

Refresh the canonical protected-source receipt.
2026-10-27 15:36:54 -05:00
Dom 9e5bc45ff7 Fix Ford angle shadow publication and bounded restart 2026-10-27 15:35:46 -05:00
Dom 44dfd7493f Handle absent AOL latch in Subaru test diagnostics 2026-10-27 15:27:42 -05:00
Dom 0422970f2d Compress Ford radar fixture without changing decoded samples 2026-10-27 15:05:25 -05:00
prabhaavp 3b67457f9a Revise privacy policy for Galaxy: StarPilot apps
Updated privacy policy to clarify data handling for Galaxy: StarPilot Companion and Galaxy: StarPilot Auto.
2026-10-27 15:52:21 -04:00
Dom 16d118c4a3 Keep automatic mirrored GM pedal discovery on normal CAN wiring
Scope live VIN and firmware queries to bus0 only when the passive pedal
layout and configured/effective GM query topology agree. Preserve normal
CAN fingerprinting, control configuration, and all Panda fault vetoes.

Regression covers recorded Bolt startup layout, real ISO-TP discovery,
negative topology cases, and unchanged pedal safety configuration.
2026-10-27 14:41:11 -05:00
firestar5683 b5807a459d Restore Mach-E stopped-lead braking on rolling approaches 2026-10-27 13:45:34 -05:00
firestar5683 43c5c1538a Rebind Unchanged Map Graphics Capability Guards 2026-10-27 13:41:13 -05:00
firestar5683 6294915b59 Compress Ford Stop Fixture Without Changing Samples 2026-10-27 13:33:58 -05:00
firestar5683 1454919bfe Preserve Subaru AOL intent across native-first hard stops 2026-10-27 13:23:12 -05:00
firestar5683 c1df455977 Retain established Ford model stops through stationary noise 2026-10-27 13:10:51 -05:00
firestar5683 ac9cb41002 Use Strictly Expired Ford Proof in Deadline Refusal Test 2026-10-27 12:57:32 -05:00
firestar5683 c9734821f9 Resynchronize Ford angle only after steering rejections 2026-10-27 12:51:32 -05:00
firestar5683 e1e02b3949 Preserve Ford lateral intent when cancelling cruise 2026-10-27 12:41:47 -05:00
firestar5683 6a029b3faa Avoid Device Artifacts in Documentation CI Checkout 2026-10-27 12:32:56 -05:00
firestar5683 a30733cf64 Retain Ford AOL through native-first brake withdrawal 2026-10-27 12:29:51 -05:00
firestar5683 ff161633bf Complete Galaxy Description and Recovery Coverage 2026-10-27 12:13:27 -05:00
Zeph Leggett cf4301b16e Jetlink: take over on native02e with the small model's warp 2026-10-27 12:10:26 -05:00
firestar5683 4cc02a4a8f Validate Extracted GPU Loader Fixture Callable 2026-10-27 11:12:20 -05:00
firestar5683 8f86a97616 Expose driver monitoring reset in Galaxy 2026-10-27 11:00:04 -05:00
firestar5683 6bad7eb908 Qualify Held Override Source Handoff Through Original Expiry 2026-10-27 10:56:43 -05:00
firestar5683 6effaaa356 Accelerate Chestnut model loading 2026-10-27 10:55:08 -05:00
firestar5683 81f9630470 Align FLM Test Indentation 2026-10-27 10:41:15 -05:00
firestar5683 ec56bb1f3e Align Ford Curvature Oracle with Qualified Mode Handoff 2026-10-27 10:32:42 -05:00
firestar5683 0ae41eacb6 Refresh GM lateral proof at authority selection 2026-10-27 10:13:15 -05:00
firestar5683 dd37cc81e9 Preserve HKG held-brake lateral proof until its original expiry 2026-10-27 10:08:25 -05:00
firestar5683 eec5cde2c5 Restore Dom low-speed vision stop buffer for HKG longitudinal 2026-10-27 10:07:20 -05:00
firestar5683 a6654bf867 Harden Galaxy drive analysis recovery 2026-10-27 10:05:44 -05:00
firestar5683 42c0d6f24a Qualify Toyota Replay Profile and Braking Expectations 2026-10-27 10:02:02 -05:00
firestar5683 860d2559c5 Require native effect in the selected FLM trial direction
Symmetric native probes could qualify a coordinate even when only the opposite direction changed its encoded command. Carry each direction’s measured visibility and physical effect into selection and interrupted-study recovery so a quantized direction cannot borrow the other direction’s evidence.

Keep existing trial bounds, slew, deadlines and independent acceptance criteria. Validate the previous selector failure, full FLM suite and recorded d5 producer/learner replay. Real-drive improvement requires post-update recorded evidence.
2026-10-27 09:53:24 -05:00
firestar5683 1184e06221 Align Ford startup test with stock idle handoff 2026-10-27 09:31:54 -05:00
firestar5683 5fc5709e4d Block Elantra Steering on Panda CAN Interrupt Faults 2026-10-27 09:28:22 -05:00
firestar5683 f63c935029 Build Toyota source deadline continuity fix 2026-10-27 09:24:33 -05:00
firestar5683 c8ae020038 Merge current changes for Toyota source cutoff fix 2026-10-27 09:23:03 -05:00
firestar5683 887401fc58 Preserve unexpired Toyota command proofs across source cutoffs 2026-10-27 09:22:55 -05:00
firestar5683 d60f2d7ada Synchronize Ford curvature mode handoff with active commands 2026-10-27 09:17:24 -05:00
firestar5683 7a2a88c40c Match Ford BluePilot acknowledgements to emitted packets 2026-10-27 08:55:40 -05:00
Dom 3f251eb5ee Account for measured Ford AOL module workload 2026-10-27 04:42:22 -05:00
firestarsdog 5f84b6c763 Unify speed typography for clearer hierarchy 2026-10-27 04:54:30 -04:00
Dom 5b486edff4 Declare exact pandad build outputs for CI provenance 2026-10-27 03:22:05 -05:00
Dom 7dc37f190f Repair Galaxy lint and qualification fixtures after app split 2026-10-27 02:01:37 -05:00
prabhaavp 2571b8d5c4 Merge pull request #257 from dirwin31:AppSplit
Galaxy: Beta group install, app key import and Sentry app alerts
2026-10-27 02:48:02 -04:00
Prabhaav Pillai d0badbe715 Update app IDs and notification handling for Galaxy app 2026-10-27 02:46:51 -04:00
Danny 0c5f83e690 Galaxy: Remove the APK QR code and hide phone internet sharing
The QR code only linked to the removed APK download, so /api/companion/qr.svg is gone and /api/galaxy/qr.svg answers only when Galaxy is paired. Hide the Enable phone internet button, its restart checkbox and the sharing blurb on the Galaxy page.
2026-10-27 02:46:51 -04:00
Danny 7d81b58d52 Companion: Stop shipping the Direct APK
Android users join the Play beta group from Galaxy, so remove the bundled installer, its version file and the workflow that verified them. The comma now reports no shipped app version, so it no longer offers an offline update.
2026-10-27 02:46:51 -04:00
Danny 8f6d643936 Galaxy: Send Android users to the beta group instead of the APK download 2026-10-27 02:46:51 -04:00
Danny d08cd1b08b Galaxy: Add remote keys and app Sentry notifications
Serve the Galaxy app key and its QR code from /api/galaxy/session to direct local connections so the Android app can import the tunnel key on its own. Let the app and the iOS app register for Sentry push alerts.
2026-10-27 02:46:50 -04:00
Prabhaav Pillai 50e60ef156 Add car control sections and update projections in settings for AA support 2026-10-27 02:36:31 -04:00
Dom cb16d27d3d Qualify SLC receipt retention and aligned SET behavior 2026-10-27 01:20:39 -05:00
Dom 6f3e156abd Refresh protected inventory receipt after SLC source update 2026-10-27 00:47:02 -05:00
firestarsdog 6221a89f4b SLC Cleanup 2026-10-27 01:39:37 -04:00
Dom c79315f8d4 Refresh measured Ford custom test workload estimates 2026-10-27 00:14:15 -05:00
Dom 12dab8c31d Calibrate lifecycle workloads and align Honda cruise fixture 2026-10-26 23:24:41 -05:00
firestar5683 03f9a9576e Restore generic Ford curvature cluster ownership 2026-10-26 23:17:19 -05:00
firestar5683 1320712ab4 Pace Ford BluePilot commands from accepted CAN returns 2026-10-26 22:34:06 -05:00
Dom 3758148f83 Mark published Panda ELF objects as binary resources 2026-10-26 22:32:20 -05:00
Dom 6ebdb1b748 Align qualification fixtures and typing with established contracts 2026-10-26 22:13:51 -05:00
firestar5683 7d218d0a4b Restore recorded Elantra diagnostic admission and Panda fault logging 2026-10-26 22:05:26 -05:00
firestar5683 bbbed56e54 Build final Toyota transmit receipt identity fix 2026-10-26 21:59:05 -05:00
firestar5683 34dc3be528 Merge latest steering learning changes for Toyota publication 2026-10-26 21:57:22 -05:00
firestar5683 080d1d3a04 Build Toyota transmit receipt identity fix 2026-10-26 21:57:04 -05:00
firestar5683 6103e0395b Preserve FLM learning windows across unchanged steering repeats
The d5 Ioniq 6 logs repeatedly reset source tracking when Card repeats a command that already arrived on time. Discard only a recent unchanged repeat after the existing native CAN verifier succeeds; add no sample, application time or permission.

Use independently admitted pending CarState brackets to complete original poses without waiting for that command to reach Card. Classify stale control source clocks as transient stale evidence, preserving the original bounded study deadline. Replace Python character loops with equivalent strict text predicates.

Validation: focused regression suite and paced d5 replay with original Card command references; actual controller/CAN trial application and worsening-trial rollback. No timing/acceptance threshold changes or claimed road improvement.
2026-10-26 21:55:26 -05:00
firestar5683 be522b4ccf Merge branch 'SecretGoodStarPilot' of https://github.com/StarPilotHQ/StarPilot into HEAD 2026-10-26 21:54:12 -05:00
firestar5683 9c0cbcd5f4 Ignore Toyota transmit receipts when checking adapter identity 2026-10-26 21:54:11 -05:00
firestarsdog ed2abcc4b9 Torque Bar Optimization + Gradient 2026-10-26 21:55:36 -04:00
firestar5683 eebd74e745 Use an independent target for single-line tracking 2026-10-26 20:13:26 -05:00
firestarsdog 8e4710a4e6 Merge remote-tracking branch 'origin/SecretGoodStarPilot' into SecretGoodStarPilot 2026-10-26 21:00:28 -04:00
firestar5683 0d01b3d818 Restore Subaru AOL steering angle and speed envelope 2026-10-26 19:46:38 -05:00
firestarsdog 1e90f2da72 Speed Limit Confirmations UI Work 2026-10-26 20:38:11 -04:00
firestarsdog 6a07f58ea6 Remove unnecessary 3-second wait from automatic SLC adoption 2026-10-26 20:38:11 -04:00
firestarsdog 632fa39562 Handle Toyota SLC targets within native cruise limits 2026-10-26 20:38:11 -04:00
firestarsdog 2e395aa714 Align shared SLC actions and simplify common flow 2026-10-26 20:38:11 -04:00
Zeph Leggett 8b4902209c Jetlink: move the warp output when the small model loads
The output used to move after the small model's warmup had linked the
warp to its address, which needed the warp to link again by dropping a
private tinygrad cached property. Had that ever stopped working, the
small model's own warp would have kept writing the freed memory.

modeld now asks the adapter's coherent_warp() (Jetlink's make_coherent
with Jetlink on, off legacy9d) and load_verified_model moves the small
model's warp output before warmup(), as Jetlink's own warp moves its
output before its first call. CatalogWarp only reads the answer
(warp_copied): sent from where it is, or copied out, chosen once.
2026-10-26 19:32:00 -05:00
Zeph Leggett 0701c25b14 Jetlink bf4dc94
make_coherent's contract is one rule: move a buffer before the first call of the JIT that writes it. CREDITS.md and JETLINK.md name the new revision.
2026-10-26 19:31:43 -05:00
firestar5683 f6b2c132fb Fix Ford CAN-FD cluster ownership and parked startup warnings 2026-10-26 19:20:54 -05:00
Zeph Leggett f36b39a68b Jetlink: send the warp from coherent memory on a comma
tinygrad maps the warp's QCOM output write-combined, which the CPU reads
uncached: CatalogWarp spent ~2.9 ms a frame copying the frame out, out
of the 46 ms a host has to answer in. It now moves the output to
IO-coherent write-back memory with Jetlink's warp.make_coherent, as
Jetlink's own warp does, and the link sends straight from it (0.22 ms
measured for Jetlink's warp). The warp has run by then, and tinygrad
bakes a held buffer's address in at link time, so its program links
again on the next call. If the kernel refuses the memory, the frame is
copied out as before, and the adapter's log says which.

TinyJit's per-call input checks are left alone: 0.045 ms a call on a
Mac under this tinygrad, not the ~1 ms they cost under Jetlink's.
2026-10-26 19:04:34 -05:00
Zeph Leggett bf09b8eea4 Jetlink e0257f4
warp.make_coherent is public, for an adapter's own warp: it moves a QCOM
output to memory the CPU reads cached, as Jetlink's warp does.
CREDITS.md and JETLINK.md name the new revision.
2026-10-26 19:04:05 -05:00
Zeph Leggett 6e430242e3 Jetlink: the link's warp goes through the runner's warp_frames
CatalogModelState.warp_frames is the warp branch of run(), which run()
and CatalogWarp now both call, so the link enqueues exactly what the JIT
was captured with and reads npy and input_queues live rather than as
they were when it was built. CatalogWarp binds the output once (the
captured JIT returns the same buffer every call), reuses one pair of
warm-up frames for the probe and frees them, and drops their blob
cache entries. model_warp lets a failure raise without logging it
too: Jetlink logs it as it keeps the small model.
2026-10-26 18:50:12 -05:00
dirwin31 6c9ae11f63 Jetlink: send the small model's warp off legacy9d
Jetlink's stock driving warp is a legacy9d pickle, which a native02e
modeld cannot load, so the link never took over there. The adapter's
model_warp (Jetlink's optional hook since 0605e34) now hands Jetlink
the small model's own camera warp (CatalogWarp) on any other runtime:
the same JIT, inputs and blob cache, read into host memory after each
frame, with a probe at load that refuses an output not read live.
Under legacy9d it returns None and Jetlink loads the stock pickle.

From #253, without its edit to jetlink_repo or the hold tolerance
override.
2026-10-26 18:43:20 -05:00
Zeph Leggett db94a2be1a Jetlink 0605e34
The adapter may now supply the warp the link sends (ModelSide.model_warp,
optional; what it returns is checked against interface.FrameWarp before
the frame loop). Without one, or when it returns None, Jetlink loads the
stock driving warp pickle as before. API stays 3.

CREDITS.md and JETLINK.md name the new revision.
2026-10-26 18:41:10 -05:00
firestar5683 c417e793c5 Add Heatdeath Controls Benchmark 2026-10-26 18:28:20 -05:00
firestar5683 718a2c3ddd Keep SpacePilot open across cold setup and interrupted clock reads
Discard a preempted clock pair and try up to three fresh pairs while
retaining the 1 ms accepted span and all existing freshness/resume gates.
Yield after receive-only decoder construction so normal UI collection
refreshes before full physical admission, Offroad request or input.

Recorded d5 clock failure; reproduced timing regressions; 250 focused
native cases, lint/type checks and independent source/evidence review.
No vehicle commands, device deployment or physical cure claim.
2026-10-26 18:17:20 -05:00
firestar5683 76b5f7e512 Avoid rebuilding the favorites catalog on each tap 2026-10-26 17:36:13 -05:00
firestar5683 5e5fa8d9c1 Preserve EV6 AOL on unassigned cruise CANCEL 2026-10-26 17:21:22 -05:00
firestar5683 36111dfdb6 Preserve Subaru AOL intent through stops 2026-10-26 16:52:11 -05:00
prabhaavp 8ead1ab0f9 Merge pull request #252 from dirwin31:AA-Wireless-Setup
Android Auto: improve wireless adapter setup and pairing
2026-10-26 17:44:33 -04:00
Prabhaav Pillai 6ba879ca3b Preserve the full discovery deadline for slow adapters, clear stale receiver selection when switching setup types, reuse the existing UI spinner, and tighten comments and regression tests. 2026-10-26 17:44:18 -04:00
firestar5683 a9af491104 Preserve HKG AOL authorization across button counter rollover 2026-10-26 16:36:50 -05:00
firestar5683 42a918acc2 Keep Encouragement and Gas Override From Stacking 2026-10-26 16:29:29 -05:00
firestar5683 c604521745 Build final Toyota and Lexus configuration port 2026-10-26 16:26:58 -05:00
firestar5683 09d7e2873e Merge current changes for Toyota publication 2026-10-26 16:25:08 -05:00
firestar5683 5189f468d2 Build Toyota and Lexus configuration parity 2026-10-26 16:25:06 -05:00
Dom 40a23df6be Align damping qualification fixtures and preserve MISRA parameter immutability 2026-10-26 16:23:24 -05:00
prabhaavp 7a38780739 Merge pull request #250 from dirwin31:SGSP-Better-Phone-GPS
Preserve usable phone GPS fixes and enforce measurement freshness
2026-10-26 17:22:14 -04:00
firestar5683 5c78ec40d7 Merge current changes for Toyota configuration parity 2026-10-26 16:19:46 -05:00
firestar5683 c6f00f7a4d Complete Toyota and Lexus configuration parity with Dom 2026-10-26 16:19:46 -05:00
firestar5683 a119dfbae0 Encourage Persistent Clear-Road Hesitation 2026-10-26 16:16:35 -05:00
prabhaavp fc6501364b Improve README.md badges and links
Updated badge links in README.md for better visibility.
2026-10-26 16:57:06 -04:00
firestar5683 1fa5818bb3 Refresh StarPilotHQ Project Homepage 2026-10-26 15:47:38 -05:00
firestar5683 ebfea6b3b7 Build SecOC Yaris stock and alpha support 2026-10-26 15:35:00 -05:00
firestar5683 e672bd30c1 Support SecOC Yaris AOL with stock and alpha longitudinal 2026-10-26 15:33:22 -05:00
Danny 4c37c009b7 Better Bugsink Reporting 2026-10-26 13:25:49 -07:00
firestar5683 ba87dd4c70 Build Subaru angle routing fix 2026-10-26 15:23:09 -05:00
Danny a581b01d84 Revert "Android Auto: restore optional car pairing row for wireless adapters"
This reverts commit 44d9c868b2.
2026-10-26 13:20:57 -07:00
firestar5683 63888e0fd8 Restore Subaru angle steering routing and collision warning checks 2026-10-26 15:20:15 -05:00
dirwin31 44d9c868b2 Android Auto: restore optional car pairing row for wireless adapters 2026-10-26 13:06:37 -07:00
firestar5683 e2a5d6ca66 Build RAV4 Prime AOL and sequential gear fix 2026-10-26 15:06:20 -05:00
firestar5683 8f3be8f6fb Enable key-ready RAV4 Prime AOL in sequential gear 2026-10-26 15:05:09 -05:00
dirwin31 b7fcbfee64 Unify Android Auto setup layouts and add themed pairing progress indicators.
Clarify connection stages across wireless, adapter, USB, and tablet options with regression coverage for progress states and responsive layouts.

(cherry picked from commit 489dad0e7733127c7a9654c9a7491d1ab8a554a4)
2026-10-26 13:00:54 -07:00
dirwin31 a420ff55ed AA: Layout Again 2026-10-26 13:00:52 -07:00
dirwin31 54b2f060e7 AA: Put Messages in Pairing Box, Hide Banner 2026-10-26 13:00:51 -07:00
dirwin31 8822e26d5b Android Auto: Wait Longer for Adapters 2026-10-26 13:00:49 -07:00
dirwin31 1d38987d61 AA: Layout Unification 2026-10-26 13:00:48 -07:00
dirwin31 f7fed1776d AA BT: Fix Pairing Window Request from Galaxy 2026-10-26 13:00:47 -07:00
dirwin31 8d2872bed4 Android Auto: Unify Pages 2026-10-26 13:00:45 -07:00
dirwin31 2141786f05 Android Auto: Unblock Pairing Circumstances 2026-10-26 13:00:43 -07:00
dirwin31 2f7d990597 BT: Show Device List Again 2026-10-26 13:00:42 -07:00
dirwin31 49660054f9 Android Auto: Add support for forgetting selected car during auto-connect retries 2026-10-26 13:00:40 -07:00
dirwin31 4c07adab7d UI: Selector Fixes, Less Duplicate for AA Wireless. 2026-10-26 13:00:39 -07:00
dirwin31 6b09da8676 Android Auto: apply workflow fixes
Split Android Auto changes from 08fb4c3001fe95dbbe784af54e09ecc33043a8bd.
2026-10-26 13:00:38 -07:00
dirwin31 8d9115f9e7 Android Auto: Allow choosing wired AA with Jetlink on 2026-10-26 13:00:36 -07:00
dirwin31 2c80f161a2 Android Auto: Retry Bluetooth connects that report unimplemented 2026-10-26 13:00:34 -07:00
dirwin31 2635773393 Android Auto: Send failed session traces to Bugsink 2026-10-26 13:00:33 -07:00
dirwin31 7fea4f939d Android Auto: AAWireless v1 adapter option
Co-authored-by: Peter Nguyen <trung791997@gmail.com>
2026-10-26 13:00:31 -07:00
dirwin31 55a41e9026 AA UI: Fix Blocked Wired 2026-10-26 13:00:30 -07:00
dirwin31 06fefc6768 Android Auto UI: Integrate wired and app settings directly into step 2 2026-10-26 13:00:28 -07:00
dirwin31 dbc8e0ea39 Add car type and profile configuration options for Android Auto setup 2026-10-26 13:00:26 -07:00
firestar5683 0037bdd836 Allow Fast Return to StarPilot 6.x 2026-10-26 14:40:01 -05:00
firestar5683 02ae25b975 Make SpacePilot entry reliable at natural tapping speeds
Count distinct touch releases by their original timestamps, retain taps
during valid Park dwell, and let preparation earn its own admission.
Cancel only the pending C4 startup camera redirect for a freshly parked
open game. All session, physical, alert, control, noOutput, ignition and
input authority checks remain unchanged.

231 focused native cases, original before regressions, current successor
entry/decoder qualification, canonical lint/type checks and independent
review. Host functional evidence; no device changes or headroom claim.
2026-10-26 14:35:25 -05:00
firestar5683 b976cd2e7c Stop Paid Permanent Address Lookups 2026-10-26 14:23:42 -05:00
firestar5683 6c58d58a7a Bind CAN-FD mirror damping to the selected calibration 2026-10-26 14:15:34 -05:00
firestar5683 8098eb454c Limit Steering Counter Repair to Bolt Pedal Profiles 2026-10-26 14:00:50 -05:00
firestar5683 6e88336f3a Trim Repeated AOL Guard Commentary 2026-10-26 13:39:40 -05:00
firestar5683 76c2aba14e Keep Lane Width Calculation Out of NumPy Cold Imports 2026-10-26 13:36:59 -05:00
firestar5683 261e2ca45d Preserve completed FLM windows during inline probe input gaps 2026-10-26 13:31:42 -05:00
firestar5683 d96f6af078 Track GM Steering Acknowledgement Counters 2026-10-26 13:21:55 -05:00
firestar5683 e1818affa2 Preserve Ford AOL through gas-held engagement and steering release 2026-10-26 12:46:43 -05:00
firestar5683 a71a67fd1f Allow Onroad Wi-Fi Settings 2026-10-26 12:37:26 -05:00
firestar5683 4d0246997b Restore lane centering with fresh polled state 2026-10-26 12:36:20 -05:00
firestar5683 d41159e65b Preserve Dom CAN-FD torque damping across longitudinal modes 2026-10-26 12:32:32 -05:00
dirwin31 306afa06a8 AOL: Skip the temporary steering warning while the driver steers 2026-10-26 17:32:23 +00:00
firestar5683 ec25e2330a Consume pending native evidence before FLM collection 2026-10-26 11:41:40 -05:00
Dom 2551c9ce5f lint: make Rebecca shield zip default explicit 2026-10-26 11:26:52 -05:00
firestarsdog cabc7da5b9 Add Rebecca power-ups, Bizarro mode, and scaling difficulty (contributed by Mary) 2026-10-26 12:05:20 -04:00
Dom df021f4e14 test: initialize lane inactivity reason in shared longitudinal fixture 2026-10-26 10:52:03 -05:00
dirwin31 be8cf95de8 AOL: Skip the temporary steering warning while the driver steers
A hard override on the Civic puts the EPS in NO_TORQUE_ALERT_1, which
reads as a temporary steering fault. The armed-AOL warning added
steerTempUnavailableSilent for it every time, even though the driver was
holding the wheel. CarSpecificEvents already stays quiet in that case.
Route 00000060: all 23 status-2 windows cleared before the driver let go.
The warning still shows if the fault is present while the wheel is free.

(cherry picked from commit f9167e2a24007c0822b4eae8870a24100a709325)
2026-10-26 08:25:28 -07:00
firestar5683 7f5d30c30d Reset lane centering for lane changes and identify input refusals 2026-10-26 10:13:21 -05:00
dirwin31 41543cd593 Preserve usable phone GPS fixes and enforce measurement freshness 2026-10-26 08:02:48 -07:00
firestar5683 0f00621c98 Coalesce refreshed FLM receipts with scheduled tracking 2026-10-26 09:34:09 -05:00
firestar5683 099cbd2809 Keep SpacePilot awake and release game mode on verified key-off
Renew the existing activity timer only during active offroad game ticks.
Observe only the exact game-owned Offroad request and restore Auto after
continuous fresh all-Panda ignition-off, closed game, stopped driving
services and healthy noOutput through the existing owner transaction.
Ordinary exit, stale data, reconnect and external mode changes never
restore Auto. UI restart does not adopt a previous game request.

142 source-bound focused cases, original timeout regression, lint/type
checks and independent source/evidence review. Synthetic host lifecycle
qualification only; no device changes or new physical/headroom claim.
2026-10-26 09:17:19 -05:00
firestar5683 0e5e5574c4 Publish refreshed FLM collection receipts on tracking 2026-10-26 09:09:55 -05:00
firestar5683 347397ad38 Add exclusive parked SpacePilot mode and native Big UI
Use the supported Offroad lifecycle with fresh Park, receive-only CAN,
stopped-process and noOutput checks. Add offroad touch/controller play
and the native Big UI five-version-tap entry. Exiting never restores
driving mode automatically within the existing manager session.

Qualified by210 unique focused passing cases, lint/type checks, native
host rendering/input/resource checks and independent lifecycle review.
Actual vehicle lifecycle, hardware inputs and device headroom remain
unqualified; no deployment is part of this change.
2026-10-26 00:01:36 -05:00
firestar5683 35f62c3c5a Bound sampled lane clearance with corroborated body geometry 2026-10-25 23:39:42 -05:00
firestar5683 3da487cd19 Mark NumPy sinc as an intentional API name 2026-10-25 23:11:00 -05:00
firestar5683 d914cf86ba Register automatic Ford discovery regressions in vehicle CI 2026-10-25 23:02:34 -05:00
firestar5683 6413fef0f9 Refine C3 Disengage Sound 2026-10-25 23:00:35 -05:00
firestar5683 87b42f9127 Use combined HKG lateral permission for steering TX and forwarding 2026-10-25 22:59:49 -05:00
firestar5683 822dbe692f Keep automatic Ford discovery on the normal CAN harness 2026-10-25 22:55:54 -05:00
firestar5683 eb2b6c2d23 Preserve Local Changes During Fast Updates 2026-10-25 22:18:41 -05:00
firestar5683 0009df9084 Require fresh Ioniq 6 parser receipts after ECU handoff 2026-10-25 22:06:07 -05:00
firestar5683 f3bbd38e81 Wake Auto Learn on fresh motion and native evidence 2026-10-25 21:05:53 -05:00
firestar5683 a3811ef5dd Keep Auto Learn collection alive during slow metadata delivery 2026-10-25 20:50:06 -05:00
firestar5683 8807d791f1 Require qualified body bounds before added lane correction 2026-10-25 20:04:27 -05:00
Dom a168542200 Fix shared lane test feed cadence without hidden historical batches 2026-10-25 19:49:28 -05:00
firestar5683 aa17fb6f0e Add native parked SpacePilot Easter egg on comma four 2026-10-25 19:43:58 -05:00
firestar5683 d59a37d37f Track single lane lines with smooth transitions and predicted clearance 2026-10-25 19:17:20 -05:00
Dom fff069c0e3 tests: qualify GM factory clocks and FLM physical-effect fixtures 2026-10-25 19:13:41 -05:00
firestar5683 f2b235bc7b Defer EV6 ECU takeover until the inactive control producer is ready 2026-10-25 19:07:34 -05:00
firestar5683 318c440b2b Allow measurable Auto Learn trials without increasing slew 2026-10-25 18:32:04 -05:00
firestar5683 567c190d5d Restore Galaxy settings when returning from tools 2026-10-25 18:23:30 -05:00
Dom ef6354430a ci: refresh protected inventory receipt for privacy policy update 2026-10-25 18:16:42 -05:00
prabhaavp da972263c3 Revise privacy policy for Galaxy: StarPilot Companion 2026-10-25 19:13:17 -04:00
Dom 1cd3c7f179 test: qualify Bolt ACC factory brake clock in native and AOL fixtures 2026-10-25 18:04:20 -05:00
Prabhaav Pillai 19d4b21f6b README and privacy policy for Android App 2026-10-25 19:02:39 -04:00
firestar5683 0ddf2cb8de Reduce comma 3 speaker resonance 2026-10-25 17:53:57 -05:00
firestar5683 eb7afba097 Show Jetlink model status in GPU feedback 2026-10-25 17:41:51 -05:00
firestar5683 27b552cbd8 Reset Jetlink fallback history without GPU compilation 2026-10-25 17:39:43 -05:00
firestar5683 13e1875d20 Synchronize Bolt ACC pedal brake frames with factory counters 2026-10-25 17:33:45 -05:00
Dom 5ccbcc5752 ci: recognize the NumPy sinc identifier in speaker filtering 2026-10-25 17:11:03 -05:00
firestar5683 b5c90af1cf Fix Galaxy Jetlink setup guide link 2026-10-25 16:58:36 -05:00
firestar5683 33fc8be564 Adapt StarPilot sounds for the comma 3 speaker 2026-10-25 16:57:26 -05:00
Dom 2084622bec ci: calibrate material workload estimates from both hosted runs 2026-10-25 16:54:37 -05:00
Dom d3193635a2 ci: rebalance custom pytest using current hosted workload measurements 2026-10-25 16:21:40 -05:00
Dom 3b3960a5b8 Align EV9 pedal contract test with independent lateral authority 2026-10-25 15:37:59 -05:00
firestar5683 de97a6eff3 Resume Auto Learn discovery when the road regime changes 2026-10-25 15:09:51 -05:00
firestar5683 9fc77a256b Try feasible Auto Learn directions at coordinate bounds 2026-10-25 14:59:31 -05:00
firestar5683 447373d78b Verify PE AOL steering continuity through pedal overrides 2026-10-25 14:40:56 -05:00
firestar5683 8a5fbf0be8 Preserve bounded clearance with one visible lane line 2026-10-25 14:31:40 -05:00
firestar5683 1a5ee718c9 Keep HKG AOL armed through full engagement and pedal overrides 2026-10-25 14:28:55 -05:00
Dom 5e83cb218f Condense transport intent comments within source size limit 2026-10-25 14:16:20 -05:00
firestar5683 fc5875831c Arm Ford AOL when cruise engages before the automatic MAIN pulse 2026-10-25 14:10:02 -05:00
firestar5683 24174ec6a6 Build Toyota pedal source continuity fix 2026-10-25 14:02:52 -05:00
firestar5683 ef03c7ba36 Merge current changes for Toyota pedal continuity 2026-10-25 13:58:28 -05:00
firestar5683 59c6935f3c Preserve Toyota AOL intent through held accelerator gaps 2026-10-25 13:57:49 -05:00
firestar5683 3c740d2050 Allow conflated car state snapshots in Auto Learn 2026-10-25 13:55:50 -05:00
firestar5683 3b67ac199e Refresh GM lateral evidence after control preparation 2026-10-25 13:54:50 -05:00
firestar5683 90916a5ff2 Cover recorded RAV4 configuration in brake regression 2026-10-25 13:51:35 -05:00
Dom 71632a6b48 Refresh prebuilt receipt for validated Arabesque assets 2026-10-25 13:50:12 -05:00
firestar5683 baae36fc3a Preserve Toyota AOL intent through held-brake source gaps 2026-10-25 13:47:45 -05:00
firestarsdog 0c842da32e Arabesque: Rebecca, Adrift 2026-10-25 14:33:26 -04:00
Dom d6fe10f60b Fix file manager type contracts and refresh documentation receipt 2026-10-25 13:01:13 -05:00
firestarsdog faa61d48fc Arabesque Files 2026-10-25 13:50:43 -04:00
firestar5683 d33f5b550c Preserve AOL intent during temporary EPS event joins 2026-10-25 12:25:17 -05:00
Dom e404020aca Assert UI test bounds before indexing optional values 2026-10-25 12:22:06 -05:00
firestar5683 0421b31bb1 Remove duplicate Ioniq 6 blind-spot sound alias 2026-10-25 12:14:32 -05:00
firestarsdog 32360730ba Build Panda firmware with tici stall recovery 2026-10-25 05:08:08 -04:00
firestarsdog 4a84cf290d Unify speed-limit signs across UIs for consistent sizing and clarity 2026-10-25 05:05:36 -04:00
firestarsdog 2248773fcc Restore tici fan retries after stalls 2026-10-25 04:58:10 -04:00
Dom 286641c1fe Declare logger metadata test build output 2026-10-25 02:06:43 -05:00
Dom 4b30638588 Isolate FLM native binding fixtures from preloaded extensions 2026-10-25 01:24:48 -05:00
firestar5683 4cd78e7366 Log bounded GM lateral withdrawals after command publication 2026-10-25 01:06:32 -05:00
firestar5683 d17623d930 Preserve Ford AOL intent across EPS gas-release receipt joins 2026-10-25 00:43:59 -05:00
firestar5683 9ca75ddefd Report interrupted pending trials to the learner 2026-10-25 00:42:22 -05:00
Dom cafb6df8c2 Balance custom pytest pedal caller workload 2026-10-25 00:29:21 -05:00
firestar5683 b6e7e63d87 Detect unresponsive steering on stock ASCM Volts 2026-10-24 23:48:28 -05:00
firestar5683 82abd03443 Retain pending learning study across consumed-source timing gaps 2026-10-24 23:36:17 -05:00
firestar5683 5674f11442 Avoid loading model runtime in Controls 2026-10-24 23:26:26 -05:00
firestar5683 49ab8cf392 Record model identity in qlog opening metadata 2026-10-24 23:20:11 -05:00
firestar5683 202a52c353 Match BluePilot angle delay and missing-model input behavior 2026-10-24 22:09:51 -05:00
firestar5683 766f4ceabb Use an explicit failure for unapproved capture in source tests 2026-10-24 22:07:06 -05:00
firestarsdog baab9caf43 Fix false GPU fallback icons 2026-10-24 23:02:19 -04:00
firestar5683 4c250a6a06 Restore Auto Learn source admission after GPU startup fix 2026-10-24 21:55:54 -05:00
firestar5683 809681dc24 Reduce repeated EV9 configuration copies 2026-10-24 21:54:37 -05:00
firestar5683 c12afe13e7 Capture native audio callback timing 2026-10-24 21:46:43 -05:00
firestar5683 d6b8ade37f Defer torque EV ECU disable until inactive controls are ready 2026-10-24 21:34:54 -05:00
Dom 0427ba9fbf Isolate Bolt saved-owner test Params lock 2026-10-24 21:30:33 -05:00
firestar5683 3d8f901f1c Exit failed GPU startup before isolated Small recovery 2026-10-24 21:25:48 -05:00
firestar5683 58bb172bda Reduce repeated startup configuration serialization 2026-10-24 21:02:51 -05:00
firestar5683 e3872c4760 Retain Auto Learn source rejection evidence in route logs 2026-10-24 20:27:33 -05:00
firestar5683 b83bddccf3 Keep sign parity tests independent of a graphics context 2026-10-24 20:25:47 -05:00
firestar5683 d5dcbd1891 Restore original small UI sign proportions 2026-10-24 20:08:38 -05:00
firestar5683 192b64e407 Match compact sign tests to the shorter outline 2026-10-24 18:47:48 -05:00
firestar5683 b1b48eaaf3 Keep UI modules within the source size limit 2026-10-24 18:21:24 -05:00
prabhaavp 3530f447c1 Merge pull request #246 from natehuby76/fix/c4-menu-start
reset c4 menu on reopen
2026-10-24 19:08:19 -04:00
whoisdomi e97e07e1b3 Model runtime fix
Select the model runtime before validating device artifacts
2026-10-24 18:02:09 -05:00
firestar5683 2251e13068 Compact small UI speed limit signs 2026-10-24 17:51:54 -05:00
Nathan 69b0d7c600 reset c4 menu on reopen 2026-10-24 17:42:05 -05:00
firestar5683 1c332c67f8 Reduce digest size-selection overhead 2026-10-24 17:11:24 -05:00
firestar5683 eb97258ed1 Optimize scalar PID clipping 2026-10-24 16:23:06 -05:00
firestar5683 4fe5cca267 Preserve model source renderer API and static contracts 2026-10-24 15:54:45 -05:00
firestarsdog d656e9f422 Large UI Model Loading 2026-10-24 16:49:56 -04:00
Dom 44ad6f5342 Balance lifecycle shards using measured pedal caller cost 2026-10-24 15:43:48 -05:00
firestar5683 a6779a8aa0 Reduce feedback observation copying 2026-10-24 15:41:39 -05:00
firestar5683 d43477ec41 Refresh release receipt after Vienna sign polish 2026-10-24 15:12:30 -05:00
firestarsdog 0f981ad88f Vienna Sign Polish 2026-10-24 16:09:36 -04:00
Dom dbd28f84c4 Narrow Node executable before projection contract subprocess 2026-10-24 14:52:09 -05:00
firestar5683 f95f5a88cf Apply shared host warning policy to inactive AOL source pauses 2026-10-24 14:50:30 -05:00
firestar5683 5bef40f51c Cover shared AOL source pauses across all makes and Volt owners 2026-10-24 14:42:42 -05:00
firestarsdog 6b9b8a4dfb Unify SLC signs for clarity and fix Android Auto layout saving 2026-10-24 15:38:38 -04:00
firestar5683 823f6923c8 Retain AOL intent through fully withdrawn source pauses 2026-10-24 14:28:39 -05:00
firestar5683 9e6dabb149 Schedule slow Toyota caller modules from measured CI timings 2026-10-24 13:26:26 -05:00
firestar5683 9712480382 Retain Ford AOL intent through temporary EPS recovery with gas held 2026-10-24 12:42:09 -05:00
firestar5683 e5265f88b9 Preserve the local test runner discovery interface 2026-10-24 12:28:58 -05:00
firestar5683 4a9881ff5b Prevent synchronized crash-session uploads during driving 2026-10-24 12:25:09 -05:00
firestar5683 b00ca4c52e Retain armed reverse intent through an inactive source gap 2026-10-24 12:24:46 -05:00
firestar5683 25cbb14cbd Balance vehicle caller CI workloads from hosted timings 2026-10-24 11:38:52 -05:00
firestar5683 1ebbff5cd9 Isolate Android Auto rendering from driving cores 2026-10-24 11:29:24 -05:00
firestar5683 163751cb7e Keep wheel actions bound across equivalent CarParams layouts 2026-10-24 11:01:12 -05:00
Dom 91b378cc5b test: keep capture recovery publication on the synthetic clock 2026-10-24 10:58:33 -05:00
firestar5683 3f693e1735 Keep Ford angle AOL within native steering limits through turns and pedals 2026-10-24 10:40:09 -05:00
firestar5683 d646a5dea8 Format source study regression tests 2026-10-24 10:29:09 -05:00
firestar5683 0b54917f90 Keep bounded source trials progressing across road variation 2026-10-24 10:17:07 -05:00
firestar5683 e15bd6bf3d Keep Android Auto below controls priority 2026-10-24 10:11:30 -05:00
firestar5683 baf9a3fff6 Prevent Android Auto renderer CPU starvation 2026-10-24 10:08:26 -05:00
firestar5683 3b081b2fc2 Format source recovery regression tests 2026-10-24 09:55:07 -05:00
firestar5683 24126eafdc Keep source learning progressing after interrupted trials 2026-10-24 09:45:44 -05:00
firestar5683 44efc8d935 Allow Android Auto renderer cold startup to finish 2026-10-24 09:45:31 -05:00
firestar5683 5dddf9ae1d Stabilize native registry and asynchronous source fixtures 2026-10-24 00:10:31 -05:00
firestar5683 b863214df8 Preserve original RELEASE skip accounting across shards 2026-10-23 23:41:28 -05:00
firestar5683 6efe2e5379 Keep CI fixtures current and partition complete RELEASE contracts 2026-10-23 23:24:10 -05:00
firestar5683 04c590f7c4 Exercise composite MAIN freshness through native steering transmission 2026-10-23 22:52:46 -05:00
firestar5683 39d30d89c1 Correct pose interval fixture typing 2026-10-23 22:48:49 -05:00
firestar5683 567bde2b4e Balance independent Toyota caller tests across custom jobs 2026-10-23 22:44:02 -05:00
firestar5683 4f4cb4e7d9 Align source fixtures and cover composite MAIN freshness 2026-10-23 22:43:56 -05:00
firestar5683 15dc2cb26c Distribute the growing lifecycle catalog across more jobs 2026-10-23 22:35:57 -05:00
firestar5683 317f08d15a Separate eager startup fixtures from deferred takeover 2026-10-23 22:32:50 -05:00
firestar5683 c0ae7127ef Keep vehicle refusal fixtures aligned with qualified configurations 2026-10-23 22:31:54 -05:00
firestar5683 b50b5cc1a3 Allow slow source tracking experiments from measured native sensitivity 2026-10-23 22:29:52 -05:00
firestar5683 e9d39b4f5e Repair learner receipt test fixtures 2026-10-23 22:27:02 -05:00
firestar5683 d5700b6910 Account for combined clock resolution in motion admission 2026-10-23 21:54:31 -05:00
firestar5683 b0c054d881 Reacquire SDGM Volt steering after inactive brake feedback 2026-10-23 21:39:15 -05:00
firestar5683 d4dbb83a55 Build matching Toyota pedal and hold firmware 2026-10-23 21:29:59 -05:00
firestar5683 b6b820a275 Support shared Toyota pedal and brake hold paths 2026-10-23 21:26:01 -05:00
firestar5683 d8a218b807 Restore GM AOL arming from persistent cruise main 2026-10-23 21:02:48 -05:00
firestar5683 0a2ee300e7 Build matching BluePilot angle and Ford AOL firmware 2026-10-23 20:58:48 -05:00
firestar5683 1996510d7a Enable BluePilot angle steering with retained Ford AOL 2026-10-23 20:52:06 -05:00
firestar5683 a8e10e4e52 Restore compatible model runtime 2026-10-23 20:48:09 -05:00
Dom a5f43c1d4b Use typed boolean values in Ford preference cleanup regression 2026-10-23 20:41:03 -05:00
firestar5683 a25dcb535a Align model publication regression with repository lint rules 2026-10-23 20:38:01 -05:00
firestar5683 0172b49987 Restore Dom model publication guard after dropped camera frames 2026-10-23 20:34:08 -05:00
firestar5683 c5ae97f216 Dispatch shared AOL validation only to the current vehicle owner 2026-10-23 20:13:42 -05:00
firestar5683 d2f3acc248 Keep calibrated configuration across learner receipt gaps 2026-10-23 20:10:59 -05:00
firestar5683 976d2a2222 Reject unrelated deadline and F1 owners before qualification 2026-10-23 20:06:11 -05:00
firestar5683 a67202cfb4 Build matching Toyota DSU bypass firmware 2026-10-23 20:01:28 -05:00
firestar5683 4194390dc1 Support Toyota DSU bypass on camera bus 2026-10-23 19:59:49 -05:00
firestar5683 276f3561ce Preserve Ford hands-free display preference across ignition 2026-10-23 19:58:59 -05:00
Dom 7afcf3a8ac Align Prius CI fixtures with qualified hardware profiles 2026-10-23 19:53:04 -05:00
firestar5683 7403185bd8 Keep Ford steering selection visible with AOL enabled 2026-10-23 19:46:20 -05:00
firestar5683 df22d36413 Balance lifecycle CI jobs using measured runtimes 2026-10-23 19:14:21 -05:00
firestar5683 656ba431e9 Update model runtime test contracts 2026-10-23 19:07:47 -05:00
firestar5683 50fc142dd7 Handle unavailable Python Bluetooth socket connections 2026-10-23 19:04:37 -05:00
firestar5683 2a7ebf190c Build matching RAV4 Hybrid pedal and CAN filter firmware 2026-10-23 19:04:07 -05:00
firestar5683 c276b589c7 Support RAV4 Hybrid pedal and CAN filter together 2026-10-23 19:02:36 -05:00
Dom 04b8eb0cd3 Correct manual Matrix four-byte test fixtures 2026-10-23 18:29:58 -05:00
firestar5683 806783c148 Keep receipt callback checks after the output drain 2026-10-23 18:22:41 -05:00
firestar5683 c7e6c27eda Use explicit manual Matrix test fixtures 2026-10-23 18:19:33 -05:00
firestar5683 1a7c20ab6c Compile native RDF Chestnut model 2026-10-23 18:13:43 -05:00
firestar5683 d2d2399609 Build matching Toyota radar and LTA pedal firmware 2026-10-23 18:13:27 -05:00
firestar5683 0cc30a9b30 Support Toyota radar and LTA pedals 2026-10-23 18:11:57 -05:00
firestar5683 1e7d9c311f Preserve FLM capture with native model startup 2026-10-23 18:10:40 -05:00
firestar5683 0eec2e2a55 Bound native model loading memory 2026-10-23 18:09:38 -05:00
firestar5683 748e7b13fc Fix model runtime test fixtures 2026-10-23 18:08:05 -05:00
firestar5683 1f17185669 Keep F1 Volt pedal retention bound to physical receipts 2026-10-23 18:07:54 -05:00
firestar5683 6fae1942e4 Load optional learning capture before the controls loop 2026-10-23 18:06:10 -05:00
firestar5683 094332da2c Build matching EPS73 Prius CAN filter firmware 2026-10-23 17:51:41 -05:00
firestar5683 16eaf005fe Support EPS73 Prius four-byte CAN filters 2026-10-23 17:48:29 -05:00
firestar5683 12601d1fac Pin model runtime source imports before ambient hooks 2026-10-23 17:38:06 -05:00
firestar5683 0022fde1cb Fix Auto Learn collection recovery after evidence gaps 2026-10-23 17:36:10 -05:00
firestar5683 332540a931 Preserve GM cold-start wait provenance 2026-10-23 17:24:35 -05:00
firestar5683 d28aa489ca Compile older Chestnut models 2026-10-23 17:13:33 -05:00
firestar5683 a5eda8a63a Fix Auto Learn authorization and tracking delivery timing 2026-10-23 17:13:21 -05:00
firestar5683 6e5879e035 Preserve stock angle lateral recovery within original source deadline 2026-10-23 17:12:44 -05:00
firestar5683 d377f5efd7 Keep GM lateral intent armed through startup and pedal withdrawal 2026-10-23 17:01:06 -05:00
firestar5683 db297a341b Reuse exact Hyundai deadline admission during control cycles 2026-10-23 16:55:56 -05:00
firestar5683 091682eb06 Fix native model static checks 2026-10-23 16:39:50 -05:00
firestar5683 c779fc4672 Keep Hyundai factory cruise alive until controls are ready 2026-10-23 16:30:25 -05:00
firestar5683 26bc78b69d Compile native Chestnut models 2026-10-23 16:17:25 -05:00
Dom 3a1d0bc615 Restore selfdrived executable mode 2026-10-23 16:13:30 -05:00
firestar5683 7897829d9b Preserve observed worker exit evidence 2026-10-23 16:09:42 -05:00
firestar5683 1353dc4943 Support manual Matrix CAN filter configurations 2026-10-23 16:09:21 -05:00
firestar5683 f50aaa1d6d Handle Hyundai longitudinal withdrawal before brake receipt 2026-10-23 16:03:05 -05:00
firestar5683 076cda2d6c Support stock Prius retrofit AOL 2026-10-23 15:54:01 -05:00
firestar5683 37dc1533ef Reuse exact native qualification across all vehicle makes 2026-10-23 15:42:44 -05:00
firestar5683 f9bf710396 Fix audio integration static checks 2026-10-23 15:33:38 -05:00
firestar5683 04030a447e Use a typed failure callback in HKG admission regression 2026-10-23 15:31:32 -05:00
firestar5683 eb955e391c Reduce repeated HKG source deadline qualification 2026-10-23 15:29:10 -05:00
firestar5683 95d87910f9 Reuse exact native configuration admission for EV6 and EV9 2026-10-23 15:25:08 -05:00
prabhaavp 4b54f61deb Merge pull request #245 from dirwin31:Stardust-Spotify-Companion
Improve Spotify Connect recovery and Bluetooth phone audio
2026-10-23 16:16:54 -04:00
Prabhaav Pillai c14afb153e Merge branch 'SecretGoodStarPilot' into pr/dirwin31/245 2026-10-23 16:16:34 -04:00
Prabhaav Pillai 5755042c96 - First-time pairing interrupted by the new login timeout.
- IPv6-only startup blocked indefinitely.
- Saved initial volume overridden with 100%.
- Unbounded diagnostic logging in the audio loop.
- Hidden speaker-disconnection status while paused.
- Duplicate slider handlers, redundant CSS, verbose documentation, and test lint issues.
2026-10-23 16:15:21 -04:00
firestar5683 e1c73f2359 Keep superseded CI runs cancelled 2026-10-23 14:56:13 -05:00
firestar5683 0c03e1b1b8 Reduce Hyundai longitudinal configuration copy overhead 2026-10-23 14:52:29 -05:00
firestar5683 63fffaa27b Build matching Toyota Matrix stock-control firmware 2026-10-23 14:48:41 -05:00
firestar5683 eac8cea6ee Support Toyota Matrix stock-control AOL 2026-10-23 14:40:44 -05:00
firestar5683 4b5a99e66d Restore independent AOL arming on non-SCC Hyundai vehicles 2026-10-23 14:37:40 -05:00
firestar5683 0a7529919f Balance current AOL lifecycle workloads 2026-10-23 14:14:05 -05:00
Dom 77f54a22f0 CI: balance lifecycle shards using measured native caller cost 2026-10-23 14:09:22 -05:00
firestar5683 1887a45a68 Refresh Galaxy build receipt 2026-10-23 13:34:13 -05:00
Prabhaav Pillai d45c1f2b1b Group Galaxy audio settings under Android Auto and standardize banners 2026-10-23 14:25:42 -04:00
firestar5683 2f68bdedca Build matching Ford hands-free display firmware 2026-10-23 13:12:45 -05:00
firestar5683 dcb7d5de82 Keep Ford hands-free display active through driver turns 2026-10-23 13:10:24 -05:00
firestar5683 657a9462ce Preserve SDGM Volt MAIN intent through startup and braking 2026-10-23 12:59:58 -05:00
firestar5683 8da7e4f493 Link local Galaxy access from app installation 2026-10-23 12:41:57 -05:00
firestar5683 db24c48edc Report bounded learner source observation refusals 2026-10-23 12:41:37 -05:00
firestar5683 01a31c1f2f Build matching original Prius four-byte filter artifacts 2026-10-23 12:25:50 -05:00
firestar5683 9d2d8f3a31 Support original Prius four-byte CAN filters 2026-10-23 12:17:52 -05:00
firestar5683 7df9af125d Keep confirmed vision signs current during cooldown 2026-10-23 12:08:22 -05:00
firestar5683 dc9a5743ee Avoid repeated work for retained learning poses 2026-10-23 12:00:57 -05:00
Dom 165651b096 Register complete HKG module in lifecycle CI 2026-10-23 11:55:24 -05:00
firestar5683 a75d3fd787 Include camera conversion in vision CPU budget 2026-10-23 11:45:33 -05:00
firestar5683 b00a235274 Fix alert preview test startup 2026-10-23 11:43:54 -05:00
firestar5683 b0c6319a1a Restore GV70 two-Panda AOL ownership and stopping parity 2026-10-23 11:37:31 -05:00
firestar5683 2993c8fb07 Restore vision candidate follow-up cadence 2026-10-23 11:20:42 -05:00
firestar5683 c7e47a0289 Reorder lateral settings 2026-10-23 11:18:11 -05:00
firestar5683 733253c7c5 Preserve ASCM Volt intent across complete Card source gaps 2026-10-23 10:18:57 -05:00
dirwin31 f487123e4c Merge SecretGoodStarPilot into Stardust-Spotify-Companion 2026-10-23 07:47:13 -07:00
firestar5683 131739bda6 Format audio backend diagnostics and fixture assertions 2026-10-23 09:44:46 -05:00
firestar5683 90ca300382 Avoid duplicate native history queue in recovery fixtures 2026-10-23 09:34:16 -05:00
firestar5683 9d50b3734c Report active audio playback backend 2026-10-23 09:25:22 -05:00
firestar5683 7fe4b5b550 Bound learner history before tracking initialization 2026-10-23 09:14:39 -05:00
Dom 5961cbd675 Use real alert manager in lane change tests 2026-10-23 09:11:12 -05:00
firestar5683 f19c719d76 Align learner recovery and pilot refusal fixtures 2026-10-23 09:02:15 -05:00
firestar5683 6e72874e55 Strengthen numerical learner regression checks 2026-10-23 08:40:39 -05:00
firestar5683 dc2b45ef1e Reduce vision proposal decoding overhead 2026-10-23 08:34:37 -05:00
firestar5683 9b1ebf0635 Show steering pause warning only for acknowledged lateral requests 2026-10-23 08:32:01 -05:00
firestar5683 4d203961da Align learner protocol and lifecycle fixtures 2026-10-23 08:29:20 -05:00
firestar5683 7c25601821 Track actual poll deliveries in learner fixtures 2026-10-23 08:05:49 -05:00
firestar5683 09dbb29d5a Queue original CAN evidence in decoder fixtures 2026-10-23 08:00:07 -05:00
firestar5683 dc86e03e60 Build matching original Prius smartDSU artifacts 2026-10-23 07:40:24 -05:00
firestar5683 da51312a6f Support original Prius smartDSU configurations 2026-10-23 07:27:12 -05:00
firestar5683 b98d9a1e6e Balance measured Toyota caller workloads 2026-10-23 07:12:39 -05:00
Dom 18b6a4a458 Align event frame archive fixture 2026-10-23 07:07:26 -05:00
firestar5683 6d201df86a Align UI fixtures and complete Galaxy setting descriptions 2026-10-23 06:36:08 -05:00
firestar5683 43007a6318 Build documentation with the standard library 2026-10-23 06:35:32 -05:00
firestar5683 80ca622ea6 Align Toyota caller fixtures with qualified AOL and native acknowledgments 2026-10-23 06:27:12 -05:00
firestar5683 266ee4ce07 Model suppressed ADRV diagnostic replies in startup regressions 2026-10-23 06:25:05 -05:00
firestar5683 b46431793b Align drive and session fixtures with current contracts 2026-10-23 06:23:00 -05:00
firestar5683 c06d1dfe16 Verify Ford event-first SOFT retention through its current owner 2026-10-23 06:07:05 -05:00
firestar5683 2d2f662bdf Balance independent Toyota cases and audit existing map GPU guards 2026-10-23 06:06:24 -05:00
firestar5683 ed3f88ef52 Scope HKG startup fixture preparation to Hyundai interfaces 2026-10-23 06:02:47 -05:00
firestar5683 29ab44fc4d Align model compiler and steering-delay test fixtures 2026-10-23 05:56:55 -05:00
firestar5683 b42499038d Load native safety in fresh Volkswagen caller tests 2026-10-23 05:52:43 -05:00
Dom 5dea258223 Align custom pytest discovery contract with complete shard gate 2026-10-23 05:37:12 -05:00
firestar5683 7d2c0aafea Distribute custom pytest workloads without dropping cases 2026-10-23 05:10:16 -05:00
Dom 92f0c26228 Cover Toyota camera identity contradiction latch 2026-10-23 04:47:13 -05:00
firestar5683 3b22e29c20 Prepare classic HKG startup owners in family regression callers 2026-10-23 04:44:38 -05:00
firestar5683 206907f651 Preserve armed intent through split SOFT recovery 2026-10-23 04:42:58 -05:00
Dom e0bea55fa9 Align uploader and torque layout test fixtures 2026-10-23 04:39:20 -05:00
Dom 7c795af543 Compact Toyota camera test fixture without changing data 2026-10-23 04:25:15 -05:00
firestar5683 8b9338a0a6 Preserve Chrysler physical cancel in location fault tests 2026-10-23 04:09:50 -05:00
firestar5683 9a2d62445d Align Toyota SecOC tests with qualified RAV4 angle support 2026-10-23 04:04:20 -05:00
firestar5683 314c92e51f Build matching Toyota camera filter artifacts 2026-10-23 04:00:12 -05:00
firestar5683 af84074472 Support camera Toyota radar filter configurations 2026-10-23 03:55:56 -05:00
Dom 4a38356a5f Apply onroad graphics fixtures to unittest runner 2026-10-23 03:53:13 -05:00
firestar5683 ecc1db481b Balance independent lifecycle workloads within existing deadlines 2026-10-23 03:51:56 -05:00
firestar5683 6eb9df9642 Align Tesla screen settings fixture with Alpha availability 2026-10-23 03:14:37 -05:00
firestar5683 8c814777f6 Align HKG unit callers with shared intent and startup contracts 2026-10-23 03:12:42 -05:00
firestar5683 9e6a49742e Isolate synthetic state machine event rules from CAN faults 2026-10-23 03:11:33 -05:00
firestar5683 359549db26 Align generic wheel policy fixture with current capabilities 2026-10-23 03:04:50 -05:00
firestar5683 468770cabd Retain host test crash and timeout evidence 2026-10-23 03:03:56 -05:00
firestar5683 1ec8ca7abb Align HKG location regressions with shared armed intent policy 2026-10-23 02:28:18 -05:00
Dom d049218089 Align Toyota legacy filter safety tests 2026-10-23 02:08:27 -05:00
firestarsdog 7a8e17dda5 fix(galaxy): expand timeline for readable history 2026-10-23 02:55:27 -04:00
firestar5683 d48e34a3a0 Build matching Toyota legacy filter artifacts 2026-10-23 01:49:56 -05:00
firestar5683 6f4fdfb851 Support legacy Toyota four-byte CAN filter profiles 2026-10-23 01:42:12 -05:00
firestarsdog 7011142a49 feat(replay): keep seek targets visible while scrubbing 2026-10-23 02:30:11 -04:00
Dom d048a8eb98 Align EV9 stopping marker test contract 2026-10-23 01:29:12 -05:00
Dom 22b2f2a898 Preserve bootstub binary output modes 2026-10-23 01:17:30 -05:00
Dom 4e74d8890d Align interface and confirmation test contracts 2026-10-23 00:59:33 -05:00
firestar5683 7e2cf618e0 Cover HKG native catalog admission and isolate inherited test policy 2026-10-23 00:49:31 -05:00
firestar5683 1f8205231f Scope complete build identity to lifecycle gates 2026-10-23 00:26:57 -05:00
firestarsdog 4bee14a4d7 build(replay): refresh ARM64 prebuilt and receipt 2026-10-23 01:24:45 -04:00
firestarsdog 68629dcb82 Fix compact UI confirmations, text overlap, and one-way Display controls 2026-10-23 01:24:18 -04:00
firestarsdog 29da914430 feat(replay): color timelines to show drive history 2026-10-23 01:24:18 -04:00
Dom c4c5d276d5 Align vehicle CI provenance fixtures 2026-10-23 00:22:53 -05:00
firestar5683 a012e58a99 Align controller and UI fixtures with current contracts 2026-10-23 00:15:50 -05:00
firestar5683 c8d2a8aaf0 Preserve exact ASCM Volt steering receipts across receive ordering 2026-10-23 00:14:03 -05:00
firestar5683 b2296b4844 Pin lifecycle build inputs across independent shards 2026-10-23 00:04:03 -05:00
Dom 403e360551 Synchronize simulated startup readiness fixture 2026-10-22 23:54:46 -05:00
firestar5683 c1d966997d Run Ford legacy long denial with an explicit release fixture 2026-10-22 23:51:04 -05:00
firestar5683 143b9b5d4f Bound Subaru StopStart pulse with integer nanosecond deadline 2026-10-22 23:50:06 -05:00
firestar5683 6053d3e6ff Restore HKG stock angle arming and steering recovery 2026-10-22 23:47:42 -05:00
Dom 9e08f0b1bb Align GM startup tests with retained lateral intent 2026-10-22 23:45:07 -05:00
firestar5683 1e189a1192 Restore legacy Toyota PCM stop latch transitions 2026-10-22 23:42:56 -05:00
firestar5683 84fb671740 Type Ford CAN and developer panel test fixtures 2026-10-22 23:38:16 -05:00
firestar5683 86b752f9d2 Record qualified Subaru smoothing startup metadata in replay reference 2026-10-22 23:37:28 -05:00
Dom 2fe363a1f8 Align Ford CI fixture typing 2026-10-22 23:24:20 -05:00
firestar5683 989beaae3c Shard complete lifecycle qualification by module 2026-10-22 23:21:56 -05:00
firestar5683 d6273c7137 Verify Vision adoption and command identity 2026-10-22 23:18:11 -05:00
firestar5683 1a2cd5e55d Merge current family fixes with qualified Toyota CAN filters 2026-10-22 23:10:14 -05:00
firestar5683 3149512d97 Build qualified Chrysler native guards 2026-10-22 23:09:38 -05:00
firestar5683 edb618e910 Qualify Chrysler native guards without changing safety limits 2026-10-22 23:04:49 -05:00
firestar5683 482e3cd7f2 Restore Dom mode transitions for qualified Volt longitudinal control 2026-10-22 22:57:39 -05:00
firestar5683 687bc6835d Build Ford configuration parity firmware and pandad 2026-10-22 22:54:30 -05:00
firestar5683 aaa2f687ea Complete Ford release long manual and multi-Panda support 2026-10-22 22:49:49 -05:00
firestar5683 c9fbbc0331 Merge gateway MEB support and qualified Toyota replay metadata 2026-10-22 22:48:54 -05:00
firestar5683 e24c677109 Refresh qualified Toyota standstill metadata expectations 2026-10-22 22:47:53 -05:00
firestar5683 996542ffef Build gateway MEB native safety 2026-10-22 22:41:30 -05:00
firestar5683 dbc5af5d55 Merge current native artifacts with Toyota filter configurations 2026-10-22 22:41:18 -05:00
firestar5683 f99d84daa8 Restore Toyota EPS8 four-byte CAN filter configurations 2026-10-22 22:41:05 -05:00
firestar5683 b98f1bfce5 Restore gateway MEB independent lateral and saved speed control 2026-10-22 22:40:45 -05:00
firestar5683 1fdc8b89e8 Preserve HKG AOL arming and original source deadlines 2026-10-22 22:33:16 -05:00
Dom da6e0bd0a9 Align CI formatting and Mici toggle fixture 2026-10-22 22:28:48 -05:00
firestar5683 c642fd9427 Keep classic HKG discovery on observed normal wiring 2026-10-22 22:22:36 -05:00
firestar5683 0147c90469 Build GM safety checks 2026-10-22 22:13:50 -05:00
firestar5683 4e956b46ec Keep GM safety checks explicit and remove unreachable forwarding 2026-10-22 22:12:36 -05:00
firestar5683 107e99a5fc Merge Tesla safety firmware with current qualification fixes 2026-10-22 22:10:23 -05:00
firestar5683 7610f33129 Qualify Subaru RELEASE AVH guard and ordinary engagement 2026-10-22 22:09:05 -05:00
firestar5683 5acadf1d91 Build Tesla Pre-AP safety cleanup firmware 2026-10-22 22:08:34 -05:00
firestar5683 33ca04f895 Resolve Tesla Pre-AP native safety diagnostics 2026-10-22 22:07:42 -05:00
firestarsdog 0171603f6d Fix compact Driving Controls availability so the menu opens its editor 2026-10-22 23:06:18 -04:00
firestar5683 cac674d270 Run Honda native lifecycle tests under both CI runners 2026-10-22 22:05:31 -05:00
firestar5683 faa890e903 Exercise GM interceptor release guards in native coverage 2026-10-22 22:04:47 -05:00
firestar5683 c7fe28fdfd Merge current tests with Toyota smartDSU priority 2026-10-22 21:55:14 -05:00
firestar5683 cc9161776a Preserve Toyota smartDSU priority over camera traffic 2026-10-22 21:55:13 -05:00
firestar5683 8e1bb6995c Repair Bluetooth and HTTP caller qualification 2026-10-22 21:51:18 -05:00
firestar5683 1ad498846f Type Rivian native test payloads explicitly 2026-10-22 21:49:07 -05:00
firestar5683 fe5b2c66da Merge current qualification tests with Toyota smartDSU artifacts 2026-10-22 21:46:10 -05:00
firestar5683 f7ca352105 Build legacy Toyota smartDSU artifacts 2026-10-22 21:46:00 -05:00
firestar5683 8a3e80cddd Restore executable modes for daemon entrypoints 2026-10-22 21:44:22 -05:00
firestar5683 d429cc3c90 Verify Toyota angle factory and independent LTA health 2026-10-22 21:44:16 -05:00
firestar5683 6095ea559c Accept current native layout documents 2026-10-22 21:42:06 -05:00
firestar5683 cdb7e7a5d4 Reject missing Auto Learn withdrawal receipts 2026-10-22 21:39:39 -05:00
firestar5683 95155234e8 Publish explicit Auto Learn source withdrawals 2026-10-22 21:37:28 -05:00
firestar5683 de94ce4ad8 Qualify native variants and current caller fixtures 2026-10-22 21:36:56 -05:00
firestar5683 ab2baa98ea Cover Rivian unsupported profiles and permanent EPS retirement 2026-10-22 21:35:57 -05:00
prabhaavp 4e46321160 Merge pull request #238 from dirwin31:PR-Smaller-4
App and web: Companion apps, Galaxy pages, Spotify, Bluetooth and Android Auto screens
2026-10-22 22:31:29 -04:00
firestar5683 15a78b9b33 Maintain Auto Learn source delivery cadence 2026-10-22 21:29:34 -05:00
Prabhaav Pillai a57cd3d54a Merge SecretGoodStarPilot into PR-Smaller-4, preserving both Galaxy feature sets 2026-10-22 22:29:25 -04:00
firestar5683 82f3baaebb Merge qualified Nissan coverage and lateral lifecycle fixes 2026-10-22 21:29:16 -05:00
firestar5683 6dff9ba47f Test unsupported Nissan AOL safety words 2026-10-22 21:28:13 -05:00
firestar5683 9e191161d7 Merge current vehicle fixes with legacy Toyota smartDSU support 2026-10-22 21:24:55 -05:00
firestar5683 011e46b8ed Preserve armed lateral intent through temporary warnings 2026-10-22 21:22:55 -05:00
firestar5683 9ce2893646 Build qualified Outback and Subaru smoothing artifacts 2026-10-22 21:14:26 -05:00
firestar5683 4d8467ce37 Restore legacy Toyota smartDSU control paths 2026-10-22 21:14:16 -05:00
firestar5683 c6a366b013 Restore Outback angle and frozen Subaru model smoothing 2026-10-22 21:13:43 -05:00
Prabhaav Pillai d921cf6559 Refactor Galaxy tools into a searchable nested catalog
Render tool menus from the catalog and update navigation, guides, and tests
2026-10-22 22:03:31 -04:00
firestar5683 15578da372 Preserve HKG angle AOL intent through healthy stops 2026-10-22 20:59:02 -05:00
firestar5683 6148a86dc1 Preserve Tesla schema and radar fixture contracts 2026-10-22 20:49:31 -05:00
firestar5683 a0fd5ea025 Include native pytest cases in safety coverage 2026-10-22 20:47:05 -05:00
firestar5683 16509c3803 Expose Alpha Longitudinal in Vehicle Controls 2026-10-22 20:46:13 -05:00
firestar5683 0f1cce2285 Refresh stock longitudinal planner expectations 2026-10-22 20:45:40 -05:00
firestar5683 83a90e89b4 Merge qualified HKG and MQB source 2026-10-22 20:15:07 -05:00
firestar5683 82367fd413 Build MQB direct-drive native safety 2026-10-22 20:14:23 -05:00
firestar5683 ca2373a9a0 Refresh Auto Learn inputs after queued decoding 2026-10-22 20:14:03 -05:00
firestar5683 d4027487c0 Repair HKG startup and native qualification fixtures 2026-10-22 20:12:30 -05:00
firestar5683 9cf41c7b38 Restore MQB direct-drive lateral and longitudinal ownership 2026-10-22 20:08:25 -05:00
firestar5683 f9a06b7d1a Merge qualified Forester and shared build artifacts 2026-10-22 20:05:30 -05:00
firestar5683 dc1e248d03 Build qualified Forester angle artifacts 2026-10-22 20:02:48 -05:00
firestar5683 2d58df950b Share Subaru angle receive storage across exact profiles 2026-10-22 20:02:08 -05:00
firestar5683 9983d3a4cb Merge preview and CI fixes with Toyota filter support 2026-10-22 20:00:23 -05:00
firestar5683 08c0efb01d Merge qualified shared source 2026-10-22 19:59:30 -05:00
firestar5683 8adbeddcbb Accept forwarded Toyota radar-filter advertisements 2026-10-22 19:59:30 -05:00
firestar5683 2c352b90b4 Keep offset sign previews within the visible canvas 2026-10-22 19:57:34 -05:00
firestar5683 0852af76e3 Preserve validated CI import provenance 2026-10-22 19:57:15 -05:00
firestar5683 d747e5ca08 Restore Forester first generation angle control 2026-10-22 19:52:53 -05:00
firestar5683 6633870fb3 Merge CI contracts and recording artifacts with Toyota filter support 2026-10-22 19:52:02 -05:00
firestar5683 a9edd2ca3b Accept forwarded Toyota smartDSU status copies 2026-10-22 19:50:42 -05:00
firestar5683 4840e9e1d9 Build Auto Learn recording and replay 2026-10-22 19:49:51 -05:00
firestar5683 94f88b41a9 Repair CI contracts and preview rendering 2026-10-22 19:44:47 -05:00
firestar5683 2077b98d76 Merge Subaru qualification tests with Toyota parity 2026-10-22 19:41:55 -05:00
firestar5683 8bd7ac53c7 Build EPS8 Toyota smartDSU artifacts 2026-10-22 19:41:23 -05:00
firestar5683 332d235693 Qualify Subaru native discovery and receiver guards 2026-10-22 19:39:12 -05:00
firestar5683 7aea14397c Merge Rivian static corrections with Toyota parity 2026-10-22 19:39:01 -05:00
firestar5683 951947cef2 Merge CI schema and Rivian static qualification 2026-10-22 19:35:52 -05:00
firestar5683 3f06976373 Merge current CI and GM source fixes with Toyota parity 2026-10-22 19:34:46 -05:00
firestar5683 26025d80dd Build EPS8 Toyota smartDSU artifacts 2026-10-22 19:33:38 -05:00
firestar5683 e18bf451d0 Fix SDGM Volt Alpha lateral startup and withdrawal 2026-10-22 19:32:53 -05:00
firestar5683 84087746d7 Align PE protocol checks with supported Alpha profile 2026-10-22 19:32:52 -05:00
firestar5683 6a8c1cf8cf Build Rivian native static corrections 2026-10-22 19:32:20 -05:00
firestar5683 ec957d6430 Fix Rivian native static diagnostics 2026-10-22 19:31:45 -05:00
firestar5683 27c7ba7827 Preserve CI schema and replay contracts 2026-10-22 19:31:25 -05:00
firestar5683 194d6fc106 Merge current safety and Auto Learn fixes for Toyota qualification 2026-10-22 19:29:03 -05:00
firestar5683 23ca75140a Build EPS8 Toyota smartDSU artifacts 2026-10-22 19:28:08 -05:00
firestar5683 d0a90d10ef Share Toyota safety RX workspace 2026-10-22 19:25:32 -05:00
firestar5683 778eed3a60 Make Honda atomic state and fixtures statically verifiable 2026-10-22 19:24:28 -05:00
firestar5683 62dc081f70 Keep Auto Learn source publication responsive 2026-10-22 19:10:12 -05:00
firestar5683 204a6f8534 Restore EPS8 Toyota smartDSU ownership 2026-10-22 19:09:43 -05:00
firestar5683 4413c29923 Restore MQB fixed and second-Panda lateral ownership 2026-10-22 19:04:28 -05:00
Dom 43360170dd Align PE marker and Subaru build variant tests 2026-10-22 18:42:16 -05:00
firestar5683 281f5b6b3a Strengthen PE release and pedal regression coverage 2026-10-22 18:32:02 -05:00
firestar5683 9ec0303f93 Preserve PE independent lateral intent alongside Alpha longitudinal 2026-10-22 18:13:36 -05:00
firestar5683 bb51c66589 Build qualified Subaru angle and startup changes 2026-10-22 18:04:07 -05:00
firestar5683 8c5f4bae13 Restore Ascent angle target shaping 2026-10-22 18:03:21 -05:00
firestar5683 9c5da263cc Merge qualified shared source 2026-10-22 18:02:10 -05:00
firestar5683 8025957925 Merge current shared source for HKG qualification 2026-10-22 17:50:11 -05:00
firestar5683 d4a49cb8c1 Allow tuning preparation to be turned off 2026-10-22 17:50:08 -05:00
firestar5683 bb39b9f388 Build guarded Legacy AVH startup request 2026-10-22 17:49:21 -05:00
firestar5683 7926a909e1 Restore guarded Legacy AVH startup request 2026-10-22 17:48:36 -05:00
Danny b4acca0798 Spotify: Preserve final volume behavior on the newer base 2026-10-22 15:47:15 -07:00
dirwin31 c7cc700e4e BlueALSA: Bump to v5.0.0 2026-10-22 15:47:15 -07:00
dirwin31 89509ba3c7 BT: Dont try and re connect to established audio Stream 2026-10-22 15:47:15 -07:00
Danny 843c364494 Test 2026-10-22 15:47:15 -07:00
Danny b5a58c741f BT: Track Skipping Mute Toggles 2026-10-22 15:47:15 -07:00
Danny f702c707e5 Use Colored Bar 2026-10-22 15:47:15 -07:00
Danny ce980ed98f Spotify: Account for network starting later 2026-10-22 15:47:15 -07:00
Danny 5814e26a53 Spotify: Advertise across different networks better, Swap Devices More Seemlessly 2026-10-22 15:47:15 -07:00
firestar5683 2cc8849544 Restore four-byte Toyota radar filter variants 2026-10-22 17:46:06 -05:00
Dom fb6b75a81d Align vehicle contract tests with intended safety ownership 2026-10-22 17:44:38 -05:00
firestar5683 7f584d7e58 Deliver Ford MAIN rearm after prior feedback clears 2026-10-22 17:43:06 -05:00
Danny c777a9ec8a iOS: Add the Galaxy companion with live device pages and CarPlay
Add LAN and Bluetooth connections, secure pairing, device-matched Galaxy page
caching, phone location and CarPlay navigation. Include connection recovery,
diagnostics, regression tests and a simulator CI build. Add shared companion
docs and host tests, and remove obsolete bundled-web lint exceptions.
2026-10-22 15:37:20 -07:00
Danny f63e715d2d Android: Add the StarPilot companion and signed release workflow
Connect over LAN or Bluetooth with network discovery, build-matched Galaxy
pages, phone GPS and a background uplink. Guide pairing with readiness retries,
cancellable progress and bond cleanup. Include app updates, regression tests,
the current APK, and a workflow to test, build and sign releases.
2026-10-22 15:37:20 -07:00
Danny e11ce3071d Galaxy: Add companion, Spotify and phone-aware web controls
Add companion installation and updates, Spotify setup and playback, destination
sharing, and Android Auto controls. Adapt Galaxy pages to phone transports and
device type, improve Bluetooth pairing feedback, and cover the new web flows.
2026-10-22 15:37:20 -07:00
Danny a531afd02a Bluetooth: Coordinate companion pairing and Android Auto ownership
Expose pairing readiness and coordinate adapter hand-offs with Android Auto.
Show app pairings separately, support individual removal, and improve pairing
controls on both comma screens. Add Galaxy companion endpoints and web asset
manifests, preserve the plain camera look, and fix headless speed-limit shapes.
2026-10-22 15:37:20 -07:00
firestar5683 a57a08843a Maintain Auto Learn history through pauses 2026-10-22 17:35:15 -05:00
firestar5683 3c592b58b0 Recover Auto Learn capture after interruptions 2026-10-22 17:34:49 -05:00
firestar5683 b781fa90d7 Recognize four-byte RAV4 radar filter advertisement 2026-10-22 17:32:58 -05:00
firestar5683 59c29e6e9c Build Ford lateral session handoff firmware 2026-10-22 17:13:31 -05:00
firestar5683 ba906bb038 Start fresh Ford lateral sessions on driver activation 2026-10-22 17:13:02 -05:00
firestar5683 ab1aeda540 Restore Avalon smartDSU ownership 2026-10-22 17:10:18 -05:00
firestar5683 46051ce2b4 Restore legacy Toyota smartDSU ownership 2026-10-22 17:00:52 -05:00
firestar5683 5ac0bbab48 Enable Turn Signal Guidance by default 2026-10-22 16:57:10 -05:00
firestar5683 236fd3942f Build guarded Legacy Stop Start startup request 2026-10-22 16:56:39 -05:00
firestar5683 c6db508304 Restore guarded Legacy Stop Start startup request 2026-10-22 16:55:56 -05:00
firestar5683 3305afdf98 Enable torque tuning for every torque controller 2026-10-22 16:52:31 -05:00
firestar5683 08d38e9bdf Increase clear-road encouragement 2026-10-22 16:51:45 -05:00
firestar5683 c8bbe72a2a Correct Palisade AOL status relay ownership 2026-10-22 16:45:09 -05:00
Dom 57bfa98906 Fix Rivian and Sentry test static checks 2026-10-22 16:37:30 -05:00
firestar5683 38bee61910 Authorize EV9 lateral intent from physical cruise SET release 2026-10-22 16:30:49 -05:00
firestar5683 9748e4a449 Pulse newly displayed speed limits 2026-10-22 16:24:13 -05:00
firestar5683 ea61d3002c Restore Camry cluster radar parser 2026-10-22 16:21:46 -05:00
firestar5683 3f8f1c69e4 Restore Toyota camera pedal configurations 2026-10-22 16:17:12 -05:00
firestar5683 7ac512caf2 Build Subaru angle and LKAS display repair 2026-10-22 16:17:02 -05:00
firestar5683 e3070b7b42 Preserve Subaru angle bounds and current LKAS display 2026-10-22 16:16:26 -05:00
firestar5683 de28764611 Merge qualified Toyota angle and Small UI updates 2026-10-22 16:15:10 -05:00
firestar5683 3366652afa Build independent RAV4 angle lateral control 2026-10-22 16:14:16 -05:00
firestar5683 928aeb8fe2 Restore original Small UI speed limit sign 2026-10-22 16:07:32 -05:00
firestar5683 40afc1bad2 Restore independent RAV4 angle lateral control 2026-10-22 16:05:39 -05:00
firestar5683 ecc570756a Build guarded Subaru stop and go 2026-10-22 15:55:14 -05:00
firestar5683 a7c78e97b0 Restore guarded Dom Subaru stop and go 2026-10-22 15:54:33 -05:00
firestar5683 9b1bbf7d21 Turn Signal Guidance 2026-10-22 15:47:13 -05:00
firestar5683 e70f7ea88b Preserve the original EV6 output proof across an input timeout 2026-10-22 15:43:58 -05:00
firestar5683 e72e3a875b Restore classic Honda alpha lateral ownership 2026-10-22 15:40:40 -05:00
firestar5683 db741fd789 Cover unbelted lateral recovery fault-marker vetoes 2026-10-22 15:36:09 -05:00
firestar5683 2325f052f0 Fix Subaru software cruise parked settings import 2026-10-22 15:33:32 -05:00
firestar5683 b41fdc6545 Restore selected Honda and CAN-FD Panda ownership 2026-10-22 15:33:11 -05:00
firestar5683 c03714aab2 Merge qualified Mazda startup with planner test typing 2026-10-22 15:31:48 -05:00
firestar5683 b79ea58759 Build Mazda MAIN-on lateral startup 2026-10-22 15:28:42 -05:00
firestar5683 66f0ec1371 Restore Mazda MAIN-on lateral startup 2026-10-22 15:28:09 -05:00
Dom 40e84d4a54 Type encouragement planner test options 2026-10-22 15:25:44 -05:00
firestar5683 057b881da8 Build guarded Subaru software cruise 2026-10-22 15:21:12 -05:00
firestar5683 7924313af9 Restore Dom Subaru software cruise with guarded stock buttons 2026-10-22 15:20:37 -05:00
firestar5683 7ebd1b46a7 Fit compact speed limit sign to visible offset 2026-10-22 15:18:04 -05:00
firestar5683 6a9df653ab Build Rivian GEN1 always-on lateral 2026-10-22 15:17:58 -05:00
firestar5683 036d2f0fee Place Rivian H7 receive workspace in AXI SRAM 2026-10-22 15:17:26 -05:00
firestar5683 d0a252d34b Merge shared vehicle qualification updates 2026-10-22 15:17:11 -05:00
Dom 11b26d3511 Align Hyundai startup fixtures with staged handoff 2026-10-22 15:07:02 -05:00
firestar5683 17f3920a55 Restore Rivian GEN1 always-on lateral 2026-10-22 15:05:24 -05:00
firestar5683 456c1955a1 Restore Honda Nidec pedal lateral ownership 2026-10-22 14:59:39 -05:00
firestar5683 744032b961 Encouragement 2026-10-22 14:58:27 -05:00
firestar5683 6a773e7d2d Restore Camry and C-HR radar CAN filter ownership 2026-10-22 14:51:22 -05:00
Dom f87e6e4093 Audit concrete parameterized vehicle test classes 2026-10-22 14:46:12 -05:00
firestar5683 4c7854dea8 Preserve armed lateral intent during unbelted pause recovery 2026-10-22 14:38:52 -05:00
firestar5683 45a73a439b Restore Honda CAN-FD and alternate radar lateral ownership 2026-10-22 14:25:55 -05:00
firestar5683 8462a39b65 Build Volkswagen cold MAIN lateral startup 2026-10-22 14:13:32 -05:00
firestar5683 29546c710a Restore MQB cold MAIN lateral startup 2026-10-22 14:12:53 -05:00
firestar5683 d8d39d1b35 Restore Auto Learn producer admission 2026-10-22 14:08:07 -05:00
firestar5683 178c8694f2 Restore Mazda camera stop-sign observations 2026-10-22 14:00:06 -05:00
firestar5683 2985ed9f6d Restore independent EV9 lateral control with Alpha longitudinal 2026-10-22 13:56:27 -05:00
firestar5683 bd764bbf2e Reduce Chestnut model startup overhead 2026-10-22 13:52:19 -05:00
firestar5683 8e27decb08 Restore 2022 RAV4 radar CAN filter and lateral ownership 2026-10-22 13:43:31 -05:00
firestar5683 1382b62b00 Retain armed lateral intent through inactive control receipts 2026-10-22 13:41:06 -05:00
firestar5683 ffaa466852 Build RAV4 angle-control radar CAN filter ownership 2026-10-22 13:28:11 -05:00
firestar5683 0c0d4bc8cb Restore RAV4 angle-control radar CAN filter ownership 2026-10-22 13:26:13 -05:00
Dom c78aeed361 Align classic CI with safety build modes and fix test typing 2026-10-22 13:05:30 -05:00
firestar5683 c145bf602f Fix RAV4 Continental radar setting authorization 2026-10-22 13:00:15 -05:00
firestar5683 7e19d85b8b Build Subaru stock-cruise always-on lateral 2026-10-22 12:57:47 -05:00
firestar5683 4523d17ecd Restore supported Subaru stock-cruise always-on lateral 2026-10-22 12:57:05 -05:00
firestar5683 9306f865e8 Add Ioniq left paddle Bookmark assignment 2026-10-22 12:51:07 -05:00
Peter Nguyen 3464d40526 2026-10-22 Sentry Discord and webhook alerts attach the saved wide and cabin images again
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 12:34:39 -05:00
firestar5683 b51b8dafd6 Handle Galaxy update tunnel disconnects 2026-10-22 12:34:31 -05:00
firestar5683 46c752caea Build Volkswagen gateway OP-long lateral independence 2026-10-22 12:28:56 -05:00
firestar5683 01479b56b9 Restore gateway MQB OP-long lateral independence 2026-10-22 12:27:51 -05:00
firestar5683 a64dd65025 Fix replay torque bar freshness 2026-10-22 12:04:37 -05:00
firestar5683 838e93b934 Test Honda EPS recovery and Reverse native lifecycle 2026-10-22 11:59:59 -05:00
firestar5683 b88e2eccb9 Gate stock MQB AOL lifecycles in vehicle CI 2026-10-22 11:57:28 -05:00
firestar5683 86db7a63ba Build Volkswagen stock always-on lateral 2026-10-22 11:55:19 -05:00
firestar5683 6120da2d54 Restore stock MQB always-on lateral 2026-10-22 11:54:11 -05:00
firestar5683 5277fc139f Retain lateral intent while a newer native FULL grant is acknowledged 2026-10-22 11:45:13 -05:00
firestar5683 cef4eec062 Restore Galaxy update reconnect screen 2026-10-22 11:44:05 -05:00
firestar5683 2d10707628 Gate shared AOL lifecycles in vehicle CI 2026-10-22 11:37:43 -05:00
firestar5683 d0400456cf Keep neutral classic SCC replacement running during initialization 2026-10-22 11:26:01 -05:00
firestar5683 872de3b085 Warn when a soft fault withdraws independent lateral control 2026-10-22 11:23:45 -05:00
firestar5683 e6f7d9c473 Fix GM control-loop load and lateral recovery after lag 2026-10-22 11:22:03 -05:00
firestar5683 e57c9a5989 Retain stock Ford intent when braking overlaps temporary EPS loss 2026-10-22 11:19:01 -05:00
firestar5683 deefc26c85 Retain armed lateral intent when an axis publication expires 2026-10-22 11:15:57 -05:00
firestar5683 1f66903899 Recover Auto Learn after calibration delivery gaps 2026-10-22 11:11:11 -05:00
firestar5683 98a6ca21f3 Build Ford accelerator-override lateral safety firmware 2026-10-22 11:08:22 -05:00
firestar5683 a3b270cbfc Retain Ford lateral intent when cruise engages under accelerator override 2026-10-22 11:06:50 -05:00
firestar5683 3ce8e82c73 Retain Mazda lateral intent at standstill 2026-10-22 11:03:51 -05:00
firestar5683 0970a7b685 Retain Mazda lateral intent through Reverse 2026-10-22 10:59:50 -05:00
firestar5683 36ecda9f25 Exercise EV6 Alpha startup in release regression coverage 2026-10-22 10:55:07 -05:00
firestar5683 0cdc3287b5 Preserve Mazda lateral intent for mapped Cancel actions 2026-10-22 10:53:47 -05:00
firestar5683 d69a9f4db5 Restore Small UI sign and parked Galaxy offroad 2026-10-22 10:52:58 -05:00
firestar5683 9ab83d8d59 Refresh prebuilt inventory after Nissan test typing 2026-10-22 10:52:11 -05:00
Dom f8c91e6c62 Fix Nissan test CAN values typing 2026-10-22 10:46:20 -05:00
firestar5683 86be96c44c Withdraw Toyota pedal steering when MAIN expires 2026-10-22 10:17:30 -05:00
firestar5683 82f9995887 Build Nissan always-on lateral 2026-10-22 10:13:01 -05:00
firestar5683 9af5e64787 Reuse Nissan safety RX workspace 2026-10-22 10:10:36 -05:00
firestar5683 075b73e940 Restore Nissan stock-cruise always-on lateral 2026-10-22 10:05:08 -05:00
firestar5683 e914d761b1 Improve Auto Learn feedback 2026-10-22 08:44:35 -05:00
firestar5683 f33c8605da Preserve stock lateral intent after temporary steering faults 2026-10-22 08:37:24 -05:00
firestar5683 155b105a44 Restore mapped classic Hyundai MAIN and LKAS AOL controls 2026-10-22 08:23:03 -05:00
firestar5683 ce66b2ed7d Test Ford AOL retention across temporary location loss 2026-10-22 08:21:35 -05:00
firestar5683 d36a4b8888 Cover Chrysler stock ACC location recovery 2026-10-22 08:12:00 -05:00
firestar5683 fb943dd85a Retain qualified lateral intent across temporary withdrawals 2026-10-22 08:09:06 -05:00
firestar5683 371000a848 Preserve Ford AOL intent with the initial integrity epoch 2026-10-22 08:06:00 -05:00
firestar5683 33952c42b9 Build Tesla screen AOL safety firmware 2026-10-22 07:58:35 -05:00
firestar5683 7731bee396 Preserve Tesla screen AOL intent through temporary inhibition 2026-10-22 07:55:42 -05:00
firestar5683 74d2ef9698 Initialize classic AOL dash icons after profile finalization 2026-10-22 07:49:23 -05:00
firestar5683 ccdd137671 Build Lexus IS DSU MAIN AOL 2026-10-22 07:27:57 -05:00
firestar5683 5480ae3caa Restore Lexus IS DSU MAIN lateral ownership 2026-10-22 07:24:49 -05:00
firestar5683 2b42135302 Validate published prebuilt receipts before device updates 2026-10-22 07:04:46 -05:00
Prabhaav Pillai 8f2e33dead (Another eight) round of Galaxy UI bug fixes
Made card headers, icons, spacing, and controls more consistent across Galaxy pages.
Improved vehicle selection and Bluetooth device layouts.
Refined Model Manager filters and moved running model status into its four-item summary.
Moved Jetlink above Controls.
Updated UI tests to match the revised layouts.
2026-10-22 04:12:20 -04:00
Prabhaav Pillai 91e1ce8466 Improve error handling and UI elements for software status 2026-10-22 03:49:43 -04:00
Prabhaav Pillai ee83b3010a (Another seventh) round of Galaxy UI bug fixes
Improved consistency across Galaxy pages
Reworked the Model Manager layout, filters, downloads, randomizer, and Jetlink controls.
Improved vehicle selection and the Galaxy pairing and installation flow.
Fixed settings-row rendering, Android Auto upload timeouts, and Sentry event ID validation.
Updated tests for current UI behavior - all 102 JavaScript and 797 Python Galaxy tests pass.
2026-10-22 03:39:05 -04:00
firestarsdog d773f87897 ui: refine speed-limit signs and speed display hierarchy 2026-10-22 03:22:11 -04:00
firestarsdog d2dbeddcf1 ui: unify speed-limit signs for clearer confirmation cues 2026-10-22 02:49:00 -04:00
Dom aab66cd015 Fix device artifact test fixture for FLM native binding 2026-10-21 23:43:36 -05:00
firestar5683 b151ca11cb Global FLM Auto Learn 2026-10-21 23:28:39 -05:00
firestar5683 21c74f72ef Honor Panda fault policy before ECU takeover 2026-10-21 22:50:55 -05:00
firestar5683 4b9de265bf Handle Ioniq 6 native brake withdrawal before host sampling 2026-10-21 22:48:16 -05:00
firestar5683 70799d5bc3 Fix GM steering companion receive ordering 2026-10-21 22:45:01 -05:00
firestarsdog a5f18d35cd Restore C4 MAX fade; contain SLC offsets for readability 2026-10-21 23:12:12 -04:00
prabhaavp 9ab16c0838 Merge pull request #240 from dirwin31:3X-Widgets
Allow Full Widget Editing and Arrangement on 3X
2026-10-21 22:39:15 -04:00
Prabhaav Pillai 51dd8d48ce Removed duplicate font opacity handling 2026-10-21 22:38:59 -04:00
firestar5683 55a40280fb Merge latest tests with vehicle regression coverage 2026-10-21 21:18:38 -05:00
Danny f964651e60 No Bounds 2026-10-21 21:37:24 -04:00
Danny 4366e6ac7a Allow Full Widget Editing and Arrangement on 3X, add AA only widgets to 3X screen 2026-10-21 21:37:24 -04:00
firestar5683 55bbf6b98a Require emitted EV9 steering and select native test variants 2026-10-21 19:59:07 -05:00
Dom 77347170f8 Clarify SLC UI test preconditions and method mocks 2026-10-21 19:58:59 -05:00
firestar5683 d77bea5972 Pair GV60 longitudinal startup with exact native heartbeat safety 2026-10-21 19:56:15 -05:00
firestar5683 1973ad6439 Defer EV9 ECU takeover until replacement controls are ready 2026-10-21 19:37:46 -05:00
firestar5683 44beeb7606 Preserve HKG READY evidence across batched CAN frames 2026-10-21 18:51:48 -05:00
firestar5683 4854bc74b6 Preserve Mazda lateral control on unassigned Cancel 2026-10-21 18:38:17 -05:00
firestarsdog 3d2d094c8b SLC Fixes 2026-10-21 19:25:05 -04:00
firestarsdog 0972fec4ef SLC Styling 2026-10-21 19:25:05 -04:00
firestarsdog ab8368f287 Big SLC Overhaul 2026-10-21 19:22:59 -04:00
Dom 0299391cf8 Use pinned capnproto dependency in host heartbeat tests 2026-10-21 17:53:46 -05:00
firestar5683 db73e3bdbb Verify HKG AOL fault recovery against emitted steering commands 2026-10-21 16:49:14 -05:00
Dom 7edb10e149 Fix vision typing and projection HTTP test fixtures 2026-10-21 15:47:55 -05:00
firestar5683 4f3110af3a Preserve classic HKG AOL intent through location input faults 2026-10-21 15:41:17 -05:00
firestar5683 c7c3dbd699 Preserve HKG stock CAN-FD AOL intent during location input loss 2026-10-21 14:59:29 -05:00
firestar5683 58a4514321 Prepare ordinary HKG HDA-II takeover before publishing longitudinal control 2026-10-21 14:24:11 -05:00
firestar5683 4b3017a7fc Keep registered Ford CAN-FD discovery on normal bus 0 2026-10-21 13:59:11 -05:00
firestar5683 8e6cd917e4 Cover Ioniq 5 PE READY stock fallback with captured startup CAN 2026-10-21 12:56:37 -05:00
Dom ab9b2d7624 Make CI fixture preconditions and AST body types explicit 2026-10-21 12:49:11 -05:00
firestar5683 5ffbe27aaa Keep Ioniq 5 PE READY startup stock until ECU takeover is proven 2026-10-21 12:47:27 -05:00
firestar5683 66a573747d Prepare Ioniq 5 PE longitudinal takeover from a proven parked READY state 2026-10-21 12:45:05 -05:00
firestar5683 0c1f0b3d8d Retain Confirmed Vision Speed Limits 2026-10-21 12:43:45 -05:00
firestar5683 b3c26b5824 Match Dom hands-free cluster scope across Ford CAN-FD 2026-10-21 12:36:48 -05:00
firestar5683 43eea2e977 Restore Vision Speed Limits 2026-10-21 12:30:55 -05:00
firestar5683 3c687b55f7 Fix GM startup and steering receipt timing faults 2026-10-21 12:16:00 -05:00
prabhaavp 3372a71b4a Merge pull request #237 from dirwin31:PR-Smaller-3
Android Auto: Car screen widgets, navigation overlay, now playing and diagnostics
2026-10-21 13:11:34 -04:00
Prabhaav Pillai 9632656c59 Trim trivial verbose comments 2026-10-21 13:10:49 -04:00
Prabhaav Pillai 5a2e9d2606 AA documentation update and improve network recovery logic 2026-10-21 13:07:44 -04:00
firestar5683 1477a59fcd Prepare radar HDA-II takeover before publishing longitudinal control 2026-10-21 11:59:37 -05:00
firestar5683 8090e48441 Preserve HKG stock AOL intent through temporary location input loss 2026-10-21 11:44:58 -05:00
firestar5683 297f56f29a Match Dom Mach-E cluster messages with stock idle ownership 2026-10-21 11:32:09 -05:00
Dom 4fff2bba47 Assert captured messages before GM successor test mutations 2026-10-21 11:20:54 -05:00
firestar5683 4f1b668aea Keep Mach-E stock display ownership before lateral engagement 2026-10-21 11:10:14 -05:00
firestar5683 1319c60fda Preserve fresh GM lateral authority while braking 2026-10-21 10:54:26 -05:00
firestar5683 223c4f1322 Enforce current Hyundai startup evidence before controller output 2026-10-21 10:51:38 -05:00
Dom d3c0e9df8b Use typed CAN packets in Volt lifecycle fixture 2026-10-21 10:41:28 -05:00
firestar5683 e9abadd2f4 Capture F1 Volt withdrawal receipts in Card 2026-10-21 10:18:27 -05:00
firestar5683 9de201d18d Prepare radar HDA-I takeover before publishing longitudinal control 2026-10-21 09:54:27 -05:00
firestar5683 b300093058 Build conventional-cruise F1 Volt lateral startup 2026-10-21 09:52:58 -05:00
firestar5683 97a50dead6 Restore conventional-cruise F1 Volt lateral startup 2026-10-21 09:52:19 -05:00
Dom 210e6504fe Align updater test mocks with offroad status keyword 2026-10-21 09:39:51 -05:00
firestar5683 31d888ba79 Build RAV4 TSS2 pedal pairing 2026-10-21 09:35:25 -05:00
firestar5683 5ee8d662a7 Restore RAV4 TSS2 pedal interceptor pairing 2026-10-21 09:33:25 -05:00
firestar5683 c569a798ba Build Corolla TSS2 pedal pairing 2026-10-21 09:08:33 -05:00
firestar5683 f334ac0693 Restore Corolla TSS2 pedal interceptor pairing 2026-10-21 09:06:29 -05:00
firestar5683 defaf64c0e Allow Forced Offroad Updates 2026-10-21 08:31:44 -05:00
firestar5683 ba6eac264e Fix Big Model Cold Startup 2026-10-21 08:23:17 -05:00
firestar5683 4ed9546240 Test Subaru automatic stock startup ownership 2026-10-21 05:35:53 -05:00
firestar5683 1cbf3b927e Test Honda automatic stock startup and native handoff 2026-10-21 05:26:08 -05:00
firestar5683 a4e1631228 Keep Hyundai startup testers until controller publication succeeds 2026-10-21 04:12:13 -05:00
firestar5683 21ee77ea30 Prepare classic Hyundai ECU takeover before publishing longitudinal control 2026-10-21 03:41:28 -05:00
Dom fc6d6604aa Make bounded route inventory test limits explicit 2026-10-21 03:34:52 -05:00
firestar5683 cb85a84e30 Preserve AOL intent across native updates and classic cruise cancellation 2026-10-21 02:03:14 -05:00
firestar5683 708a68adeb Preserve Hyundai diagnostic settle time during stock restoration 2026-10-21 00:52:55 -05:00
firestar5683 67c1fb6a64 Preserve independent Bolt lateral below cruise engagement speed 2026-10-21 00:10:13 -05:00
firestar5683 fc2a89b988 Preserve GM MAIN intent through parked startup 2026-10-20 23:57:45 -05:00
firestar5683 f1434fe34d Retain Sonata Hybrid lateral intent when cruise is canceled 2026-10-20 23:51:40 -05:00
firestar5683 da628907d0 Preserve Hyundai diagnostic continuity across startup owners 2026-10-20 23:43:42 -05:00
firestar5683 5748148e7c Arm classic stock AOL after successful full engagement 2026-10-20 23:27:39 -05:00
firestar5683 8824bea044 Optimize FLM job encoding 2026-10-20 23:19:02 -05:00
firestar5683 2fb333f279 Build RAV4 Hybrid smartDSU ownership 2026-10-20 23:13:16 -05:00
firestar5683 4000b44866 Verify Toyota RX workspace reconfiguration 2026-10-20 23:13:14 -05:00
firestar5683 10be50fd7c Share Toyota RX checks across exact filter profiles 2026-10-20 23:08:42 -05:00
firestar5683 ad84d01235 Restore RAV4 Hybrid smartDSU ownership 2026-10-20 23:00:30 -05:00
firestar5683 ad3fa91b09 Restore recorded Sonata Hybrid diagnostic admission 2026-10-20 22:21:40 -05:00
firestar5683 01b06dcc71 Keep lateral renewal across stock cruise disengagement 2026-10-20 22:17:53 -05:00
Prabhaav Pillai 409601216b Build 2026-10-20 23:05:29 -04:00
Prabhaav Pillai d7db526f8c Revert "Add bundled Ion headers to device include path (WSL Fix)"
This reverts commit b209e6a655.
2026-10-20 23:05:04 -04:00
Prabhaav Pillai 4b1466f5eb Revert "Fix WSL device build headers and FFmpeg linkage"
Reverts the source changes from commit 470ab5cc70. The generated prebuilt manifest remains with the rebuilt artifacts.
2026-10-20 23:05:04 -04:00
firestar5683 51646bf4cb Receive fresh lateral inputs before withdrawing AOL 2026-10-20 22:04:30 -05:00
Dom 6ca072af44 Narrow test fixture types before mutation 2026-10-20 21:56:51 -05:00
firestar5683 ea916e50bd Restore classic Hyundai AOL steering dashboard icons 2026-10-20 21:43:04 -05:00
firestar5683 5cf5637d65 Detect refused steering on qualified SDGM Volts 2026-10-20 21:42:46 -05:00
firestar5683 2abc15c222 Preserve stock ASCM Volt lateral intent below cruise speed 2026-10-20 21:33:52 -05:00
firestarsdog 42d0beabee test: avoid build env cap'n proto links 2026-10-20 22:32:31 -04:00
firestar5683 f5f8a331f1 Preserve G90 diagnostic continuity through controller startup 2026-10-20 21:32:06 -05:00
firestar5683 a278aa8fba Preserve GPS daemon executable mode 2026-10-20 21:23:22 -05:00
firestar5683 ec856f38e6 Retain stock Ford AOL intent across short command gaps 2026-10-20 21:21:56 -05:00
firestar5683 f0f9b43bef Restore car GPS fixes on the standard logging channel 2026-10-20 21:21:52 -05:00
firestar5683 0b01007a9b Bound FLM replay memory 2026-10-20 21:20:01 -05:00
firestar5683 b9a8a41784 Build Jeep Grand Cherokee brake hold 2026-10-20 21:16:58 -05:00
firestar5683 2ad95d4176 Restore Jeep Grand Cherokee stock ACC brake hold 2026-10-20 21:12:26 -05:00
Danny e26202eae2 Bluetooth and Spotify: Phone audio, pairing bridge and Spotify player 2026-10-20 21:05:02 -05:00
firestar5683 4da8dd6c3f Build Panda authority-based siren warning 2026-10-20 21:02:58 -05:00
firestar5683 c0b16fe8cc Separate armed AOL heartbeat from Panda siren activity 2026-10-20 21:00:57 -05:00
Danny 7775d42443 FAQ update 2026-10-20 20:09:00 -04:00
Danny ab34e5d8a5 Trailing TLS OpenHeadUnit Fix 2026-10-20 20:09:00 -04:00
Danny 12b6dadf17 Android Auto: Car screen widgets, navigation overlay, now playing and diagnostics 2026-10-20 20:09:00 -04:00
prabhaavp 7ea822bbb1 Merge pull request #236 from dirwin31:PR-Smaller-2
Bluetooth and Spotify: Phone audio, pairing bridge and Spotify player
2026-10-20 20:06:28 -04:00
Prabhaav Pillai 86aa912748 Fix Tests for AA 2026-10-20 20:04:53 -04:00
Prabhaav Pillai 3c7898921b Safe app key removal after Bluetooth forget succeeds only 2026-10-20 20:03:38 -04:00
firestar5683 f6a03dce08 Build Ford AOL safety firmware 2026-10-20 18:52:57 -05:00
firestar5683 31c5010e11 Preserve Ford AOL intent across qualified physical pauses 2026-10-20 18:51:56 -05:00
Danny 7ea5058365 Bluetooth and Spotify: Phone audio, pairing bridge and Spotify player 2026-10-20 19:42:21 -04:00
firestar5683 7c9b45bb5b Build Tesla Pre-AP startup and safety artifacts 2026-10-20 18:39:50 -05:00
firestar5683 f4b8982ee0 Restore calibrated Tesla Pre-AP pedal control 2026-10-20 18:35:44 -05:00
Prabhaav Pillai 470ab5cc70 Fix WSL device build headers and FFmpeg linkage 2026-10-20 19:23:48 -04:00
Prabhaav Pillai cdcf6a0dd1 Build verified device artifacts 2026-10-20 19:22:40 -04:00
firestar5683 28a017e12e Reject stale and malformed Tesla HW1 radar observations 2026-10-20 18:03:22 -05:00
firestar5683 2a73dc50ff Restore Hyundai ECU response handling before longitudinal takeover 2026-10-20 18:02:47 -05:00
firestar5683 bd490d2ee3 Keep Transit steering angle encoding inside native limits 2026-10-20 18:01:32 -05:00
firestar5683 edb2be5716 Build Prius ZSS support 2026-10-20 17:59:09 -05:00
James f5c56bc631 ZSS support
Co-Authored-By: Erich Moraga <33645296+erichmoraga@users.noreply.github.com>
2026-10-20 17:56:01 -05:00
firestar5683 fc753b3900 Build Chrysler lateral intent 2026-10-20 17:48:18 -05:00
Dom b801a9ff42 Merge published navigation and device build updates 2026-10-20 17:44:27 -05:00
firestar5683 764b94f2ab Restore Chrysler stock ACC lateral intent 2026-10-20 17:41:20 -05:00
Dom de0092349d Fix navigation GPS carrier subscriptions
Use GPS transport services in navtilesd and MapFeed, preserving phone GPS through starpilotNavigation. Add startup subscription regressions.

Source-only correction; no prebuilt receipt or native artifacts changed.
2026-10-20 17:39:33 -05:00
Prabhaav Pillai b209e6a655 Add bundled Ion headers to device include path (WSL Fix) 2026-10-20 18:21:15 -04:00
prabhaavp d2db94e177 Merge pull request #235 from dirwin31:PR-Smaller-1
Core: Companion phone uplink, phone GPS, cellular off switch and usage stats
2026-10-20 18:16:33 -04:00
Prabhaav Pillai cfaf5b05b2 Refactor cellular data toggle descriptions for clarity and update GPS validation logic 2026-10-20 18:13:47 -04:00
Jeffrey Wong fdcf1db290 Build: keep the hash-pinned model guard libraries instead of recompiling them 2026-10-20 17:00:04 -05:00
firestar5683 68f0dde601 Build Volt conventional-cruise F1 firmware 2026-10-20 16:12:24 -05:00
firestar5683 19e0619ae9 Support no-camera Volt conventional cruise with F1 brakes 2026-10-20 16:09:48 -05:00
firestar5683 12d77dd2d6 Fix Ioniq reverse latch and left-paddle support 2026-10-20 15:49:56 -05:00
firestar5683 7d14e28c4d Build removed-camera Volt cruise-fault safety firmware 2026-10-20 15:43:46 -05:00
firestar5683 749dba88f5 Reject faulted cruise engagement on removed-camera Volts 2026-10-20 15:41:56 -05:00
firestarsdog 6bc930a07b Color border cleanup 2026-10-20 16:14:25 -04:00
firestarsdog 219dc02f11 Keep AOL cyan on gas without longitudinal control 2026-10-20 14:21:03 -04:00
firestar5683 37bd6981ad Build removed-camera Volt CAN isolation firmware 2026-10-20 13:03:25 -05:00
firestar5683 ded5368547 Keep removed-camera Volt traffic off the absent camera bus 2026-10-20 12:58:05 -05:00
firestar5683 aeb33f3ae7 Build Toyota stationary AOL continuity fix 2026-10-20 12:44:58 -05:00
firestar5683 9f2830b94b Merge current vehicle fixes for Toyota continuity 2026-10-20 12:41:08 -05:00
firestar5683 b07d3f94e2 Keep Toyota AOL armed through stationary output gaps 2026-10-20 12:41:07 -05:00
firestar5683 afd4cde972 Fix Volt source handoffs and transient clock sampling 2026-10-20 12:14:47 -05:00
firestarsdog d2cc8e282e + Gray Border Override for All Engagement
Update
2026-10-20 12:52:54 -04:00
Prabhaav Pillai 18505b6af7 Update updater status message for clarity 2026-10-20 12:09:10 -04:00
Prabhaav Pillai 223f1869eb Fix "This scan was incomplete. More local segments may exist." 2026-10-20 12:06:19 -04:00
firestar5683 c871086830 Build Bolt pedal AOL recovery firmware 2026-10-20 11:04:30 -05:00
Prabhaav Pillai 662e152627 Add DualShock 4 profile to joystick controller configurations Part 2 2026-10-20 12:02:59 -04:00
firestar5683 b432b14a3b Fix Bolt pedal startup, resume, and AOL override recovery 2026-10-20 10:58:04 -05:00
Prabhaav Pillai 1f733a74ef Add DualShock 4 profile to joystick controller configurations 2026-10-20 11:47:59 -04:00
Danny 2919499c61 Build qualified Honda AOL recovery firmware 2026-10-20 10:43:45 -05:00
Danny 9deea7d855 Honda: Keep longitudinal through temporary EPS faults and ignore camera CAN interrupt-rate faults 2026-10-20 10:41:03 -05:00
firestar5683 73c3ddd45c Keep HKG angle AOL armed through temporary stop restrictions 2026-10-20 09:59:17 -05:00
firestar5683 e96ba4205f Preserve stock HKG lateral ownership through delayed brake status 2026-10-20 09:29:55 -05:00
Danny 40abb31b4d Honda: Keep longitudinal through temporary EPS faults and ignore camera CAN interrupt-rate faults 2026-10-20 07:27:23 -07:00
Danny a8d121bf45 Core: Update developer panel tests for the cellular off switch 2026-10-20 06:08:45 -07:00
Danny 786a1981b6 Mapd: Ship the binary built with phone GPS support 2026-10-20 06:03:40 -07:00
Danny 8f628ba48b Core: Companion phone uplink, phone GPS, cellular off switch and usage stats 2026-10-20 06:03:37 -07:00
firestar5683 5c27cc667b Fix HKG brake continuity and duplicate PE startup receipts 2026-10-20 06:04:21 -05:00
firestar5683 9685faaed1 Build qualified HKG MAIN and angle AOL firmware 2026-10-20 03:09:13 -05:00
firestar5683 11a3272049 Preserve HKG MAIN rearm and mapped angle AOL cancellation 2026-10-20 03:08:45 -05:00
firestarsdog d9bd0a06a9 Galaxy Routes Gate Update 2026-10-20 03:34:53 -04:00
firestarsdog 651fc56ae6 Local Routes Explain 2026-10-20 03:27:17 -04:00
firestarsdog 63f6655dce Update camera colors 2026-10-20 03:04:39 -04:00
firestarsdog 2cb5b05746 Tweak disengaged muted effect 2026-10-20 02:45:39 -04:00
firestarsdog cda3693dd8 Unify Technicolor camera grading to distinguish engagement states 2026-10-20 02:20:29 -04:00
Prabhaav Pillai f23e0449ff Pause stars on slow scrolling in Firefox Mobile 2026-10-20 02:11:22 -04:00
Prabhaav Pillai 722ce72d33 Use restart background for Galaxy loading screen 2026-10-20 01:55:27 -04:00
Prabhaav Pillai 7f1196d00e Fix Galaxy update handoff and reuse boot screen 2026-10-20 01:49:49 -04:00
firestar5683 5b9713a042 Preserve qualified Sonata AOL through cruise CANCEL 2026-10-20 00:21:03 -05:00
3640 changed files with 551002 additions and 39999 deletions
+21 -1
View File
@@ -12,6 +12,14 @@ openpilot/selfdrive/modeld/models/big_*_tinygrad.pkl -text
*.woff2 -text
*.wav -text
openpilot/starpilot/galaxy/web/vendor/vue/vue.esm-browser.js -text
*.apk -text
openpilot/starpilot/galaxy/bin/hev-socks5-tunnel_linux_arm64 -text
openpilot/starpilot/galaxy/bin/hostapd_linux_arm64 -text
openpilot/starpilot/galaxy/bin/hostapd_cli_linux_arm64 -text
openpilot/starpilot/soloist/pulseaudio/bin/pulseaudio -text
openpilot/starpilot/soloist/pulseaudio/bin/pactl -text
openpilot/starpilot/soloist/bluealsa/bin/bluealsa-aplay -text
openpilot/starpilot/soloist/bluealsa/bin/bluealsad -text
openpilot/selfdrive/car/tests/test_models_segs.txt -text
openpilot/common/hardware/comma/updater -text
@@ -33,7 +41,6 @@ tinygrad_model_runtime/** !filter !diff !merge -text
*.a -text
*.so -text
*.so.* -text
openpilot/cereal/messaging/bridge -text
openpilot/selfdrive/pandad/pandad -text
openpilot/tools/replay/replay -text
openpilot/system/camerad/camerad -text
@@ -42,4 +49,17 @@ openpilot/system/loggerd/encoderd -text
openpilot/system/loggerd/loggerd -text
openpilot/starpilot/galaxy/bin/frpc_linux_arm64 -text
openpilot/starpilot/maps/provider/mapd -text
openpilot/tools/arabesque_firefox/offline/*.deb -text
openpilot/tools/arabesque_firefox/offline/*.tar.xz -text
openpilot/selfdrive/test/process_replay/refs/starpilot/*.zst -text
# Published Panda object files are AArch64 ELF, not text.
openpilot/selfdrive/pandad/main.o -text
openpilot/selfdrive/pandad/panda.o -text
openpilot/selfdrive/pandad/panda_safety.o -text
openpilot/selfdrive/pandad/panda_usb.o -text
openpilot/selfdrive/pandad/pandad.o -text
openpilot/selfdrive/pandad/spi.o -text
# Compressed recorded regression fixture; retain exact binary bytes.
openpilot/starpilot/tests/testdata/ford_mache_lead_departure.json.gz -text
+7 -3
View File
@@ -5,7 +5,7 @@ on:
branches:
- master
- Domathon
- SecretGoodStarPilot
- Dom
pull_request:
workflow_call:
inputs:
@@ -29,11 +29,15 @@ jobs:
steps:
- uses: commaai/timeout@v1
- uses: actions/checkout@v7
- run: ./tools/op.sh setup
with:
sparse-checkout: |
/docs/
/openpilot/selfdrive/assets/icons_mici/settings/comma_icon.png
sparse-checkout-cone-mode: false
# Build
- name: Build docs
run: ./tools/op.sh docs --build
run: python3 docs/serve.py --build
# Push to docs.comma.ai
- uses: actions/checkout@v7
+77
View File
@@ -0,0 +1,77 @@
name: FLM offroad contracts
on:
push:
branches: [Dom]
paths:
- 'openpilot/starpilot/flm/**'
- 'openpilot/starpilot/lateral/**'
- 'openpilot/starpilot/galaxy/web/js/flm*.js'
- 'openpilot/starpilot/galaxy/tests/test_flm*.mjs'
- 'openpilot/selfdrive/controls/lib/latcontrol*'
- 'openpilot/selfdrive/controls/controlsd.py'
- 'openpilot/system/manager/process_config.py'
- '.github/workflows/flm-offroad.yaml'
- 'pyproject.toml'
- 'uv.lock'
workflow_dispatch:
inputs:
source_sha:
description: Full candidate commit SHA to qualify without creating a branch
type: string
required: false
permissions:
contents: read
concurrency:
group: flm-offroad-${{ github.ref }}
cancel-in-progress: true
env:
CI: 1
PYTHONPATH: ${{ github.workspace }}
RAYLIB_BACKEND: headless
jobs:
contracts:
name: offroad backend contracts
runs-on: ubuntu-24.04
timeout-minutes: 20
steps:
- uses: actions/checkout@v7
with:
ref: ${{ inputs.source_sha || github.sha }}
- name: Verify exact source under test
env:
SOURCE_SHA: ${{ inputs.source_sha }}
run: |
git rev-parse HEAD
test -z "$SOURCE_SHA" || test "$(git rev-parse HEAD)" = "$SOURCE_SHA"
- run: ./tools/op.sh setup
- name: Check all FLM and integrated controller source
timeout-minutes: 1
run: |
ruff check openpilot/starpilot/flm openpilot/selfdrive/controls/controlsd.py openpilot/selfdrive/controls/lib/latcontrol_pid.py openpilot/selfdrive/controls/lib/latcontrol_torque.py openpilot/system/manager/process_config.py
ty check openpilot/starpilot/flm openpilot/selfdrive/controls/controlsd.py openpilot/selfdrive/controls/lib/latcontrol_pid.py openpilot/selfdrive/controls/lib/latcontrol_torque.py openpilot/system/manager/process_config.py
- name: Build native dependencies
timeout-minutes: 10
run: scons
- name: Test offroad recommendations, storage, startup and rollback contracts
timeout-minutes: 3
run: >-
tools/op.sh test
openpilot/starpilot/flm/tests/test_offroad_backend.py
--json-output ci-results/flm-offroad.json
- name: Test completed-drive and steering-result presentation
timeout-minutes: 1
run: |
node openpilot/starpilot/galaxy/tests/test_flm_training_results.mjs
node openpilot/starpilot/galaxy/tests/test_flm_tracking_results.mjs
- name: Retain offroad contract results
if: always()
uses: actions/upload-artifact@v4
with:
name: flm-offroad-contracts
path: ci-results/flm-offroad.json
if-no-files-found: warn
@@ -0,0 +1,59 @@
name: HKG angle recovery qualification
on:
push:
branches: [codex/hkg-angle-retention-20261106]
permissions:
contents: read
jobs:
recovery:
runs-on: ubuntu-24.04
timeout-minutes: 30
env:
PYTHONPATH: ${{ github.workspace }}
SCALE: '1'
RAYLIB_BACKEND: headless
steps:
- uses: actions/checkout@v7
- name: Fetch exact baseline source
run: git -c gc.auto=0 -c maintenance.auto=false fetch --no-tags --depth=1 --filter=blob:none origin 68434cf39c501604fe962e9488a03bde0fed0d73
- uses: actions/setup-go@v7
with:
go-version: '1.25.1'
cache-dependency-path: mapd_repo/go.sum
- name: Fetch pinned map provider dependencies
working-directory: mapd_repo
env:
GOTOOLCHAIN: local
run: go mod download
- run: ./tools/op.sh setup
- name: Build matching Linux dependencies
run: >-
.venv/bin/scons -j2
openpilot/starpilot/ui/native/_raylib_extra.so
openpilot/common/libparams_c.so
msgq_repo/msgq/_ipc.so
msgq_repo/msgq/visionipc/_visionipc.so
opendbc_repo/opendbc/can/_raw_value.so
opendbc_repo/opendbc/dbc
openpilot/selfdrive/controls/lib/longitudinal_mpc_lib/c_generated_code/acados_ocp_solver_pyx.so
openpilot/selfdrive/locationd/models/generated/libcar.so
- name: Run steering retention and independent safety veto regressions
run: |
.venv/bin/python -m pytest -q --junitxml=qualification.xml openpilot/starpilot/aol/tests/test_hkg_angle_eps_retention.py openpilot/starpilot/aol/tests/test_pe_combined_profile_contract.py openpilot/starpilot/aol/tests/test_stock_eps_recovery.py openpilot/starpilot/aol/tests/test_runtime.py openpilot/starpilot/aol/tests/test_source_pause_driver_monitoring.py openpilot/system/hardware/tests openpilot/starpilot/drive_state/tests/test_hardware_lifecycle.py openpilot/starpilot/ui/tests/test_weather.py openpilot/starpilot/flm/tests/test_pid_global_capture.py openpilot/starpilot/flm/tests/test_registered_controls_global.py::test_actual_controls_source_freshness_calibration_and_override_rejection openpilot/starpilot/lateral/tests/test_ev6_default.py openpilot/starpilot/lateral/tests/test_ioniq6_policy.py tools/ci/tests/test_replay_references.py
- name: Check owned Python files
run: |
.venv/bin/ruff check openpilot/starpilot/aol/temporary_steering.py openpilot/starpilot/aol/tests/test_hkg_angle_eps_retention.py openpilot/starpilot/aol/transport_pause.py openpilot/system/hardware/hardwared.py openpilot/system/hardware/hardware_timing.py openpilot/system/hardware/tests/test_hardware_timing.py openpilot/starpilot/ui/tests/test_weather.py openpilot/starpilot/lateral/tests/test_ev6_default.py openpilot/starpilot/lateral/tests/test_ioniq6_policy.py
.venv/bin/ty check openpilot/starpilot/aol/temporary_steering.py openpilot/starpilot/aol/tests/test_hkg_angle_eps_retention.py openpilot/starpilot/aol/transport_pause.py openpilot/system/hardware/hardwared.py openpilot/system/hardware/hardware_timing.py openpilot/system/hardware/tests/test_hardware_timing.py openpilot/starpilot/ui/tests/test_weather.py openpilot/starpilot/lateral/tests/test_ev6_default.py openpilot/starpilot/lateral/tests/test_ioniq6_policy.py
- name: Compare both reviewed default-controller recordings
run: .venv/bin/python openpilot/selfdrive/test/process_replay/test_processes.py --whitelist-cars HYUNDAI HYUNDAI2 --whitelist-procs controlsd --output reviewed-defaults -j2
- name: Save exact results
if: always()
uses: actions/upload-artifact@v4
with:
name: hkg-angle-recovery-results
path: |
baseline.xml
baseline-result.json
qualification.xml
reviewed-defaults/coverage.json
reviewed-defaults/diff.txt
+69
View File
@@ -0,0 +1,69 @@
name: iOS companion
on:
push:
branches: [Dom, secretgood-ios-integration]
paths:
- 'openpilot/starpilot/companion/ios/**'
- 'openpilot/starpilot/galaxy_bluetooth/**'
- '.github/workflows/ios-companion.yaml'
pull_request:
paths:
- 'openpilot/starpilot/companion/ios/**'
- 'openpilot/starpilot/galaxy_bluetooth/**'
- '.github/workflows/ios-companion.yaml'
workflow_dispatch:
permissions:
contents: read
concurrency:
group: ios-companion-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
jobs:
app:
name: Swift tests and iOS Simulator build
runs-on: macos-26
timeout-minutes: 25
defaults:
run:
working-directory: openpilot/starpilot/companion/ios
steps:
- uses: actions/checkout@v4
with:
submodules: false
lfs: false
persist-credentials: false
- uses: actions/setup-node@v4
with:
node-version: '22'
- name: Run companion regressions
env:
CI_PRIMARY_REPOSITORY_PATH: ${{ github.workspace }}/openpilot/starpilot/companion/ios
run: sh ci_scripts/ci_post_clone.sh
- name: Build for iOS Simulator
run: >-
xcodebuild -project GalaxyBluetooth.xcodeproj
-scheme GalaxyBluetooth -configuration Debug
-sdk iphonesimulator -destination 'generic/platform=iOS Simulator'
-derivedDataPath "$RUNNER_TEMP/galaxy-ios-build"
CODE_SIGNING_ALLOWED=NO build
bridge:
name: Comma Bluetooth bridge tests
runs-on: ubuntu-24.04
timeout-minutes: 10
steps:
- uses: actions/checkout@v4
with:
submodules: false
lfs: false
persist-credentials: false
- uses: actions/setup-python@v5
with:
python-version: '3.12'
- name: Install bridge test dependencies
run: python -m pip install pycryptodome
- name: Run bridge regressions
run: python -m unittest discover -s openpilot/starpilot/galaxy_bluetooth/tests -v
+1 -1
View File
@@ -2,7 +2,7 @@ name: safety qualification
on:
push:
branches: [master, Domathon, SecretGoodStarPilot]
branches: [master, Domathon, Dom]
pull_request:
workflow_dispatch:
+221 -10
View File
@@ -5,7 +5,7 @@ on:
branches:
- master
- Domathon
- SecretGoodStarPilot
- Dom
pull_request:
workflow_dispatch:
workflow_call:
@@ -34,13 +34,20 @@ jobs:
contents: read
steps:
- uses: actions/checkout@v7
- name: Validate published device build receipt
if: github.event_name == 'push' && github.ref == 'refs/heads/Dom'
run: python3 tools/ci/check_prebuilt_receipt.py --revision HEAD
- name: Check shipped device runtime dependencies
run: |
python3 -m unittest tools.laptop_device_build.tests.test_runtime_dependencies -v
python3 -c 'from pathlib import Path; assert b"starting pandad (comma hardware)" in Path("openpilot/selfdrive/pandad/pandad").read_bytes(), "Shipped pandad must use the comma hardware build profile"'
- name: Validate tracked dependencies
run: python3 tools/vendor/check.py --revision HEAD
- run: ./tools/op.sh setup
id: setup
- name: Build native dependencies for packaging tests
id: build
run: scons -j2 openpilot/common/libparams_c.so msgq_repo/msgq/ipc_pyx.so
run: scons -j2 openpilot/common/libparams_c.so msgq_repo/msgq/_ipc.so
- name: Test dependency tools
if: ${{ !cancelled() && steps.build.outcome == 'success' }}
run: python3 -m unittest discover -s tools/vendor/tests -v
@@ -50,6 +57,9 @@ jobs:
- name: Test CI tools
if: ${{ !cancelled() && steps.build.outcome == 'success' }}
run: python3 -m unittest discover -s tools/ci/tests -v
- name: Test authenticated monitoring release artifacts
if: ${{ !cancelled() && steps.build.outcome == 'success' }}
run: python3 -m pytest -q tools/ci/tests/test_stock_monitoring_release.py
- name: Test updater
if: ${{ !cancelled() && steps.build.outcome == 'success' }}
run: python3 -m unittest discover -s openpilot/system/updated/tests -v
@@ -191,22 +201,31 @@ jobs:
run: |
uv pip install --python .venv/bin/python --only-binary av av==19.0.1
.venv/bin/python -c "import av; assert 'libx264' in av.codecs_available"
- name: Provide pinned OpenCV DNN for vision contract tests
if: ${{ !cancelled() && steps.build.outcome == 'success' }}
run: |
uv pip install --python .venv/bin/python --no-deps --only-binary :all: opencv-python-headless==4.11.0.86
.venv/bin/python -c "import cv2; assert cv2.__version__ == '4.11.0'; assert callable(cv2.dnn.readNetFromONNX); assert callable(cv2.dnn.NMSBoxes)"
- name: Run unit tests
if: ${{ !cancelled() && steps.build.outcome == 'success' }}
timeout-minutes: ${{ contains(runner.name, 'nsc') && 2 || 20 }}
env:
RAYLIB_BACKEND: headless
STARPILOT_TEST_DIAGNOSTICS: ci-results/host-test-diagnostics/unittest
run: tools/op.sh test --json-output unit-test-results.json
- name: Test custom module-level pytest cases
if: ${{ !cancelled() && steps.build.outcome == 'success' }}
timeout-minutes: 15
env:
RAYLIB_BACKEND: headless
run: python3 tools/ci/run_host_tests.py --pytest
- name: Test GM vehicle configurations and CAN behavior
if: ${{ !cancelled() && steps.build.outcome == 'success' }}
timeout-minutes: 5
run: tools/op.sh test opendbc_repo/opendbc/car/gm/tests --json-output gm-test-results.json
- name: Test shared GM discovery, routing, and manual overrides
if: ${{ !cancelled() && steps.build.outcome == 'success' }}
timeout-minutes: 2
run: >-
.venv/bin/python -m pytest -q --import-mode=importlib
opendbc_repo/opendbc/car/gm/tests/test_shared_discovery.py
opendbc_repo/opendbc/car/gm/tests/test_automatic_discovery.py
opendbc_repo/opendbc/car/gm/tests/test_bolt_vin_discovery.py
--junitxml=gm-discovery-results.xml
- name: Test native independent-axis protocol
if: ${{ !cancelled() && steps.build.outcome == 'success' }}
run: |
@@ -214,6 +233,7 @@ jobs:
openpilot/selfdrive/pandad/tests/test_pandad_canprotocol
openpilot/selfdrive/pandad/tests/test_aol_protocol
openpilot/selfdrive/pandad/tests/test_aol_wire
openpilot/selfdrive/pandad/tests/test_panda_faults
openpilot/selfdrive/pandad/tests/test_producer_timing
openpilot/selfdrive/pandad/tests/test_producer_sink
- name: Test map provider to host messaging
@@ -227,13 +247,142 @@ jobs:
STARPILOT_PYTHON: ${{ github.workspace }}/.venv/bin/python
run: go test -mod=readonly -race -run '^Test(MsgqNativeLayout|Shadow(GoPythonIPC|ExecutableHostGpsIPC))$' -count=1 -v
- name: Save complete unit test results
if: ${{ always() && (hashFiles('unit-test-results.json') != '' || hashFiles('gm-test-results.json') != '') }}
if: ${{ always() && (hashFiles('unit-test-results.json') != '' || hashFiles('gm-test-results.json') != '' || hashFiles('gm-discovery-results.xml') != '' || hashFiles('ci-results/host-test-diagnostics/**') != '') }}
uses: actions/upload-artifact@v4
with:
name: unit-test-results
path: |
unit-test-results.json
gm-test-results.json
gm-discovery-results.xml
ci-results/host-test-diagnostics/
if-no-files-found: error
custom_pytest:
name: custom pytest (${{ matrix.shard }})
runs-on: ubuntu-24.04
timeout-minutes: 35
permissions:
contents: read
strategy:
fail-fast: false
matrix:
shard: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31]
steps:
- uses: actions/checkout@v7
- name: Provide JavaScript runtime for Galaxy contract tests
uses: actions/setup-node@v7
with:
node-version: '24'
package-manager-cache: false
- name: Provide pinned map provider toolchain
uses: actions/setup-go@v7
with:
go-version: '1.25.1'
cache-dependency-path: mapd_repo/go.sum
- name: Fetch pinned map provider dependencies
working-directory: mapd_repo
env:
GOTOOLCHAIN: local
run: go mod download
- run: ./tools/op.sh setup
id: setup
- name: Build openpilot
id: build
run: scons
- name: Provide H.264 encoder for Android Auto integration tests
if: ${{ !cancelled() && steps.build.outcome == 'success' }}
run: |
uv pip install --python .venv/bin/python --only-binary av av==19.0.1
.venv/bin/python -c "import av; assert 'libx264' in av.codecs_available"
- name: Test every assigned custom pytest node serially
if: ${{ !cancelled() && steps.build.outcome == 'success' }}
timeout-minutes: 15
env:
RAYLIB_BACKEND: headless
run: .venv/bin/python -m tools.ci.custom_pytest_shards run --index "${{ matrix.shard }}" --count 32 --output "ci-results/custom-pytest-${{ matrix.shard }}"
- name: Save collection, every outcome, JUnit and durable worker diagnostics
if: always()
uses: actions/upload-artifact@v4
with:
name: custom-pytest-${{ matrix.shard }}
path: ci-results/custom-pytest-${{ matrix.shard }}/
if-no-files-found: error
custom_pytest_complete:
name: complete custom pytest catalog
needs: custom_pytest
if: ${{ always() && !cancelled() }}
runs-on: ubuntu-24.04
permissions:
contents: read
steps:
- uses: actions/checkout@v7
- uses: actions/download-artifact@v4
with:
pattern: custom-pytest-*
path: ci-results/custom-pytest-shards
- name: Require every case passed or audited capability-unavailable
run: python3 -m tools.ci.custom_pytest_shards aggregate --directory ci-results/custom-pytest-shards --count 32 --output ci-results/custom-pytest-complete.json
- name: Save complete custom catalog verdict
if: always()
uses: actions/upload-artifact@v4
with:
name: custom-pytest-complete
path: ci-results/custom-pytest-complete.json
if-no-files-found: error
aol_lifecycles:
name: AOL lifecycles (${{ matrix.shard }})
runs-on: ubuntu-24.04
timeout-minutes: 35
permissions:
contents: read
strategy:
fail-fast: false
matrix:
shard: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
env:
FUZZ_SEED: '0'
steps:
- uses: actions/checkout@v7
- run: ./tools/op.sh setup
- name: Build native host dependencies
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()
uses: actions/upload-artifact@v4
with:
name: aol-lifecycle-${{ matrix.shard }}
path: ci-results/aol-lifecycle-${{ matrix.shard }}/
if-no-files-found: error
aol_lifecycle_complete:
name: complete AOL lifecycle catalog
needs: aol_lifecycles
if: ${{ always() && !cancelled() }}
runs-on: ubuntu-24.04
permissions:
contents: read
steps:
- uses: actions/checkout@v7
- uses: actions/download-artifact@v4
with:
pattern: aol-lifecycle-*
path: ci-results/aol-lifecycles
- name: Require every full-catalog case exactly once and passed
run: python3 tools/ci/lifecycle_shards.py ci-results/aol-lifecycles --count 16 --output ci-results/aol-lifecycle-complete.json
- name: Save complete catalog verdict
if: always()
uses: actions/upload-artifact@v4
with:
name: aol-lifecycle-complete
path: ci-results/aol-lifecycle-complete.json
if-no-files-found: error
map_provider:
@@ -269,7 +418,7 @@ jobs:
strategy:
fail-fast: false
matrix:
suite: [interfaces, safety-debug, safety-release]
suite: [interfaces, safety-debug]
env:
FUZZ_SEED: '0'
steps:
@@ -295,6 +444,59 @@ jobs:
path: ci-results/${{ matrix.suite }}/
if-no-files-found: error
safety_release_contracts:
name: RELEASE vehicle contracts (${{ matrix.shard }})
runs-on: ubuntu-24.04
timeout-minutes: 35
permissions:
contents: read
strategy:
fail-fast: false
matrix:
shard: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
env:
FUZZ_SEED: '0'
steps:
- uses: actions/checkout@v7
- run: ./tools/op.sh setup
- name: Build native host dependencies
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 }}"
- name: Save every RELEASE outcome and native build
if: always()
uses: actions/upload-artifact@v4
with:
name: safety-release-${{ matrix.shard }}
path: ci-results/safety-release-${{ matrix.shard }}/
if-no-files-found: error
safety_release_complete:
name: complete RELEASE vehicle catalog
needs: safety_release_contracts
if: ${{ always() && !cancelled() }}
runs-on: ubuntu-24.04
permissions:
contents: read
steps:
- uses: actions/checkout@v7
- uses: actions/download-artifact@v4
with:
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
- name: Save complete RELEASE verdict
if: always()
uses: actions/upload-artifact@v4
with:
name: safety-release-complete
path: ci-results/safety-release-complete.json
if-no-files-found: error
recorded_vehicles:
name: recorded vehicles (${{ matrix.variant }})
runs-on: ubuntu-24.04
@@ -398,6 +600,15 @@ jobs:
with:
name: process_replay_diff.txt
path: openpilot/selfdrive/test/process_replay/diff.txt
- name: Save complete replay outputs for review
uses: actions/upload-artifact@v7
if: always()
with:
name: process-replay-outputs
path: |
openpilot/selfdrive/test/process_replay/fakedata/*.zst
openpilot/selfdrive/test/process_replay/coverage.json
if-no-files-found: error
- name: Upload diff report
uses: actions/upload-artifact@v7
if: always() && github.event_name == 'pull_request'
+7 -1
View File
@@ -18,6 +18,7 @@ a.out
.venv-linux-arm64/
bin/
!openpilot/starpilot/galaxy/bin/
!openpilot/starpilot/soloist/pulseaudio/bin/
# created at launch for comma hardware PYTHONPATH (PC uses editable installs via pyproject.toml)
/msgq
@@ -44,8 +45,12 @@ bin/
*.os-*
*.so
!openpilot/tools/replay/libmedia_mux.so
!openpilot/starpilot/soloist/pulseaudio/lib/*.so
!openpilot/starpilot/soloist/pulseaudio/modules/*.so
*.a
st[0-9A-Za-z][0-9A-Za-z][0-9A-Za-z][0-9A-Za-z][0-9A-Za-z][0-9A-Za-z]
!/openpilot/starpilot/system/starport/
!/openpilot/tools/starport/
*.unchunked
*.clb
*.class
@@ -69,7 +74,6 @@ openpilot/selfdrive/pandad/tests/test_aol_wire
openpilot/selfdrive/pandad/tests/test_panda_usb
openpilot/cereal/services.h
openpilot/cereal/gen
openpilot/cereal/messaging/bridge
openpilot/selfdrive/ui/translations/tmp
openpilot/selfdrive/car/tests/cars_dump
openpilot/system/camerad/camerad
@@ -138,3 +142,5 @@ openpilot/selfdrive/modeld/models/*.unchunked
/jetlink_repo/jetlink.egg-info/
# Local Google Play resolver build output (source and lockfile are versioned).
openpilot/starpilot/flm/_primitive_walk.cpp
+26 -8
View File
@@ -2,7 +2,7 @@
## Jetlink external inference
The comma-side Jetlink client and USB transport in `jetlink_repo` are by Zeph Leggett and the [Jetlink contributors](https://github.com/zoompilot/jetlink). They are vendored from revision `d24328d849b0e5154b55f149dc18c5dfe8bd79cc` under the MIT license, retained in [jetlink_repo/LICENSE](jetlink_repo/LICENSE). StarPilot's adapter (`openpilot/starpilot/models/jetlink_adapter.py`) builds on Jetlink's openpilot layer and owns its param names, Always On Lateral rule, model runner reset, settings and alerts. Companion applications and inference servers remain distributed by Jetlink.
The comma-side Jetlink client and USB transport in `jetlink_repo` are by Zeph Leggett and the [Jetlink contributors](https://github.com/zoompilot/jetlink). They are vendored from revision `9cfde98eec1d7cc714d800504944cf1a3944513d` under the MIT license, retained in [jetlink_repo/LICENSE](jetlink_repo/LICENSE). StarPilot's adapter (`openpilot/starpilot/models/jetlink_adapter.py`) builds on Jetlink's openpilot layer and owns its param names, Always On Lateral rule, model runner reset, settings and alerts. Companion applications and inference servers remain distributed by Jetlink.
## MRR35 Hyundai angle and radar
@@ -12,16 +12,34 @@ The copied MRR35 generator source and DBC definition are at `opendbc_repo/opendb
## Ford support adapted from BluePilot
The Ford lateral strategies, extended CAN constructors, camera-radar filtering and native checks are adapted from StarPilot `2efe6017bfe14cee4807833e05f9a7f6ba255cdb`. Their lineage includes BluePilot's `bp-7.0` Ford work, principally by Alan Polk, with contributions from John Christman, Jacob Neulight, tonesto7, Haibin Wen and other sunnypilot and BluePilot contributors. The reference snapshot is [`e1d051d7ba270261b4455068bd68f1a58db15a4a`](https://github.com/BluePilotDev/bluepilot/commit/e1d051d7ba270261b4455068bd68f1a58db15a4a); the exact checkout used for the earlier StarPilot import was not recorded.
Ford lateral control vendors [BluePilot lateral](https://github.com/BluePilotDev/bluepilot-lateral) v0.2.1 (`2b9dc6cc784faac47a1885d0bda0561f7b76f74d`), developed by Alan Polk (`alan-polk`) and the BluePilot contributors. Alan provided the [StarPilot integration in PR #269](https://github.com/StarPilotHQ/StarPilot/pull/269); his commits and authorship are retained. BluePilot maintains the library and its integration; StarPilot keeps its settings, AOL policy, packaging and integration tests separate. Complete contributor and license notices remain in [the library credits](opendbc_repo/opendbc/bluepilot_lateral/CREDITS.md) and [license](opendbc_repo/opendbc/bluepilot_lateral/LICENSE).
Relevant lineage includes the controller and integration work in [`db2bdff05`](https://github.com/BluePilotDev/bluepilot/commit/db2bdff05df103d71df62f45c2a3cb5211aba6e6) and [`d0aac605f`](https://github.com/BluePilotDev/bluepilot/commit/d0aac605f99d37e9da205e419f7989c1e9eaa386), extended CAN and safety work in [`8f8d6d15f`](https://github.com/BluePilotDev/bluepilot/commit/8f8d6d15f0a590f42b78de964ffb0d0af7f5d63d), and manual-turn detection in [`97867c1eb`](https://github.com/BluePilotDev/bluepilot/commit/97867c1eb57b7472f6fc3de62f0fef576e5a5497).
The Ford CAN FD camera-lead radar filtering is adapted from StarPilot `2efe6017bfe14cee4807833e05f9a7f6ba255cdb`. Its lineage includes BluePilot's `bp-7.0` Ford work, principally by Alan Polk, with contributions from John Christman, Jacob Neulight, tonesto7, Haibin Wen and other sunnypilot and BluePilot contributors. The reference snapshot is [`e1d051d7ba270261b4455068bd68f1a58db15a4a`](https://github.com/BluePilotDev/bluepilot/commit/e1d051d7ba270261b4455068bd68f1a58db15a4a); the exact checkout used for the earlier StarPilot import was not recorded.
The vehicle strategy and CAN/native owners preserve that attribution while adapting to the current controller interfaces and shared safety limits. Upstream contributors do not maintain or endorse this adaptation. Applicable notices are retained in [THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md).
## Optional Ford angle controller
The vendored `opendbc_repo/opendbc/bluepilot_lateral` angle core, codecs and Ford-only safety hooks are adapted directly from [BluePilot bp-7.0 e1d051d7ba270261b4455068bd68f1a58db15a4a](https://github.com/BluePilotDev/bluepilot/commit/e1d051d7ba270261b4455068bd68f1a58db15a4a), principally by Alan Polk, with John Christman, Jacob Neulight, Nathan Ingraham, Praeuner and other BluePilot, sunnypilot and comma contributors. Exact source paths and intentionally stricter safety checks are recorded in that folder. Both upstream `LICENSE` and `LICENSE.md` notices are retained. Its layout follows Alan Polk's September 2026 library proposal; this is a StarPilot adapter, not a published BluePilot library release.
The radar interface preserves that attribution while adapting to the current interfaces. Upstream contributors do not maintain or endorse this adaptation. Applicable notices are retained in [THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md).
## RAV4 Continental ARS510 receiver
The optional ACC-target and fused-track receivers and DBC preserve firestar5683's Dom integration from `b220a37f4bd119e402225a7aa16a7cd84878d0af` (Sea Turtles, 2026-10-13) and its ARS510 source attribution to ds-sebastian/ars510-radar `fb5b29c2b5999f34afeb6f7ac494a73949d66c67`, copyright 2026 ARS510 radar contributors. The complete MIT permission and warranty notice is retained in THIRD_PARTY_NOTICES.md. Startup/settings ownership is adapted to the current StarPilot architecture; no original firmware binaries or safety permissions are copied.
## Spotify Bluetooth output
Spotify's Bluetooth output binds the comma's BlueALSA A2DP source to the selected speaker through a process-local ALSA configuration, leaving the system ALSA default, soundd and the warning speaker untouched. That approach is adapted from firestar5683's RoadScore Bluetooth output (`roadscore/prototype/bluetooth_output.py`, firestar5683/StarPilot `RoadScore` at `249eb88b3a397db847e4a595d7e23b7e85d329e5`). The Soloist player itself is never included: each user downloads it from Spotify, and the PulseAudio server that carries its audio is vendored unmodified from Ubuntu (see THIRD_PARTY_NOTICES.md).
## Tesla Pre-AP pedal control
The Pre-AP configuration, pedal codec/feedback, VirtualDAS feedforward table, grade compensation, torque-zero estimator and vehicle-specific gains preserve firestar5683's intentional Dom work, including `7837a3c1aab`, `aed0b7fdb9e9` and pinned `b220a37f4bd119e402225a7aa16a7cd84878d0af`. Original author attribution: firestar5683 <168790843+firestar5683@users.noreply.github.com>. Startup/calibration ownership, current upstream button/PID interfaces and native command bounds are adapted to the current StarPilot architecture; the old permissive native gateway is not transplanted. Existing source/license notices remain intact.
## Subaru angle steering from Jacob Waller's fork
Subaru angle support includes implementation maintained and integrated by **Jacob Waller (jacobw)** in [jacobwaller/openpilot, jul-angle-based](https://github.com/jacobwaller/openpilot/tree/542816196a18dba5f6f0af093c1ece4f415b5a22), with its [opendbc implementation at 8acbeccdab45a47aa7af170d2d17b61f0eb5e4f0](https://github.com/jacobwaller/opendbc/tree/8acbeccdab45a47aa7af170d2d17b61f0eb5e4f0). Jacob authored the VehicleModel angle-limit type correction and integrated the Crosstrek support. The branch also retains the original angle implementation and follow-up work by **lukasloetkolben and elkoled**, alongside comma contributors; their authorship remains theirs.
The inherited source covers Subaru angle platform/firmware definitions, `ES_Status` cruise state, angle controller/CAN integration, vehicle-model limits and native angle enforcement. StarPilot has additional platform-specific limits, driver handoff, discovery and AOL safety guards; this attribution does not claim identical tuning or complete parity with the reference fork. Original source commits, author names, emails and titles are recorded in [the Subaru origin history](opendbc_repo/opendbc/car/subaru/ORIGIN_HISTORY.tsv).
The explicit StarPilot [jacobw Subaru adapter](opendbc_repo/opendbc/car/subaru/JACOBW.md) additionally integrates that branch's low-speed filter, target-first limiting and Crosstrek EPS envelope. Its preserved stricter guards and platform scope are documented alongside the code.
## StarPilot Ford demand tuning
The previous optional StarPilot Controller, formerly in `openpilot/starpilot/car/ford/starpilot_tuning.py`, restored demand tuning from StarPilot `8da0c9d916^`, descended from BluePilot bp-7.0 `e1d051d7ba270261b4455068bd68f1a58db15a4a` through StarPilot `2efe6017bfe14cee4807833e05f9a7f6ba255cdb`. Contributors to that lineage include Alan Polk, John Christman, Jacob Neulight, tonesto7 and Haibin Wen. The current protocol adapter is maintained by StarPilot separately from the pinned BluePilot library; this attribution does not imply BluePilot maintains or endorses the adapted tune.
The current StarPilot Controller replaces that historical curvature-primary tune with a separately maintained adaptation of BluePilot v0.2.1's angle strategy (`2b9dc6cc784faac47a1885d0bda0561f7b76f74d`, `core/lateral_angle_ext.py`). Alan Polk is the principal controller author; John Christman contributed angle-mode anti-stall work, Jacob Neulight the pinion/shadow measurement, Nathan Ingraham and Praeuner high-speed damping, and other contributors are retained in the pinned library's `CREDITS.md`. StarPilot adds bounded small-signal conditioning ahead of the unchanged angle limit chain. This adaptation is not maintained or endorsed by BluePilot; the pinned library and its imported hooks are unchanged.
+202
View File
@@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
+87 -11
View File
@@ -1,16 +1,90 @@
# StarPilot
StarPilot is an independent fork of [openpilot](https://github.com/commaai/openpilot), with custom driving controls, vehicle integrations, raylib interfaces for comma 3 and comma four, and the Galaxy companion interface.
**Domathon is the development branch for StarPilot 7.0.** It rebuilds StarPilot on a current openpilot foundation. Feature migration and vehicle qualification are still in progress; this branch is not a completed replacement for the existing Dom release.
[![Ask DeepWiki](https://img.shields.io/badge/Ask-DeepWiki-blue?logo=readthedocs&logoColor=white)](https://deepwiki.com/StarPilotHQ/StarPilot)
[![Discord](https://img.shields.io/discord/1387432184121393333?label=Discord)](https://firestar.link/discord)
[![Last Updated](https://img.shields.io/github/last-commit/StarPilotHQ/StarPilot/StarPilot)](https://github.com/StarPilotHQ/StarPilot)
[![Wiki](https://img.shields.io/badge/Wiki-StarPilot-blue?logo=wiki)](https://wiki.firestar.link)
[![iOS App Store](https://img.shields.io/badge/iOS-App_Store-black?logo=apple&logoColor=white)](https://apps.apple.com/ca/app/starpilot-bt/id6821039119)
[![Android Beta](https://img.shields.io/badge/Android-Join_Beta-green?logo=android&logoColor=white)](https://groups.google.com/g/galaxy-starpilot-android-beta)
## Explore the project
**StarPilot** is a custom fork of [comma.ai's openpilot](https://comma.ai/openpilot),
an open source driver assistance system.
- [Developer commands](tools/STARPILOT_DEVELOPMENT.md): host tools, replay, and device builds.
- [Driving interfaces](openpilot/starpilot/ui/README.md) and [Galaxy](openpilot/starpilot/galaxy/README.md): native and companion controls.
- [Driving models](openpilot/starpilot/models/README.md): model management, artifact compatibility, and Model Laboratory status.
- [Builds and developer tools](docs/how-to/laptop-device-build.md): local development and device builds.
- [Dependency maintenance](tools/vendor/README.md): tracked source folders and upstream sync records.
**This is the StarPilot 7.0 development branch (`SecretGoodStarPilot`). Feature migration,
vehicle qualification, and Auto Learn validation are still in progress.**
Openpilot provides
* Automated Lane Centering
* Adaptive Cruise Control
* Lane Change Assist
* Driver Monitoring *without wheel nags*
Ford-specific lateral-control and vehicle-support work includes substantial adaptations from
[BluePilot](https://github.com/BluePilotDev/bluepilot/tree/bp-7.0), principally developed by
[Alan Polk](https://github.com/alan-polk) with additional BluePilot contributors. StarPilot's
implementation has since diverged, but that does not erase its lineage. See [CREDITS.md](CREDITS.md)
for the code-level provenance and [THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md) for applicable
upstream notices and terms. BluePilot and its contributors do not maintain or endorse StarPilot;
please direct support requests for this adaptation to the StarPilot project.
Hyundai, Kia, and Genesis angle steering and related vehicle support include substantial adaptations
from [sunnypilot](https://github.com/sunnypilot/sunnypilot/tree/hkg-angle-steering-2025) and its
[opendbc angle-steering branch](https://github.com/sunnypilot/opendbc/tree/hkg-angle-steering-2025).
StarPilot's implementation has diverged significantly; the upstream contributors do not maintain
this adaptation. Detailed code lineage is recorded in [CREDITS.md](CREDITS.md).
StarPilot has a vibrant, welcoming community [discord](https://firestar.link/discord).
Stop by to chat or ask questions!
If your car isn't driving as well as you'd like, [open a ticket so we can help](https://firestar.link/logs)!
## Documentation
Please see [https://wiki.firestar.link](https://wiki.firestar.link) for hardware lists,
installation guides, and software configuration.
## Features
* **Full support for Comma C3, C3X, and C4**
* Full support for chestnut/eGPU on C3, C3X, and C4. C3/C3X may require special gps handling.
* Model switcher with all of comma's tinygrad driving models
* StarPilot custom torque controller
* StarPilot custom longitudinal planner for both radar and VoACC (visual only, radar-less) vehicles
* **Custom tuning for an expanding list of vehicles**
* Firestar Lateral Method -
Auto tuning system to adapt openpilot to *your* car's unique lateral characteristics.
* Galaxy: StarPilot's portal to configure your comma device using your phone from anywhere.
Download models, change settings, update software, diagnostics, manage dashcam video, and more.
* Navigation - Display routes and give the driving model hints on taking exits and turns.
* Always On Lateral (full time steering assist)
* Lane Centering - Enable to center between detected lane lines,
with graceful fallback to model E2E lateral outputs and safety breakout
* Force Stop 2.0 - Better stops at stop signs and traffic lights.
* Speed Limit Controller - Use dashboard, map data, and/or vision detection for speed limit signs
* Curve Speed Controller - Learns from how you take curves
* Conditional Experimental Mode (CEM)
* Conditional Chill Mode (CCM)
* Driving Profiles and Traffic Mode
* Custom themes
* Alert volume customization
* Comma Pedal Interceptor support
* Bluetooth support - Connect keyboards, controllers, steering wheel remotes, and more.
* Sentry mode - Record when motion is detected while parked.
* V-ASM - Detect vehicles in the adjacent/blind spots using the driver-facing camera.
* Additional car support - Toyota SDSU, ZSS, CAN filter; pre-AP tesla; ford; Polestar 2; and more.
* And more!
## GM-only Features
* Increased LKAS fault resiliency
* ASCM_INT and SASCM support
* Custom lateral torque controller, with special tuning for Bolts
* 50% extra torque on 2017 Chevy Bolt
* Improved lateral and longitudinal tuning
* Dashboard cruise control display speed spoofing for vehicles with pedal interceptor
* Extra steering wheel button functionality for vehicles with pedal interceptor
* Optional toggle to boot comma when remote starting your vehicle
## Development
@@ -36,8 +110,10 @@ StarPilot retains upstream vehicle and Panda safety interfaces and adds regressi
Support depends on the vehicle configuration and enabled feature. An existing vehicle identifier or settings page does not by itself establish driving support. Model Laboratory currently exposes management controls, but paired driving inference remains unavailable pending integration and Chestnut validation.
## Upstream and attribution
## Third-Party Notices
The current openpilot baseline is [`78dccf0b482ea1298fd1f6fa59b0554187b02ce6`](https://github.com/commaai/openpilot/commit/78dccf0b482ea1298fd1f6fa59b0554187b02ce6). Its history remains intact, followed by the StarPilot foundation and feature commits. Upstream development continues independently; the baseline changes only after an update has been integrated and checked.
* Portions of this software include modified versions of the Material Design Icons provided by Google under the Apache License 2.0. A copy of the license is included in the `LICENSE-MDI` file.
* Ford support includes software adapted from BluePilot's `bp-7.0` branch. The source repository contains both a standard MIT notice and a separate custom SUNNYPILOT LLC notice; StarPilot preserves both in [THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md).
* Hyundai, Kia, and Genesis support includes software adapted directly from sunnypilot's HKG angle-steering branch and sunnypilot/opendbc. StarPilot preserves the applicable notices in [THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md).
See [LICENSE](LICENSE), [THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md), and [CREDITS.md](CREDITS.md) for licensing and source attribution. Component licenses remain with their source folders. StarPilot is maintained independently of comma.
> This project uses software from Haibin Wen and SUNNYPILOT LLC and is licensed under a custom license requiring permission for use.
+18 -16
View File
@@ -57,7 +57,7 @@ assert arch in [
"Darwin", # macOS arm64 (x86 not supported)
]
pkg_names = ['acados', 'capnproto', 'ffmpeg', 'json11', 'ncurses', 'zeromq', 'zstd']
pkg_names = ['acados', 'capnproto', 'ffmpeg', 'json11', 'ncurses', 'zstd']
if arch != 'Darwin':
pkg_names.append('libusb')
pkgs = [importlib.import_module(name) for name in pkg_names]
@@ -172,7 +172,7 @@ env = Environment(
COMPILATIONDB_USE_ABSPATH=True,
REDNOSE_ROOT="#rednose_repo",
tools=["default", "cython", "compilation_db", "rednose_filter"],
toolpath=["#msgq_repo/site_scons/site_tools", "#rednose_repo/site_scons/site_tools"],
toolpath=["#rednose_repo/site_scons/site_tools"],
)
# SCons' Darwin linker tool doesn't define the variables used to expand RPATH.
if arch == "Darwin":
@@ -292,7 +292,7 @@ SConscript(['opendbc_repo/opendbc/can/SConscript'])
# Build common module
SConscript(['openpilot/common/SConscript'])
Import('_common')
common = [_common, 'json11', 'zmq']
common = [_common, 'json11']
Export('common')
# Build messaging (cereal + msgq + socketmaster + their dependencies)
@@ -318,7 +318,7 @@ SConscript([
if arch == "comma_arm64":
SConscript(['openpilot/system/camerad/SConscript'])
SConscript(['openpilot/starpilot/system/android_auto/SConscript'])
SConscript(['openpilot/starpilot/system/starport/SConscript'])
# Build selfdrive
SConscript([
@@ -329,6 +329,8 @@ SConscript([
'openpilot/selfdrive/ui/SConscript',
'openpilot/starpilot/audio/native/SConscript',
'openpilot/starpilot/models/runtime_bridge/SConscript',
'openpilot/starpilot/ui/native/SConscript',
'openpilot/starpilot/flm/SConscript',
])
# Replay is also the isolated on-device viewer engine.
@@ -346,7 +348,7 @@ if GetOption('extras') and arch != "comma_arm64":
env.CompilationDatabase('compile_commands.json')
# progress output
def count_scons_nodes(nodes):
def get_scons_nodes(nodes):
seen = set()
stack = list(nodes)
@@ -355,28 +357,28 @@ def count_scons_nodes(nodes):
if node in seen:
continue
seen.add(node)
executor = node.get_executor()
if executor is not None:
stack += executor.get_all_prerequisites() + executor.get_all_children()
# Only scan directories; generated sources are not ready yet.
stack += (node.prerequisites or []) + node.children(scan=isinstance(node, SCons.Node.FS.Dir))
return len(seen)
return seen
progress_interval = 5
progress_count = 0
progress_total = max(1, count_scons_nodes(env.arg2nodes(BUILD_TARGETS or [Dir('.')], env.fs.Entry)))
progress_nodes = get_scons_nodes(env.arg2nodes(BUILD_TARGETS or [Dir('.')], env.fs.Entry))
progress_total = max(1, len(progress_nodes))
def progress_function(node):
global progress_count
if progress_count >= progress_total:
if node not in progress_nodes:
return
progress_count = min(progress_count + progress_interval, progress_total)
progress = round(100. * progress_count / progress_total, 1)
progress_nodes.remove(node)
if len(progress_nodes) % progress_interval:
return
progress = round(100. * (1 - len(progress_nodes) / progress_total), 1)
sys.stderr.write("\rBuilding: %5.1f%%" % progress if sys.stderr.isatty() else "progress: %.1f\n" % progress)
if progress == 100. and sys.stderr.isatty():
sys.stderr.write("\n")
sys.stderr.flush()
Progress(progress_function, interval=progress_interval)
Progress(progress_function)
AddPostAction(BUILD_TARGETS or [Dir('.')], prune_cache_dir)
# Release CI rebuilds tracked device outputs for the host architecture.
+50 -11
View File
@@ -1,5 +1,16 @@
# Third-party notices
## Arabesque Firefox
The offline payload contains original Mozilla and Ubuntu archives. The pinned
versions, upstream download URLs and hashes are listed in
[`runtime.json`](openpilot/tools/arabesque_firefox/runtime.json). Mozilla publishes
the [Firefox 153.6.0esr source](https://archive.mozilla.org/pub/firefox/releases/153.6.0esr/source/)
and [Firefox notices](https://www.mozilla.org/en-US/about/legal/firefox/).
The Ubuntu archives retain their package copyright files. The
[Ubuntu package archive](https://ubuntu.com/project/docs/how-ubuntu-is-made/concepts/package-archive/)
provides source packages alongside the binaries.
## Jetlink
Jetlink's comma client is copyright (c) 2026 Zeph Leggett and distributed under the MIT License. Its complete license and permission notice are retained in [jetlink_repo/LICENSE](jetlink_repo/LICENSE); source and revision are recorded in [CREDITS.md](CREDITS.md#jetlink-external-inference) and `upstream-sync.json`.
@@ -73,17 +84,7 @@ the exclusion of the other.
## BluePilot Ford support
The Ford lateral strategies, CAN constructors, camera-radar filtering and native checks include work adapted from BluePilot's `bp-7.0` source, through StarPilot `2efe6017bfe14cee4807833e05f9a7f6ba255cdb`. See [CREDITS.md](CREDITS.md#ford-support-adapted-from-bluepilot) for contributors and source revisions. The upstream extension-file and `LICENSE.md` notices reproduced above also apply to the Ford extension sources; they are retained without resolving the difference between their published license descriptions.
## Optional BluePilot angle library adapter
The angle-only adapter in `opendbc_repo/opendbc/bluepilot_lateral` is sourced directly from BluePilot bp-7.0 commit e1d051d7ba270261b4455068bd68f1a58db15a4a. The complete upstream MIT notice is retained in that folder's `LICENSE`, and the simultaneously published Custom MIT notice is retained verbatim in `LICENSE.md`. The applicable SUNNYPILOT acknowledgments reproduced above are also retained in its `CREDITS.md`. This preserves the published terms without resolving their scope or representing the adapted extension as unambiguously plain MIT.
The optional, default-disabled angle lane-centering trim additionally derives from
BluePilot bp-dev e22afa6be9b881fa784c92ebb316db47728a3d81. Its exact source and
contributors are recorded in the library CREDITS.md; the same root/opendbc MIT
and Custom MIT notices coexist unchanged at that revision. This adds attribution,
not a license-scope decision or permission grant.
The Ford CAN FD camera-radar filtering includes work adapted from BluePilot's `bp-7.0` source, through StarPilot `2efe6017bfe14cee4807833e05f9a7f6ba255cdb`. See [CREDITS.md](CREDITS.md#ford-support-adapted-from-bluepilot) for contributors and source revisions. The upstream extension-file and `LICENSE.md` notices reproduced above also apply to that source; they are retained without resolving the difference between their published license descriptions.
## Continental ARS510 radar
@@ -112,3 +113,41 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
## PulseAudio server for Spotify
`openpilot/starpilot/soloist/pulseaudio/` contains unmodified AArch64 binaries from Ubuntu 24.04 packages: PulseAudio 16.1 (LGPL-2.1-or-later), libltdl (LGPL-2.1-or-later), libtdb (LGPL-3.0-or-later), libsoxr (LGPL-2.1-or-later), liborc and speexdsp (BSD-style). The complete copyright and license notices are in that directory's `licenses/`, package hashes in `PACKAGES.sha512`, and the corresponding source is available from Ubuntu's archive for the exact versions listed in its README. The libraries are dynamically linked and may be replaced with `update.sh`.
## Galaxy Android companion tunnel
`openpilot/starpilot/galaxy/bin/hev-socks5-tunnel_linux_arm64` is the upstream
[hev-socks5-tunnel 2.18.0](https://github.com/heiher/hev-socks5-tunnel/releases/tag/2.18.0)
static Linux ARM64 release, under the MIT license. Its notice is retained in
`openpilot/starpilot/galaxy/bin/LICENSE.hev-socks5-tunnel`; the adjacent SHA-256
file pins the downloaded artifact. The Android companion uses the Kotlin
standard library (Apache-2.0) and the Gradle wrapper (Apache-2.0); no third-party
UI framework is bundled.
## Galaxy Bluetooth companion source snapshot
The Galaxy Bluetooth phone bridge and iOS app from https://github.com/StarPilotHQ/StarPilot
(natehuby76, PRs #224/#225) are included under `openpilot/starpilot/galaxy_bluetooth/`
and `openpilot/starpilot/companion/ios/`, with their license files (`UPSTREAM-LICENSE`, `openpilot/starpilot/companion/ios/UPSTREAM-GALAXY-LICENSE`).
The bridge runs on openpilot's Python with PyCryptodome 3.23.0 (BSD/public-domain
components, from opendbc). Its other dependencies are vendored unmodified, as
pure-Python sources, in `openpilot/starpilot/galaxy_bluetooth/vendor/`:
dbus-next 0.2.3 (MIT), ifaddr 0.2.0 (MIT) and Zeroconf 0.151.3
(LGPL-2.1-or-later). Their license texts are in `vendor/licenses/`, and
`vendor/VENDOR.json` records each PyPI source file and its SHA-256. Zeroconf is
kept as separate, unmodified source files that can be replaced with another
compatible version, as the LGPL requires.
## Subaru angle support from jacobw
The Subaru angle implementation includes work from Jacob Waller's `jul-angle-based` openpilot/opendbc branches and their upstream contributors. See [CREDITS.md](CREDITS.md#subaru-angle-steering-from-jacob-wallers-fork) and `opendbc_repo/opendbc/car/subaru/ORIGIN_HISTORY.tsv` for pinned sources, original authors and titles. The source repositories publish the comma MIT license; the complete applicable notice remains in the project `LICENSE` and `opendbc_repo/LICENSE`. StarPilot's adaptations preserve additional native and caller safety guards.
## StarPilot Ford demand tuning
The historical tuning formerly in `openpilot/starpilot/car/ford/starpilot_tuning.py` included the BluePilot bp-7.0-derived work identified in [CREDITS.md](CREDITS.md#starpilot-ford-demand-tuning). The published upstream license notices retained above and in the pinned BluePilot library remain applicable to the adapted source. StarPilot's separate protocol adapter does not modify those notices or resolve their scope.
The angle-primary adaptation in `openpilot/starpilot/car/ford/starpilot_controller.py` is derived from BluePilot v0.2.1. It retains the complete MIT copyright and permission notice in `opendbc_repo/opendbc/bluepilot_lateral/LICENSE`, including Comma.ai, BluePilot and StarPilot contributors. See the contributor attribution in `CREDITS.md`.
+1 -1
View File
@@ -77,7 +77,7 @@ while [[ $# -gt 0 ]]; do
;;
--cereal|-cereal)
shortcut_targets+=(openpilot/cereal/libcereal.a openpilot/cereal/libsocketmaster.a
openpilot/cereal/messaging/bridge openpilot/cereal/services.h)
openpilot/cereal/services.h)
;;
-j|--jobs)
passthrough_args+=("$1")
+26 -1
View File
@@ -5,6 +5,8 @@ openpilot ALC and openpilot LDW do not automatically drive the vehicle or reduce
While changing lanes, openpilot is not capable of looking next to you or checking your blind spot. Only nudge the wheel to initiate a lane change after you have confirmed it's safe to do so.
Unexpected steering may occur. openpilot ALC may not steer to avoid stopped or slow-moving vehicles, cross traffic, construction barriers or cones, motorcycles, children, pedestrians, animals, or other objects on the road.
Many factors can impact the performance of openpilot ALC and openpilot LDW, causing them to be unable to function as intended. These include, but are not limited to:
* Poor visibility (heavy rain, snow, fog, etc.) or weather conditions that may interfere with sensor operation.
@@ -13,6 +15,14 @@ Many factors can impact the performance of openpilot ALC and openpilot LDW, caus
* The device is mounted incorrectly.
* When in sharp curves, like on-off ramps, intersections etc...; openpilot is designed to be limited in the amount of steering torque it can produce.
* In the presence of restricted lanes or construction zones.
* When the lane markings are unclear, worn, missing, or they cross each other.
* Tar marks, shadows, road cracks, temporary or construction lane markings, or other road imperfections.
* When lanes merge or split.
* When entering a highway on-ramp or exiting an off-ramp.
* On roads with intersections or roundabouts.
* On slippery roads.
* When towing a trailer.
* When entering or exiting a tunnel.
* When driving on highly banked roads or in presence of strong cross-wind.
* Extremely hot or cold temperatures.
* Bright light (due to oncoming headlights, direct sunlight, etc.).
@@ -24,6 +34,8 @@ The list above does not represent an exhaustive list of situations that may inte
openpilot ACC and openpilot FCW are not systems that allow careless or inattentive driving. It is still necessary for the driver to pay close attention to the vehicle’s surroundings and to be ready to re-take control of the gas and the brake at all times.
openpilot ACC may not stop your vehicle in time to avoid a collision with a vehicle that quickly stops in front of you. openpilot ACC may occasionally cause your vehicle to brake when not required or when you are not expecting it.
Many factors can impact the performance of openpilot ACC and openpilot FCW, causing them to be unable to function as intended. These include, but are not limited to:
* Poor visibility (heavy rain, snow, fog, etc.) or weather conditions that may interfere with sensor operation.
@@ -35,9 +47,20 @@ Many factors can impact the performance of openpilot ACC and openpilot FCW, caus
* In presence of traffic signs or stop lights, which are not detected by openpilot at this time.
* When the posted speed limit is below the user selected set speed. openpilot does not detect speed limits at this time.
* In presence of vehicles in the same lane that are not moving.
* When a vehicle you are following moves out of your driving path and a stationary or slow-moving vehicle or object is in front of you.
* Narrow vehicles, such as bicycles or motorcycles.
* Vehicles or objects only partially in the driving lane.
* On curves, openpilot ACC may not detect a vehicle in the same lane, and may accelerate to the set speed.
* Cross traffic.
* Oncoming vehicles.
* Animals or objects in the road.
* Rare objects such as trailers, ramps, cargo, open doors, etc. protruding from vehicles.
* When abrupt braking maneuvers are required. openpilot is designed to be limited in the amount of deceleration and acceleration that it can produce.
* When surrounding vehicles perform close cut-ins from neighbor lanes.
* Driving on hills, narrow, or winding roads.
* On slippery roads.
* When towing a trailer.
* When entering or exiting a tunnel.
* Extremely hot or cold temperatures.
* Bright light (due to oncoming headlights, direct sunlight, etc.).
* Interference from other equipment that generates radar waves.
@@ -46,7 +69,9 @@ The list above does not represent an exhaustive list of situations that may inte
## Limitations of openpilot DM
openpilot DM should not be considered an exact measurement of the alertness of the driver.
openpilot DM displays a series of escalating warnings. Disregard of these warnings may result in openpilot being unavailable for a period of time.
openpilot DM should not be considered an exact measurement of the alertness of the driver. openpilot DM cannot always detect if the driver is tired or inattentive.
Many factors can impact the performance of openpilot DM, causing it to be unable to function as intended. These include, but are not limited to:
+6
View File
@@ -4,6 +4,12 @@ openpilot is an Adaptive Cruise Control (ACC) and Automated Lane Centering (ALC)
Like other ACC and ALC systems, openpilot is a failsafe passive system and it requires the
driver to be alert and to pay attention at all times.
openpilot is not an autonomous driving system. It is not a collision avoidance system or a
substitute for safe and attentive driving. The driver is always responsible for the safe
operation of the vehicle and must be ready to take control at any time. The driver can override
openpilot at any time by braking, accelerating, or steering the vehicle. openpilot may
disengage without warning. Read the [limitations](https://github.com/commaai/openpilot/blob/master/docs/LIMITATIONS.md) carefully before using openpilot.
To assist the driver in maintaining alertness, openpilot includes a driver monitoring feature
that alerts when it detects driver distraction.
Executable
+8
View File
@@ -0,0 +1,8 @@
#!/usr/bin/env bash
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PYTHON_BIN="${HEATDEATH_PYTHON:-python3}"
if [[ -z "${HEATDEATH_PYTHON:-}" && -x /usr/local/venv/bin/python3 ]]; then
PYTHON_BIN=/usr/local/venv/bin/python3
fi
exec "$PYTHON_BIN" -B "$ROOT_DIR/tools/heatdeath/heatdeath.py" "$@"
+1 -1
View File
@@ -10,4 +10,4 @@ jetlink: run openpilot's large driving models on an attached Jetson.
# so does the release check (macos/scripts/check-version.sh). The comma runs
# jetlink from a checkout, not an install, so this is also all it has. The Swift
# reads it from Pinned.swift: run JetlinkKit/Scripts/make_pins.py after a bump.
__version__ = '0.8.7'
__version__ = '0.9.0'
+21 -6
View File
@@ -224,12 +224,17 @@ class JetlinkClient:
# -- handshake ------------------------------------------------------------
def hello(self, timeout: float = 5.0) -> dict:
def hello(self, timeout: float = 5.0, pick: dict | None = None) -> dict:
"""Introduce this client. The server starts its session over on a hello,
so this is also how a new owner of the gadget takes over one the server
never saw end; see Session.greet in JetlinkServer."""
never saw end; see Session.greet in JetlinkServer. `pick` is the comma's
own big-model pick with its age and its date by the comma's clock, {ref,
sha256, nbytes, name, age_s, at}; the answer's `pick` is the newer of it
and the server's (PickStore)."""
seq = self._next_seq()
client = {'nonce': self.nonce, 'name': self.name}
if pick is not None:
client['pick'] = pick
try:
link = self.t.link_info()
except Exception:
@@ -301,7 +306,8 @@ class JetlinkClient:
frame_skip: int = DEFAULT_FRAME_SKIP,
progress: ProgressFn | None = None,
build_timeout: float = 900.0,
should_stop: StopFn | None = None) -> ModelSpec:
should_stop: StopFn | None = None,
fetch: bool = False, ref: str | None = None) -> ModelSpec:
"""Make the server ready to run this model, uploading and building if needed.
Blocks until ready or the build fails, and returns the spec the server
@@ -309,8 +315,12 @@ class JetlinkClient:
parser rejects an export (tinygrad and the org.tinygrad domain) still runs.
`onnx_path` None means the caller cannot upload (modeld) and a missing
engine is EngineMissing. `progress(stage, frac, msg)` has stage in
upload/patch/parse/build/load. `should_stop` is polled during long waits.
engine is EngineMissing. `fetch` asks a server that lacks the model to
download it itself (state downloading, stage download) rather than answer
need_upload; one that cannot, or is too old to, answers need_upload as
before. `ref` is the catalog ref the model is known by.
`progress(stage, frac, msg)` has stage in
download/upload/patch/parse/build/load. `should_stop` is polled during long waits.
"""
self.progress_cb = progress
self._should_stop = should_stop or (lambda: False)
@@ -318,7 +328,12 @@ class JetlinkClient:
self._engine_state = None
seq = self._next_seq()
self.t.send_json(P.Msg.ENGINE_REQ, seq, {'sha256': sha256, 'nbytes': nbytes, 'frame_skip': frame_skip})
request = {'sha256': sha256, 'nbytes': nbytes, 'frame_skip': frame_skip}
if fetch:
request['fetch'] = True
if ref:
request['ref'] = ref
self.t.send_json(P.Msg.ENGINE_REQ, seq, request)
resp = json.loads(bytes(self._expect(P.Msg.ENGINE_RESP, seq, 60.0).payload))
self._engine_state = resp
log.info("server engine state: %s (%s)", resp['state'], resp.get('detail', ''))
+1 -1
View File
@@ -55,7 +55,7 @@ POLL = 0.5
# what a borrower passes on of the server's hello (Loan.note_server): the owner
# never speaks the protocol, and sleep_after is how it knows whether letting
# go of the gadget lets the Jetson sleep. The rest is for its status record
SERVER_FIELDS = ('protocol', 'device', 'backend', 'runtime_version', 'trt_version', 'sleep_after')
SERVER_FIELDS = ('protocol', 'device', 'backend', 'runtime_version', 'trt_version', 'sleep_after', 'pick', 'fetches')
def _send(conn: socket.socket, msg: dict) -> None:
+29 -8
View File
@@ -40,7 +40,7 @@ from jetlink.openpilot.interface import MODES, STATES, Keys, ModelFace, Openpilo
from jetlink.openpilot.parts import Parts, for_this_process
from jetlink.openpilot.status import Status
API = 3
API = 4
__all__ = ['API', 'MODES', 'STATES', 'Jetlink', 'Keys', 'ModelFace', 'Openpilot', 'OwnerConfig', 'Status', 'bind',
'conformance', 'SHUTDOWN_TIMEOUT', 'Flags', 'SwapEvents']
@@ -302,13 +302,34 @@ class Jetlink:
self._shutdown_asked = time.monotonic()
return True
def should_extend_catalog(self) -> bool:
"""Should the model manager's big-model catalog carry the newer catalogs'
models? With no chestnut fitted. Hardware, not the link setting: the model
manager drops a pick its catalog does not list."""
def lists_big_models(self) -> bool:
"""Is the model manager's big-model list jetlink's (big_catalog) rather
than sunnypilot's? With no chestnut fitted. Hardware, not the link
setting: the model manager drops a pick its catalog does not list."""
return not self._parts.chestnut_fitted()
def big_catalog(self, selector: int) -> dict | None:
"""The model index as the model manager's big-model catalog, every bundle
at `selector` (its REQUIRED_JSON_VERSION), fetched again
(Models.big_catalog). None when it cannot be had, offline say, and the
model manager keeps the list it has."""
from jetlink.registry.index import RegistryError
try:
return self._parts.models.big_catalog(selector)
except RegistryError as e:
self._log.warning("jetlink: could not fetch the model index: %s", e)
return None
def listed_ref(self, ref: str) -> str | None:
"""The ref big_catalog() lists a pick's model under, for a pick a new list
no longer names: a recompile is listed once, under its newest ref. None
while the list is not jetlink's (lists_big_models)."""
return self._parts.models.listed_ref(ref) if self.lists_big_models() else None
# API 3's, until the next minor release: the catalog is left as fetched.
# Its picks still drive, since the index knows every ref it ever saw
def should_extend_catalog(self) -> bool:
return False
def extend_catalog(self, catalog: dict) -> dict:
"""The big-model catalog the model manager fetched, with the models newer
catalogs list folded in. Never raises."""
return self._parts.models.big_catalog(catalog)
return catalog
+17 -5
View File
@@ -11,9 +11,10 @@ manager, hardwared, the UI and the model manager call these on every device,
link on or off, and modeld on every drive: whatever jetlink does wrong turns
the link off and is logged, and never takes one of them down. Each hook has
the answer it gets with the link off, and gives it on any failure. They are
not all False: attach() keeps modeld's model, extend_catalog() the catalog,
and shutdown_step() says yes to a power-off that is wanted, since a broken
jetlink must never keep a comma on to drain the car's battery. selfdrived's
not all False: attach() keeps modeld's model, big_catalog() is None so the
model manager keeps its own list, and shutdown_step() says yes to a power-off
that is wanted, since a broken jetlink must never keep a comma on to drain the
car's battery. selfdrived's
swap events (swap_events()) answer no event at all.
The fork's guard, which builds Hooks or else Off inside a function, and its
@@ -39,7 +40,7 @@ from jetlink.openpilot import API, Flags, Jetlink, SwapEvents, bind
# the APIs a fork's adapter may be written to: today's, and after a bump the
# previous one too, for one minor release (jetlink/openpilot/__init__.py)
COMPATIBLE = frozenset({API})
COMPATIBLE = frozenset({API, 3})
# the hooks that ask jetlink with link_on() saying off. status(): the UI shows
# the big model's name from the model library. shutdown_step(): hardwared
@@ -47,7 +48,7 @@ COMPATIBLE = frozenset({API})
# and its wait, for a link turned off while one is pending. The model
# manager's: the catalog is hardware and the library, not the setting
BINDS_WITH_LINK_OFF = frozenset({'status', 'shutdown_step', 'request_shutdown', 'shutdown_pending', 'model_state',
'should_extend_catalog', 'extend_catalog'})
'lists_big_models', 'big_catalog', 'listed_ref', 'should_extend_catalog', 'extend_catalog'})
# what selfdrived is told when the swap events cannot run: none at all
NO_EVENTS = Flags(False, False, False, False, False)
@@ -156,6 +157,17 @@ class _Hooks:
def model_state(self, ref: str) -> str | None:
return self._call('model_state', None, ref)
def lists_big_models(self) -> bool:
return self._call('lists_big_models', False)
def big_catalog(self, selector: int) -> dict | None:
"""None with the link off or on any failure: the model manager keeps its list."""
return self._call('big_catalog', None, selector)
def listed_ref(self, ref: str) -> str | None:
return self._call('listed_ref', None, ref)
# API 3's, until the next minor release
def should_extend_catalog(self) -> bool:
return self._call('should_extend_catalog', False)
+37 -4
View File
@@ -45,15 +45,24 @@ class Keys:
offroad: str # BOOL: is the car parked (manager's IsOffroad)
progress: str # JSON {stage, frac, msg, drops}: provisioning and join progress
spec: str # JSON: the built model's spec and whether its engine is built
pointers: str # JSON: catalog ref -> {oid, size}
# sunnypilot's model manager: the big-model pick, JSON {ref, displayName},
# and the big-model catalog, JSON {bundles}. Without one both are None, and
# jetlink runs its own default big model
# jetlink runs its own default big model, or the one picked on the server.
# A fork with a picker of its own and no catalog (StarPilot) declares only
# big_model, and may name its pick by the ONNX's hash, {sha256,
# displayName}; a newer pick made on the server is written into it
big_model: str | None = None
catalog: str | None = None
# BOOL: an iPhone on a direct cable is asked to charge from the comma
# (comma.port). Off when unset, and without the key
charge_phone: str | None = None
# STRING: where the model manager takes a pick by its ref and stores it in
# big_model (sunnypilot's ModelManager_DownloadRef). A newer pick made on
# the server is written there, so the comma's own list shows it; without
# the key it drives all the same
select: str | None = None
# API 3's kept pointers, unread since the model index: until the next minor release
pointers: str | None = None
@dataclass(frozen=True)
@@ -89,7 +98,6 @@ class StatusSide(Protocol):
"""Every reader: manager, the UI, hardwared, the model manager."""
keys: Keys
log: Any # logging.Logger-like (cloudlog): debug, info, warning, error, exception
catalog_selector: int # the model manager's REQUIRED_JSON_VERSION; 0 without a model manager
def params_dir(self) -> Path:
"""The params store's directory, by params.cc's rule, worked out on every
@@ -152,6 +160,16 @@ class ModelSide(WorkerSide, Protocol):
frame thread at a hand-back, so it zeroes the history in place and
allocates and compiles nothing; warp.reset_queues makes one."""
def model_warp(self, small: Any, cam_w: int, cam_h: int, model_w: int, model_h: int) -> FrameWarp | None:
"""Optional (OPTIONAL): the warp the link sends frames through, warmed,
or None for openpilot's driving warp pickle at warp_path (warp.Warp). For
a fork whose runtime cannot load that pickle, typically with its small
model's own warp. Asked once on modeld's main thread before the frame
loop, after the small model's warmup; raising keeps the drive on the
small model, as a pickle that does not load does. The offroad checks
still look for the pickle (Warps.built): only modeld has a small model
to ask, so warp_path must name a file either way."""
class Openpilot(ModelSide, Protocol):
"""The whole adapter: one object implements every side."""
@@ -198,13 +216,28 @@ class JoinedModel(Protocol):
"""Let go of the link at modeld's exit."""
class FrameWarp(Protocol):
"""What an adapter's model_warp returns, warp.Warp's shape, run on modeld's
frame thread. Only one of the small and the large model runs a frame, so a
warp that shares the small model's JIT and buffers is never in use by both.
On a comma, a warp that reads its GPU output back moves it first with
warp.make_coherent, before the warp's JIT first runs."""
output: Any # the warped frame, (2, 6, model_h/2, model_w/2) uint8 as bytes: read after wait(), until the next start()
def start(self, frame: int, big_frame: int, tfm: Any, big_tfm: Any) -> None:
"""Warp the camera buffers at these addresses under their 3x3 transforms."""
def wait(self) -> None:
"""Return once `output` holds what start() warped."""
# The members of each protocol an implementation may lack: jetlink reads them
# with getattr and a fallback. conformance() passes an object without one and
# holds one with it to its signature, and adding one keeps API. Kept out of
# the protocols, where a name would be a member itself; a type checker still
# asks for these, as typing has no optional member
OPTIONAL: dict[type, tuple[str, ...]] = {
ModelSide: ('small_reset',),
ModelSide: ('small_reset', 'model_warp'),
SmallModel: ('desire_key', 'numpy_inputs', 'lat_delay', 'PLANPLUS_CONTROL'),
JoinedModel: ('get_action_from_model', 'constants', 'LAT_SMOOTH_SECONDS', 'LONG_SMOOTH_SECONDS', 'lat_delay'),
}
+12 -5
View File
@@ -116,7 +116,7 @@ LAGS = (BEHIND, BAD_OUTPUT)
RECONNECTING = {LOST: ('reconnecting', 'lost'), BEHIND: ('fell behind, reconnecting', 'behind'),
NOT_UP: ('load failed, reconnecting', 'load_failed'),
BAD_OUTPUT: ('bad output, reconnecting', 'bad_output')}
# how long a model name the catalog had not resolved stays the sha's
# how long a model name the comma's index did not have stays the sha's
NAME_RETRY = 30.0
@@ -334,7 +334,7 @@ class JoiningModelState:
self._demote(BEHIND)
elif self._active is not self._small and self._frame_drop_ratio > DROP_LIMIT:
# modeld is behind the large model; this frame is the small model's
self._log.warning("jetlink: modeld dropped %.2f %% of camera frames behind the large model, "
self._log.warning("jetlink: modeld dropped %.2f %% of camera frames behind the large model, " +
"the small model drives from this one", self._frame_drop_ratio * 100)
self._demote(BEHIND)
self._maybe_swap()
@@ -694,8 +694,9 @@ class JoiningModelState:
'lags': self._lags}
def _name_of(self, sha: str, names: dict) -> str:
"""The catalog's name, kept once it is one. The sha's stand-in (a row not
resolved yet, or a lookup that failed) is asked again after NAME_RETRY."""
"""The index's name, kept once it is one. The sha's stand-in (a model the
comma's index does not list yet, or a lookup that failed) is asked again
after NAME_RETRY."""
name, retry_at = names.get(sha, (None, 0.0))
if time.monotonic() >= retry_at:
try:
@@ -745,6 +746,7 @@ def join(parts, cam_w: int, cam_h: int, small) -> JoiningModelState:
link = links.Link(parts.log)
def prepare():
from jetlink.openpilot.interface import FrameWarp, conformance
from jetlink.openpilot.warp import Warp, prepare_reset
# the gadget first, so the Jetson enumerates while the warp loads. Left to
# the join thread the bind lands ~3 s later, behind the small model's
@@ -759,7 +761,12 @@ def join(parts, cam_w: int, cam_h: int, small) -> JoiningModelState:
try:
# the fork's own reset of its small model, else jetlink's from its shape
ready['reset_small'] = getattr(op, 'small_reset', lambda small: None)(small) or prepare_reset(small)
warp = Warp(parts.warps.load(cam_w, cam_h, *geometry), face.frame_size(cam_w, cam_h))
# the fork's own warp, else openpilot's driving warp pickle
warp = getattr(op, 'model_warp', lambda *args: None)(small, cam_w, cam_h, *geometry)
if warp is None:
warp = Warp(parts.warps.load(cam_w, cam_h, *geometry), face.frame_size(cam_w, cam_h))
elif problems := conformance(warp, FrameWarp):
raise RuntimeError(f"the adapter's model_warp is not a warp: {', '.join(problems)}")
except Exception:
link.close()
raise
+28 -16
View File
@@ -43,6 +43,8 @@ PRESENT_TIMEOUT = 5.0
# hardware; the ceiling is only there so a server that has stopped answering
# does not hold the caller for the rest of the day.
BUILD_TIMEOUT = 1800.0
# and on top of that, a server downloading the model itself: 1.75 GB at 1 MB/s
FETCH_TIMEOUT = 1800.0
class ModelMissing(RuntimeError):
@@ -223,15 +225,16 @@ def connect_patiently(link: Link):
# -- making the Jetson ready ------------------------------------------------------
def identity(parts, entry: dict) -> tuple[str, int]:
"""The picked model's sha256 and byte count, from the catalog model's LFS
pointer, so the comma can name the model without holding or hashing the
ONNX. Looked up once per model ever and kept in a param: the one part of
provisioning that needs the internet."""
"""The picked model's sha256 and byte count, from the model index, so the
comma can name the model without holding or hashing the ONNX. A row has
them already where the comma's copy of the index knows the model; one it
does not fetches the index again, the one part of provisioning that may
need the internet."""
sha256, nbytes = entry.get('oid'), entry.get('size')
if sha256 and nbytes:
return sha256, int(nbytes)
parts.progress.report('connect', 0.0, 'finding model', code='finding_model')
return parts.models.resolve_pointer(entry['ref'])
return parts.models.resolve(entry.get('ref'), sha256)
# What has already been hashed in this process, keyed on the file as it was
@@ -278,21 +281,27 @@ def verified_upload(log, model_path: Path | None, sha256: str, nbytes: int) -> P
def ensure(parts, client, sha256: str, nbytes: int, model_path: Path | None, *,
progress=None, should_stop=None, build_timeout: float = BUILD_TIMEOUT):
progress=None, should_stop=None, build_timeout: float = BUILD_TIMEOUT, ref: str | None = None,
fetch: bool = True):
"""Make the server ready for this model and remember what it answered.
Asks without the file first: the server answers from the sha alone when it
has the model, which is every parked poll and every join. EngineMissing means
the Jetson has neither the plan nor the bytes, and neither has the caller."""
has the model, which is every parked poll and every join. Without the file
here either, the server is asked to download the model itself, which only
the Jetson needs. EngineMissing means the Jetson has neither the plan nor
the bytes and could not get them, and neither has the caller. `fetch` False
never asks for the download: a stand-in is a model the server has. `ref`,
the catalog's name for the model, goes with a download."""
from jetlink.client import EngineMissing
ask = functools.partial(client.ensure_engine, sha256, nbytes, progress=progress,
build_timeout=build_timeout, should_stop=should_stop)
fetch = fetch and model_path is None
ask = functools.partial(client.ensure_engine, sha256, nbytes, progress=progress, should_stop=should_stop)
try:
spec = ask(onnx_path=None)
spec = ask(onnx_path=None, fetch=fetch, ref=ref if fetch else None,
build_timeout=build_timeout + (FETCH_TIMEOUT if fetch else 0.0))
except EngineMissing:
upload = verified_upload(parts.log, model_path, sha256, nbytes)
if upload is None:
raise
spec = ask(onnx_path=upload)
spec = ask(onnx_path=upload, build_timeout=build_timeout)
parts.spec.store(spec)
return spec
@@ -315,8 +324,7 @@ def stand_in(parts, hello: dict, pick: str, built) -> tuple[str, int] | None:
if pick in have:
return None
if loaded:
# its size names it to the server, and only a catalog model resolved
# here, or the record, has one
# its size names it to the server: the record's, else the index's
nbytes = built.nbytes if built is not None and built.sha256 == loaded else parts.models.size_for(loaded)
if nbytes:
return loaded, nbytes
@@ -346,11 +354,15 @@ def open_link(parts, link: Link, should_stop=None):
# booting; both resolve on their own
client = connect_patiently(link)
try:
hello = client.hello(timeout=10.0)
sent = parts.models.hello_pick()
hello = parts.models.heard(client.hello(timeout=10.0, pick=sent), sent)
parts.log.warning("jetlink: %s trt %s, engine %s, loaded %s",
hello.get('device'), hello.get('trt_version'),
hello.get('engine_state'), str(hello.get('loaded'))[:16])
link.note_server(hello)
# a newer pick on the server drives, and is ours from now on
parts.models.adopt()
selected = parts.models.selected_model() or selected
sha256, nbytes = identity(parts, selected)
path = parts.models.shipped_model_path()
built = parts.spec.ready_spec()
@@ -370,7 +382,7 @@ def open_link(parts, link: Link, should_stop=None):
# server that restarted reloads from the plan cache, 13 to 25 s; one
# that has never seen this model builds it, 102 to 294 s
spec = ensure(parts, client, sha256, nbytes, path, progress=parts.progress.report_with_eta,
should_stop=should_stop)
should_stop=should_stop, ref=selected.get('ref'), fetch=standin is None)
client.deadline = INFERENCE_TIMEOUT
return client, spec
except EngineMissing as e:
@@ -157,9 +157,9 @@ class JetlinkModelState:
# model manager key off this flag
self.chestnut = True
# a warp.Warp, loaded and warmed ahead: its first call links the JIT, and
# this runs on modeld's frame thread. Frames go out of its output as they
# are, over either transport
# a warp.Warp or the adapter's model_warp, loaded and warmed ahead: its
# first call links the JIT, and this runs on modeld's frame thread. Frames
# go out of its output as they are, over either transport
self.warp = warp
self.input_shapes = spec.input_shapes
@@ -305,9 +305,9 @@ class JetlinkModelState:
# over the cable: how frames go, how many never came back, and what the
# kernel dropped on the way in (softnet), since the link came up
drops = getattr(self.client.t, 'net_drops', lambda: None)()
cable = (f"; frames {'as datagrams' if self.client.t.datagrams else 'on the stream'}, "
cable = (f"; frames {'as datagrams' if self.client.t.datagrams else 'on the stream'}, " +
f"{self.client.frames_lost} lost, net drops {drops}" if drops is not None else '')
self._log.warning("jetlink: frame %d warp %.1f data %.1f send %.1f wait %.1f ms%s; "
self._log.warning("jetlink: frame %d warp %.1f data %.1f send %.1f wait %.1f ms%s; " +
"server gpu %.1f queue %.1f total %.1f ms; ffs maxima prepare %.1f read_wait %.1f handoff %.1f ms%s",
self._frame_id, (frame.t1 - frame.t0) * 1e3, (frame.t2 - frame.t1) * 1e3,
(frame.sent - frame.t2) * 1e3, (t4 - waiting_from) * 1e3,
+380 -159
View File
@@ -7,132 +7,246 @@ See the LICENSE file in the root directory for more details.
Which large model runs, and where its bytes are.
The large model is the model manager's big-model pick, the same slot a
chestnut runs from (Keys.big_model). Its bundles are tinygrad pkls for a GPU
the Jetson does not have, but each one names the comma commit it was compiled
from, and that commit's ONNX in comma's LFS is what the Jetson runs. The
catalog says what exists, the slot says which one, the pointer at the commit
says which bytes. A commit that ships a precompiled pkl instead names its
export, and jetlink's registry follows that to comma's model repo; see
jetlink.registry.lfs.
chestnut runs from (Keys.big_model). It names a comma commit, and what the
Jetson runs is the ONNX that commit was built from: the model index
(jetlink.registry.index) says which, for every big-model commit, and lists
what can be picked. The comma keeps the last index that parsed under
model_dir(), so it names, sizes and lists its models with no network; a pick
the copy does not know fetches it again (resolve).
Without a chestnut fitted the model manager's big-model list is the index's
(big_catalog), so what the comma lists is what the Jetson runs. With one, the
list is sunnypilot's own, and jetlink stays out of it.
Without a model manager (Keys.big_model and Keys.catalog None) there is no
pick and no catalog, and the large model is jetlink's own default, named by its
ref: its pointer resolves from the ref as any pick's does.
pick and no catalog, and the large model is the one picked on the server, else
jetlink's own default, named by its ref.
A fork with a picker of its own but no catalog jetlink reads (Keys.big_model
without Keys.catalog, StarPilot's) may name its pick by the ONNX's sha256
instead of a ref, which is all its catalog knows of a model; the index sizes
it. With no catalog no model manager owns the slot either, so jetlink writes a
newer pick from the server into it itself, and the fork's picker shows what
drives.
"""
from __future__ import annotations
import json
import os
import re
import time
from pathlib import Path
from jetlink.registry.catalog import DEFAULT_BIG_MODEL_NAME, DEFAULT_BIG_MODEL_REF
from jetlink.registry.index import (DEFAULT_BIG_MODEL_NAME, DEFAULT_BIG_MODEL_REF, ModelIndex, NetworkError, Pointer, RegistryError,
is_ref, is_sha256, parse_index)
POINTER_TIMEOUT = 10.0
_REF = re.compile(r'[0-9a-f]{40}')
# the build date a catalog name ends in, " (September 17, 2026)"; any other parenthesis stays
_TRAILING_DATE = re.compile(r' \([A-Za-z]+ \d{1,2}, \d{4}\)$')
# the index and the slot are JSON params, and the UI names the active model
# every frame; the status line can lag a new pick by this long
# the index and the slot are a file and a JSON param, and the UI names the
# active model every frame; the status line can lag a new pick by this long
INDEX_TTL = 2.0
# the comma's copy of the model index, under model_dir()
INDEX_FILE = 'index.json'
# a pick the comma's copy does not know fetches the index again, at most this often
INDEX_MISS_RETRY = 60.0
# A big model's download over a car's connection: the waits between the
# attempts of one provisioning run, one more attempt than waits. Each carries
# on from the .part the last left, so a stall costs the wait, not the bytes
# (lfs.lfs_download)
DOWNLOAD_RETRY_DELAYS = (5.0, 15.0, 30.0, 60.0, 120.0)
# server_pick() before this process has heard a hello
_UNHEARD = object()
# heard() told nothing of the hello's own pick: an older caller
_UNTOLD = object()
# the slot as jetlink saw it last, under model_dir(): to tell a slot the user
# emptied (the default, chosen) from one never filled
SLOT_STATE = 'slot.json'
def _dated(pick) -> dict | None:
"""A server's pick as its hello carries it, dated by this clock:
{ref, sha256, nbytes, name, at}. None for none, or one naming no model."""
if not isinstance(pick, dict) or not isinstance(pick.get('age_s'), (int, float)):
return None
ref = pick['ref'] if is_ref(pick.get('ref')) else None
sha256 = pick['sha256'] if is_sha256(pick.get('sha256')) else None
if ref is None and sha256 is None:
return None
return {'ref': ref, 'sha256': sha256, 'nbytes': pick.get('nbytes'), 'name': pick.get('name'),
'at': time.time() - float(pick['age_s'])}
class Models:
def __init__(self, op):
self.op = op
self._index_cache: tuple[float, list[dict], dict[str, dict]] | None = None
self._slot_cache: tuple[float, dict | None] | None = None
# (looked at, the file's (mtime, size), the index it held and its rows)
self._index_cache: tuple[float, tuple | None, ModelIndex | None, list[dict]] | None = None
self._refresh_after = 0.0
self._slot_cache: tuple[float, dict | None, float | None] | None = None
# SLOT_STATE as this process last read or wrote it
self._slot_state: dict | None = None
# the pick of the last hello this process heard, and the owner's record
# of the last hello any process heard
self._heard: dict | None | object = _UNHEARD
self._record_cache: tuple[float, dict | None] | None = None
def catalog(self) -> list[dict]:
"""sunnypilot's big-model bundles as {name, ref}, newest first, as the model
manager's own picker lists them. From the cached JSON rather than the parsed
bundles: parsing builds capnp objects and writes chunk manifests, for two
fields. Never raises."""
try:
key = self.op.keys.catalog
bundles = ((self.op.get(key) if key else None) or {}).get('bundles', [])
found = [b for b in bundles if _REF.fullmatch(str(b.get('ref')))
and int(b.get('minimum_selector_version', 0)) == self.op.catalog_selector]
except Exception:
self.op.log.exception("jetlink: could not read the big-model catalog")
return []
found.sort(key=lambda b: int(b.get('index', 0)), reverse=True)
return [{'name': str(b.get('display_name') or b['ref'][:10]), 'ref': b['ref']} for b in found]
# -- the model index --------------------------------------------------------
def pointers(self) -> dict[str, dict]:
value = self.op.get(self.op.keys.pointers)
return value if isinstance(value, dict) else {}
def index_path(self) -> Path:
return self.model_dir() / INDEX_FILE
def fetch_pointer(self, ref: str) -> tuple[str, int]:
"""The oid and size of the ONNX a comma commit names, in its tree or, for a
precompiled-pkl commit, in comma's model repo. The Jetson's registry does
the same lookup, so the two ends agree on every model's identity."""
from jetlink.registry.lfs import fetch_pointer as registry_fetch_pointer
pointer = registry_fetch_pointer(ref, timeout=POINTER_TIMEOUT)
return pointer.oid, pointer.size
def resolve_pointer(self, ref: str) -> tuple[str, int]:
"""The oid and size behind a catalog model, fetched the first time and kept for good."""
known = self.pointers()
if ref in known:
return known[ref]['oid'], int(known[ref]['size'])
oid, size = self.fetch_pointer(ref)
known[ref] = {'oid': oid, 'size': size}
self._keep_pointers(known)
self.op.log.warning("jetlink: %s is %s, %d MB", ref[:10], oid[:16], size >> 20)
return oid, size
def _keep_pointers(self, known: dict[str, dict]) -> None:
# blocking: the next lookup reads this back, and a put still in flight would be lost under it
self.op.put(self.op.keys.pointers, known, block=True)
self._index_cache = None
@staticmethod
def _row(name: str, ref: str, known: dict[str, dict]) -> dict:
p = known.get(ref) or {}
return {'name': name, 'ref': ref, 'oid': p.get('oid'), 'size': int(p['size']) if p.get('size') else None}
def _index(self) -> tuple[list[dict], dict[str, dict]]:
"""The catalog's rows and the pointers behind them, read together once per INDEX_TTL."""
now = time.monotonic()
if self._index_cache is None or now - self._index_cache[0] >= INDEX_TTL:
known = self.pointers()
self._index_cache = (now, [self._row(b['name'], b['ref'], known) for b in self.catalog()], known)
return self._index_cache[1], self._index_cache[2]
def index(self) -> ModelIndex | None:
"""The comma's copy of the model index, None before the first fetch.
Looked at once per INDEX_TTL and parsed again only when the file
changed. Never raises."""
return self._read_index()[0]
def model_index(self) -> list[dict]:
"""Every catalog model, {name, ref, oid, size}. oid and size are None until
the model has been selected and resolved. No network."""
return self._index()[0]
"""Every model the index lists, {name, ref, oid, size}, in its order. No network."""
return self._read_index()[1]
def _read_index(self) -> tuple[ModelIndex | None, list[dict]]:
now = time.monotonic()
cache = self._index_cache
if cache is not None and now - cache[0] < INDEX_TTL:
return cache[2], cache[3]
path = self.index_path()
try:
st = path.stat()
seen = (st.st_mtime_ns, st.st_size)
except OSError:
seen = None
if cache is not None and cache[1] == seen:
# the same file, parsed or not: a copy that does not parse is told of once
index = cache[2]
else:
index = None
if seen is not None:
try:
index = parse_index(json.loads(path.read_text()))
except (OSError, ValueError, RegistryError):
self.op.log.exception("jetlink: the comma's model index does not parse")
self._keep_index(now, seen, index)
return self._index_cache[2], self._index_cache[3]
def _keep_index(self, now: float, seen: tuple | None, index: ModelIndex | None) -> None:
rows = self._index_cache[3] if self._index_cache is not None and self._index_cache[2] is index else \
[{'name': m.name, 'ref': m.ref, 'oid': m.oid, 'size': m.size} for m in index.models] if index else []
self._index_cache = (now, seen, index, rows)
def refresh_index(self) -> ModelIndex:
"""The model index, fetched and kept as the comma's copy, which is left
alone when nothing changed so no reader parses it again. Raises a
RegistryError for one that cannot be had or used, and keeps the copy."""
from jetlink.comma import gadget
from jetlink.registry.index import fetch_index
data, index = fetch_index()
path = self.index_path()
if gadget.read_record(path) != data:
gadget.write_record(path, data)
st = path.stat()
self._keep_index(time.monotonic(), (st.st_mtime_ns, st.st_size), index)
return index
def _row(self, name: str, ref: str | None, sha256: str | None = None) -> dict:
"""A model as a row: its ONNX by its ref, from the index, else by its hash
and sized from the index. oid or size is None where the index does not know."""
index = self.index()
pointer = index.pointer(ref) if index is not None and ref else None
if pointer is not None:
return {'name': name, 'ref': ref, 'oid': pointer.oid, 'size': pointer.size}
return {'name': name, 'ref': ref, 'oid': sha256, 'size': index.size(sha256) if index is not None and sha256 else None}
def resolve(self, ref: str | None, sha256: str | None) -> tuple[str, int]:
"""A pick's oid and size, from the index; one the comma's copy does not
know fetches it again, at most once per INDEX_MISS_RETRY. Raises a
RegistryError for a model the index does not know, or no index at all."""
if (found := self._identity(ref, sha256)) is None and time.monotonic() >= self._refresh_after:
self._refresh_after = time.monotonic() + INDEX_MISS_RETRY
self.refresh_index()
found = self._identity(ref, sha256)
if found is None:
raise RegistryError(f"the model index does not know {(ref or sha256 or 'the model')[:16]}")
return found
def _identity(self, ref: str | None, sha256: str | None) -> tuple[str, int] | None:
row = self._row('', ref, sha256)
return (row['oid'], int(row['size'])) if row['oid'] and row['size'] else None
def selected_slot(self) -> dict | None:
"""The big-model slot's pick as {name, ref}, from the raw param rather than a
parsed bundle, and memoised: the UI asks every frame."""
"""The big-model slot's pick as {name, ref, sha256}, from the raw param
rather than a parsed bundle, and memoised with when it was made
(picked_at): the UI asks every frame. It names its model by the catalog's
ref, by the ONNX's sha256 (a fork whose catalog knows only that), or by
both; the one it lacks is None."""
now = time.monotonic()
if self._slot_cache is not None and now - self._slot_cache[0] < INDEX_TTL:
return self._slot_cache[1]
key = self.op.keys.big_model
slot = self.op.get(key) if key else None
ref = slot.get('ref') if isinstance(slot, dict) else None
pick = None
if isinstance(ref, str) and ref:
pick = {'name': str(slot.get('displayName') or ref[:10]), 'ref': ref}
self._slot_cache = (now, pick)
slot = slot if isinstance(slot, dict) else {}
ref = slot['ref'] if isinstance(slot.get('ref'), str) and slot['ref'] else None
sha256 = slot['sha256'] if is_sha256(slot.get('sha256')) else None
pick, at = None, None
if ref or sha256:
pick = {'name': str(slot.get('displayName') or (ref or sha256)[:10]), 'ref': ref, 'sha256': sha256}
try:
at = os.stat(Path(self.op.params_dir()) / key).st_mtime
except OSError:
pass
self._note_slot(pick)
self._slot_cache = (now, pick, at)
return pick
def _note_slot(self, pick: dict | None) -> None:
"""Keep SLOT_STATE in step with the slot: what is in it, and when it was
emptied. A pick taken out is the default chosen (the model manager's
Default, here or on sunnylink), a pick like any other, which the hello
names and a server's older pick must not undo. Read again only when the
slot no longer matches what this process knows, so per process it costs
a file read at a change. Never raises."""
last = {'ref': pick['ref'], 'sha256': pick['sha256']} if pick else None
state = self._slot_state
if state is not None and state.get('last') == last:
return
path = self.model_dir() / SLOT_STATE
try:
state = json.loads(path.read_text())
state = state if isinstance(state, dict) else {}
except (OSError, ValueError):
state = {}
if state and state.get('last') == last:
self._slot_state = state
return
if last is None and not state:
# empty with nothing known before it: never picked, as far as we know
self._slot_state = {'last': None, 'cleared_at': None}
return
state = {'last': last, 'cleared_at': time.time() if last is None else None}
self._slot_state = state
from jetlink.comma import gadget
try:
gadget.write_record(path, state)
except OSError:
self.op.log.exception("jetlink: could not keep the big-model slot's state")
def default_chosen_at(self) -> float | None:
"""When the slot was emptied by this clock, the default chosen; None while
it holds a pick, or was never filled."""
if self.selected_slot() is not None or self._slot_state is None:
return None
at = self._slot_state.get('cleared_at')
return float(at) if isinstance(at, (int, float)) else None
def default_model(self) -> dict | None:
"""What runs with no pick: jetlink's default big model, else the newest the
catalog lists. Not the chestnut's default, which is the model in the tree.
A fork with no catalog at all (Keys.catalog None) gets jetlink's default by
its ref; one whose catalog has not been fetched yet waits for it."""
if self.op.keys.catalog is None:
return self._row(DEFAULT_BIG_MODEL_NAME, DEFAULT_BIG_MODEL_REF, self._index()[1])
"""What runs with no pick: the index's default, else the first it lists.
Not the chestnut's default, which is the model in the tree. Before the
comma has an index, jetlink's own by its ref, which a provisioning run
resolves by fetching one."""
if (index := self.index()) is None:
return self._row(DEFAULT_BIG_MODEL_NAME, DEFAULT_BIG_MODEL_REF)
models = self.model_index()
return next((m for m in models if m['ref'] == DEFAULT_BIG_MODEL_REF), models[0] if models else None)
return next((m for m in models if m['ref'] == index.default), models[0] if models else None)
def default_model_name(self) -> str | None:
"""default_model()'s name without the catalog's trailing build date, the
@@ -142,12 +256,156 @@ class Models:
return _TRAILING_DATE.sub('', model['name']) if model else None
def selected_model(self) -> dict | None:
"""The slot's pick, listed in the catalog or not: a ref is enough to find its
pointer. With no pick, default_model()."""
if (pick := self.selected_slot()) is not None:
return self._row(pick['name'], pick['ref'], self._index()[1])
"""The model picked last, here or on the server: the slot's pick, listed
or not (the index knows every ref it ever saw), unless the server's last
hello named a newer one. With neither, default_model()."""
if (theirs := self._newer_server_pick()) is not None:
return self._server_row(theirs)
if (own := self.selected_slot()) is not None:
return self._own_row(own)
return self.default_model()
def _own(self) -> tuple[dict | None, float | None]:
"""Our pick as a row, and when it was made by this clock: the slot's, or
the default when it was chosen by emptying the slot. (None, None) with
neither."""
if (own := self.selected_slot()) is not None:
return self._own_row(own), self.picked_at()
if (at := self.default_chosen_at()) is not None and (default := self.default_model()) is not None:
return default, at
return None, None
def _own_row(self, own: dict) -> dict:
return self._row(own['name'], own['ref'], own['sha256'])
# -- picks on both ends -----------------------------------------------------
# A pick is made on either end: in the comma's list (the default included,
# chosen by emptying the slot), or on the server (its web page, an app's Use
# Model). Each hello carries the comma's own pick, when it was made by this
# clock and how long ago, and the server answers with the newer of that and
# its own (PickStore in JetlinkServer), which it settles by what the comma
# named before rather than by its clock where it can: a Jetson's clock
# starts again behind at every power-on. The answer is kept with the rest of
# the hello (gadget.SERVER, through the owner) along with the pick the hello
# carried, and selected_model() takes the server's when that is still our
# pick: the server chose knowing it. So a model picked on the server drives
# without being picked here too, and one picked here goes to the server at
# the next hello.
def picked_at(self) -> float | None:
"""When the slot was last written, by this clock: when the pick was made.
None with no pick of our own, or no date for it."""
return self._slot_cache[2] if self.selected_slot() is not None and self._slot_cache else None
def hello_pick(self) -> dict | None:
"""Our own pick, for the hello: {ref, sha256, nbytes, name, age_s, at},
its oid and size where the index knows them (no network here), `at` when it
was made by this clock. None with nothing picked here, or no date for it:
the server's pick, if any, stands."""
row, at = self._own()
if row is None or at is None or not (row['ref'] or row['oid']):
return None
return {'ref': row['ref'], 'sha256': row['oid'], 'nbytes': row['size'], 'name': row['name'],
'age_s': round(max(0.0, time.time() - at), 1), 'at': round(at, 3)}
def heard(self, hello: dict, asked=_UNTOLD) -> dict:
"""The server's hello with its `pick` dated by this clock (_dated), and
`asked`, the hello_pick() the hello carried, kept with it: which is how
the owner keeps it (lending.SERVER_FIELDS) and server_pick() reads it.
This process goes by it at once, whatever the owner does with it."""
self._heard = _dated(hello.get('pick'))
if self._heard is not None and asked is not _UNTOLD:
self._heard['asked'] = {k: asked.get(k) for k in ('ref', 'sha256', 'at')} if isinstance(asked, dict) else None
return {**hello, 'pick': self._heard} if self._heard is not None else hello
def server_pick(self) -> dict | None:
"""The pick the server last answered a hello with, {ref, sha256, nbytes,
name, at}, or None: a server too old to say, or one with no pick. What
this process heard last, else the owner's record of the last hello: the
UI says no hello, and reads it every frame."""
if self._heard is not _UNHEARD:
return self._heard
now = time.monotonic()
if self._record_cache is None or now - self._record_cache[0] >= INDEX_TTL:
from jetlink.comma import gadget
pick = (gadget.read_record(gadget.SERVER) or {}).get('pick')
self._record_cache = (now, pick if isinstance(pick, dict) and isinstance(pick.get('at'), (int, float)) else None)
return self._record_cache[1]
def _newer_server_pick(self) -> dict | None:
"""The server's last pick, when it is another model than ours and was
picked after it, or we have none. The server settled that when the hello
carried this pick of ours; a pick made here since that hello is newer.
An answer kept without the pick it was asked with (an older jetlink) goes
by its date."""
theirs = self.server_pick()
if theirs is None:
return None
own, at = self._own()
if own is None:
return theirs
if self._same(theirs, own):
return None
if 'asked' in theirs:
asked = theirs['asked']
fresh = isinstance(asked, dict) and self._same(asked, own) and isinstance(asked.get('at'), (int, float)) \
and at is not None and abs(asked['at'] - at) < 1.0
return theirs if fresh else None
return None if theirs['at'] <= (at or 0.0) else theirs
@staticmethod
def _same(pick: dict, row: dict) -> bool:
"""One model: by the catalog ref where both have one, since two refs can
share one export, else by the hash. The server's PickStore.sameModel."""
if pick.get('ref') and row['ref']:
return pick['ref'] == row['ref']
return bool(pick.get('sha256')) and pick['sha256'] == row['oid']
def _server_row(self, pick: dict) -> dict:
"""The server's pick as a row: named as we list it, with the server's oid
and size, which name the bytes it has."""
listed = next((m for m in self.model_index() if pick['ref'] and m['ref'] == pick['ref']), None)
row = self._row(listed['name'] if listed else pick.get('name') or (pick['ref'] or pick['sha256'])[:16],
pick['ref'], pick['sha256'])
if pick['sha256'] and pick.get('nbytes'):
row['oid'], row['size'] = pick['sha256'], int(pick['nbytes'])
return row
def adopt(self) -> None:
"""Make a newer pick from the server ours. With a catalog, ask the model
manager for it (Keys.select), so the comma's own list shows what drives:
only a model the catalog lists, which is all the model manager takes; any
other drives all the same (selected_model). Without one, no model manager
owns the slot, so the pick is written into it, whatever the model. Never
raises."""
try:
if (theirs := self.server_pick()) is None or self._newer_server_pick() is None:
return
model, keys = self._server_row(theirs), self.op.keys
if keys.catalog is None:
if keys.big_model:
self.op.log.warning("jetlink: %s was picked on the server, picking it here too", model['name'])
slot = {k: v for k, v in (('displayName', model['name']), ('ref', model['ref']), ('sha256', model['oid'])) if v}
self.op.put(keys.big_model, slot, block=True)
self._slot_cache = None
return
# a download the user asked for that the model manager has not taken
# yet is left alone: the next hello asks again
key = keys.select
if key and model['ref'] and self.op.get(key) is None and model['ref'] in self._catalog_refs():
self.op.log.warning("jetlink: %s was picked on the server, picking it here too", model['name'])
self.op.put(key, model['ref'])
self._slot_cache = None
except Exception:
self.op.log.exception("jetlink: could not take the server's pick")
def _catalog_refs(self) -> set[str]:
"""The refs the model manager's catalog lists: the index's without a
chestnut fitted (big_catalog), else sunnypilot's."""
key = self.op.keys.catalog
bundles = ((self.op.get(key) if key else None) or {}).get('bundles')
return {b['ref'] for b in bundles if isinstance(b, dict) and is_ref(b.get('ref'))} if isinstance(bundles, list) else set()
def selected_model_name(self) -> str | None:
selected = self.selected_model()
return selected['name'] if selected else None
@@ -201,8 +459,6 @@ class Models:
the run for the next one. Each attempt asks the LFS server again:
the address it hands out expires. `retrying(error, delay)` hears of each
wait; a run that is stopped stops waiting."""
from jetlink.registry.catalog import NetworkError
from jetlink.registry.lfs import Pointer
model = self.selected_model()
if model is None or not model['oid']:
return None
@@ -226,30 +482,23 @@ class Models:
time.sleep(min(1.0, end - time.monotonic()))
def _fetch_once(self, pointer, dest: Path, progress, should_stop) -> Path:
from jetlink.registry.catalog import NetworkError
from jetlink.registry.lfs import lfs_download, lfs_resolve
for endpoint in self.lfs_endpoints():
href = lfs_resolve(endpoint, pointer)
if href is None:
continue
from jetlink.registry.lfs import lfs_download, lfs_hrefs
for endpoint, href in lfs_hrefs(self.lfs_endpoints(), pointer):
self.op.log.warning("jetlink: fetching the large model (%d MB) from %s", pointer.size >> 20, endpoint)
return lfs_download(href, pointer, dest, progress=progress, should_stop=should_stop)
raise NetworkError(f"no LFS server has {pointer.oid[:16]}")
def _row_for(self, oid: str) -> dict | None:
"""The catalog model with this oid, among those we have resolved."""
return next((m for m in self.model_index() if m['oid'] == oid), None)
def name_for(self, oid: str) -> str:
"""The catalog's name for the model with this oid, or the oid's first 16
characters when it lists none we have resolved."""
row = self._row_for(oid)
return row['name'] if row is not None else oid[:16]
"""The index's name for the model with this oid, or the oid's first 16
characters when it lists none."""
index = self.index()
listed = index.by_oid.get(oid) if index is not None else None
return listed.name if listed is not None else oid[:16]
def size_for(self, oid: str) -> int | None:
"""The byte count of the catalog model with this oid, once resolved here."""
row = self._row_for(oid)
return row['size'] if row is not None else None
"""The byte count of the ONNX with this oid, as the index knows it."""
index = self.index()
return index.size(oid) if index is not None else None
def has_file(self, model: dict) -> bool:
"""Is this model's ONNX on the comma, whole? Its size is the cheap check."""
@@ -262,54 +511,26 @@ class Models:
# -- the model manager's catalog --------------------------------------------
def big_catalog(self, catalog: dict) -> dict:
"""The big-model catalog with every newer one sunnypilot has published folded in.
A Jetson runs the commit's ONNX, so a model sunnypilot only builds for its next
runtime is still one it can run; see jetlink.registry.catalog.fetch_catalogs.
Never raises: a probe that fails keeps what the last one found."""
try:
from jetlink.registry.catalog import fetch_catalogs, merge_catalogs
fetched = fetch_catalogs()
self.keep_pins(fetched.get('pointers', {}))
merged = merge_catalogs([catalog, fetched], selector=self.op.catalog_selector)
merged.pop('pointers', None) # the pins are kept above, never in the model manager's catalog
added = len(merged.get('bundles', [])) - len(catalog.get('bundles', []))
if added <= 0:
return catalog
self.op.log.warning("jetlink: %d model(s) only newer catalogs list", added)
return merged
except Exception:
self.op.log.exception("jetlink: could not check for newer catalogs")
return self._found_before(catalog)
def big_catalog(self, selector: int) -> dict:
"""The index's models as a catalog in sunnypilot's shape, for the model
manager's big-model list when no chestnut is fitted: what the comma lists
is then what the Jetson runs, each with the index's fields, in its folder
and order. Only `selector` is the fork's (its model manager's
REQUIRED_JSON_VERSION, which it parses), and no artifacts: a chestnut
has nothing to download, and an accelerator runs the ONNX. The index is
fetched again; raises a RegistryError when it cannot be, so the model
manager keeps the list it has."""
models = self.refresh_index().models
return {'bundles': [{'ref': m.ref, 'display_name': m.name, 'short_name': m.short_name or m.ref[:7], 'index': len(models) - i,
'is_big': True, 'is_20hz': m.is_20hz, 'generation': m.generation, 'environment': m.environment,
'runner': m.runner, 'minimum_selector_version': str(selector), 'build_time': m.build_time,
'overrides': {'folder': m.folder}, 'models': []} for i, m in enumerate(models)]}
def keep_pins(self, pins: dict[str, dict]) -> None:
"""Writes the list's pins over the kept pointers (see catalog.pinned_pointers)."""
known = self.pointers()
changed = {ref: pin for ref, pin in pins.items() if known.get(ref) != pin}
if not changed:
return
known.update(changed)
self._keep_pointers(known)
for ref, pin in changed.items():
self.op.log.warning("jetlink: %s is pinned to %s", ref[:10], pin['oid'][:16])
def listed_ref(self, ref: str) -> str | None:
"""The ref the index lists a model under: its own, or the newest one with
the same ONNX (a recompile is listed once). None for a model it does not
list. No network."""
index = self.index()
listed = index.listed(ref) if index is not None and is_ref(ref) else None
return listed.ref if listed is not None else None
def _found_before(self, catalog: dict) -> dict:
"""The catalog with the models the last probe found folded in again, from the
model manager's cached copy: every entry there the fetched catalog does not
list. Not only merge_catalogs' entries with no artifacts: a newer catalog at
the same selector version comes through with its builds (v26 lists Cinque
Terre V3 that way). Dropped with a probe that failed, a pick only a newer
catalog lists would be reset at the manager's next start and the owner would
provision the default in its place; a refresh on a flaky network is enough.
A model sunnypilot withdrew stays listed until a probe works."""
try:
listed = {b.get('ref') for b in catalog.get('bundles', [])}
key = self.op.keys.catalog
cached = ((self.op.get(key) if key else None) or {}).get('bundles', [])
kept = [b for b in cached if isinstance(b, dict) and b.get('ref') not in listed]
except Exception:
return catalog
if not kept:
return catalog
self.op.log.warning("jetlink: keeping %d model(s) the last probe found", len(kept))
return {**catalog, 'bundles': [*catalog.get('bundles', []), *kept]}
+54 -32
View File
@@ -126,10 +126,8 @@ class ProvisioningRun:
return path
def needs_download(self) -> bool:
"""Does the comma have to fetch the picked model before a Jetson can build
it? Only while its engine is not known to be built and the file is not here.
Asked without the Jetson: a download needs only the internet, and a car
whose Jetson goes off with the ignition has no Jetson while parked."""
"""Could the Jetson need the picked model from the comma? Only while its
engine is not known to be built and the file is not here."""
parts = self.parts
entry = parts.models.selected_model()
if entry is None:
@@ -140,12 +138,17 @@ class ProvisioningRun:
return parts.models.shipped_model_path() is None
def provision(self) -> bool:
"""Make the Jetson ready for the selected model; a host is attached. run()
has fetched the file already unless the spec record said the model was
built; a server that lost it since asks for the bytes, and they are
fetched then."""
"""Make the Jetson ready for the picked model; a host is attached. The
hello settles which pick that is, the comma's or a newer one made on the
server. A server without the model downloads it itself; only one that
cannot (no internet, or too old) gets the bytes from the comma, fetched
here then."""
from jetlink.client import EngineMissing
parts = self.parts
parts.progress.report('connect', 0.0, 'connecting', code='connecting')
hello = self.hello()
self.log.warning("jetlink: server %s trt %s", hello.get('device'), hello.get('trt_version'))
parts.models.adopt()
entry = parts.models.selected_model()
if entry is None:
# no catalog yet; not an error
@@ -154,27 +157,24 @@ class ProvisioningRun:
return False
sha256, nbytes = link.identity(parts, entry)
# only needed if the server turns out not to have this model; None is a
# legitimate state here, see EngineMissing below
# only needed if the server turns out not to have this model and cannot
# get it; None is a legitimate state here, see EngineMissing below
model_path = parts.models.shipped_model_path()
self.log.warning("jetlink: provisioning %s (%d MB, sha %s)",
entry.get('name', sha256[:16]), nbytes >> 20, sha256[:16])
parts.progress.report('connect', 0.0, 'connecting', code='connecting')
def ensure(path):
return link.ensure(parts, self.client, sha256, nbytes, path, progress=parts.progress.report_with_eta,
should_stop=lambda: self.stop)
should_stop=lambda: self.stop, ref=entry.get('ref'))
hello = self.hello()
self.log.warning("jetlink: server %s trt %s", hello.get('device'), hello.get('trt_version'))
try:
spec = ensure(model_path)
except EngineMissing:
# the server has nothing to build from, and neither have we: fetch the
# model and hand it over in this run. The owner lends the link for the
# whole run; leaving after the download would put the build a
# WORKER_BACKOFF later
# the server has nothing to build from and could not download it, and
# neither have we: fetch the model and hand it over in this run. The
# owner lends the link for the whole run; leaving after the download
# would put the build a WORKER_BACKOFF later
if model_path is not None or (model_path := self.fetch_model()) is None:
raise
# a download of minutes can outlast the session; a hello starts a new one
@@ -186,16 +186,36 @@ class ProvisioningRun:
return True
def hello(self) -> dict:
"""Say hello, and pass the answer on to the owner, which needs its
sleep_after to decide whether letting go of the gadget is worth what it
costs and never speaks the protocol to learn it."""
hello = self.client.hello(timeout=10.0)
"""Say hello with our pick, and pass the answer on to the owner, which
needs its sleep_after to decide whether letting go of the gadget is worth
what it costs, never speaks the protocol to learn it, and keeps the
server's pick and whether it downloads for the comma (Models.heard)."""
models = self.parts.models
sent = models.hello_pick()
hello = models.heard(self.client.hello(timeout=10.0, pick=sent), sent)
if self.loan is not None:
self.loan.note_server(hello)
return hello
# -- the parked car -------------------------------------------------------
def without_jetson(self) -> None:
"""No Jetson came to the bind: a car whose Jetson is switched with the
ignition has none while parked. Only the Jetson needs the model, and one
that can download it gets it itself when it is on; the comma fetches it
now only for a Jetson that cannot (no internet when it was last here, or
too old to say), so the build can start the moment it is."""
if not self.has_work():
return
if self.needs_download():
if server_fetches():
self.parts.progress.report('waiting', 0.0, 'the jetson downloads it when it is on', code='waiting_for_host')
return
if self.fetch_model() is None:
return
self.parts.progress.report('waiting', 0.0, 'downloaded, builds when the jetson is on',
code='downloaded_waiting_host')
def has_work(self) -> bool:
"""Is there a reason to wake the Jetson? Only things the link can fix count."""
spec = self.parts.spec.load()
@@ -259,25 +279,21 @@ class ProvisioningRun:
self.log.warning("jetlink: no warp built for this camera, nothing to provision for")
return True
if not self.has_work():
# Nothing to build, unless a model was picked on the server since: a
# Jetson awake on the bus is asked, which costs a hello and no wake. Not
# a phone's cable, which is there only once the app dials in
if not self.has_work() and not (gadget.host_attached() and gadget.link_kind(self.parts.settings.mode()) == 'usb'):
self.log.warning("jetlink: nothing to provision")
return True
finished = False
try:
# the bytes first while no host is on the cable: a parked car whose
# Jetson is switched with the ignition never has one to wait for. A host
# that is there is asked first, since one with the model built needs
# nothing from the comma; provision() fetches it if the host has not
if self.needs_download() and not gadget.host_attached() and self.fetch_model() is None:
return False
if not self.open_link():
return False
if not self.wait_for_jetson():
self.log.warning("jetlink: no jetson within %.0f s, leaving it for the next run", WAKE_TIMEOUT)
if self.parts.models.shipped_model_path() is not None:
self.parts.progress.report('waiting', 0.0, 'downloaded, builds when the jetson is on',
code='downloaded_waiting_host')
self.close_link()
self.without_jetson()
return False
finished = self.provision()
except Exception:
@@ -288,6 +304,12 @@ class ProvisioningRun:
return finished
def server_fetches() -> bool:
"""Did the server the comma last spoke to say it downloads a model itself
(its hello's `fetches`, as the owner keeps it)? No for one too old to say."""
return (gadget.read_record(gadget.SERVER) or {}).get('fetches') is True
def main(argv: list[str] | None = None) -> None:
"""One provisioning round over the fork's adapter, as the owner starts it.
Exits 0 when nothing is left to do, and 1 when the owner should try again."""
+14 -8
View File
@@ -31,14 +31,17 @@ PROGRESS_MIN_INTERVAL = 0.25
# what a progress record's `code` may be, for a fork that words the line
# itself; `msg` stays the English one. `args`, where a code has them:
# downloading and upload {got_mb, total_mb}, download_stalled {retry_s},
# reconnecting {why: lost, behind, load_failed or bad_output}, build {eta_s}.
# From upload on they are stage names, and the server's stages are its own:
# downloading, download and upload {got_mb, total_mb}, download_stalled
# {retry_s}, reconnecting {why: lost, behind, load_failed or bad_output},
# build {eta_s}. downloading is the comma fetching the model, download the
# server fetching it itself. From download on they are stage names, and the
# server's stages are its own:
# a server can add one, so a fork falls back to `msg` for a code it does not
# know. A server message with no stage has no code
PROGRESS_CODES = ('waiting_for_host', 'finding_model', 'model_missing', 'ready_waiting_window', 'reconnecting',
'connecting', 'downloading', 'download_stalled', 'download_failed', 'downloaded_waiting_host',
'engine_ready', 'failed', 'upload', 'patch', 'parse', 'convert', 'compile', 'build', 'load', 'warm')
'engine_ready', 'failed', 'download', 'upload', 'patch', 'parse', 'convert', 'compile', 'build',
'load', 'warm')
def estimated_build_seconds(size: int) -> int:
@@ -104,12 +107,15 @@ class Progress:
size. Only the build: the upload reports MB of MB and a connect has nothing
to predict."""
args = None
size = self._size() if self._size is not None and stage in ('build', 'upload') else None
size = self._size() if self._size is not None and stage in ('build', 'upload', 'download') else None
if size and stage == 'build':
left = estimated_build_seconds(size) * max(0.0, 1.0 - frac)
msg, args = _eta(left), {'eta_s': round(left)}
elif size:
args = {'got_mb': int(frac * size) >> 20, 'total_mb': size >> 20}
if stage == 'download' and args:
# the server downloading the model itself: not the comma's own 'downloading'
msg = f"jetson downloading {args['got_mb']} of {args['total_mb']} MB"
self.report(stage, frac, msg, code=stage or None, args=args)
@@ -427,11 +433,11 @@ def read(parts, mode: str) -> Status:
def model_state(parts, ref: str) -> str | None:
"""One catalog model, for the picker's list: 'ready' when the Jetson has
"""One listed model, for the picker's list: 'ready' when the Jetson has
built it (the spec record), 'downloaded' when the comma has its file, else
None. Records and one stat; a model never resolved has neither."""
None. Records and one stat."""
model = next((m for m in parts.models.model_index() if m['ref'] == ref), None)
if model is None or not model.get('oid'):
if model is None:
return None
if parts.spec.engine_ready_for(model['oid']):
return 'ready'
+3 -9
View File
@@ -16,8 +16,8 @@ capnp, swaglog loads zmq, and model_face() imports modeld itself, so it is for
modeld alone, where that module is already loaded.
Data are class attributes, behaviour small methods, each a fork may override;
one override point per value: a fork sets KEYS and CATALOG_SELECTOR, and the
interface's keys and catalog_selector read them. Both are jetlink's contract:
one override point per value: a fork sets KEYS, and the interface's keys
reads it. It is jetlink's contract:
renaming or removing one, or changing what one means, is a breaking change
and bumps API. scripts/upstream (the upstream-paths CI job) checks the paths
and literals here against commaai/openpilot master weekly.
@@ -57,9 +57,7 @@ class Stock:
# the canonical names, which a fork declares in params_keys.h. No model
# manager upstream: no big-model slot or catalog, so jetlink runs its default
KEYS = Keys(link='JetlinkLink', offroad='IsOffroad', progress='JetlinkProgress', spec='JetlinkSpec',
pointers='JetlinkModelPointers', charge_phone='JetlinkChargePhone')
# the model manager's catalog version; none upstream
CATALOG_SELECTOR = 0
charge_phone='JetlinkChargePhone')
# comma's chestnut, running and in its ROM (common.hardware.usb's
# CHESTNUT_USB_IDS + CHESTNUT_ROM_USB_IDS, which CI holds this to): written
# out, as the owner cannot import that package
@@ -88,10 +86,6 @@ class Stock:
def keys(self) -> Keys:
return self.KEYS
@property
def catalog_selector(self) -> int:
return self.CATALOG_SELECTOR
@cached_property
def log(self):
from openpilot.common.swaglog import cloudlog
+2 -1
View File
@@ -192,7 +192,8 @@ _MODEL = object()
HOOK_CALLS = {'should_run': [(False, None, None)], 'status': [()], 'reason': [()], 'prepare': [(False,), (True,)],
'attach': [(_MODEL, object(), 1928, 1208)], 'handed_over': [(_MODEL,)], 'state': [(_MODEL,)],
'shutdown_step': [(False,), (True, 'test')], 'request_shutdown': [('test',)], 'shutdown_pending': [()],
'model_state': [('f' * 40,)], 'should_extend_catalog': [()], 'extend_catalog': [({'bundles': []},)],
'model_state': [('f' * 40,)], 'lists_big_models': [()], 'big_catalog': [(21,)], 'listed_ref': [('f' * 40,)],
'should_extend_catalog': [()], 'extend_catalog': [({'bundles': []},)],
'runtime': [()], 'swap_events': [(0.01,)]}
+10 -5
View File
@@ -161,7 +161,7 @@ class Warp:
out = jit.captured.ret.uop.base.buffer
self._device = out.device
if self._device.startswith('QCOM'):
_make_coherent(out)
make_coherent(out)
# the two buffers every frame passes, written in place by start()
self._tensors = {name: Tensor(np.zeros(shape, dtype=dtype), device=device).realize()
for name, (shape, dtype, device) in specs.items()}
@@ -191,10 +191,15 @@ class Warp:
self._replay(self._inputs, {})
def _make_coherent(out) -> None:
"""Move a QCOM buffer to KGSL memory flagged IOCOHERENT and write-back.
Raises if the kernel keeps either flag back (the allocation's record holds
its answer): write-back memory that is not coherent reads stale frames."""
def make_coherent(out) -> None:
"""Move a QCOM buffer to KGSL memory flagged IOCOHERENT and write-back, so
the CPU reads it through its cache once the GPU is done (see Warp). Raises
if the kernel keeps either flag back (the allocation's record holds its
answer): write-back memory that is not coherent reads stale frames.
For an adapter's model_warp too. Move a buffer before the first call of
the JIT that writes it: that call links its address into the JIT, which
would keep writing the memory this frees."""
from tinygrad.device import Device
dev = Device[out.device]
mem = dev._gpu_alloc(out.nbytes, flags=COHERENT_WRITEBACK)
+2 -2
View File
@@ -4,8 +4,8 @@ Copyright (c) 2026-, Zeph Leggett.
This file is part of jetlink and is licensed under the MIT License.
See the LICENSE file in the root directory for more details.
Which large models exist and how to get their bytes: sunnypilot's catalog
(catalog) and a model's ONNX by its git-lfs pointer (lfs).
Which large models exist and how to get their bytes: the model index, which
names every big model's ONNX (index), and an ONNX by its oid (lfs).
The comma imports both, so this package stays stdlib only and imports nothing
itself. The server's side of it is JetlinkRegistry in Swift, held to these
-211
View File
@@ -1,211 +0,0 @@
"""
Copyright (c) 2026-, Zeph Leggett.
This file is part of jetlink and is licensed under the MIT License.
See the LICENSE file in the root directory for more details.
sunnypilot's big-model catalog: which large models exist and which commit each one is.
The catalog is the same JSON the comma's model manager caches for its chestnut
slot, so a model picked here is a model the comma can ask for. A bundle's
artifacts are tinygrad pkls for a GPU we do not have; the only field that
matters is `ref`, the comma openpilot commit the bundle was compiled from,
because that commit's ONNX is what a jetlink server runs.
Stdlib only, and every network call takes an `opener` so tests stay offline.
"""
from __future__ import annotations
import json
import re
import urllib.error
import urllib.request
from dataclasses import dataclass
CATALOG_URL_TEMPLATE = 'https://raw.githubusercontent.com/sunnypilot/sunnypilot-models/refs/heads/gh-pages/docs/driving_models_chestnut_v{version}.json'
# v26 is v25 plus Cinque Terre V3, at the same selector version. v27 onwards
# are selector 20 and a newer tinygrad, for sunnypilot's next sync.
CATALOG_VERSION = 26
CATALOG_URL = CATALOG_URL_TEMPLATE.format(version=CATALOG_VERSION)
# sunnypilot publishes a new catalog version for new models or a new runtime,
# and keeps the old ones. The versions after the pinned one are probed up to
# the first that is not there, so a model published later is listed without a
# release of this package. See merge_catalogs.
PROBE_LIMIT = 10
# zoompilot's own big models: ones sunnypilot does not list (yet), such as
# previews from comma's open pull requests, in the catalog's shape. Merged
# after sunnypilot's catalogs, so their entry for the same commit wins. An
# edit on main reaches every comma within the hour, with no release.
EXTRA_CATALOG_URL = 'https://raw.githubusercontent.com/zoompilot/jetlink/refs/heads/main/catalog/extra_big_models.json'
# The selector version the fork requires (REQUIRED_JSON_VERSION on the comma).
# It is a string in the JSON; bundles at any other version describe fields we
# would misread.
REQUIRED_SELECTOR_VERSION = 19
# Cinque Terre V3 Model (September 17, 2026)
DEFAULT_BIG_MODEL_REF = 'bf3e3631b3f91d92a1020a5e0dd4298b93ff4244'
# its name as sunnypilot's catalog lists it, without the build date: what a
# comma with no catalog to name it from shows (jetlink.openpilot, on a fork
# without sunnypilot's model manager)
DEFAULT_BIG_MODEL_NAME = 'Cinque Terre V3 Model'
CATALOG_TIMEOUT = 10.0
_REF = re.compile(r'[0-9a-f]{40}')
_SHA256 = re.compile(r'[0-9a-f]{64}')
class RegistryError(Exception):
"""Anything the registry refuses to do."""
class NetworkError(RegistryError):
"""A server could not be reached or did not answer sensibly."""
class VerifyError(RegistryError):
"""Bytes arrived, but not the bytes that were asked for."""
class NotFound(NetworkError):
"""The server answered that there is nothing at the URL."""
@dataclass(frozen=True)
class CatalogModel:
name: str
short_name: str
ref: str
build_time: str
index: int
def is_ref(value: str) -> bool:
"""A comma openpilot commit: 40 lowercase hex characters."""
return isinstance(value, str) and _REF.fullmatch(value) is not None
def is_sha256(value: str) -> bool:
"""A model identity: 64 lowercase hex characters, which is also the LFS oid."""
return isinstance(value, str) and _SHA256.fullmatch(value) is not None
def parse_catalog(data: dict) -> list[CatalogModel]:
"""The big-model bundles, newest first. No IO, and no bundle can raise.
A malformed entry in a catalog served to every comma must cost that entry and
nothing else, so each bundle is parsed in its own try.
"""
found: dict[str, CatalogModel] = {}
for bundle in _bundles(data):
try:
ref = bundle.get('ref')
if not is_ref(ref) or ref in found:
continue
if _selector(bundle) != REQUIRED_SELECTOR_VERSION or not bundle.get('is_big'):
continue
found[ref] = CatalogModel(name=str(bundle.get('display_name') or ref[:10]), short_name=str(bundle.get('short_name') or ''),
ref=ref, build_time=str(bundle.get('build_time') or ''), index=int(bundle.get('index', 0)))
except (AttributeError, TypeError, ValueError):
continue
return sorted(found.values(), key=lambda m: m.index, reverse=True)
def http_get(url: str, timeout: float, opener=None, limit: int = -1) -> bytes:
"""The body at `url`, at most `limit` bytes. A 404 is NotFound, and every
other failure a NetworkError, so a caller only catches what it treats apart."""
opener = opener or urllib.request.urlopen
try:
with opener(url, timeout=timeout) as response:
return response.read(limit)
except urllib.error.HTTPError as e:
raise (NotFound if e.code == 404 else NetworkError)(f"could not fetch {url}: {e}") from e
except (OSError, ValueError) as e:
raise NetworkError(f"could not fetch {url}: {e}") from e
def http_json(url: str, timeout: float, opener=None, kind: type = dict):
"""http_get, parsed, and of the type asked for."""
body = http_get(url, timeout, opener)
try:
data = json.loads(body.decode())
except ValueError as e:
raise NetworkError(f"{url} did not serve JSON: {e}") from e
if not isinstance(data, kind):
raise NetworkError(f"{url} did not serve a JSON {kind.__name__}")
return data
def fetch_catalog(url: str = CATALOG_URL, timeout: float = CATALOG_TIMEOUT, opener=None) -> dict:
"""The catalog JSON. Every failure, transport or content, is a NetworkError."""
return http_json(url, timeout, opener)
def fetch_catalogs(opener=None) -> dict:
"""The pinned catalog merged with every one sunnypilot has published since,
whose commits the server runs too, and then zoompilot's extra models. The
probe stops at the first version that is not there, and a missing extra list
adds nothing. Any other failure raises: a short list would drop a model
found only past it from whatever the caller caches, so the caller keeps its
last complete one instead."""
found = [fetch_catalog(CATALOG_URL, opener=opener)]
for v in range(CATALOG_VERSION + 1, CATALOG_VERSION + 1 + PROBE_LIMIT):
try:
found.append(fetch_catalog(CATALOG_URL_TEMPLATE.format(version=v), opener=opener))
except NotFound:
break
try:
extra = fetch_catalog(EXTRA_CATALOG_URL, opener=opener)
except NotFound:
extra = {}
merged, pins = merge_catalogs([*found, extra]), pinned_pointers(extra)
return {**merged, 'pointers': pins} if pins else merged
def pinned_pointers(data: dict) -> dict[str, dict]:
"""The extra list's well-formed pins, {ref: {'oid', 'size'}}: the ONNX jetlink
runs for that commit whatever a lookup said. See catalog/README.md."""
pins = data.get('pointers')
if not isinstance(pins, dict):
return {}
return {ref: {'oid': p['oid'], 'size': p['size']} for ref, p in pins.items()
if is_ref(ref) and isinstance(p, dict) and is_sha256(p.get('oid'))
and type(p.get('size')) is int and p['size'] > 0}
def merge_catalogs(catalogs: list[dict], selector: int = REQUIRED_SELECTOR_VERSION) -> dict:
"""One catalog in the first one's shape, listing every big model of them all.
A model some catalog lists at `selector` comes through as published, so a
chestnut can still fetch its build. One listed only at another selector
version, which is where sunnypilot puts every model once it moves runtimes,
comes from the newest catalog that has it, retyped to `selector` and with no
artifacts: an accelerator runs the commit's ONNX and needs nothing else from
the entry, and a chestnut has nothing to download.
"""
if not catalogs:
return {}
kept: dict[str, dict] = {}
others: dict[str, dict] = {}
for data in catalogs:
for bundle in _bundles(data):
ref = bundle.get('ref')
if not is_ref(ref):
continue
if _selector(bundle) == selector:
kept.setdefault(ref, bundle)
elif bundle.get('is_big'):
others[ref] = bundle # the newest catalog's entry wins
extra = [{**b, 'minimum_selector_version': str(selector), 'models': []} for ref, b in others.items() if ref not in kept]
return {**catalogs[0], 'bundles': [*kept.values(), *extra]}
def _bundles(data: dict) -> list[dict]:
bundles = data.get('bundles') if isinstance(data, dict) else None
return [b for b in bundles if isinstance(b, dict)] if isinstance(bundles, list) else []
def _selector(bundle: dict) -> int | None:
try:
return int(bundle.get('minimum_selector_version', 0))
except (TypeError, ValueError):
return None
+193
View File
@@ -0,0 +1,193 @@
"""
Copyright (c) 2026-, Zeph Leggett.
This file is part of jetlink and is licensed under the MIT License.
See the LICENSE file in the root directory for more details.
Which big models a Jetson can run, and which ONNX each one is: the model
index, zoompilot/jetlink-model-index.
A big model is a comma openpilot commit (its ref), and what a Jetson runs is
the ONNX that commit was built from. Finding that ONNX takes reading comma's
trees, its precompiled pkls' weights and its model repo, so the index repo does
it once an hour for every commit any catalog, comma master or comma pull
request has named, and publishes the answers:
default the model a Jetson runs with nothing picked
refs every big-model commit ever seen -> {oid, size}: an old pick still resolves
onnx every ONNX comma has published -> {size}: a pick by hash alone is sized
models what to list, one entry per ONNX, in display order, with its folder
and the catalog fields a list in sunnypilot's shape needs
The comma and the server each keep the last index that parsed, so both work
offline with what they had, and neither looks anything up itself.
Stdlib only, and every network call takes an `opener` so tests stay offline.
"""
from __future__ import annotations
import json
import re
import urllib.request
from dataclasses import dataclass, field
INDEX_URL = 'https://raw.githubusercontent.com/zoompilot/jetlink-model-index/main/index/v1.json'
# the index's own version: another one describes fields we would misread
SCHEMA = 1
INDEX_TIMEOUT = 10.0
# a listed model with no folder of its own
OTHER_FOLDER = 'Other Models'
# the runtime fields of a listed model the index has none for (an index older
# than them): what sunnypilot's big models are built for
RUNTIME = {'generation': '12', 'environment': 'development', 'runner': 'tinygrad'}
# what runs with nothing picked before the comma or server has an index, which
# names its own (ModelIndex.default): Cinque Terre V3 Model (September 17, 2026)
DEFAULT_BIG_MODEL_REF = 'bf3e3631b3f91d92a1020a5e0dd4298b93ff4244'
# its name without the build date: what a comma with no index to name it from
# shows (jetlink.openpilot, on a fork without sunnypilot's model manager)
DEFAULT_BIG_MODEL_NAME = 'Cinque Terre V3 Model'
_REF = re.compile(r'[0-9a-f]{40}')
_SHA256 = re.compile(r'[0-9a-f]{64}')
class RegistryError(Exception):
"""Anything the registry refuses to do."""
class NetworkError(RegistryError):
"""A server could not be reached or did not answer sensibly."""
class VerifyError(RegistryError):
"""Bytes arrived, but not the bytes that were asked for."""
class IndexInvalid(RegistryError):
"""An index that cannot be used: another schema, or nothing listed."""
def is_ref(value) -> bool:
"""A comma openpilot commit: 40 lowercase hex characters."""
return isinstance(value, str) and _REF.fullmatch(value) is not None
def is_sha256(value) -> bool:
"""A model identity: 64 lowercase hex characters, which is also the LFS oid."""
return isinstance(value, str) and _SHA256.fullmatch(value) is not None
@dataclass(frozen=True)
class Pointer:
oid: str # the ONNX SHA-256
size: int
@dataclass(frozen=True)
class IndexModel:
ref: str
oid: str
size: int
name: str # as sunnypilot's catalog names it, build date and all
short_name: str # '' when the index has none
folder: str
build_time: str # ISO-8601, or ''
pr: int | None # the comma pull request a model listed only as one is
is_20hz: bool
generation: str # the runtime it was built for, as sunnypilot's catalog says
environment: str
runner: str
@dataclass(frozen=True)
class ModelIndex:
models: tuple[IndexModel, ...] # in display order
refs: dict[str, Pointer] # every ref the index resolves, the listed ones included
sizes: dict[str, int] # every ONNX the index knows, by oid
default: str | None = None # the listed ref that runs with nothing picked
# the listed models by their ONNX: one entry per ONNX, so one each
by_oid: dict[str, IndexModel] = field(init=False, repr=False, compare=False)
def __post_init__(self):
object.__setattr__(self, 'by_oid', {m.oid: m for m in reversed(self.models)})
def pointer(self, ref: str) -> Pointer | None:
return self.refs.get(ref)
def size(self, oid: str) -> int | None:
return self.sizes.get(oid)
def listed(self, ref: str) -> IndexModel | None:
"""The listed model for a ref's ONNX: its own entry, or for a recompile of a
listed model, the newest ref's (each ONNX is listed once)."""
pointer = self.refs.get(ref)
return self.by_oid.get(pointer.oid) if pointer is not None else None
def _pointer(value) -> Pointer | None:
if not isinstance(value, dict) or not is_sha256(value.get('oid')):
return None
size = _size(value.get('size'))
return Pointer(value['oid'], size) if size else None
def _size(value) -> int | None:
# bool is an int; a size is not
return value if isinstance(value, int) and not isinstance(value, bool) and value > 0 else None
def _text(value) -> str:
return value if isinstance(value, str) else ''
def parse_index(data) -> ModelIndex:
"""The index, or IndexInvalid naming why it cannot be used. No IO. An entry
that is malformed costs that entry and nothing else; an index of another
schema, or one listing nothing, is refused whole, so a caller keeps the
last one that parsed rather than an empty list."""
if not isinstance(data, dict):
raise IndexInvalid('the index is not a JSON object')
if data.get('schema') != SCHEMA:
raise IndexInvalid(f"the index is schema {data.get('schema')!r}, not {SCHEMA}")
refs = {ref: p for ref, value in _items(data.get('refs')) if is_ref(ref) and (p := _pointer(value))}
sizes = {oid: s for oid, value in _items(data.get('onnx')) if is_sha256(oid) and isinstance(value, dict) and (s := _size(value.get('size')))}
models: dict[str, IndexModel] = {}
for entry in data.get('models') if isinstance(data.get('models'), list) else []:
if not isinstance(entry, dict) or not is_ref(entry.get('ref')) or entry['ref'] in models or (p := _pointer(entry)) is None:
continue
ref = entry['ref']
models[ref] = IndexModel(ref=ref, oid=p.oid, size=p.size, name=_text(entry.get('name')) or ref[:10],
short_name=_text(entry.get('short_name')), folder=_text(entry.get('folder')) or OTHER_FOLDER,
build_time=_text(entry.get('build_time')), pr=_size(entry.get('pr')),
is_20hz=entry['is_20hz'] if isinstance(entry.get('is_20hz'), bool) else True,
**{k: _text(entry.get(k)) or v for k, v in RUNTIME.items()})
if not models:
raise IndexInvalid('the index lists no model')
# a listed model is what is shown and run, so its own entry names its ONNX
for m in models.values():
refs[m.ref] = Pointer(m.oid, m.size)
for p in refs.values():
sizes.setdefault(p.oid, p.size)
default = data.get('default')
return ModelIndex(models=tuple(models.values()), refs=refs, sizes=sizes, default=default if default in models else None)
def _items(value) -> list[tuple]:
return list(value.items()) if isinstance(value, dict) else []
def fetch_index(url: str = INDEX_URL, timeout: float = INDEX_TIMEOUT, opener=None) -> tuple[dict, ModelIndex]:
"""The index as served, and parsed: the raw JSON is what a caller keeps.
A transport failure is a NetworkError, an index that cannot be used
IndexInvalid; both are RegistryErrors, and either way nothing is kept."""
opener = opener or urllib.request.urlopen
try:
with opener(url, timeout=timeout) as response:
body = response.read()
except (OSError, ValueError) as e: # HTTPError is an OSError
raise NetworkError(f"could not fetch {url}: {e}") from e
try:
data = json.loads(body.decode())
except ValueError as e:
raise NetworkError(f"{url} did not serve JSON: {e}") from e
return data, parse_index(data)
+37 -222
View File
@@ -4,27 +4,14 @@ Copyright (c) 2026-, Zeph Leggett.
This file is part of jetlink and is licensed under the MIT License.
See the LICENSE file in the root directory for more details.
Getting a large model's ONNX by its git-lfs oid.
Getting a large model's ONNX by its oid, the one the model index names
(jetlink.registry.index).
comma overwrites one file per model, so the commit is the only name a big
model's ONNX has. GitHub's raw host serves the LFS pointer for any commit it
holds, merged or not, and that pointer carries the oid and size the comma will
ask a jetlink server for. The bytes themselves are on comma's LFS servers,
which is GitLab and not GitHub: each is asked in turn because which one has an
object varies with the model's age.
Since openpilot #38930 a commit can ship a precompiled tinygrad
pkl instead, with no ONNX in the tree at all; Cinque Terre V3 is one. Its
subject names the export ("Use f78ed37d for the precompiled eGPU driving
model") and the ONNX is in comma's HuggingFace model repo, in the folder that
id starts. sunnypilot's model builds find it the same way. That repo speaks
the LFS batch protocol too, so it is one more endpoint to ask.
Not every such commit names its export. ResAction's pull request (#39037)
adds the ONNX in one commit and replaces it with the pkl in the
next, subject "compiled", and a squash merge's subject only names the pull
request. The pointer is still in a diff: the one the commit's own patch
deletes, or the last one its pull request's patch carries.
The bytes are on comma's LFS servers. Since openpilot #39206 comma's own LFS
client stores objects in a HuggingFace bucket, read by oid; the older LFS
servers, and comma's model repo for the exports behind a precompiled pkl,
speak the LFS batch protocol, and which one has an object varies with the
model's age, so each is asked in turn.
The zoompilot fork downloads through this module too, asking its own
.lfsconfig endpoint first. No openpilot imports.
@@ -34,49 +21,29 @@ from __future__ import annotations
import hashlib
import json
import logging
import re
import shutil
import urllib.parse
import urllib.request
from collections.abc import Callable
from dataclasses import dataclass
from pathlib import Path
from jetlink.registry.catalog import NetworkError, NotFound, RegistryError, VerifyError, http_get, http_json, is_sha256
from jetlink.registry.index import NetworkError, Pointer, RegistryError, VerifyError
__all__ = ['BUCKET', 'LFS_ENDPOINTS', 'Pointer', 'lfs_download', 'lfs_hrefs', 'lfs_resolve']
log = logging.getLogger('jetlink.registry')
BIG_ONNX = 'big_driving_supercombo.onnx'
POINTER_URL = 'https://raw.githubusercontent.com/commaai/openpilot/{ref}/openpilot/selfdrive/modeld/models/' + BIG_ONNX
# a commit's subject without the API, whose anonymous limit a car behind CGNAT shares
COMMIT_PATCH_URL = 'https://github.com/commaai/openpilot/commit/{ref}.patch'
# every commit of a pull request, one patch after another, for a squash merge's subject
PULL_PATCH_URL = 'https://github.com/commaai/openpilot/pull/{number}.patch'
# how much of a patch is read for the pointers in its diffs; a model commit's is 3 KB
PATCH_MAX = 256 << 10
# the diff header of the big ONNX, wherever the tree keeps selfdrive
_ONNX_DIFF = re.compile(r'^diff --git a/\S*selfdrive/modeld/models/' + re.escape(BIG_ONNX) + r' ')
# a squash merge's subject ends with its pull request
_PULL = re.compile(r'\(#([0-9]+)\)$')
# where comma's LFS client keeps objects since openpilot #39206. A HuggingFace
# bucket is not an LFS server: an object is read by its oid under it (lfs_resolve)
BUCKET = 'https://huggingface.co/buckets/commaai/openpilot-lfs'
DRIVING_MODELS_REPO = 'commaai/openpilot_driving_models'
DRIVING_MODELS_TREE_URL = f'https://huggingface.co/api/models/{DRIVING_MODELS_REPO}/tree/main'
LFS_ENDPOINTS = (
BUCKET,
'https://gitlab.com/commaai/openpilot-lfs.git/info/lfs', # every object, older and PR-branch models included
'https://huggingface.co/commaai/openpilot-lfs.git/info/lfs', # where comma is moving them; the current ones
'https://huggingface.co/commaai/openpilot-lfs.git/info/lfs', # the current ones
f'https://huggingface.co/{DRIVING_MODELS_REPO}.git/info/lfs', # the exports behind a precompiled pkl
)
# an export folder is a uuid; the subject names its first eight
_EXPORT_ID = re.compile(r'\b[0-9a-f]{8}\b')
# how comma's model commits name an export: the whole uuid, or its first eight
# and the checkpoint ('8d736683/12864'). A bare eight could be any hex word
_NAMED_EXPORT = r'\b{}[-/]'
LFS_MEDIA_TYPE = 'application/vnd.git-lfs+json'
POINTER_TIMEOUT = 10.0
CONNECT_TIMEOUT = 30.0
# A pointer is 134 bytes. Anything larger is the ONNX itself, served by a host
# that resolved the LFS filter for us, and reading a gigabyte to find that out
# is not on.
POINTER_MAX = 4096
CHUNK = 4 << 20
FREE_SLACK = 64 << 20
@@ -84,190 +51,30 @@ ProgressFn = Callable[[float], None]
StopFn = Callable[[], bool]
@dataclass(frozen=True)
class Pointer:
oid: str # the ONNX SHA-256
size: int
def parse_pointer_text(text: str) -> Pointer | None:
"""The oid and size in a git-lfs pointer's text, or None if it is not one."""
if not isinstance(text, str) or len(text.encode('utf-8', 'replace')) > POINTER_MAX:
return None
oid = None
size = None
for line in text.splitlines():
key, _, value = line.partition(' ')
if key == 'oid':
oid = value.removeprefix('sha256:').strip()
elif key == 'size':
try:
size = int(value)
except ValueError:
return None
if not is_sha256(oid) or size is None or size <= 0:
return None
return Pointer(oid, size)
def fetch_pointer(ref: str, timeout: float = POINTER_TIMEOUT, opener=None) -> Pointer:
"""The oid and size of the ONNX at a comma commit: in its tree, or for a
commit that ships a precompiled pkl instead, the ONNX it was built from."""
def _bucket_href(bucket: str, pointer: Pointer, timeout: float, opener) -> str | None:
"""A bucket's address for the object, or None when it does not have it or
cannot be reached: its first byte is asked for, as cheap as a HEAD."""
url = f"{bucket}/resolve/sha256/{pointer.oid}"
try:
text = http_get(POINTER_URL.format(ref=ref), timeout, opener, POINTER_MAX).decode('utf-8', 'replace')
except NotFound:
return fetch_export_pointer(ref, timeout=timeout, opener=opener)
pointer = parse_pointer_text(text)
if pointer is None:
raise RegistryError(f"{ref[:10]} did not serve an lfs pointer")
return pointer
def patch_subjects(text: str) -> list[str]:
"""Every commit's subject in a patch, in order: one for a commit's, one per
commit for a pull request's."""
lines = text.splitlines()
subjects = []
for i, line in enumerate(lines):
if line.startswith('Subject:'):
subject = [line.removeprefix('Subject:').strip()]
# a long subject is folded onto indented lines; the headers end at a blank one
for cont in lines[i + 1:]:
if not cont[:1].isspace() or not cont.strip():
break
subject.append(cont.strip())
subjects.append(re.sub(r'^\[PATCH[^\]]*\]\s*', '', ' '.join(subject)))
return subjects
def diff_pointer(text: str) -> Pointer | None:
"""The big ONNX's pointer in a patch's diffs, the last one wins: the pointer a
diff gives the file, or for a diff that deletes it, the pointer it had. A
commit that swaps the ONNX for a pkl deletes the ONNX the pkl was built from."""
found = None
section: list[str] | None = None
def close():
nonlocal found
if section is not None:
added = parse_pointer_text('\n'.join(x[1:] for x in section if x.startswith('+') and not x.startswith('+++')))
removed = parse_pointer_text('\n'.join(x[1:] for x in section if x.startswith('-') and not x.startswith('---')))
found = added or removed or found
for line in text.splitlines():
# each diff, and each commit of a pull request's patch, ends the one before
if line.startswith(('diff --git ', 'From ')):
close()
section = [] if _ONNX_DIFF.match(line) else None
elif section is not None:
section.append(line)
close()
return found
def commit_subject(ref: str, timeout: float = POINTER_TIMEOUT, opener=None) -> str:
"""A comma commit's subject line, from the head of its patch."""
url = COMMIT_PATCH_URL.format(ref=ref)
subjects = patch_subjects(http_get(url, timeout, opener, POINTER_MAX).decode('utf-8', 'replace'))
if not subjects:
raise RegistryError(f"{url} has no subject line")
return subjects[0]
def _tree(path: str, timeout: float, opener) -> list[dict]:
url = DRIVING_MODELS_TREE_URL + (f"/{urllib.parse.quote(path)}?recursive=true" if path else '')
return [e for e in http_json(url, timeout, opener, list) if isinstance(e, dict) and isinstance(e.get('path'), str)]
def fetch_export_pointer(ref: str, timeout: float = POINTER_TIMEOUT, opener=None) -> Pointer:
"""The big ONNX a precompiled-pkl commit was built from. In turn: the export
its subject names, in comma's model repo; the pointer its own diff deletes;
and for a squash merge, the last pointer its pull request carried, or the
export one of that pull request's commits names.
The newest export named wins: a pull request's commits are in order, and
Mountain Dew's (#39047) named three before the one it shipped. One that is
not in the model repo yet stops the search rather than falling back to an
older one, a different model."""
url = COMMIT_PATCH_URL.format(ref=ref)
patch = http_get(url, timeout, opener, PATCH_MAX).decode('utf-8', 'replace')
subjects = patch_subjects(patch)
if not subjects:
raise RegistryError(f"{url} has no subject line")
subject = subjects[0]
ids = _newest_first(subject)
pointer, missing = _export_pointer(ref, ids, subject, timeout, opener)
if pointer is not None:
return pointer
tried = [f"its subject {_not_uploaded(missing)}" if missing else f"no folder in {DRIVING_MODELS_REPO} for {', '.join(ids)}" if ids
else 'its subject names no export']
if (pointer := diff_pointer(patch)) is not None:
log.info("%s deletes %s %s", ref[:10], BIG_ONNX, pointer.oid[:16])
return pointer
tried.append(f"its diff has no {BIG_ONNX}")
if (pull := _PULL.search(subject)) is not None:
number = pull.group(1)
pull_patch = http_get(PULL_PATCH_URL.format(number=number), timeout, opener, PATCH_MAX).decode('utf-8', 'replace')
if (pointer := diff_pointer(pull_patch)) is not None:
log.info("%s merged #%s, whose last %s is %s", ref[:10], number, BIG_ONNX, pointer.oid[:16])
return pointer
if missing is None: # an export the subject names and the repo lacks stops the search here too
pull_subjects = ' '.join(patch_subjects(pull_patch))
pull_ids = [i for i in _newest_first(pull_subjects) if i not in ids]
pointer, pull_missing = _export_pointer(ref, pull_ids, pull_subjects, timeout, opener)
if pointer is not None:
return pointer
tried.append(f"pull request #{number} {_not_uploaded(pull_missing)}" if pull_missing else f"nor does pull request #{number}")
raise RegistryError(f"{ref[:10]} has no {BIG_ONNX}: {'; '.join(tried)} ({subject!r})")
def _newest_first(text: str) -> list[str]:
"""The eight-hex ids `text` names, the last mention first."""
return list(dict.fromkeys(reversed(_EXPORT_ID.findall(text))))
def _not_uploaded(export: str) -> str:
return f"names export {export}, which is not in {DRIVING_MODELS_REPO} yet"
def _export_pointer(ref: str, ids: list[str], subject: str, timeout: float, opener) -> tuple[Pointer | None, str | None]:
"""The ONNX in the first export folder one of `ids` starts, and None; or
None and the first id `subject` names as an export that has no folder, where
the search stops; or None and None when no id has a folder."""
if not ids:
return None, None
folders = [e['path'] for e in _tree('', timeout, opener) if e.get('type') == 'directory']
for export in ids:
matches = [f for f in folders if f.startswith(export)]
if not matches:
if re.search(_NAMED_EXPORT.format(export), subject):
return None, export
continue
if len(matches) > 1:
raise RegistryError(f"{ref[:10]}: {export} starts {len(matches)} folders in {DRIVING_MODELS_REPO}")
files = [e for e in _tree(matches[0], timeout, opener)
if e.get('type') == 'file' and e['path'].rsplit('/', 1)[-1] == BIG_ONNX]
if len(files) > 1:
# a subject that names the checkpoint, as '1a421175-.../12864' does, picks one
files = [f for f in files if f['path'].rsplit('/', 1)[0] in subject] or files
if len(files) != 1:
raise RegistryError(f"{ref[:10]}: {len(files)} copies of {BIG_ONNX} under {matches[0]}")
lfs = files[0].get('lfs') or {}
oid, size = lfs.get('oid'), lfs.get('size')
if not is_sha256(oid) or not isinstance(size, int) or size <= 0:
raise RegistryError(f"{files[0]['path']} is not an lfs object")
log.info("%s names export %s: %s", ref[:10], export, files[0]['path'])
return Pointer(oid, size), None
return None, None
with opener(urllib.request.Request(url, headers={'Range': 'bytes=0-0'}), timeout=timeout) as response:
response.read(1)
except (OSError, ValueError) as e:
log.warning("%s has no %s (%s)", bucket, pointer.oid[:16], e)
return None
return url
def lfs_resolve(endpoint: str, pointer: Pointer, timeout: float = CONNECT_TIMEOUT, opener=None) -> str | None:
"""Ask one LFS server for a download href, or None if it does not have it.
A HuggingFace bucket, comma's (BUCKET) or one a checkout's .lfsconfig
names, is asked for the object itself.
A server that is down is not different from a server that lacks the object:
either way the caller moves to the next one, so nothing raises here.
"""
opener = opener or urllib.request.urlopen
if '/buckets/' in endpoint:
return _bucket_href(endpoint, pointer, timeout, opener)
body = json.dumps({
'operation': 'download',
'transfers': ['basic'],
@@ -294,6 +101,14 @@ def lfs_resolve(endpoint: str, pointer: Pointer, timeout: float = CONNECT_TIMEOU
return None
def lfs_hrefs(endpoints, pointer: Pointer, timeout: float = CONNECT_TIMEOUT, opener=None):
"""Each of `endpoints` that has the object, in order, with its download href."""
for endpoint in endpoints:
href = lfs_resolve(endpoint, pointer, timeout=timeout, opener=opener)
if href is not None:
yield endpoint, href
def lfs_download(href: str, pointer: Pointer, dest: Path, progress: ProgressFn | None = None,
should_stop: StopFn | None = None, opener=None) -> Path:
"""Stream to a .part file, hashing as we go, and only then take the name.
Executable
+4
View File
@@ -0,0 +1,4 @@
#!/usr/bin/env bash
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
exec "${ROOT_DIR}/scripts/host_tool_runner.sh" jotpluggler "$@"
+37 -4
View File
@@ -57,6 +57,37 @@ 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"
@@ -75,8 +106,10 @@ function launch {
if [ $? -eq 0 ]; then
echo "${DIR} has been modified, skipping overlay update installation"
else
if [ -f "${STAGING_ROOT}/finalized/.overlay_consistent" ]; then
if [ ! -d /data/safe_staging/old_openpilot ]; then
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
echo "Valid overlay update found, installing"
LAUNCHER_LOCATION="${BASH_SOURCE[0]}"
@@ -84,10 +117,10 @@ function launch {
mv "${STAGING_ROOT}/finalized" "$DIR"
cd "$DIR"
# releases before the openpilot/ move can't swap AGNOS on newer AGNOS, use ours
[ ! -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
maybe_activate_legacy_agnos "$DIR" /data/safe_staging/old_openpilot/openpilot/common/hardware/comma/agnos.py system/hardware/tici/agnos.json
echo "Restarting launch script ${LAUNCHER_LOCATION}"
unset AGNOS_VERSION
reset_update_os_environment
exec "${LAUNCHER_LOCATION}"
else
echo "openpilot backup found, not updating"
+15 -1
View File
@@ -1,5 +1,10 @@
#!/usr/bin/env bash
# Device Params must not depend on the platform used to compile native libraries.
if [ -f /AGNOS ] && [ -z "${PARAMS_ROOT:-}" ]; then
export PARAMS_ROOT="/data/params"
fi
export OMP_NUM_THREADS=1
export MKL_NUM_THREADS=1
export NUMEXPR_NUM_THREADS=1
@@ -21,9 +26,18 @@ fi
export QCOM_PRIORITY=12
if [ -z "$AGNOS_VERSION" ]; then
export AGNOS_VERSION="19.8.1"
export AGNOS_VERSION="20.2.2"
fi
export AGNOS_UPDATE_POLICY="auto"
# C3 selects its complete matching firmware chain in the AGNOS manifest loader.
# Unknown hardware retains its installed OS.
if [ -f /AGNOS ] || [ -e /sys/firmware/devicetree/base/model ]; then
case "$(tr -d '\0' 2>/dev/null < /sys/firmware/devicetree/base/model)" in
"comma tici"|"comma tizi"|"comma mici") ;;
*) export AGNOS_UPDATE_POLICY="retain" ;;
esac
fi
export STAGING_ROOT="/data/safe_staging"
+11 -1
View File
@@ -10,7 +10,12 @@ $Go.import("pfeifer.dev/mapd/cereal/custom");
# DO rename the structs
# DON'T change the identifier (e.g. @0x81c2f05a394cf4af)
struct CustomReserved0 @0x81c2f05a394cf4af {
struct StarPilotCarControl @0x81c2f05a394cf4af {
# The first three fields are pointer slots mapd does not read.
hudControl @0 :AnyPointer;
steeringLimitInfo @1 :AnyPointer;
navigation @2 :AnyPointer;
phoneGps @3 :StarPilotCarState.Gps; # sourceMonoTime is comma receive time minus the phone's fix age
}
struct CustomReserved1 @0xaedffd8f31e7b55d {
@@ -286,6 +291,11 @@ struct MapdOut @0xa4f1eb3323f5f582 {
computedMonoTime @31 :UInt64; # Computation boot-time nanoseconds, not send time.
sourceGeneration @32 :UInt64;
producerSession @33 :UInt64; # Random nonzero process session.
# Data-only preview from the same qualified GPS fix; no controller smoothing.
previewSpeedLimit @34 :Float32;
previewDistance @35 :Float32;
previewBoundaryId @36 :Text;
previewWayId @37 :Int64;
enum SampleStatus {
unknown @0;
+456 -334
View File
@@ -9,69 +9,137 @@ import (
math "math"
)
type CustomReserved0 capnp.Struct
type StarPilotCarControl capnp.Struct
// CustomReserved0_TypeID is the unique identifier for the type CustomReserved0.
const CustomReserved0_TypeID = 0x81c2f05a394cf4af
// StarPilotCarControl_TypeID is the unique identifier for the type StarPilotCarControl.
const StarPilotCarControl_TypeID = 0x81c2f05a394cf4af
func NewCustomReserved0(s *capnp.Segment) (CustomReserved0, error) {
st, err := capnp.NewStruct(s, capnp.ObjectSize{DataSize: 0, PointerCount: 0})
return CustomReserved0(st), err
func NewStarPilotCarControl(s *capnp.Segment) (StarPilotCarControl, error) {
st, err := capnp.NewStruct(s, capnp.ObjectSize{DataSize: 0, PointerCount: 4})
return StarPilotCarControl(st), err
}
func NewRootCustomReserved0(s *capnp.Segment) (CustomReserved0, error) {
st, err := capnp.NewRootStruct(s, capnp.ObjectSize{DataSize: 0, PointerCount: 0})
return CustomReserved0(st), err
func NewRootStarPilotCarControl(s *capnp.Segment) (StarPilotCarControl, error) {
st, err := capnp.NewRootStruct(s, capnp.ObjectSize{DataSize: 0, PointerCount: 4})
return StarPilotCarControl(st), err
}
func ReadRootCustomReserved0(msg *capnp.Message) (CustomReserved0, error) {
func ReadRootStarPilotCarControl(msg *capnp.Message) (StarPilotCarControl, error) {
root, err := msg.Root()
return CustomReserved0(root.Struct()), err
return StarPilotCarControl(root.Struct()), err
}
func (s CustomReserved0) String() string {
func (s StarPilotCarControl) String() string {
str, _ := text.Marshal(0x81c2f05a394cf4af, capnp.Struct(s))
return str
}
func (s CustomReserved0) EncodeAsPtr(seg *capnp.Segment) capnp.Ptr {
func (s StarPilotCarControl) EncodeAsPtr(seg *capnp.Segment) capnp.Ptr {
return capnp.Struct(s).EncodeAsPtr(seg)
}
func (CustomReserved0) DecodeFromPtr(p capnp.Ptr) CustomReserved0 {
return CustomReserved0(capnp.Struct{}.DecodeFromPtr(p))
func (StarPilotCarControl) DecodeFromPtr(p capnp.Ptr) StarPilotCarControl {
return StarPilotCarControl(capnp.Struct{}.DecodeFromPtr(p))
}
func (s CustomReserved0) ToPtr() capnp.Ptr {
func (s StarPilotCarControl) ToPtr() capnp.Ptr {
return capnp.Struct(s).ToPtr()
}
func (s CustomReserved0) IsValid() bool {
func (s StarPilotCarControl) IsValid() bool {
return capnp.Struct(s).IsValid()
}
func (s CustomReserved0) Message() *capnp.Message {
func (s StarPilotCarControl) Message() *capnp.Message {
return capnp.Struct(s).Message()
}
func (s CustomReserved0) Segment() *capnp.Segment {
func (s StarPilotCarControl) Segment() *capnp.Segment {
return capnp.Struct(s).Segment()
}
// CustomReserved0_List is a list of CustomReserved0.
type CustomReserved0_List = capnp.StructList[CustomReserved0]
// NewCustomReserved0 creates a new list of CustomReserved0.
func NewCustomReserved0_List(s *capnp.Segment, sz int32) (CustomReserved0_List, error) {
l, err := capnp.NewCompositeList(s, capnp.ObjectSize{DataSize: 0, PointerCount: 0}, sz)
return capnp.StructList[CustomReserved0](l), err
func (s StarPilotCarControl) HudControl() (capnp.Ptr, error) {
return capnp.Struct(s).Ptr(0)
}
// CustomReserved0_Future is a wrapper for a CustomReserved0 promised by a client call.
type CustomReserved0_Future struct{ *capnp.Future }
func (s StarPilotCarControl) HasHudControl() bool {
return capnp.Struct(s).HasPtr(0)
}
func (f CustomReserved0_Future) Struct() (CustomReserved0, error) {
func (s StarPilotCarControl) SetHudControl(v capnp.Ptr) error {
return capnp.Struct(s).SetPtr(0, v)
}
func (s StarPilotCarControl) SteeringLimitInfo() (capnp.Ptr, error) {
return capnp.Struct(s).Ptr(1)
}
func (s StarPilotCarControl) HasSteeringLimitInfo() bool {
return capnp.Struct(s).HasPtr(1)
}
func (s StarPilotCarControl) SetSteeringLimitInfo(v capnp.Ptr) error {
return capnp.Struct(s).SetPtr(1, v)
}
func (s StarPilotCarControl) Navigation() (capnp.Ptr, error) {
return capnp.Struct(s).Ptr(2)
}
func (s StarPilotCarControl) HasNavigation() bool {
return capnp.Struct(s).HasPtr(2)
}
func (s StarPilotCarControl) SetNavigation(v capnp.Ptr) error {
return capnp.Struct(s).SetPtr(2, v)
}
func (s StarPilotCarControl) PhoneGps() (StarPilotCarState_Gps, error) {
p, err := capnp.Struct(s).Ptr(3)
return StarPilotCarState_Gps(p.Struct()), err
}
func (s StarPilotCarControl) HasPhoneGps() bool {
return capnp.Struct(s).HasPtr(3)
}
func (s StarPilotCarControl) SetPhoneGps(v StarPilotCarState_Gps) error {
return capnp.Struct(s).SetPtr(3, capnp.Struct(v).ToPtr())
}
// NewPhoneGps sets the phoneGps field to a newly
// allocated StarPilotCarState_Gps struct, preferring placement in s's segment.
func (s StarPilotCarControl) NewPhoneGps() (StarPilotCarState_Gps, error) {
ss, err := NewStarPilotCarState_Gps(capnp.Struct(s).Segment())
if err != nil {
return StarPilotCarState_Gps{}, err
}
err = capnp.Struct(s).SetPtr(3, capnp.Struct(ss).ToPtr())
return ss, err
}
// StarPilotCarControl_List is a list of StarPilotCarControl.
type StarPilotCarControl_List = capnp.StructList[StarPilotCarControl]
// NewStarPilotCarControl creates a new list of StarPilotCarControl.
func NewStarPilotCarControl_List(s *capnp.Segment, sz int32) (StarPilotCarControl_List, error) {
l, err := capnp.NewCompositeList(s, capnp.ObjectSize{DataSize: 0, PointerCount: 4}, sz)
return capnp.StructList[StarPilotCarControl](l), err
}
// StarPilotCarControl_Future is a wrapper for a StarPilotCarControl promised by a client call.
type StarPilotCarControl_Future struct{ *capnp.Future }
func (f StarPilotCarControl_Future) Struct() (StarPilotCarControl, error) {
p, err := f.Future.Ptr()
return CustomReserved0(p.Struct()), err
return StarPilotCarControl(p.Struct()), err
}
func (p StarPilotCarControl_Future) HudControl() *capnp.Future {
return p.Future.Field(0, nil)
}
func (p StarPilotCarControl_Future) SteeringLimitInfo() *capnp.Future {
return p.Future.Field(1, nil)
}
func (p StarPilotCarControl_Future) Navigation() *capnp.Future {
return p.Future.Field(2, nil)
}
func (p StarPilotCarControl_Future) PhoneGps() StarPilotCarState_Gps_Future {
return StarPilotCarState_Gps_Future{Future: p.Future.Field(3, nil)}
}
type CustomReserved1 capnp.Struct
@@ -2819,12 +2887,12 @@ type MapdOut capnp.Struct
const MapdOut_TypeID = 0xa4f1eb3323f5f582
func NewMapdOut(s *capnp.Segment) (MapdOut, error) {
st, err := capnp.NewStruct(s, capnp.ObjectSize{DataSize: 112, PointerCount: 6})
st, err := capnp.NewStruct(s, capnp.ObjectSize{DataSize: 128, PointerCount: 7})
return MapdOut(st), err
}
func NewRootMapdOut(s *capnp.Segment) (MapdOut, error) {
st, err := capnp.NewRootStruct(s, capnp.ObjectSize{DataSize: 112, PointerCount: 6})
st, err := capnp.NewRootStruct(s, capnp.ObjectSize{DataSize: 128, PointerCount: 7})
return MapdOut(st), err
}
@@ -3192,12 +3260,54 @@ func (s MapdOut) SetProducerSession(v uint64) {
capnp.Struct(s).SetUint64(104, v)
}
func (s MapdOut) PreviewSpeedLimit() float32 {
return math.Float32frombits(capnp.Struct(s).Uint32(76))
}
func (s MapdOut) SetPreviewSpeedLimit(v float32) {
capnp.Struct(s).SetUint32(76, math.Float32bits(v))
}
func (s MapdOut) PreviewDistance() float32 {
return math.Float32frombits(capnp.Struct(s).Uint32(112))
}
func (s MapdOut) SetPreviewDistance(v float32) {
capnp.Struct(s).SetUint32(112, math.Float32bits(v))
}
func (s MapdOut) PreviewBoundaryId() (string, error) {
p, err := capnp.Struct(s).Ptr(6)
return p.Text(), err
}
func (s MapdOut) HasPreviewBoundaryId() bool {
return capnp.Struct(s).HasPtr(6)
}
func (s MapdOut) PreviewBoundaryIdBytes() ([]byte, error) {
p, err := capnp.Struct(s).Ptr(6)
return p.TextBytes(), err
}
func (s MapdOut) SetPreviewBoundaryId(v string) error {
return capnp.Struct(s).SetText(6, v)
}
func (s MapdOut) PreviewWayId() int64 {
return int64(capnp.Struct(s).Uint64(120))
}
func (s MapdOut) SetPreviewWayId(v int64) {
capnp.Struct(s).SetUint64(120, uint64(v))
}
// MapdOut_List is a list of MapdOut.
type MapdOut_List = capnp.StructList[MapdOut]
// NewMapdOut creates a new list of MapdOut.
func NewMapdOut_List(s *capnp.Segment, sz int32) (MapdOut_List, error) {
l, err := capnp.NewCompositeList(s, capnp.ObjectSize{DataSize: 112, PointerCount: 6}, sz)
l, err := capnp.NewCompositeList(s, capnp.ObjectSize{DataSize: 128, PointerCount: 7}, sz)
return capnp.StructList[MapdOut](l), err
}
@@ -3327,306 +3437,318 @@ func NewMapdOut_GpsSource_List(s *capnp.Segment, sz int32) (MapdOut_GpsSource_Li
return capnp.NewEnumList[MapdOut_GpsSource](s, sz)
}
const schema_b526ba661d550a59 = "x\xda\x94z\x0d\x90T\xd5\x95\xff9\xef\xf5L\xcf'" +
"M\xf3\x1a\x01\x01\xdb!\x928\xc41\xc0\xc8?\x0eh" +
"\x86\xa1g@\xa8\xeea\xde<>\x1c\x94\xaa\xdc\xe9w" +
"\xa7\xe7\xc1\x9b\xf7\x9a\xf7^\xcfLSX \x81*\xf4" +
"\x8f\x15\xcdj\x82)\xd9\xf8\x81\xb5\x9a\x95\x84du\x8b" +
"XX\x0b\x14\xa9B\x17\xaa4\xa5)c\xcc\xfaQ\xd9" +
"\xd2d\xb5\x8cY\xad5\xd9X\xbdu\xee\xed\xaf\xf9\x88" +
"8E\xd5t\xf7\xef\x9cw\xee\xb9\xe7\x9e{\xee\xf9\xdd" +
"\xc7r6\x7fmhE\xf3?\x8d\x81bdkjj" +
"\x0b\xd1\xdd\x03\xef:\xc1O\x0e@t!\x16\x06\x1a\xb6" +
".\x1ez\xfe\xab\xcfA(\x0c\xd0\xfe\x8b5;P\xbb" +
"\xbc&\x0cj\xe1\xa7\x9f$;v\xfc\xe9\xfc\xdd\xd3h" +
"=MZ/\x08\xad\xdb\x13\xe7\xbf;\xf8\xd5\x1f\x1d\x86" +
"\xe8B\xa5\xa2\x05\xd8\xfe\xf0\x9aM\xa8\x9d\\#\xd5\xdd" +
"\x10`\xa1\xed\xc5\xdc\xed?^p\xdf\x03\xa0\xb7a}" +
"\xe1\xbc\xf3o\x8d\xdb.\xdc\xf9\xdfP\x83\xa4\xb3t\xed" +
"\xbd\xa8\xdd\xba\x96\xbev\xac}K\x01,\xfc\xe3\xd7?" +
"\xcaY{w|\x1f\xa2_Q\x0a\x07?\xfd\xf4+\xed" +
"\xff\xf5\xf1\x09\xb2\xbc'q\x10\xb5\xc3\x890\x80vw" +
"\xe2\x07\x80\x859\xcfb&s\xf1\xf2#\xd3x\xfaJ" +
"b\x10\xb5\xffL\x90\xa7m\xbf\x8d\xab\xdd\xe1\xe1\xc7\xa6" +
"\xd1:\x97\xd8\x81\xdakB\xcbV\xdb\xd7~6X\xfb" +
"\x04\xe8\x0b\xb1J\xadF\xe8\xfd\x8c\xf4~\x99\x90\x8fl" +
"G\xc0\x8a_\xfaB\x9cU\xa5^K:\xabz\xaeF" +
"\xad\xa7\xe7k\x00\xed\xacg,\x0cX\xe8N\x9c}\xe9" +
"\xe4[O<9%Z\x7fH\x1dD\xed\xf3\x14\xcd\xe9" +
"/\xa9o\x02\x16\xb6\x9e\xd0\x7fs\xc3\xe8\xe5'\xa7\xf1" +
"\xf6\xf3\xd4\x0e\xd4\xa2\xbd\xe4\xedO~\x9b~\xe8\xff?" +
"\xf5\xd1?O\xb1\xf7aju\xc5\xdef\xc0\xc2\xce\xe7" +
"\xde[\xf1\xdd\xcf\xdf\xfa\xc94\xf6\x9a{w\xa0\xd6\"" +
"\xecu\xbe\xf5\xc7\xbbR\xd7n?5\xed\xa8\x83\xa5Q" +
"/b\xb2\xc5J;\xa7\xa7\xd1\xfa\x90|C\xa15|" +
"\xe6\x1f\x1e4\xd6t\xbf0\x8d\xd6\xef\xc8\xd6\xc7)\xd2" +
":\xa4\xde\xf46\xbf\xe5\xba\xf3\xd3h]&\xadw\x84" +
"\xd6G\xff\xf2\xadWn\xbeu\xd7\xc5\xc9\xab\xa2\x90\xde" +
"\x0b\xa99\xa8]\xa6\xb9\xb6\xbf\x98\x8a\xd3\xaa\xac\x1e\xd8" +
"\x9e\xb5_\xfb\xd1\xbfO7t/\x0d-\x1c|\xf9\xe0" +
"\xa3\x99\xff}\xe3\xfb\x97\xa7\x1b\x9a\xb4\xde\x11Z\x8b/" +
"\xa4\xdf|38\xf8\xca\x94\x10\xbf\xd0\xbb\x0e\xb5\xcb\xbd" +
"b\xd8^\x91\x0c\x1d\x1dg\x1e\xbbt\xef\xff\xfc\x9a\xbc" +
"T'o\xac\xcd\x9bP\xbb\xbc\x99\x16\xe4\xc5\xcd\xef\x01" +
"\x16V%\xbb\xbfsl\xfb\xf7\xdf\x98f\xf4\x93};" +
"P;\xd7G\xa3w|\xe7\xa5\x1f\xfe\xa0\xe6\x07\xffA" +
"&\x95Ij\xc7\xfb\xd6\xa1v\xb2\x8fL>\xdd7\x06" +
"X\xb8\xfe\x95_\x7ftC\xcf\xb3\x7f\x9e\xb2c\x9a\xf5" +
"]\xa8\xb5\xe8\xa4\xb9X\xcf\x00V6\xdfD\xb3r+" +
"n$\xed\x9d\xfa<\x80v\xae\x87)m\x17\x1c?\xbe" +
"\xb1\xfe\x83\xab>\x99\xc6\xd7\x91\x81\x1d\xa8\xdd=@\xbe" +
">\x11\xca~\xbe\xe6\xd0}\x7f\x99Fk'i\xed\x11" +
"Z\xcb\xde_\xda\xfb\xde\x9d\xdf\xfa\xeb\x94x\xa6\x06\x06" +
"Qc\x03\xe4\xe4\xce\x81\xfd\x80\x85\xc7\xfd\xbe\x8b/\xed" +
"|\xe2\xaf\x93\x9d\x14\x8b~|\xe0 j?\x1b\x10\xe1" +
"\x1a\x10\xd1\x8f\xfe\xeb\xd8\x91\x0f\xbb\x06\xff6\xcd\xf0m" +
"w\x0c\xa2\xd6u\x07\x0d\xbf\xff\xd8\xa9\xf7\x8ccG\x0a" +
"\x933I\xcc|\xf1\x1d\xcf\xa3\xb6\xea\x0era\xc5\x1d" +
"?\x85\xc2\xdf\xfd\xf7P!\xcd=\xce\xeco\xa4C9" +
"?pG\xbe\x91\x16\x1f7\xa6Y\xd6\xc9\xaeN\x88\x1f" +
"\xfd\xdc\xe7\xde(W\xcd\x9b\xfa\x10g\xa2\xbf\xfcJ\xfa" +
")\x9657:\xd9\\\xb0%\x9f\xe5\x00}\x88\xfa\x19" +
"T\x00\xb46e\x13\x00\xce\xd2Z\x95\x9f\x03`Dk" +
"U\x1e\x07\xc0\xd9Z\xab\xf2c\x00\x8cj\xad\xcay\x00" +
"\x9c\xa3\xb5*o\x00`\xa3\xd6\xa6\x0c\x02\xa0\xa6\xb5*" +
"/\x01`Lk\x13\x9f\xa8\xadP\xf6\x02\xa0\xa2\xb5)" +
"\xbb\x00p\xae\xd6*~_\xa5-U\xfe\x08\x80\xf3\xb4" +
"V\xe5W\x008_kS\xde\x05\xc0\x05\xda\x0a\xf1\xfb" +
"jm\x95\xf2C\x00\\\xa8\xad\x12\xe3.\xd2V\x09{" +
"\x8b\xb5\x0e\xf1\xfb\x1a\xadC\xf8\xa5\x16\x7f\x87\xb4\x0e\xe1" +
"O\\\xeb\x10v\xaf\xd5n\x15\xf6Z\xda\xbb\x94\xd5\x08" +
"\x805\xdaF\xe5y\x00\xac\xd56\x0a\x07\x9a\xb5\x1ee" +
"\x07\x006i]\xc2\xb1%\xda\xad\xe2\xc1\xafh]\xc2" +
"\xf0u\xc5\xcf\xa5\xed]\xca\x1c2\xd0\xa0mT\xee\x05" +
"\xc0\xafj\x1b\x95?\x03\xe0\xd7\xdaS\xca\x12\x12\\\xaf" +
"m\x15!hm\x1fP\x14\x02\x96\xb5\xef\x94_\xbe\xae" +
"1\xe5{\x00x\x83\xc6\xc4\xa3m\x1a\x13\xc1\x0b\x17\x7f" +
"\xd7iL9\x08\x80\xf5\xdaN\xf1y\xa36@>\x14" +
"Lw\xcc\xb1]f\x02@\xc1\xe7\xc1\x16\xe6e8\x06" +
"I\x16p\x8f\xd9\xf1\xaet\x9a\xdb\x84\x1bY\xceML" +
"Z#V\xb0yh(\xec\xf3`\x12\x9ap\x9dH\xe0" +
"\xb9B9\xc5\xb2\x89\\\x8d7\xca\x85<\xe1:$\x00" +
"\x9f\x07\xdb,\xdfr\x9dDn\x82H\x95\x0f%\xddL" +
"\x92CxT\x8e'4\x15\xa9*|\"\x97\xba\xa4\x8f" +
"\xd5\xb2\x94\xe5H\xf16\x80\x82\xc7i&\x06\x87\xce " +
"\xb0\x9c\x8c_\xf0\xd9(7x\x10@D\xfe\xe4A\x8f" +
"\xc3\x06m\xe8\x94\xc3O6\xb6\xd5\xe7B\xce\x8dHI" +
",\xa6\xa2L\x90e9G\xb3<{E\xcc\xbeJ\x1a" +
".>y\x9bk\x9bI\x85\xf9\x81\xc1\xb9#TI\x13" +
"\x83\xaa(\x0bt\x13W\xbd\xdd\x93\xc1\xaet\xb8\x18x" +
"\x81*\x12\xddb\x8d\xf0\xcdCC>\x0fd \xba\xf9" +
"\x10\xcb\xa1\x1d$\x99\xc3\xb7[a3\x18.\xbb\x8c\xa5" +
"\xb8\xc5E\xe0\x0a\x14\x18R\xc7\x9c\x1dPD\xac0\x05" +
"\x84\xd0~\x9evkFF\xb8crSH\x9c\x8cO" +
"ke\xd8\xeeX\xb7;\xe6\xacw\xbd^>.\x1dH" +
"Fh\xb2\x95\xb9o\xcdN\x90Z\xe1\xa2\x94\xe6n\xa8" +
"\xa59\x07\xdb\x87-\x9b'\x86\x99\x93\xb1\x9c\x8c\xc1;" +
"\xa5\xba\x18\xbd\x8f{>Z~\xc0\x9d\x80\x04r\xd9\xd2" +
"\xccIs\xbb\xdb\x85N\x99\x9b\xc5\xf4\xd8\xe4\x83\xea:" +
"\xc5\x1f\x86\x0b\x91\x9c\x97\xe6bQ\xc7\x03\xee)\x0e\xb3" +
"\xcba\x9e\x90\x8eB\x8c%q<9a\x0e2{\xfb" +
"<+\xeezV\x90/\xe3\xaa4CN\xf3~\xbe'" +
"gy\xdc\xa7\xdd\x90\xc5\xa0\xc0\xe830\xb2X\x1aM" +
".G\x9f\xc7}_\xd9\xc0\xfc-nWQc\xc2x" +
"]\xe6\xae\x9c\xafR\xf8\xe5jVkUb'@%" +
"\xa8L\x85V\xddUsAy\x88z1\xc4\xe6Q\xee" +
"y\x96\xc9+\x8a\xb4j\x09\xd71\xad\xc0r'\x07\xa3" +
"\xb4\x03\x8daf\xbac\x09\xe6\x19\x01\x0bPDO@" +
"8\x96rMno[)\xa7R\xc46d\xfd\xa4\x9b" +
"f\x112H\xf0&\xdfu\xfa\x90\x05\xc3\xebm\x97\x15" +
"g-\xb0Z\x16\x0co\xe1\xe3\xc2\x05\x01\xb0`x\x9d" +
"[=d\xd1\x14Y\xa2\xf5\x88x\x0e\xb3\xcb\x87E\xcd" +
"4\x87\x85\x110\xaf\xcf\xb2\xdd\xa0\xe8,\xbfqC\xd6" +
"\x97\x87\xc6Mj\x08 \x84t\xe0\xe2^\x00\xe3NT" +
"\xd1\x18F\x05\xa3\x881$\x9c\xe3&\x00\xc3$<K" +
"\xb8\xa2\xc4\xc4A3\x82\xfd\x00\x86M\xf88\xe1\xaa\x1a" +
"C\x15@\xcb\x09\xfd\x80\xf0\x03\x84\x87\xeab\x18\x02\xd0" +
"\xee\xc2\x95\x00\xc68\xe1\x87\x08\xaf\xa9\x8fa\x0d\xf5\xef" +
"\xb8\x03\xc08@\xf8Q\xc2k\x1bbX\x0b\xa0\xdd\x83" +
"?\x040\x8e\x12~\x8c\xf0pc\x8c\x0eh\xed!\xbc" +
"\x17\xc08F\xf8\x09\xc2\xeb\x9abX\x07\xa0=&\xf4" +
"O\x10~\x8a\xf0\xfa\xe6\x18\xd6\x03h'\xd1\x030\x9e" +
"!\xfc4\xe1\x0d\xe1\x186\x00h\xcf\xe1\xe3\x00\xc6i" +
"\xc2/\xa0\x82-\x8dJ\x0c\x1b\x01\xb4s\xb8\x1a\xc08" +
"C\xf8ET\x10\x9bb\xd8\x04\xa0\xfd\x12\x97\x01\x18g" +
"\x09~\x13\x15,\xf8.m\x99\x94\x0b\x9d\x8eKi\x85" +
"\xf5\xa0`=`\xc1f\x81\x15\xe4L\x0et\xbc\x82\x82" +
"\x8d\x84\xb9N\x86@@^\xc6\x98=E/\xee\x8bm" +
"\xd4\x00\x0a6\x00\x16\x069\xf3,'\xd3\x0d*\xcf\x94" +
"\xc1a\xd7\xb3\xf6\xbaN\x80\xcc\xeeJ\xa7s\x1eS\xd3" +
"\xf9\xb2p\x94{\x81\x95f6JQ:O\x07\xe0D" +
"k]X\x14uW[\xf5e\xa5LC\\\xc8\xcax" +
"\xce\xb1\xc6\xc5\xec\xfc\x80\x8ddS\x96\x1d\xb6-\x1fk" +
"@\xc1\x1a\xc0\xcea\xe6\xaf\xb7\xc6\x11AA\x04\x8c\x8c" +
"\xf6\xf6t\xe3,\xc0>\x15\x85\x81Y\x80_\x98\x96\xd4" +
"\xc3l\xce\x057\x1al$ksJ\xcc\x9c/\x92r" +
">%X\xb4k\x1d5\"\xd1\x8e\x95\xd4\x87DW\xd0" +
"\xa9\xafF\xdb\x08\x0cE\x97R/P\x13m\xd9\x05\xb0" +
"?\xe7\xecv\xdc1'\xee\xb8\x1b\xb2~\xc1q\x13\xee" +
"(\xf7\x18\xa8\x19\xbe\xdfqS,H\x0f\x17F\xe8/" +
"7{\xa1\xd3\x95%\xa4\x08$AV\xe1/\xdd\x99\x85" +
"\xcd\x15+g\xd8\xcau|\x99V\x8e\xf61\x9d\x1a\x9b" +
"\xd5\\ \"P\xda\x96\xd1\x87\xef\x05\xd0\x1fQQ\x7f" +
"\xaa\xb2'\xa3On\x02\xd0O\xa8\xa8\x9f\xaal\xc8\xe8" +
"\xc9e\x00\xfaS*\xea\x17*\xbb1z\x8e4\xcf\xaa" +
"\xa8_R\x10Cb'F_<\x08\xa0_TQ\x7f" +
"\x95\xb6!\x8am\x18}e\x10@\x7fYE\xfdM\x05" +
"\xcb\x9d\x0c\xf6yn\x86\xea$\xb5\x90\x95f\x1c\x10g" +
"S\xd2\x94\x8e8\xea\xc7@\xc1&\xc0H\x96\x05\xc3\xa5" +
"\x1c\x98]aC\x80\"\x1b\xb2\xae/**\x08{e" +
"bS\xb4g\xbbn\xb6\x9f\x05\x1c\xbbh\x013\xbc\x92" +
"\xbb%\x09\x84S\x96SFKAU\xff^n\xa9\xb9" +
"@\xaf\xc3\xeak\x84\xfa]U\x0c\xa9\xbe\xbfPJ=" +
"\x88P\xf2\x156d}\x83\xf65 \xd7\x8f\x94+\xe3" +
"\xd3\xea:\xaa,*U\x16\xb5\xaa2\x9eT\xa9R<" +
"E\xf8\xb3jUe\xfc\x99J\x15\xf0\x14\xe1gT\x05" +
"\xb1X\x18\x7f\xa1R\xa1;M\xf0\x05R\x0f\xa1,\x8c" +
"\xe7T*\xbcg\x09\xbfDx\x8d\"\x0b\xe3\x8b\xea\xf3" +
"\x00\xc6%\xc2_'\xbcV\x95\x85\xf151\xec\xcb\x84" +
"\xbfIx8$\x0b\xe3o\x84\xfd\xd7\x09\xff=\xe1u" +
"\xaa,\x8c\xef\xa8T\x18\x7fO\xf8\x9f\x08\xaf\x0f\xc9\xc2" +
"\xf8\xa1J\x85\xf1\x03\xc2?#\xbc\xa1F\x16\xc6O\xd5" +
"\xef\x01\x18\x9f\x11\x1e\x0a)\x18m\xac\x95\x85\x11C\xbf" +
"\x020\xeaB*\x1a1\xc2\x9b\xde\x96\x951\x1a\"\x7f" +
"\x9a\x08\x9fOx\xf3\xe2\x186\x03hsCT\xf0g" +
"\x13\xbe\x88\xf0Y\xef\xc4p\x16\x80\xb6 D~\xce'" +
"\xfc:\xc2#u1\x8c\x00h-\xa1\x97\x00\x8c\xeb\x09" +
"\xbf\x89\xf0\xd9\xf51\x9cM4,D\xf1YN\xf8-" +
"\x84G\x1bb\x18\x05\xd0:B4\xaf[\x08\xbf\x8d\xf0" +
"9\x91\x18\xce\x01\xd0zB\x83\x00F7\xe1}\x84k" +
"\x8d1\xd4\x00\xb4T\xe8\xe7\x00F\x1f\xe1w\x12\x1ek" +
"\x8aa\x0c@\x1b\x08\xd1\x81r'\xe1\xc3\x84\xcfm\x8e" +
"\xe1\\:\xf8B\x14\x1f\x93\xf0,\xe1W-\x8a\xe1U" +
"t\xf0\x09\xfd,\xe1\xfb\x08\x9f\xf7n\x0c\xe7\x01hy" +
"\xe1\xcf>\xc2\x8f\x10>\x7fq\x0c\xe7\x03h\x87C\xbb" +
"\x00\x8cC\x84\xdfO\xf8\x82\xba\x18.\x00\xd0\xee\x13\xf1" +
"9B\xf8\x83\x84_]\x13\xc3\xab\x01\xb4\x07\x84\x9f\x0f" +
"\x12\xfe(\xe1\x0b\xaf\x89\xe1B\x00\xed\xb8\xf0\xe7\x11\xc2" +
"\x9f\"|Q<\x86\x8b\x00\xb4'E<O\x10~\x8a" +
"\xf0\xc5\xd7\xc5p1\xa5g\x88\x0e\xe8g\x08?M\xf8" +
"5\x0d1\xbc\x86\x0e>\x11\x9fg\x09?Kx\xbc1" +
"\x86q\x00\xed\x051\xaf\xb3\x84_\"\xfc\xda\xa6\x18^" +
"Ky(\xf0K\x84\xbfNxKs\x0c[(\x0fC" +
"\x07\x01\x8cW\x09\x7f;\xa4\xe0\xfe1\x96\xefe#\xbc" +
"T\x0a:\xc7X\xbe\x9f\x0f\x95~\x16<\x97\x99$\xaf" +
"\xaa\x16\xf2\xd8IZ#\xa0ZAy[;\xc5&\x18" +
":e\xcb5E\x80\x12\xef\xb6:\xfd\x80\xda\xdb\x92B" +
"\xe70\xdb\xcb<\xb3l\x9d\xf4oc{\x19\xa8\xd3\x80" +
"\xe8\x99\xdd\x16=\xafV\x0c\x14\x989j\xf9\xae\x97\x87" +
"\xb81\xe1$\xa6\x87\xba\xccQ\x0bI(]\x98\"S" +
"J\xb2\xa2\xdd4V\x1cs\x1d\xbe\x9d\xe5K\x07e\xdc" +
"f\x0e\xf7\xb1\x16\x14\xac\x05,\x04\x96\xcd\x93D\x1bU" +
"n\x96T\xca\x91Q\xac\xc0\xc8e2\xdc\x0f\xb8)\x8c" +
"W\x9d\xe8~Q\x00\x9d\xe6Dw\xb9\x1fX#TH" +
"\xcd~\x97\x99\xdb-S\x0d\x86\xcbBZ\x07\xeab!" +
"\xcc\xc7\x03\x8cT\xee\x0f\x011\x02X0\xadbT\xd7" +
"{\xee\xc8v\x96O\xc4\xb9CM\x7f\xb9\xc7(\x12O" +
",1\xcf*\x8fF\x88/y\xa3|r\xfc\xc6X\xde" +
"\xe06O#\x15\x7fyc\x81\x91\xca5Pq\xe4\xd2" +
"\x9c\xd1\x92\x9d{P\x1d\x90a+3<\xc6\xf2\x09\x88" +
"\xd8\xcc\xf71R\xb9\x94\x93O\xc7\xc7X~\xa3Yj" +
"M\x0a\xe9b\xf3>\x89\xabT2O\x14\xfem\x1c\xe2" +
"\x1eM\x07\xc3\xa0`\xb8\x18\x1d\xeaD@\xcd\xd1 \xe5" +
"\x93\xa3\xe8b\xa6r@`\xa4r\x94T\xa4)\xd7q" +
"\xb7@\xb8\xba!L\xbb#\xd9\\\xc0M\x142\x8bv" +
"@Y&\xbb\xc8\x0d\x1c\x1d\xee1y2\x96eY\xcf" +
"5si\xee\xa1\xc1}\xf2\xb1\"\xf9\xc2^\xbfLU" +
"$\xbd\x15\xd1\xa6\x96\xa2N4\x09\xd1\xd5\xa2\xa9\xaa_" +
"\x07@\xbb'\xb0\xd2\xfb\xb3\xdcKs'\x98IO\xb3" +
"\xaa\xba\xa7\x99\xee\xf8\xa5\xbcKt\xbaN\xc0\xc7E?" +
"\xd3$\x06_,;\xba\xb9\xcbDG\xd7\xbc\x0e`\xff" +
"\x90\xc7\xf9\x18\xcbG\xd2V\x90/ur3\xf1d\xc5" +
"\x8c\xba\xb1\xb0\xb9b\xa67q\xdf\x9c\xe9\x00\xabf\xfa" +
"\xc0\x8a+\x05S\xdc\xf5\xa1Cq\x8c\x95\xfb\xc2\xbb\xa8" +
"\xdb\x1bWQ?T\xd5\x17\xde\xbd\x12@\xdf\xa7\xa2~" +
"DA,\xb6\x85\x87\x97\x00\xe8\x07T\xd4\x8fR[8" +
"[\xb6\x85\xf7\xd0\xd3\x87T\xd4\xef\xaf4\"\xd1\xfb\xa8" +
"W<\xaa\xa2~L\xc1H\x90\xcfR\x8a\x97\xdf\xed\x14" +
"w\xd9\x10\xb1\xd5\xd2\xbe\x0e\xfb\x81W\xee\xfc\x06]\xd7" +
".\xef\xd6]E\xe2Z]\xecg\x12\x93\xe53\x0db" +
"\xfb\x95\x1e\xb8\xadX?\xa8z\x88\xfdp\xb3\x88\xcec" +
"2%\x1f\xde$R\xf2\xa1]\x82d<\xb0R\x90\x8c" +
"{\xfa\x05\xc98L*\xb5\xd1\xbb\x06\x010\x1c\xcd\x13" +
"X\x17\xcdy\xb4S\xa3{\xe8\xb9\x86\xe8\x08=\xd7\x18" +
"\xb5\xe8\xa3)\xcaI\xb39\xca\xaa\xc8Ia\xc4\x0d\\" +
"o\x8c\xe5\x01\xa0\xf2=\x92\xb4\x9c\xdd\xf1\xc0\xcb9\xbb" +
"\x0b\xe2o\xd2r\x00w\xef\xcfz\xd6\x08\xf3\xf2\x85\xe2" +
"g\x12\xc2\x96\xb3\xbb\xe0s*j\xcc\x03\xccW\xbe\xc7" +
"\xf3d\xa3\x10\x10\xf1c\x9e0_\xfe.\xcc\x17rN" +
"\x9a\xe6lAd\xc8\xe2f\xc1\xe3\xbeer*\xff\x16" +
"\xb3\x0b\xb65j9\x19\xea\x7f=\xce\x83+\xd2\x14Z" +
"\xd2>\xd7r\x02yy0\xbb\x9c\x8d\x8cR\xe7\xdb*" +
"\xeavU6Z\xfd\x00\xfa\xb0\x8az@\xcdqH\xa6" +
"\xe3\x1e\x02\xb3*\xea\xfb(\x1dkd:\xe6\xf7V\x92" +
"\xf9K\x13\xe8t\xce\x1beA\xce\x83\xcaA[\x08\xe4" +
"\x05&\x87N\xdb\xa5\x922\x85+|\x89\x1d\xdf\xfee" +
"\x08[_\x89\xc9\x88\xd2Z\x8eC+\xc5\xe1z\x15\xf5" +
"\x9b\xaa\xe2\xb0\x82\xa6\xbc\\E\xfd\x96\x99\xcc\xee\x0a\x0e" +
"\x10q\x16tE\\\xdd\x89\xd5\x10\xf1m]&\x12\xba" +
"E&\xf4\xe2M\"\xa1\x17,\x01\x888\xae\xc3\x0b\x16" +
"\x9d\xe5\x0e\xb3)Q\xf8x\xf9{8\xcd\xbc/\x1cr" +
"\xc2\x15R$`\x01\xd7CX\xf5>7\x8aK\xc2\x1b" +
"\xb2\xbe~\xa0\xcc\x9a\xf6\xa8\xd4\xf6f\x89F\xec\xabf" +
"MyAk\xf6\x11~\xa4\x9a5\x1dV\xa9-=D" +
"\xf8\xfd\x84\xab(i\xd3}\xea\xe3\x00\xc6\xfd\x84?\"" +
"h\x93*i\xd3\xc3\xc2\xfe1\xc2O\x08\xda\x14\x92\xb4" +
"\xe91\x95\xda\xd2G\x09\x7fF\xd0\xa6\x1aI\x9b\x9e\x16" +
"v\x9e!\xfc\xb4\xa0M\xb5\x926=\xa7\x9e\x9f@\xcb" +
"\xea\xc2\x926\x9d\x13\xac\xef\x0c\xe1\x17\x05m\xaa\x93\xb4" +
"\xe9\x97\x82f] \xfceA\x9b\xea%m\xba,X" +
"_\x85\xae56H\xda\xf4\x9a\xf0\xf3U\xc2\xdf&\xbc" +
"\xa9Q\xd2\xa6\xdf\x09\x9a\xf56\xe1\x1f\x10\xde\xdc$i" +
"\xd3\x1fTj\xdb\xdf'\xfc\x13\xc2g5K\xda\xf4\xb1" +
"\xa0q\x9f\xa8*\xf6\x0b\xd64K\xb2\xa6\xcf\xc5\xb0\x7f" +
"#\xf5:\xc1\x9a\"\x925\xd5\x88.?D\xdd\xf9l" +
"\xc1\x9afK\xd6\xd4,XD\x85\xad\xcd\x89J\xd64" +
"7\xf4\xfc\x04V\xa6\xa9\x925\xb5\x84\xc8\xcd\xeb\x08_" +
".X\xd3\x1c\xc9\x9a\xda\x84\x9d\x1b\x08\xbfY\xb0&M" +
"\xb2\xa6UB\xfff\xc2\xbb\x05k\x8aI\xd6\xd4%X" +
"M\x85\x95\xcd\x9b+YSJ\xd8I\x12~\xbb`M" +
"WI\xd6\xb4U\xb0\x8b-\x84\x7f[\xb0\xa6y\x925" +
"\xed\x14l\xb0\xc2\xd6\xae^+Y\x13\x17\xe3\x0e\x13\x1e" +
"\x08\xd6\xd4%Y\xd3\x1ea\xa7\xc2\xd6\x16\xad\x93\xac)" +
"/\xf0q\xc2\x0f\x09\xd6\x94\x90\xac\xe9n\x81\x1f \xfc" +
"\xa8`M\xdd\x925\xdd#\xfc,\xb35\x8cK\xd2\xf4" +
"@h\x09\x80q\x94\xe0c!\x05\xc5}\xb6\xdd\xe7q" +
"\x88\xf8~U\xf7\xca\xec1\x96\xf77;X|\x1b\xd4" +
"\xd3)^qT\x14\x06=\xb6\x9b'\xad\x0c\x84\x87\x03" +
"\xbf\x8c\x9a\xcc\x1f\x1et\x99\x87f\xd5\xcdv\xb9\xa6\x99" +
"|\xda\xb1\xca\x0d\xbc\xb8\xe1\xe6&L\x95$]'C" +
"\xd2\xf0t\xcf)\xdb\xb8\x97/iT?\xbf\x9f\xa7\xdd" +
"\x0d\x9cy\xe5'\x86\\/\xcd\x13.\x03\xd5\x0f\xca\xa0" +
"\xe5\xf71o7\xd1\x82\xca\xc0Y\x96\xf3y\x92\x05\x10" +
"\xa1\xc9O\x82]\x07E\xf5\xb3\x1c\x86v\x15\x01r\xbd" +
"`\x03g\x80\x95\xf1\x02\x8f\x0d\x0dY\xe9\x14\xba\xa6|" +
"GTM\x102\xcc7\x02\x96\xde]=\xee\x88kr" +
"\x9a\x05L\x98\xa8,l}\x1e\xc4\xf9\x17.RW\xa7" +
"m\xbbc\xd5\x11\xaa,G\xe0f\x0d+\x83N\x99\xc5" +
"\xc9\x17-S\x8dJ<\xe9b)\x9e8Y\xb6\x8dc" +
")\xde\x93\x9e\xcdr\x93\xd9)6\x0e\xf1~\x9e\xe1\xce" +
"D\\\xc4\xad+\x1dX\xa3\xbc2\xe1\x80\xfb6K$" +
"z\xa0\xd3\xc9\xb0L\x95\xa9\xa2\xa0\x1b-\x9f\x0b\x11N" +
"\x91\xf5\xa2\x1bty#\xd5\x09#\xb2\xf96\x9ap\xc2" +
"\xcbY~\xd5@\"\xf7\xa6\x95ds\xb6\xcf\xbb\x1c\x13" +
"\xe2\x1bl\xcb\xe4%<\x9c\xc9\xfa8\xbbr`\x14\xef" +
"\xfcfp.\xdf<\xc3\xce\xfd\xff]I\x7f{\x91\x95" +
"\x0aR\x1a\xceg\xc5)*\xbb\x94U\xb2-l\xeb\x17" +
"\xa7h\xab<E\x97n\x12ma\xcb2\x80\xfd\xe9\x9c" +
"\xe7\x11i\xcaz\xdc\xb4\xd2\x01\x074\x0bY\xd7\xf7\xad" +
"A\x9b\x97\xceV\xc7\x14\xbb 2\xc4,\xfb\x8a\x17\xe1" +
"\xdd\xc5+\xd7\xf2\x8d\xeb\xa4k\xe0\xd5\x00\xfa\x83*\xea" +
"\x8fV5\x16\xc7\xfb\xab\xee\x86\x15\x94\xae?\xb9\xa3\xea" +
"nXUd\x83u\xf2 \x80\xfe\x8c\x8a\xfa\xe9\xca5" +
"\xf0s\xf4\xf4\xb3*\xea/W]\x03_&\xc5K*" +
"\xea\xef+\xd8\xc9DvMJW\x9b\xe6Z\xc9\x1d7" +
"`\xf6z\xcb\x06\x95\xfbX\x07\x0a\xd6A\xd5\xf517" +
"\xd7[6\xf7\xa1,\xb1\x8b\xef\xad\x00\xfd\xd2-qS" +
"\xf1MAI\x84\xdd<`\x96\xedC\xe5\x1a\xb9\xfc\x1f" +
"6\x8a\xd7\xc83!\x06\x13\xb2\xe0J\xc1/\xbdV\xeb" +
"\xee\x94>\x08\xeaZ^\x83\x1ej\xee\xbaU\xd4\xfb\x14" +
",-A\x8a\xa2\x9dTQ\xbf\xbdj\x09\xb6R\x10\xb7" +
"\xa8\xa8\x7f[\xa9\x9ar\x15\x15\x9aY\xd4\xfe/\x00\x00" +
"\xff\xff\x00\xd1\x99X"
const schema_b526ba661d550a59 = "x\xda\x94zkp\x1c\xd5\x95\xff9\xdd3\x1a==" +
"\x1a\xdf1\xd8\x92\xc5\xd8\xc6\xc2\xc8A\xc4F\xf8\x1f\xe4" +
"\xe0\x08Y2\xb6U#\xacV\xdb\x16v\xe1\xaa\\M" +
"_\xcd\xb4\xdd\xea\x1ew\xf7H\x1a\x17.\x1b\xc7T\x01" +
"\x7f\xa8@6\x0f'\x156<LU\x92\x85\x84\xcd\xc2" +
"\x96\x97\"\xb5\x84\"U\x86\xc5U\x90\x0a\xa9<\xc8\xe2" +
"\xe4\x0bI\x96\x14\x9b,\xa9\xcdfC\xf5\xd6\xb9=\x8f" +
"\xd6#\x18}\xf1\xa8\x7f\xe7\xf4\xb9\xe7\x9e{\xee\xb9\xe7" +
"w\xdb[N\xb6\xdc\x16\xdb\xda\x96o\x05E{8\xde" +
"\x10\xa4\x8e\x1d\xfa\xb5\xed\x7f\xe74\xa4:18\xd4|" +
"\xa0k\xea\x85\xeb\x9e\x87X\x02\xa0oM\xc3ad\xbd" +
"\x0d\x09P\x83\xef~\x90\xed?\xfc\x9f/\xdf3_+" +
".\xd5\x9a\x1aN \xebjH\x00\xb05\x0d\xef\x02\x06" +
"w\x0e\xbd\xfc\xf9\xc9\xeb\xbeq/\xa4:\x95\xba2`" +
"\x1f&F\x90\xadJ\xd0K\xa9\x84\x13\x03\x0cz_-" +
"\xdd\xf9\xed5\x0f=\x02Z/6\x05/\xdb\xff\xdar" +
"\xf0\x95\xbb\xfe\x0b\xe2H:\x0f\xb5<\x80\xec\xa9\x16\xfa" +
"\xf3\xf1\x96w\x14\xc0\xe0\xef?\xf1~\xc9<q\xf8\xcb" +
"\x90\xbaV\x09\xce\xfc\xe9O\xd7\xf6\xfd\xc7\x1f\xce\x93\xe5" +
"\x9f\xad8\x83\xec\xf7+\xc8\x87\xdf\xae\xf8\x0a`\xb0\xf2" +
"9\xcc\xe7/^\xfa\xfa\x12\xd3\xeaON\"\x1bM\xd2" +
"\xb4z\x7f\x91Q\x87\x13\x85\xc7\x97\xd0\xeaI\x1eF\xb6" +
"CjYj\xdfm\x7f\x9elx\x12\xb4N\\4\xfb" +
"5\xa4\xd7\x9b\x0c_\x99@\xc0\xba_Z'\xb6G\xd4" +
"\xe5\xbc\x1fm\xef@\xf6L\xfb&\x80\xbeW\xdb'\x1a" +
"\x01\x83\xe1\xa1\x97^{\xe6\x9d'\x9fZ\x14\xadC\x1d" +
"g\x90Mw\xd0\x9c\xcc\x8eO\x01\x06\x07\xcek?\xbb" +
"a\xe6\xd2SKx;\xddq\x18\xd9=\x1d\xe4\xedw" +
"~\x91\xfb\xd2\xff\xff\xe6\xfb\xff\xb0\xc8\xde\x91\x8e\xedu" +
"{\xfb\x00\x83#\xcf\xbf\xbb\xf5\xf3\x1f\xbe\xf3\x9d%\xec" +
"\x9d${\x8fH{\x03\xef\xfc\xee\xe4\xe8\xba\x89g\x97" +
"\x1cu\xb2:\xeaE\xcc\xae7s\xf6\x85%\xb4\x8e\x90" +
"\xad\xe3R\xab\xf0\xe2\xdf}Q\xff\xf4\xf0\xf7\x97\xd0\x1a" +
"%[\\j\x9dUo\xbe,n\xdd\xf8\xf2\x12Z;" +
"HK\x93Z\xef\xff\xd3g\xde\xbce\xc7\xd1\x8b\x0bW" +
"E!\xbd\xde\x8e\x95\xc8vt\xc8\xe5\xee\xc8\xd0\xaal" +
"?4Q\xb4\xde\xfa\xc6\xbf-5t'\x0d\xddIF" +
"\xdf8\xf3X\xfe\x7f\x7f\xfe\xe5KK\x0dMZ\x9a\xd4" +
"\xeaz%\xf7\xf6\xdb\xfe\x997\x17\x85\xb8\xb7s'\xb2" +
"\x1d\x9dr\xd8N\x99\x0c\xfd\xfd/>\xfe\xfa\x03\xff\xfd" +
"\x13\xf2R]\x98bkG\x90\xedXK\x0b\xd2\xbf\x96" +
"6\xce\xb6\xec\xf0\xe7\xceM|\xf9\xe7K\x8c\xde\xd5u" +
"\x18\xd9\xd6.\x1a\xbd\xffs\xaf}\xed+\xf1\xaf\xfc;" +
"\x99T\x16\xa8\xb5u\xedD\xd6\xd5%\xf7b\xd7,`" +
"p\xfd\x9b?y\xff\x86]\xcf\xfdq\xd1\x8e9\xd9u" +
"\x14\xd9#R\xf3\xa1\xae<`}\xf3\xcd7\x1bn\xc5" +
"\x7f!\xedK]W\x03\xf4\xbd\xd5\x95H\x00\x06k\x1e" +
"}to\xd3{W}\xb0\x84\xaf\xbf\xec>\x8c\xec\x0f" +
"\xdd\xe4\xeb\x93\xb1\xe2\x87\x9f>\xfb\xd0\xff,\xa1u\x89" +
"\xb4~%\xb56\xff\xa6\xfb\x8ew\xef\xfa\xcc_\x16\xc5" +
"\xf3\xfb\xdd\x93\xc8\xde\xec&'/u\x9f\x02\x0c\x9e\xf0" +
"\xc6.\xbev\xe4\xc9\xbf,tR.z\xdbug\x90" +
"\xad\xbfN\x86\xeb:\x19\xfd\xd4?\xcf\xde\xf7\xfb\xc1\xc9" +
"\xbf.1\xfc\xa3\x9b&\x91\xfd\xe3&\x1a\xfe\xd4\xb9g" +
"\xdf\xd5\xcf\xdd\x17,\xcc\xa4\xb0\x08mz\x01\xd9S\x9b" +
"\xc8\x85\xc77}\x17z\x83\x9cp\x05\xb7>\x99\x8b\x95" +
"<\xdf\x99\xfedN\xfe\xdc\x98\xe3E\xbb\xb8}H>" +
"\x8c\x0bO\xb83B5n\x1eC\xfcH}\xdd\xe7\xee" +
"\x98i9\xfe\x10w\x87\x06\x1c\xdbw\x1dk\x0cQk" +
"Wc\x001\x04H\xf1\xc3\x00\xdagU\xd4,\x05S" +
"\x88i$\xd0\xfc\x02\x80f\xa9\xa8\xcd)\x98R\x944" +
"*\x00\xa9\x12i\xfa*j\xa7\x15L\xa9j\x1aU\x80" +
"\xd4\xc9\x11\x00\xedn\x15\xb5\xfb\x14\x0c\x0a%c\x88\xc6" +
"\x00\xd5\xb1p%(\xb8\x120\xf0|!\\\xd3\xcec" +
"\xd6\x9c6\xfd\xbd\xf6\x14:5\x99\xcdg\xcc<\xf7M" +
"P\x1d\xbb\x06\x16\x0b\x8e-v\x17=\x00\xc0\xf6z\x01" +
"\x07\xc4v\xf8\xe8\xd9\x8e\xf2\xa2\xb1\xd7.\x96\xfc\xfd\xe5" +
"\xa2\x00\xa0\x89\xbeH\xae\xb3^e\x04\x00W\xb0\x1e\xe5" +
"{\x00\x98d=\xca\x13d\x9c\xf5(\xdf\x06\xc0\x14\xeb" +
"Q^\x06\xc0\x95\xacG\xf99\x00\xb6\xb0^e\x12\x00" +
"\x19\xebQ^\x03\xc04\xeb\x95\xbf\xc8\xb6*'\x00P" +
"a\xbd\xcaQ\x00\\\xc5z\xe4\xf3U\xac[\xf9\x1d\x00" +
"^\xcdz\x94\x1f\x01\xe0j\xd6\xab\xfc\x1a\x00\xd7\xb0\xad" +
"\xf2\xb9\x83mS\xbe\x06\x80\x9dl\x9b\x1cw-\xdb&" +
"\xedu\xb1~\xf9|\x0d\xeb\x97~\xa9\x95\xe7\x18\xeb\x97" +
"\xfedX\xbf\xb4\xbb\x8e\xed\x90\xf6\xd6\xf7\x0d*\xdb\x11" +
"\x00\xe3l\xaf\xf2\x02\x006\xb0\xbd\xd2\x816\xb6K9" +
"\x0c\x80\xadlP:\xb6\x81\xed\x90/^\xcb\x06\xa5\xe1" +
"\x8d\x95\xdf\xee\xbeAe%\x19hf{\x95\x07\x00\xf0" +
":\xb6W\xf9#\x00n\xea\x1bU6\x90\xe0zv@" +
"\x86\xa0\xa7\xef\x90\xa2\x10\xb0\xb9\xefH\xf8\xc7'\x18W" +
"\xbe\x00\x8070._\xede\\\x06/Qynd" +
"\\9\x03\x80M\xec\x88\xfc\xbd\x91\x1d\"\x1f\x02\xc3\x99" +
"\xb5-\x87\x1b\x00\x10x\xc2\xdf\xcf\xdd\xbc@?\xcb}" +
"\xe1r+3\x98\xcb\x09\x8bp\xbd(\x84\x11\xa6\xc8\xbe" +
"\xa9\xa9\x84'\xfc\x05\xe8\x90c'){\x09\x1e\xe5\xc5" +
"\xa1R\xdc\x9d\x11R>\x14\xa65x\xc2?hz\xa6" +
"c\x0f\x95\xe6\x89\xd4\xf0\xa5\xac\x93\xcf\x0aH\xcc\x84\xe3" +
"IM%T\x95>\x91K\x83\xa1\x8fQ\xd9\xa8i\x87" +
"\xe2\x83\x00\x81+h&\xba\x80\x01\xdf7\xed\xbc\x17x" +
"|F\xe8\xc2\xf7!\x19>\x0a\x7f\x97\xcd'-\x18\x08" +
"\x87_h\xec\x80'\xa4\\\xe8\xc9\xaaXNE\x99'" +
"+\x0a\x81Fm\xf6\x8a\x9c}D\x9a\xa8\xbc\xb9\xc7\xb1" +
"\x8c\xac\xc2=_\x17\xc2\x96\xaa\xa4\x89~$\xca\x12\x1d" +
"\x11\xaa{l!8\x98KT\x02/Q%D\xf7\x9b" +
"\xd3b\xdf\xd4\x94'\xfc0\x10\xc3b\x8a\x97\xd0\xf2\xb3" +
"\xdc\x16\x13f\xc2\xf0\x0b5\x97\xb1\x1a\xb7\x8c\x0c\\@" +
"\x81!u,Y>E\xc4LP@\x08\x1d\x179'" +
">=-lC\x18Rb\xe7=Z+\xddrf\x87" +
"\x9dY\xfbv\xc7\xbdC\xcc\x85\x0ed\x934\xd9\xfa\xdc" +
"\x0f\x14\xe7I\xcdDEJs\xd7\xd5\xea\x9c\xfd\x89\x82" +
"i\x89\xa1\x02\xb7\xf3\xa6\x9d\xd7\xc5@\xa8.G\x1f\x13" +
"\xae\x87\xa6\xe7\x0b\xdb'A\xb8l9n\xe7\x845\xec" +
"\xc0@\x98\x9b\x95\xf4\x18\xf1\xa8\x0eU\x1et\x07\x92%" +
"7'\xe4\xa2\xce\xf9\xc2Uln\xd5\xc2</\x1d\xa5" +
"\x18\xab\xe2Lv\xde\x1c\xc2\xec\x1ds\xcd\x8c\xe3\x9a~" +
"\xb9\x86\xab\xa1\x19rZ\x8c\x8b\xe3%\xd3\x15\x1e\xed\x86" +
"\"\xfa\x01\xa7__/bu\xb4p9\xc6\\\xe1y" +
"\xcan\xee\xedw\x06+\x1a\xf3\xc6\x1b4\x8e\x96<\x95" +
"\xc2\x1f\xaefT\xab\x1e;\x09*~}*\xb4\xea\x8e" +
"Z\xf2kC4\xc9!\xf6\xcd\x08\xd75\x0dQW\xa4" +
"U\x1brl\xc3\xf4Mga0\xaa;P/p\xc3" +
"\x99\x1d\xe2\xae\xees\x1fe\xf4$\x84\xb3\xa3\x8e!\xac" +
"\x837\x85S\xa9`\xbb\x8b^\xd6\xc9\xf1$\x19$x" +
"\xc4s\xec1\xe4~\xe1v\xcb\xe1\x95YK\xac\x81\xfb" +
"\x85\xfdbN\xba \x01\xee\x17v:\xd1!+\xa6\xc8" +
"\x12\xadG\xd2\xb5\xb9U;,\xe2W8\x1a\xc9Yq" +
"\xa3<s\xe8\xd0\xb8\xb9z:\xb2#x\x02@\xbf\x0b" +
"U\xd4\x0bX? \x99\xc0\x11\x00\xdd \xbc\x88\xf53" +
"\x92M\xe38\x80n\x11>\x87\xf5c\x92\x95\xa4\xbeO" +
"\xf8i\xc2c\x8di\x8c\x01\xb0\x93x\x13\x80>G\xf8" +
"Y\xc2\xe3Mi\x8c\x03\xb0{\xf00\x80~\x9a\xf0\x07" +
"\x09ohNc\x03\x00\xbb\x1f\xbf\x06\xa0?H\xf89" +
"\xc2\x13-ij\"\xd8\x97\xf0\x01\x00\xfd\x1c\xe1\xe7\x09" +
"olMc#u\x14R\xff<\xe1\xcf\x12\xde\xd4\x96" +
"\xc6&\x00\xf6\x0c\xba\x00\xfa\xd3\x84_ \xbc9\x91\xc6" +
"f\x00\xf6<>\x01\xa0_ \xfc\x15Tp}\x8b\x92" +
"\xc6\x16\x00\xf6\x03\xdc\x0e\xa0\xbfH\xf8ET\x10[\xd3" +
"\xd8\x0a\xc0~\x88\x9b\x01\xf4\x97\x08~\x1b\x15\x0c<\x87" +
"\xb6\xcc\xa8\x03\x03\xb6Ci\x85M\xa0`\x13``q" +
"\xdf\xf4K\x86\xa03\xbd\x05\x14l!\xcc\xb1\xf3\x04\x02" +
"\x8a\x1a\xc6\xadEz\x19On\xa3fP\xb0\x190\x98" +
"\x14\x9c\xba\x89aPE\xbe\x06\x16\x1c\xd7<\xe1\xd8>" +
"rk0\x97+\xb9\\\xcd\x95k\xc2\x19\xe1\xfaf\x8e" +
"[\x18\x8are:\x00\xe7[\x1b\xc4\x8ah8j\xd5" +
"\x0b+e\x0e2RV\xc3K\xb69'g\xe7\xf9|" +
"\xba8jZ\x09\xcb\xf40\x0e\x0a\xc6\x01\x07\x0a\xdc\xbb" +
"\xdd\x9cC\x04\x05\x1109s\xc7\xaea\\\x018\xa6" +
"\xa24\xb0\"\xd2\xc3\xc4\xffF\x0f\xb3\xaf\xe4\xdf\xa8\xf3" +
"\xe9\xa2%(1K\x9eL\xca\xd5\xb2\x09\x1b\xdcI\x8d" +
"H\xaa\xff&\xeaCR[\xe9\xd4WS\xbd\x04\xc6R" +
"\xdd\xd4\x0b\xc4S\xeb\x8f\x02\x9c*\xd9\xc7lg\xd6\xce" +
"\xd8\xce\xee\xa2\x17\xd8\xce\x903#\\\x0ej^\x9c\xb2" +
"\x9dQ\xee\xe7\x0a\xc14\xfd+\x8c;`\xc0\x09KH" +
"\x05\xc8BX\x85?v\x1f\x9a0\xb6\xdet\xa5Ft" +
"A\xe3\xda\x7f%}\x0a\x03\xedc:5\xf6\xa9%_" +
"F\xa0\xd6\xb4~\xf5\x01\x00\xed\xeb*j\xdf\x8c4\xad" +
"OQ+z^E\xed\xd9H\xd3\xfa\xccf\x00\xed\x9b" +
"*j\xafD\x9a\xd6\x1f\x90\xe6K*j\xaf+\x881" +
"\xb9\x13S\xaf\x9e\x01\xd0.\xaa\xa8\xfd\x98\xb6!\xcam" +
"\x98zs\x12@{CE\xedm\x05k\x9d\x0c\x8e\xb9" +
"N\x9e\xea\xa4\xecOk\x84\xa1\xd2\x9fz\xd5#\x8e\xfa" +
"1P\xb0\x150Y\xe4~\xa1\x9a\x03\xedu\xc6\x06(" +
"\xb3\xa1\xe8x\xb2\xa2\x86\xfdn\x8d|U\xecY\x8eS" +
"\x1c\xe7\xbe\xc0AZ\xc0\xbc\xa8\xe7nU\x02\x89Q\xd3" +
"\xae\xa1\xd5\xa0\xaa\x7f+\xb7\xd4\x92\xaf5b\xf4\xaa\xa3" +
"\xe9h\x84\xc55\x8d\x07\xd5\xd4\x83$%_\xb0\xbb\xe8" +
"\xe9\xb4\xaf\x01\x85v\xaeV\x19\xd7\xc4v\x02\xe8\xe9\x98" +
"\x8a\xfa\xbaX\xa42v\xc5\xa8R\xac&|c,R" +
"\x19\xd7\xc7\xa8\x02\xae#\xfc\x86\x98\x82X)\x8c=1" +
"*t\xd7\x13|3\xa9\xc70,\x8c[cTx\xb7" +
"\x10~+\xe1q%,\x8c\xfd\xb1\x17\x00\xf4[\x09\xdf" +
"Cx\x83\x1a\x16\xc6]r\xd8\xdb\x08\xcf\x12\x9e\x88\x85" +
"\x85q\xaf\xb4\xbf\x87\xf0\xfd\x847\xaaaa\xd4bT" +
"\x18\xf7\x13\xfeY\xc2\x9bbaa<\x12\xa3\xc2x\x17" +
"\xe1\x05\xc2\x9b\xe3aa\x14\xb1/\x00\xe8\x05\xc2}\xc2" +
"[\x1a\xc2\xc2x<\xf6#*\xe0\x84\x9f%\xbc\xf5r" +
"X\x19\xef\x91\xfe\xdcM\xf8}\x84\xb7u\xa5\xb1\x0d\x80" +
"\xdd\x1b\xa3\x82\x7f\x9a\xf0\x07\x09_\xf1\xab4\xae\xa0\xc2" +
".\xfd\xbc\x8f\xf0/\x12\x9elLc\x12\x80=\x12{" +
"\x8d\x0a;\xe1\xe7\x09ooJc;\x15v\x19\x9f\xc7" +
"\x08\x7f\x9a\xf0Ts\x1aS\x00\xec[r^O\x13~" +
"\x81\xf0\x95\xc94\xae\xa4\xc2\x1e\x9b\x04\xd0\x9f#\xfc%" +
"\xc2YK\x1a\x19\x00\xfb~\xec{T\xc1\x09\x7f\x9d\xf0" +
"tk\x1a\xd3\x00\xec\xd5\x18\x1d(\xaf\x13\xfeS\xc2W" +
"\xb5\xa5q\x15\x00{K\xc6\xe7\xc7\x84_&\xfc\xaa\xb5" +
"i\xbc\x0a\x80\xfdR\xea_&\xfc=\xc2\xaf\xfeu\x1a" +
"\xaf\x06`\xbf\x95\xfe\xbcG\xf8\x9f\x09_\xdd\x95\xc6\xd5" +
"\x00\xecO\xb1\xa3\x00\xfa\x071\x15\xc7\xe3\x0a\xa6\xd64" +
"\xa6q\x0d\x00\xfbP\x86\xe7\xcf\xa4\x1e#\xbc#\x9e\xc6" +
"\x0e\x00\x86qr3\x16WQo'\xbc\xf3\x9a4v" +
"\x02\xb0\xb68\xb9\xd3J\xf8j\xc2\xd7f\xd2\xb8\x16\x80" +
"\xad\x8aS8\xd3\x84\xaf#\xbckc\x1a\xbb(;\xe3" +
"t>\xaf%\xfcz\xc2\xafiN\xe35\x00\xac;N" +
"\xe1\xd9H\xf8\x16\xc23-i\xcc\x10q\x8c\xd3\xb4\xb6" +
"\x10~+\xe1\xebZ\xd3\xb8\x8e\xd2P\xe2\xb7\x12\xbe\x87" +
"\xf0\xf5mi\\Oi\x18?\x03\xa0\x0f\x13>F\xf8" +
"\x06\x96\xc6\x0d\x00l4N\xe93F\xf8]\x84_\xbb" +
"6\x8d\xd7\x02\xb0CR\xffN\xc2\x0d\xc276\xa4q" +
"#\x00\xe3R\xdf \xbcHxw2\x8d\xdd\xd4_\xc4" +
")l\x16\xe1sq\x05O\xcd\xf2\xf2\x1d|ZT+" +
"\xcd\xc0,/\x8f\x8b\xa9\xeac\xe0:\xdc y\xa4\x18" +
"\x85\xa7Z\xd6\x9c\x06\xd5\xf4kU\xc3\xae\xf4\xd80\x10" +
"vt\x8b\x04\x18\xe2\xc3\xe6\x80\xe7S\xf7\\U\x18(" +
"\xf0\x13\xdc5j\xd6I\x7f\x0f?\xc1A]\x02D\xd7" +
"\x186\xe9}\xb5n \xe0\xc6\x8c\xe99n\x192\xfa" +
"\xbc\x83\x9e^\x1a4fL$a\xe8\xc2\"\x99R\x95" +
"U\xec\xe6\xb0\xee\x98c\x8b\x09^\xae\x9e\xc3\x19\x8b\xdb" +
"\xc2\xc3\x06P\xb0\x010\xf0MKd\x89\x95\xaa\xc2\xa8" +
"\xaa\xd4\"\xa3\x98\xbe^\xca\xe7\x85\xe7\x0bC\x1a\x8f4" +
"\x0c^E\x00\x03\xc6|w\x85\xe7\x9b\xd3T\xa7\x8dq" +
"\x87\x1b\x13\xa6\xa1\xfa\x85\x9a\x90\xd6\x81\x9adH\x889" +
"\x1f\x93\xf5+T@L\x02\x06\x86Y\x89\xea\xed\xae3" +
"=\xc1\xcbC\x19a\x13\xa7\xa8\xb50\x15^\x8bUb" +
"\x1b\xf1h\x9a\xe8\x98;#\x16\xc6o\x96\x97ua\x89" +
"\x1c\xd2\xd9\x12^\x88`\xb2~\x13V\x19\xb9:g4" +
"Cb\xe0G\x03R0\xf3\x85Y^\x1e\x82\xa4\xc5=" +
"\x0f\x93\xf5{\xc9\xf0\xed\xcc,/\xef5\xaa\x9dO\x90" +
"\xabp\x83\x05T\xa8\x9ey\xf2\\9( \xe3\xd2t" +
"0\x01\x0a&*\xd1\xa1F\x07\xd4\x12\x0dR;\x98*" +
".\xe6\xeb\xe7\x0f&\xeb'U]:\xea\xd8\xce~H" +
"D\xfb\xcd\x9c3],\xf9\xc2@)3i\x07\xd4d" +
"a\x93\xba[\xa0-\\\x1e\x1e\xbc5Y\xd1u\x8cR" +
"N\xb8\xa8\x0b\x8f|\x8cJ\xc4\x8c)fu\xac3\xed" +
"Z\xa8+\xb2a\xac,#,\x94\xecD\xa7d\x1b\xdc" +
"-\xef\xc5\xfa\xae\xa8\xc8& 9?\x8a\x1f\xc5Yj" +
"\x94+\xa4\xe9rY\xa95j\x94\xcdNj\xbbl\x0e" +
"\x9bv\x02\xd06\xf5\xcd\xdc\xa9\xa2ps\xc2\xf6\x97\xd3" +
"\x9bm\x8b\xf6fK\xb5\x11\x94\xe0\xf22Q\xcc\xc9\xbe" +
"\xacU\x0e\xde\x15v\xa6\xab6\xcb\xce\xb4m'\xc0\xa9" +
")W\x88Y^N\xe6L\xbf\\\xedH\x97\xe3\xc9\xd6" +
"eu\x95\x09c\xeb\xcd\xcblC?\xb5\xdc\x01\xb6-" +
"\xf7\x85\xadW\x0a\xa6\xbc\xb3D\x9b\xe2\x98\xae\xf5\xb7'" +
"\xa9k\x9dSQ;\x1b\xe9o\xef\xb9\xa9~\xd5\x8a\x95" +
"\xf6\xf6\xde\x0d\x00\xdai\x15\xb5\x07\xa9\xbdm\x0f\xdb\xdb" +
"\xfb\xe9\xed\xb3*j\x0f\xd7\x1b\xaa\xd4C\xd4\xf3>\xa8" +
"\xa2vN\xc1\xa4_.\xd2^\xaa}G\xabl\xe7)" +
"b\xdd\xd5\xdcMx\xbe[\xeb`'\x1d\xc7\xaa\x95\x85" +
"\xa3\x15\x02\x1e=U\x96\x13\x93-\xcb\x0db\xdf\x95^" +
"\xd8S)TT\xa6\xe4~\xb8EF\xe7\xf10%\xbf" +
":\"S\xf2KG%Yz\xe4&I\x96\xee\x1f\x97" +
"d\xe9^RiH\x9d\x9c\x04\xc0D\xaaL`c\xaa" +
"\xe4RIH\x1d\xa7\xf7\x9aS\xd3\xf4^K\xca\xa4\x9f" +
"\xd6\x94 \xcd\xb6\x14\x8f\x90\xac`\xda\xf1\x1dw\x96\x97" +
"\x01\xa0\xfew2k\xda\xc72\xbe[\xb2\x8f\x05\xf2\xdf" +
"\xaci\x03\x1e;Ut\xcdi\xee\x96\x83\xcao\x16\x12" +
"\xa6},\xf0\x04UO\xee\x02\x96\xeb\x7fg\xcad#" +
"\xf0\x89\xc0rW\x9a\xaf\xfd-\xcd\x07%;Gs6" +
"!9e\x0a#p\x85g\x1a\x82\xce\x19\x93[\x81e" +
"\xce\x98v\x9e\xfaxW\x08\xff\x8at\x8b\x96t\xcc1" +
"m?\xbc\x04\x89|\"\x18Y\xea\x13\xc18\x80VP" +
"Q\xf3\xa9\xc9\x8f\x85\xe9x\x9c\xc0\xa2\x8a\xda\xdd\x94\x8e" +
"\xf10\x1d\xcb'\xea\xc9\xfc\xb1/\x02r%w\x86\xfb" +
"%\x17\xea'z\xe0\x87\x17\xb1\x02\x06,\x87J\xca\"" +
"\xce\xf31v|\xdf\xc7!\x9ecUF&Kk-" +
"\x0e=\x14\x87\xebU\xd4n\x8e\xc4a+My\x8b\x8a" +
"\xda\xad\xcb\x99\xdd\x15\x1c\xd8W\xf2o\x94\xb4K^A" +
"\xca\xd5\x90\xf1\xed\xd9,\x13z}\x98\xd0]#2\xa1" +
"\xd7l\x00H\xda\x8e-\x02\x93\x9a\x06\x9b[\x94(b" +
"\xae\xf6w\"\xc7\xdd\x8f\xfd\x95HO\xfa\xdc\x17Z\x0c" +
"#\xdf\xceS\xb8!\xb1\xbb\xe8i\xa7k\xec\xef\xb8J" +
"}hQUQ\xbf[\x8d\xb0\xbf\xb2J\xf4\xecn\xc2" +
"\xefS#\xec\xef^\x95\xfa\xeb\xb3\x84?L\xb8\x8a!" +
"\xfd{H}\x02@\x7f\x98\xf0\xaf\x13\x1eSC\xfa\xf7" +
"Ui\xff\x1c\xe1\xe7\x09\x8f\xc7B\xfa\xf7\xb8J\xfd\xf2" +
"c\x84?MxC<\xa4\x7f\xdf\x92v\x9e&\xfc\x02" +
"\xe1\x89\x86\x90\xfe=\xaf\xbe\x0c\xa0_ \xfc\x15\xc2\x1b" +
"\x13!\xfd\xfb\x81J\xec\xf5E\xc2/\x12\xde\xd4\x18\xd2" +
"\xbf\x1f\xaa\xc4\x1b^!\xfc\x0d\xc2\x9b\x9bB\xfawI" +
"%\xf6\xfa:\xe1?%\xbc\xa59\xa4\x7foI?\x7f" +
"L\xf8e\xc2[[B\xfa\xf7K\x95\xfa\xf7\xcb\x84\xbf" +
"Gx[kH\xff~\xab\x12\xff\xf8\x0d\xe1\x1f\x10\xbe" +
"\xa2-\xa4\x7f\x7fP\x89&}\xa0\xaa8.\xd9\xdf\x8a" +
"\x90\xfd}(\x87\xfd+\xa97J\xf6\x97\x0c\xd9_\\" +
"\xb2\xb9\x18\xd1\xa4v\xc9\xfe\xdaC\xf6\xd7&\xd9Y+" +
"\xe1\xab%\xfbK\x85\xeco\x95d\xcduR\xce\xd4\x90" +
"\xfd\xad\x97\xacv#\xe1[$\xfb[\x19\xb2\xbf^i" +
"\xe7\x06\xc2o\x91\xec\x8f\x85\xeco\x9b\xd4\xbf\x85\xf0a" +
"\xc9\xfe\xd2!\xfb\x1b\x94,r\x98\xf01\xc9\xfeV\x85" +
"\xecoT\xda\xc9\x12~\xa7d\x7fW\x85\xec\xef@\xec" +
"\xcc<\xf6\xbd\xe6\xea\x90\xfe\x1d\x91\xac\xb6\xce\xbe;n" +
"\x0b\xe9\xdfB\xf6\xdd9\x18\xd2\xbf\xe3\xd2N\x91\xf0\xbb" +
"\x09_\xbb3\xa4\x7fe\x89\xd7Yy\xd7PH\xff\xee" +
"\x91x\x9d}_3\x1c\xd2\xbf\xfb\xa5\x9f5\xf6\x8d\x99" +
"\x90\xfd=\x12\xdb\x00\xa0?H\xf0\xb9\x98\x82\xf2^\xde" +
"\x1as\x05$=/\xd2&sk\x96\x97\xbd}6V" +
"\xbej\xed\x1a\x90\x9fj\xea\x0a\x93.?&\xb2f\x1e" +
"\x12\x05\xdf\xab\xa1\x06\xf7\x0a\x93\x0ew\xd1\x88\xdc\xd0\xd7" +
"j\x9a!\x96\x1c\xab\xc6\x14\xe4M\xbd0`\xb1$\xeb" +
"\xd8y\x92&\x96zO9(\xdcrU#\xfa\xfe)" +
"\x91sv\x0b\xee\xd6\xde\x98r\xdc\x9c\x18r8\xa8\x9e" +
"_\x03Mo\x8c\xbb\xc7\x88\x7f\xd4\x07.\xf2\x92'\xb2" +
"\xdc\x87$M~\x01\xec\xd8(\xab\x9fis\xb4\"L" +
"\xcbq\xfd\xdd\x82\x03\xd6\xc7\xf3]>5e\xe6F\xd1" +
"1\xc2o]Q&\x92\xe7\x9e\xee\xf3\xdc\xb1\xe8\xb8\xd3" +
"\x8e!h\x160o\xa2aa\x1bs!#>r\x91" +
"\x06\x07,\xcb\x99\x8dF\xa8\xbe\x1c\xbeS\xd4\xcd<\xda" +
"5\xba\x18~0Zl4\xc4\xb3\x0eV\xe3\x89\x0be" +
"\x07\x05V\xe3\xbd\xe0\xdd\xa20\xb85\xca\xe7 3." +
"\xf2\xc2\x9e\x8f\xcb\xb8\x0d\xe6|sF\xd4'\xec\x0b\xcf" +
"\xe2CC\xbb`\xc0\xce\xf3|\xc4TE@\xfcCH" +
"\x11.\x92\xdd\x81\x8e?\xe8NG\x13Ff\xf3\x1e\x9a" +
"\xf0\x90[2\xbd\xc8@2\xf7\x96\x94\x14K\x96'\x06" +
"m\x032\xbb-\xd3\x10U<\x91/z\xcb\xfcV\xbf" +
"\xe0\\\xbee\x99\x9d\xfb\xff\xbb\x92\xfeD\x85\xfeJ\xf6" +
"\x9b(\x17\xe5)\x1av)\xdb\xc2\xb6\xb0w\\\x9e\xa2" +
"=\xe1)\xda=\"\xdb\xc2\xf5\x9b\x01N\xe5J\xaeK" +
"\xa4\xa9\xe8\x0a\xc3\xcc\xf9\x02\xd0\x08\x8a\x8e\xe7\x99\x93\x96" +
"\xa8\x9e\xad\xb6!wAr\x8a\x9b\xd6\x15/\xf4\x87+" +
"W\xc7\xb5\x9b\xe3\x05\xd7\xd9\xdb\x01\xb4/\xaa\xa8=\x16" +
"i,\x1e\x1d\x8f\xdcq+\x18\xba\xfe\xd4\xe1\xc8\x1d\xb7" +
"\xaa\x84\x0d\xd63g\x00\xb4\xa7U\xd4.\xd4\xaf\xb3\x9f" +
"\xa7\xb7\x9fSQ{#r\x9d}\x89\x14_WQ\xfb" +
"\x8d\x82\x03\\f\xd7\x82t\xb5h\xae\xf5\xdcq|n" +
"\xddnZ\xa0\x0a\x0f\x1bA\xc1F\x88\\\x83\x0b\xe3v" +
"\xd3\x12\x1e\xd4$V\xe5\xfb\x1b\xa0W\xbd\xedn\xad|" +
"\xf1\xa8\x8apX\xf8\xdc\xb4<\xa8_\x87\xd7\xfesL" +
"\xe5:|9\xc4`^\x16\\)\xf8\xd5\xcf\x83\xc3\x03" +
"\xa1\x0f\x92\xba\xd6\xd6`\x175w\xc3*jc\x0aV" +
"\x97`\x94\xa2\x9dUQ\xbb3\xb2\x04\x07(\x88\xfbU" +
"\xd4>\xabD\xa6\x1c\xa1B\xcb\x8b\xda\xff\x05\x00\x00\xff" +
"\xff1<\xb37"
func RegisterSchema(reg *schemas.Registry) {
reg.Register(&schemas.Schema{
+113 -13
View File
@@ -1,8 +1,11 @@
package cereal
import (
"encoding/json"
"log/slog"
"math"
"os"
"regexp"
"capnproto.org/go/capnp/v3"
"pfeifer.dev/mapd/cereal/custom"
@@ -15,6 +18,8 @@ const (
externalFixMaxAge = 500_000_000 // 10 Hz source: 500 ms
internalFixMaxAge = 2_000_000_000 // 1 Hz source: 2 s
carFixMaxAge = 2_500_000_000 // Optional CAN observation: 2.5 s
phoneFixMaxAge = 10_000_000_000 // Companion app in the background sends sparsely: 10 s
gpsModeRefresh = 1_000_000_000 // Galaxy navigation setting reread interval
maxHorizontalAccuracy = 50.0 // input sanity ceiling, not road-match qualification
maxClockPairSkew = 1_000_000 // an interrupted pair cannot date a source event
maxClockOffsetDrift = 1_000_000 // a larger BOOTTIME/MONOTONIC step fences queued fixes
@@ -39,8 +44,53 @@ const (
GpsSourceInternal
GpsSourceExternal
GpsSourceCar
GpsSourcePhone
)
// Galaxy navigation's GPS source setting: backup uses the phone only without
// comma or car GPS, comma never uses it, phone prefers it whenever it has a fix.
const (
GpsModeBackup = "backup"
GpsModeComma = "comma"
GpsModePhone = "phone"
)
var storagePrefix = regexp.MustCompile(`^[A-Za-z0-9_-]{1,64}$`)
func navigationSettingsPath() string {
prefix := os.Getenv("OPENPILOT_PREFIX")
if prefix == "" {
return "/data/starpilot/navigation/settings.json"
}
if !storagePrefix.MatchString(prefix) {
return ""
}
return "/data/starpilot-" + prefix + "/navigation/settings.json"
}
// ReadGpsMode returns the saved choice, or backup when it is missing or invalid.
func ReadGpsMode() string {
path := navigationSettingsPath()
if path == "" {
return GpsModeBackup
}
raw, err := os.ReadFile(path)
if err != nil || len(raw) > 1<<20 {
return GpsModeBackup
}
var settings struct {
GpsSource string `json:"gpsSource"`
}
if json.Unmarshal(raw, &settings) != nil {
return GpsModeBackup
}
switch settings.GpsSource {
case GpsModeComma, GpsModePhone:
return settings.GpsSource
}
return GpsModeBackup
}
type GpsSample struct {
Location log.GpsLocationData
Source GpsSource
@@ -60,7 +110,12 @@ type GpsSub struct {
gpsLocation Subscriber[log.GpsLocationData]
gpsLocationExternal Subscriber[log.GpsLocationData]
carGps *Subscriber[custom.StarPilotCarState_Gps]
phoneGps *Subscriber[custom.StarPilotCarState_Gps]
car gpsCandidate
phone gpsCandidate
mode func() string
modeValue string
modeReadAt uint64
internal gpsCandidate
external gpsCandidate
selected GpsSource
@@ -83,6 +138,8 @@ func GpsFixFreshAt(source GpsSource, fixMonoTime, now uint64) bool {
return now-fixMonoTime <= externalFixMaxAge
case GpsSourceCar:
return now-fixMonoTime <= carFixMaxAge
case GpsSourcePhone:
return now-fixMonoTime <= phoneFixMaxAge
case GpsSourceInternal:
return now-fixMonoTime <= internalFixMaxAge
default:
@@ -134,6 +191,35 @@ func (s *GpsSub) clearCandidates() {
s.internal = gpsCandidate{}
s.external = gpsCandidate{}
s.car = gpsCandidate{}
s.phone = gpsCandidate{}
}
func (s *GpsSub) gpsMode(now uint64) string {
if s.modeValue == "" || now < s.modeReadAt || now-s.modeReadAt >= gpsModeRefresh {
read := s.mode
if read == nil {
read = ReadGpsMode
}
s.modeValue, s.modeReadAt = read(), now
}
return s.modeValue
}
// updateWrapped admits a StarPilotCarState.Gps-shaped fix dated by its own source time.
func (c *gpsCandidate) updateWrapped(event DecodedEvent[custom.StarPilotCarState_Gps], sensor log.GpsLocationData_SensorSource,
clock clockSample, barrierMono, maxAge uint64) bool {
sourceTime := event.Value.SourceMonoTime()
if sourceTime == 0 || sourceTime > event.LogMonoTime || event.LogMonoTime > clock.monoAfter {
return false
}
location, err := carGpsLocation(event.Value)
if err != nil {
return false
}
location.SetSource(sensor)
valid := event.Valid && !math.IsNaN(float64(location.Speed())) && !math.IsInf(float64(location.Speed()), 0) && location.Speed() >= 0 &&
!math.IsNaN(location.Altitude()) && !math.IsInf(location.Altitude(), 0)
return c.updateWithAge(DecodedEvent[log.GpsLocationData]{Value: location, Valid: valid, LogMonoTime: sourceTime}, clock, barrierMono, maxAge)
}
func (s *GpsSub) clockReady(clock clockSample) bool {
@@ -166,7 +252,7 @@ func (s *GpsSub) ReadSample() (sample GpsSample, success bool) {
clock = s.clock()
}
ready := s.clockReady(clock)
internalNew, externalNew, carNew := false, false, false
internalNew, externalNew, carNew, phoneNew := false, false, false, false
if event, ok := s.gpsLocation.ReadEvent(); ok && ready {
internalNew = s.internal.update(event, clock, s.barrierMono)
}
@@ -176,23 +262,29 @@ func (s *GpsSub) ReadSample() (sample GpsSample, success bool) {
if s.carGps != nil {
if event, ok := s.carGps.ReadEvent(); ok && ready {
sourceTime := event.Value.SourceMonoTime()
if sourceTime > 0 && sourceTime <= event.LogMonoTime && event.LogMonoTime <= clock.monoAfter {
location, err := carGpsLocation(event.Value)
if err == nil {
carNew = s.car.updateWithAge(DecodedEvent[log.GpsLocationData]{Value: location, Valid: event.Valid && !math.IsNaN(float64(location.Speed())) && !math.IsInf(float64(location.Speed()), 0) && location.Speed() >= 0 && !math.IsNaN(location.Altitude()) && !math.IsInf(location.Altitude(), 0), LogMonoTime: sourceTime}, clock, s.barrierMono, carFixMaxAge)
}
}
carNew = s.car.updateWrapped(event, log.GpsLocationData_SensorSource_car, clock, s.barrierMono, carFixMaxAge)
}
}
if s.phoneGps != nil {
if event, ok := s.phoneGps.ReadEvent(); ok && ready {
// The enum has no phone value; android is the closest.
phoneNew = s.phone.updateWrapped(event, log.GpsLocationData_SensorSource_android, clock, s.barrierMono, phoneFixMaxAge)
}
}
mode := s.gpsMode(clock.boot)
phoneFresh := ready && mode != GpsModeComma && GpsFixFreshAt(GpsSourcePhone, s.phone.fixMonoTime, clock.boot)
source := GpsSourceNone
if ready && GpsFixFreshAt(GpsSourceExternal, s.external.fixMonoTime, clock.boot) {
if phoneFresh && mode == GpsModePhone {
source = GpsSourcePhone
} else if ready && GpsFixFreshAt(GpsSourceExternal, s.external.fixMonoTime, clock.boot) {
source = GpsSourceExternal
} else if ready && GpsFixFreshAt(GpsSourceInternal, s.internal.fixMonoTime, clock.boot) {
source = GpsSourceInternal
} else if ready && GpsFixFreshAt(GpsSourceCar, s.car.fixMonoTime, clock.boot) {
source = GpsSourceCar
} else if phoneFresh {
source = GpsSourcePhone
}
sample.SourceChanged = source != s.selected
if sample.SourceChanged {
@@ -215,6 +307,10 @@ func (s *GpsSub) ReadSample() (sample GpsSample, success bool) {
sample.Location = s.car.location
sample.FixMonoTime = s.car.fixMonoTime
sample.NewFix = carNew
case GpsSourcePhone:
sample.Location = s.phone.location
sample.FixMonoTime = s.phone.fixMonoTime
sample.NewFix = phoneNew
default:
return sample, false
}
@@ -234,24 +330,28 @@ func (s *GpsSub) Close() {
if s.carGps != nil {
s.carGps.Sub.Msgq.Close()
}
if s.phoneGps != nil {
s.phoneGps.Sub.Msgq.Close()
}
}
func GetGpsSub() (gpsSub GpsSub) {
return GpsSub{
carGps: optionalCarGpsSubscriber(),
carGps: optionalWrappedGpsSubscriber("starpilotCarState", CarGpsReader),
phoneGps: optionalWrappedGpsSubscriber("starpilotNavigation", PhoneGpsReader),
gpsLocation: NewSubscriber("gpsLocation", GpsLocationReader, true, ms.Settings.SubscriberSettings.ShadowGpsLocation),
gpsLocationExternal: NewSubscriber("gpsLocationExternal", GpsLocationExternalReader, true, ms.Settings.SubscriberSettings.ShadowGpsLocationExternal),
}
}
func optionalCarGpsSubscriber() (subscriber *Subscriber[custom.StarPilotCarState_Gps]) {
func optionalWrappedGpsSubscriber(service string, reader func(log.Event) (custom.StarPilotCarState_Gps, error)) (subscriber *Subscriber[custom.StarPilotCarState_Gps]) {
defer func() {
if recover() != nil {
subscriber = nil
slog.Warn("Optional car GPS subscriber unavailable")
slog.Warn("Optional GPS subscriber unavailable", "service", service)
}
}()
value := NewSubscriber("starpilotCarState", CarGpsReader, true, false)
value := NewSubscriber(service, reader, true, false)
return &value
}
+1 -1
View File
@@ -2623,7 +2623,7 @@ struct Event {
# DO change the name of the field and struct
# DON'T change the ID (e.g. @107)
# DON'T change which struct it points to
customReserved0 @107 :Custom.CustomReserved0;
starpilotNavigation @107 :Custom.StarPilotCarControl;
customReserved1 @108 :Custom.CustomReserved1;
starpilotCarState @109 :Custom.StarPilotCarState;
customReserved3 @110 :Custom.CustomReserved3;
File diff suppressed because it is too large Load Diff
+125
View File
@@ -0,0 +1,125 @@
package cereal
import (
"capnproto.org/go/capnp/v3"
"pfeifer.dev/mapd/cereal/custom"
"pfeifer.dev/mapd/cereal/log"
"testing"
)
func phoneGpsWire(t *testing.T, source, envelope uint64) []byte {
t.Helper()
message, segment, err := capnp.NewMessage(capnp.SingleSegment(nil))
if err != nil {
t.Fatal(err)
}
event, err := log.NewRootEvent(segment)
if err != nil {
t.Fatal(err)
}
event.SetLogMonoTime(envelope)
event.SetValid(true)
navigation, err := event.NewStarpilotNavigation()
if err != nil {
t.Fatal(err)
}
gps, err := navigation.NewPhoneGps()
if err != nil {
t.Fatal(err)
}
gps.SetSourceMonoTime(source)
gps.SetHasFix(true)
gps.SetLatitude(41)
gps.SetLongitude(-111)
gps.SetAltitude(100)
gps.SetSpeed(12)
gps.SetBearingDeg(90)
gps.SetHorizontalAccuracy(5)
gps.SetVerticalAccuracy(5)
gps.SetBearingAccuracyDeg(10)
gps.SetSpeedAccuracy(1)
raw, err := message.Marshal()
if err != nil {
t.Fatal(err)
}
return raw
}
func attachPhone(s *GpsSub, mode *string) *[][]byte {
queue := [][]byte{}
s.phoneGps = &Subscriber[custom.StarPilotCarState_Gps]{reader: PhoneGpsReader, maxBytes: 250 * 1024, readBytes: func() []byte {
if len(queue) == 0 {
return nil
}
value := queue[0]
queue = queue[1:]
return value
}}
s.mode = func() string { return *mode }
return &queue
}
func TestPhoneGpsFollowsTheGalaxySetting(t *testing.T) {
now := uint64(10_000_000_000)
mode := GpsModeBackup
s, internal, _ := testGpsSub(&now)
car := attachCar(s)
phone := attachPhone(s, &mode)
*phone = append(*phone, phoneGpsWire(t, now-300_000_000, now))
sample, ok := s.ReadSample()
if !ok || sample.Source != GpsSourcePhone || sample.FixMonoTime != now-300_000_000 || sample.Location.Longitude() != -111 {
t.Fatalf("backup without comma or car GPS: %+v %v", sample, ok)
}
*car = append(*car, carGpsWire(t, now-1, now, true, true))
if sample, ok = s.ReadSample(); !ok || sample.Source != GpsSourceCar {
t.Fatal("car ranks above the phone in backup")
}
*internal = append(*internal, gpsWire(t, GpsSourceInternal, now, true, true, 1, 2, 5))
if sample, ok = s.ReadSample(); !ok || sample.Source != GpsSourceInternal {
t.Fatal("comma ranks above the phone in backup")
}
mode = GpsModePhone
now += gpsModeRefresh
*phone = append(*phone, phoneGpsWire(t, now-100_000_000, now))
*internal = append(*internal, gpsWire(t, GpsSourceInternal, now, true, true, 1, 2, 5))
if sample, ok = s.ReadSample(); !ok || sample.Source != GpsSourcePhone {
t.Fatal("prefer phone")
}
now += phoneFixMaxAge
*internal = append(*internal, gpsWire(t, GpsSourceInternal, now, true, true, 1, 2, 5))
if sample, ok = s.ReadSample(); !ok || sample.Source != GpsSourceInternal {
t.Fatal("prefer phone falls back to comma once the phone fix expires")
}
mode = GpsModeComma
now += gpsModeRefresh
*phone = append(*phone, phoneGpsWire(t, now, now))
if sample, ok = s.ReadSample(); !ok || sample.Source != GpsSourceInternal {
t.Fatal("comma and car only keeps comma GPS")
}
now += internalFixMaxAge + 1
*phone = append(*phone, phoneGpsWire(t, now, now))
if _, ok = s.ReadSample(); ok {
t.Fatal("comma and car only must never use the phone")
}
}
func TestPhoneGpsModeIsRereadAtMostOnceASecond(t *testing.T) {
now := uint64(10_000_000_000)
reads := 0
s, _, _ := testGpsSub(&now)
s.mode = func() string { reads++; return GpsModeBackup }
s.ReadSample()
now += gpsModeRefresh - 1
s.ReadSample()
if reads != 1 {
t.Fatalf("mode reread early: %d", reads)
}
now++
s.ReadSample()
if reads != 2 {
t.Fatalf("mode not reread: %d", reads)
}
}
+8
View File
@@ -45,3 +45,11 @@ func CarGpsReader(evt log.Event) (custom.StarPilotCarState_Gps, error) {
}
return state.Gps()
}
func PhoneGpsReader(evt log.Event) (custom.StarPilotCarState_Gps, error) {
navigation, err := evt.StarpilotNavigation()
if err != nil {
return custom.StarPilotCarState_Gps{}, err
}
return navigation.PhoneGps()
}
+1
View File
@@ -0,0 +1 @@
{"description":"Synthetic Go-produced v2 current-road and data-only preview fixture; no real road data.","event_base64":"AAAAACQAAAAAAAAAAgABAADyBSoBAAAAjwAAAAAAAAAAAAAAEAAHAAAAoEEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIABQABAAAAAAAAAAIAAAAAAHBBgAELJwEAAAAA8gUqAQAAAAEAAAAAAAAAKQAAAAAAAAAAAHBCAAAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAoCAABhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhAAAAAAAAAAA="}
+52 -2
View File
@@ -1,7 +1,6 @@
package cereal
import (
"bytes"
"capnproto.org/go/capnp/v3"
"encoding/base64"
"encoding/json"
@@ -61,7 +60,58 @@ func TestMapdV1HostWireFixture(t *testing.T) {
if err != nil {
t.Fatal(err)
}
if !bytes.Equal(raw, expected) {
actualMessage, err := capnp.Unmarshal(raw)
if err != nil {
t.Fatal(err)
}
oldMessage, err := capnp.Unmarshal(expected)
if err != nil {
t.Fatal(err)
}
actualRoot, err := actualMessage.Root()
if err != nil {
t.Fatal(err)
}
oldRoot, err := oldMessage.Root()
if err != nil {
t.Fatal(err)
}
// Appended default fields can enlarge storage without changing old values.
equal, err := capnp.Equal(actualRoot, oldRoot)
if err != nil || !equal {
t.Fatal("provider wire differs from shared host fixture; review both schemas")
}
}
func TestPreviewHostWireFixture(t *testing.T) {
data, err := os.ReadFile("testdata/mapd_preview_wire.json")
if err != nil {
t.Fatal(err)
}
var fixture struct {
EventBase64 string `json:"event_base64"`
}
if err := json.Unmarshal(data, &fixture); err != nil {
t.Fatal(err)
}
raw, err := base64.StdEncoding.DecodeString(fixture.EventBase64)
if err != nil {
t.Fatal(err)
}
msg, err := capnp.Unmarshal(raw)
if err != nil {
t.Fatal(err)
}
event, err := log.ReadRootEvent(msg)
if err != nil {
t.Fatal(err)
}
out, err := event.MapdOut()
if err != nil {
t.Fatal(err)
}
boundary, err := out.PreviewBoundaryId()
if err != nil || !event.Valid() || out.SampleVersion() != 2 || out.SpeedLimit() != 20 || out.PreviewSpeedLimit() != 15 || out.PreviewDistance() != 60 || len(boundary) != 64 || out.PreviewWayId() != 2 {
t.Fatal("provider preview differs from shared host fixture")
}
}
+82
View File
@@ -0,0 +1,82 @@
package main
import (
"crypto/sha256"
"fmt"
"math"
"pfeifer.dev/mapd/cereal/custom"
"pfeifer.dev/mapd/maps"
)
const previewHorizonM = 500
const previewMaxWays = 64
type LimitPreview struct {
Speed float32
Distance float32
BoundaryID string
WayID int64
}
// A preview follows only a unique legal continuation, never a guessed turn.
func (s *State) previewLimit() LimitPreview {
if !s.ShadowOnly || s.SnapshotID == "" || s.CurrentWay.SelectionType != custom.WaySelectionType_current || s.MatchedLimitMps <= 0 {
return LimitPreview{}
}
way, forward, position := s.CurrentWay.Way, s.CurrentWay.OnWay.IsForward, s.Position
distance := float32(0)
seen := map[int64]bool{way.Id(): true}
for range previewMaxWays {
_, end := way.GetStartEnd(forward)
if distance+position.DistanceTo(end) > previewHorizonM {
return LimitPreview{}
}
segment, found, err := way.DistanceToNode(position, forward, end)
if err != nil || !found || segment < 0 || math.IsNaN(float64(segment)) || math.IsInf(float64(segment), 0) {
return LimitPreview{}
}
distance += segment
box := s.Data.OverlapBox()
if distance <= 0 || distance > previewHorizonM || !box.PosInside(end) {
return LimitPreview{}
}
matches, err := way.MatchingWays(&s.Data, end)
if err != nil {
return LimitPreview{}
}
legal := map[int64]maps.Way{}
for _, next := range matches {
first, last := next.GetStartEnd(true)
if first.Equals(last) && !next.OneWay() {
return LimitPreview{} // A two-way loop has two possible entry directions.
}
if next.Id() > 0 && (next.IsForwardFrom(end) || !next.OneWay()) {
legal[next.Id()] = next
}
}
if len(legal) != 1 {
return LimitPreview{}
}
var next maps.Way
for _, candidate := range legal {
next = candidate
}
if seen[next.Id()] {
return LimitPreview{}
}
nextForward := next.IsForwardFrom(end)
road := CurrentWay{Way: next, OnWay: maps.OnWayResult{IsForward: nextForward}}
speed := road.EffectiveMaxSpeed()
if speed <= 0 || math.IsNaN(speed) || math.IsInf(speed, 0) || speed > math.MaxFloat32 {
return LimitPreview{}
}
if math.Abs(speed-float64(s.MatchedLimitMps)) > .02 {
key := fmt.Sprintf("%s:%d:%d:%t:%.7f:%.7f", s.SnapshotID, way.Id(), next.Id(), nextForward, end.Lat(), end.Lon())
return LimitPreview{float32(speed), distance, fmt.Sprintf("%x", sha256.Sum256([]byte(key))), next.Id()}
}
seen[next.Id()] = true
way, forward, position = next, nextForward, end
}
return LimitPreview{}
}
+200
View File
@@ -0,0 +1,200 @@
package main
import (
"crypto/sha256"
"fmt"
"math"
"os"
"path/filepath"
"strings"
"testing"
"time"
"capnproto.org/go/capnp/v3"
"pfeifer.dev/mapd/cereal"
"pfeifer.dev/mapd/cereal/custom"
"pfeifer.dev/mapd/cereal/offline"
"pfeifer.dev/mapd/maps"
m "pfeifer.dev/mapd/math"
)
func previewState(t *testing.T, specs []syntheticWay, index int, forward bool, latitude float64) State {
t.Helper()
tile := packedRoadTile(t, specs)
return State{ShadowOnly: true, SnapshotID: "verified-snapshot", Data: tile,
Position: m.NewPosition(latitude, -97.9), MatchedLimitMps: float32(specs[index].speed),
CurrentWay: CurrentWay{Way: tile.Ways.At(index), OnWay: maps.OnWayResult{IsForward: forward}, SelectionType: custom.WaySelectionType_current}}
}
func previewRoads() []syntheticWay {
return []syntheticWay{
{1, 20, 35.1, -97.9, 35.101, -97.9, false, 2},
{2, 20, 35.101, -97.9, 35.102, -97.9, false, 2},
{3, 10, 35.102, -97.9, 35.103, -97.9, false, 2},
}
}
func TestPreviewRawDistanceAndStableBoundaryAcrossApproachWays(t *testing.T) {
s := previewState(t, previewRoads(), 0, true, 35.1005)
a := s.previewLimit()
s = previewState(t, previewRoads(), 1, true, 35.1015)
b := s.previewLimit()
if a.Speed != 10 || a.WayID != 3 || a.BoundaryID == "" || a.BoundaryID != b.BoundaryID || math.Abs(float64(a.Distance-b.Distance)-111.2) > 1 {
t.Fatalf("unexpected road preview: %+v %+v", a, b)
}
s.Position = m.NewPosition(35.1014, -97.9)
if s.previewLimit().Distance <= b.Distance {
t.Fatal("raw distance was held at a previous minimum")
}
s.SnapshotID = "changed-snapshot"
if s.previewLimit().BoundaryID == b.BoundaryID {
t.Fatal("boundary identity survived a different snapshot")
}
}
func TestPreviewDirectionUnknownLimitsBranchesAndHorizon(t *testing.T) {
s := previewState(t, previewRoads(), 2, false, 35.1025)
if p := s.previewLimit(); p.Speed != 20 || p.WayID != 2 || p.Distance <= 0 {
t.Fatalf("reverse traversal failed: %+v", p)
}
for _, mode := range []string{"branch", "unknown", "oneway", "horizon", "unqualified", "unverified"} {
t.Run(mode, func(t *testing.T) {
roads := previewRoads()
index, forward, latitude := 0, true, 35.1005
switch mode {
case "branch":
roads = append(roads, syntheticWay{4, 30, 35.101, -97.9, 35.102, -97.899, false, 2})
case "unknown":
roads[1].speed = 0
case "oneway":
roads[1].oneWay = true
index, forward, latitude = 2, false, 35.1025
case "horizon":
roads[0].lat1 = 35.09
latitude = 35.091
}
s := previewState(t, roads, index, forward, latitude)
if mode == "unqualified" {
s.CurrentWay.SelectionType = custom.WaySelectionType_possible
}
if mode == "unverified" {
s.SnapshotID = ""
}
if p := s.previewLimit(); p != (LimitPreview{}) {
t.Fatalf("uncertain preview escaped: %+v", p)
}
})
}
}
func TestPreviewClosedWayRequiresOneLegalDirection(t *testing.T) {
for _, tc := range []struct {
name string
oneWay, branch, wantPreview bool
}{
{"one-way loop", true, false, true},
{"two-way loop", false, false, false},
{"two-way loop plus continuation", false, true, false},
} {
t.Run(tc.name, func(t *testing.T) {
roads := []syntheticWay{
{1, 20, 35.1, -97.9, 35.101, -97.9, true, 2},
{2, 10, 35.101, -97.9, 35.102, -97.9, tc.oneWay, 2},
}
if tc.branch {
roads = append(roads, syntheticWay{3, 15, 35.101, -97.9, 35.102, -97.899, false, 2})
}
decoded, err := capnp.UnmarshalPacked(packedRoadTileBytes(t, roads))
if err != nil {
t.Fatal(err)
}
root, err := decoded.Root()
if err != nil {
t.Fatal(err)
}
msg, _, err := capnp.NewMessage(capnp.SingleSegment(nil))
if err != nil {
t.Fatal(err)
}
if err := msg.SetRoot(root); err != nil {
t.Fatal(err)
}
raw, err := offline.ReadRootOffline(msg)
if err != nil {
t.Fatal(err)
}
ways, err := raw.Ways()
if err != nil {
t.Fatal(err)
}
loop := ways.At(1)
loop.SetMaxLon(-97.898)
nodes, err := loop.NewNodes(4)
if err != nil {
t.Fatal(err)
}
for i, p := range [][2]float64{{35.101, -97.9}, {35.102, -97.9}, {35.102, -97.899}, {35.101, -97.9}} {
nodes.At(i).SetLatitude(p[0])
nodes.At(i).SetLongitude(p[1])
}
data, err := msg.MarshalPacked()
if err != nil {
t.Fatal(err)
}
tile := maps.ReadOffline(data)
if !tile.Loaded {
t.Fatal("closed road tile rejected")
}
s := State{ShadowOnly: true, SnapshotID: "verified-snapshot", Data: tile,
Position: m.NewPosition(35.1005, -97.9), MatchedLimitMps: 20,
CurrentWay: CurrentWay{Way: tile.Ways.At(0), OnWay: maps.OnWayResult{IsForward: true}, SelectionType: custom.WaySelectionType_current}}
p := s.previewLimit()
if tc.wantPreview {
if p.Speed != 10 || p.WayID != 2 || p.Distance <= 0 || p.BoundaryID == "" {
t.Fatalf("legal loop preview missing: %+v", p)
}
} else if p != (LimitPreview{}) {
t.Fatalf("ambiguous loop produced a preview: %+v", p)
}
})
}
}
func TestShadowPreviewSharesFixFreshnessWithoutControlOutputs(t *testing.T) {
roads := []syntheticWay{{1, 20, 35.149, -97.9, 35.151, -97.9, false, 2}, {2, 10, 35.151, -97.9, 35.153, -97.9, false, 2}}
data := packedRoadTileBytes(t, roads)
id := strings.Repeat("a", 64)
dir := filepath.Join(t.TempDir(), id)
area := maps.Area{Box: m.Box{MinPos: m.NewPosition(35, -98), MaxPos: m.NewPosition(35.25, -97.75)}}
path := maps.GenerateBoundsFileName(area, maps.OfflineSettings{OutputDirectory: dir})
if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil {
t.Fatal(err)
}
if err := os.WriteFile(path, data, 0644); err != nil {
t.Fatal(err)
}
rel, err := filepath.Rel(dir, path)
if err != nil {
t.Fatal(err)
}
load := maps.SnapshotLoader(dir, maps.SnapshotReceipt{Tiles: []maps.SnapshotTile{{Path: rel, Size: int64(len(data)), SHA256: fmt.Sprintf("%x", sha256.Sum256(data))}}})
s := State{ShadowOnly: true, SnapshotID: id}
s.Init()
boot := uint64(10_100_000_000)
s.bootNow = func() uint64 { return boot }
sample := syntheticSample(t, cereal.GpsSourceExternal, 10_000_000_000, 1, true, true)
s.ProcessGps(sample, true, load, time.Unix(0, 0))
sample.SourceChanged, sample.FixMonoTime = false, 10_050_000_000
s.ProcessGps(sample, true, load, time.Unix(0, 1))
event, output := decodedOutput(t, &s)
boundary, _ := output.PreviewBoundaryId()
if !event.Valid() || output.SpeedLimit() != 20 || output.PreviewSpeedLimit() != 10 || boundary == "" || output.PreviewDistance() <= 0 || output.NextSpeedLimit() != 0 || output.SuggestedSpeed() != 0 {
t.Fatalf("shadow preview: valid=%v status=%v way=%v limit=%v preview=%+v", event.Valid(), output.RoadStatus(), output.WaySelectionType(), output.SpeedLimit(), s.LimitPreview)
}
boot = 10_600_000_000
event, output = decodedOutput(t, &s)
boundary, _ = output.PreviewBoundaryId()
if event.Valid() || output.PreviewSpeedLimit() != 0 || boundary != "" {
t.Fatal("expired GPS retained an upcoming limit")
}
}
+12 -1
View File
@@ -11,6 +11,7 @@ import (
"os"
"reflect"
"runtime"
"slices"
"strconv"
"strings"
"syscall"
@@ -162,7 +163,6 @@ func GenerateOffline(s OfflineSettings) error {
MaxSpeedBackward: ParseMaxSpeed(tags["maxspeed:backward"]),
MaxSpeedAdvisory: ParseMaxSpeed(tags["maxspeed:advisory"]),
Lanes: uint8(lanes),
OneWay: tags["oneway"] == "yes",
Id: int64(way.ID),
HighwayClass: HighwayClassFromTag(tags["highway"]),
@@ -191,6 +191,17 @@ func GenerateOffline(s OfflineSettings) error {
tmpWay.Nodes[i].Latitude = n.Lat
tmpWay.Nodes[i].Longitude = n.Lon
}
switch tags["oneway"] {
case "yes", "1":
tmpWay.OneWay = true
case "-1":
tmpWay.OneWay = true
slices.Reverse(tmpWay.Nodes)
tmpWay.MaxSpeedForward, tmpWay.MaxSpeedBackward = tmpWay.MaxSpeedBackward, tmpWay.MaxSpeedForward
tmpWay.MaxSpeedForwardConditional, tmpWay.MaxSpeedBackwardConditional = tmpWay.MaxSpeedBackwardConditional, tmpWay.MaxSpeedForwardConditional
case "":
tmpWay.OneWay = tags["junction"] == "roundabout" || tags["highway"] == "motorway"
}
tmpWay.Box.MinPos = m.NewPosition(minLat, minLon)
tmpWay.Box.MaxPos = m.NewPosition(maxLat, maxLon)
scannedWays = append(scannedWays, tmpWay)
+78
View File
@@ -0,0 +1,78 @@
package maps
import (
"math"
"os"
"path/filepath"
"testing"
m "pfeifer.dev/mapd/math"
)
func TestGeneratorPreservesFixedOneWayDirectionAndSpeedTags(t *testing.T) {
for _, tc := range []struct {
name, oneWay, highway, junction string
wantOneWay, reverse bool
}{
{"ordinary", "", "residential", "", false, false},
{"yes", "yes", "residential", "", true, false},
{"numeric", "1", "residential", "", true, false},
{"reverse", "-1", "residential", "", true, true},
{"roundabout", "", "residential", "roundabout", true, false},
{"motorway", "", "motorway", "", true, false},
{"roundabout explicit no", "no", "residential", "roundabout", false, false},
{"roundabout explicit zero", "0", "residential", "roundabout", false, false},
{"motorway explicit no", "no", "motorway", "", false, false},
{"motorway explicit zero", "0", "motorway", "", false, false},
{"motorway link", "", "motorway_link", "", false, false},
{"reversible", "reversible", "residential", "roundabout", false, false},
{"alternating", "alternating", "motorway", "", false, false},
} {
t.Run(tc.name, func(t *testing.T) {
forwardRule, backwardRule := "15 @ (Mo-Fr 08:00-09:00)", "25 @ (Sa 10:00-11:00)"
tags := []string{"highway", tc.highway, "maxspeed", "30 mph",
"maxspeed:forward", "10", "maxspeed:backward", "20",
"maxspeed:forward:conditional", forwardRule, "maxspeed:backward:conditional", backwardRule}
if tc.oneWay != "" {
tags = append(tags, "oneway", tc.oneWay)
}
if tc.junction != "" {
tags = append(tags, "junction", tc.junction)
}
root := t.TempDir()
pbf := filepath.Join(root, "road.osm.pbf")
if err := os.WriteFile(pbf, syntheticPBF(tags...), 0600); err != nil {
t.Fatal(err)
}
output := filepath.Join(root, "tiles")
if err := GenerateOffline(OfflineSettings{InputFile: pbf, OutputDirectory: output, Overlap: .001,
Box: m.Box{MinPos: m.NewPosition(35, -98), MaxPos: m.NewPosition(35.25, -97.75)}}); err != nil {
t.Fatal(err)
}
tile, err := FindWaysAroundPositionIn(m.NewPosition(35.15, -97.9), output)
if err != nil || !tile.Loaded || tile.Ways.Len() != 1 {
t.Fatalf("generated road not loaded: %v", err)
}
way := tile.Ways.At(0)
if way.Nodes.Len() != 2 {
t.Fatalf("generated road has %d nodes", way.Nodes.Len())
}
first, last := 35.1, 35.2
forwardSpeed, backwardSpeed := 10./3.6, 20./3.6
if tc.reverse {
first, last = last, first
forwardSpeed, backwardSpeed = backwardSpeed, forwardSpeed
forwardRule, backwardRule = backwardRule, forwardRule
}
firstNode, lastNode := way.Nodes.At(0), way.Nodes.At(1)
if way.Id() != 42 || way.OneWay() != tc.wantOneWay ||
math.Abs(firstNode.Lat()-first) > 1e-9 || math.Abs(lastNode.Lat()-last) > 1e-9 ||
math.Abs(way.MaxSpeed()-13.4112) > 1e-6 ||
math.Abs(way.MaxSpeedForward()-forwardSpeed) > 1e-5 || math.Abs(way.MaxSpeedBackward()-backwardSpeed) > 1e-5 ||
way.ConditionalMaxSpeedRaw(true) != forwardRule || way.ConditionalMaxSpeedRaw(false) != backwardRule {
t.Fatalf("generated road lost direction or speed metadata: oneWay=%v forward=%v backward=%v",
way.OneWay(), way.MaxSpeedForward(), way.MaxSpeedBackward())
}
})
}
}
+12 -4
View File
@@ -309,17 +309,25 @@ func pbfBlock(kind string, raw []byte) []byte {
data = append(data, header...)
return append(data, blob...)
}
func syntheticPBF() []byte {
func syntheticPBF(tags ...string) []byte {
header := pbfBytesField(nil, 4, []byte("OsmSchema-V0.6"))
header = pbfBytesField(header, 5, []byte("LocationsOnWays"))
if len(tags) == 0 {
tags = []string{"highway", "residential", "maxspeed", "30 mph"}
}
var table []byte
for _, value := range []string{"", "highway", "residential", "maxspeed", "30 mph"} {
for _, value := range append([]string{""}, tags...) {
table = pbfBytesField(table, 1, []byte(value))
}
var keys, values []uint64
for i := 0; i < len(tags); i += 2 {
keys = append(keys, uint64(i+1))
values = append(values, uint64(i+2))
}
var way []byte
way = pbfVarintField(way, 1, 42)
way = pbfBytesField(way, 2, pbfPacked(1, 3))
way = pbfBytesField(way, 3, pbfPacked(2, 4))
way = pbfBytesField(way, 2, pbfPacked(keys...))
way = pbfBytesField(way, 3, pbfPacked(values...))
way = pbfBytesField(way, 8, pbfPacked(protowire.EncodeZigZag(1), protowire.EncodeZigZag(1)))
way = pbfBytesField(way, 9, pbfPacked(protowire.EncodeZigZag(351000000), protowire.EncodeZigZag(1000000)))
way = pbfBytesField(way, 10, pbfPacked(protowire.EncodeZigZag(-979000000), protowire.EncodeZigZag(0)))
+9 -6
View File
@@ -149,8 +149,12 @@ func (w *Way) IsForwardFrom(matchNode m.Position) bool {
return true
}
lastNode := w.Nodes.At(w.Nodes.Len()-1)
return !lastNode.Equals(matchNode)
lastNode := w.Nodes.At(w.Nodes.Len() - 1)
if lastNode.Equals(matchNode) {
// A closed one-way can be entered at its repeated endpoint in stored order.
return w.OneWay() && matchNode.Equals(w.Nodes.At(0))
}
return true
}
func IsForward(lineStart m.Position, lineEnd m.Position, bearing float64) bool {
@@ -431,7 +435,7 @@ func (w *Way) DistanceFrom(pos m.Position) (DistanceResult, error) {
minIdx := 0
for i := 0; i < w.Nodes.Len()-1; i++ {
nodeStart := w.Nodes.At(i)
nodeEnd := w.Nodes.At(i+1)
nodeEnd := w.Nodes.At(i + 1)
line := m.Line{Start: nodeStart, End: nodeEnd}
linePosition := line.NearestPosition(pos)
distance := pos.DistanceTo(linePosition.Pos)
@@ -447,7 +451,7 @@ func (w *Way) DistanceFrom(pos m.Position) (DistanceResult, error) {
onWayDistance := minNodeStart.DistanceTo(minLinePosition.Pos)
for i := range minIdx {
nodeStart := w.Nodes.At(i)
nodeEnd := w.Nodes.At(i+1)
nodeEnd := w.Nodes.At(i + 1)
onWayDistance += nodeStart.DistanceTo(nodeEnd)
}
@@ -491,8 +495,7 @@ func (w *Way) MatchingWays(offlineMaps *Offline, matchNode m.Position) ([]Way, e
continue
}
box := way.Box()
if box.Equals(w.Box()) {
if way.Id() == w.Id() {
continue
}
+45
View File
@@ -55,3 +55,48 @@ func TestBearingAlignmentIsAnUndirectedNonnegativePenalty(t *testing.T) {
}
}
}
func TestWayEntryDirectionForOpenAndClosedWays(t *testing.T) {
start, end := m.NewPosition(35.15, -97.9), m.NewPosition(35.151, -97.9)
for _, tc := range []struct {
name string
closed, oneWay, fromEnd, wantForward bool
}{
{"open start", false, true, false, true},
{"open end", false, true, true, false},
{"closed one-way", true, true, false, true},
{"closed two-way", true, false, false, false},
} {
t.Run(tc.name, func(t *testing.T) {
_, seg, err := capnp.NewMessage(capnp.SingleSegment(nil))
if err != nil {
t.Fatal(err)
}
raw, err := offline.NewRootWay(seg)
if err != nil {
t.Fatal(err)
}
raw.SetOneWay(tc.oneWay)
positions := []m.Position{start, end}
if tc.closed {
positions = append(positions, start)
}
nodes, err := raw.NewNodes(int32(len(positions)))
if err != nil {
t.Fatal(err)
}
for i, p := range positions {
nodes.At(i).SetLatitude(p.Lat())
nodes.At(i).SetLongitude(p.Lon())
}
from := start
if tc.fromEnd {
from = end
}
way := NewWay(raw)
if got := way.IsForwardFrom(from); got != tc.wantForward {
t.Fatalf("entry direction: got %v want %v", got, tc.wantForward)
}
})
}
}
+7 -1
View File
@@ -24,7 +24,7 @@ type syntheticWay struct {
// The actual offline Capnp representation is packed, decoded, and passed to
// State; tests do not mock GetCurrentWay or BuildMessage.
func packedRoadTile(tb testing.TB, specs []syntheticWay) maps.Offline {
func packedRoadTileBytes(tb testing.TB, specs []syntheticWay) []byte {
tb.Helper()
msg, seg, err := capnp.NewMessage(capnp.SingleSegment(nil))
if err != nil {
@@ -65,6 +65,12 @@ func packedRoadTile(tb testing.TB, specs []syntheticWay) maps.Offline {
if err != nil {
tb.Fatal(err)
}
return data
}
func packedRoadTile(tb testing.TB, specs []syntheticWay) maps.Offline {
tb.Helper()
data := packedRoadTileBytes(tb, specs)
parsed := maps.ReadOffline(data)
if !parsed.Loaded {
tb.Fatal("packed tile rejected")
+1
View File
@@ -61,6 +61,7 @@ var ServiceQueueSize = map[string]int64{
"selfdriveState": QUEUE_SIZE_SMALL,
"gpsLocation": QUEUE_SIZE_SMALL,
"starpilotCarState": QUEUE_SIZE_SMALL,
"starpilotNavigation": QUEUE_SIZE_MEDIUM,
"gpsLocationExternal": QUEUE_SIZE_SMALL,
"liveLocationKalman": QUEUE_SIZE_SMALL,
"gpsNMEA": QUEUE_SIZE_SMALL,
+10 -1
View File
@@ -46,6 +46,7 @@ type State struct {
SourceGeneration uint64
ProducerSession uint64
MatchedLimitMps float32
LimitPreview LimitPreview
lastMapLoadAttempt time.Time
bootNow func() uint64
// The fix the current road result was computed from. The loop runs at
@@ -90,6 +91,7 @@ func (s *State) clearRoadDerived() {
s.TargetVelocities = nil
s.MapCurveSpeed = 0
s.MatchedLimitMps = 0
s.LimitPreview = LimitPreview{}
s.NextHazard.Reset()
s.NextAdvisorySpeed.Reset()
s.clearSpeedLimitEvidence()
@@ -176,7 +178,7 @@ func (s *State) ProcessGps(sample cereal.GpsSample, success bool, load MapLoader
pos := m.PosFromLocation(location)
s.Position = pos
// Unknown CAN direction or speed uncertainty cannot create confident road evidence.
if sample.Source == cereal.GpsSourceCar && (math.IsNaN(float64(location.BearingAccuracyDeg())) || math.IsInf(float64(location.BearingAccuracyDeg()), 0) || location.BearingAccuracyDeg() <= 0 || location.BearingAccuracyDeg() >= 180 || math.IsNaN(float64(location.SpeedAccuracy())) || math.IsInf(float64(location.SpeedAccuracy()), 0) || location.SpeedAccuracy() >= 100) {
if (sample.Source == cereal.GpsSourceCar || sample.Source == cereal.GpsSourcePhone) && (math.IsNaN(float64(location.BearingAccuracyDeg())) || math.IsInf(float64(location.BearingAccuracyDeg()), 0) || location.BearingAccuracyDeg() <= 0 || location.BearingAccuracyDeg() >= 180 || math.IsNaN(float64(location.SpeedAccuracy())) || math.IsInf(float64(location.SpeedAccuracy()), 0) || location.SpeedAccuracy() >= 100) {
s.clearRoadDerived()
s.roadFixMonoTime = 0
s.RoadStatus = custom.MapdOut_SampleStatus_noMatch
@@ -255,6 +257,7 @@ func (s *State) ProcessGps(sample cereal.GpsSample, success bool, load MapLoader
s.MatchedLimitMps = float32(limit)
s.RoadStatus = custom.MapdOut_SampleStatus_matchedLimit
}
s.LimitPreview = s.previewLimit()
}
func (s *State) SuggestedSpeed() float32 {
@@ -361,6 +364,12 @@ func (s *State) buildMessageAt(now uint64) (*capnp.Message, error) {
output.SetRoadName(s.CurrentWay.Way.Name())
output.SetSpeedLimit(s.MatchedLimitMps)
if s.LimitPreview.BoundaryID != "" {
output.SetPreviewSpeedLimit(s.LimitPreview.Speed)
output.SetPreviewDistance(s.LimitPreview.Distance)
output.SetPreviewBoundaryId(s.LimitPreview.BoundaryID)
output.SetPreviewWayId(s.LimitPreview.WayID)
}
output.SetConditionalSpeedLimit(s.CurrentWay.ConditionalMaxSpeedRaw())
+3
View File
@@ -20,3 +20,6 @@ libmessaging.*
libmessaging_shared.*
.sconsign.dblite
.mypy_cache/
/msgq/_ipc.pyi
/msgq/visionipc/_visionipc.pyi
+2 -3
View File
@@ -1,4 +1,3 @@
include README.md pyproject.toml setup.py SConstruct SConscript
recursive-include site_scons *.py
recursive-include msgq *.py *.pyx *.pxd *.h *.cc
global-exclude *.so *.o *.os *.a *.pyc
recursive-include msgq *.py py.typed *.h *.cc
global-exclude *.pyi *.so *.o *.os *.a *.pyc
+33 -4
View File
@@ -1,7 +1,29 @@
Import('env', 'envCython', 'common')
import sys
import sysconfig
from pathlib import Path
import nanobind
Import('env', 'arch', 'common')
envNanobind = env.Clone(LIBS=[])
nb_dir = Path(nanobind.source_dir()).parent
envNanobind.Append(CPPPATH=[sysconfig.get_paths()['include'], nanobind.include_dir(), str(nb_dir / "ext/robin_map/include")],
CCFLAGS=["-fvisibility=hidden", "-fno-strict-aliasing"])
if arch == "Darwin":
envNanobind.Append(LINKFLAGS=["-bundle", "-undefined", "dynamic_lookup"])
else:
# Python symbols resolve at import time.
envNanobind["LINKFLAGS"] = [f for f in env["LINKFLAGS"] if f != "-Wl,--no-undefined"] + ["-pthread", "-shared"]
# Third-party runtime sources do not follow our shadowing warning policy.
envNanobindRuntime = envNanobind.Clone()
envNanobindRuntime['CCFLAGS'] = [flag for flag in envNanobindRuntime['CCFLAGS'] if not flag.startswith('-Wshadow')]
nanobind_lib = envNanobindRuntime.SharedObject('msgq/nb_combined.os', str(nb_dir / "src/nb_combined.cpp"))
visionipc_dir = Dir('msgq/visionipc')
python_suffix = ".so"
# Build msgq
msgq_objects = env.SharedObject([
@@ -10,7 +32,7 @@ msgq_objects = env.SharedObject([
'msgq/msgq.cc',
])
msgq = env.Library('msgq', msgq_objects)
msgq_python = envCython.Program('msgq/ipc_pyx.so', 'msgq/ipc_pyx.pyx', LIBS=envCython["LIBS"]+[msgq]+common)
msgq_python = envNanobind.Program(f'msgq/_ipc{python_suffix}', ['msgq/ipc_py.cc', nanobind_lib], LIBS=envNanobind["LIBS"]+[msgq]+common)
# Build Vision IPC
vipc_files = ['visionipc.cc', 'visionipc_server.cc', 'visionipc_client.cc']
@@ -24,10 +46,17 @@ vipc_objects = env.SharedObject(vipc_sources)
visionipc = env.Library('visionipc', vipc_objects)
vipc_libs = envCython["LIBS"] + [visionipc, msgq] + common
envCython.Program(f'{visionipc_dir.abspath}/visionipc_pyx.so', f'{visionipc_dir.abspath}/visionipc_pyx.pyx',
vipc_libs = envNanobind["LIBS"] + [visionipc, msgq] + common
visionipc_python = envNanobind.Program(f'{visionipc_dir.abspath}/_visionipc{python_suffix}', [f'{visionipc_dir.abspath}/visionipc_py.cc', nanobind_lib],
LIBS=vipc_libs)
# ASan modules require a preloaded runtime before Python can import them.
if "-fsanitize=address" not in env["LINKFLAGS"]:
root = Dir('.').abspath
for module in ('msgq._ipc', 'msgq.visionipc._visionipc'):
env.Command(module.replace('.', '/') + '.pyi', [msgq_python, visionipc_python],
f'"{sys.executable}" -m nanobind.stubgen -i "{root}" -m {module} -o "$TARGET"')
if GetOption('extras'):
env.Program('msgq/test_runner', ['msgq/msgq_tests.cc'], LIBS=[msgq]+common)
+1 -15
View File
@@ -1,7 +1,6 @@
import os
import platform
import subprocess
import sysconfig
arch = subprocess.check_output(["uname", "-m"], encoding='utf8').rstrip()
if platform.system() == "Darwin":
@@ -14,7 +13,6 @@ cpppath = [
"#/",
'#msgq/',
'/usr/lib/include',
sysconfig.get_paths()['include'],
]
AddOption('--minimal',
@@ -62,21 +60,9 @@ env = Environment(
CFLAGS="-std=gnu11",
CXXFLAGS="-std=c++1z",
CPPPATH=cpppath,
CYTHONCFILESUFFIX=".cpp",
tools=["default", "cython"]
tools=["default"]
)
Export('env', 'arch', 'common')
envCython = env.Clone(LIBS=[])
envCython["CCFLAGS"] += ["-Wno-#warnings", "-Wno-cpp", "-Wno-shadow", "-Wno-deprecated-declarations"]
envCython["CCFLAGS"].remove('-Werror')
if arch == "Darwin":
envCython["LINKFLAGS"] = ["-bundle", "-undefined", "dynamic_lookup"]
else:
envCython["LINKFLAGS"] = ["-pthread", "-shared"]
Export('envCython')
SConscript(['SConscript'])
+2 -2
View File
@@ -18,12 +18,12 @@ test:
ty:
run: ty check .
codespell:
run: codespell {files} -L ned,stdio,master --builtin clear,rare,informal,usage,code,names,en-GB_to_en-US -S uv.lock,*_pyx.cpp
run: codespell {files} -L ned,stdio,master --builtin clear,rare,informal,usage,code,names,en-GB_to_en-US -S uv.lock
files: git ls-tree -r HEAD --name-only
cppcheck:
run: cppcheck --error-exitcode=1 --inline-suppr --language=c++ --force --quiet -j4 --check-level=exhaustive $(git ls-files '*.cc' | grep -v -E '(msgq_tests|test_runner)\.cc')
cpplint:
run: cpplint --exclude=msgq/ipc_pyx.cpp --exclude=msgq/visionipc/visionipc_pyx.cpp --recursive --quiet --counting=detailed --linelength=240 --filter=-build,-legal,-readability,-runtime,-whitespace,+build/include_subdir,+build/forward_decl,+build/include_what_you_use,+build/deprecated,+whitespace/comma,+whitespace/line_length,+whitespace/empty_if_body,+whitespace/empty_loop_body,+whitespace/empty_conditional_body,+whitespace/forcolon,+whitespace/parens,+whitespace/semicolon,+whitespace/tab,+readability/braces msgq/
run: cpplint --recursive --quiet --counting=detailed --linelength=240 --filter=-build,-legal,-readability,-runtime,-whitespace,+build/include_subdir,+build/forward_decl,+build/include_what_you_use,+build/deprecated,+whitespace/comma,+whitespace/line_length,+whitespace/empty_if_body,+whitespace/empty_loop_body,+whitespace/empty_conditional_body,+whitespace/forcolon,+whitespace/parens,+whitespace/semicolon,+whitespace/tab,+readability/braces msgq/
# *** tests ***
test_runner:
Binary file not shown.
Binary file not shown.
-1
View File
@@ -1 +0,0 @@
ipc_pyx.cpp
+4 -4
View File
@@ -1,8 +1,8 @@
from msgq.ipc_pyx import Poller, SubSocket, PubSocket, SocketEventHandle, toggle_fake_events, \
from msgq._ipc import Poller, SubSocket, PubSocket, SocketEventHandle, toggle_fake_events, \
set_fake_prefix, get_fake_prefix, delete_fake_prefix, wait_for_one_event
from msgq.ipc_pyx import MultiplePublishersError, IpcError
from msgq._ipc import MultiplePublishersError, IpcError
from typing import Optional, List, Union
from typing import Optional, List
__all__ = [
"Poller",
@@ -25,7 +25,7 @@ __all__ = [
NO_TRAVERSAL_LIMIT = 2**64-1
def fake_event_handle(endpoint: str, identifier: Optional[Union[str, bytes]] = None, override: bool = True, enable: bool = False) -> SocketEventHandle:
def fake_event_handle(endpoint: str, identifier: Optional[str] = None, override: bool = True, enable: bool = False) -> SocketEventHandle:
ident = identifier if identifier is not None else get_fake_prefix()
handle = SocketEventHandle(endpoint, ident, override)
if override:
BIN
View File
Binary file not shown.
-61
View File
@@ -1,61 +0,0 @@
# distutils: language = c++
#cython: language_level=3
from libcpp.string cimport string
from libcpp.vector cimport vector
from libcpp cimport bool
cdef extern from "msgq/event.h":
cdef cppclass Event:
@staticmethod
int wait_for_one(vector[Event], int) except +
Event()
Event(int)
void set()
int clear()
void wait(int) except +
bool peek()
int fd()
cdef cppclass SocketEventHandle:
@staticmethod
void toggle_fake_events(bool)
@staticmethod
void set_fake_prefix(string)
@staticmethod
string fake_prefix()
SocketEventHandle(string, string, bool)
bool is_enabled()
void set_enabled(bool)
Event recv_called()
Event recv_ready()
cdef extern from "msgq/ipc.h":
cdef cppclass Message:
void init(size_t)
void init(char *, size_t)
void close()
size_t getSize()
char *getData()
cdef cppclass SubSocket:
SubSocket() nogil
int connect(string, bool, size_t) nogil
Message * receive(bool) nogil
void setTimeout(int) nogil
cdef cppclass PubSocket:
PubSocket()
int connect(string, size_t)
int sendMessage(Message *)
int send(char *, size_t)
bool all_readers_updated()
cdef cppclass Poller:
Poller()
void registerSocket(SubSocket *)
vector[SubSocket*] poll(int) nogil
+113
View File
@@ -0,0 +1,113 @@
#include <cerrno>
#include <chrono>
#include <cstdint>
#include <cstring>
#include <memory>
#include <optional>
#include <stdexcept>
#include <string>
#include <vector>
#include <nanobind/nanobind.h>
#include <nanobind/stl/optional.h>
#include <nanobind/stl/string.h>
#include <nanobind/stl/vector.h>
#include "msgq/event.h"
#include "msgq/ipc.h"
namespace nb = nanobind;
namespace {
struct IpcError : std::runtime_error {
using std::runtime_error::runtime_error;
};
struct MultiplePublishersError : IpcError {
using IpcError::IpcError;
};
void check_error(bool failed, int error, const std::string &endpoint = "") {
if (!failed) return;
std::string message = "Messaging failure " + (endpoint.empty() ? "" : "with " + endpoint) + ": " + std::strerror(error);
if (error == EADDRINUSE) throw MultiplePublishersError(message);
throw IpcError(message);
}
} // namespace
NB_MODULE(_ipc, m) {
// Daemon threads can still hold sockets at exit.
nb::set_leak_warnings(false);
nb::exception<IpcError> ipc_error(m, "IpcError");
nb::exception<MultiplePublishersError>(m, "MultiplePublishersError", ipc_error);
m.def("toggle_fake_events", &SocketEventHandle::toggle_fake_events, nb::arg("enabled"));
m.def("set_fake_prefix", &SocketEventHandle::set_fake_prefix, nb::arg("prefix"));
m.def("get_fake_prefix", &SocketEventHandle::fake_prefix);
m.def("delete_fake_prefix", [] { SocketEventHandle::set_fake_prefix(""); });
m.def("wait_for_one_event", &Event::wait_for_one, nb::arg("events"), nb::arg("timeout") = -1, nb::call_guard<nb::gil_scoped_release>());
nb::class_<Event>(m, "Event")
.def(nb::init<>())
.def("set", &Event::set)
.def("clear", &Event::clear)
.def("wait", &Event::wait, nb::arg("timeout") = -1, nb::call_guard<nb::gil_scoped_release>())
.def("peek", &Event::peek)
.def_prop_ro("fd", &Event::fd)
.def_prop_ro("ptr", [](Event &event) { return reinterpret_cast<uintptr_t>(&event); });
nb::class_<SocketEventHandle>(m, "SocketEventHandle")
.def(nb::init<std::string, std::string, bool>(), nb::arg("endpoint"), nb::arg("identifier"), nb::arg("override"))
.def_prop_rw("enabled", &SocketEventHandle::is_enabled, &SocketEventHandle::set_enabled)
.def_prop_ro("recv_called_event", &SocketEventHandle::recv_called, nb::keep_alive<0, 1>())
.def_prop_ro("recv_ready_event", &SocketEventHandle::recv_ready, nb::keep_alive<0, 1>());
nb::class_<SubSocket>(m, "SubSocket")
.def(nb::init<>())
.def("connect", [](SubSocket &socket, const std::string &name, bool conflate, size_t segment_size) {
nb::gil_scoped_release release;
int result = socket.connect(name, conflate, segment_size);
check_error(result != 0, errno, name);
}, nb::arg("endpoint"), nb::arg("conflate") = false, nb::arg("segment_size") = 0)
.def("setTimeout", &SubSocket::setTimeout, nb::arg("timeout"), nb::call_guard<nb::gil_scoped_release>())
.def("receive", [](SubSocket &socket, bool non_blocking) -> std::optional<nb::bytes> {
std::unique_ptr<Message> message;
{
nb::gil_scoped_release release;
message.reset(socket.receive(non_blocking));
}
if (!message) return std::nullopt;
return nb::bytes(message->getData(), message->getSize());
}, nb::arg("non_blocking") = false);
nb::class_<Poller>(m, "Poller")
.def(nb::init<>())
.def("registerSocket", &Poller::registerSocket, nb::arg("socket"), nb::keep_alive<1, 2>())
.def("poll", &Poller::poll, nb::arg("timeout"), nb::rv_policy::reference, nb::call_guard<nb::gil_scoped_release>());
nb::class_<PubSocket>(m, "PubSocket")
.def(nb::init<>())
.def("connect", [](PubSocket &socket, const std::string &name, size_t segment_size) {
int result = socket.connect(name, segment_size);
check_error(result != 0, errno, name);
}, nb::arg("endpoint"), nb::arg("segment_size") = 0)
.def("send", [](PubSocket &socket, nb::bytes data) {
int result = socket.send(const_cast<char *>(data.c_str()), data.size());
check_error(result != static_cast<int>(data.size()), errno);
}, nb::arg("data"))
.def("all_readers_updated", &PubSocket::all_readers_updated)
.def("wait_for_readers", [](PubSocket &socket, double timeout, double interval) {
// Python's sleep releases the GIL and handles signals while waiting.
nb::object sleep = nb::module_::import_("time").attr("sleep");
auto deadline = std::chrono::steady_clock::now() + std::chrono::duration<double>(timeout);
while (std::chrono::steady_clock::now() < deadline) {
if (socket.all_readers_updated()) return;
sleep(interval);
}
PyErr_SetString(PyExc_TimeoutError, "subscriber did not connect");
throw nb::python_error();
}, nb::arg("timeout") = 1.0, nb::arg("interval") = 0.001);
}
-230
View File
@@ -1,230 +0,0 @@
# distutils: language = c++
# cython: c_string_encoding=ascii, language_level=3
import time
from libcpp.string cimport string
from libcpp.vector cimport vector
from libcpp cimport bool
from libc cimport errno
from libc.string cimport strerror
from cython.operator import dereference
from .ipc cimport SubSocket as cppSubSocket
from .ipc cimport PubSocket as cppPubSocket
from .ipc cimport Poller as cppPoller
from .ipc cimport Message as cppMessage
from .ipc cimport Event as cppEvent, SocketEventHandle as cppSocketEventHandle
class IpcError(Exception):
def __init__(self, endpoint=None):
suffix = f"with {endpoint.decode('utf-8')}" if endpoint else ""
message = f"Messaging failure {suffix}: {strerror(errno.errno).decode('utf-8')}"
super().__init__(message)
class MultiplePublishersError(IpcError):
pass
def toggle_fake_events(bool enabled):
cppSocketEventHandle.toggle_fake_events(enabled)
def set_fake_prefix(string prefix):
cppSocketEventHandle.set_fake_prefix(prefix)
def get_fake_prefix():
return cppSocketEventHandle.fake_prefix()
def delete_fake_prefix():
cppSocketEventHandle.set_fake_prefix(b"")
def wait_for_one_event(list events, int timeout=-1):
cdef vector[cppEvent] items
for event in events:
items.push_back(dereference(<cppEvent*><size_t>event.ptr))
return cppEvent.wait_for_one(items, timeout)
cdef class Event:
cdef cppEvent event;
cdef setEvent(self, cppEvent event):
self.event = event
def set(self):
self.event.set()
def clear(self):
return self.event.clear()
def wait(self, int timeout=-1):
self.event.wait(timeout)
def peek(self):
return self.event.peek()
@property
def fd(self):
return self.event.fd()
@property
def ptr(self):
return <size_t><void*>&self.event
cdef class SocketEventHandle:
cdef cppSocketEventHandle * handle;
def __cinit__(self, string endpoint, string identifier, bool override):
self.handle = new cppSocketEventHandle(endpoint, identifier, override)
def __dealloc__(self):
del self.handle
@property
def enabled(self):
return self.handle.is_enabled()
@enabled.setter
def enabled(self, bool value):
self.handle.set_enabled(value)
@property
def recv_called_event(self):
e = Event()
e.setEvent(self.handle.recv_called())
return e
@property
def recv_ready_event(self):
e = Event()
e.setEvent(self.handle.recv_ready())
return e
cdef class Poller:
cdef cppPoller * poller
cdef list sub_sockets
def __cinit__(self):
self.sub_sockets = []
self.poller = new cppPoller()
def __dealloc__(self):
del self.poller
def registerSocket(self, SubSocket socket):
self.sub_sockets.append(socket)
self.poller.registerSocket(socket.socket)
def poll(self, timeout):
sockets = []
cdef int t = timeout
with nogil:
result = self.poller.poll(t)
for s in result:
socket = SubSocket()
socket.setPtr(s)
sockets.append(socket)
return sockets
cdef class SubSocket:
cdef cppSubSocket * socket
cdef bool is_owner
def __cinit__(self):
with nogil:
self.socket = new cppSubSocket()
self.is_owner = True
def __dealloc__(self):
if self.is_owner:
with nogil:
del self.socket
cdef setPtr(self, cppSubSocket * ptr):
if self.is_owner:
with nogil:
del self.socket
self.is_owner = False
self.socket = ptr
def connect(self, string endpoint, bool conflate=False, size_t segment_size=0):
cdef int r
with nogil:
r = self.socket.connect(endpoint, conflate, segment_size)
if r != 0:
if errno.errno == errno.EADDRINUSE:
raise MultiplePublishersError(endpoint)
else:
raise IpcError(endpoint)
def setTimeout(self, int timeout):
with nogil:
self.socket.setTimeout(timeout)
def receive(self, bool non_blocking=False):
cdef cppMessage *msg
with nogil:
msg = self.socket.receive(non_blocking)
if msg == NULL:
return None
else:
sz = msg.getSize()
m = msg.getData()[:sz]
with nogil:
del msg
return m
cdef class PubSocket:
cdef cppPubSocket * socket
def __cinit__(self):
self.socket = new cppPubSocket()
def __dealloc__(self):
del self.socket
def connect(self, string endpoint, size_t segment_size=0):
r = self.socket.connect(endpoint, segment_size)
if r != 0:
if errno.errno == errno.EADDRINUSE:
raise MultiplePublishersError(endpoint)
else:
raise IpcError(endpoint)
def send(self, bytes data):
length = len(data)
r = self.socket.send(<char*>data, length)
if r != length:
if errno.errno == errno.EADDRINUSE:
raise MultiplePublishersError
else:
raise IpcError
def all_readers_updated(self):
return self.socket.all_readers_updated()
def wait_for_readers(self, double timeout=1.0, double interval=0.001):
deadline = time.monotonic() + timeout
while time.monotonic() < deadline:
if self.all_readers_updated():
return
time.sleep(interval)
raise TimeoutError("subscriber did not connect")
Binary file not shown.
+95
View File
@@ -569,3 +569,98 @@ TEST_CASE("32 active subscribers retain every message across publisher restart")
msgq_close_queue(&pub);
std::remove(file.c_str());
}
static int64_t millis_since(std::chrono::steady_clock::time_point start)
{
return std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - start).count();
}
static std::thread send_later(msgq_queue_t *q, int delay_ms)
{
return std::thread([=]() {
std::this_thread::sleep_for(std::chrono::milliseconds(delay_ms));
uint64_t value = 1;
msgq_msg_t msg;
msgq_msg_init_data(&msg, (char *)&value, sizeof(value));
msgq_msg_send(&msg, q);
msgq_msg_close(&msg);
});
}
TEST_CASE("msgq_poll times out")
{
cleanup_test_queue();
msgq_queue_t writer, reader;
msgq_new_queue(&writer, "test_queue", 1024);
msgq_new_queue(&reader, "test_queue", 1024);
msgq_init_publisher(&writer);
msgq_init_subscriber(&reader);
msgq_pollitem_t items[] = {{&reader, 0}};
auto start = std::chrono::steady_clock::now();
REQUIRE(msgq_poll(items, 1, 100) == 0);
REQUIRE(millis_since(start) >= 100);
REQUIRE(msgq_poll(items, 1, 0) == 0);
send_later(&writer, 0).join();
REQUIRE(msgq_poll(items, 1, 0) == 1);
msgq_close_queue(&writer);
msgq_close_queue(&reader);
}
TEST_CASE("msgq_poll wakes up on send")
{
cleanup_test_queue();
msgq_queue_t writer, reader;
msgq_new_queue(&writer, "test_queue", 1024);
msgq_new_queue(&reader, "test_queue", 1024);
msgq_init_publisher(&writer);
msgq_init_subscriber(&reader);
msgq_pollitem_t items[] = {{&reader, 0}};
std::thread sender = send_later(&writer, 50);
auto start = std::chrono::steady_clock::now();
int n = msgq_poll(items, 1, 5000);
int64_t elapsed = millis_since(start);
sender.join();
REQUIRE(n == 1);
REQUIRE(elapsed < 1000);
msgq_close_queue(&writer);
msgq_close_queue(&reader);
}
TEST_CASE("msgq_poll multiple queues")
{
cleanup_test_queue();
remove("/dev/shm/msgq_test_queue2");
remove("/tmp/msgq_test_queue2");
msgq_queue_t writer1, writer2, reader1, reader2;
msgq_new_queue(&writer1, "test_queue", 1024);
msgq_new_queue(&writer2, "test_queue2", 1024);
msgq_new_queue(&reader1, "test_queue", 1024);
msgq_new_queue(&reader2, "test_queue2", 1024);
msgq_init_publisher(&writer1);
msgq_init_publisher(&writer2);
msgq_init_subscriber(&reader1);
msgq_init_subscriber(&reader2);
msgq_pollitem_t items[] = {{&reader1, 0}, {&reader2, 0}};
std::thread sender = send_later(&writer2, 50);
auto start = std::chrono::steady_clock::now();
int n = msgq_poll(items, 2, 5000);
int64_t elapsed = millis_since(start);
sender.join();
REQUIRE(n == 1);
REQUIRE(elapsed < 1000);
REQUIRE(items[0].revents == 0);
REQUIRE(items[1].revents == 1);
msgq_close_queue(&writer1);
msgq_close_queue(&writer2);
msgq_close_queue(&reader1);
msgq_close_queue(&reader2);
}
-2
View File
@@ -1,2 +0,0 @@
visionipc_pyx.cpp
*.so
+1 -1
View File
@@ -1,4 +1,4 @@
from msgq.visionipc.visionipc_pyx import VisionBuf, VisionIpcClient, VisionIpcServer, get_endpoint_name
from msgq.visionipc._visionipc import VisionBuf, VisionIpcClient, VisionIpcServer, get_endpoint_name
__all__ = [
"VisionBuf",
Binary file not shown.
+106 -55
View File
@@ -1,6 +1,7 @@
import gc
import uuid
import struct
import unittest
import uuid
from typing import Optional
from msgq.visionipc import VisionIpcServer, VisionIpcClient
@@ -10,7 +11,6 @@ class TestVisionIpc(unittest.TestCase):
client: Optional[VisionIpcClient]
def setUp(self):
# Each test needs independent listener and publisher endpoints in parallel runs.
self.name = f"test_{uuid.uuid4().hex}"
self.server = None
self.client = None
@@ -19,88 +19,139 @@ class TestVisionIpc(unittest.TestCase):
self.client = None
self.server = None
def setup_vipc(self, *stream_types, num_buffers=1, width=100, height=100, conflate=False):
self.server = VisionIpcServer(self.name)
for stream_type in stream_types:
self.server.create_buffers(stream_type, num_buffers, width, height)
self.server.start_listener()
def setup_vipc(
self, name: str, stream_type: int, *other_stream_types: int,
num_buffers: int = 1, width: int = 100, height: int = 100, conflate: bool = False,
) -> tuple[VisionIpcServer, VisionIpcClient, int]:
self.server = server = VisionIpcServer(self.name)
for stream in (stream_type, *other_stream_types):
server.create_buffers(stream, num_buffers, width, height)
server.start_listener()
if len(stream_types):
self.client = VisionIpcClient(self.name, stream_types[0], conflate)
assert self.client.connect(True)
else:
self.client = None
return self.server, self.client
self.client = client = VisionIpcClient(self.name, stream_type, conflate)
assert client.connect(True)
buffer_len = client.buffer_len
assert buffer_len is not None
return server, client, buffer_len
def test_connect(self):
self.setup_vipc(0)
assert self.client is not None
assert self.client.is_connected()
_, client, _ = self.setup_vipc("camerad", 0)
assert client.is_connected()
def test_available_streams(self):
stream_types = (0, 2)
self.setup_vipc(*stream_types)
self.setup_vipc("camerad", *stream_types)
available_streams = VisionIpcClient.available_streams(self.name, True)
assert available_streams == set(stream_types)
def test_buffers(self):
width, height, num_buffers = 100, 200, 5
self.setup_vipc(0, num_buffers=num_buffers, width=width, height=height)
assert self.client is not None
assert self.client.width == width
assert self.client.height == height
assert self.client.buffer_len is not None and self.client.buffer_len > 0
assert self.client.num_buffers == num_buffers
_, client, buffer_len = self.setup_vipc("camerad", 0, num_buffers=num_buffers, width=width, height=height)
assert client.width == width
assert client.height == height
assert buffer_len > 0
assert client.num_buffers == num_buffers
def test_send_single_buffer(self):
self.setup_vipc(0)
assert self.server is not None
assert self.client is not None
assert self.client.buffer_len is not None
buf = bytearray(self.client.buffer_len)
server, client, buffer_len = self.setup_vipc("camerad", 0)
buf = bytearray(buffer_len)
struct.pack_into("<Q", buf, 0, 1234)
self.server.send(0, buf, frame_id=1337)
server.send(0, buf, frame_id=1337)
recv_buf = self.client.recv()
recv_buf = client.recv()
assert recv_buf is not None
data = recv_buf.data
assert isinstance(data, memoryview)
assert struct.unpack_from("<Q", data, 0)[0] == 1234
assert len(data) == self.client.buffer_len
assert data[8:].nbytes == self.client.buffer_len - 8
assert self.client.frame_id == 1337
assert len(data) == buffer_len
assert data[8:].nbytes == buffer_len - 8
assert client.frame_id == 1337
assert recv_buf.frame_id == 1337
def test_no_conflate(self):
self.setup_vipc(0)
assert self.server is not None
assert self.client is not None
assert self.client.buffer_len is not None
buf = bytearray(self.client.buffer_len)
self.server.send(0, buf, frame_id=1)
self.server.send(0, buf, frame_id=2)
server, client, buffer_len = self.setup_vipc("camerad", 0)
buf = bytearray(buffer_len)
server.send(0, buf, frame_id=1)
server.send(0, buf, frame_id=2)
recv_buf = self.client.recv()
recv_buf = client.recv()
assert recv_buf is not None
assert self.client.frame_id == 1
assert client.frame_id == 1
recv_buf = self.client.recv()
recv_buf = client.recv()
assert recv_buf is not None
assert self.client.frame_id == 2
assert client.frame_id == 2
def test_conflate(self):
self.setup_vipc(0, conflate=True)
assert self.server is not None
assert self.client is not None
assert self.client.buffer_len is not None
buf = bytearray(self.client.buffer_len)
self.server.send(0, buf, frame_id=1)
self.server.send(0, buf, frame_id=2)
server, client, buffer_len = self.setup_vipc("camerad", 0, conflate=True)
buf = bytearray(buffer_len)
server.send(0, buf, frame_id=1)
server.send(0, buf, frame_id=2)
recv_buf = self.client.recv()
recv_buf = client.recv()
assert recv_buf is not None
assert self.client.frame_id == 2
assert client.frame_id == 2
recv_buf = self.client.recv(timeout_ms=5)
recv_buf = client.recv(timeout_ms=5)
assert recv_buf is None
def test_send_buffer_types(self):
server, client, buffer_len = self.setup_vipc("camerad", 0)
data = bytes(i % 256 for i in range(buffer_len))
strided = bytearray(len(data) * 2)
strided[::2] = data
for payload in (data, bytearray(data), memoryview(data), memoryview(strided)[::2], memoryview(data[::-1])[::-1]):
with self.subTest(type=type(payload)):
server.send(0, payload, frame_id=42, timestamp_sof=123, timestamp_eof=456)
buf = client.recv()
assert buf is not None
self.assertEqual(bytes(buf.data), data)
self.assertEqual(buf.frame_id, 42)
self.assertEqual(client.timestamp_sof, 123)
self.assertEqual(client.timestamp_eof, 456)
self.assertFalse(client.valid)
def test_send_wrong_size(self):
server, _, _ = self.setup_vipc("camerad", 0)
with self.assertRaises(AssertionError):
server.send(0, b"short")
def test_buffer_keeps_client_alive(self):
server, client, buffer_len = self.setup_vipc("camerad", 0)
data = b"a" * buffer_len
server.send(0, data, frame_id=42)
buf = client.recv()
assert buf is not None
self.client = None
del client
gc.collect()
self.assertEqual(buf.frame_id, 42)
view = buf.data
del buf
gc.collect()
self.assertEqual(bytes(view), data)
self.assertFalse(view.readonly)
view[0] = ord("b")
self.assertEqual(view[0], ord("b"))
def test_unconnected_properties(self):
client = VisionIpcClient("unconnected", 0, False)
for value in (client.width, client.height, client.stride, client.uv_offset, client.buffer_len):
self.assertIsNone(value)
self.assertEqual(client.num_buffers, 0)
self.assertFalse(client.is_connected())
self.assertFalse(client.connect(False))
self.assertIsNone(client.recv(timeout_ms=0))
def test_exact_uint64_timestamps_survive_receive_and_timeout(self):
server, client, size = self.setup_vipc("clock", 0, width=32, height=18)
payload = bytes(size)
for frame, sof, eof in ((0, 0, 0), (1, 2**53 + 1, 2**53 + 19),
(2**32 - 1, 2**63 + 17, 2**64 - 1)):
with self.subTest(frame=frame, sof=sof, eof=eof):
server.send(0, payload, frame_id=frame, timestamp_sof=sof, timestamp_eof=eof)
buffer = client.recv(timeout_ms=1000)
assert buffer is not None
assert (client.frame_id, client.timestamp_sof, client.timestamp_eof) == (frame, sof, eof)
assert client.recv(timeout_ms=0) is None
assert (client.frame_id, client.timestamp_sof, client.timestamp_eof) == (frame, sof, eof)
-52
View File
@@ -1,52 +0,0 @@
# distutils: language = c++
#cython: language_level=3
from libcpp.string cimport string
from libcpp.vector cimport vector
from libcpp.set cimport set
from libc.stdint cimport uint32_t, uint64_t
from libcpp cimport bool, int
cdef extern from "msgq/visionipc/visionbuf.h":
ctypedef uint32_t VisionStreamType "VisionStreamType"
cdef cppclass VisionBuf:
void * addr
int fd
size_t len
size_t width
size_t height
size_t stride
size_t uv_offset
size_t idx
void set_frame_id(uint64_t id)
uint64_t get_frame_id()
cdef extern from "msgq/visionipc/visionipc.h":
struct VisionIpcBufExtra:
uint32_t frame_id
uint64_t timestamp_sof
uint64_t timestamp_eof
bool valid
cdef extern from "msgq/visionipc/visionipc_server.h":
string get_endpoint_name(string, VisionStreamType)
cdef cppclass VisionIpcServer:
VisionIpcServer(string)
void create_buffers(VisionStreamType, size_t, size_t, size_t)
void create_buffers_with_sizes(VisionStreamType, size_t, size_t, size_t, size_t, size_t, size_t)
VisionBuf * get_buffer(VisionStreamType)
void send(VisionBuf *, VisionIpcBufExtra *, bool)
void start_listener()
cdef extern from "msgq/visionipc/visionipc_client.h":
cdef cppclass VisionIpcClient:
int num_buffers
VisionBuf buffers[1]
VisionIpcClient(string, VisionStreamType, bool)
VisionBuf * recv(VisionIpcBufExtra *, int) nogil
bool connect(bool)
bool is_connected()
@staticmethod
set[VisionStreamType] getAvailableStreams(string, bool)
+109
View File
@@ -0,0 +1,109 @@
#include <cstdint>
#include <cstring>
#include <optional>
#include <string>
#include <nanobind/nanobind.h>
#include <nanobind/ndarray.h>
#include <nanobind/stl/optional.h>
#include <nanobind/stl/set.h>
#include <nanobind/stl/string.h>
#include "msgq/visionipc/visionbuf.h"
#include "msgq/visionipc/visionipc.h"
#include "msgq/visionipc/visionipc_client.h"
#include "msgq/visionipc/visionipc_server.h"
namespace nb = nanobind;
namespace {
struct PythonVisionIpcClient : VisionIpcClient {
using VisionIpcClient::VisionIpcClient;
VisionIpcBufExtra extra = {};
};
using Buffer = nb::ndarray<nb::memview, uint8_t, nb::ndim<1>, nb::device::cpu>;
using InputBuffer = nb::ndarray<const uint8_t, nb::ndim<1>, nb::device::cpu>;
} // namespace
NB_MODULE(_visionipc, m) {
// Daemon threads can still hold clients and servers at exit.
nb::set_leak_warnings(false);
m.def("get_endpoint_name", &get_endpoint_name, nb::arg("name"), nb::arg("stream"));
nb::class_<VisionBuf>(m, "VisionBuf")
.def_prop_ro("data", [](VisionBuf &buf) { return Buffer(buf.addr, {buf.len}); }, nb::rv_policy::reference_internal,
nb::sig("def data(self) -> memoryview"))
.def_ro("width", &VisionBuf::width)
.def_ro("height", &VisionBuf::height)
.def_ro("stride", &VisionBuf::stride)
.def_ro("uv_offset", &VisionBuf::uv_offset)
.def_ro("idx", &VisionBuf::idx)
.def_ro("fd", &VisionBuf::fd)
.def_prop_ro("frame_id", &VisionBuf::get_frame_id);
nb::class_<VisionIpcServer>(m, "VisionIpcServer")
.def(nb::init<std::string>(), nb::arg("name"))
.def("create_buffers", &VisionIpcServer::create_buffers, nb::arg("tp"), nb::arg("num_buffers"), nb::arg("width"), nb::arg("height"))
.def("create_buffers_with_sizes", &VisionIpcServer::create_buffers_with_sizes,
nb::arg("tp"), nb::arg("num_buffers"), nb::arg("width"), nb::arg("height"), nb::arg("size"), nb::arg("stride"), nb::arg("uv_offset"))
.def("send", [](VisionIpcServer &server, VisionStreamType tp, InputBuffer data,
uint32_t frame_id, uint64_t timestamp_sof, uint64_t timestamp_eof) {
VisionBuf *buf = server.get_buffer(tp);
if (buf->len != data.size()) {
PyErr_SetString(PyExc_AssertionError, "buffer size does not match frame size");
throw nb::python_error();
}
// The old binding accepted strided byte views as well as contiguous data.
auto *source = data.data();
if (data.stride(0) == 1) {
std::memcpy(buf->addr, source, data.size());
} else {
auto *target = static_cast<uint8_t *>(buf->addr);
for (size_t i = 0; i < data.size(); ++i) target[i] = source[static_cast<int64_t>(i) * data.stride(0)];
}
buf->set_frame_id(frame_id);
VisionIpcBufExtra extra = {frame_id, timestamp_sof, timestamp_eof, false};
server.send(buf, &extra, false);
}, nb::arg("tp"), nb::arg("data").noconvert(), nb::arg("frame_id") = 0, nb::arg("timestamp_sof") = 0, nb::arg("timestamp_eof") = 0,
nb::sig("def send(self, tp: int, data: bytes | bytearray | memoryview, frame_id: int = 0, timestamp_sof: int = 0, timestamp_eof: int = 0) -> None"))
.def("start_listener", &VisionIpcServer::start_listener);
nb::class_<PythonVisionIpcClient>(m, "VisionIpcClient")
.def(nb::init<std::string, VisionStreamType, bool>(), nb::arg("name"), nb::arg("stream"), nb::arg("conflate"))
.def_prop_ro("width", [](PythonVisionIpcClient &client) -> std::optional<size_t> {
return client.num_buffers ? std::make_optional(client.buffers[0].width) : std::nullopt;
})
.def_prop_ro("height", [](PythonVisionIpcClient &client) -> std::optional<size_t> {
return client.num_buffers ? std::make_optional(client.buffers[0].height) : std::nullopt;
})
.def_prop_ro("stride", [](PythonVisionIpcClient &client) -> std::optional<size_t> {
return client.num_buffers ? std::make_optional(client.buffers[0].stride) : std::nullopt;
})
.def_prop_ro("uv_offset", [](PythonVisionIpcClient &client) -> std::optional<size_t> {
return client.num_buffers ? std::make_optional(client.buffers[0].uv_offset) : std::nullopt;
})
.def_prop_ro("buffer_len", [](PythonVisionIpcClient &client) -> std::optional<size_t> {
return client.num_buffers ? std::make_optional(client.buffers[0].len) : std::nullopt;
})
.def_prop_ro("num_buffers", [](PythonVisionIpcClient &client) { return client.num_buffers; })
.def_prop_ro("frame_id", [](PythonVisionIpcClient &client) { return client.extra.frame_id; })
.def_prop_ro("timestamp_sof", [](PythonVisionIpcClient &client) { return client.extra.timestamp_sof; })
.def_prop_ro("timestamp_eof", [](PythonVisionIpcClient &client) { return client.extra.timestamp_eof; })
.def_prop_ro("valid", [](PythonVisionIpcClient &client) { return client.extra.valid; })
.def("recv", [](PythonVisionIpcClient &client, int timeout_ms) {
nb::gil_scoped_release release;
return client.recv(&client.extra, timeout_ms);
}, nb::arg("timeout_ms") = 100, nb::rv_policy::reference_internal,
nb::sig("def recv(self, timeout_ms: int = 100) -> VisionBuf | None"))
// Holds the GIL: connect remaps the buffers other threads may be reading.
.def("connect", [](PythonVisionIpcClient &client, bool blocking) {
return client.connect(blocking);
}, nb::arg("blocking"))
.def("is_connected", [](PythonVisionIpcClient &client) { return client.is_connected(); })
.def_static("available_streams", &VisionIpcClient::getAvailableStreams, nb::arg("name"), nb::arg("block"),
nb::call_guard<nb::gil_scoped_release>());
}
@@ -1,10 +0,0 @@
# distutils: language = c++
#cython: language_level=3
from .visionipc cimport VisionBuf as cppVisionBuf
cdef class VisionBuf:
cdef cppVisionBuf * buf
@staticmethod
cdef create(cppVisionBuf*)
-163
View File
@@ -1,163 +0,0 @@
# distutils: language = c++
# cython: c_string_encoding=ascii, language_level=3
from libc.string cimport memcpy
from libc.stdint cimport uint32_t, uint64_t
from libcpp cimport bool
from libcpp.string cimport string
from .visionipc cimport VisionIpcServer as cppVisionIpcServer
from .visionipc cimport VisionIpcClient as cppVisionIpcClient
from .visionipc cimport VisionBuf as cppVisionBuf
from .visionipc cimport VisionIpcBufExtra, VisionStreamType
from .visionipc cimport get_endpoint_name as cpp_get_endpoint_name
def get_endpoint_name(string name, VisionStreamType stream):
return cpp_get_endpoint_name(name, stream).decode('utf-8')
cdef class VisionBuf:
@staticmethod
cdef create(cppVisionBuf * cbuf):
buf = VisionBuf()
buf.buf = cbuf
return buf
@property
def data(self):
cdef unsigned char[:] data = <unsigned char[:self.buf.len]> self.buf.addr
return memoryview(data)
@property
def width(self):
return self.buf.width
@property
def height(self):
return self.buf.height
@property
def stride(self):
return self.buf.stride
@property
def uv_offset(self):
return self.buf.uv_offset
@property
def idx(self):
return self.buf.idx
@property
def fd(self):
return self.buf.fd
@property
def frame_id(self):
return self.buf.get_frame_id()
cdef class VisionIpcServer:
cdef cppVisionIpcServer * server
def __init__(self, string name):
self.server = new cppVisionIpcServer(name)
def create_buffers(self, VisionStreamType tp, size_t num_buffers, size_t width, size_t height):
self.server.create_buffers(tp, num_buffers, width, height)
def create_buffers_with_sizes(self, VisionStreamType tp, size_t num_buffers, size_t width, size_t height, size_t size, size_t stride, size_t uv_offset):
self.server.create_buffers_with_sizes(tp, num_buffers, width, height, size, stride, uv_offset)
def send(self, VisionStreamType tp, const unsigned char[:] data, uint32_t frame_id=0, uint64_t timestamp_sof=0, uint64_t timestamp_eof=0):
cdef cppVisionBuf * buf = self.server.get_buffer(tp)
# Populate buffer
assert buf.len == len(data)
memcpy(buf.addr, &data[0], len(data))
buf.set_frame_id(frame_id)
cdef VisionIpcBufExtra extra
extra.frame_id = frame_id
extra.timestamp_sof = timestamp_sof
extra.timestamp_eof = timestamp_eof
extra.valid = False
self.server.send(buf, &extra, False)
def start_listener(self):
self.server.start_listener()
def __dealloc__(self):
del self.server
cdef class VisionIpcClient:
cdef cppVisionIpcClient * client
cdef VisionIpcBufExtra extra
def __cinit__(self, string name, VisionStreamType stream, bool conflate):
self.client = new cppVisionIpcClient(name, stream, conflate)
def __dealloc__(self):
del self.client
@property
def width(self):
return self.client.buffers[0].width if self.client.num_buffers else None
@property
def height(self):
return self.client.buffers[0].height if self.client.num_buffers else None
@property
def stride(self):
return self.client.buffers[0].stride if self.client.num_buffers else None
@property
def uv_offset(self):
return self.client.buffers[0].uv_offset if self.client.num_buffers else None
@property
def buffer_len(self):
return self.client.buffers[0].len if self.client.num_buffers else None
@property
def num_buffers(self):
return self.client.num_buffers
@property
def frame_id(self):
return self.extra.frame_id
@property
def timestamp_sof(self):
return self.extra.timestamp_sof
@property
def timestamp_eof(self):
return self.extra.timestamp_eof
@property
def valid(self):
return self.extra.valid
def recv(self, int timeout_ms=100):
cdef cppVisionBuf * buf
# release the GIL, this can block for timeout_ms
with nogil:
buf = self.client.recv(&self.extra, timeout_ms)
if not buf:
return None
return VisionBuf.create(buf)
def connect(self, bool blocking):
return self.client.connect(blocking)
def is_connected(self):
return self.client.is_connected()
@staticmethod
def available_streams(string name, bool block):
return cppVisionIpcClient.getAvailableStreams(name, block)
Binary file not shown.
+13 -13
View File
@@ -1,5 +1,5 @@
[build-system]
requires = ["setuptools>=77", "Cython>=3.1", "scons"]
requires = ["setuptools>=77", "nanobind>=2.12", "scons"]
build-backend = "setuptools.build_meta"
[project]
@@ -21,10 +21,14 @@ dependencies = []
Repository = "https://github.com/commaai/msgq"
[project.optional-dependencies]
dev = [
"setuptools", # for distutils
"Cython",
# for building from source with SCons
build = [
"nanobind>=2.12",
"scons",
]
dev = [
"msgq-ipc[build]",
"setuptools", # for distutils
"ruff",
"cppcheck",
"cpplint",
@@ -41,13 +45,16 @@ include = ["msgq", "msgq.*"]
namespaces = false
[tool.setuptools.package-data]
"msgq" = ["*.pxd", "*.h"]
"msgq.visionipc" = ["*.pxd", "*.h"]
"msgq" = ["*.pyi", "py.typed", "*.h"]
"msgq.visionipc" = ["*.pyi", "*.h"]
[tool.cibuildwheel]
build = "cp311-* cp312-* cp313-* cp314-*"
test-command = "python -m unittest discover -s msgq"
[tool.cibuildwheel.macos.environment]
MACOSX_DEPLOYMENT_TARGET = "10.13"
# https://beta.ruff.rs/docs/configuration/#using-pyprojecttoml
[tool.ruff]
lint.select = ["E", "F", "W", "PIE", "C4", "ISC", "RUF100", "A"]
@@ -56,10 +63,3 @@ lint.flake8-implicit-str-concat.allow-multiline=false
line-length = 160
target-version="py311"
[tool.ty.src]
exclude = ["site_scons/"]
[tool.ty.rules]
# Cython modules are compiled at build time, not available for static analysis
unresolved-import = "ignore"
+7 -3
View File
@@ -2,6 +2,7 @@ import os
from pathlib import Path
import subprocess
import sys
import sysconfig
from setuptools import Extension, setup
from setuptools.command.build_ext import build_ext
@@ -11,16 +12,19 @@ class SConsBuildExt(build_ext):
def build_extensions(self):
subprocess.check_call([sys.executable, "-m", "SCons", "--minimal", "-j", str(self.parallel or os.cpu_count() or 1)])
for ext in self.extensions:
source = Path(*ext.name.split(".")).with_suffix(".so")
module = Path(*ext.name.split("."))
source = module.with_suffix(sysconfig.get_config_var("EXT_SUFFIX"))
target = Path(self.get_ext_fullpath(ext.name))
target.parent.mkdir(parents=True, exist_ok=True)
self.copy_file(str(source), str(target))
stub = module.with_suffix(".pyi")
self.copy_file(str(stub), str(target.parent / stub.name))
setup(
ext_modules=[
Extension("msgq.ipc_pyx", sources=[]),
Extension("msgq.visionipc.visionipc_pyx", sources=[]),
Extension("msgq._ipc", sources=[]),
Extension("msgq.visionipc._visionipc", sources=[]),
],
cmdclass={"build_ext": SConsBuildExt},
)
-72
View File
@@ -1,72 +0,0 @@
import re
import SCons
from SCons.Action import Action
from SCons.Scanner import Scanner
pyx_from_import_re = re.compile(r'^from\s+(\S+)\s+cimport', re.M)
pyx_import_re = re.compile(r'^cimport\s+(\S+)', re.M)
cdef_import_re = re.compile(r'^cdef extern from\s+.(\S+).:', re.M)
def pyx_scan(node, env, path, arg=None):
contents = node.get_text_contents()
# from <module> cimport ...
matches = pyx_from_import_re.findall(contents)
# cimport <module>
matches += pyx_import_re.findall(contents)
# Modules can be either .pxd or .pyx files
files = [m.replace('.', '/') + '.pxd' for m in matches]
files += [m.replace('.', '/') + '.pyx' for m in matches]
# cdef extern from <file>
files += cdef_import_re.findall(contents)
# Handle relative imports
cur_dir = str(node.get_dir())
files = [cur_dir + f if f.startswith('/') else f for f in files]
# Filter out non-existing files (probably system imports)
files = [f for f in files if env.File(f).exists()]
return env.File(files)
pyxscanner = Scanner(function=pyx_scan, skeys=['.pyx', '.pxd'], recursive=True)
cythonAction = Action("$CYTHONCOM")
def create_builder(env):
try:
cython = env['BUILDERS']['Cython']
except KeyError:
cython = SCons.Builder.Builder(
action=cythonAction,
emitter={},
suffix=cython_suffix_emitter,
single_source=1
)
env.Append(SCANNERS=pyxscanner)
env['BUILDERS']['Cython'] = cython
return cython
def cython_suffix_emitter(env, source):
return "$CYTHONCFILESUFFIX"
def generate(env):
env["CYTHON"] = "cythonize"
env["CYTHONCOM"] = "$CYTHON $CYTHONFLAGS $SOURCE"
env["CYTHONCFILESUFFIX"] = ".cpp"
c_file, _ = SCons.Tool.createCFileBuilders(env)
c_file.suffix['.pyx'] = cython_suffix_emitter
c_file.add_action('.pyx', cythonAction)
c_file.suffix['.py'] = cython_suffix_emitter
c_file.add_action('.py', cythonAction)
create_builder(env)
def exists(env):
return True
+2 -2
View File
@@ -18,8 +18,8 @@ lefthook run test
trap 'rm -rf "$TEST_DIR"' EXIT
uv venv --python "$DIR/.venv/bin/python" "$TEST_DIR/.venv"
uv pip install --python "$TEST_DIR/.venv/bin/python" "$DIR"
cd "$TEST_DIR"
"$TEST_DIR/.venv/bin/python" -m unittest msgq.tests.test_messaging
cd "$("$TEST_DIR/.venv/bin/python" -c "import sysconfig; print(sysconfig.get_path('purelib'))")"
"$TEST_DIR/.venv/bin/python" -m unittest discover -t . -s msgq
)
# *** all done ***
+1 -1
View File
@@ -30,7 +30,7 @@ body:
attributes:
label: Provide a route where the issue occurs
description: Ensure the route is fully uploaded at https://useradmin.comma.ai. We cannot look into issues without routes, or at least a Dongle ID.
placeholder: 77611a1fac303767|2020-05-11--16-37-07
placeholder: 77611a1fac303767/2020-05-11--16-37-07
validations:
required: true
+1 -1
View File
@@ -22,7 +22,7 @@ body:
attributes:
label: Provide a route where the issue occurs if applicable
description: Ensure the route is fully uploaded at https://useradmin.comma.ai
placeholder: 77611a1fac303767|2020-05-11--16-37-07
placeholder: 77611a1fac303767/2020-05-11--16-37-07
- type: input
id: version
+4
View File
@@ -109,6 +109,10 @@ Start off by recording a route with lots of interesting events: enable LKAS and
Use the [longitudinal maneuvers](https://github.com/commaai/openpilot/tree/master/tools/longitudinal_maneuvers) report to evaluate your car's longitudinal control and tune it.
### Validation
Before submitting a port, go through the [car porting checklist](docs/CAR_PORTING_CHECKLIST.md).
## Contributing
All opendbc development is coordinated on GitHub and [Discord](https://discord.comma.ai). Check out the `#dev-opendbc-cars` channel and `Vehicle Specific` section.
+270
View File
@@ -0,0 +1,270 @@
# Car porting checklist
This checklist covers common issues we find in new car ports and many of the things we check before merging them. Use it as a guide to help you write a high-quality port and get it reviewed faster.
Not everything will apply to your car. If something can't be tested, let us know what and why. Where it applies, check each supported variant and both stock ACC and openpilot longitudinal. Some checks need the full openpilot stack, not just opendbc. Field names like `accFaulted` are defined in [`car.capnp`](../opendbc/car/car.capnp).
## Vehicle and setup
- Vehicle details
- [ ] Record make, model, year, trim, market, and relevant driver assistance options in `values.py`.
- If all variants in the US market come equipped with the necessary ADAS package, no package documentation is required.
- If a vehicle varies in features in another market, add a new [`CarDocs`](../opendbc/car/docs_definitions.py) entry.
- [ ] Add every observed ECU firmware version to `fingerprints.py`.
- [ ] Identify differences from related supported cars; verify reused signals and commands on this variant.
- Gas, gear, and brake signals often move between ICE, hybrid, and EV variants (e.g. Hyundai, Subaru, Toyota SecOC).
- Harness and CAN topology
- [ ] Confirm harness pinout, CAN bus assignments, and CAN/CAN FD configuration.
- [ ] Identify which ECUs send, receive, and forward each relevant message.
- This is useful to know which messages we need to forward and block in safety.
- Stock behavior and power lifecycle
- [ ] Record baseline stock LKAS, ACC, stop-and-go, and driver override behavior for reference when writing the port.
- Stock behavior should be matched when possible, e.g. how stock ACC commands standstill and resume.
- [ ] Verify ignition detection, startup, shutdown, and vehicle sleep with the device connected.
## Identification and parameters
- [ ] The car identifies correctly on repeated cold starts without a manually forced fingerprint or fingerprint cache.
- [ ] [Firmware queries](../opendbc/car/fw_versions.py) use the correct ECU addresses and buses without causing any DTCs on the dashboard.
- [ ] Exact and [fuzzy fingerprinting](../opendbc/car/fw_versions.py) distinguish variants that need different control or safety behavior.
- [ ] Unknown or ambiguous firmware does not silently select an incompatible platform.
- A vehicle variant we do not support that has identical firmware (such as lacking ACC or LKAS hardware) should be detected with a CAN signal in `interface.py` or `carstate.py` depending on what we can check, and set `dashcamOnly`.
- [ ] Reliable source for wheelbase, steering ratio, and other vehicle model parameters.
- [ ] `minSteerSpeed`, `minEnableSpeed`, `autoResumeSng`, and `steerAtStandstill` in `carParams` match observed behavior.
## CAN parsing and message construction
- Signal definitions and input validation
- [ ] Message addresses, lengths, buses, frequencies, endianness, signedness, scales, and units are verified.
- [ ] Checksums and counters validate against real captures, including counter wraparound.
- [ ] Missing, stale, malformed, or checksum-invalid messages produce the expected invalid CAN state.
- [ ] Variable-rate or intermittent messages have an [explicit frequency](../opendbc/can/parser.py) in `get_can_parsers` so they don't cause false CAN errors.
- For example, VW `Blinkmodi_02`, `HCA_01`, and `AWV_03` are 1 Hz when inactive and 50 Hz when active.
- [ ] Startup defaults and invalid signal values cannot look like valid engagement conditions.
- Outgoing messages and forwarding
- [ ] Copied stock messages preserve unrelated fields and use fresh source data.
- [ ] Transmitted messages have the correct bus, timing, counters, checksums, and request bits.
- [ ] Forwarding and replacement rules prevent duplicate actuation messages or bus loops.
- Diagnostics
- [ ] Diagnostic sessions, [ECU disabling](../opendbc/car/disable_ecu.py), and keepalives work across startup, restart, and shutdown, if used.
- This is for disabling an ECU, such as to gain longitudinal control. Some ports do not require this.
## Vehicle state: basics
- Seatbelt and doors
- [ ] Driver seatbelt: buckle and unbuckle both update `seatbeltUnlatched` correctly.
- [ ] Doors: open and close each door individually; `doorOpen` remains true until all monitored doors close.
- Gears
- [ ] Gear selection: ideally we parse park, reverse, neutral, drive, and alternate drive modes such as sport, B (brake) gear on hybrids, etc.
- If we cannot parse all gears, ensure we can disengage cruise on non-drive/sport gears, or anything that stock ACC would normally disengage for.
- [ ] Unknown gear values are handled explicitly instead of defaulting to drive.
- Pedals and regen
- [ ] Brake pedal: light press, firm press, hold, and release update `brakePressed` reliably.
- [ ] Accelerator pedal: press and release update `gasPressed`, including while cruise is active.
- [ ] Driver pedal signals are distinguished from braking or acceleration commanded by ACC/openpilot.
- In the past, we found some accelerator signals either activate when stock ACC accelerates, or don't activate when the user's gas was under the stock ACC request. `gasPressed` must be true whenever the user is pressing the gas pedal any amount, in any state.
- [ ] Regen paddles or other driver regen controls update `regenBraking`, if applicable.
- Parking brake and auto hold
- [ ] Parking brake engagement and release update `parkingBrake`.
- [ ] Auto hold engagement and release update `brakeHoldActive`, independently of the brake pedal.
- Speed and standstill
- [ ] Vehicle speed and individual wheel speeds have correct units and plausible values.
- [ ] Speed and `standstill` use the same signals as the safety model.
- For example, Nissan and GM use the rear wheel speeds to match safety.
- [ ] `standstill` distinguishes a complete stop from slow creep and updates promptly on drive-off.
- [ ] Cluster speed and cruise set speed agree with the car in both metric and imperial settings.
- Steering
- [ ] Steering angle has the correct sign, scale, center offset, and behavior through its full range.
- [ ] Steering torque has the correct sign and scale; driver torque and EPS torque are not confused.
- [ ] `steeringPressed` detects driver input in both directions without triggering on normal commanded steering.
- Use [`CS.update_steering_pressed`](../opendbc/car/interfaces.py) if the signal is noisy.
- Blinkers and blind spots
- [ ] Left/right blinkers are reported correctly.
- If we only have lamp state, use [`CS.update_blinker_from_lamp`](../opendbc/car/interfaces.py) so it doesn't oscillate with the blinker.
- If we only have stalk position, use [`CS.update_blinker_from_stalk`](../opendbc/car/interfaces.py) so short taps are held.
- [ ] Left/right blind spot indications are mapped correctly and clear when the stock indication clears.
- Faults and stock system state
- [ ] Temporary and permanent steering faults are logged correctly and recover only when the fault clears.
- EPS initializing states at startup are not permanent faults (e.g. VW, BYD).
- Expected low-speed steering lockouts should set `lowSpeedAlert` instead of a fault, usually with hysteresis.
- [ ] `accFaulted`, `carFaultedNonCritical`, `carNotReady`, and `vehicleSensorsInvalid` are reported correctly.
- [ ] ESP disabled/active states and stock AEB/FCW states are parsed where available.
- [ ] Stock LKAS settings (`invalidLkasSetting`) and conventional/non-adaptive cruise modes (`cruiseState.nonAdaptive`) are detected where relevant.
- For example, Nissan requires stock LKAS off, Mazda requires it on, and Tesla requires Autosteer off.
## Buttons and cruise state
- [ ] Set, resume, cancel, main cruise, speed up/down, gap, and LKAS buttons are mapped where present.
- [ ] Button press and release edges each occur once; held buttons do not cause unintended repeated engagement.
- [ ] Short presses, long presses, and simultaneous button presses are handled correctly.
- [ ] Cruise available (main), enabled, standstill, and set speed are parsed properly and match stock behavior.
- Cruise enabled should stay true during driver accelerator override and at standstill. Some dash signals fall in these states (e.g. Subaru).
- `cruiseState.standstill` should only be true if the car requires a resume press to drive off.
- [ ] Set speed changes, unit changes, and minimum/maximum set speeds behave correctly.
## Engagement and cancellation
- Engagement conditions
- [ ] The intended set/resume action engages once, only when the car is ready (`carNotReady`).
- [ ] Engagement refusal and disengagement for seatbelt, doors, gear, parking brake, and faults match the intended policy.
- Cancellation
- [ ] The physical cancel button disengages openpilot and cancels stock cruise as intended.
- [ ] If stock ACC is controlling longitudinal, verify our cancel message stops commanding acceleration.
- [ ] Software cancellation works in each supported cruise state, including standstill and driver accelerator override.
- [ ] Cancel wins over simultaneous set/resume requests; repeated cancel requests do not resume or re-enable cruise.
- If the cancel button can toggle pause/resume or the main button can engage, use `blockPcmEnable` to only allow PCM enable after a real user button press (e.g. Hyundai).
- [ ] Cruise main off disengages and prevents unintended re-engagement.
- Disengagement and recovery
- [ ] Releasing a pedal, closing a door, buckling a belt, or clearing a fault does not unexpectedly re-engage unless we are still in the soft-disable grace period.
- [ ] Stock ACC disengagement/fault causes the expected openpilot response when using stock longitudinal control.
- [ ] Lateral-only and longitudinal-active states set their respective actuation request bits correctly.
- For example, `CC.enabled` should be used when keeping the car's cruise engaged during a longitudinal override while `CC.longActive` falls.
## Lateral control
- Commands, limits, and driver override
- [ ] Small left/right commands produce the expected steering direction.
- [ ] Command magnitude and rate limits match the platform and safety implementation.
- [ ] Driver override reduces or cancels openpilot steering assistance as intended, in both directions.
- Some platforms disengage both steering and cruise on a steering override (e.g. Tesla); use `steeringDisengage` for this.
- Low-speed behavior
- [ ] Steering behaves correctly around the minimum steering speed, including crossing it in both directions.
- [ ] Check for low-speed oscillation and flag it if present. One solution is to low-pass filter the request below a certain speed.
- Faults and recovery
- [ ] Sustained steering, high steering rates, and request-bit transitions do not cause unexpected EPS faults.
- [ ] Temporary steering unavailability stops active requests and produces the expected user alert.
- Tuning and reported output
- [ ] Tuning is checked across representative speeds and curves for oscillation, lag, and saturation.
- [ ] Reported `actuatorsOutput` matches the command actually sent after clipping and rate limiting.
## Longitudinal control
- Supported control modes
- [ ] Stock ACC works correctly with openpilot lateral control, including cancel, override, and stop-and-go.
- There are two levels of support with openpilot-controlled longitudinal:
- [ ] Alpha longitudinal is chosen if we cannot maintain stock AEB or other safety features, or the car does not have a radar we can parse. openpilot longitudinal is then only available on development branches.
- [ ] openpilot longitudinal is the only mode and available in all branches if both of the above requirements are met, plus we have a good understanding of the car's longitudinal API.
- Acceleration, braking, and driver override
- [ ] Acceleration and braking command signs, scales, limits, and rate limits are verified.
- [ ] Acceleration, coasting, and braking transitions are smooth and free of conflicting requests.
- Some cars accept gas and brake commands separately; never request both at once.
- [ ] Brake press disengages while moving as well as while at a standstill.
- [ ] Accelerator override and release behave correctly, i.e. we stop requesting acceleration (inactive, or 0 m/s^2) while continuing to steer.
- Stopping, holding, and resuming
- [ ] Low-speed braking reaches a complete stop without unexpected creep or brake release.
- [ ] Stop hold works for short and extended stops; timeout and parking brake behavior are understood.
- [ ] Resume/drive-off releases hold in the correct sequence without unintended acceleration.
- [ ] Resume is sent only when requested and supported.
- [ ] Stops and starts on grades behave correctly without unexpected rollback or parking brake/gear transitions.
- [ ] `stopAccel`, `vEgoStopping`, `vEgoStarting`, `startingState`, and `startAccel` are tuned if needed.
- Powertrain states and faults
- [ ] Behavior is checked across relevant powertrain states, such as engine stop/start.
- [ ] ACC faults and transient not-ready states inhibit requests appropriately and recover cleanly.
- Tuning and stock safety features
- [ ] `longitudinalActuatorDelay` and tuning are evaluated across representative speeds, following, stops, and starts.
- [ ] Stock AEB/FCW interaction and any feature loss from ECU disabling are understood and documented.
## Radar, alerts, and stock features
- Radar
- [ ] Radar distance, lateral position, relative velocity, signs, units, and track IDs are verified, if supported.
- [ ] Radar track creation, updates, disappearance, invalid targets, and timeouts behave correctly.
- [ ] `radarDelay` is set if known.
- HUD and alerts
- [ ] HUD engaged/standby state, set speed, lane lines, lead indication, and gap bars match the requested state.
- [ ] Takeover, steering unavailable, and other required alerts appear and clear correctly.
- Stock feature interaction
- [ ] Stock LKAS and ACC do not fight openpilot commands.
- [ ] Stock safety messages are forwarded or arbitrated as intended; validate AEB interactions with replay/bench tests.
- [ ] Normal operation does not introduce persistent dash warnings or diagnostic trouble codes.
- Some cars may display warning messages on the dash or force a disengage if they detect a conflict between what their camera is sending and our request through read-back signals from the PCM or EPS. If we need to spoof a message to the camera/ADAS ECU, we need to verify it does not affect stock safety features such as LKAS or AEB.
## Safety and failure handling
- Message permissions and engagement
- [ ] The selected safety mode permits only intended transmit addresses, buses, and message lengths.
- [ ] Safety engagement/disengagement agrees with the car interface for cruise, buttons, pedals, and motion state.
- [ ] Non-zero actuation or active request bits are rejected when safety's [`controls_allowed`](../opendbc/safety/safety.h) is false.
- [ ] Any messages permitted while disengaged are limited to their intended inactive/cancel behavior.
- Actuation limits
- [ ] Steering magnitude, rate, driver torque, measured torque, and request-bit checks are covered as applicable.
- [ ] Longitudinal limits, inactive values, and cancel/resume restrictions are covered as applicable.
- [ ] All actuation-capable fields in transmitted messages are constrained, including fields unused by the controller.
- Communication failures and recovery
- [ ] Invalid counters/checksums, missing required input messages, and stale state disable control as intended.
- [ ] Relay malfunction detection and forwarding restrictions cover the actual harness topology.
- Test coverage
- [ ] Safety tests cover every new safety flag variant and reject disallowed commands.
## Regression tests
- Routes and automated checks
- [ ] Add representative routes to the [car test routes](../opendbc/car/tests/routes.py), covering relevant variants and modes.
- [ ] Add or update tests for fingerprinting, parsing, command construction, and discovered edge cases.
- [ ] Run applicable [car interface tests](../opendbc/car/tests/) and [safety tests](../opendbc/safety/tests/).
- [ ] [`test_models.py`](../opendbc/car/tests/test_models.py) passes on the new routes; it replays them to check CAN validity and that safety agrees with carstate and accepts our commands.
- [ ] Run the repository checks in [`test.sh`](../test.sh) and fix any failures.
- Regression validation
- [ ] Recheck related existing cars affected by shared DBC, controller, interface, or safety changes.
- [ ] Repeat startup, engagement, cancellation, and shutdown over multiple drives and ignition cycles.
@@ -0,0 +1 @@
.github export-ignore
@@ -0,0 +1 @@
__pycache__/
@@ -1,35 +1,53 @@
# BluePilot angle source
# Credits
Adapted from BluePilotDev/bluepilot bp-7.0 at
[e1d051d7ba270261b4455068bd68f1a58db15a4a](https://github.com/BluePilotDev/bluepilot/commit/e1d051d7ba270261b4455068bd68f1a58db15a4a),
principally Alan Polk, with John Christman, Jacob Neulight, Nathan Ingraham,
Praeuner, tonesto7, Haibin Wen and other BluePilot, sunnypilot and comma contributors.
Source paths: opendbc/sunnypilot/car/ford/lateral_angle_ext.py,
fordcan_ext.py, human_turn.py and opendbc/safety/modes/ford.h.
The simultaneous upstream LICENSE and LICENSE.md notices are retained.
This software is licensed under a custom license requiring permission for use.
This project uses software from Haibin Wen and SUNNYPILOT LLC and is licensed under a custom license requiring permission for use.
The BluePilot Ford lateral library: Ford's four-signal lateral control in curvature and angle
modes, the CAN messages that carry it, and the panda safety that checks it. Everything here is
under the MIT terms in [LICENSE](LICENSE).
This is a StarPilot adapter to the September 2026 proposed library shape.
It is not a released BluePilot library, tag, or claim of BluePilot validation.
The existing curvature controller remains StarPilot-owned. Pinion curvature,
curvature-primary settings and the unsafe native reset bypass are not imported.
## Contributors
The exact upstream root notices are additionally retained as upstream-root-LICENSE
and upstream-root-LICENSE.md; LICENSE/ LICENSE.md retain the opendbc notices.
ORIGIN_HISTORY.tsv records the inspected path history with commit IDs/authors/dates.
The private compact upstream root clone preserves its reachable root history.
Pre-conversion opendbc submodule objects are not included in that root history.
- **Alan Polk (`alan-polk`)**: principal author of the curvature and angle controllers, the
human-turn behavior, the angle-mode side channel and its panda safety, and the lane centering
trim.
- **tonesto7**: the CAN message builders and BluePilot's early releases.
- **John Christman**: lateral integration, platform data, and the angle-mode anti-stall work.
- **Jacob Neulight**: the steering-pinion curvature measurement and its panda safety, and the
shadow-curvature measurement.
- **Blue Genie (`blue-genie`)**: the split of BluePilot's lateral control into opendbc that this
library grew from.
- **ghbarker**: the lane centering trim's confidence blend, fallback, filter and rate limit.
- **Nathan Ingraham**: the angle-mode high-speed damping adjustment.
- **Praeuner**: the angle-mode damping range.
- **OneFast440**: the early Ford safety declarations.
- **nitrobass24**: the library's structure, its inputs and settings plumbing, and the safety tests
and CI.
Angle lane-centering trim is separately adapted from BluePilot bp-dev
[e22afa6be9b881fa784c92ebb316db47728a3d81](https://github.com/BluePilotDev/bluepilot/commit/e22afa6be9b881fa784c92ebb316db47728a3d81),
`opendbc/sunnypilot/car/ford/lane_center_trim.py` (blob
`5eedebd79e99493624f9d6af0750d1dd5713306c`). Alan Polk authored the initial
trim, model-position fallback, correction slew and default-strength changes;
ghbarker contributed reference/NaN guards and tests. The source explicitly credits
StarPilot/u/jc01rho for curvature-domain placement/geometry. The core adaptation
adds immutable host projection and conservative stale/driver-pressure suppression.
The pinned bp-7.0 controller baseline and default-disabled policy are unchanged.
Both simultaneous root/opendbc MIT and Custom MIT notices also exist unchanged at
this trim revision; all retained notices and permission acknowledgments apply
without assigning an exclusive license or claiming a new permission grant.
## Third-party code
### comma.ai's opendbc
MIT, Copyright (c) 2020, Comma.ai, Inc.
- `can/fordcan_ext.py`: `create_lat_ctl_msg` and `create_lat_ctl2_msg`, comma's Ford lateral
message builders from `opendbc/car/ford/fordcan.py`, with ramp-type and precision parameters
added.
- `safety/lateral.h`: the measured-table rate check, curvature-error band and lateral-accel cap,
from comma's earlier checks in `opendbc/safety/lateral.h`, and `ford_bp_curvature_cmd_checks`,
adapted from comma's `steer_curvature_cmd_checks`.
- `safety/ford.h`: the rate-of-change shape of the path angle and path offset checks, the
curvature-error band in the shadow-curvature check, and the message-rate formula.
- `core/lateral_curv_ext.py`: `apply_ford_curvature_limits_ext`, adapted from comma's
`apply_ford_curvature_limits`, with its road-roll and lateral-accel constants.
- Small pieces: the message packing and the yaw-rate curvature measurement in
`core/carcontroller_ext.py`, and the model time-index formula in `core/values_ext.py`.
- The BluePilot safety tests start from comma's Ford safety test skeletons.
### StarPilot
MIT, Copyright (c) 2026, firestar5683 and StarPilot contributors
- `core/lane_center_trim.py`: the lane-centering core (the correction formula, lookahead clip,
lane-line probability, std and width gates, the 0.004 correction limit and the lane-line
handling), ported from StarPilot's `selfdrive/controls/lib/lane_centering.py`
(`LaneCenteringController`, originally by jc01rho). The confidence blend, fallback, filter and
rate limit are BluePilot's.
@@ -0,0 +1,83 @@
# Bringing BluePilot lateral to a fork
How an openpilot fork takes BluePilot's Ford lateral control. There are two ways to do it:
1. **Use a release on your own** (preferred). Pin a release of this library and write your own hooks and settings against [HOSTS.md](HOSTS.md). BluePilot isn't in the loop and nothing waits on BluePilot: you consume lateral code that has been driven on Fords.
2. **Work with BluePilot.** BluePilot helps develop your hooks, keeps them in this repo, and tests the library on your fork in its CI, every PR and every night.
Either way the library is the same, and the same rules apply to it (below).
**This is a modified Ford safety model.** The panda safety allows signals and ranges that upstream's Ford safety blocks. Your fork decides whether to ship it, and whether BluePilot lateral is on or off by default for your users.
## The rules for either way
- **Pin a release, unmodified.** The library sits at `<pkg>/bluepilot_lateral/` in your opendbc (`<pkg>` is its package name) as one of:
- a **git submodule** pointing at a release tag, if your fork uses submodules; updating is moving the pointer;
- a **stamped copy** made with `tools/vendor.sh`, if it doesn't (see "The stamp tools").
- **Don't edit inside the library folder**, carry it as a git subtree with its history, or copy parts of it elsewhere. A fix to the library is a PR to this repo; once it's released, every fork gets it.
- **Everything else is yours:** the hooks in your opendbc, the code in your openpilot that fills the library's inputs every frame, and your settings UI.
## Option 1: use a release on your own
1. Pick a release from this repo's releases. Each one says what changed in the lateral control, the CAN messages and the panda safety, and what was validated on a car.
2. Mount it (submodule or stamped copy, above).
3. Write the hooks in your opendbc. [HOSTS.md](HOSTS.md) has the contract: the hook sites, the per-frame inputs and settings, the startup safety bits, and the host bindings to define if your safety function names differ from upstream opendbc's. Each release attaches the hook patch for sunnypilot/opendbc as a worked example.
4. Write your adapter: the small module that hands the library your fork's types (`hosts/sunnypilot/` is the example to copy). It lives in your fork, outside the library folder.
5. Run the library's tests in your tree ([HOSTS.md](HOSTS.md), "Running the tests") and drive it before you ship it.
6. To update, move to a newer release and rerun your tests.
BluePilot doesn't review or approve any of it. Questions and bugs in the library are welcome as issues in this repo.
## Option 2: work with BluePilot
For a fork that wants BluePilot to help with the hooks and keep them working. Open an issue in this repo, "Host: <fork>": where the fork's source and integration branch are, how it carries its opendbc, and how it takes outside changes. From there, this repo keeps for that fork:
- **its adapter**, `hosts/<fork>/`, plus host bindings if its safety names differ from upstream's;
- **its hooks**, `hosts/<fork>/hooks.patch`, until the fork carries them;
- **`hosts/<fork>/host.sh`**: `tree <dir>` builds the fork's opendbc at a pinned commit with the hooks and the library, placed by `vendor.sh`; `test <dir>` runs the library tests, the fork's safety suite with the BluePilot safety tests, and coverage; `make <dir>` writes `hooks.patch` back after a hook is edited in `<dir>/host`;
- **a CI job** that runs `host.sh tree` and `test` on every PR to this repo, so a library change that would break the fork fails here first;
- **a nightly check** that tries the fork's latest integration branch: a pass moves the pinned commit, a failure opens one issue saying which step broke. Once the fork carries the hooks it tests the fork's tree as it is, and runs `check_vendored.sh` on its copy.
The steps:
1. Develop and test the hooks and the fork's side in the fork, together.
2. Add the adapter, `hooks.patch`, `host.sh`, the CI job and the nightly check here, in one PR.
3. Release the library with them.
4. BluePilot opens the PR to the fork: the hooks, its settings UI and the library at that release. Later releases arrive the same way, as PRs the fork takes when it chooses.
An adapter lives in this repo only if this repo's CI tests the library on that fork.
## The stamp tools
For a fork that takes no submodules. Both run from a clone of this repo that has the release tag (`git fetch --tags`).
**`tools/vendor.sh <tag> <folder>`** makes `<folder>` exactly the library at `<tag>`:
- it empties the folder first, so files a release removed don't linger;
- it copies the release's files, without the library's own CI (`.github/`);
- it writes `<folder>/VERSION`: the tag and the commit, e.g. `v1.2.3 0a1b2c3d...`.
```bash
git clone https://github.com/BluePilotDev/bluepilot-lateral.git
cd bluepilot-lateral
tools/vendor.sh <tag> <fork checkout>/<path to its opendbc package>/bluepilot_lateral
```
Commit the folder in the fork. To update, run it again with the new tag: the diff shows exactly what changed between the two releases.
**`tools/check_vendored.sh <folder>`** reads `<folder>/VERSION` and compares the folder with that release, file by file (Python caches ignored). It lists every added, removed or changed file, and exits non-zero if there is one:
```bash
tools/check_vendored.sh <fork checkout>/<path to its opendbc package>/bluepilot_lateral
```
Run it in your CI to catch an edit to the copy. For a fork working with BluePilot, this repo's nightly check runs it too.
## Installer note: nested submodule
A fork that embeds the library as a *nested* submodule (Option 2 — e.g. `fork → opendbc → opendbc/bluepilot_lateral`) can end up with the library missing after an end-user install. Some installers run a non-recursive `git submodule update --init`, which checks out the first-level submodule but not the library nested inside it, and the build then fails to import it. Installs that use `git submodule update --init --recursive` are unaffected.
If your fork is installed that way, self-heal on launch: before the build imports the library, when its directory is empty, run a recursive submodule update scoped to the embedding submodule — guard on the library's `__init__.py`, retry, and fail soft so an offline boot is no worse than before.
## How BluePilot releases
- Lateral changes and changes to the library's safety headers are driven on a car before they're released.
- Every release says what changed in the lateral control, the CAN messages and the panda safety, and what was validated.
@@ -0,0 +1,129 @@
# Hosting the BluePilot lateral
A host is an opendbc fork with this library at `<pkg>/bluepilot_lateral/` and the hook patch applied, plus the openpilot on top of it that fills the library's inputs each frame. `<pkg>` is the fork's opendbc package: `opendbc` on sunnypilot/opendbc (ford-op/opendbc, under bp-sunnypilot), `iqdbc` on IQ.Pilot, `opendbc` on StarPilot. This page is what a host provides and what it gets back.
## Mounting the library
A host mounts this repo at `<pkg>/bluepilot_lateral/`, directly inside its opendbc package, as a git submodule (ford-op/opendbc) or, in a host that takes no submodules (IQPilot, at `artifacts/package_sources/iqdbc/iqdbc/bluepilot_lateral/`), as a stamped copy of a release. Updating is a bump of the submodule's commit, or vendoring the new release (below). The hook patch below carries every file ford-op/opendbc adds or changes outside the library, so since it mounts the library it also carries the mount's `.gitmodules` entry.
The library never names its host's package. Python imports are relative: the library's own modules by `.`/`..`, the host's opendbc by one more level (`from ...car.ford.values import CarControllerParams` in `core/`), and `tests/test_core_imports.py` fails on any absolute `opendbc`/`iqdbc`/`openpilot` import. Safety headers include each other as `../safety/<file>.h` and the host's declarations as `../../safety/declarations.h`, relative to the including file. The host includes the library's safety as `"<pkg>/bluepilot_lateral/safety/ford.h"`.
### Without submodules: a stamped copy
From a clone of this repo, `tools/vendor.sh <release tag> <host>/<pkg>/bluepilot_lateral` makes the folder exactly the library at that tag (files it no longer has are removed), without `.github/` (`.gitattributes`), plus a `VERSION` file: the tag and the commit. Nothing inside the folder is ever edited: a change goes into this repo, gets released, and the host vendors the new tag. `tools/check_vendored.sh <folder>` lists anything in the folder that differs from the release in its `VERSION` and fails if there is something; a host can run it in its CI, and this repo's IQPilot checks run it on IQPilot's copy.
### Host bindings
The safety headers are written against upstream opendbc's names. A host whose names differ defines these before it includes `safety/ford.h` (`safety/lateral_declarations.h` has the defaults):
| macro | default (upstream, sunnypilot) | IQ.Pilot |
|---|---|---|
| `BP_HOST_CURVATURE_LIMITS` | `CurvatureSteeringLimits` | `NativeCurvatureSteeringLimits` |
| `BP_HOST_CURVATURE_STATE` | `curvature_state` | `native_curvature_state` |
| `BP_HOST_RT_CURVATURE_RATE_CHECK` | `rt_curvature_rate_limit_check` | `rt_native_curvature_rate_limit_check` |
| `BP_HOST_LAT_ALLOWED()` | `(controls_allowed \|\| controls_allowed_lateral)` | `is_lat_active()` |
| `BP_HOST_SPEED_MISMATCH_CHECK()` | `speed_mismatch_check(vehicle_speed_2 ...)` | `((void)0)`: IQ's `ford_rx_hook` runs it |
The limits struct needs only `max_curvature`, `curvature_to_can`, `frequency`, `max_curvature_error` and `curvature_error_min_speed`.
## The hook patch
`tools/make_hook_patch.sh` prints it: every file outside the library that ford-op/opendbc changes from `master`, minus its own `.github/` (its CI). It fails unless the edited files are exactly the non-`.github` entries of `.github/bluepilot-hooks.txt`.
| file | what the hook does |
|---|---|
| `opendbc/car/ford/carcontroller.py` | Builds one `BluePilotLateral(CP, CP_SP, packer, CAN)`, the sunnypilot adapter (`hosts/sunnypilot/lateral.py`, below). Each frame `bp.update(CC_SP, frame)` says whether BluePilot lateral is active; if it is, `bp.step_lateral` sends the steering message at `STEER_STEP` (and returns the curvature to pass back next time) and `bp.step_lka` sends Lane_Assist_Data1 at `LKA_STEP`. Otherwise the stock path runs. |
| `opendbc/car/structs.py` | Imports `LateralInputs` from `host_types.py` and adds `CarControlSP.lateralInputs` and `CarParamsSP.lateralInputsRequired`. |
| `opendbc/sunnypilot/car/interfaces.py` | Calls `init_ford_safety_param_sp(CP, CP_SP, params_dict)` (`hosts/sunnypilot/safety_param.py`) at car init: the startup safety bits below, and `lateralInputsRequired` for every Ford. |
| `opendbc/safety/modes/ford.h` | Includes `safety/ford.h` and makes one call per hook site: counter and quality checks for the pinion message, the yaw measurement skipped while the pinion source is on, `ford_bp_rx_hook` (pinion measurement), `ford_bp_lka_tx` (angle-mode state from Lane_Assist_Data1), `ford_bp_lmc_tx` in place of the stock steering check when `BP_LATERAL` is set (the stock check runs unchanged when it isn't), and the pinion RX check list from `ford_bp_init`. |
| `opendbc/safety/tests/test_bluepilot_lateral.py` | New: hands the library's `safety/tests/` to upstream's test discovery, so upstream's `test.sh` and `mutation.py` run the BluePilot safety tests. |
## Every frame: inputs
A host fills these and the settings below for a car whose `CarParamsSP.lateralInputsRequired` is set: every Ford, BluePilot lateral on or off (`init_ford_safety_param_sp`).
`CarControlSP.lateralInputs` (`host_types.LateralInputs`):
| fields | from |
|---|---|
| `pathYawRate`, `pathX`, `pathY` | `modelV2.orientationRate.z`, `modelV2.position.x`, `.y` (33 points) |
| `laneLineLeftX/Y`, `laneLineRightX/Y` | `modelV2.laneLines[1]`, `[2]` |
| `laneLineProbs`, `laneLineStds` | `modelV2.laneLineProbs`, `.laneLineStds` |
| `laneChangeState`, `laneChangeDirection` | `modelV2.meta` |
| `steerRatio`, `stiffnessFactor`, `roll`, `angleOffsetDeg` | the learned vehicle parameters (`vehicleParameters`, paramsd) |
| `lateralDelay` | `lateralDelay.lateralDelay` |
| `valid` | true only when both the model and the vehicle parameters have been seen and every field above is filled |
Each path array has `host_types.MODEL_PATH_POINTS` (33) entries at the times in `host_types.MODEL_T_IDXS` (openpilot's `ModelConstants.T_IDXS`): the core interpolates over that grid. On a frame that isn't valid, or whose arrays aren't the full shape or contain a non-finite value, the library keeps the previous inputs.
## Every frame: settings
Every key in `opendbc.bluepilot_lateral.core.values_ext.BP_LATERAL_PARAMS` (the per-frame settings in `params/manifest.py`; hosts import the list from there), every frame, as (key, value) with the value as bytes (sunnypilot: `CarControlSP.params` entries): bools `"1"`/`"0"`, numbers as text. A missing, empty or unparsable value gives the manifest default. A host publishes its own registry default for a key nobody has set, so a host can carry its own defaults (BP7 uses 3.0 for `LC_PID_gain_UI_curv`). `python -m <pkg>.bluepilot_lateral.params.manifest` prints the keys as `params_keys.h` lines (IQ.Pilot drops `BACKUP`, which its params don't have).
## At startup: the safety bits
`values_ext.ford_safety_param(CP, params_dict)` reads the startup settings (`disable_BP_lat_UI`, `FordPrefSteerAngleCurvature`) and returns the bits. The host sends them to the panda in its second safety param (sunnypilot: the adapter ORs them into `CP_SP.safetyParam`). StarPilot has no second param: its adapter shifts them into bits 9-14 of its Ford `safetyParam` (`hosts/starpilot/safety_param.py`, clear of the bits StarPilot's Ford safety reads), and its `ford_init` hands `(param >> 9) & 0x3F` to `ford_bp_init`:
| bit | |
|---|---|
| 5 `BP_LATERAL` | the panda runs the BluePilot steering checks; the car controller sends the 4-signal messages only when it's set |
| 0 `STEER_ANGLE_CURVATURE`, 1-4 geometry index | the pinion angle replaces the yaw rate as the curvature measurement, on platforms with a row in the pinion table |
The panda keeps what it was given at init: changing `disable_BP_lat_UI` while driving takes full effect at the next init.
## Reading status
`BluePilotLateral.status()` returns `host_types.LateralStatus`, this frame's values for a BluePilot UI, under the names BP7's `controllerStateBP` publisher reads off its car controller:
| field | |
|---|---|
| `lateralUncertainty` | from whichever strategy ran |
| `angleRateLimited` | angle mode: the path angle soft rate limit bit this frame |
| `curvatureRateLimited` | angle mode: the equivalent curvature would have been rate-limited |
| `curvatureDeviationLimited` | either mode: the current-curvature error clip constrained the command |
| `humanTurnLateralPaused` | angle mode: lateral sent inactive during a manual turn |
| `stallBlipActive` | angle mode: the brief mode-0 pulse after a post-override stall |
| `disable_BP_lat_UI` | BluePilot lateral off: the setting, or the panda bit not set at init |
| `primary_lateral_control` | 0 = curvature, 1 = angle |
The four angle-mode values read false outside angle mode. bp-sunnypilot doesn't read status; it has no BluePilot UI.
## What the library uses from the host's opendbc
Every host is an opendbc fork, so the library uses these directly:
- `<pkg>.car`: `structs`, `DT_CTRL`, `ACCELERATION_DUE_TO_GRAVITY`, `scale_rot_inertia_per_mass` and `scale_tire_stiffness_per_mass` (or, on an opendbc from before the per-mass vehicle model, `scale_rot_inertia` and `scale_tire_stiffness`, at unit mass), `common.pid.PIDController`, `vehicle_model.VehicleModel` (either signature), `lateral` (`AngleSteeringLimits`, `apply_std_steer_angle_limits`, `ISO_LATERAL_ACCEL`), `ford.fordcan.CanBus`, `ford.values` (`CarControllerParams`, `FordFlags`).
- `can/fordcan_ext.py` sets LateralMotionControl2's checksum itself: upstream's packer fills Ford checksums (and overwrites it with the same value), IQ.Pilot's doesn't.
- The safety headers use `modes/ford.h`'s Ford constants and upstream's safety helpers, through the bindings above. `safety/lateral.h` and `can/fordcan_ext.py` copy four upstream functions, pinned per host package by `tests/test_bluepilot_upstream_copies.py`, so a host where one changed fails that test (a host with no pins skips it).
- The safety tests use the host's test harness: `<pkg>.safety.tests.test_ford`, `common` and `libsafety.libsafety_py`. `tests/host.py` names the host's own structs, adapter and libsafety setters for the tests that touch them.
## The core's API, and the host adapters
The core takes only the library's types:
- `core.carcontroller_ext.BluePilotLateral(CP, safety_param, packer, CAN)`: `safety_param` is the startup bits (`ford_safety_param`).
- `update(inputs, params, frame)`: `inputs` is a `host_types.LateralInputs`, `params` a `params.param_store.ParamStore` built from the frame's (key, value) settings. Then `step_lateral`, `step_lka` and `status()` as above.
Nothing under `core/`, `can/`, `params/` or `safety/` imports a host (`tests/test_core_imports.py`). `hosts/sunnypilot/`:
- `lateral.py`: a `BluePilotLateral` subclass with sunnypilot's types, `(CP, CP_SP, packer, CAN)` and `update(CC_SP, frame)`. It passes `CP_SP.safetyParam`, `CC_SP.lateralInputs` and the `CC_SP.params` entries as (key, value) pairs.
- `safety_param.py`: `init_ford_safety_param_sp(CP, CP_SP, params_dict)` ORs `ford_safety_param` into `CP_SP.safetyParam`. Its own module, so car init imports only `values_ext`.
`hosts/iqpilot/`, the same for IQ.Pilot's types:
- `lateral.py`: `BluePilotLateral(CP, CP_IQ, packer, CAN)` and `update(CC_IQ, frame)`, passing `CP_IQ.iqSafetyFlags`, `CC_IQ.lateralInputs` and the `CC_IQ.params` entries.
- `safety_param.py`: `init_ford_safety_param_iq(CP, CP_IQ, params_dict)` ORs `ford_safety_param` into `CP_IQ.iqSafetyFlags` (the panda's `current_safety_param_iq`) and sets `CP_IQ.lateralInputsRequired`.
`hosts/starpilot/`, for StarPilot, which has neither a second safety param nor CarControl / CarParams extension structs:
- `lateral.py`: `BluePilotLateral(CP, packer, CAN)`, reading the startup bits back out of the Ford `safetyParam`, and `update(frame_inputs, frame)`. `FrameInputs` carries `lateralInputs` and the `params` entries; StarPilot's openpilot side fills one each frame and hands it to the Ford CarController, in place of a CarControl extension.
- `safety_param.py`: `init_ford_safety_param_starpilot(CP, params_dict)` ORs `ford_safety_param << 9` into `CP.safetyConfigs[-1].safetyParam`; `bp_safety_param(CP)` reads them back; `lateral_inputs_required(CP)` says whether to fill `FrameInputs`. StarPilot's Transit (`FordFlags.LKA_STEERING`, steering through Lane_Assist_Data1 alone) gets neither.
Another host writes its own adapter against the same API.
## Running the tests
- Python tests: `python -m unittest discover -s <pkg>/bluepilot_lateral/tests -t .`, from the directory that holds `<pkg>`.
- Safety tests: the host's `<pkg>/safety/tests/test.sh` runs them, through a loader (ford-op/opendbc's `test_bluepilot_lateral.py`) or by naming `../../bluepilot_lateral/safety/tests` (IQ.Pilot: its cross-mode TX sweep reads `TX_MSGS` off every `Test*` class in its directory, so a loader there breaks it). Coverage of the BluePilot headers and `modes/ford.h`: `safety/tests/bluepilot_coverage.sh`, which runs the library's tests itself where the host has no loader; mutation: `safety/tests/bluepilot_mutation.py` (needs the host's `mutation.py`, which IQ.Pilot doesn't have).
- On a clean sunnypilot/opendbc checkout: `tools/test_on_host.sh <checkout>`, from a ford-op/opendbc checkout. It copies the library as committed and applies the hook patch to a temporary clone, then runs both.
- On IQPilot: `hosts/iqpilot/host.sh tree <dir>` builds iqdbc from IQ.Lvbs's repo at the pinned commit with `hosts/iqpilot/hooks.patch` (IQPilot's hooks, kept here until IQPilot carries them) and the library, vendored; `host.sh test <dir>` runs the library tests, IQPilot's safety suite with the BluePilot ones (minus `known_failures.txt`, IQPilot's own) and coverage; `host.sh make <dir>` writes the hook patch back after editing the hooks in `<dir>/host`. CI's `iqpilot` job runs tree and test. Every night `iqpilot pin check` tries IQ.Lvbs's latest `beta`: green moves the pin in a PR it merges itself (dropping `hooks.patch` once IQPilot carries the hooks), red keeps one issue open saying which step failed.
@@ -1,4 +1,6 @@
Copyright (c) 2020, Comma.ai, Inc.
Copyright (c) 2026, firestar5683 and StarPilot contributors
Copyright (c) 2024-2026, BluePilot contributors
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
@@ -1,21 +0,0 @@
# Custom MIT License
Copyright (c) 2024, Haibin Wen, SUNNYPILOT LLC
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to view and modify the Software, subject to the following conditions:
1. **Permission Required**: Permission Required for Commercial, For-Profit, or Closed Source Use: Use of the Software, in whole or in part, for any commercial purposes, for-profit projects, or in closed source projects requires explicit written permission from the original author(s).
2. **Redistribution**: Any redistribution of the Software, modified or unmodified, must retain this license notice and the following acknowledgment:
"This software is licensed under a custom license requiring permission for use."
3. **Visibility**: Any project that uses the Software must visibly mention the following acknowledgment:
"This project uses software from Haibin Wen and SUNNYPILOT LLC and is licensed under a custom license requiring permission for use."
4. **No Warranty**: THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Contact sunnypilot Support <support@sunnypilot.ai> for permission requests.
---
Haibin Wen, SUNNYPILOT LLC
@@ -1,38 +0,0 @@
9d190417bf74563abfa6f36996f1e673e9ee0a1b Praeuner 2026-08-13T22:26:15-05:00 Update dampening factor to .25
a881646ea5edc21c0db4c4e74076e0dc14671a4e alan-polk 2026-08-10T09:04:52-05:00 Merge pull request #176 from BluePilotDev/bp-no-stall
9012f76666a5c90764fcaec40832b9a607488c27 John Christman 2026-08-05T17:49:59-04:00 anti-stall
3610e3f18a0ad37d793d7ada61dbc739ab1077a3 Nathan Ingraham 2026-07-23T22:26:29-05:00 self.path_angle_gain_lowC_highV remains the platform-specific base gain. The toggle now controls separate self.user_dampening_factor. l ow_gain_calc uses: self.path_angle_gain_lowC_highV * self.user_dampening_factor UI placement, description, default, and 0.75–1.25 range remain unchanged.
1db52bc79e607ddb00214254dd4d732615e27fe4 Nathan Ingraham 2026-07-23T00:00:51-05:00 -Added “High Speed Dampening” directly below “High Speed Adjustment Factor”, range 0.75–1.25, step 0.01, and default 1.0.
5d994c24edf63f3b9004b2963f18ef4199e5d368 Jacob Neulight 2026-07-18T20:17:04-06:00 Ford safety: optional pinion-sourced angle_meas behind STEER_ANGLE_CURVATURE SP flag
699c17d9fde62c690eed9d68eed7d731744d5137 Jacob Neulight 2026-07-18T19:53:46-06:00 Ford: publish angle-mode shadow curvature from measured curvature during overrides
b7428fee4b116d4832f91c850d54cac2730de2ef alan-polk 2026-07-18T08:52:24-05:00 restore curvature safety checks to bp-6.0 logic.
97867c1eb57b7472f6fc3de62f0fef576e5a5497 alan-polk 2026-07-16T19:18:05-05:00 Human Turn Reset for Angle (#118)
7efee67e3e643d52b973895d6c6765c5cc12e314 alan-polk 2026-07-16T08:19:43-05:00 angle mode: rescale exit-detection threshold for 20Hz cadence
1958a3501ead10ae1f496f0d11e1e8cda6e2dbcc alan-polk 2026-07-14T15:22:38-05:00 Bp 7.0 variable cleanup (#111)
8f8d6d15f0a590f42b78de964ffb0d0af7f5d63d alan-polk 2026-07-14T08:53:28-05:00 Bp 7.0 panda2 (#109)
11ac3b760671b489ca449d522f1edf240a232884 alan-polk 2026-07-09T07:53:21-05:00 angle mode: fix ROC values tuned for 100Hz cadence running at actual 20Hz
dbdcea747e65f574d326a55c9bf1a9d6c9485e22 alan-polk 2026-07-08T09:21:32-05:00 Revert "ford: add current-curvature deviation check to angle mode (panda-side)"
69a9714563622729ba304ac75354ba3fd37b0b39 alan-polk 2026-07-08T09:17:09-05:00 ford: add current-curvature deviation check to angle mode (panda-side)
be7f86a5d0dc1ea3b3f0052d4b54ff0b8a70f843 John Christman 2026-07-06T21:53:55-04:00 cast on debug
5c1fbb07dd4d7d950a01cb2cd1faddd90d41e632 alan-polk 2026-07-06T14:20:49-05:00 controllerStateBP: add angleRateLimited + curvatureRateLimited diagnostics
06d654e5f48ea8fcffce6e98e48e3a30450b5097 alan-polk 2026-06-25T23:28:51-05:00 Revert "temp blend ratio change" — restore predicted-curvature blend to 0.50
540e69fcf4861a2ce5d7ca41467effc231638e27 alan-polk 2026-06-22T08:35:44-05:00 temp blend ratio change
e3819c7e630d85cfb44858840124fe7f72f1762d alan-polk 2026-06-13T20:47:09-05:00 ford.h: don't block angle mode on the inactive curvature signal
5d343738e30267688d0ffa6eebe2ec3ca57b10ee alan-polk 2026-06-13T18:28:30-05:00 ford.h: widen path_angle ROC to 2% looser than lateral_angle_ext _soft_roc
d0aac605f99d37e9da205e419f7989c1e9eaa386 alan-polk 2026-06-13T11:15:50-05:00 BluePilot: add angle-primary lateral control mode
51d5de8991508c862e7c51333e5055425b2b8d23 alan-polk 2026-04-11T12:00:10-05:00 fixing missing punctation in ford.h
8194ceb7e0bbc112c73d393a8f0add3261f6592d alan-polk 2026-04-11T11:51:49-05:00 make safety limits symetrical but control limits asymetrical. Add 1.5s duration to detect human turn
db2bdff05df103d71df62f45c2a3cb5211aba6e6 alan-polk 2026-04-09T23:47:30-05:00 move all external logic to lateral_curv_ext.py. Apply some order of operations fixes to try and eleminate panda blocking messages.
1a8ac23b5bd5d7952c15eb913c2b1e39f14ad92e Blue Genie 2026-04-08T15:10:24-05:00 Adjust panda limits
892be076daf898d806983f847c72357accff7312 Blue Genie 2026-04-08T11:45:28-05:00 Add logging functionality
ce7d27230657159332f64a253a0faea6cf8dcc95 alan-polk 2026-04-08T11:40:02-05:00 fix rate of change limits
021b5146e80ac58fe2e81dbd848bc7cd409ef08e alan-polk 2026-04-07T16:24:38-05:00 align ford.h limits with dbc and carcontroller
7f9212f7f8dc22c4a0a22443158554f4d3652a3c tonesto7 2026-04-05T21:28:37-04:00 Merge bp-6.1 upstream changes into bp-6.0-dev (#124)
dd1a9d3fd8f60462d3d8ac6de3e7860e70e1f667 alan-polk 2025-12-12T12:22:31-06:00 convert opendbc from a submodule to a subfolder
10101012d8a4cc37951a557023824cdd995443a6 alan-polk 2026-08-10T17:23:04Z BluePilot: angle-mode lane centering trim (advanced lane positioning)
560e80e9d593badca08aa60e8ddb692e58033425 alan-polk 2026-08-11T12:50:00Z BluePilot: angle-mode lane centering falls back to model position, not zero
94bf8144d4a683e9f3408f3babf892878f3b81f4 ghbarker 2026-08-12T01:31:33Z ford ALP: restore StarPilot guards (narrow lane, laneline std, NaN params) and cap applied correction at 0.0015
80825b81568ad684fe4ec37a797d3cbaa004fdcf ghbarker 2026-08-13T01:04:02Z split applied-correction ceiling into its own PR per review
e5b92cc48b34ea43ac756a96a96687c9851a188d ghbarker 2026-08-13T01:05:07Z add tests for the restored guards
af4bc410c9ef417ba723212ceaa2a77758dd8ecb alan-polk 2026-08-14T03:24:28Z BluePilot: rate-limit the lane centering correction, not just filter it
8eea9eb68e0c4126ee1c52f251e274d5865b6743 alan-polk 2026-08-19T13:33:00Z BluePilot: lower angle-mode lane centering default strength to 0.25
Can't render this file because it contains an unexpected character in line 14 and column 85.
@@ -1,81 +1,14 @@
# BluePilot Ford angle control
# BluePilot lateral
StarPilot adaptation of BluePilotDev/bluepilot `bp-7.0`, pinned to
[e1d051d7ba270261b4455068bd68f1a58db15a4a](https://github.com/BluePilotDev/bluepilot/commit/e1d051d7ba270261b4455068bd68f1a58db15a4a).
The layout follows Alan Polk's September 2026 library proposal. This is an
adaptation, not an upstream library release.
BluePilot's Ford lateral control as a library an opendbc fork mounts at `opendbc/bluepilot_lateral/`: both modes (curvature and angle), the CAN messages that carry them, the panda safety that checks them, and a settings manifest.
Existing curvature control remains the default. Explicit `FordLateralMode=1`
selects angle control next drive; the saved choice persists when unavailable.
Only this selector is exposed. Fixed defaults and bounds live in
`params/manifest.json`; pinion-yaw correction and curvature-primary tuning are
outside this library. Angle lane-centering trim is available as a default-disabled
core capability, separately sourced from BluePilot bp-dev
`e22afa6be9b881fa784c92ebb316db47728a3d81`; it adds no public settings.
**This is a modified Ford safety model.** The panda safety allows signals and ranges that upstream's Ford safety blocks. A fork chooses whether to ship it.
`core/` accepts scalar/model inputs without openpilot, Params or messaging
imports. `can/` owns encoding, `safety/ford_bp.h` owns native enforcement, and
`hosts/starpilot.py` connects the host. `params/protocol.json` generates the wire
constants. The folder is vendored without submodules, LFS or an external runtime.
**Status:** `v0.2.0`. It runs on bp-sunnypilot, a sunnypilot fork, through ford-op/opendbc (sunnypilot's opendbc plus the hooks).
## Admission
- [FORKS.md](FORKS.md): how a fork takes BluePilot lateral and works with BluePilot: carrying the library (a submodule or a stamped copy), the stamp tools, and adding a fork.
- [HOSTS.md](HOSTS.md): what a host provides, the hook patch, and how to run the tests.
- [can/LANE_ASSIST_DATA1.md](can/LANE_ASSIST_DATA1.md): the Lane_Assist_Data1 bits angle mode uses.
- [CREDITS.md](CREDITS.md) and [LICENSE](LICENSE) (MIT).
Classic: Bronco Sport Mk1, Escape Mk4, Focus Mk4, Maverick Mk1, Explorer Mk6.
CAN FD: Escape Mk4.5, Expedition Mk4, F150 Mk14, F150 Lightning Mk1, Ranger Mk2,
Mustang Mach-E Mk1.
Exact classic safety parameters 32/33 map to angle 128/129; CAN FD 18/19 and
66/67 map to 130/131. Parameter 131 requires DEBUG; RELEASE retains stock 130.
Longitudinal, radar, fingerprint and parser policy remain host-owned. Admission
uses the final CarParams contract, not brand or steering-control enum alone.
AOL, alternative experiences, Transit, Edge, Mondeo, SecOC, passive, dashcam,
notCar and unsupported static flags are excluded. Auxiliary noOutput requires
parameter zero; production deployments with two Pandas remain unqualified until
actual startup, bus routing and public acknowledgments are verified.
## Safety design
The extension uses small Ford hooks without changing shared safety headers or
existing curvature namespaces. Active steering requires ordinary controls
permission, healthy physical RX and a fresh matching public Panda acknowledgment
of safety parameter, alternative experience, controls, RX validity, heartbeat and
faults. Cruise, gas/brake, checksum, counter, quality and freshness checks apply.
Neutral mode cannot grant permission.
The BluePilot reset-bypass latch is omitted. Rejected frames preserve accepted
actuator history and withdraw the shadow lease; accepted neutral commands or
permission withdrawal reset history. Lost counters recover through neutral
resynchronization. Version or checksum failure cannot fall through to curvature
control.
Active frames carry neutral curvature, offset and curvature rate, with checked
mode, ramp, precision, path angle, counter, checksum and cadence. Native enforcement
checks the shadow against the actual path across gains 0.95–1.3, bounds both by
`min(0.02, 2.4114/v²)`, and bounds the full actual-curvature interval against
measured curvature above 10 m/s. Actual path also obeys the 3.5886 m/s³ equivalent
jerk limit and rolling message-rate limit. Core quantization and timing margins
keep packed commands inside these bounds; an impossible intersection is neutral.
The versioned 0x3CA announcement preserves StarPilot's existing byte 4 bit 1
curvature flag and requires it clear in angle mode. It carries a counter, signed
shadow curvature, protocol version and checksum; its lease expires after 60 ms.
The shadow derives from the final path after rate limiting and saturation.
Control computes at 20 Hz, announces through 33 Hz LKA, then sends the latest
announced result at 20 Hz, adding at most one steering tick. Permission withdrawal
sends neutral immediately. Human-turn recovery uses bounded neutral blips without
a native bypass. The retained Dom factory steering delay is 0.22 s; pinned
BluePilot uses 0.20 s.
These stricter native limits and versioned metadata intentionally differ from
BluePilot 7.0. Identical bytes or safety decisions are not claimed for affected
commands. Runtime and physical ECU qualification remain pending.
## Attribution and notices
See `CREDITS.md`, `ORIGIN_HISTORY.tsv` and the project `THIRD_PARTY_NOTICES.md`.
Both upstream MIT and Custom MIT notices are retained verbatim in `LICENSE`,
`LICENSE.md`, `upstream-root-LICENSE` and `upstream-root-LICENSE.md`. The maintainer
reports author permission to use this adaptation under MIT. Both published notices
remain intact.
History before 2026-09-15 lives in [BluePilotDev/bluepilot](https://github.com/BluePilotDev/bluepilot).

Some files were not shown because too many files have changed in this diff Show More