commit 9f9c9a70cce4655844c33b8758c33fce09bfcf4f Author: IQ.Lvbs history cleanup Date: Sat Aug 22 23:42:42 2026 -0500 IQ.Pilot Prebuilt Release @ ab07000 diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 000000000..bd9b84ed6 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,57 @@ +* text=auto eol=lf + +# to move existing files into LFS: +# git add --renormalize . +selfdrive/assets/icons/iq/rotate-ccw.svg -filter -diff -merge -text +selfdrive/assets/icons/iq/rotate-ccw.png -filter -diff -merge -text +selfdrive/assets/icons/iq/rotate-cw.svg -filter -diff -merge -text +selfdrive/assets/icons/iq/rotate-cw.png -filter -diff -merge -text +selfdrive/assets/icons/iq/square-parking.svg -filter -diff -merge -text +selfdrive/assets/icons/iq/square-parking.png -filter -diff -merge -text + +# Keep this icon in regular git (not LFS) for lightweight branding iteration. +selfdrive/assets/icons_mici/experimental_mode_mici.png -filter -diff -merge -text +selfdrive/assets/icons_mici/experimental_mode_tizi.png -filter -diff -merge -text +selfdrive/assets/icons_mici/iqdynamic_mode_mici.png -filter -diff -merge -text +selfdrive/assets/icons_mici/iqdynamic_mode_tizi.png -filter -diff -merge -text +selfdrive/assets/icons_mici/iqstandard_mode_mici.png -filter -diff -merge -text +selfdrive/assets/icons_mici/iqstandard_mode_tizi.png -filter -diff -merge -text +selfdrive/assets/icons_mici/onroad/driver_monitoring/dm_center.png -filter -diff -merge -text +selfdrive/assets/icons_mici/onroad/driver_monitoring/dm_cone.png -filter -diff -merge -text +selfdrive/assets/icons_mici/settings/konn3kt_icon.png -filter -diff -merge -text +selfdrive/assets/icons_mici/buttons/toggle_dot_enabled.png -filter -diff -merge -text +iqpilot/selfdrive/assets -filter -diff -merge -text +selfdrive/assets/icons_mici/buttons/toggle_pill_enabled.png -filter -diff -merge -text +selfdrive/assets/icons_mici/offroad_alerts/green_wheel.png -filter -diff -merge -text +selfdrive/assets/icons_mici/setup/green_button.png -filter -diff -merge -text +selfdrive/assets/icons_mici/setup/green_button_pressed.png -filter -diff -merge -text +selfdrive/assets/icons_mici/setup/green_dm.png -filter -diff -merge -text +selfdrive/assets/icons_mici/setup/green_info.png -filter -diff -merge -text +selfdrive/assets/icons_mici/setup/small_slider/slider_green_rounded_rectangle.png -filter -diff -merge -text +selfdrive/assets/images/spinner_comma.png -filter -diff -merge -text +selfdrive/assets/images/k3_spinner.png -filter -diff -merge -text +selfdrive/assets/icons/camera.png -filter -diff -merge -text +selfdrive/assets/fonts/Syncopate-Regular.ttf -filter -diff -merge -text +selfdrive/assets/fonts/Tektur-Variable.ttf -filter -diff -merge -text +tools/jotpluggler/assets/bootstrap-icons.ttf -filter -diff -merge -text + + + +# IQ assets, including icon_longitudinal.png, should stay in normal git (not LFS). +iqpilot/selfdrive/assets/** -filter -diff -merge -text + +# mici UI icons stored as plain PNG — gitlab LFS server lacks these objects +selfdrive/assets/icons_mici/buttons/button_circle_pressed.png -filter -diff -merge -text +selfdrive/assets/icons_mici/buttons/button_circle_red_pressed.png -filter -diff -merge -text +selfdrive/assets/icons_mici/models.png -filter -diff -merge -text +selfdrive/assets/icons_mici/settings/brightness.png -filter -diff -merge -text +selfdrive/assets/icons_mici/settings/camera.png -filter -diff -merge -text +selfdrive/assets/icons_mici/settings/keyboard/enter.png -filter -diff -merge -text +selfdrive/assets/icons_mici/settings/keyboard/enter_disabled.png -filter -diff -merge -text +selfdrive/assets/icons_mici/settings/sd_card.png -filter -diff -merge -text +selfdrive/assets/icons_mici/settings/software.png -filter -diff -merge -text +selfdrive/assets/icons_mici/settings/trips.png -filter -diff -merge -text +selfdrive/assets/icons_mici/settings/vehicle.png -filter -diff -merge -text +selfdrive/assets/icons_mici/speedometer.png -filter -diff -merge -text + +selfdrive/assets/icons/iq/*.png -filter -diff -merge -text diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..80fb9e9ee --- /dev/null +++ b/.gitignore @@ -0,0 +1,180 @@ +venv/ +.venv/ +!artifacts/runtime/ble/rootfs/usr/local/venv/ +!artifacts/runtime/ble/rootfs/usr/local/venv/** +# Runtime install dir for proprietary bundles — rewritten by ensure_private_installed +# on every service start. Tracking anything under it keeps the working tree +# perpetually dirty and sends the updater into an apply/restart loop that kills +# ble-transportd (and comma.service) every few minutes. +/.iqpilot/ +.ci_cache +.env +.clang-format +http-client.private.env.json +.DS_Store +.tags +.ipynb_checkpoints +.idea +.overlay_init +.overlay_consistent +.sconsign.dblite +a.out +.hypothesis +.cache/ + +/docs_site/ + +*.mp4 +*.dylib +*.DSYM +*.d +!artifacts/runtime/ble/rootfs/etc/udev/rules.d/ +!artifacts/runtime/ble/rootfs/etc/udev/rules.d/** +*.pyc +!artifacts/iqpilot_*_private/**/*.pyc +*.pyo +.*.swp +.*.swo +.*.un~ +*.tmp +*.o +*.o-* +*.os +*.os-* +*.so +!artifacts/iqpilot_*_private/**/*.so +!artifacts/runtime/ble/rootfs/usr/local/venv/**/*.so +artifacts/iqpilot_private/ +artifacts/iqpilot_*_private/python/iqpilot_private/**/*.cpython-*-darwin.so +*.a +*.clb +*.class +*.pyxbldc +*.vcd +*.mo +*_pyx.cpp +*.stats +config.json +clcache +compile_commands.json +compare_runtime*.html + +selfdrive/pandad/pandad +cereal/services.h +cereal/gen +cereal/messaging/bridge +selfdrive/ui/translations/tmp +selfdrive/car/tests/cars_dump +system/camerad/camerad +system/camerad/test/ae_gray_test + +.coverage* +coverage.xml +htmlcov +pandaextra + +.mypy_cache/ +flycheck_* + +cppcheck_report.txt +comma*.sh + +selfdrive/modeld/models/*.pkl +!selfdrive/modeld/models/driving_vision_tinygrad.pkl +!selfdrive/modeld/models/driving_policy_tinygrad.pkl +!selfdrive/modeld/models/dmonitoring_model_tinygrad.pkl +!selfdrive/modeld/models/driving_vision_metadata.pkl +!selfdrive/modeld/models/driving_policy_metadata.pkl +!selfdrive/modeld/models/dmonitoring_model_metadata.pkl +iqpilot/modeld*/thneed/compile +iqpilot/modeld*/models/*.thneed +iqpilot/modeld*/models/*.pkl + +# openpilot log files +*.bz2 +*.zst + +build/ + +!**/.gitkeep + +poetry.toml +Pipfile + +### VisualStudioCode ### +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +!.vscode/*.code-snippets + +# Local History for Visual Studio Code +.history/ + +# Built Visual Studio Code Extensions +*.vsix + +### VisualStudioCode Patch ### +# Ignore all local history of files +.history +.ionide + +.claude/ +PLAN.md +TASK.md + +### JetBrains ### +!.idea/customTargets.xml +!.idea/tools/* +!.run/* +/.antigravitycli/ +/artifacts/iqpilot_alc_private/python/iqpilot_private/konn3kt/iqlvbs/_iqcrypto.cpython-312-aarch64-linux-gnu.so +/artifacts/iqpilot_alc_private/python/iqpilot_private/konn3kt/iqlvbs/alc_handshake.cpython-312-aarch64-linux-gnu.so +/artifacts/iqpilot_alc_bundle.json +/iqpilot/selfdrive/iqlocd/iqlocd +/iqpilot/selfdrive/iqlocd/models/generated/car.cpp +/iqpilot/selfdrive/iqlocd/models/generated/car.h +/tools/scripts/debug/ +!scripts/iqpilot/iqos_usrlocal_larch64.tar.zst + +# pretty-build capture logs (model compile / mpc + dbc codegen stdout+stderr) +*_tinygrad.pkl.log +*_metadata.pkl.log +selfdrive/controls/lib/*_mpc_lib/gen.log +tools/cabana/dbc/*.json.log +iqpilot/selfdrive/iqlocd/models/generated/ +osmaps_build/ + +# --- local scratch/debug + large model artifacts (never commit) --- +/infinite_driver/ +/konn3kt_private/ +/_pq*.py +/bus_topo.py +/cluster_bus.py +/real_cluster_bus.py +/dbg_big.py +/decode_*.py +/dtc_decoder.py +/eps_decode.py +/extract_nm_*.py +/ffff_map.py +/kmatrix_*.py +/mcd_*.py +/shot_*.py +/verify_crc.py +/CODEX_HANDOFF_*.md +/SUNNYPILOT_REMEDIATION_PLAN.md +/prompt.md +/iqos44_bluetooth_gamepad_spec.md +tools/mac_egpu/*.pkl +selfdrive/ui/tests/test_ui/nav_demo_report/ +**/__pycache__/ +*.pyc +*.mlpackage/ + + +# fetcher: closed-source precompiled component — ship only the obfuscated .so +/iqpilot/models_private_src/fetcher.py + +/tools/iqmacvisiond/macos/dist/ diff --git a/.lfsconfig-comma b/.lfsconfig-comma new file mode 100644 index 000000000..42dfa2d94 --- /dev/null +++ b/.lfsconfig-comma @@ -0,0 +1,4 @@ +[lfs] + url = https://gitlab.com/commaai/openpilot-lfs.git/info/lfs + pushurl = ssh://git@gitlab.com/commaai/openpilot-lfs.git + locksverify = false diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 000000000..484d284d0 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,230 @@ +# IQ.Pilot User Changelog + +This changelog is written for everyday drivers and focuses on what you will notice on the road, as well as changes under-the-hood. + +## IQ.Pilot 1.0c + + +**Navigate on IQ.Pilot** + +Navigate on IQ.Pilot is here. Search for a destination, pick from route alternatives, and let IQ.Pilot guide you turn by turn with live rerouting when you miss an exit. Along with on-screen-maps, you can see your position, the route, and upcoming turns all at once without leaving the driving view. Speed, turn, and highway exit handling are route-aware, so IQ.Pilot knows what's coming before you do. Mapbox is included at no cost to you for enhanced online map data and routing. On supported vehicles, Navigate on IQ.Pilot will command turn signals automatically based on the route. Map Curve speed control pulls limit data from OpenStreetMaps when offline and Mapbox when available. + +When approaching a highway exit, IQ.Pilot now initiates the lane change toward the exit. If your car has Blind Spot Monitoring (BSM), it can perform the lane change fully automatically, there's no blinker nudge required. Without BSM, you confirm with a brief blinker push and IQ.Pilot takes it from there. + +**Speed Limit Control (SLC)** + +IQ.Pilot can now read and act on speed limits from your car's dash, Mapbox, TomTom, HERE, (included at no cost to our users!) and offline maps. You pick what mode you want in settings: display only, warn you when you're over, or actually adjust your cruise speed. You also pick which source wins when they disagree (dash, Mapbox, map data, highest, or lowest reported limit). There's a look ahead setting so IQ.Pilot can start reacting to an upcoming speed change before you hit the sign to decelerate to the limit before crossing into the new speed limit. + +SLC can now also raise your cruise speed automatically when the speed limit increases, not just lower it. Toggle "confirm higher speed limit" off to enable this, SLC will adjust up to a higher accepted limit with a small prompt, without any confirmation input needed. + +**Camera Alerts (Speed Cameras, Red Light Cameras, and Surveillance / ALPR Cameras)** + +IQ.Pilot now detects upcoming speed cameras, red light cameras, and ALPR/surveillance cameras (including Flock Safety cameras) sourced from OpenStreetMap's and alerts you before you reach them. Each camera type has its own toggle so you can pick what you want to be warned about. Speed cameras can also trigger a speed reduction to the limit when detected if enabled. Camera data is sourced from OSM and is updated periodically. + +**Direct Flock / ALPR Camera Detection (Bluetooth & WiFi)** + +Beyond map data, IQ.Pilot can now spot Flock Safety and similar ALPR cameras directly over the air by their Bluetooth and WiFi signatures as you approach them. Because it's sensing the actual hardware rather than relying on a map, this works anywhere, including fully offline and even for cameras that haven't been mapped yet, so you get a heads-up the moment one is nearby. When IQ.Pilot picks up a camera directly, that live detection takes priority over map data, so you see a single clear "Flock Camera Detected" alert instead of a duplicate. It shares the same Flock camera alert toggle, runs quietly in the background only when that's enabled, and is built to stay out of the way of your Bluetooth (phone link, game controllers) and WiFi connections. + +**IQ.Dynamic and Driving Behavior** + +In IQ.Dynamic blended mode, when IQ.Pilot sees a stop light ahead, and the model agrees you need to stop, and there's no lead car to track, it will now commit (force) to stopping on its own, no lead car required. Gas pedal overrides it instantly. The stop prediction horizon is adjustable in IQ.Dynamic settings. Behavior for curves, low-speed driving, stopped leads, speed-limit fallbacks, and vision-based stops is now configurable. On-device IQ.Dynamic tuning is accessible by double-tapping IQ.Dynamic in longitudinal mode selection. + +Force Stops now include "Smooth Stops" under the same toggle thank's to SpysyWeeb! With Force Stops on, all stops including model predicted stops at signs and lights now use the smooth landing law, so every stop settles gently rather than dropping in hard. The minimum force stop distance slider enforces minimum distance when configured. + +IQ.Dynamic on supported Volkswagen platforms including MQB, and PQ now support blending OEM Stock Radar ACC with IQ.Dynamic to allow for a blended longitudinal experience while maintaining E2E IQ.Pilot functionality. + +**Driving Models** + +IQ.Pilot updated to a new default driving model, `Pop!` + +IQ.Pilot also has the latest bleeding-edge models, as always, including the latest TobyRL model, NoPP model, DeeperRL model, DeepRLv3/4/5 models, OP Model 16 Deep, and all future RL models as they are released. + +IQ.Pilot maintains supports for all legacy models like `Notre Dame (v1/3)`, `FarmVille`, `WD40`, etc. + +**Dashcam, Live View, and Alerts** + +- WebSSH now connects in under 15 seconds and connects the first time, every time. +- Live View in the Konn3kt app now processes full-resolution HDR input from the Comma 4 driver camera for a noticeably sharper, and more accurate picture. Live View performance was optimized, and microphone audio (one way) streaming is now included. +- You can fully disable dashcam recording from the Konn3kt app. Turning it off stops all recording, no logs, no video, no audio, full stop. +- Audible alerts now ramp in volume smoothly instead of cutting in abruptly for enhanced auditory alerts. +- On-road live streaming, with Two-Way Audio streaming from your IQ.Pilot devices camera feed live through the Konn3kt app. Onroad choppiness fixed, keyframe-on-demand enabled for instant camera switches, and variable network-condition-adaptive bitrate. +- Konn3kt can now take a snapshot from any camera (road, driver, or wide) on-demand, both onroad and offroad, and returns it as a JPEG instantly for a glance. + +**Volkswagen** + +Volkswagen support got a significant overhaul: + +- Lateral and longitudinal tuning greatly improved. +- Accelerator override behavior now matches stock feel. +- MQB SnG handling improved for supported non-EPB ACC FtS vehicles. +- IQ.Pilot now supports all VW PQ and MQB CC-only and (A)CC-less cars, including CC-only PQ cars without an ADAS gateway. +- Volkswagen Passat B7 (PQ) with TRW450 now supports Stop-and-Go. +- Volkswagen MEB/MQBevo now only go on-road when in Drive and no longer in Park for 15 minutes after parking the car. +- Added Passat (PQ) model year ECU fingerprint. +- Konn3kt can now check EPS compatibility and LKAS coding status on VW MQB vehicles with a comma power. +- Konn3kt can enable LKAS coding on supported VW MQB vehicles with EPS that didn't ship with factory LKAS with a comma power. +- Volkswagen MQB/PQ now supports full Radar Blending with IQ.Dynamic for an enhanced E2E + Highway experience. (limited by OEM ACC minimum speed) + +**Volkswagen MEB and MQBevo** + +IQ.Pilot now **officially** supports the Volkswagen MEB and MQBevo platforms! Including the ID.4, ID.3, ID.5, and Golf MK8 through model year 2025, as long as you have a compatible camera or gateway harness. Both LKAS and ACC are supported. This is the foundation of the `release-meb` branch, which is auto-synced from `release` and tailored specifically for these platforms. + +**Toyota/Lexus** + +Support added for Stop-and-Go and SDSU for Toyota/Lexus vehicles. + +**Hyundai/Kia** + +Fingerprint coverage expanded to cover more Hyundai/Kia variants that were previously unrecognized, and proper CAN-FD handling for newer HKG. + +**UI — Comma 4 (mici)** + +The Comma 4's offroad UI has been completely redesigned and has had major performance optimizations, it contains the same settings that BIG UI contains on Comma 3x/3 devices. + +**UI — Comma 3x and Comma 3 (tizi/tici)** + +The tizi/tici (Comma 3x and Comma 3) onroad and offroad UI has been fully redesigned as well, matching the new IQ.Pilot design language with a brand new home screen, status bar, settings menu's, and should be a much better experience. + +**UI Improvements** + +IQ.Pilot's on-road UI got a number of improvements: + +- The Steering Assistance border now has a lower portion that distinguishes lateral only engagement from full engagement. +- IQLong Personality can now be cycled on-road by tapping the driver-monitoring icon on BIG UI devices. The icon color reflects the currently selected personality as well as an on screen current profile confirmation. +- IQLong mode IQ.Standard has been renamed to IQ.Chill to lessen confusion on long modes. +- IQLong mode can now be cycled on-road by tapping the nucleus icon in the top right corner on BIG UI devices. The icon changes to reflect IQ.Chill/Dynamic/Pilot. +- Fixed augmented road view calibration showing invalid calibration data on the model path / tracked lane lines by refreshing the matrix cache correctly for calibration to properly update on startup with Navigation enabled. +- Live Konn3kt accent color sync: whatever color you pick in the Konn3kt app flows to your device UI instantly. +- Revamped Branch switcher to properly switch branches, and updater has had bugfixes to fix install issues where the device claims to have updated but has not actually updated. + + +**IQ.OS 4.9.1** + +IQ.OS 4.9.1 is bundled with IQ.Pilot 1.0c. IQ.OS is available for all supported devices: Comma 3, Comma 3x, Comma 4, Konik A1/M, and Mr.One C3/C3 Lite. It's a lightweight OS based on Ubuntu 24.04, includes Bluetooth (BLE), has a much smaller install footprint, and is continuously optimized for IQ.Pilot. + +- Konn3kt now stays online regardless of IQ.Pilot's status. If IQ.Pilot fails to boot, Konn3kt remains available so you can switch branches, SSH in, and recover remotely without a physical connection, including over cellular. +- Bug causing konn3kt setup time on a fresh install dropped from ~30 minutes fixed, setup dropped down to ~10 seconds. +- Automatic LocalAPI configuration in Konn3kt. +- Fixed Upstream Comma 4/3x/3 AGNOS Wi-Fi driver crash causing random crashes while driving. +- Fixed Comma 4 green-dot-matrix text aliasing issue. +- Fixed Comma 4 display calibration not showing for accurate colors. + +**Updater: Pre-download Mode** + +A new "Update Install Mode" setting in Software settings gives you control over how updates are applied: + +- **Predownload Only** — updates download in the background but wait for you to confirm before installing. +- **Predownload + Preinstall** — downloads and installs automatically on next boot, as it was before. + +**Konn3kt Services** + +Konn3kt's WebApp has migrated to `app.konn3kt.com` + +Connection stability between Konn3kt and IQ.Pilot (Konn3ktion) is greatly improved. + +**eSIM** + +eSIM detection, provisioning, and profile management groundwork is now built into the device. The app can detect whether your device has an embedded SIM, provision it, and manage profiles without a physical SIM swap. eSIM support requires a compatible data plan; Contact IQ.Pilot support in the discord for known working eSIM carriers and plans. Note: eSIM is experimental and generally requires a hotspot-style data plan or an MVNO that does not IMEI filter. + +**Additional 1.0c Updates** + +**Navigate on IQ.Pilot and Offline Maps** + +- Added Home, Work, and Recent destination shortcuts, route cancellation, saved-destination management, and course-up map rotation. +- Added an interactive off-road map with panning and current-location display, plus on-screen maneuver artwork for turns, merges, forks, off-ramps, continuation instructions, and arrival. +- Added navigation-memory handling so important fork and exit guidance remains available when a model output briefly omits it. +- Added full offline routing through a packaged Valhalla runtime. Navigation can now operate without Mapbox or an active internet connection. +- Added simultaneous installation of multiple offline regions, including offline raster tiles for the displayed map as well as routing and road metadata. +- Added separate Online On-Screen Maps and Offline On-Screen Maps controls, resumable regional downloads, combined routing-and-tile download progress, automatic mapd v2 region recognition and missing-data restoration. +- Added a hosted regional tile-bundle service with a fallback source, background tile decoding, and bounded texture caching to keep map work off the UI render path. +- Reduced navigation CPU, GPU, and memory overhead so maps can remain open for long drives without competing with the driving model. + +**IQ Speed Assist and Camera Alerts** + +- Added the new IQ Speed Assist architecture, with TomTom data alongside dashboard, Mapbox, and offline OpenStreetMap sources. +- Added optional cruise-set-speed mirroring, percentage-based offset zones for low, medium, and higher speed ranges, and earlier, smoother upcoming-speed-limit reactions in the longitudinal cruise envelope. +- Added clearer on-road SLC source, state, pending-limit, and adaptation feedback. +- Added individual controls for speed, red-light, and Flock/ALPR alerts, plus optional speed-camera slowdown using the detected limit and a configurable safety factor. +- Added haptic speed-camera feedback on supported Hyundai, Kia, and Genesis vehicles. + +**Construction Zone Assist** + +- Added optional camera-based Construction Zone Assist, which detects bright-orange work-zone barrels and markers. +- Added an adjustable work-zone target speed (60 mph by default), plus daylight, road-speed, and active-zone checks to reduce unrelated reflective detections. +- Added lightweight processing designed to run alongside the driving and driver-monitoring models. + +**IQ.Dynamic, Stops, and Longitudinal Control** + +- Added separate IQ.Dynamic road-speed and lead-speed thresholds, configurable curve, low-speed, slower-lead, stopped-lead, and model-predicted-stop activation, and an adjustable model-stop prediction horizon. +- Added automatic Volkswagen stock-radar set-speed and following-gap synchronization when radar blending is enabled on supported PQ vehicles. +- Added an independent custom stopping-distance adjustment, smoother stopped-lead pull-away, and end-to-end launch assistance when the model predicts an opening path. +- Added dedicated departure chimes for an opening path and a pulling-away lead, Experimental Lead MPC, and end-to-end cruise convergence toward the selected speed as the road opens. +- Added configurable end-to-end set-speed selection using either a preset or the driver’s current target, per-platform stopping overrides, accelerator-override longitudinal control on supported vehicles, and predictive reactions to speed limits and curves. + +**Steering, Lane Changes, and Environment View** + +- Added an on-road Always-On Lateral control path for supported Hyundai LFA buttons and Bluetooth-controller commands for testing or controlling it in Joystick Mode. +- Added continuous lane changes while the blinker is held, while retaining driver confirmation for subsequent maneuvers. +- Added configurable model-action smoothing, angle-based lateral control, optional VW ALC torque blending, a dedicated Volkswagen PQ HCA7 torque controller, and live-learned curvature correction for supported Volkswagen MEB configurations. +- Added an optional Environment View that can replace the model scene and render 3D object boxes, a ground grid, lane lines, road edges, and the planned path. +- Added a precompiled Qualcomm IQ.Vision model and bandwidth-efficient camera preprocessing to reduce contention with the primary driving model. + +**Driving Models and IQModeld** + +- Added the unified IQModeld runtime and native bridge for current combined models and legacy split models. +- Added fused vision-and-policy execution for supported supercombo bundles, zero-copy camera-frame handling through the tinygrad runner, and unified combined-artifact, combined-split, fused, tinygrad, and ONNX runner support. +- Added current-model redownload, manifest refresh when published models change, a clear Driving Model Updating state, and engagement gating until the selected model is ready. + +**Home, On-Road UI, and Visuals** + +- Added dedicated Routes, Navigation, Video, and status surfaces, a selectable home-panel widget, 60 fps off-road BIG UI presentation, and device-specific frame pacing. +- Added Comma 4 anti-aliasing, augmented-road presentation, driver-camera orientation improvements, and an expanded status bar with temperature, vehicle state, Konn3kt status, and connected Wi-Fi network name. +- Added the installed IQ.OS version to Software settings, tappable branch information in the BIG UI header, smooth edge-swipe navigation, transitions, parallax, shadows, animated controls, and a seamless build-spinner-to-UI transition. +- Added a glowing primary-lead orb, IQ.Pilot teal and pink acceleration-bar styling, a top-bar Silent Mode bell, Night Mode display sleep after sunset, and screen recording through Konn3kt. + +**Dashcam and Route Viewer** + +- Increased qcamera resolution by 5× while balancing storage and upload use. +- Added crash-safe route writes, frequent durable checkpoints, power-loss preservation, and boot-time recovery for incomplete recordings. +- Added an on-device Routes screen with date, duration, camera availability, cloud-upload state, and local, uploading, uploaded, and cloud-only badges. +- Added local playback of road, wide, and driver video with synchronized audio; cloud-only route discovery and streaming through Konn3kt; and synchronized telemetry, model path, steering angle, driver-monitoring, vehicle speed, and cruise-set-speed overlays. +- Added qlog-only route visualization, fullscreen playback, auto-hiding controls, loading feedback, scrubbing, fast-forward speeds, hardware HEVC decode, local-time presentation, bounded playback buffering, and screen-awake behavior during playback. + +**Live View, Audio, and WebSSH** + +- Added a Live View indicator on the driving screen, dual-camera picture-in-picture streaming, and live model-path overlay while both the device and Konn3kt use cellular. +- Added instant keyframe requests, network-adaptive encoder bitrate, and full-resolution HDR driver-camera input on Comma 4. + +**Konn3kt Services, Bluetooth, and Backup** + +- Added independent device services that stay available when IQ.Pilot is stopped or cannot enter its main UI, including OS-level recovery over Wi-Fi and cellular. +- Added a dedicated `iquploaderd` route-and-log uploader, automatic IQ.Pilot crash-log uploads, Volkswagen and Tesla odometer display, backup-and-restore services, encrypted backup archives, backup status, and Konn3kt RPC support for compatible Volkswagen coding and diagnostics. +- Added direct Bluetooth Low Energy control across comma 3, comma 3X, comma 4, Konik, and Mr.One hardware, including authenticated requests, replay protection, settings synchronization, automatic discovery and pairing, and fallback when internet connectivity is unavailable. +- Added BLE control for supported IQ.Pilot, vehicle, display, network, navigation, and driving settings; live propagation to active services; and setup-stage Wi-Fi scanning, Wi-Fi connection, network status, channel selection, install start, setup progress, and installation status. + +**Volkswagen PQ and MQB** + +- Added Always-On Lateral for ECAN, non-ECAN/ACAN, CC-only, and CC-less PQ configurations; SEAT Alhambra TRW460i Stop-and-Go with automatic resume; and Stop-and-Go for additional electronic-parking-brake configurations. +- Added MQB-A0 automatic resume and steering-lockout options, standstill handling for supported non-EPB ACC vehicles, and ACC-command suppression while braking. +- Added dedicated PQ radar engagement, cancellation, set-speed, acceleration, and following-gap management; automatic PQ EPS-patch detection; and patch-aware minimum-steering-speed handling. +- Added a PQ firmware tool with backup, dump upload, patch detection, programming, and recovery; persistent PQ LKAS coding over TP2.0/KWP; model-year ECU fingerprinting; expanded Passat identification; distance-button mapping; dashboard personality presentation; and continued MQB lateral control while cruise is faulted. +- Added stock lateral handoff when IQ.Pilot lateral is inactive, explicit Volkswagen readiness handling, and Volkswagen odometer persistence with cloud backup. + +**Platform Support** + +- Added MEB/MQBevo steering ratios, steering behavior, zero-speed steering where supported, ACC-HUD handling, and Drive/Park state handling. Supported configurations include ID.3, ID.4, ID.5, and Golf Mk8 through model year 2025. +- Added Honda-specific final-stopping deceleration control, Subaru Creep from Standstill, and guarded Tesla vehicle-bus parsing for supported harnesses. +- Added more Hyundai/Kia/Genesis CAN-FD and HDA2 handling, radar-track and corner-radar controls, Camera SCC and platform-specific cruise controls, Auto Cruise Control and Auto Engage, custom steering and steering-rate controls, lane-change steering-rate controls, and expanded parameter/fingerprint diagnostics. + +**IQ.OS, Power, Network, and Recovery** + +- Updated the bundled platform to IQ.OS 4.9.1, with device-specific boot and display support, Comma 4 display calibration, HDR camera and display-color support, USB 3 logging, USB link-error recovery, 2 GB compressed zram swap, and consistent startup runtime packaging. +- Added FastSleep deep standby, including five-minute idle entry after parking, low-voltage entry, staged voltage shutdown thresholds, suspension of high-power services, Konn3kt recovery connectivity during standby, voltage-aware power decisions, and immediate wake on ignition or charging. +- Added zero-touch Bluetooth onboarding, Konn3kt pairing-code presentation, IQ.OS update confirmation, Bluetooth game-controller support for Joystick Mode, and Bluetooth operation independent of the main IQ.Pilot process. +- Added a redesigned Network settings experience, explicit Wi-Fi Disconnect, cellular reconnection after APN changes, Comma 3X SIM electrical-probe recovery, and QR/manual eSIM activation with profile listing, refresh, enable, disable, and deletion. +- Added Force On-Road for a temporary ten-minute parked diagnostic session, an Update & Reboot recovery-screen action, and USB Storage mode. + +**Updater, Reliability, and Performance** + +- Added Konn3kt confirmation before IQ.OS updates, interrupted submodule/artifact-preparation recovery, precompiled release preparation for comma 3, comma 3X, and comma 4, and crash-screen update-and-reboot without entering the full UI. +- Added independent model, navigation, map, uploader, backup, and perception services; navigation/map recovery that does not block engagement; power-state coordination; startup parameter ownership and permission normalization; message-queue dead-reader cleanup; lower shared-memory use; and clearer build, process-state, and diagnostic output. diff --git a/LICENSE b/LICENSE new file mode 100644 index 000000000..16c9b47c9 --- /dev/null +++ b/LICENSE @@ -0,0 +1,107 @@ +IQ.Lvbs License v0.1a + +Copyright (c) 2026 IQ.Lvbs LLC, a part of Project Teal Lvbs Inc. All Rights Reserved. + +DEFINITIONS + +"Software" refers to IQ.Pilot, konn3kt, and all associated source code, +documentation, and assets owned by the Copyright Holder. + +"Open Components" refers to portions of the Software explicitly marked as +open source. + +"Proprietary Components" refers to all portions of the Software not made +available to the public in source form. + +"Copyright Holder" refers to IQ.Lvbs LLC, a part of Project Teal Lvbs Inc. + +GRANT OF LICENSE + +Subject to the terms of this license, you are granted a limited, +non-exclusive, revocable license to: + +1. View, study, and learn from the Open Components +2. Modify the Open Components for personal, internal, or open-source public use +3. Run the Software for personal, non-commercial purposes + +RESTRICTIONS + +You may NOT: + +1. Claim ownership of any part of the Software, excluding your own + modifications that do not incorporate Proprietary Components. + +2. Reverse engineer, decompile, disassemble, or in any way attempt to + circumvent the obfuscation of the Proprietary Components. + +3. Use the Software or any derivative for commercial purposes without + explicit written permission from the Copyright Holder. + +4. Remove or alter any copyright notices or this license. + +5. Sublicense, sell, or transfer rights to the Software. + +6. Use the Software and/or its source code to compete with or create a + substantially similar product. + +7. Use the Software in closed source software not licensed by IQ.Lvbs LLC. + +CONSEQUENCES OF VIOLATION + +In the event any Restriction is violated, any product created using inspiration from, or source code from, IQ.Pilot or Konn3kt shall be subject to a licensing fee determined solely by the Copyright Holder. Additionally, the violating party hereby grants IQ.Lvbs LLC an exclusive, irrevocable, worldwide, royalty-free license to use any and all assets from the infringing product on IQ.Lvbs webpages, advertising materials, and in any other manner IQ.Lvbs sees fit. + +OWNERSHIP + +All rights, title, and interest in the Software remain exclusively with the +Copyright Holder. Any modifications, improvements, or derivative works you +create based on the Software are owned by the Copyright Holder. By +contributing modifications, you irrevocably assign all rights to the +Copyright Holder. + +PROPRIETARY COMPONENTS + +The Proprietary Components are provided in binary or obfuscated form only. +Reverse engineering, decompilation, or disassembly of Proprietary Components +is strictly prohibited. Violation of this provision entitles IQ.Lvbs LLC to +pursue all available legal remedies to protect its intellectual property and +trade secrets. + +NO WARRANTY + +THE SOFTWARE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND. THE COPYRIGHT +HOLDER DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT +LIMITED TO MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND +NON-INFRINGEMENT. + +LIMITATION OF LIABILITY + +IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES, OR +OTHER LIABILITY ARISING FROM THE USE OF THE SOFTWARE. THE USER ACCEPTS FULL +RESPONSIBILITY FOR ANY AND ALL LIABILITIES WHEN USING IQ.LVBS SOFTWARE. + +TERMINATION + +This license terminates automatically if you violate any of its terms. Upon +termination, you must destroy all copies of the Software in your possession. + +The Copyright Holder reserves the right to revoke this license at any time +for any reason. + +GOVERNING LAW + +This license shall be governed by the laws of the State of Illinois, United +States of America. Any disputes arising under this license shall be subject +to the exclusive jurisdiction of the courts located in The State of Illinois, United States. + +--- + +For commercial licensing inquiries, contact: support@iqlvbs.com + +IQ.Pilot is a fork of Stock OpenPilot maintained by Comma.ai, Inc. licensed under the following notice: +Copyright (c) 2018, Comma.ai, Inc. + +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: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +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. diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 000000000..b6e8c82d2 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,107 @@ +IQ.Lvbs License v0.1a + +Copyright (c) 2026 IQ.Lvbs LLC, a part of Project Teal Lvbs Inc. All Rights Reserved. + +DEFINITIONS + +"Software" refers to IQ.Pilot, konn3kt, and all associated source code, +documentation, and assets owned by the Copyright Holder. + +"Open Components" refers to portions of the Software explicitly marked as +open source. + +"Proprietary Components" refers to all portions of the Software not made +available to the public in source form. + +"Copyright Holder" refers to IQ.Lvbs LLC, a part of Project Teal Lvbs Inc. + +GRANT OF LICENSE + +Subject to the terms of this license, you are granted a limited, +non-exclusive, revocable license to: + +1. View, study, and learn from the Open Components +2. Modify the Open Components for personal, internal, or open-source public use +3. Run the Software for personal, non-commercial purposes + +RESTRICTIONS + +You may NOT: + +1. Claim ownership of any part of the Software, excluding your own + modifications that do not incorporate Proprietary Components. + +2. Reverse engineer, decompile, disassemble, or in any way attempt to + circumvent the obfuscation of the Proprietary Components. + +3. Use the Software or any derivative for commercial purposes without + explicit written permission from the Copyright Holder. + +4. Remove or alter any copyright notices or this license. + +5. Sublicense, sell, or transfer rights to the Software. + +6. Use the Software and/or its source code to compete with or create a + substantially similar product. + +7. Use the Software in closed source software not licensed by IQ.Lvbs LLC. + +CONSEQUENCES OF VIOLATION + +In the event any Restriction is violated, any product created using inspiration from, or source code from, IQ.Pilot or Konn3kt shall be subject to a licensing fee determined solely by the Copyright Holder. Additionally, the violating party hereby grants IQ.Lvbs LLC an exclusive, irrevocable, worldwide, royalty-free license to use any and all assets from the infringing product on IQ.Lvbs webpages, advertising materials, and in any other manner IQ.Lvbs sees fit. + +OWNERSHIP + +All rights, title, and interest in the Software remain exclusively with the +Copyright Holder. Any modifications, improvements, or derivative works you +create based on the Software are owned by the Copyright Holder. By +contributing modifications, you irrevocably assign all rights to the +Copyright Holder. + +PROPRIETARY COMPONENTS + +The Proprietary Components are provided in binary or obfuscated form only. +Reverse engineering, decompilation, or disassembly of Proprietary Components +is strictly prohibited. Violation of this provision entitles IQ.Lvbs LLC to +pursue all available legal remedies to protect its intellectual property and +trade secrets. + +NO WARRANTY + +THE SOFTWARE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND. THE COPYRIGHT +HOLDER DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT +LIMITED TO MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND +NON-INFRINGEMENT. + +LIMITATION OF LIABILITY + +IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES, OR +OTHER LIABILITY ARISING FROM THE USE OF THE SOFTWARE. THE USER ACCEPTS FULL +RESPONSIBILITY FOR ANY AND ALL LIABILITIES WHEN USING IQ.LVBS SOFTWARE. + +TERMINATION + +This license terminates automatically if you violate any of its terms. Upon +termination, you must destroy all copies of the Software in your possession. + +The Copyright Holder reserves the right to revoke this license at any time +for any reason. + +GOVERNING LAW + +This license shall be governed by the laws of the State of Illinois, United +States of America. Any disputes arising under this license shall be subject +to the exclusive jurisdiction of the courts located in Henry County, Illinois. + +--- + +For commercial licensing inquiries, contact: support@iqlvbs.com + +IQ.Pilot is a fork of Stock OpenPilot maintained by Comma.ai, Inc. licensed under the following notice: +Copyright (c) 2018, Comma.ai, Inc. + +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: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +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. diff --git a/README.md b/README.md new file mode 100644 index 000000000..6380bdc7e --- /dev/null +++ b/README.md @@ -0,0 +1,63 @@ +# IQ.Pilot: + + + +## Join Our Public Beta at: https://discord.iqlvbs.com + +## Running IQ.Pilot +* A modern comma, or clone device to run this software (Comma 3, 3x, 4, Konik A1/M, Mr.One C3, C3 Lite) +* One of [the supported cars](https://gitlvb.teallvbs.xyz/IQ.Lvbs/IQ.Pilot/src/branch/release/iqdbc_repo/docs/CARS.md). +* A [car harness](https://comma.ai/shop/products/car-harness) to connect to your car +#### Wondering if IQ.Pilot supports your car? IQ.Pilot supports every car [stock openpilot](https://gitlvb.teallvbs.xyz/IQ.Lvbs/IQ.Pilot/src/branch/release/iqdbc_repo/docs/CARS.md) supports! +## Installation +#### Installing Via Installer URL: +#### Enter the following into your device custom URL box to install IQ.Pilot: +`installer.iqlvbs.com/release` +#### Having Trouble? If your device is currently running AGNOS 13.1 or older, you should install latest stock openpilot, then install IQ.Pilot, or try one of the alternative methods listed below! + + +## Alternative Methods of Installation: +#### Enabling SSH: +* In your device's settings, go into "Developer Settings" +* Enable the "Enable SSH" toggle if it is not already on. +* Next to "SSH Keys", click on "Add" and then enter your GitHub username. +* Run the command below (replace your_email@example.com with your GitHub account email, then paste the output [here](https://github.com/settings/keys) after clicking on "New SSH Key", then reboot your comma device. +#### SSH Key Command: `ssh-keygen -t ed25519 -C "your_email@example.com" -f ~/.ssh/id_ed25519 -N "" && cat ~/.ssh/id_ed25519.pub` +#### Note: If you have already setup SSH and can SSH into your device, skip this section. + +#### Installing Via SSH: +#### Once you are connected to your device via SSH, you can paste the following command below to install IQ.Pilot: +`cd .. && rm -rf openpilot && git clone https://git.konn3kt.com/IQ.Lvbs/IQ.Pilot openpilot -b release && cd openpilot && sudo reboot` +#### If you'd like to backup your previous installation as well, paste the following command below to install IQ.Pilot: +`cd .. && mv openpilot openpilot_backup_X && git clone https://git.konn3kt.com/IQ.Lvbs/IQ.Pilot -b release && cd openpilot && sudo reboot` +#### Alternatively, you can use your existing fork's built in tools to switch your branch as well: +`git remote add iqpilot https://git.konn3kt.com/IQ.Lvbs/IQ.Pilot && op switch iqpilot release` + +--- + +## Releases + +| Branch | Status | Description | +|---|---|---------------------------------------------------------------------------------------------------------------------------------------------------| +| `beta` | Pre-release | Preview of the next release in progress. Expect bugs. Use it if you want early access, just know things can break. | +| `release` | **Stable 1.0c** | Current stable production release of IQ.Pilot. | +| `release-meb` | **Stable 1.0c** | Up to date with `release`, built specifically for VW MEB and MQBevo platform vehicles (ID.4, ID.3, ID.5, Golf MK8, Tiguan/Atlas 2024+, and more). | + +--- +#### Side Note: Beta's are releases of IQ.Pilot that are released publically for beta testing by the IQ.Pilot team, expect bugs, and unstable behavior, for stability please use release. + +## 📊 User Data +### IQ.Pilot uploads your data to Konn3kt, by IQ.Lvbs. +#### Konn3kt is the most secure, encrypted, feature rich management platform for your IQ.Pilot device. Konn3kt has dual end-to-end encryption, your data is encrypted in transit, and at rest, you, and only you have access to your device, and your data. + +#### IQ.Pilot allows users to disable uploading entirely if they wish. +#### Konn3kt encrypts your logs, and video data with dual end-to-end encryption, ensuring that your data is accessible to you, and only you, not even accessible to IQ.Lvbs. + +## Terms of Service / Privacy Policy / Licensing +#### IQ.Pilot is subject to the License found in this repository, [Terms of Service](https://konn3kt.com/tos), and, [Privacy Policy](https://konn3kt.com/privacy). + + +## Support IQ.Pilot? +Sorry, I have better things to do than ask my users for donations. - Teal + +- IQ.Lvbs, by Project Teal Lvbs diff --git a/RELEASES.md b/RELEASES.md new file mode 100644 index 000000000..04bd2f483 --- /dev/null +++ b/RELEASES.md @@ -0,0 +1,7 @@ +IQ.Pilot 1.0c +======================== +* Navigation;) + +IQ.Pilot 1.0b +======================== +* Major Refactor! diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 000000000..2dd09dbbb --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,52 @@ +# Security Policy + +## Supported Versions + +| Component | Version | Supported | +| --------- | ------- | --------- | +| IQ.Pilot | latest | TRUE | +| konn3kt | latest | TRUE | + +## Scope + +This security policy covers the following components, but applies to all IQ.Lvbs software: + +- **IQ.Pilot** +- **konn3kt** + +## Reporting a Vulnerability + +If you discover a security vulnerability in IQ.Pilot or konn3kt, please report it responsibly, abuse is against the terms of service for IQ.Lvbs software. + +**Email:** security@iqlvbs.com + +Please include: + +- A description of the vulnerability and its impact, and potential impact +- Steps to reproduce the issue +- Affected component(s) +- Any relevant logs, screenshots, or proof of concept code that can assist with reproduction. + +## Response Timeline + +- **Acknowledgment:** Within 168 hours of receipt +- **Initial Assessment:** Within 1-14 days +- **Resolution Target:** Depends on severity; critical issues are prioritized immediately + +## Responsible Disclosure + +We ask that you: + +- Allow reasonable time for us to investigate and address the issue. +- Avoid accessing or modifying other users' data, this is against IQ.Lvbs policy, and illegal in many jurisdictions. +- Do not exploit vulnerabilities beyond what is necessary to demonstrate the issue. + +## Out of Scope + +- Issues in third-party dependencies unless the vulnerability is triggered by our specific usage + +## Recognition + +We appreciate and recognize security researchers who help keep IQ.Lvbs software safe. With your permission, we will credit you in our release notes when a reported vulnerability is fixed. + +- IQ.Lvbs, a part of Project Teal Lvbs Inc ©️ diff --git a/TOOLS_UPSTREAM_SYNC_PLAN.md b/TOOLS_UPSTREAM_SYNC_PLAN.md new file mode 100644 index 000000000..f701c70f3 --- /dev/null +++ b/TOOLS_UPSTREAM_SYNC_PLAN.md @@ -0,0 +1,433 @@ +# Upstream sync plan — `tools/cabana` and `tools/jotpluggler` + +Hand-port the current upstream (commaai/openpilot) versions of Cabana and jotpluggler into +iqpilot without losing konn3kt integration. iqpilot does not use comma connect: every route +list, route-file listing, JWT and live-CAN path goes through `api-iqlabs.konn3kt.com`, and the +DBC source is `iqdbc`, not `opendbc_repo`. + +Reference clone used for this analysis: `/tmp/openpilot-upstream` @ `e7b1ee3a5` +("jp: fix segment range parsing", 2026-08-05). + +--- + +## 1. Where we actually are + +Baselines were established by blob-matching every iqpilot file against upstream history, not by +reading commit messages. + +| Tool | iqpilot baseline | Upstream HEAD | Gap | +|---|---|---|---| +| `tools/cabana` | `bcdeec313` (2025-12-16, #36884) | `e7b1ee3a5` (2026-08-05) | **53 commits, ~8 months** | +| `tools/jotpluggler` | `61608db78` (2026-07-15, #38353) | `e7b1ee3a5` | **6 commits, ~3 weeks** | +| `tools/plotjuggler` | `a46ff01ca` (2026-01-19) | `e7b1ee3a5` | 14 commits (secondary scope) | + +Cabana is the real work. jotpluggler was hand-ported in May–July 2026 (`4b68073c4`, `b26218e85`, +`2cd89a198`) and is nearly current — most of its files are **byte-identical** to upstream HEAD. + +A critical structural fact: upstream moved the whole tree under a nested `openpilot/` package +(`#38219`/`#38220`/`#38223`, 2026-06-21) and moved the HAL to `common/hardware/` (`#38202`). +iqpilot has **not** adopted either. Every ported file needs a de-prefix pass: + +- `#include "openpilot/cereal/..."` → `"cereal/..."` +- `#include "common/hardware/hw.h"` → `"system/hardware/hw.h"` +- `#include "json11/json11.hpp"` → `"third_party/json11/json11.hpp"` +- `repo_root() / "openpilot" / "tools" / ...` → `repo_root() / "tools" / ...` +- SCons nodes `#openpilot/common`, `#openpilot/cereal` → `#common`, `#cereal` +- `opendbc_repo/opendbc/dbc` → `iqdbc/dbc`; `#opendbc_repo` → `#iqdbc_repo` + +--- + +## 2. konn3kt invariants — the contract the port must not break + +These are the only genuinely IQ-owned surfaces. Everything else in both trees is upstream code we +are behind on. **Anything not on this list should be taken from upstream verbatim** (modulo §1). + +### Cabana + +| File | What is IQ-owned | Disposition | +|---|---|---| +| `utils/api.{cc,h}` | `CommaApi::BASE_URL = API_HOST ?: https://api-iqlabs.konn3kt.com`; RSA-signed device JWT; Qt `HttpRequest` | **Delete** — fold into `tools/replay/api.{cc,h}` (§3.A) | +| `streams/routes.{cc,h}` | konn3kt device list + route listing | Rewrite on upstream HEAD shape, konn3kt endpoints | +| `konn3kt_canproxy.py` | `canlived → konn3kt relay → local ZMQ → Cabana` proxy | Keep; must stay wired to the Device/ZMQ stream (§3.C) | +| `SConscript` | iqdbc paths, non-nested paths, `PrettyAction`, extra libs | Rebase onto upstream, re-apply IQ deltas | +| `mainwin.cc` | "Load DBC from commaai/**iqdbc**" menu | Re-apply rename only | +| `dbc/generate_dbc_json.py` | `from iqdbc.car import ...` | Re-apply rename only | +| `assets/assets.cc` (tracked) | bootstrap-icons LFS bypass | See §3.K | +| `dbc/car_fingerprint_to_dbc.json` (+`.log`) | tracked build product | Keep tracked | +| `.gitignore`, `README.md` | binary name, iqdbc, konn3kt route examples | Re-apply | + +### jotpluggler + +All IQ deltas are already carrying `// IQ.Pilot patch:` comments — preserve that convention. + +| File | IQ patch | +|---|---| +| `SConscript` | `.venv` site-packages injection; `iqdbc.dbc.generator.generator.create_all()` instead of upstream `get_generated_dbcs()`; no `bootstrap_icons` module; explicit `avformat/avcodec/avutil/x264/yuv/z/bz2/zstd/curl/ssl/crypto` + `OpenCL`/`va`/`va-drm`/`drm`; `PrettyAction`; guards `imgui.MESA_DIR` existence | +| `icons.cc` | vendored `tools/jotpluggler/assets/bootstrap-icons.ttf` (third_party/bootstrap is LFS, TTF not checked in) | +| `common.cc` | iqpilot `common/util.h` has no `check_system`; inline `std::system` + stderr | +| `runtime.cc` | no `common/yuv.h`; vendored libyuv lacks `NV12ToABGR` → `NV12ToARGB` + `ARGBToABGR` | +| `sketch_layout.cc` | `PyDownloader::getRouteFiles` → `CommaApi2::httpGet(BASE_URL + "/v1/route//files")`; `androidLog` (cereal not renamed); iqdbc paths | +| `app.{cc,h}`, `layout.cc`, `layout_io.cc`, `map.cc`, `custom_series.cc` | path/include de-prefixing; `LogOrigin::Android`; iqdbc | +| `dbc.h` | empty-multiplex guard (`mux.empty() ? 0 : std::stoi(mux)`) — a genuine bugfix, **upstream this** | +| `generate_event_extractors.py` | `getBusTimeDEPRECATED()` (iqpilot cereal keeps a flat field, not upstream's `deprecated :group`) | +| `pluggle.py` | iqpilot-only launcher (upstream has none) | + +--- + +## 3. Conflict matrix — upstream change vs konn3kt + +### A. File downloading / API moved from C++ to Python (`#37497`, `#38430`) — **highest impact** + +Upstream cabana HEAD calls `PyDownloader::getDevices()`, `getDeviceRoutes()`, `getRouteFiles()` +(`tools/replay/py_downloader.h`), which shell into `openpilot/tools/lib/`. iqpilot's replay has +**no** `py_downloader`; it keeps a C++/libcurl path in `tools/replay/api.{cc,h}` +(`CommaApi2::BASE_URL`, `create_token(use_jwt)`, `httpGet`). jotpluggler already bridges this gap +in `sketch_layout.cc` — **reuse that pattern**, don't invent a second one. + +Resolution: extend `tools/replay/api.{cc,h}` with + +``` +std::string getDevices(); // GET /v1/me/devices/ +std::string getDeviceRoutes(dongle_id, start_ms, end_ms, preserved); + // GET /v1/devices//routes_segments?start=&end= + // GET /v1/devices//routes/preserved +std::string getRouteFiles(route); // GET /v1/route//files (already reachable via httpGet) +``` + +returning the same JSON shapes upstream's `PyDownloader` returns, then port upstream's `routes.cc` +verbatim with `PyDownloader::` → `CommaApi2::`. Upstream's HEAD `routes.cc` is already Qt-free +(json11 + `strptime`/`strftime` for the preserved-route ISO timestamps) — that code ports as-is. + +Auth semantics to preserve: `create_token(!Hardware::PC())` — device RSA-JWT on device, +`~/.comma/auth.json` `access_token` on a PC. Upstream's Python path only ever does the latter. + +Consequence: `tools/cabana/utils/api.{cc,h}` (Qt `QNetworkAccessManager` + duplicate JWT code) +**gets deleted**, which drops `Qt5Network` from the cabana link and converges with §3.B. + +### B. de-Qt: `#37519`, `#37521`, `#37522`, `#37523`, `#38357`, `#38359`, `#38360` + +The bulk of the 53-commit gap. `QString`→`std::string` through the DBC core, new Qt-free +`core/{can_data,color,message_id,settings}.h`, Qt/core split in `dbc/dbcqt.{cc,h}`, QtXml dropped +(bootstrap SVG symbol extraction hand-rolled in `utils/util.cc`), QtConcurrent dropped, QtSerialBus +dropped (SocketCAN now raw `linux/can` sockets, `#37553` excludes it on macOS). + +**Zero konn3kt content.** Pure upstream adoption. It is also why almost every cabana file shows a +large diff — those are not IQ changes, they are upstream changes we lack. + +Net effect on `SConscript`: `qt_modules` goes from +`["Widgets","Gui","Core","Network","Concurrent","DBus","Xml"]` → `["Widgets","Gui","Core"]`, +and `Qt5SerialBus` / `QtSerialBus` framework drop out. + +### C. `--zmq` bridge path (`#38484`) — **breaks konn3kt_canproxy if ported blind** + +Upstream HEAD `DeviceStream::start()` forks `cereal/messaging/bridge /"can/"` and +`streamThread()` then always subscribes on `127.0.0.1`. `bridge ` is +`zmq_to_msgq` (see `cereal/messaging/bridge.cc:60`) — it ZMQ-subscribes from `` and republishes +to local msgq. + +iqpilot's documented remote-CAN workflow is the opposite direction: +`konn3kt_canproxy.py` sets `ZMQ=1` and **publishes** capnp `Event` frames on a local ZMQ `can` +socket; the user then picks *Live → Device → ZMQ → 127.0.0.1*. Under upstream HEAD semantics that +forks a bridge which subscribes to the same loopback endpoint the proxy publishes on and +republishes into msgq, while cabana reads ZMQ — the proxy path stops being the thing cabana reads. + +Resolution: keep the pre-`#38484` direct-attach semantics as an explicit IQ escape hatch. Port +upstream's bridge-forking code, but subscribe to `zmq_address` directly (no bridge fork) when the +stream is started in "attach to an existing ZMQ publisher" mode — either by treating loopback +addresses as attach-mode, or by adding a third radio button next to MSGQ/ZMQ. Mark it +`// IQ.Pilot patch:` and state why (konn3kt_canproxy). **This must be re-verified end-to-end on a +real device before the sync is called done** (§5.3). + +### D. libyuv removed, `common/yuv.h` added (`#38306`) + +jotpluggler already patches around this. Cabana's `cameraview.cc` does NV12 conversion too. +Decision: port `common/yuv.h` into iqpilot once, then **delete** the `runtime.cc` IQ patch and take +upstream's `cameraview.cc` verbatim. One-time cost, removes a recurring patch site. + +### E. Vendored native deps via `comma-deps-*` wheels (`#37327`, `#37994`, `#37681`, `#38308`) + +Upstream `SConstruct` now exports `ffmpeg_libs` and pulls capnproto/ffmpeg/zstd/zeromq/json11/ +libusb/imgui/bootstrap-icons from pip wheels. iqpilot pins `imgui`/`libusb` from +`git.konn3kt.com/IQ.Lvbs/dependencies`, vendors json11 in `third_party/json11`, and has **no** +`bootstrap_icons` module and **no** `ffmpeg_libs` export. + +Resolution: add an `ffmpeg_libs` list + `Export` to iqpilot's `SConstruct` mirroring upstream's +(`avformat avcodec swresample avutil` [+ `x264 z` [+ `va va-drm drm`]]) so both tool SConscripts can +take upstream's form nearly verbatim instead of hand-listing libs. Keep `third_party/json11`. +Do **not** chase the wheels. + +### F. `androidLog` → `operatingSystemLog` (`#38209`) + +iqpilot cereal still has `androidLog`. Keep the existing `sketch_layout.cc` / `app.h` IQ patch. +Renaming cereal is out of scope (it is a schema change with device-side blast radius). + +### G. Tests: catch2 dropped (`#38408`), unittest conversion (`#38387`, `#38384`) + +Upstream deleted `tools/cabana/tests/test_runner.cc` and split a deliberately Qt-free +`tests/test_dbc_core` target (objects built from the **base** env, linking no Qt — it exists +specifically to stop Qt creeping back into the DBC core). Adopt that shape; drop `test_runner.cc`. +jotpluggler gains `test_jotpluggler.py`. + +### H. `cabana` launcher script + `_cabana` binary (`#37814`, `c02cf706a`, `05cf8023a`) + +Upstream builds `Program('_cabana')` and ships `tools/cabana/cabana` as a bash launcher that +installs Qt if missing and runs `scons -u openpilot/tools/cabana/_cabana openpilot/cereal/messaging/bridge`. +iqpilot builds `Program('cabana')` — so `tools/cabana/cabana` is currently a **committed binary**. +Adopting upstream's split means: `.gitignore` `cabana` → `_cabana`, and the launcher's scons +targets de-prefixed to `tools/cabana/_cabana cereal/messaging/bridge`. + +### I. `assets.cc` / bootstrap icons (`#37994`, `71290f380`, `15267e408`) + +Upstream generates `assets/assets.cc` at build time (`rcc` over an `assets.generated.qrc` that +interpolates the packaged bootstrap-icons SVG path) and gitignores it. iqpilot tracks the generated +14.9k-line `assets.cc` because the icons package isn't available. + +Recommended: vendor the bootstrap-icons **SVG** next to the already-vendored TTF +(`tools/jotpluggler/assets/bootstrap-icons.ttf`), add a tiny local shim module exposing +`SVG_PATH`/`TTF_PATH`, adopt upstream's generation, and untrack `assets.cc`. This also lets +jotpluggler's `icons.cc` IQ patch collapse back to upstream's `BOOTSTRAP_ICONS_TTF` define. +Acceptable fallback: keep tracking `assets.cc` and keep both patches. + +--- + +## Status + +**Phases 0–2 are done and landed.** Both tools are at upstream `e7b1ee3a5` (2026-08-05) with +konn3kt preserved. Departures from the plan as written, and what remains: + +- §3.I resolved better than planned: `third_party/bootstrap/bootstrap-icons.svg` turned out to be + **tracked already**, so upstream's build-time embedding was adopted as-is — no vendored SVG, and + the 14.9k-line generated `assets.cc` is gone (it was never tracked; it was a gitignored build + product). +- §3.D done both ways: `common/yuv.{cc,h}` ported, and `tools/replay/framereader.cc` moved off + libyuv too, so the jotpluggler `runtime.cc` patch disappeared entirely. +- Unplanned dependency found during the port: upstream's `#38430` also added download/decompress/ + parse instrumentation to `LogReader`, which jotpluggler's load-stats panel reads. Ported into + iqpilot's `LogReader` around its own FileReader/decompress steps (so decompress is timed + separately, unlike upstream where the Python downloader does it inline). +- §3.C landed as a **three-mode** `DeviceStream` (`Msgq` / `Zmq` / `Bridge`) rather than an + escape hatch. Upstream's ZMQ path forks a bridge that republishes to **msgq** while still + setting `ZMQ=1` and subscribing over **ZMQ** — that combination reads nothing. Keeping the modes + explicit preserves upstream's bridge convenience *and* the direct attach `konn3kt_canproxy.py` + needs, and fixes that inconsistency. `--bridge ` is a new flag. +- Also de-comma'd beyond the plan: jotpluggler's "Useradmin" / "comma connect" buttons became a + single **konn3kt** link (`KONN3KT_APP_HOST`, default `https://konn3kt.com`, same + `/` path shape `tools/lib/logreader.py` already parses). + +### Phase 3 verification actually performed (macOS arm64, live konn3kt) + +- **konn3kt API, live, real credentials** — every endpoint the port builds returns 200 with real + data: `/v1/me/devices/` (2 devices), `/v1/devices//routes_segments?start=&end=` (1776 + routes), `/v1/devices//routes/preserved` (16), `/v1/route//files` (48 logs / 48 qlogs + / 48 qcameras). +- **Real route in the real GUI** — `_cabana --qcam 0f53129ed44f6920|000001a5--07db1da02e` loaded + **48 valid segments** and came up as a foreground Cocoa app (not offscreen). +- **Live CAN over the ZMQ attach** — a local publisher mimicking `konn3kt_canproxy.py` fed + `_cabana --zmq 127.0.0.1`; cabana ingested **2179 CAN frames** and wrote them to + `~/cabana_live_stream/.../rlog`. This is the exact path `#38484` would have broken. +- **Fixed while verifying:** `utils::icon()` painted a null QPixmap whenever an empty icon id was + requested (`ToolButton("")` in `chartswidget.cc`), logging two `QPainter` warnings on every + dark-theme macOS start. Pre-existing upstream bug, not a port regression; guarded here and worth + upstreaming alongside the `dbc.h` mux fix. + +**Still outstanding:** +- **Phase 3.3 (device half):** the konn3kt relay accepts the websocket and authorizes the stream, + but `0f53129ed44f6920` emits no frames because `canlived` only runs while the `CanLiveStreaming` + param is set. Needs a device with that enabled to close the loop. +- **Phase 3.5:** cabana has not been built for larch64. +- Charts/video widgets were not visually inspected — `screencapture` is blocked by macOS + screen-recording permission for the terminal, so GUI rendering is evidenced by process state and + the ingested-frame log rather than by a screenshot. + +Tests added: `tools/replay/tests/test_api.cc` (konn3kt endpoint paths + error envelope), +`tools/cabana/test_cabana_konn3kt.py`, `tools/jotpluggler/test_jotpluggler.py`, and +`tools/test_tools_local_route.py` (synthesizes a local route and drives both tools headlessly). + +## 4. Execution plan + +Land each phase as its own commit. Build after every sub-step — do not batch §4.2. + +### Phase 0 — infrastructure (no tool code) + +0.1 `SConstruct`: add and `Export` `ffmpeg_libs`. +0.2 Port `common/yuv.h`; drop the `runtime.cc` libyuv patch. +0.3 Vendor bootstrap-icons SVG + local `bootstrap_icons` shim (`SVG_PATH`, `TTF_PATH`). +0.4 `tools/replay/api.{cc,h}`: add `getDevices()`, `getDeviceRoutes()`, `getRouteFiles()` against + the konn3kt endpoints, matching upstream `PyDownloader` JSON shapes. + +**Gate:** `scons -u -j8 tools/replay` clean; a scratch binary lists real devices from +`api-iqlabs.konn3kt.com` with both a device JWT and a PC `auth.json` token. + +### Phase 1 — jotpluggler (6 commits; do first, it validates the whole pattern cheaply) + +1.1 `3f49e2d33` thumbnail source — add `thumbnail.{cc,h}`; `common.h` `kSpecialItemSpecs` 5→6; + `common.cc` `PaneKind::Thumbnail`; `layout_io.cc` `"thumbnail"`; `app.{cc,h}` `ThumbnailView` + + `ThumbnailFrame`; `session.cc` `setThumbnails`. Re-apply the json11/path/`Android` patches. +1.2 `576de9c7e` build speedup — take upstream `generate_event_extractors.py` **wholesale** + (iqpilot's copy has diverged: `event_base_slots`, `static_enums`, single-use scalar getters) + and re-apply only the `getBusTimeDEPRECATED()` patch. +1.3 `e7b1ee3a5` segment-range parsing — empty-begin guard in `sketch_layout.cc`. +1.4 `6b47a5b6b` decompress-in-downloader — iqpilot has no `PyDownloader`; confirm the replay-side + decompression path is unaffected (expected no-op). +1.5 `fef29ad22`/`911f07ee8` — add `test_jotpluggler.py`. + +**Gate:** `scons -u -j8 tools/jotpluggler`; `./tools/jotpluggler/pluggle.py ` opens, +thumbnails render, CAN series decode against iqdbc, a saved layout round-trips. + +### Phase 2 — cabana (53 commits). Bottom-up, one layer per build. + +2.1 **DBC core** — `dbc/{dbc,dbcfile,dbcmanager}.{cc,h}`, new `core/{can_data,color,message_id,settings}.h`, + new `dbc/dbcqt.{cc,h}`. Re-apply iqdbc rename in `dbc/generate_dbc_json.py`. +2.2 **Streams** — `abstractstream`, `livestream`, `replaystream`, `pandastream`, `socketcanstream` + (raw `linux/can`, macOS-excluded), `devicestream`. Re-apply the §3.C canproxy escape hatch. +2.3 **Routes** — port upstream HEAD `streams/routes.{cc,h}`, swap `PyDownloader::` → `CommaApi2::`. + **Delete `utils/api.{cc,h}`.** +2.4 **Widgets** — `binaryview`, `historylog`, `videowidget`, `signalview`, `detailwidget`, + `messageswidget`, `mainwin` (re-apply the iqdbc menu label), `streamselector`, `commands`, + `settings`, `cameraview`, `chart/*`, `tools/*`, `utils/{util,export,elidedlabel}`. +2.5 **SConscript** — rebase on upstream; re-apply: non-nested paths, `iqdbc/dbc` + + `#iqdbc_repo`, `PrettyAction` wrappers, `third_party/json11`, `ffmpeg_libs` from Phase 0.1. + Qt modules collapse to `Widgets/Gui/Core` (+Charts). +2.6 **Tests** — adopt the Qt-free `tests/test_dbc_core` target; drop `tests/test_runner.cc`. +2.7 **Launcher** — adopt `cabana` bash script + `Program('_cabana')`; de-prefix its scons targets; + fix `.gitignore` (`cabana` → `_cabana`, `*.generated.qrc`, `bootstrap_icons.cc`). + +**Gate (each sub-step):** `scons -u -j8 tools/cabana` clean, no new warnings. + +### Phase 3 — konn3kt verification (the part that actually proves the sync) + +3.1 Remote-routes dialog against `api-iqlabs.konn3kt.com`: device list populates; all five periods + (7d / 14d / 30d / 6mo / **Preserved**) return routes with correct start times and durations. + Preserved uses ISO timestamps, the rest use `*_utc_millis` — both paths must be exercised. +3.2 Open a konn3kt route end-to-end: route-file listing, log download + decompression, qcam/fcam + video, iqdbc DBC load, chart, CSV export. +3.3 **Remote live CAN:** `canlived` on device → konn3kt relay → `konn3kt_canproxy.py` on laptop → + Cabana *Live → Device → ZMQ → 127.0.0.1*. Frames must decode. Per prior work this is the + untested leg of the canlive feature — run it on `gutek-a1`. +3.4 Local live CAN: panda stream, MSGQ device stream, SocketCAN (Linux only). +3.5 larch64 device build, if cabana is still expected to build on-device. + +### Phase 4 — plotjuggler (secondary, 14 commits) + +Only `juggle.py`, `layouts/`, `README.md` diverge, and the only IQ delta is the `iqdbc.car.fingerprints` +import plus non-nested paths. Cheap; fold in after Phase 3 or skip. + +--- + +## 5. Risks and rules + +- **Don't batch Phase 2.** ~40 files change; a single "port everything then build" pass produces an + unbisectable wall of link errors. One layer, one build. +- **`git add` only ported files.** The tree currently contains built artifacts (`*.o`, `*.a`, + `moc_*.cc`, `tools/cabana/cabana`, `tools/jotpluggler/jotpluggler`, + `generated_event_extractors.h`, `generated_dbcs/`). Never `git add -A` here. +- `dbc/car_fingerprint_to_dbc.json` is a tracked build product regenerated by scons — expect churn + on every build; keep it tracked so cabana doesn't need iqdbc importable at runtime. +- Dropping `Qt5Network/Concurrent/DBus/Xml/SerialBus` changes the larch64 link set; verify the + device build before assuming the SConscript is done. +- §3.C is the one place where a faithful upstream port actively regresses a konn3kt feature. If + Phase 3.3 can't be run on hardware in this pass, **do not** land 2.2 with upstream's bridge fork + as the only ZMQ path. +- Upstream candidate: the `dbc.h` empty-multiplex-indicator guard is a real upstream bug fix worth + sending back. + +## 6. Explicit non-goals + +- Adopting upstream's nested `openpilot/` tree layout. +- Migrating iqpilot's replay to `PyDownloader` / `tools/lib` Python downloading. +- Renaming cereal `androidLog` → `operatingSystemLog`. +- Converging on the `comma-deps-*` pip wheels. + +--- + +## Appendix A — cabana commits to port (`bcdeec313..e7b1ee3a5`, 53) + +``` +4cfdcea1d 2026-07-28 cabana: fix macOS build and --zmq bridge path (#38484) <- §3.C +6b47a5b6b 2026-07-23 tools: decompress in the python downloader (#38430) <- §3.A +75d590fb9 2026-07-21 replace catch2 tests for 20% faster builds (#38408) <- §3.G +e124d6df9 2026-07-19 more dead code gc +a04c045cd 2026-07-17 cabana: de-Qt, part 3 (#38360) <- §3.B +5d23a78c7 2026-07-16 cabana: de-Qt, part 2 (#38359) <- §3.B +06a73f538 2026-07-16 cabana: de-Qt, part 1 (#38357) <- §3.B +39117a587 2026-07-08 remove submodule symlinks (#38312) +f283f6703 2026-07-08 Revert "try removing submodule symlinks (#38310)" +b827c0f55 2026-07-08 try removing submodule symlinks (#38310) +1e49eac4d 2026-07-08 rm libyuv (#38306) <- §3.D +9877f6ac0 2026-07-08 ffmpeg: use shared libraries (#38308) <- §3.E +05cf8023a 2026-07-07 fix cabana <- §3.H +c02cf706a 2026-07-07 fix cabana launch script <- §3.H +5edc0bd89 2026-06-21 mv root dirs into nested openpilot (#38219) <- §1 (de-prefix) +20e0f21b5 2026-06-21 prefix paths with openpilot (#38223) <- §1 (de-prefix) +37eda06c9 2026-06-21 move cereal/ into nested openpilot (#38220) <- §1 (de-prefix) +bfd8d4868 2026-06-06 cabana: fix "seperated" typo in findsignal placeholders (#38142) +5408c86b7 2026-05-28 Cabana: Fixed internal typos and method casing (#38099) +15267e408 2026-05-11 cabana: gitignore generated file <- §3.I +bea893820 2026-05-10 use packaged bootstrap icons (#37994) <- §3.I +bd1c7f39e 2026-05-07 scons build cleanups (#37981) +0584a5f5e 2026-04-12 add bridge target to cabana run script (#37814) <- §3.H +31e4fe55a 2026-03-22 tools: setup ffmpeg hwaccel (#37718) +a8b5c7450 2026-03-21 prep for imgui tools (#37712) +240e0036d 2026-03-20 macOS: fix build (#37686) +a68ea44af 2026-03-14 cabana: use vendored libusb from commaai/dependencies (#37681) <- §3.E +5e7f5dd84 2026-03-14 replay/cabana: remove unused openssl dependency (#37680) +ee9da82aa 2026-03-13 cleanup build paths (#37667) +71290f380 2026-03-08 cabana: gitignore assets.cc <- §3.I +e42ee228c 2026-03-08 gitignore cleanups (#37615) +5e1a576f3 2026-03-07 cabana: exclude SocketCAN on macOS (#37553) <- §3.B +0c452dbaf 2026-03-03 cabana: fix right pane width limitation (#37527) +06b2c68e0 2026-03-01 macOS: fix cabana builds (#37518) +3478ac133 2026-03-01 cabana: remove QtSerialBus (#37523) <- §3.B +ce04d25f7 2026-03-01 cabana: remove QtConcurrent (#37522) <- §3.B +0c7abf385 2026-03-01 cabana: remove QtXml (#37521) <- §3.B +0b9ab8bb9 2026-03-01 cabana: replace Qt types with stdlib (#37519) <- §3.B (largest) +885658512 2026-02-28 new demo route (#37457) +e7cc70f3f 2026-02-28 consolidate file downloading from C++ to Python (#37497) <- §3.A +276713ddf 2026-02-27 add back bz2 support with vendored bzip2 (#37459) +238fca233 2026-02-25 tools: fix darwin compile errors (#37399) +8810948ec 2026-02-24 CI: ensure no brew (#37387) +76d084d87 2026-02-23 switch to system compilers (GCC on Linux, Apple Clang on macOS) (#37355) +f4a36f7f7 2026-02-22 rm cpp bz2 (#37332) +4bffe422e 2026-02-22 vendor capnproto and ffmpeg via dependencies repo (#37327) <- §3.E +c98ba4ff4 2026-02-20 Qt is optional (#37295) +037e6e749 2026-02-17 cabana: fix crash when zmq address is used (#37222) +af1583cdf 2026-02-12 Reapply tgwarp w NV12 fix (#37168) +45099e7fc 2026-02-10 Revert tgwarp again (#37161) +667f3bb32 2026-02-07 Revert "revert tg calib and opencl cleanup (#37113)" (#37115) +51312afd3 2026-02-07 revert tg calib and opencl cleanup (#37113) +d5cbb89d8 2026-02-06 Remove all the OpenCL (#37105) +``` + +## Appendix B — jotpluggler commits to port (`61608db78..e7b1ee3a5`, 6) + +``` +e7b1ee3a5 2026-08-05 jp: fix segment range parsing +6b47a5b6b 2026-07-23 tools: decompress in the python downloader (#38430) +576de9c7e 2026-07-21 speed up jotpluggler build (#38406) +911f07ee8 2026-07-21 convert tests to unittest (#38387) +fef29ad22 2026-07-19 start porting tests to unittest style (#38384) +3f49e2d33 2026-07-18 jp: add thumbnail source (#38363) +``` + +## Appendix C — how the baselines were established + +Blob-hash matching, not commit archaeology: for each iqpilot file, walk upstream history +newest-first and report the newest commit whose blob for that path is byte-identical. Files with no +match are IQ-modified or IQ-new; the minimum over the untouched files bounds the fork point. + +``` +cd /tmp/openpilot-upstream +git log --format='%H %ad %s' --date=short -30 -- openpilot/tools/cabana tools/cabana > /tmp/cab_commits.txt +for f in $(cd $LOCAL/tools/cabana && find . -type f \( -name '*.cc' -o -name '*.h' \) -not -name 'moc_*' | sed 's|^\./||' | sort); do + h=$(git hash-object "$LOCAL/tools/cabana/$f") || continue + while read -r c d rest; do + b=$(git rev-parse "$c:openpilot/tools/cabana/$f" 2>/dev/null || git rev-parse "$c:tools/cabana/$f" 2>/dev/null) + [ "$b" = "$h" ] && { echo "$f -> ${c:0:9} $d $rest"; break; } + done < /tmp/cab_commits.txt +done +``` + +Cross-checked against the last upstream-authored commits in iqpilot's own history +(`bcdeec313 Reduce pub-sub memory usage by 10x (#36884)` for cabana) and against feature markers +(`virtual std::string routeName` first appears in `0b9ab8bb9`, 2026-03-01 — absent from iqpilot, +confirming the fork predates it). diff --git a/artifacts/__init__.py b/artifacts/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/artifacts/iqpilot_alc_private/manifest.json b/artifacts/iqpilot_alc_private/manifest.json new file mode 100644 index 000000000..fb42059df --- /dev/null +++ b/artifacts/iqpilot_alc_private/manifest.json @@ -0,0 +1,43 @@ +{ + "python/iqpilot_private/__init__.py": { + "mode": 420, + "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "size": 0 + }, + "python/iqpilot_private/konn3kt/__init__.py": { + "mode": 420, + "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "size": 0 + }, + "python/iqpilot_private/konn3kt/iqlvbs/__init__.py": { + "mode": 420, + "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "size": 0 + }, + "python/iqpilot_private/konn3kt/iqlvbs/alc.cpython-312-aarch64-linux-gnu.so": { + "mode": 493, + "sha256": "16990383c7ba1d2f3d33e5e4c1746019101bc79bed867721b51de853a68002ed", + "size": 401688 + }, + "python/iqpilot_private/konn3kt/iqlvbs/vehicle_state.cpython-312-aarch64-linux-gnu.so": { + "mode": 493, + "sha256": "bd060a8527d21501ce866f24d055f1c8bb03209b2730eb745b786780aace0aae", + "size": 69688 + }, + "runtime": { + "entries": { + "iqpilot_private.konn3kt.iqlvbs.alc": { + "path": "python/iqpilot_private/konn3kt/iqlvbs/alc.cpython-312-aarch64-linux-gnu.so", + "type": "python-module" + }, + "iqpilot_private.konn3kt.iqlvbs.vehicle_state": { + "path": "python/iqpilot_private/konn3kt/iqlvbs/vehicle_state.cpython-312-aarch64-linux-gnu.so", + "type": "python-module" + } + } + }, + "signatures": { + "python/iqpilot_private/konn3kt/iqlvbs/alc.cpython-312-aarch64-linux-gnu.so": "IVz01J4HpdUHDrjPP9yB+9ksGFberB9MeFiCNLcv8hMzXCLIjIATl3JXTwpBqU8Kad/q683nkhgL5IJ3tCKdCw==", + "python/iqpilot_private/konn3kt/iqlvbs/vehicle_state.cpython-312-aarch64-linux-gnu.so": "/aW/StEMRpge+zzihQDK7VPLmKhgwCucX8JielvPnPTud/mk0pmCy5pE8fB+TbwtWZCBfVhEd1bidRgsBqZfBA==" + } +} diff --git a/artifacts/iqpilot_alc_private/python/iqpilot_private/__init__.py b/artifacts/iqpilot_alc_private/python/iqpilot_private/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/artifacts/iqpilot_alc_private/python/iqpilot_private/konn3kt/__init__.py b/artifacts/iqpilot_alc_private/python/iqpilot_private/konn3kt/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/artifacts/iqpilot_alc_private/python/iqpilot_private/konn3kt/iqlvbs/__init__.py b/artifacts/iqpilot_alc_private/python/iqpilot_private/konn3kt/iqlvbs/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/artifacts/iqpilot_alc_private/python/iqpilot_private/konn3kt/iqlvbs/alc.cpython-312-aarch64-linux-gnu.so b/artifacts/iqpilot_alc_private/python/iqpilot_private/konn3kt/iqlvbs/alc.cpython-312-aarch64-linux-gnu.so new file mode 100755 index 000000000..ffc616041 Binary files /dev/null and b/artifacts/iqpilot_alc_private/python/iqpilot_private/konn3kt/iqlvbs/alc.cpython-312-aarch64-linux-gnu.so differ diff --git a/artifacts/iqpilot_alc_private/python/iqpilot_private/konn3kt/iqlvbs/vehicle_state.cpython-312-aarch64-linux-gnu.so b/artifacts/iqpilot_alc_private/python/iqpilot_private/konn3kt/iqlvbs/vehicle_state.cpython-312-aarch64-linux-gnu.so new file mode 100755 index 000000000..c86c5cda4 Binary files /dev/null and b/artifacts/iqpilot_alc_private/python/iqpilot_private/konn3kt/iqlvbs/vehicle_state.cpython-312-aarch64-linux-gnu.so differ diff --git a/artifacts/iqpilot_commander_private/manifest.json b/artifacts/iqpilot_commander_private/manifest.json new file mode 100644 index 000000000..e2776727b --- /dev/null +++ b/artifacts/iqpilot_commander_private/manifest.json @@ -0,0 +1,33 @@ +{ + "python/iqpilot_private/__init__.py": { + "mode": 420, + "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "size": 0 + }, + "python/iqpilot_private/konn3kt/__init__.py": { + "mode": 420, + "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "size": 0 + }, + "python/iqpilot_private/konn3kt/iqlvbs/__init__.py": { + "mode": 420, + "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "size": 0 + }, + "python/iqpilot_private/konn3kt/iqlvbs/iqlvbs_commander.cpython-312-aarch64-linux-gnu.so": { + "mode": 493, + "sha256": "384571df8380e5eef92806f3fdb6c9d1bc879af2ebdce9259bcfc486d506a4d0", + "size": 68080 + }, + "runtime": { + "entries": { + "iqpilot_private.konn3kt.iqlvbs.iqlvbs_commander": { + "path": "python/iqpilot_private/konn3kt/iqlvbs/iqlvbs_commander.cpython-312-aarch64-linux-gnu.so", + "type": "python-module" + } + } + }, + "signatures": { + "python/iqpilot_private/konn3kt/iqlvbs/iqlvbs_commander.cpython-312-aarch64-linux-gnu.so": "cH88ob+s+ZRJaNgbrRN2CQqCiQMBlo9yXdBvN3gteSC7OYlJ5z8OZND7eZSEfeeq2O7E8ZeZDs9WDU0WfP8DCw==" + } +} diff --git a/artifacts/iqpilot_commander_private/python/iqpilot_private/__init__.py b/artifacts/iqpilot_commander_private/python/iqpilot_private/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/artifacts/iqpilot_commander_private/python/iqpilot_private/konn3kt/__init__.py b/artifacts/iqpilot_commander_private/python/iqpilot_private/konn3kt/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/artifacts/iqpilot_commander_private/python/iqpilot_private/konn3kt/iqlvbs/__init__.py b/artifacts/iqpilot_commander_private/python/iqpilot_private/konn3kt/iqlvbs/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/artifacts/iqpilot_commander_private/python/iqpilot_private/konn3kt/iqlvbs/iqlvbs_commander.cpython-312-aarch64-linux-gnu.so b/artifacts/iqpilot_commander_private/python/iqpilot_private/konn3kt/iqlvbs/iqlvbs_commander.cpython-312-aarch64-linux-gnu.so new file mode 100755 index 000000000..e93281f7b Binary files /dev/null and b/artifacts/iqpilot_commander_private/python/iqpilot_private/konn3kt/iqlvbs/iqlvbs_commander.cpython-312-aarch64-linux-gnu.so differ diff --git a/artifacts/iqpilot_hephaestusd_private/manifest.json b/artifacts/iqpilot_hephaestusd_private/manifest.json new file mode 100644 index 000000000..80bdbbd09 --- /dev/null +++ b/artifacts/iqpilot_hephaestusd_private/manifest.json @@ -0,0 +1,195 @@ +{ + "python/iqpilot_private/__init__.py": { + "mode": 420, + "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "size": 0 + }, + "python/iqpilot_private/konn3kt/__init__.py": { + "mode": 420, + "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "size": 0 + }, + "python/iqpilot_private/konn3kt/backups/__init__.py": { + "mode": 420, + "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "size": 0 + }, + "python/iqpilot_private/konn3kt/backups/archive_codec.cpython-312-aarch64-linux-gnu.so": { + "mode": 493, + "sha256": "e79be61e50cd0830197f44f8a6840b86a74b8059da45da65a4696587168ac466", + "size": 135536 + }, + "python/iqpilot_private/konn3kt/backups/backup_keys.cpython-312-aarch64-linux-gnu.so": { + "mode": 493, + "sha256": "ebfeedba6cc5cac6dca1cf8125879fcb2d6afdbaec31a98dd46c3d346f55c982", + "size": 67664 + }, + "python/iqpilot_private/konn3kt/backups/backup_orchestrator.cpython-312-aarch64-linux-gnu.so": { + "mode": 493, + "sha256": "11ffe8f261b78990aa743b43aed3a5534105da848be1450026090ae873ad0a2e", + "size": 204624 + }, + "python/iqpilot_private/konn3kt/backups/cbc_vault.cpython-312-aarch64-linux-gnu.so": { + "mode": 493, + "sha256": "e62a6438c18b231f83cc873ac8170a64e670c8912ffba13edf47f163886aef19", + "size": 69752 + }, + "python/iqpilot_private/konn3kt/backups/imahelper.cpython-312-aarch64-linux-gnu.so": { + "mode": 493, + "sha256": "07195ca6d4e1ff26441e860d954e9c79e3da608244d110bfa62b3aa0818a79dc", + "size": 203072 + }, + "python/iqpilot_private/konn3kt/flockd/__init__.py": { + "mode": 420, + "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "size": 0 + }, + "python/iqpilot_private/konn3kt/flockd/flockd.cpython-312-aarch64-linux-gnu.so": { + "mode": 493, + "sha256": "d18b8d67100f5906339dde577bc14f567168ff62e1b9166c011af853c016191e", + "size": 202168 + }, + "python/iqpilot_private/konn3kt/flockd/signatures.cpython-312-aarch64-linux-gnu.so": { + "mode": 493, + "sha256": "1a601a0de826fb41fadb3cef9e8b8645d99e41507befc36e87d077d49efd3025", + "size": 68080 + }, + "python/iqpilot_private/konn3kt/hephaestus/__init__.py": { + "mode": 420, + "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "size": 0 + }, + "python/iqpilot_private/konn3kt/hephaestus/_vendor/localapi_runtime.zip": { + "mode": 420, + "sha256": "c3063f3d380627f604b0ae1cdd20fbfdfa7b64d212c86f20abd470230af8e169", + "size": 2100238 + }, + "python/iqpilot_private/konn3kt/hephaestus/ble_auth.cpython-312-aarch64-linux-gnu.so": { + "mode": 493, + "sha256": "57dbd02e130361d33c8a52635261ce85f3a91c577b70d5fad8a0e9e7557f9d46", + "size": 268064 + }, + "python/iqpilot_private/konn3kt/hephaestus/ble_gatt.cpython-312-aarch64-linux-gnu.so": { + "mode": 493, + "sha256": "1febeeb24421c82a64416a7ab04def21d57a39acf0caaf2103b4ebfbae655fc2", + "size": 406216 + }, + "python/iqpilot_private/konn3kt/hephaestus/ble_rpc_dispatch.cpython-312-aarch64-linux-gnu.so": { + "mode": 493, + "sha256": "42656c3b54f6392e2311e4039296e4906558fd8c63ba6be04b157445b96d38c9", + "size": 68048 + }, + "python/iqpilot_private/konn3kt/hephaestus/ble_transportd.cpython-312-aarch64-linux-gnu.so": { + "mode": 493, + "sha256": "acdf51e90428e4487f0daf6e0e43110ef5fc1c7705d16aa3b9880618030f41f3", + "size": 335728 + }, + "python/iqpilot_private/konn3kt/hephaestus/bt_gamepad.cpython-312-aarch64-linux-gnu.so": { + "mode": 493, + "sha256": "95afe64579cac0148c3de23867a333651dfed4606c218208dcf37b40a1cc6450", + "size": 271680 + }, + "python/iqpilot_private/konn3kt/hephaestus/cloud_routes.cpython-312-aarch64-linux-gnu.so": { + "mode": 493, + "sha256": "fd983039accfaa30abf3d46f41c70241d1c3fcf4e21a1de7d3270ab9fce9b03c", + "size": 134408 + }, + "python/iqpilot_private/konn3kt/hephaestus/hephaestusd.cpython-312-aarch64-linux-gnu.so": { + "mode": 493, + "sha256": "cc299f0d560bbd611cab099923bb1817229df3cbc15cef22d6909091e9de826b", + "size": 3581928 + }, + "python/iqpilot_private/konn3kt/hephaestus/kwp2000.cpython-312-aarch64-linux-gnu.so": { + "mode": 493, + "sha256": "0dbcb5d9ec1af832bcaceaa3ae96a4785a432778c1fb3cdc8b74e62b5d915158", + "size": 136448 + }, + "python/iqpilot_private/konn3kt/hephaestus/manage_hephaestusd.cpython-312-aarch64-linux-gnu.so": { + "mode": 493, + "sha256": "cabf37e151994a223f890627c0128990e56505bc704108f050d2422e118a273a", + "size": 68136 + }, + "python/iqpilot_private/konn3kt/hephaestus/motd.cpython-312-aarch64-linux-gnu.so": { + "mode": 493, + "sha256": "10af02f5daf288e465bcaa64509972a1a0b268f779b1c12c58bf63c79e52eb96", + "size": 67840 + }, + "python/iqpilot_private/konn3kt/hephaestus/tp20.cpython-312-aarch64-linux-gnu.so": { + "mode": 493, + "sha256": "855f128d248c94fbd00f550f7dd388ccf1a487935d7f11cac00e5240b526d674", + "size": 135648 + }, + "python/iqpilot_private/konn3kt/hephaestus/vw_pq_flasher.cpython-312-aarch64-linux-gnu.so": { + "mode": 493, + "sha256": "8de50d50308777ff9dc22d4d5232cba38f2f332207313999ec8aaa67e5004f4e", + "size": 269568 + }, + "python/iqpilot_private/konn3kt/uploaderd/__init__.py": { + "mode": 420, + "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "size": 0 + }, + "python/iqpilot_private/konn3kt/uploaderd/iquploaderd.cpython-312-aarch64-linux-gnu.so": { + "mode": 493, + "sha256": "11d16de5d29a37f64351acabfa6372966447a88cd7d799580a6f2f1fa745c15a", + "size": 204184 + }, + "runtime": { + "entries": { + "iqpilot_private.konn3kt.backups.backup_orchestrator": { + "path": "python/iqpilot_private/konn3kt/backups/backup_orchestrator.cpython-312-aarch64-linux-gnu.so", + "type": "python-module" + }, + "iqpilot_private.konn3kt.flockd.flockd": { + "path": "python/iqpilot_private/konn3kt/flockd/flockd.cpython-312-aarch64-linux-gnu.so", + "type": "python-module" + }, + "iqpilot_private.konn3kt.hephaestus.ble_transportd": { + "path": "python/iqpilot_private/konn3kt/hephaestus/ble_transportd.cpython-312-aarch64-linux-gnu.so", + "type": "python-module" + }, + "iqpilot_private.konn3kt.hephaestus.cloud_routes": { + "path": "python/iqpilot_private/konn3kt/hephaestus/cloud_routes.cpython-312-aarch64-linux-gnu.so", + "type": "python-module" + }, + "iqpilot_private.konn3kt.hephaestus.hephaestusd": { + "path": "python/iqpilot_private/konn3kt/hephaestus/hephaestusd.cpython-312-aarch64-linux-gnu.so", + "type": "python-module" + }, + "iqpilot_private.konn3kt.hephaestus.manage_hephaestusd": { + "path": "python/iqpilot_private/konn3kt/hephaestus/manage_hephaestusd.cpython-312-aarch64-linux-gnu.so", + "type": "python-module" + }, + "iqpilot_private.konn3kt.hephaestus.motd": { + "path": "python/iqpilot_private/konn3kt/hephaestus/motd.cpython-312-aarch64-linux-gnu.so", + "type": "python-module" + }, + "iqpilot_private.konn3kt.uploaderd.iquploaderd": { + "path": "python/iqpilot_private/konn3kt/uploaderd/iquploaderd.cpython-312-aarch64-linux-gnu.so", + "type": "python-module" + } + } + }, + "signatures": { + "python/iqpilot_private/konn3kt/backups/archive_codec.cpython-312-aarch64-linux-gnu.so": "1DjE26Z6yX6VuP+Jx/DxxS2PjWXSXLd+bS1RgYEo9WNOEHENVDv1xeoyXjFmrXe6/QTILJpv+LnqWegfVSnFBQ==", + "python/iqpilot_private/konn3kt/backups/backup_keys.cpython-312-aarch64-linux-gnu.so": "ytsUI7NI3t/9m+zEfMCbXVCIggeKSk8lnTrVYWQr0uryX5tTtQXyR+f+rIPSZWzzHu80z3GE0jmnQEEN2m6XCA==", + "python/iqpilot_private/konn3kt/backups/backup_orchestrator.cpython-312-aarch64-linux-gnu.so": "Y57ZaVfeeB1Mnu4vFIQNSZIuob+jmBkuuZ86Zc0sOWvqgkE8gT41pivlipr/R/du5+HSqXvTv/iuQS8ekTBpCg==", + "python/iqpilot_private/konn3kt/backups/cbc_vault.cpython-312-aarch64-linux-gnu.so": "PGjuQc316L/r8sSGF0Hg5EnFWj/Bhk27UYBgGvQ8xxLvoVJIkRKVJ/ZFLFZo6rdEWrd0GQECUYeR9ewU6ufFAA==", + "python/iqpilot_private/konn3kt/backups/imahelper.cpython-312-aarch64-linux-gnu.so": "CVrgu0dEypYq6IIA38Lh/w4KKVNpP5+dfV0dkOpDA4UkyH2IoX3Wiywpz3o1jfjrSXE3oiPdk52qRE+pzLLxDQ==", + "python/iqpilot_private/konn3kt/flockd/flockd.cpython-312-aarch64-linux-gnu.so": "+grQAam3UiFIBqGcvuAI+7aYW6UCU83YLlxCkPoo6YaX9SitDVqQk6bNQwqxYUzuUZ2s8BdhlbgseTuImRZMBw==", + "python/iqpilot_private/konn3kt/flockd/signatures.cpython-312-aarch64-linux-gnu.so": "yhxeGAidy3Auu+i2wZgxD1bK18VeWknYuQQpeiylNyWNfzN/xPUDGFyyV0CpNY3FqEqH8LcHLzsa8Bz4JTbWDw==", + "python/iqpilot_private/konn3kt/hephaestus/ble_auth.cpython-312-aarch64-linux-gnu.so": "OX/9GfQQf/krppHPChnXqvvHCs+8LqdpnRtRxJ9AYeqRLwNMrTgCSLOsU3YGlHiF78U5jIldNdkr4aOGBHRVAg==", + "python/iqpilot_private/konn3kt/hephaestus/ble_gatt.cpython-312-aarch64-linux-gnu.so": "n06LgyR6cPqHLaGsiEXlSFiNKFkT3S14mPEz+xtLw0y3bAuNod5Jpp6KiVuoTcVdzHjXPFWwYLgqbtDxSZe/BQ==", + "python/iqpilot_private/konn3kt/hephaestus/ble_rpc_dispatch.cpython-312-aarch64-linux-gnu.so": "JJ8eaFR2wkJW4/gw31a35LvyHLKTLfPwFwIC3cE5Y4AYbLkrVuVbizPefhUrcFMF0ZxzjQulU0YNAL7EMbzOAA==", + "python/iqpilot_private/konn3kt/hephaestus/ble_transportd.cpython-312-aarch64-linux-gnu.so": "Vr/pX91mN1D0Qcl9PNJiT2DUq9LxP13qjO5g7Ve/KDPwAqL9nKJXeAqbhKXIqFyRHp82PAHKaVhHsF39QtLRAA==", + "python/iqpilot_private/konn3kt/hephaestus/bt_gamepad.cpython-312-aarch64-linux-gnu.so": "yjHeFmvqDWSEXIft0n93zTSZemP4L9hmXZtauDFL7bRnGHMnTVvrq7olJJoXvQITVGyGzDl+BuDF5pjOKau+AQ==", + "python/iqpilot_private/konn3kt/hephaestus/cloud_routes.cpython-312-aarch64-linux-gnu.so": "7sVJ5qadF1T48RP8FlwynsJEC0w6OtN+BGBf93YABFXgYDPY/YAMm5aAicR6YmGvgkFqtfCf5g8ozdrz5fIuBg==", + "python/iqpilot_private/konn3kt/hephaestus/hephaestusd.cpython-312-aarch64-linux-gnu.so": "54+MaaeOIEbj+uZrcphTbHQ8K1jm4gRe2JYZyYNkwxPcT6dkcWLM6NZiNVzXiD6Ph87hTxUbecJnhVeT80pDAw==", + "python/iqpilot_private/konn3kt/hephaestus/kwp2000.cpython-312-aarch64-linux-gnu.so": "xJnkGTaVVWne7p83UhTU2C/miZg4XR7pLrd6A7j7TvetRCFvQPxyuAeFi5lJsKKwhaWV3dInGZcVWrGV+kV0AA==", + "python/iqpilot_private/konn3kt/hephaestus/manage_hephaestusd.cpython-312-aarch64-linux-gnu.so": "P+LVQJc3vD5Z9i/jxNxziNbgCiGrI+5P9saUGHMPMKUoWhVEBAAVU8xuVyd6GjSd8M5VrximPVrJyMCE9fEfAw==", + "python/iqpilot_private/konn3kt/hephaestus/motd.cpython-312-aarch64-linux-gnu.so": "vfDMFKWuTD2XSE7JF/qmHxcOXgdXRZN9fi3FTSffmr6t22Tz0N7v8MwVayiPKrYNt2gHixFZ9aAkvF2SC+yDBg==", + "python/iqpilot_private/konn3kt/hephaestus/tp20.cpython-312-aarch64-linux-gnu.so": "Orgr5aTB5g3NRH8AdsuNkpcmIfSQDBFHzegDN875SxuvHuCOfsVVYgx2TtRiT0wqSR9hlQQwhelyRHRQjcMCAQ==", + "python/iqpilot_private/konn3kt/hephaestus/vw_pq_flasher.cpython-312-aarch64-linux-gnu.so": "tZNbbDyMxkFOcrvWCEBQz6ZTGdLZVesuz0dbUMO1OBaSYAG4MRBQ5y9fZwRQZAvcEa+NMlNZK2SGoC6r5BxgDA==", + "python/iqpilot_private/konn3kt/uploaderd/iquploaderd.cpython-312-aarch64-linux-gnu.so": "sgYjdE7ZRvdKlehs1/h3z/n14fksR+Z+KhCzi0BvFPcbMcx0Pod/L42IQeEDfcQfoFBURPNoI1OTwWc31H/NAQ==" + } +} diff --git a/artifacts/iqpilot_hephaestusd_private/python/iqpilot_private/__init__.py b/artifacts/iqpilot_hephaestusd_private/python/iqpilot_private/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/artifacts/iqpilot_hephaestusd_private/python/iqpilot_private/konn3kt/__init__.py b/artifacts/iqpilot_hephaestusd_private/python/iqpilot_private/konn3kt/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/artifacts/iqpilot_hephaestusd_private/python/iqpilot_private/konn3kt/backups/__init__.py b/artifacts/iqpilot_hephaestusd_private/python/iqpilot_private/konn3kt/backups/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/artifacts/iqpilot_hephaestusd_private/python/iqpilot_private/konn3kt/backups/archive_codec.cpython-312-aarch64-linux-gnu.so b/artifacts/iqpilot_hephaestusd_private/python/iqpilot_private/konn3kt/backups/archive_codec.cpython-312-aarch64-linux-gnu.so new file mode 100755 index 000000000..86354b636 Binary files /dev/null and b/artifacts/iqpilot_hephaestusd_private/python/iqpilot_private/konn3kt/backups/archive_codec.cpython-312-aarch64-linux-gnu.so differ diff --git a/artifacts/iqpilot_hephaestusd_private/python/iqpilot_private/konn3kt/backups/backup_keys.cpython-312-aarch64-linux-gnu.so b/artifacts/iqpilot_hephaestusd_private/python/iqpilot_private/konn3kt/backups/backup_keys.cpython-312-aarch64-linux-gnu.so new file mode 100755 index 000000000..dd2ce74e0 Binary files /dev/null and b/artifacts/iqpilot_hephaestusd_private/python/iqpilot_private/konn3kt/backups/backup_keys.cpython-312-aarch64-linux-gnu.so differ diff --git a/artifacts/iqpilot_hephaestusd_private/python/iqpilot_private/konn3kt/backups/backup_orchestrator.cpython-312-aarch64-linux-gnu.so b/artifacts/iqpilot_hephaestusd_private/python/iqpilot_private/konn3kt/backups/backup_orchestrator.cpython-312-aarch64-linux-gnu.so new file mode 100755 index 000000000..30328babd Binary files /dev/null and b/artifacts/iqpilot_hephaestusd_private/python/iqpilot_private/konn3kt/backups/backup_orchestrator.cpython-312-aarch64-linux-gnu.so differ diff --git a/artifacts/iqpilot_hephaestusd_private/python/iqpilot_private/konn3kt/backups/cbc_vault.cpython-312-aarch64-linux-gnu.so b/artifacts/iqpilot_hephaestusd_private/python/iqpilot_private/konn3kt/backups/cbc_vault.cpython-312-aarch64-linux-gnu.so new file mode 100755 index 000000000..33eb1805a Binary files /dev/null and b/artifacts/iqpilot_hephaestusd_private/python/iqpilot_private/konn3kt/backups/cbc_vault.cpython-312-aarch64-linux-gnu.so differ diff --git a/artifacts/iqpilot_hephaestusd_private/python/iqpilot_private/konn3kt/backups/imahelper.cpython-312-aarch64-linux-gnu.so b/artifacts/iqpilot_hephaestusd_private/python/iqpilot_private/konn3kt/backups/imahelper.cpython-312-aarch64-linux-gnu.so new file mode 100755 index 000000000..a16510003 Binary files /dev/null and b/artifacts/iqpilot_hephaestusd_private/python/iqpilot_private/konn3kt/backups/imahelper.cpython-312-aarch64-linux-gnu.so differ diff --git a/artifacts/iqpilot_hephaestusd_private/python/iqpilot_private/konn3kt/flockd/__init__.py b/artifacts/iqpilot_hephaestusd_private/python/iqpilot_private/konn3kt/flockd/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/artifacts/iqpilot_hephaestusd_private/python/iqpilot_private/konn3kt/flockd/flockd.cpython-312-aarch64-linux-gnu.so b/artifacts/iqpilot_hephaestusd_private/python/iqpilot_private/konn3kt/flockd/flockd.cpython-312-aarch64-linux-gnu.so new file mode 100755 index 000000000..5713e2e15 Binary files /dev/null and b/artifacts/iqpilot_hephaestusd_private/python/iqpilot_private/konn3kt/flockd/flockd.cpython-312-aarch64-linux-gnu.so differ diff --git a/artifacts/iqpilot_hephaestusd_private/python/iqpilot_private/konn3kt/flockd/signatures.cpython-312-aarch64-linux-gnu.so b/artifacts/iqpilot_hephaestusd_private/python/iqpilot_private/konn3kt/flockd/signatures.cpython-312-aarch64-linux-gnu.so new file mode 100755 index 000000000..c4be8a76c Binary files /dev/null and b/artifacts/iqpilot_hephaestusd_private/python/iqpilot_private/konn3kt/flockd/signatures.cpython-312-aarch64-linux-gnu.so differ diff --git a/artifacts/iqpilot_hephaestusd_private/python/iqpilot_private/konn3kt/hephaestus/__init__.py b/artifacts/iqpilot_hephaestusd_private/python/iqpilot_private/konn3kt/hephaestus/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/artifacts/iqpilot_hephaestusd_private/python/iqpilot_private/konn3kt/hephaestus/_vendor/localapi_runtime.zip b/artifacts/iqpilot_hephaestusd_private/python/iqpilot_private/konn3kt/hephaestus/_vendor/localapi_runtime.zip new file mode 100644 index 000000000..ffe483400 Binary files /dev/null and b/artifacts/iqpilot_hephaestusd_private/python/iqpilot_private/konn3kt/hephaestus/_vendor/localapi_runtime.zip differ diff --git a/artifacts/iqpilot_hephaestusd_private/python/iqpilot_private/konn3kt/hephaestus/ble_auth.cpython-312-aarch64-linux-gnu.so b/artifacts/iqpilot_hephaestusd_private/python/iqpilot_private/konn3kt/hephaestus/ble_auth.cpython-312-aarch64-linux-gnu.so new file mode 100755 index 000000000..24d362d19 Binary files /dev/null and b/artifacts/iqpilot_hephaestusd_private/python/iqpilot_private/konn3kt/hephaestus/ble_auth.cpython-312-aarch64-linux-gnu.so differ diff --git a/artifacts/iqpilot_hephaestusd_private/python/iqpilot_private/konn3kt/hephaestus/ble_gatt.cpython-312-aarch64-linux-gnu.so b/artifacts/iqpilot_hephaestusd_private/python/iqpilot_private/konn3kt/hephaestus/ble_gatt.cpython-312-aarch64-linux-gnu.so new file mode 100755 index 000000000..520fa348c Binary files /dev/null and b/artifacts/iqpilot_hephaestusd_private/python/iqpilot_private/konn3kt/hephaestus/ble_gatt.cpython-312-aarch64-linux-gnu.so differ diff --git a/artifacts/iqpilot_hephaestusd_private/python/iqpilot_private/konn3kt/hephaestus/ble_rpc_dispatch.cpython-312-aarch64-linux-gnu.so b/artifacts/iqpilot_hephaestusd_private/python/iqpilot_private/konn3kt/hephaestus/ble_rpc_dispatch.cpython-312-aarch64-linux-gnu.so new file mode 100755 index 000000000..9240a8d66 Binary files /dev/null and b/artifacts/iqpilot_hephaestusd_private/python/iqpilot_private/konn3kt/hephaestus/ble_rpc_dispatch.cpython-312-aarch64-linux-gnu.so differ diff --git a/artifacts/iqpilot_hephaestusd_private/python/iqpilot_private/konn3kt/hephaestus/ble_transportd.cpython-312-aarch64-linux-gnu.so b/artifacts/iqpilot_hephaestusd_private/python/iqpilot_private/konn3kt/hephaestus/ble_transportd.cpython-312-aarch64-linux-gnu.so new file mode 100755 index 000000000..a8bee2a86 Binary files /dev/null and b/artifacts/iqpilot_hephaestusd_private/python/iqpilot_private/konn3kt/hephaestus/ble_transportd.cpython-312-aarch64-linux-gnu.so differ diff --git a/artifacts/iqpilot_hephaestusd_private/python/iqpilot_private/konn3kt/hephaestus/bt_gamepad.cpython-312-aarch64-linux-gnu.so b/artifacts/iqpilot_hephaestusd_private/python/iqpilot_private/konn3kt/hephaestus/bt_gamepad.cpython-312-aarch64-linux-gnu.so new file mode 100755 index 000000000..ea41b12e2 Binary files /dev/null and b/artifacts/iqpilot_hephaestusd_private/python/iqpilot_private/konn3kt/hephaestus/bt_gamepad.cpython-312-aarch64-linux-gnu.so differ diff --git a/artifacts/iqpilot_hephaestusd_private/python/iqpilot_private/konn3kt/hephaestus/cloud_routes.cpython-312-aarch64-linux-gnu.so b/artifacts/iqpilot_hephaestusd_private/python/iqpilot_private/konn3kt/hephaestus/cloud_routes.cpython-312-aarch64-linux-gnu.so new file mode 100755 index 000000000..3be2eb085 Binary files /dev/null and b/artifacts/iqpilot_hephaestusd_private/python/iqpilot_private/konn3kt/hephaestus/cloud_routes.cpython-312-aarch64-linux-gnu.so differ diff --git a/artifacts/iqpilot_hephaestusd_private/python/iqpilot_private/konn3kt/hephaestus/hephaestusd.cpython-312-aarch64-linux-gnu.so b/artifacts/iqpilot_hephaestusd_private/python/iqpilot_private/konn3kt/hephaestus/hephaestusd.cpython-312-aarch64-linux-gnu.so new file mode 100755 index 000000000..27b8f0a33 Binary files /dev/null and b/artifacts/iqpilot_hephaestusd_private/python/iqpilot_private/konn3kt/hephaestus/hephaestusd.cpython-312-aarch64-linux-gnu.so differ diff --git a/artifacts/iqpilot_hephaestusd_private/python/iqpilot_private/konn3kt/hephaestus/kwp2000.cpython-312-aarch64-linux-gnu.so b/artifacts/iqpilot_hephaestusd_private/python/iqpilot_private/konn3kt/hephaestus/kwp2000.cpython-312-aarch64-linux-gnu.so new file mode 100755 index 000000000..ee371a8ff Binary files /dev/null and b/artifacts/iqpilot_hephaestusd_private/python/iqpilot_private/konn3kt/hephaestus/kwp2000.cpython-312-aarch64-linux-gnu.so differ diff --git a/artifacts/iqpilot_hephaestusd_private/python/iqpilot_private/konn3kt/hephaestus/manage_hephaestusd.cpython-312-aarch64-linux-gnu.so b/artifacts/iqpilot_hephaestusd_private/python/iqpilot_private/konn3kt/hephaestus/manage_hephaestusd.cpython-312-aarch64-linux-gnu.so new file mode 100755 index 000000000..2cf1d2e87 Binary files /dev/null and b/artifacts/iqpilot_hephaestusd_private/python/iqpilot_private/konn3kt/hephaestus/manage_hephaestusd.cpython-312-aarch64-linux-gnu.so differ diff --git a/artifacts/iqpilot_hephaestusd_private/python/iqpilot_private/konn3kt/hephaestus/motd.cpython-312-aarch64-linux-gnu.so b/artifacts/iqpilot_hephaestusd_private/python/iqpilot_private/konn3kt/hephaestus/motd.cpython-312-aarch64-linux-gnu.so new file mode 100755 index 000000000..bbf30475c Binary files /dev/null and b/artifacts/iqpilot_hephaestusd_private/python/iqpilot_private/konn3kt/hephaestus/motd.cpython-312-aarch64-linux-gnu.so differ diff --git a/artifacts/iqpilot_hephaestusd_private/python/iqpilot_private/konn3kt/hephaestus/tp20.cpython-312-aarch64-linux-gnu.so b/artifacts/iqpilot_hephaestusd_private/python/iqpilot_private/konn3kt/hephaestus/tp20.cpython-312-aarch64-linux-gnu.so new file mode 100755 index 000000000..9eb411b7a Binary files /dev/null and b/artifacts/iqpilot_hephaestusd_private/python/iqpilot_private/konn3kt/hephaestus/tp20.cpython-312-aarch64-linux-gnu.so differ diff --git a/artifacts/iqpilot_hephaestusd_private/python/iqpilot_private/konn3kt/hephaestus/vw_pq_flasher.cpython-312-aarch64-linux-gnu.so b/artifacts/iqpilot_hephaestusd_private/python/iqpilot_private/konn3kt/hephaestus/vw_pq_flasher.cpython-312-aarch64-linux-gnu.so new file mode 100755 index 000000000..5c6e64cbb Binary files /dev/null and b/artifacts/iqpilot_hephaestusd_private/python/iqpilot_private/konn3kt/hephaestus/vw_pq_flasher.cpython-312-aarch64-linux-gnu.so differ diff --git a/artifacts/iqpilot_hephaestusd_private/python/iqpilot_private/konn3kt/uploaderd/__init__.py b/artifacts/iqpilot_hephaestusd_private/python/iqpilot_private/konn3kt/uploaderd/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/artifacts/iqpilot_hephaestusd_private/python/iqpilot_private/konn3kt/uploaderd/iquploaderd.cpython-312-aarch64-linux-gnu.so b/artifacts/iqpilot_hephaestusd_private/python/iqpilot_private/konn3kt/uploaderd/iquploaderd.cpython-312-aarch64-linux-gnu.so new file mode 100755 index 000000000..1e5ca4493 Binary files /dev/null and b/artifacts/iqpilot_hephaestusd_private/python/iqpilot_private/konn3kt/uploaderd/iquploaderd.cpython-312-aarch64-linux-gnu.so differ diff --git a/artifacts/iqpilot_iqvd_private/manifest.json b/artifacts/iqpilot_iqvd_private/manifest.json new file mode 100644 index 000000000..3f3bb1744 --- /dev/null +++ b/artifacts/iqpilot_iqvd_private/manifest.json @@ -0,0 +1,75 @@ +{ + "python/iqpilot_private/__init__.py": { + "mode": 420, + "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "size": 0 + }, + "python/iqpilot_private/iqvd/__init__.cpython-312-aarch64-linux-gnu.so": { + "mode": 493, + "sha256": "48bad6268fabc23a9be62fcd7f61240ebcbabd5e3c6afcf7d3328bd33d93d3a2", + "size": 67616 + }, + "python/iqpilot_private/iqvd/__init__.py": { + "mode": 420, + "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "size": 0 + }, + "python/iqpilot_private/iqvd/iqvd.cpython-312-aarch64-linux-gnu.so": { + "mode": 493, + "sha256": "674c4ecd09e36af6c2b9a41285e78914ee94a0e8c4de728904171dd59e02edda", + "size": 135576 + }, + "python/iqpilot_private/iqvd/offload/__init__.cpython-312-aarch64-linux-gnu.so": { + "mode": 493, + "sha256": "7ce45dfb9a6ffe8e5426f261cf7e9b3a3825e3d284a0675013d43697e73d56d9", + "size": 67616 + }, + "python/iqpilot_private/iqvd/offload/__init__.py": { + "mode": 420, + "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "size": 0 + }, + "python/iqpilot_private/iqvd/offload/client.cpython-312-aarch64-linux-gnu.so": { + "mode": 493, + "sha256": "439f88b68d220912db2f7e9a43d41827d298f68b113e7c5e5858adea3a05e963", + "size": 135296 + }, + "python/iqpilot_private/iqvd/offload/geometry.cpython-312-aarch64-linux-gnu.so": { + "mode": 493, + "sha256": "0199f81ce365fb956d729c61c20c8eaa0120448171db6b83544f3dd1a0861877", + "size": 68232 + }, + "python/iqpilot_private/iqvd/offload/perception.cpython-312-aarch64-linux-gnu.so": { + "mode": 493, + "sha256": "678ebcdd9a97fe527970bbb93021d2ae8f4f9935444b6558dd61b399ab3fe3e9", + "size": 135976 + }, + "python/iqpilot_private/iqvd/offload/protocol.cpython-312-aarch64-linux-gnu.so": { + "mode": 493, + "sha256": "a8d24107433a037d19876b2240e980b09c89f560ba960dda5e28c16665a03ca6", + "size": 68120 + }, + "python/iqpilot_private/iqvd/yolov8_net.cpython-312-aarch64-linux-gnu.so": { + "mode": 493, + "sha256": "943e0ef218eed109289d208ea76407e223a4e8b81659f0ec79dc090fbd7ed329", + "size": 204568 + }, + "runtime": { + "entries": { + "iqpilot_private.iqvd.iqvd": { + "path": "python/iqpilot_private/iqvd/iqvd.cpython-312-aarch64-linux-gnu.so", + "type": "python-module" + } + } + }, + "signatures": { + "python/iqpilot_private/iqvd/__init__.cpython-312-aarch64-linux-gnu.so": "YPDPJiSbuWXyg3MpOJXzZtejR5sazfra6c/AFVm+tzE+i9OA9/OPS5qvpFSaHOBPaVZYtsmxkOFq4Q5GJlYOAg==", + "python/iqpilot_private/iqvd/iqvd.cpython-312-aarch64-linux-gnu.so": "H+2OPwV0XS7biX05P6fBzRBCN1Cjnq9eH6YSLk+9BX8OPbdiyJTbDpt8YZYk4nU0LO5FF4dMkEXzFRsewB7BAw==", + "python/iqpilot_private/iqvd/offload/__init__.cpython-312-aarch64-linux-gnu.so": "vbv8RlHm7LGdrojZuRhs+2StjASHEC1GbkZspXh6FCbmyKmW7F9B5GzHjIwr7HVFSNX6u+ELPPKebfnIsOkmAQ==", + "python/iqpilot_private/iqvd/offload/client.cpython-312-aarch64-linux-gnu.so": "pfDu4/OifTpFWqMD/hyQN+p+9syWaiAavHCkvxG9++0VvHDwKsBPJFxRk8r3vBdylSz4zkIFXlqPPt/hNuzJAg==", + "python/iqpilot_private/iqvd/offload/geometry.cpython-312-aarch64-linux-gnu.so": "Ll6azxnD3jGmyPPjd+yTmAVCl+CwM2iSdSqpYtTtzqPIb6GFBp0FfF5w+FTpacKjoQ4i3cXV4gdUVOf3qRWzCg==", + "python/iqpilot_private/iqvd/offload/perception.cpython-312-aarch64-linux-gnu.so": "ww5BVRLiGB+LkqREN1f7njOrrgOcWghyTXNXTJTJHYeLx5AgIrvsqg1PH7+nq1LuSvk+LHDTOSUAcs7JIDh3DQ==", + "python/iqpilot_private/iqvd/offload/protocol.cpython-312-aarch64-linux-gnu.so": "3OzAWo66E+qe/H+HtGEP1i73WLW9SPa9+zeb8fOWBe7Nlsx7WrgGcURBq0VTycv2v3QLNLEluei/rnQEYBRMCA==", + "python/iqpilot_private/iqvd/yolov8_net.cpython-312-aarch64-linux-gnu.so": "C64cBnuSM2wm6vCEO9W3cNqbB4P7Yuy78pFUXxn8iesCQD0hbZ1+w8IuH4VuxB88ubLRLQt2GZ8LYIESb+HYAg==" + } +} diff --git a/artifacts/iqpilot_iqvd_private/models/yolov8n.safetensors b/artifacts/iqpilot_iqvd_private/models/yolov8n.safetensors new file mode 100644 index 000000000..186d6fd31 Binary files /dev/null and b/artifacts/iqpilot_iqvd_private/models/yolov8n.safetensors differ diff --git a/artifacts/iqpilot_iqvd_private/models/yolov8n_qcom.pkl b/artifacts/iqpilot_iqvd_private/models/yolov8n_qcom.pkl new file mode 100644 index 000000000..fce21e97e Binary files /dev/null and b/artifacts/iqpilot_iqvd_private/models/yolov8n_qcom.pkl differ diff --git a/artifacts/iqpilot_iqvd_private/python/iqpilot_private/__init__.py b/artifacts/iqpilot_iqvd_private/python/iqpilot_private/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/artifacts/iqpilot_iqvd_private/python/iqpilot_private/iqvd/__init__.cpython-312-aarch64-linux-gnu.so b/artifacts/iqpilot_iqvd_private/python/iqpilot_private/iqvd/__init__.cpython-312-aarch64-linux-gnu.so new file mode 100755 index 000000000..d7a4479f3 Binary files /dev/null and b/artifacts/iqpilot_iqvd_private/python/iqpilot_private/iqvd/__init__.cpython-312-aarch64-linux-gnu.so differ diff --git a/artifacts/iqpilot_iqvd_private/python/iqpilot_private/iqvd/__init__.py b/artifacts/iqpilot_iqvd_private/python/iqpilot_private/iqvd/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/artifacts/iqpilot_iqvd_private/python/iqpilot_private/iqvd/iqvd.cpython-312-aarch64-linux-gnu.so b/artifacts/iqpilot_iqvd_private/python/iqpilot_private/iqvd/iqvd.cpython-312-aarch64-linux-gnu.so new file mode 100755 index 000000000..db25f80ff Binary files /dev/null and b/artifacts/iqpilot_iqvd_private/python/iqpilot_private/iqvd/iqvd.cpython-312-aarch64-linux-gnu.so differ diff --git a/artifacts/iqpilot_iqvd_private/python/iqpilot_private/iqvd/offload/__init__.cpython-312-aarch64-linux-gnu.so b/artifacts/iqpilot_iqvd_private/python/iqpilot_private/iqvd/offload/__init__.cpython-312-aarch64-linux-gnu.so new file mode 100755 index 000000000..a5f6cbc73 Binary files /dev/null and b/artifacts/iqpilot_iqvd_private/python/iqpilot_private/iqvd/offload/__init__.cpython-312-aarch64-linux-gnu.so differ diff --git a/artifacts/iqpilot_iqvd_private/python/iqpilot_private/iqvd/offload/__init__.py b/artifacts/iqpilot_iqvd_private/python/iqpilot_private/iqvd/offload/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/artifacts/iqpilot_iqvd_private/python/iqpilot_private/iqvd/offload/client.cpython-312-aarch64-linux-gnu.so b/artifacts/iqpilot_iqvd_private/python/iqpilot_private/iqvd/offload/client.cpython-312-aarch64-linux-gnu.so new file mode 100755 index 000000000..0e75f6819 Binary files /dev/null and b/artifacts/iqpilot_iqvd_private/python/iqpilot_private/iqvd/offload/client.cpython-312-aarch64-linux-gnu.so differ diff --git a/artifacts/iqpilot_iqvd_private/python/iqpilot_private/iqvd/offload/geometry.cpython-312-aarch64-linux-gnu.so b/artifacts/iqpilot_iqvd_private/python/iqpilot_private/iqvd/offload/geometry.cpython-312-aarch64-linux-gnu.so new file mode 100755 index 000000000..f4e614ed8 Binary files /dev/null and b/artifacts/iqpilot_iqvd_private/python/iqpilot_private/iqvd/offload/geometry.cpython-312-aarch64-linux-gnu.so differ diff --git a/artifacts/iqpilot_iqvd_private/python/iqpilot_private/iqvd/offload/perception.cpython-312-aarch64-linux-gnu.so b/artifacts/iqpilot_iqvd_private/python/iqpilot_private/iqvd/offload/perception.cpython-312-aarch64-linux-gnu.so new file mode 100755 index 000000000..f34cba010 Binary files /dev/null and b/artifacts/iqpilot_iqvd_private/python/iqpilot_private/iqvd/offload/perception.cpython-312-aarch64-linux-gnu.so differ diff --git a/artifacts/iqpilot_iqvd_private/python/iqpilot_private/iqvd/offload/protocol.cpython-312-aarch64-linux-gnu.so b/artifacts/iqpilot_iqvd_private/python/iqpilot_private/iqvd/offload/protocol.cpython-312-aarch64-linux-gnu.so new file mode 100755 index 000000000..a09d623ea Binary files /dev/null and b/artifacts/iqpilot_iqvd_private/python/iqpilot_private/iqvd/offload/protocol.cpython-312-aarch64-linux-gnu.so differ diff --git a/artifacts/iqpilot_iqvd_private/python/iqpilot_private/iqvd/yolov8_net.cpython-312-aarch64-linux-gnu.so b/artifacts/iqpilot_iqvd_private/python/iqpilot_private/iqvd/yolov8_net.cpython-312-aarch64-linux-gnu.so new file mode 100755 index 000000000..2e3553369 Binary files /dev/null and b/artifacts/iqpilot_iqvd_private/python/iqpilot_private/iqvd/yolov8_net.cpython-312-aarch64-linux-gnu.so differ diff --git a/artifacts/iqpilot_maps_private/manifest.json b/artifacts/iqpilot_maps_private/manifest.json new file mode 100644 index 000000000..0e5cad0c2 --- /dev/null +++ b/artifacts/iqpilot_maps_private/manifest.json @@ -0,0 +1,34 @@ +{ + "python/iqpilot_private/__init__.py": { + "mode": 420, + "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "size": 0 + }, + "python/iqpilot_private/maps/__init__.cpython-312-aarch64-linux-gnu.so": { + "mode": 493, + "sha256": "f7cbc5f8aa87cfb7edf0e3aac69893132e9af9502a684a282ee510b69ada3568", + "size": 67616 + }, + "python/iqpilot_private/maps/__init__.py": { + "mode": 420, + "sha256": "5128931b54d4f1ff131185e7711d56a788b5e2cb71e3c8154a2f79fef184b452", + "size": 118 + }, + "python/iqpilot_private/maps/git_auth.cpython-312-aarch64-linux-gnu.so": { + "mode": 493, + "sha256": "39fdad3afca4fa8cf848a03495e6f89f11ee21a4e75ec815774ee3f119576cb2", + "size": 67856 + }, + "runtime": { + "entries": { + "iqpilot_private.maps.git_auth": { + "path": "python/iqpilot_private/maps/git_auth.cpython-312-aarch64-linux-gnu.so", + "type": "python-module" + } + } + }, + "signatures": { + "python/iqpilot_private/maps/__init__.cpython-312-aarch64-linux-gnu.so": "bN3jbaaSHL7++7hBCWbpAWKJVgRdgBvtv6lTHEHeyBVVIJUYot1j/eEr+ilJEbIYzSHm7sK7rO/AwWBk10CWAw==", + "python/iqpilot_private/maps/git_auth.cpython-312-aarch64-linux-gnu.so": "EC7a8gmfNquIzXiR08p2kzD3kAd0Rp00ey/hnZOaQRAZ2ziD9BpPf9IK0AawuH2SPE7JNxab4T35PhhhRnbpCg==" + } +} diff --git a/artifacts/iqpilot_maps_private/python/iqpilot_private/__init__.py b/artifacts/iqpilot_maps_private/python/iqpilot_private/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/artifacts/iqpilot_maps_private/python/iqpilot_private/maps/__init__.cpython-312-aarch64-linux-gnu.so b/artifacts/iqpilot_maps_private/python/iqpilot_private/maps/__init__.cpython-312-aarch64-linux-gnu.so new file mode 100755 index 000000000..19844e3cc Binary files /dev/null and b/artifacts/iqpilot_maps_private/python/iqpilot_private/maps/__init__.cpython-312-aarch64-linux-gnu.so differ diff --git a/artifacts/iqpilot_maps_private/python/iqpilot_private/maps/__init__.py b/artifacts/iqpilot_maps_private/python/iqpilot_private/maps/__init__.py new file mode 100644 index 000000000..297dca9e1 --- /dev/null +++ b/artifacts/iqpilot_maps_private/python/iqpilot_private/maps/__init__.py @@ -0,0 +1,3 @@ +""" +Copyright © IQ.Lvbs, apart of Project Teal Lvbs, All Rights Reserved, licensed under https://konn3kt.com/tos +""" diff --git a/artifacts/iqpilot_maps_private/python/iqpilot_private/maps/git_auth.cpython-312-aarch64-linux-gnu.so b/artifacts/iqpilot_maps_private/python/iqpilot_private/maps/git_auth.cpython-312-aarch64-linux-gnu.so new file mode 100755 index 000000000..bf6f429b7 Binary files /dev/null and b/artifacts/iqpilot_maps_private/python/iqpilot_private/maps/git_auth.cpython-312-aarch64-linux-gnu.so differ diff --git a/artifacts/iqpilot_model_selector_private/manifest.json b/artifacts/iqpilot_model_selector_private/manifest.json new file mode 100644 index 000000000..7b41bcb63 --- /dev/null +++ b/artifacts/iqpilot_model_selector_private/manifest.json @@ -0,0 +1,52 @@ +{ + "python/iqpilot_private/__init__.py": { + "mode": 420, + "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "size": 0 + }, + "python/iqpilot_private/models/__init__.cpython-312-aarch64-linux-gnu.so": { + "mode": 493, + "sha256": "7e758a0beaf3cbe79e42f90fd26b299518cc08c8c77081a0e938af6bc2d0d6bb", + "size": 67616 + }, + "python/iqpilot_private/models/__init__.py": { + "mode": 420, + "sha256": "5128931b54d4f1ff131185e7711d56a788b5e2cb71e3c8154a2f79fef184b452", + "size": 118 + }, + "python/iqpilot_private/models/fetcher.cpython-312-aarch64-linux-gnu.so": { + "mode": 493, + "sha256": "e6304dce5745990f888615cf1dcffd1d7999fd29655305a6071159d1dfc6f469", + "size": 136176 + }, + "python/iqpilot_private/models/git_auth.cpython-312-aarch64-linux-gnu.so": { + "mode": 493, + "sha256": "3e0d4d4c67ba5920a43106c011c6e6db2e4e7704d220978be3b34ac8ecc84739", + "size": 67864 + }, + "python/iqpilot_private/models/helpers.cpython-312-aarch64-linux-gnu.so": { + "mode": 493, + "sha256": "e1516afc6c6d8213cd33d27069a8c6b6f5b4c7c9013772b3ec0bd74629878339", + "size": 137200 + }, + "python/iqpilot_private/models/manager.cpython-312-aarch64-linux-gnu.so": { + "mode": 493, + "sha256": "9893dccb4b99429dde00edcb16555b59103263a7be46ec76164f97462c3934c1", + "size": 274096 + }, + "runtime": { + "entries": { + "iqpilot_private.models.manager": { + "path": "python/iqpilot_private/models/manager.cpython-312-aarch64-linux-gnu.so", + "type": "python-module" + } + } + }, + "signatures": { + "python/iqpilot_private/models/__init__.cpython-312-aarch64-linux-gnu.so": "Otp/bhI6d4HMKzf34+A5XEhtFFsnGpw35c73IjjiO5M55JPRezqkEl1NqNWhOXGC6z33ogl8M6vqEQLiBGpdAg==", + "python/iqpilot_private/models/fetcher.cpython-312-aarch64-linux-gnu.so": "oC5sSq9qY2AvC9RI85Ts3tHyXv4K28Z6VyZUBbPlYKGymyDSrRq61+cXDXFWnxNRrNiP+6e+ul0XgcM4GtxNBg==", + "python/iqpilot_private/models/git_auth.cpython-312-aarch64-linux-gnu.so": "mPec9Doi0XJ+o/q/e65Y9464A+vfMpvWlI5g/CBA0EKuxuNEGekDqzWa92w4FzEPX5c/luwWtUgMBNgF+TS2Cg==", + "python/iqpilot_private/models/helpers.cpython-312-aarch64-linux-gnu.so": "CmRB3GasUFKV3tLeJgJg0LcQwcO0wZTT1O99hGCjIDf2bHd14NXGMsGVoAizBHbnM+bL6gqcH8zq/69CrJZGDg==", + "python/iqpilot_private/models/manager.cpython-312-aarch64-linux-gnu.so": "yNAlwoYAQzy5ig3Mqh4Vhjug0TaQywZ8ORbP6YHnZFRbOqggKgX29qKQ0+LOVDrktGZwwegqnLdjpseXxT29Aw==" + } +} diff --git a/artifacts/iqpilot_model_selector_private/python/iqpilot_private/__init__.py b/artifacts/iqpilot_model_selector_private/python/iqpilot_private/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/artifacts/iqpilot_model_selector_private/python/iqpilot_private/models/__init__.cpython-312-aarch64-linux-gnu.so b/artifacts/iqpilot_model_selector_private/python/iqpilot_private/models/__init__.cpython-312-aarch64-linux-gnu.so new file mode 100755 index 000000000..80cee14e3 Binary files /dev/null and b/artifacts/iqpilot_model_selector_private/python/iqpilot_private/models/__init__.cpython-312-aarch64-linux-gnu.so differ diff --git a/artifacts/iqpilot_model_selector_private/python/iqpilot_private/models/__init__.py b/artifacts/iqpilot_model_selector_private/python/iqpilot_private/models/__init__.py new file mode 100644 index 000000000..297dca9e1 --- /dev/null +++ b/artifacts/iqpilot_model_selector_private/python/iqpilot_private/models/__init__.py @@ -0,0 +1,3 @@ +""" +Copyright © IQ.Lvbs, apart of Project Teal Lvbs, All Rights Reserved, licensed under https://konn3kt.com/tos +""" diff --git a/artifacts/iqpilot_model_selector_private/python/iqpilot_private/models/fetcher.cpython-312-aarch64-linux-gnu.so b/artifacts/iqpilot_model_selector_private/python/iqpilot_private/models/fetcher.cpython-312-aarch64-linux-gnu.so new file mode 100755 index 000000000..5368650d6 Binary files /dev/null and b/artifacts/iqpilot_model_selector_private/python/iqpilot_private/models/fetcher.cpython-312-aarch64-linux-gnu.so differ diff --git a/artifacts/iqpilot_model_selector_private/python/iqpilot_private/models/git_auth.cpython-312-aarch64-linux-gnu.so b/artifacts/iqpilot_model_selector_private/python/iqpilot_private/models/git_auth.cpython-312-aarch64-linux-gnu.so new file mode 100755 index 000000000..5cb2bd0ce Binary files /dev/null and b/artifacts/iqpilot_model_selector_private/python/iqpilot_private/models/git_auth.cpython-312-aarch64-linux-gnu.so differ diff --git a/artifacts/iqpilot_model_selector_private/python/iqpilot_private/models/helpers.cpython-312-aarch64-linux-gnu.so b/artifacts/iqpilot_model_selector_private/python/iqpilot_private/models/helpers.cpython-312-aarch64-linux-gnu.so new file mode 100755 index 000000000..ece6a71fd Binary files /dev/null and b/artifacts/iqpilot_model_selector_private/python/iqpilot_private/models/helpers.cpython-312-aarch64-linux-gnu.so differ diff --git a/artifacts/iqpilot_model_selector_private/python/iqpilot_private/models/manager.cpython-312-aarch64-linux-gnu.so b/artifacts/iqpilot_model_selector_private/python/iqpilot_private/models/manager.cpython-312-aarch64-linux-gnu.so new file mode 100755 index 000000000..d7a7b6a8c Binary files /dev/null and b/artifacts/iqpilot_model_selector_private/python/iqpilot_private/models/manager.cpython-312-aarch64-linux-gnu.so differ diff --git a/artifacts/iqpilot_navd_private/manifest.json b/artifacts/iqpilot_navd_private/manifest.json new file mode 100644 index 000000000..e64b1c5dd --- /dev/null +++ b/artifacts/iqpilot_navd_private/manifest.json @@ -0,0 +1,124 @@ +{ + "python/iqpilot_private/__init__.py": { + "mode": 420, + "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "size": 0 + }, + "python/iqpilot_private/navd/__init__.cpython-312-aarch64-linux-gnu.so": { + "mode": 493, + "sha256": "449495db52a85f8d0b0e2bb3128ee480598f8bdb5a44ec13153877fe8b372a91", + "size": 67616 + }, + "python/iqpilot_private/navd/__init__.py": { + "mode": 420, + "sha256": "3faeaa3363b2f50fa135ad4c1f2466853d684d562120425f77a58039837fd8fb", + "size": 69 + }, + "python/iqpilot_private/navd/event_builder.cpython-312-aarch64-linux-gnu.so": { + "mode": 493, + "sha256": "ae2b25f52f7c7b02f3c977bbf46f5e9e938bfeda78bdb60f2f897eb04d698e43", + "size": 136944 + }, + "python/iqpilot_private/navd/helpers.cpython-312-aarch64-linux-gnu.so": { + "mode": 493, + "sha256": "f6ad0590f2a0bdbed56e7126203df2f6cdad86074f76ab9dded38b9e1600c8af", + "size": 135936 + }, + "python/iqpilot_private/navd/iqmapd.cpython-312-aarch64-linux-gnu.so": { + "mode": 493, + "sha256": "b4ed48cada1dfb3383ff8ee106a7d93967bf65115d4c528b60dc3d73112a2f0e", + "size": 135608 + }, + "python/iqpilot_private/navd/long_decel.cpython-312-aarch64-linux-gnu.so": { + "mode": 493, + "sha256": "e68aca6829cb00c5eda5330e1dfedd6269381ea5831abaca1055adf6df1f53ff", + "size": 69728 + }, + "python/iqpilot_private/navd/mapbox_client.cpython-312-aarch64-linux-gnu.so": { + "mode": 493, + "sha256": "ebd30c5c34fbbe82d7d24343a2e791919e526997d192fd1441df749b15526704", + "size": 68088 + }, + "python/iqpilot_private/navd/nav_cameras.cpython-312-aarch64-linux-gnu.so": { + "mode": 493, + "sha256": "6e69c5fcac3c302e401b3ed3735b42f3de4af4e0d188d15a7add7aa02920f4c5", + "size": 135440 + }, + "python/iqpilot_private/navd/navd.cpython-312-aarch64-linux-gnu.so": { + "mode": 493, + "sha256": "55c88f8c5996dd7094c0feaf454fe0f1aec97276aed8926d7e8f6d1597de5a83", + "size": 664480 + }, + "python/iqpilot_private/navd/navrenderd.cpython-312-aarch64-linux-gnu.so": { + "mode": 493, + "sha256": "b971e1093f5713a633636d6ee74891206a2d5abfb492406c7ae5df7a66bdc0d9", + "size": 135560 + }, + "python/iqpilot_private/navd/osm_cameras.cpython-312-aarch64-linux-gnu.so": { + "mode": 493, + "sha256": "882ccc23c3de2e4e34c685a7abb00075f6c5be634eaf7e81cab86461c624449d", + "size": 136368 + }, + "python/iqpilot_private/navd/reroute.cpython-312-aarch64-linux-gnu.so": { + "mode": 493, + "sha256": "df0bf89d492dba3b6c3792dde1bcb30a60e692f62519be7c62a517ef514ec276", + "size": 136072 + }, + "python/iqpilot_private/navd/route_manager.cpython-312-aarch64-linux-gnu.so": { + "mode": 493, + "sha256": "0311a1fbde1510b31055606db21a7c5f74e6872f9f1aabd6fb4a0a501395eaef", + "size": 600528 + }, + "python/iqpilot_private/navd/runtime_common.cpython-312-aarch64-linux-gnu.so": { + "mode": 493, + "sha256": "1f08c7c462b4cb08a8de9140d968ef5dc74b31afffe16c9f8fad4e0271bec6cc", + "size": 135752 + }, + "python/iqpilot_private/navd/turn_desire.cpython-312-aarch64-linux-gnu.so": { + "mode": 493, + "sha256": "aea71be79c99884a0aeaaf8e5c0f70e37bb4f0c0123025cd07df830189774ba9", + "size": 69832 + }, + "python/iqpilot_private/navd/web_server.cpython-312-aarch64-linux-gnu.so": { + "mode": 493, + "sha256": "5c7dd6ef7dafb9a35662ee3d22c08b17b02807df736bf146af88cb260f5bbf09", + "size": 201384 + }, + "runtime": { + "entries": { + "iqpilot_private.navd.iqmapd": { + "path": "python/iqpilot_private/navd/iqmapd.cpython-312-aarch64-linux-gnu.so", + "type": "python-module" + }, + "iqpilot_private.navd.navd": { + "path": "python/iqpilot_private/navd/navd.cpython-312-aarch64-linux-gnu.so", + "type": "python-module" + }, + "iqpilot_private.navd.navrenderd": { + "path": "python/iqpilot_private/navd/navrenderd.cpython-312-aarch64-linux-gnu.so", + "type": "python-module" + }, + "iqpilot_private.navd.web_server": { + "path": "python/iqpilot_private/navd/web_server.cpython-312-aarch64-linux-gnu.so", + "type": "python-module" + } + } + }, + "signatures": { + "python/iqpilot_private/navd/__init__.cpython-312-aarch64-linux-gnu.so": "E3+Gd+btNi+4qFqM+nFMfWgCLJhP21ZgIwZrE/CJjqafB1htd/qOhh9QtrulS46HW5geZ/nwW3jlxPbFeNubBw==", + "python/iqpilot_private/navd/event_builder.cpython-312-aarch64-linux-gnu.so": "hHQQCCPcxRXdO9vko9476AYbgbfNwhi9bVYpoOzQQStPC8DbIO0QN/RRzaJVGr2kgJGZlXDmnGl85m7EHgC1BA==", + "python/iqpilot_private/navd/helpers.cpython-312-aarch64-linux-gnu.so": "Wz5yfWlMeoASj274KqrCWwEJD/wtm5WyxLenceYu7MkXaMlN1QyztvJhq8Y8K8Jfis7R5fQ/aAnlsQgWTIVEBg==", + "python/iqpilot_private/navd/iqmapd.cpython-312-aarch64-linux-gnu.so": "6WFWUXmkyy21rh/9xa7s7S7CTiCRnywOahU859T6iP0447NkVxHRYmXAVCH1/vRDPXASHRNPyUk8oSDLIGK1Ag==", + "python/iqpilot_private/navd/long_decel.cpython-312-aarch64-linux-gnu.so": "ZUsjQ/AGPa5pVKAv3IkycDPIVBWh+4jBNor/xrUi7BBPHki8G6T1grv3exeHhr2WF2JiqN727ge3JaqvlU1KDQ==", + "python/iqpilot_private/navd/mapbox_client.cpython-312-aarch64-linux-gnu.so": "t+VHmWhxf7oKcR5e+tJe6hPKy6xz0bMY84WEhujwcwBlWgIXscfc9aNSP6e4N7GITW7cOn+RlDRTTfwQsA6PDA==", + "python/iqpilot_private/navd/nav_cameras.cpython-312-aarch64-linux-gnu.so": "qOrpSi2I+aFLKN4WjBljfzbk2U1rvOgDuyV+p7IvshugeUOoqTKiYYSG9xwyi2/eB/syyfiVFoqAg+ETFXXPDA==", + "python/iqpilot_private/navd/navd.cpython-312-aarch64-linux-gnu.so": "PcFJWRYrb52pJCbTdsAPm3IC0OR9zt/7P0uc1GlQr4CVGvzfXukbnt/7sSeL8T29w7drf9MfGkpRe4BjbWwtDw==", + "python/iqpilot_private/navd/navrenderd.cpython-312-aarch64-linux-gnu.so": "gp1noPJ35MRVcQL3t4wKbsEH0L0+n9Yf8Zty+VCY7uy9EsK8211kW2YIw7Lcyw879VLLbVntHIWz/BMankSeAw==", + "python/iqpilot_private/navd/osm_cameras.cpython-312-aarch64-linux-gnu.so": "N6yH0R8V/Cw1J3hYPfFeZXByolRwtACwNe2wHMzPEQ69UTWnSNFRXWjOLCEvtwB8Z+iglAqFx8uztZ6wXZKUCQ==", + "python/iqpilot_private/navd/reroute.cpython-312-aarch64-linux-gnu.so": "oQQU70Yp8wzkqB9Ktm2KDEHNVZfB3otqIoG2mkqQNh6QdtVKXraIv6cAbt2Zjob5914C06El2z+NPZQE5NjODQ==", + "python/iqpilot_private/navd/route_manager.cpython-312-aarch64-linux-gnu.so": "ayPdxCUGA/o+AavLMd9mGDY6zYnZk54gAEVaMQXFYlztjXiREC+IEmV1iETOYcsEFAxr8qtUFfcwZ3p7rXljCw==", + "python/iqpilot_private/navd/runtime_common.cpython-312-aarch64-linux-gnu.so": "Rh0wUjYjRabedjFSnsE9BAjILjA1+RHMdfuCqTye1AlhFlXVUQMRo6Ier6Z71kyiOYw+CXCOrHUF9fP69cP4DQ==", + "python/iqpilot_private/navd/turn_desire.cpython-312-aarch64-linux-gnu.so": "13bqig90oceup9ovo8weu9UpdQN/l/pq7REFm8dwwgqquuh8jJFkzNz7dvIhDlaxIGAQwzWJe4+QzIQfhg0jAA==", + "python/iqpilot_private/navd/web_server.cpython-312-aarch64-linux-gnu.so": "Vm1Inx4XY0BOmLuNizOCRNAl4tPDcvJ/4A+TwVSr1cZuMg7pg1jeFg613e6cFM6t08hYvPsysXc2+gD69zkcBA==" + } +} diff --git a/artifacts/iqpilot_navd_private/python/iqpilot_private/__init__.py b/artifacts/iqpilot_navd_private/python/iqpilot_private/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/artifacts/iqpilot_navd_private/python/iqpilot_private/navd/__init__.cpython-312-aarch64-linux-gnu.so b/artifacts/iqpilot_navd_private/python/iqpilot_private/navd/__init__.cpython-312-aarch64-linux-gnu.so new file mode 100755 index 000000000..d69fb38e7 Binary files /dev/null and b/artifacts/iqpilot_navd_private/python/iqpilot_private/navd/__init__.cpython-312-aarch64-linux-gnu.so differ diff --git a/artifacts/iqpilot_navd_private/python/iqpilot_private/navd/__init__.py b/artifacts/iqpilot_navd_private/python/iqpilot_private/navd/__init__.py new file mode 100644 index 000000000..5161d7261 --- /dev/null +++ b/artifacts/iqpilot_navd_private/python/iqpilot_private/navd/__init__.py @@ -0,0 +1,3 @@ +""" +Copyright ©️ Project Teal Lvbs Licensed Under MIT License +""" diff --git a/artifacts/iqpilot_navd_private/python/iqpilot_private/navd/event_builder.cpython-312-aarch64-linux-gnu.so b/artifacts/iqpilot_navd_private/python/iqpilot_private/navd/event_builder.cpython-312-aarch64-linux-gnu.so new file mode 100755 index 000000000..54844133e Binary files /dev/null and b/artifacts/iqpilot_navd_private/python/iqpilot_private/navd/event_builder.cpython-312-aarch64-linux-gnu.so differ diff --git a/artifacts/iqpilot_navd_private/python/iqpilot_private/navd/helpers.cpython-312-aarch64-linux-gnu.so b/artifacts/iqpilot_navd_private/python/iqpilot_private/navd/helpers.cpython-312-aarch64-linux-gnu.so new file mode 100755 index 000000000..d89198be5 Binary files /dev/null and b/artifacts/iqpilot_navd_private/python/iqpilot_private/navd/helpers.cpython-312-aarch64-linux-gnu.so differ diff --git a/artifacts/iqpilot_navd_private/python/iqpilot_private/navd/iqmapd.cpython-312-aarch64-linux-gnu.so b/artifacts/iqpilot_navd_private/python/iqpilot_private/navd/iqmapd.cpython-312-aarch64-linux-gnu.so new file mode 100755 index 000000000..471549ccd Binary files /dev/null and b/artifacts/iqpilot_navd_private/python/iqpilot_private/navd/iqmapd.cpython-312-aarch64-linux-gnu.so differ diff --git a/artifacts/iqpilot_navd_private/python/iqpilot_private/navd/long_decel.cpython-312-aarch64-linux-gnu.so b/artifacts/iqpilot_navd_private/python/iqpilot_private/navd/long_decel.cpython-312-aarch64-linux-gnu.so new file mode 100755 index 000000000..aa225592c Binary files /dev/null and b/artifacts/iqpilot_navd_private/python/iqpilot_private/navd/long_decel.cpython-312-aarch64-linux-gnu.so differ diff --git a/artifacts/iqpilot_navd_private/python/iqpilot_private/navd/mapbox_client.cpython-312-aarch64-linux-gnu.so b/artifacts/iqpilot_navd_private/python/iqpilot_private/navd/mapbox_client.cpython-312-aarch64-linux-gnu.so new file mode 100755 index 000000000..ed72552c6 Binary files /dev/null and b/artifacts/iqpilot_navd_private/python/iqpilot_private/navd/mapbox_client.cpython-312-aarch64-linux-gnu.so differ diff --git a/artifacts/iqpilot_navd_private/python/iqpilot_private/navd/nav_cameras.cpython-312-aarch64-linux-gnu.so b/artifacts/iqpilot_navd_private/python/iqpilot_private/navd/nav_cameras.cpython-312-aarch64-linux-gnu.so new file mode 100755 index 000000000..eebfd4e36 Binary files /dev/null and b/artifacts/iqpilot_navd_private/python/iqpilot_private/navd/nav_cameras.cpython-312-aarch64-linux-gnu.so differ diff --git a/artifacts/iqpilot_navd_private/python/iqpilot_private/navd/navd.cpython-312-aarch64-linux-gnu.so b/artifacts/iqpilot_navd_private/python/iqpilot_private/navd/navd.cpython-312-aarch64-linux-gnu.so new file mode 100755 index 000000000..a71abd796 Binary files /dev/null and b/artifacts/iqpilot_navd_private/python/iqpilot_private/navd/navd.cpython-312-aarch64-linux-gnu.so differ diff --git a/artifacts/iqpilot_navd_private/python/iqpilot_private/navd/navrenderd.cpython-312-aarch64-linux-gnu.so b/artifacts/iqpilot_navd_private/python/iqpilot_private/navd/navrenderd.cpython-312-aarch64-linux-gnu.so new file mode 100755 index 000000000..e768be835 Binary files /dev/null and b/artifacts/iqpilot_navd_private/python/iqpilot_private/navd/navrenderd.cpython-312-aarch64-linux-gnu.so differ diff --git a/artifacts/iqpilot_navd_private/python/iqpilot_private/navd/osm_cameras.cpython-312-aarch64-linux-gnu.so b/artifacts/iqpilot_navd_private/python/iqpilot_private/navd/osm_cameras.cpython-312-aarch64-linux-gnu.so new file mode 100755 index 000000000..e375d9528 Binary files /dev/null and b/artifacts/iqpilot_navd_private/python/iqpilot_private/navd/osm_cameras.cpython-312-aarch64-linux-gnu.so differ diff --git a/artifacts/iqpilot_navd_private/python/iqpilot_private/navd/reroute.cpython-312-aarch64-linux-gnu.so b/artifacts/iqpilot_navd_private/python/iqpilot_private/navd/reroute.cpython-312-aarch64-linux-gnu.so new file mode 100755 index 000000000..f50b41597 Binary files /dev/null and b/artifacts/iqpilot_navd_private/python/iqpilot_private/navd/reroute.cpython-312-aarch64-linux-gnu.so differ diff --git a/artifacts/iqpilot_navd_private/python/iqpilot_private/navd/route_manager.cpython-312-aarch64-linux-gnu.so b/artifacts/iqpilot_navd_private/python/iqpilot_private/navd/route_manager.cpython-312-aarch64-linux-gnu.so new file mode 100755 index 000000000..358a74a70 Binary files /dev/null and b/artifacts/iqpilot_navd_private/python/iqpilot_private/navd/route_manager.cpython-312-aarch64-linux-gnu.so differ diff --git a/artifacts/iqpilot_navd_private/python/iqpilot_private/navd/runtime_common.cpython-312-aarch64-linux-gnu.so b/artifacts/iqpilot_navd_private/python/iqpilot_private/navd/runtime_common.cpython-312-aarch64-linux-gnu.so new file mode 100755 index 000000000..ac3768f15 Binary files /dev/null and b/artifacts/iqpilot_navd_private/python/iqpilot_private/navd/runtime_common.cpython-312-aarch64-linux-gnu.so differ diff --git a/artifacts/iqpilot_navd_private/python/iqpilot_private/navd/turn_desire.cpython-312-aarch64-linux-gnu.so b/artifacts/iqpilot_navd_private/python/iqpilot_private/navd/turn_desire.cpython-312-aarch64-linux-gnu.so new file mode 100755 index 000000000..c87314137 Binary files /dev/null and b/artifacts/iqpilot_navd_private/python/iqpilot_private/navd/turn_desire.cpython-312-aarch64-linux-gnu.so differ diff --git a/artifacts/iqpilot_navd_private/python/iqpilot_private/navd/web_server.cpython-312-aarch64-linux-gnu.so b/artifacts/iqpilot_navd_private/python/iqpilot_private/navd/web_server.cpython-312-aarch64-linux-gnu.so new file mode 100755 index 000000000..0c2597fb4 Binary files /dev/null and b/artifacts/iqpilot_navd_private/python/iqpilot_private/navd/web_server.cpython-312-aarch64-linux-gnu.so differ diff --git a/artifacts/iqpilot_updater_private/manifest.json b/artifacts/iqpilot_updater_private/manifest.json new file mode 100644 index 000000000..d90e7f434 --- /dev/null +++ b/artifacts/iqpilot_updater_private/manifest.json @@ -0,0 +1,34 @@ +{ + "python/iqpilot_private/__init__.py": { + "mode": 420, + "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "size": 0 + }, + "python/iqpilot_private/updater/__init__.py": { + "mode": 420, + "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "size": 0 + }, + "python/iqpilot_private/updater/_pat_blob.cpython-312-aarch64-linux-gnu.so": { + "mode": 493, + "sha256": "167d0e1863f674915dbccc7b289368fbf080a169ac1f2559848157469406c0ac", + "size": 67616 + }, + "python/iqpilot_private/updater/git_remote.cpython-312-aarch64-linux-gnu.so": { + "mode": 493, + "sha256": "a9c2e23e2a9bf56b4c18f57ca1fccc78eb75d1d00dd0c6b0fbbcb6bd31405508", + "size": 70056 + }, + "runtime": { + "entries": { + "iqpilot_private.updater.git_remote": { + "path": "python/iqpilot_private/updater/git_remote.cpython-312-aarch64-linux-gnu.so", + "type": "python-module" + } + } + }, + "signatures": { + "python/iqpilot_private/updater/_pat_blob.cpython-312-aarch64-linux-gnu.so": "tCpaz+su6ncEbjlAW/DUtNen5cJyBy3tie9desujFTToKo1KLcua6dF3n5fqoXBdzJz78RuRev3/9iYxrkvbAg==", + "python/iqpilot_private/updater/git_remote.cpython-312-aarch64-linux-gnu.so": "ZUgATP10Dv92lJmIZ1LpF4gntYF651G+ox1U/wxNZdpcS+wbETOmxwx/PtYph+nbCz9VsCc0ueD21AUcNNwhAQ==" + } +} diff --git a/artifacts/iqpilot_updater_private/python/iqpilot_private/__init__.py b/artifacts/iqpilot_updater_private/python/iqpilot_private/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/artifacts/iqpilot_updater_private/python/iqpilot_private/updater/__init__.py b/artifacts/iqpilot_updater_private/python/iqpilot_private/updater/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/artifacts/iqpilot_updater_private/python/iqpilot_private/updater/_pat_blob.cpython-312-aarch64-linux-gnu.so b/artifacts/iqpilot_updater_private/python/iqpilot_private/updater/_pat_blob.cpython-312-aarch64-linux-gnu.so new file mode 100755 index 000000000..f4d800405 Binary files /dev/null and b/artifacts/iqpilot_updater_private/python/iqpilot_private/updater/_pat_blob.cpython-312-aarch64-linux-gnu.so differ diff --git a/artifacts/iqpilot_updater_private/python/iqpilot_private/updater/git_remote.cpython-312-aarch64-linux-gnu.so b/artifacts/iqpilot_updater_private/python/iqpilot_private/updater/git_remote.cpython-312-aarch64-linux-gnu.so new file mode 100755 index 000000000..4004df4e0 Binary files /dev/null and b/artifacts/iqpilot_updater_private/python/iqpilot_private/updater/git_remote.cpython-312-aarch64-linux-gnu.so differ diff --git a/artifacts/iqpilot_valhalla_runtime/manifest.json b/artifacts/iqpilot_valhalla_runtime/manifest.json new file mode 100644 index 000000000..130eba222 --- /dev/null +++ b/artifacts/iqpilot_valhalla_runtime/manifest.json @@ -0,0 +1,234 @@ +{ + "valhalla_runtime/bin/valhalla_service": { + "sha256": "6bef5c4ca7e1eb385ac1ae00d9ee20924a81a33f3e59c2b644d31430720d408d", + "size": 10754496 + }, + "valhalla_runtime/lib/libLerc.so.4": { + "sha256": "8148fbb2d121a08abf6ffe493a4994086eb43a785059871dbee1647ce115305b", + "size": 526288 + }, + "valhalla_runtime/lib/libbrotlicommon.so.1": { + "sha256": "016600f673a5d1a565cf98f608d8425033593ab78827eccc3384596f70386c8c", + "size": 198784 + }, + "valhalla_runtime/lib/libbrotlidec.so.1": { + "sha256": "b7895f0fd4ad47408cb01fcb959ae6792804c15512526166f9bf48358714d0b5", + "size": 67688 + }, + "valhalla_runtime/lib/libbsd.so.0": { + "sha256": "6c5348ee0e1777f13e148b7492fa311b6f65adb0dc21a001a67ebc18948e6b3e", + "size": 133944 + }, + "valhalla_runtime/lib/libcap.so.2": { + "sha256": "c2f7d3a8594ea3a24b8ffcea8f929e8fbd0d6c24256f8ece625f0be6a3d4e1f0", + "size": 67704 + }, + "valhalla_runtime/lib/libcom_err.so.2": { + "sha256": "6a9d07af8817ed06466027a77fc7f2e8972a4a0e68895738d9acdc35470f416e", + "size": 67432 + }, + "valhalla_runtime/lib/libcrypto.so.3": { + "sha256": "3d51e64ad7113b2ab863b9582866ad2b31e545d1fe9f197c634a65a8f2611786", + "size": 4597928 + }, + "valhalla_runtime/lib/libcurl-gnutls.so.4": { + "sha256": "d739419f4c0abc68119f407f24f3eca0283ad510ed022c90f5f7b0c2a04d249d", + "size": 732568 + }, + "valhalla_runtime/lib/libcurl.so.4": { + "sha256": "b9764fa500494d831b6b5766e9191d1946f1dcd76f2a011aee9707f0831f0828", + "size": 798104 + }, + "valhalla_runtime/lib/libczmq.so.4": { + "sha256": "5a066a2015cf9fd5fca00a82702802538c3500e5bc26123e60da433d421ef308", + "size": 526736 + }, + "valhalla_runtime/lib/libdeflate.so.0": { + "sha256": "fc8c2ebe21ec4eebfccabcd6618b216a1bcf9a55dfc38c5af26e3b0ab835f4eb", + "size": 67384 + }, + "valhalla_runtime/lib/libffi.so.8": { + "sha256": "9dd873a938c4d7df76912d01f6a28aa4aa73d583bd20485a0dbfe5aa9673a191", + "size": 67944 + }, + "valhalla_runtime/lib/libgcrypt.so.20": { + "sha256": "763eeb8f170de52b8bc7268eaf433f72f03c980c04511130229a338a0ee2bb06", + "size": 1000536 + }, + "valhalla_runtime/lib/libgeotiff.so.5": { + "sha256": "8777a15c365f87f0d20d177600bea9cf23633e04841914e5fba7245603bf59fa", + "size": 264144 + }, + "valhalla_runtime/lib/libgmp.so.10": { + "sha256": "39b6701812ed7135f28df49352b3f6664c7a9f56880a3fe50c1b87cd7681db9b", + "size": 526632 + }, + "valhalla_runtime/lib/libgnutls.so.30": { + "sha256": "6b12c4675dbf7fca76bd47228f22b19f4218bc8c72e2f8b5cb6de172dee14961", + "size": 2171912 + }, + "valhalla_runtime/lib/libgpg-error.so.0": { + "sha256": "ff2dccba4993ef97775b70c1ed1144f70dfe8581ee48c1eb25c76878c6cdfdf7", + "size": 198648 + }, + "valhalla_runtime/lib/libgssapi_krb5.so.2": { + "sha256": "35790a9f5a49b1368614a485cb6ba7cc54cb34560831da1bda657f2502641624", + "size": 334304 + }, + "valhalla_runtime/lib/libhogweed.so.6": { + "sha256": "0495ad11a2266d7300436d57859fc8ebceac79e5235a393c42ae91d6fc23ce84", + "size": 329656 + }, + "valhalla_runtime/lib/libidn2.so.0": { + "sha256": "34a2427db57d47458b460ab15a24e7de73b56fbc7b458af837556445f60ffb4e", + "size": 132968 + }, + "valhalla_runtime/lib/libjbig.so.0": { + "sha256": "edfa3b74da686481be974c3f514dbe8487ad8faf258b634ab4227d98b33cb888", + "size": 78448 + }, + "valhalla_runtime/lib/libjpeg.so.8": { + "sha256": "d45014b67ae6df2f15c8fb3e86d37b78375deede47ef73b7c61e8b6b28c8223f", + "size": 395264 + }, + "valhalla_runtime/lib/libk5crypto.so.3": { + "sha256": "35b10e68ef45885838026423e2ea0b4a9fc12fa804081f9806b95bc18512094f", + "size": 198920 + }, + "valhalla_runtime/lib/libkeyutils.so.1": { + "sha256": "a58e06275e8e2010be20f367b7f2263e56130c04cb83523f29ae94555705ea59", + "size": 67432 + }, + "valhalla_runtime/lib/libkrb5.so.3": { + "sha256": "b411523bb29d8aef30f208c91bcead2bd8b8a64067e891686e33fe6a00369663", + "size": 859904 + }, + "valhalla_runtime/lib/libkrb5support.so.0": { + "sha256": "05fe1c19d3717a12d3a6237daa8c71604237f30b723c0a9bc40b7b759a69f9a2", + "size": 68192 + }, + "valhalla_runtime/lib/liblber.so.2": { + "sha256": "92e756c779c3fa77c4e8ad1ef0d97ebc2eaeccb2f9829da521691c52df4129d5", + "size": 67792 + }, + "valhalla_runtime/lib/libldap.so.2": { + "sha256": "6d3332e01734a21c0b1b18e993d6117a31f8227d3859556397704f266e18e262", + "size": 396656 + }, + "valhalla_runtime/lib/liblz4.so.1": { + "sha256": "d1482a2f74152881d8b83708fedae7bca039f5608024f1178dc21a17df9887a7", + "size": 133136 + }, + "valhalla_runtime/lib/liblzma.so.5": { + "sha256": "cb4d37d810d354f1c3be46bd2897ba47c9f7388da7e6470a79e3accfa8e81f41", + "size": 198584 + }, + "valhalla_runtime/lib/libmd.so.0": { + "sha256": "30d117046ce9405745e0d0d4722cff121f532913180bbb9425b38d076319ac08", + "size": 67584 + }, + "valhalla_runtime/lib/libnettle.so.8": { + "sha256": "8fd8ed433f1b993fe8b8aa8d3fa5342955ab581dc5ce418381872cd3df3dbb11", + "size": 395272 + }, + "valhalla_runtime/lib/libnghttp2.so.14": { + "sha256": "943724df4606c5eaeb47b2371da22d08fa3870249cfe90c84cef8e57ed6d8db5", + "size": 198768 + }, + "valhalla_runtime/lib/libnorm.so.1": { + "sha256": "31ec03ead2addce2d5e017e641750bd9612a9250faac2b408b669de1182428b7", + "size": 395208 + }, + "valhalla_runtime/lib/libp11-kit.so.0": { + "sha256": "10a0b7558d6cac2d777cc39a673f85ada4bfc4be7d8a2f2516e00999154b68a4", + "size": 1889776 + }, + "valhalla_runtime/lib/libpgm-5.3.so.0": { + "sha256": "bd9cf5ac5a861376c67579b8b62357df29722242aa3b47c849ae1faec24e4e92", + "size": 330392 + }, + "valhalla_runtime/lib/libprime_server.so.0": { + "sha256": "e8a7afac9407ab2119bd1d4183037577dc19e87068a09bb7d0424895f07fa4f6", + "size": 6565856 + }, + "valhalla_runtime/lib/libproj.so.25": { + "sha256": "7db50e4d982d2535c39403f27596a53ac98f1bd5f9877d43f2bd828a023a732c", + "size": 3740296 + }, + "valhalla_runtime/lib/libprotobuf-lite.so.32": { + "sha256": "e87c7af5028f82824bbff41f892c7d6d1f9c844870890bc3621f338f280007b4", + "size": 854904 + }, + "valhalla_runtime/lib/libpsl.so.5": { + "sha256": "95bd0ee93925255421d4f2adc621ac26e451fae04f613c8edd368c288be43488", + "size": 132888 + }, + "valhalla_runtime/lib/libresolv.so.2": { + "sha256": "e79ba5df3dc6e7d1bc67b16a49f13337ba46bbb01be1c896f0bdfd8e91c8e9f7", + "size": 67720 + }, + "valhalla_runtime/lib/librtmp.so.1": { + "sha256": "0e449c6660fb3dc7e06c56034f41106e9cf48a3baf5d30c1e0b53a48276d0c51", + "size": 133928 + }, + "valhalla_runtime/lib/libsasl2.so.2": { + "sha256": "f936a889bcc2d8b5ac8b89e2b37e4359bbd87cbbe87bd2b2002358dfffe04cce", + "size": 133800 + }, + "valhalla_runtime/lib/libsharpyuv.so.0": { + "sha256": "aa4617020b4bc450ef4382ea93ab3d6bef6571433498794a9b88a5675d52fe6d", + "size": 67520 + }, + "valhalla_runtime/lib/libsodium.so.23": { + "sha256": "3b72f199720e9382acdc372a944fdee980dac2f7fe2e8f6b1eed38843bc6544f", + "size": 264384 + }, + "valhalla_runtime/lib/libsqlite3.so.0": { + "sha256": "43ba1659a6113bb4360f4a6da9e465bca6403d7d1f376015d5a57901ee5639b6", + "size": 1526192 + }, + "valhalla_runtime/lib/libssh.so.4": { + "sha256": "41b9c6f2b4ad8930bc089f8f8e3b483d2d8f0760f9a7aca721a207c1286e7bde", + "size": 466056 + }, + "valhalla_runtime/lib/libssl.so.3": { + "sha256": "f23c6de6c41fb0ecc159f1af052cae3b525de697fae242290e656a242e7bc25d", + "size": 737192 + }, + "valhalla_runtime/lib/libsystemd.so.0": { + "sha256": "f1db21e4bb449d1844ac033d7b6f2e3921e140f386370a4a32f9043ea1437b9e", + "size": 989464 + }, + "valhalla_runtime/lib/libtasn1.so.6": { + "sha256": "039d653f8fdfa01240882b0033aca6887e1e9e6dc39acbea51d658fa75437955", + "size": 133048 + }, + "valhalla_runtime/lib/libtiff.so.6": { + "sha256": "96c0ab894fc511a760a08d33e53083aa9fc95f7bdfd3c3bd81cfdbdf26b592db", + "size": 591816 + }, + "valhalla_runtime/lib/libunistring.so.5": { + "sha256": "639c4c15f94e960450b69475d7282e4760fd5895a6a4f4e85f44bb15b74e512c", + "size": 1771448 + }, + "valhalla_runtime/lib/libuuid.so.1": { + "sha256": "58d35ebee086e9f858cd3e597236f1d744e587a216330e3f1d5f1e9ad011c55c", + "size": 67576 + }, + "valhalla_runtime/lib/libwebp.so.7": { + "sha256": "d921459ab6508b3239218b0e4c2e44dbd2e702aeff4273190bf25d2036373930", + "size": 395616 + }, + "valhalla_runtime/lib/libz.so.1": { + "sha256": "170380b4e7ab28ec86eb090b48df90f84089392cb72fecd5067e5b7a4dc5239f", + "size": 133272 + }, + "valhalla_runtime/lib/libzmq.so.5": { + "sha256": "1d986e7d5a45493b6adce97808ec5307d6c625ea4921e074023481521c2af4d9", + "size": 659312 + }, + "valhalla_runtime/lib/libzstd.so.1": { + "sha256": "fe726238b170b83aa83bf1f8a79109b8b61b9eb45234304511d093bbe5f5fb20", + "size": 657432 + } +} diff --git a/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/bin/valhalla_service b/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/bin/valhalla_service new file mode 100755 index 000000000..5079b847c Binary files /dev/null and b/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/bin/valhalla_service differ diff --git a/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libLerc.so.4 b/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libLerc.so.4 new file mode 100644 index 000000000..bb7ed607b Binary files /dev/null and b/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libLerc.so.4 differ diff --git a/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libbrotlicommon.so.1 b/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libbrotlicommon.so.1 new file mode 100644 index 000000000..abf0f2119 Binary files /dev/null and b/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libbrotlicommon.so.1 differ diff --git a/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libbrotlidec.so.1 b/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libbrotlidec.so.1 new file mode 100644 index 000000000..408f42eca Binary files /dev/null and b/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libbrotlidec.so.1 differ diff --git a/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libbsd.so.0 b/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libbsd.so.0 new file mode 100644 index 000000000..1bdd2de12 Binary files /dev/null and b/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libbsd.so.0 differ diff --git a/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libcap.so.2 b/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libcap.so.2 new file mode 100644 index 000000000..a44fd1ff1 Binary files /dev/null and b/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libcap.so.2 differ diff --git a/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libcom_err.so.2 b/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libcom_err.so.2 new file mode 100644 index 000000000..b35b04b2f Binary files /dev/null and b/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libcom_err.so.2 differ diff --git a/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libcrypto.so.3 b/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libcrypto.so.3 new file mode 100644 index 000000000..cae87834e Binary files /dev/null and b/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libcrypto.so.3 differ diff --git a/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libcurl-gnutls.so.4 b/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libcurl-gnutls.so.4 new file mode 100644 index 000000000..8633971a0 Binary files /dev/null and b/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libcurl-gnutls.so.4 differ diff --git a/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libcurl.so.4 b/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libcurl.so.4 new file mode 100644 index 000000000..5c8bc4381 Binary files /dev/null and b/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libcurl.so.4 differ diff --git a/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libczmq.so.4 b/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libczmq.so.4 new file mode 100644 index 000000000..6492ee5b3 Binary files /dev/null and b/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libczmq.so.4 differ diff --git a/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libdeflate.so.0 b/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libdeflate.so.0 new file mode 100644 index 000000000..dc310b344 Binary files /dev/null and b/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libdeflate.so.0 differ diff --git a/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libffi.so.8 b/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libffi.so.8 new file mode 100644 index 000000000..b61badf40 Binary files /dev/null and b/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libffi.so.8 differ diff --git a/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libgcrypt.so.20 b/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libgcrypt.so.20 new file mode 100644 index 000000000..12edbbeb1 Binary files /dev/null and b/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libgcrypt.so.20 differ diff --git a/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libgeotiff.so.5 b/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libgeotiff.so.5 new file mode 100644 index 000000000..2f0ee5f90 Binary files /dev/null and b/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libgeotiff.so.5 differ diff --git a/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libgmp.so.10 b/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libgmp.so.10 new file mode 100644 index 000000000..bc27e7805 Binary files /dev/null and b/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libgmp.so.10 differ diff --git a/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libgnutls.so.30 b/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libgnutls.so.30 new file mode 100644 index 000000000..918f69314 Binary files /dev/null and b/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libgnutls.so.30 differ diff --git a/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libgpg-error.so.0 b/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libgpg-error.so.0 new file mode 100644 index 000000000..902811740 Binary files /dev/null and b/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libgpg-error.so.0 differ diff --git a/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libgssapi_krb5.so.2 b/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libgssapi_krb5.so.2 new file mode 100644 index 000000000..9971c8381 Binary files /dev/null and b/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libgssapi_krb5.so.2 differ diff --git a/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libhogweed.so.6 b/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libhogweed.so.6 new file mode 100644 index 000000000..9e2729217 Binary files /dev/null and b/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libhogweed.so.6 differ diff --git a/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libidn2.so.0 b/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libidn2.so.0 new file mode 100644 index 000000000..cc368662a Binary files /dev/null and b/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libidn2.so.0 differ diff --git a/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libjbig.so.0 b/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libjbig.so.0 new file mode 100644 index 000000000..401a46bfa Binary files /dev/null and b/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libjbig.so.0 differ diff --git a/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libjpeg.so.8 b/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libjpeg.so.8 new file mode 100644 index 000000000..11a9d2e43 Binary files /dev/null and b/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libjpeg.so.8 differ diff --git a/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libk5crypto.so.3 b/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libk5crypto.so.3 new file mode 100644 index 000000000..d5ebf4147 Binary files /dev/null and b/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libk5crypto.so.3 differ diff --git a/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libkeyutils.so.1 b/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libkeyutils.so.1 new file mode 100644 index 000000000..d94048b4b Binary files /dev/null and b/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libkeyutils.so.1 differ diff --git a/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libkrb5.so.3 b/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libkrb5.so.3 new file mode 100644 index 000000000..4138f0102 Binary files /dev/null and b/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libkrb5.so.3 differ diff --git a/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libkrb5support.so.0 b/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libkrb5support.so.0 new file mode 100644 index 000000000..6946197cf Binary files /dev/null and b/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libkrb5support.so.0 differ diff --git a/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/liblber.so.2 b/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/liblber.so.2 new file mode 100644 index 000000000..9ea129ee9 Binary files /dev/null and b/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/liblber.so.2 differ diff --git a/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libldap.so.2 b/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libldap.so.2 new file mode 100644 index 000000000..3c4871c4f Binary files /dev/null and b/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libldap.so.2 differ diff --git a/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/liblz4.so.1 b/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/liblz4.so.1 new file mode 100644 index 000000000..1b3cb2ee1 Binary files /dev/null and b/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/liblz4.so.1 differ diff --git a/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/liblzma.so.5 b/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/liblzma.so.5 new file mode 100644 index 000000000..c14c6ffbd Binary files /dev/null and b/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/liblzma.so.5 differ diff --git a/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libmd.so.0 b/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libmd.so.0 new file mode 100644 index 000000000..890210262 Binary files /dev/null and b/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libmd.so.0 differ diff --git a/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libnettle.so.8 b/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libnettle.so.8 new file mode 100644 index 000000000..4e32dde37 Binary files /dev/null and b/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libnettle.so.8 differ diff --git a/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libnghttp2.so.14 b/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libnghttp2.so.14 new file mode 100644 index 000000000..f211c1a82 Binary files /dev/null and b/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libnghttp2.so.14 differ diff --git a/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libnorm.so.1 b/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libnorm.so.1 new file mode 100644 index 000000000..2c3c240ec Binary files /dev/null and b/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libnorm.so.1 differ diff --git a/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libp11-kit.so.0 b/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libp11-kit.so.0 new file mode 100644 index 000000000..21760a0b9 Binary files /dev/null and b/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libp11-kit.so.0 differ diff --git a/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libpgm-5.3.so.0 b/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libpgm-5.3.so.0 new file mode 100644 index 000000000..bb659d72d Binary files /dev/null and b/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libpgm-5.3.so.0 differ diff --git a/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libprime_server.so.0 b/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libprime_server.so.0 new file mode 100644 index 000000000..3883d0a23 Binary files /dev/null and b/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libprime_server.so.0 differ diff --git a/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libproj.so.25 b/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libproj.so.25 new file mode 100644 index 000000000..d8f30ad0c Binary files /dev/null and b/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libproj.so.25 differ diff --git a/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libprotobuf-lite.so.32 b/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libprotobuf-lite.so.32 new file mode 100644 index 000000000..81b6b10af Binary files /dev/null and b/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libprotobuf-lite.so.32 differ diff --git a/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libpsl.so.5 b/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libpsl.so.5 new file mode 100644 index 000000000..9c4ea4fd7 Binary files /dev/null and b/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libpsl.so.5 differ diff --git a/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libresolv.so.2 b/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libresolv.so.2 new file mode 100644 index 000000000..06360ad60 Binary files /dev/null and b/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libresolv.so.2 differ diff --git a/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/librtmp.so.1 b/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/librtmp.so.1 new file mode 100644 index 000000000..01d7d1ac6 Binary files /dev/null and b/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/librtmp.so.1 differ diff --git a/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libsasl2.so.2 b/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libsasl2.so.2 new file mode 100644 index 000000000..badf01bec Binary files /dev/null and b/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libsasl2.so.2 differ diff --git a/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libsharpyuv.so.0 b/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libsharpyuv.so.0 new file mode 100644 index 000000000..110fc6ad1 Binary files /dev/null and b/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libsharpyuv.so.0 differ diff --git a/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libsodium.so.23 b/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libsodium.so.23 new file mode 100644 index 000000000..a87cb1166 Binary files /dev/null and b/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libsodium.so.23 differ diff --git a/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libsqlite3.so.0 b/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libsqlite3.so.0 new file mode 100644 index 000000000..247df7fb0 Binary files /dev/null and b/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libsqlite3.so.0 differ diff --git a/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libssh.so.4 b/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libssh.so.4 new file mode 100644 index 000000000..2ef5c4a4c Binary files /dev/null and b/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libssh.so.4 differ diff --git a/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libssl.so.3 b/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libssl.so.3 new file mode 100644 index 000000000..7b76fec8c Binary files /dev/null and b/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libssl.so.3 differ diff --git a/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libsystemd.so.0 b/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libsystemd.so.0 new file mode 100644 index 000000000..a5a91c085 Binary files /dev/null and b/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libsystemd.so.0 differ diff --git a/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libtasn1.so.6 b/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libtasn1.so.6 new file mode 100644 index 000000000..145a1902d Binary files /dev/null and b/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libtasn1.so.6 differ diff --git a/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libtiff.so.6 b/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libtiff.so.6 new file mode 100644 index 000000000..e4146021a Binary files /dev/null and b/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libtiff.so.6 differ diff --git a/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libunistring.so.5 b/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libunistring.so.5 new file mode 100644 index 000000000..b44518b9f Binary files /dev/null and b/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libunistring.so.5 differ diff --git a/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libuuid.so.1 b/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libuuid.so.1 new file mode 100644 index 000000000..0e10134e9 Binary files /dev/null and b/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libuuid.so.1 differ diff --git a/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libwebp.so.7 b/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libwebp.so.7 new file mode 100644 index 000000000..dd228789b Binary files /dev/null and b/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libwebp.so.7 differ diff --git a/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libz.so.1 b/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libz.so.1 new file mode 100644 index 000000000..16dce3683 Binary files /dev/null and b/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libz.so.1 differ diff --git a/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libzmq.so.5 b/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libzmq.so.5 new file mode 100644 index 000000000..daaff4150 Binary files /dev/null and b/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libzmq.so.5 differ diff --git a/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libzstd.so.1 b/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libzstd.so.1 new file mode 100644 index 000000000..5e592595e Binary files /dev/null and b/artifacts/iqpilot_valhalla_runtime/valhalla_runtime/lib/libzstd.so.1 differ diff --git a/artifacts/offline_nav_assets/iqpilot_offline_maps_california_20260309233945.tar.zst.sha256 b/artifacts/offline_nav_assets/iqpilot_offline_maps_california_20260309233945.tar.zst.sha256 new file mode 100644 index 000000000..1f9530bbf --- /dev/null +++ b/artifacts/offline_nav_assets/iqpilot_offline_maps_california_20260309233945.tar.zst.sha256 @@ -0,0 +1 @@ +94b50010dc5be730b389d856cd143d2e21e9a0a31cf4bee01740a0503dbcda0c artifacts/offline_nav_assets/iqpilot_offline_maps_california_20260309233945.tar.zst diff --git a/artifacts/offline_nav_assets/iqpilot_offline_maps_illinois_20260309233729.tar.zst.sha256 b/artifacts/offline_nav_assets/iqpilot_offline_maps_illinois_20260309233729.tar.zst.sha256 new file mode 100644 index 000000000..7726d057b --- /dev/null +++ b/artifacts/offline_nav_assets/iqpilot_offline_maps_illinois_20260309233729.tar.zst.sha256 @@ -0,0 +1 @@ +239ec72f8df0e72e6a2f6432779cb758494e8fa248fdb8fb0fada47e631d98f2 artifacts/offline_nav_assets/iqpilot_offline_maps_illinois_20260309233729.tar.zst diff --git a/artifacts/runtime/apply_boot_branding.py b/artifacts/runtime/apply_boot_branding.py new file mode 100644 index 000000000..284657a45 --- /dev/null +++ b/artifacts/runtime/apply_boot_branding.py @@ -0,0 +1,317 @@ +#!/usr/bin/env python3 + +from __future__ import annotations + +import argparse +import hashlib +import json +import os +import shutil +import subprocess +import sys +import time +from collections.abc import Callable +from pathlib import Path + +_COLOR = sys.stdout.isatty() and os.environ.get("NO_COLOR") is None + + +def _paint(text: str, code: str) -> str: + return f"\033[{code}m{text}\033[0m" if _COLOR else text + + +ROOT = Path(__file__).resolve().parents[2] +ASSET_DIR = ROOT / "artifacts" / "runtime" / "boot_branding" +BG_ASSET = ASSET_DIR / "bg.jpg" +SPLASH_BMP_ASSET = ASSET_DIR / "splash_embedded.bmp" + +BG_TARGET = Path("/usr/comma/bg.jpg") +SPLASH_TARGET = Path("/dev/block/bootdevice/by-name/splash") +SPLASH_BMP_OFFSET = 16384 + +PASSWD_PATH = Path("/etc/passwd") +SHADOW_PATH = Path("/etc/shadow") +IQ_USER = "iq" +IQ_GECOS = "IQ.Pilot" +SOURCE_USER = "comma" + +STATE_DIR = Path("/data/iqpilot_boot_branding") +BACKUP_DIR = STATE_DIR / "backups" +META_PATH = BACKUP_DIR / "metadata.json" +BG_BACKUP = BACKUP_DIR / "bg.jpg.orig" +SPLASH_BACKUP = BACKUP_DIR / "splash.partition.orig.img" + + +def sha256_bytes(data: bytes) -> str: + return hashlib.sha256(data).hexdigest() + + +def sha256_path(path: Path) -> str: + h = hashlib.sha256() + with path.open("rb") as f: + for chunk in iter(lambda: f.read(1024 * 1024), b""): + h.update(chunk) + return h.hexdigest() + + +def run(cmd: list[str], capture_output: bool = False) -> str: + res = subprocess.run(cmd, check=True, text=True, capture_output=capture_output) + return res.stdout.strip() if capture_output else "" + + +def get_root_mount_options() -> str: + return run(["findmnt", "-no", "OPTIONS", "/"], capture_output=True) + + +def remount_root(mode: str) -> None: + run(["mount", "-o", mode, "/"]) + + +def splash_size() -> int: + return int(run(["blockdev", "--getsize64", str(SPLASH_TARGET)], capture_output=True)) + + +def read_splash_bytes(offset: int, size: int) -> bytes: + with SPLASH_TARGET.open("rb") as f: + f.seek(offset) + return f.read(size) + + +def write_splash_bytes(offset: int, data: bytes) -> None: + with SPLASH_TARGET.open("r+b", buffering=0) as f: + f.seek(offset) + f.write(data) + f.flush() + os.fsync(f.fileno()) + + +def backup_if_missing() -> None: + BACKUP_DIR.mkdir(parents=True, exist_ok=True) + + if not BG_BACKUP.exists(): + shutil.copy2(BG_TARGET, BG_BACKUP) + + if not SPLASH_BACKUP.exists(): + with SPLASH_TARGET.open("rb") as src, SPLASH_BACKUP.open("wb") as dst: + remaining = splash_size() + while remaining > 0: + chunk = src.read(min(1024 * 1024, remaining)) + if not chunk: + break + dst.write(chunk) + remaining -= len(chunk) + + if not META_PATH.exists(): + splash_backup_hash = sha256_path(SPLASH_BACKUP) + meta = { + "bg_backup_sha256": sha256_path(BG_BACKUP), + "splash_backup_sha256": splash_backup_hash, + "bg_asset_sha256": sha256_path(BG_ASSET) if BG_ASSET.exists() else None, + "splash_asset_sha256": sha256_path(SPLASH_BMP_ASSET) if SPLASH_BMP_ASSET.exists() else None, + "splash_bmp_offset": SPLASH_BMP_OFFSET, + } + META_PATH.write_text(json.dumps(meta, indent=2) + "\n", encoding="utf-8") + + +def _line_for_user(text: str, user: str) -> str | None: + prefix = f"{user}:" + for line in text.splitlines(): + if line.startswith(prefix): + return line + return None + + +def _expected_passwd_line(source_passwd_line: str) -> str: + """Build the iq passwd line by cloning comma's UID/GID/home/shell.""" + fields = source_passwd_line.split(":") + if len(fields) < 7: + raise ValueError(f"malformed passwd line for {SOURCE_USER!r}: {source_passwd_line!r}") + _name, passwd_x, uid, gid, _gecos, home, shell = fields[:7] + return f"{IQ_USER}:{passwd_x}:{uid}:{gid}:{IQ_GECOS}:{home}:{shell}" + + +def _expected_shadow_line(days_since_epoch: int) -> str: + return f"{IQ_USER}:!:{days_since_epoch}:0:99999:7:::" + + +def _replace_or_append(text: str, user: str, new_line: str) -> str: + prefix = f"{user}:" + out: list[str] = [] + replaced = False + for line in text.splitlines(): + if line.startswith(prefix): + if not replaced: + out.append(new_line) + replaced = True + continue + out.append(line) + if not replaced: + out.append(new_line) + return "\n".join(out) + "\n" + + +def ensure_iq_user( + passwd_path: Path | None = None, + shadow_path: Path | None = None, + remount: Callable[[str], None] | None = None, + root_opts: str | None = None, + now: Callable[[], float] | None = None, +) -> bool: + """Ensure an `iq` user exists as a UID-1000 alias of `comma`. + + Idempotent: returns False when /etc/passwd and /etc/shadow already have a + correctly-shaped `iq` entry, True when either file was modified. + """ + passwd_path = passwd_path if passwd_path is not None else PASSWD_PATH + shadow_path = shadow_path if shadow_path is not None else SHADOW_PATH + remount = remount if remount is not None else remount_root + now = now if now is not None else time.time + + passwd_text = passwd_path.read_text(encoding="utf-8") if passwd_path.exists() else "" + shadow_text = shadow_path.read_text(encoding="utf-8") if shadow_path.exists() else "" + + source_line = _line_for_user(passwd_text, SOURCE_USER) + if source_line is None: + # Without a comma user to clone from we'd create a broken iq entry; bail. + return False + + expected_passwd = _expected_passwd_line(source_line) + passwd_line = _line_for_user(passwd_text, IQ_USER) + shadow_line = _line_for_user(shadow_text, IQ_USER) + + passwd_ok = passwd_line == expected_passwd + shadow_ok = shadow_line is not None and shadow_line.startswith(f"{IQ_USER}:!:") + if passwd_ok and shadow_ok: + return False + + days = int(now() // 86400) + new_passwd = _replace_or_append(passwd_text, IQ_USER, expected_passwd) + new_shadow = _replace_or_append(shadow_text, IQ_USER, _expected_shadow_line(days)) + + opts = root_opts if root_opts is not None else get_root_mount_options() + remounted = False + try: + remount("remount,rw") + remounted = True + if not passwd_ok: + passwd_path.write_text(new_passwd, encoding="utf-8") + if not shadow_ok: + shadow_path.write_text(new_shadow, encoding="utf-8") + finally: + if remounted: + remount(f"remount,{opts}") + + return True + + +def apply_branding() -> bool: + if not BG_ASSET.exists() or not SPLASH_BMP_ASSET.exists(): + return False + + backup_if_missing() + + bg_asset_bytes = BG_ASSET.read_bytes() + splash_asset_bytes = SPLASH_BMP_ASSET.read_bytes() + + current_bg_hash = sha256_path(BG_TARGET) + current_splash_hash = sha256_bytes(read_splash_bytes(SPLASH_BMP_OFFSET, len(splash_asset_bytes))) + + bg_asset_hash = sha256_bytes(bg_asset_bytes) + splash_asset_hash = sha256_bytes(splash_asset_bytes) + + if current_bg_hash == bg_asset_hash and current_splash_hash == splash_asset_hash: + return False + + root_opts = get_root_mount_options() + remounted = False + try: + if current_bg_hash != bg_asset_hash: + remount_root("remount,rw") + remounted = True + BG_TARGET.write_bytes(bg_asset_bytes) + + if current_splash_hash != splash_asset_hash: + write_splash_bytes(SPLASH_BMP_OFFSET, splash_asset_bytes) + run(["sync"]) + finally: + if remounted: + remount_root(f"remount,{root_opts}") + + return True + + +def restore_branding() -> bool: + if not BG_BACKUP.exists() or not SPLASH_BACKUP.exists(): + return False + + root_opts = get_root_mount_options() + remounted = False + try: + remount_root("remount,rw") + remounted = True + shutil.copy2(BG_BACKUP, BG_TARGET) + + with SPLASH_BACKUP.open("rb") as src, SPLASH_TARGET.open("r+b", buffering=0) as dst: + while True: + chunk = src.read(1024 * 1024) + if not chunk: + break + dst.write(chunk) + dst.flush() + os.fsync(dst.fileno()) + run(["sync"]) + finally: + if remounted: + remount_root(f"remount,{root_opts}") + + return True + + +def status() -> dict[str, object]: + result: dict[str, object] = { + "bg_asset_exists": BG_ASSET.exists(), + "splash_asset_exists": SPLASH_BMP_ASSET.exists(), + "bg_backup_exists": BG_BACKUP.exists(), + "splash_backup_exists": SPLASH_BACKUP.exists(), + } + if BG_ASSET.exists() and BG_TARGET.exists(): + result["bg_asset_sha256"] = sha256_path(BG_ASSET) + result["bg_current_sha256"] = sha256_path(BG_TARGET) + if SPLASH_BMP_ASSET.exists() and SPLASH_TARGET.exists(): + splash_asset_bytes = SPLASH_BMP_ASSET.read_bytes() + result["splash_asset_sha256"] = sha256_bytes(splash_asset_bytes) + result["splash_current_sha256"] = sha256_bytes(read_splash_bytes(SPLASH_BMP_OFFSET, len(splash_asset_bytes))) + return result + + +def main() -> int: + if os.geteuid() != 0: + raise SystemExit("apply_boot_branding.py must run as root") + + parser = argparse.ArgumentParser() + group = parser.add_mutually_exclusive_group() + group.add_argument("--restore", action="store_true") + group.add_argument("--status", action="store_true") + args = parser.parse_args() + + if args.status: + print(json.dumps(status(), indent=2)) + return 0 + + changed = restore_branding() if args.restore else apply_branding() + + try: + iq_changed = ensure_iq_user() + except Exception as e: # noqa: BLE001 + iq_changed = False + print(_paint(f"ensure_iq_user failed: {e}", "1;38;5;203")) + + if changed or iq_changed: + print(_paint("changed", "38;5;114")) + else: + print(_paint("no-change", "2;38;5;246")) + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/artifacts/runtime/boot_branding/bg.jpg b/artifacts/runtime/boot_branding/bg.jpg new file mode 100644 index 000000000..90851aefe Binary files /dev/null and b/artifacts/runtime/boot_branding/bg.jpg differ diff --git a/artifacts/runtime/boot_branding/splash_embedded.bmp b/artifacts/runtime/boot_branding/splash_embedded.bmp new file mode 100644 index 000000000..71b3dab6f Binary files /dev/null and b/artifacts/runtime/boot_branding/splash_embedded.bmp differ diff --git a/artifacts/runtime/ensure_private_installed.sh b/artifacts/runtime/ensure_private_installed.sh new file mode 100644 index 000000000..6593075ae --- /dev/null +++ b/artifacts/runtime/ensure_private_installed.sh @@ -0,0 +1,381 @@ +#!/usr/bin/env bash +set -euo pipefail + +ROOT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )/../.." >/dev/null && pwd )" +INSTALL_ROOT="${ROOT_DIR}/.iqpilot" +BUNDLES_ROOT="${INSTALL_ROOT}/bundles" +VERIFY_SCRIPT="${ROOT_DIR}/artifacts/runtime/verify_proprietary_bundle.py" + +manifest_hash() { + local manifest_path="$1" + if command -v sha256sum >/dev/null 2>&1; then + sha256sum "${manifest_path}" | awk '{print $1}' + else + shasum -a 256 "${manifest_path}" | awk '{print $1}' + fi +} + +NAVD_BUNDLE="${ROOT_DIR}/artifacts/iqpilot_navd_private" +HEPHA_BUNDLE="${ROOT_DIR}/artifacts/iqpilot_hephaestusd_private" +MODEL_SELECTOR_BUNDLE="${ROOT_DIR}/artifacts/iqpilot_model_selector_private" +ALC_BUNDLE="${ROOT_DIR}/artifacts/iqpilot_alc_private" +COMMANDER_BUNDLE="${ROOT_DIR}/artifacts/iqpilot_commander_private" +UPDATER_BUNDLE="${ROOT_DIR}/artifacts/iqpilot_updater_private" +VALHALLA_RUNTIME_BUNDLE="${ROOT_DIR}/artifacts/iqpilot_valhalla_runtime" +BUNDLE_INSTALL_NAVD="${BUNDLES_ROOT}/iqpilot_navd_private" +BUNDLE_INSTALL_HEPHA="${BUNDLES_ROOT}/iqpilot_hephaestusd_private" +BUNDLE_INSTALL_MODEL_SELECTOR="${BUNDLES_ROOT}/iqpilot_model_selector_private" +BUNDLE_INSTALL_ALC="${BUNDLES_ROOT}/iqpilot_alc_private" +BUNDLE_INSTALL_COMMANDER="${BUNDLES_ROOT}/iqpilot_commander_private" +BUNDLE_INSTALL_UPDATER="${BUNDLES_ROOT}/iqpilot_updater_private" +BUNDLE_INSTALL_VALHALLA_RUNTIME="${BUNDLES_ROOT}/iqpilot_valhalla_runtime" +NAVD_STATE_FILE="${INSTALL_ROOT}/.installed_navd_manifest.sha256" +HEPHA_STATE_FILE="${INSTALL_ROOT}/.installed_hephaestusd_manifest.sha256" +MODEL_SELECTOR_STATE_FILE="${INSTALL_ROOT}/.installed_model_selector_manifest.sha256" +ALC_STATE_FILE="${INSTALL_ROOT}/.installed_alc_manifest.sha256" +COMMANDER_STATE_FILE="${INSTALL_ROOT}/.installed_commander_manifest.sha256" +UPDATER_STATE_FILE="${INSTALL_ROOT}/.installed_updater_manifest.sha256" +VALHALLA_RUNTIME_STATE_FILE="${INSTALL_ROOT}/.installed_valhalla_runtime_manifest.sha256" +NAVD_SENTINEL_BASE="${BUNDLE_INSTALL_NAVD}/python/iqpilot_private/navd/navd" +HEPHA_SENTINEL_BASE="${BUNDLE_INSTALL_HEPHA}/python/iqpilot_private/konn3kt/hephaestus/hephaestusd" +MODEL_SELECTOR_SENTINEL_BASE="${BUNDLE_INSTALL_MODEL_SELECTOR}/python/iqpilot_private/models/manager" +ALC_SENTINEL_BASE="${BUNDLE_INSTALL_ALC}/python/iqpilot_private/konn3kt/iqlvbs/alc" +COMMANDER_SENTINEL_BASE="${BUNDLE_INSTALL_COMMANDER}/python/iqpilot_private/konn3kt/iqlvbs/iqlvbs_commander" +UPDATER_SENTINEL_BASE="${BUNDLE_INSTALL_UPDATER}/python/iqpilot_private/updater/git_remote" +VALHALLA_RUNTIME_SENTINEL="${BUNDLE_INSTALL_VALHALLA_RUNTIME}/valhalla_runtime/bin/valhalla_service" + +NAVD_HASH="" +HEPHA_HASH="" +MODEL_SELECTOR_HASH="" +ALC_HASH="" +COMMANDER_HASH="" +UPDATER_HASH="" +VALHALLA_RUNTIME_HASH="" +NEED_INSTALL=0 +HAVE_BUNDLE=0 + +seed_hepha_ble_runtime() { + if [ ! -f "${HEPHA_BUNDLE}/manifest.json" ]; then + return 0 + fi + + PYTHONPATH="${ROOT_DIR}" python3 <<'PY' +from pathlib import Path +import os +import secrets + +BLE_ENABLE_PARAM = "Konn3ktBleTransportEnabled" +PRIMARY_AUTH_DIR = Path("/data/konn3kt_ble") +FALLBACK_AUTH_DIR = Path("/data/openpilot/.konn3kt_ble") + + +def ensure_dir(path: Path, mode: int) -> bool: + try: + path.mkdir(parents=True, exist_ok=True) + try: + os.chmod(path, mode) + except Exception: + pass + return True + except Exception: + return False + + +def write_if_missing(path: Path, contents: str, mode: int) -> None: + if path.exists(): + return + path.write_text(contents, encoding="utf-8") + try: + os.chmod(path, mode) + except Exception: + pass + + +def seed_ble_enable_param() -> None: + try: + from openpilot.common.params import Params + params = Params() + if params.get(BLE_ENABLE_PARAM) is None: + params.put_bool(BLE_ENABLE_PARAM, True) + return + except Exception: + pass + + param_path = Path("/data/params/d") / BLE_ENABLE_PARAM + if ensure_dir(param_path.parent, 0o755): + try: + if not param_path.exists(): + param_path.write_bytes(b"1") + except Exception: + pass + + +def seed_ble_auth_artifacts() -> None: + auth_dir = None + for candidate in (PRIMARY_AUTH_DIR, FALLBACK_AUTH_DIR): + if ensure_dir(candidate, 0o700): + auth_dir = candidate + break + + if auth_dir is None: + return + + write_if_missing(auth_dir / "approved_clients.json", "{}\n", 0o600) + write_if_missing(auth_dir / "install_credentials.json", "{}\n", 0o600) + write_if_missing(auth_dir / "dev_secret", f"{secrets.token_hex(32)}\n", 0o600) + + +seed_ble_enable_param() +seed_ble_auth_artifacts() +PY + + if [ -d /usr/lib/python3/dist-packages/gi ]; then + ln -sfn /usr/lib/python3/dist-packages/gi "${ROOT_DIR}/gi" + fi +} + +module_present() { + local base_path="$1" + if [ -f "${base_path}.pyc" ]; then + return 0 + fi + if compgen -G "${base_path}".*.so >/dev/null; then + return 0 + fi + return 1 +} + +apply_manifest_modes() { + local bundle_root="$1" + if [ ! -f "${bundle_root}/manifest.json" ]; then + return 0 + fi + + python3 - "${bundle_root}" <<'PY' +from pathlib import Path +import json +import os +import sys + +root = Path(sys.argv[1]) +manifest = json.loads((root / "manifest.json").read_text(encoding="utf-8")) +for rel, meta in manifest.items(): + if rel in {"signatures", "runtime"} or not isinstance(meta, dict) or "mode" not in meta: + continue + path = root / rel + if path.exists(): + os.chmod(path, int(meta["mode"])) +PY +} + +installed_bundle_valid() { + local bundle_root="$1" + if [ ! -f "${bundle_root}/manifest.json" ]; then + return 1 + fi + apply_manifest_modes "${bundle_root}" || return 1 + python3 "${VERIFY_SCRIPT}" "${bundle_root}" >/dev/null 2>&1 +} + +if [ -f "${NAVD_BUNDLE}/manifest.json" ]; then + apply_manifest_modes "${NAVD_BUNDLE}" || true + HAVE_BUNDLE=1 + NAVD_HASH="$(manifest_hash "${NAVD_BUNDLE}/manifest.json")" + NAVD_DST_HASH="" + if [ -f "${NAVD_STATE_FILE}" ]; then + NAVD_DST_HASH="$(cat "${NAVD_STATE_FILE}" 2>/dev/null || true)" + fi + if ! module_present "${NAVD_SENTINEL_BASE}" || [ "${NAVD_HASH}" != "${NAVD_DST_HASH}" ] || ! installed_bundle_valid "${BUNDLE_INSTALL_NAVD}"; then + NEED_INSTALL=1 + fi +fi + +if [ -f "${HEPHA_BUNDLE}/manifest.json" ]; then + apply_manifest_modes "${HEPHA_BUNDLE}" || true + HAVE_BUNDLE=1 + HEPHA_HASH="$(manifest_hash "${HEPHA_BUNDLE}/manifest.json")" + HEPHA_DST_HASH="" + if [ -f "${HEPHA_STATE_FILE}" ]; then + HEPHA_DST_HASH="$(cat "${HEPHA_STATE_FILE}" 2>/dev/null || true)" + fi + if ! module_present "${HEPHA_SENTINEL_BASE}" || [ "${HEPHA_HASH}" != "${HEPHA_DST_HASH}" ] || ! installed_bundle_valid "${BUNDLE_INSTALL_HEPHA}"; then + NEED_INSTALL=1 + fi +fi + +if [ -f "${MODEL_SELECTOR_BUNDLE}/manifest.json" ]; then + apply_manifest_modes "${MODEL_SELECTOR_BUNDLE}" || true + HAVE_BUNDLE=1 + MODEL_SELECTOR_HASH="$(manifest_hash "${MODEL_SELECTOR_BUNDLE}/manifest.json")" + MODEL_SELECTOR_DST_HASH="" + if [ -f "${MODEL_SELECTOR_STATE_FILE}" ]; then + MODEL_SELECTOR_DST_HASH="$(cat "${MODEL_SELECTOR_STATE_FILE}" 2>/dev/null || true)" + fi + if ! module_present "${MODEL_SELECTOR_SENTINEL_BASE}" || [ "${MODEL_SELECTOR_HASH}" != "${MODEL_SELECTOR_DST_HASH}" ] || ! installed_bundle_valid "${BUNDLE_INSTALL_MODEL_SELECTOR}"; then + NEED_INSTALL=1 + fi +fi + +if [ -f "${ALC_BUNDLE}/manifest.json" ]; then + apply_manifest_modes "${ALC_BUNDLE}" || true + HAVE_BUNDLE=1 + ALC_HASH="$(manifest_hash "${ALC_BUNDLE}/manifest.json")" + ALC_DST_HASH="" + if [ -f "${ALC_STATE_FILE}" ]; then + ALC_DST_HASH="$(cat "${ALC_STATE_FILE}" 2>/dev/null || true)" + fi + if ! module_present "${ALC_SENTINEL_BASE}" || [ "${ALC_HASH}" != "${ALC_DST_HASH}" ] || ! installed_bundle_valid "${BUNDLE_INSTALL_ALC}"; then + NEED_INSTALL=1 + fi +fi + +if [ -f "${COMMANDER_BUNDLE}/manifest.json" ]; then + apply_manifest_modes "${COMMANDER_BUNDLE}" || true + HAVE_BUNDLE=1 + COMMANDER_HASH="$(manifest_hash "${COMMANDER_BUNDLE}/manifest.json")" + COMMANDER_DST_HASH="" + if [ -f "${COMMANDER_STATE_FILE}" ]; then + COMMANDER_DST_HASH="$(cat "${COMMANDER_STATE_FILE}" 2>/dev/null || true)" + fi + if ! module_present "${COMMANDER_SENTINEL_BASE}" || [ "${COMMANDER_HASH}" != "${COMMANDER_DST_HASH}" ] || ! installed_bundle_valid "${BUNDLE_INSTALL_COMMANDER}"; then + NEED_INSTALL=1 + fi +fi + +if [ -f "${UPDATER_BUNDLE}/manifest.json" ]; then + apply_manifest_modes "${UPDATER_BUNDLE}" || true + HAVE_BUNDLE=1 + UPDATER_HASH="$(manifest_hash "${UPDATER_BUNDLE}/manifest.json")" + UPDATER_DST_HASH="" + if [ -f "${UPDATER_STATE_FILE}" ]; then + UPDATER_DST_HASH="$(cat "${UPDATER_STATE_FILE}" 2>/dev/null || true)" + fi + if ! module_present "${UPDATER_SENTINEL_BASE}" || [ "${UPDATER_HASH}" != "${UPDATER_DST_HASH}" ] || ! installed_bundle_valid "${BUNDLE_INSTALL_UPDATER}"; then + NEED_INSTALL=1 + fi +fi + +if [ -f "${VALHALLA_RUNTIME_BUNDLE}/manifest.json" ]; then + apply_manifest_modes "${VALHALLA_RUNTIME_BUNDLE}" || true + HAVE_BUNDLE=1 + VALHALLA_RUNTIME_HASH="$(manifest_hash "${VALHALLA_RUNTIME_BUNDLE}/manifest.json")" + VALHALLA_RUNTIME_DST_HASH="" + if [ -f "${VALHALLA_RUNTIME_STATE_FILE}" ]; then + VALHALLA_RUNTIME_DST_HASH="$(cat "${VALHALLA_RUNTIME_STATE_FILE}" 2>/dev/null || true)" + fi + if [ ! -x "${VALHALLA_RUNTIME_SENTINEL}" ] || [ "${VALHALLA_RUNTIME_HASH}" != "${VALHALLA_RUNTIME_DST_HASH}" ]; then + NEED_INSTALL=1 + fi +fi + +if [ "${HAVE_BUNDLE}" -eq 0 ]; then + exit 0 +fi + +if [ "${NEED_INSTALL}" -eq 0 ]; then + seed_hepha_ble_runtime || true + exit 0 +fi + +TMP_ROOT="${INSTALL_ROOT}.tmp.$$" +echo "Installing bundled private artifacts..." +rm -rf "${TMP_ROOT}" +mkdir -p "${TMP_ROOT}" +mkdir -p "${TMP_ROOT}/bundles" + +if [ -d "${INSTALL_ROOT}" ]; then + cp -a "${INSTALL_ROOT}"/. "${TMP_ROOT}/" +fi + +if [ -f "${NAVD_BUNDLE}/manifest.json" ]; then + python3 "${VERIFY_SCRIPT}" "${NAVD_BUNDLE}" + rm -rf "${TMP_ROOT}/bundles/iqpilot_navd_private" + mkdir -p "${TMP_ROOT}/bundles/iqpilot_navd_private" + cp -a "${NAVD_BUNDLE}"/. "${TMP_ROOT}/bundles/iqpilot_navd_private/" + apply_manifest_modes "${TMP_ROOT}/bundles/iqpilot_navd_private" +fi + +if [ -f "${HEPHA_BUNDLE}/manifest.json" ]; then + python3 "${VERIFY_SCRIPT}" "${HEPHA_BUNDLE}" + rm -rf "${TMP_ROOT}/bundles/iqpilot_hephaestusd_private" + mkdir -p "${TMP_ROOT}/bundles/iqpilot_hephaestusd_private" + cp -a "${HEPHA_BUNDLE}"/. "${TMP_ROOT}/bundles/iqpilot_hephaestusd_private/" + apply_manifest_modes "${TMP_ROOT}/bundles/iqpilot_hephaestusd_private" +fi + +if [ -f "${MODEL_SELECTOR_BUNDLE}/manifest.json" ]; then + python3 "${VERIFY_SCRIPT}" "${MODEL_SELECTOR_BUNDLE}" + rm -rf "${TMP_ROOT}/bundles/iqpilot_model_selector_private" + mkdir -p "${TMP_ROOT}/bundles/iqpilot_model_selector_private" + cp -a "${MODEL_SELECTOR_BUNDLE}"/. "${TMP_ROOT}/bundles/iqpilot_model_selector_private/" + apply_manifest_modes "${TMP_ROOT}/bundles/iqpilot_model_selector_private" +fi + +if [ -f "${ALC_BUNDLE}/manifest.json" ]; then + python3 "${VERIFY_SCRIPT}" "${ALC_BUNDLE}" + rm -rf "${TMP_ROOT}/bundles/iqpilot_alc_private" + mkdir -p "${TMP_ROOT}/bundles/iqpilot_alc_private" + cp -a "${ALC_BUNDLE}"/. "${TMP_ROOT}/bundles/iqpilot_alc_private/" + apply_manifest_modes "${TMP_ROOT}/bundles/iqpilot_alc_private" +fi + +if [ -f "${COMMANDER_BUNDLE}/manifest.json" ]; then + python3 "${VERIFY_SCRIPT}" "${COMMANDER_BUNDLE}" + rm -rf "${TMP_ROOT}/bundles/iqpilot_commander_private" + mkdir -p "${TMP_ROOT}/bundles/iqpilot_commander_private" + cp -a "${COMMANDER_BUNDLE}"/. "${TMP_ROOT}/bundles/iqpilot_commander_private/" + apply_manifest_modes "${TMP_ROOT}/bundles/iqpilot_commander_private" +fi + +if [ -f "${UPDATER_BUNDLE}/manifest.json" ]; then + python3 "${VERIFY_SCRIPT}" "${UPDATER_BUNDLE}" + rm -rf "${TMP_ROOT}/bundles/iqpilot_updater_private" + mkdir -p "${TMP_ROOT}/bundles/iqpilot_updater_private" + cp -a "${UPDATER_BUNDLE}"/. "${TMP_ROOT}/bundles/iqpilot_updater_private/" + apply_manifest_modes "${TMP_ROOT}/bundles/iqpilot_updater_private" +fi + +if [ -f "${VALHALLA_RUNTIME_BUNDLE}/manifest.json" ]; then + python3 "${VERIFY_SCRIPT}" "${VALHALLA_RUNTIME_BUNDLE}" + rm -rf "${TMP_ROOT}/bundles/iqpilot_valhalla_runtime" + mkdir -p "${TMP_ROOT}/bundles/iqpilot_valhalla_runtime" + cp -a "${VALHALLA_RUNTIME_BUNDLE}"/. "${TMP_ROOT}/bundles/iqpilot_valhalla_runtime/" + apply_manifest_modes "${TMP_ROOT}/bundles/iqpilot_valhalla_runtime" +fi + +if [ -d "${INSTALL_ROOT}" ]; then + rm -rf "${INSTALL_ROOT}.bak" + mv "${INSTALL_ROOT}" "${INSTALL_ROOT}.bak" +fi + +mv "${TMP_ROOT}" "${INSTALL_ROOT}" + +if [ -d "${INSTALL_ROOT}.bak" ]; then + rm -rf "${INSTALL_ROOT}.bak" +fi + +if [ -n "${NAVD_HASH}" ]; then + printf '%s\n' "${NAVD_HASH}" > "${NAVD_STATE_FILE}" +fi +if [ -n "${HEPHA_HASH}" ]; then + printf '%s\n' "${HEPHA_HASH}" > "${HEPHA_STATE_FILE}" +fi +if [ -n "${MODEL_SELECTOR_HASH}" ]; then + printf '%s\n' "${MODEL_SELECTOR_HASH}" > "${MODEL_SELECTOR_STATE_FILE}" +fi +if [ -n "${ALC_HASH}" ]; then + printf '%s\n' "${ALC_HASH}" > "${ALC_STATE_FILE}" +fi +if [ -n "${COMMANDER_HASH}" ]; then + printf '%s\n' "${COMMANDER_HASH}" > "${COMMANDER_STATE_FILE}" +fi +if [ -n "${UPDATER_HASH}" ]; then + printf '%s\n' "${UPDATER_HASH}" > "${UPDATER_STATE_FILE}" +fi +if [ -n "${VALHALLA_RUNTIME_HASH}" ]; then + printf '%s\n' "${VALHALLA_RUNTIME_HASH}" > "${VALHALLA_RUNTIME_STATE_FILE}" +fi + +seed_hepha_ble_runtime || true + +echo "Private artifacts installed." diff --git a/artifacts/runtime/test_apply_boot_branding.py b/artifacts/runtime/test_apply_boot_branding.py new file mode 100644 index 000000000..e741bf143 --- /dev/null +++ b/artifacts/runtime/test_apply_boot_branding.py @@ -0,0 +1,216 @@ +#!/usr/bin/env python3 + +from __future__ import annotations + +from pathlib import Path + +import pytest + +from artifacts.runtime import apply_boot_branding as abb + + +COMMA_HOME = "/home/comma" + +EXISTING_PASSWD = ( + "root:x:0:0:root:/root:/bin/bash\n" + "daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin\n" + f"comma:x:1000:1000:comma:{COMMA_HOME}:/bin/bash\n" + "nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin\n" +) + +EXISTING_SHADOW = ( + "root:*:19000:0:99999:7:::\n" + "daemon:*:19000:0:99999:7:::\n" + "comma:$6$abc$xyz:19000:0:99999:7:::\n" + "nobody:!:19000:0:99999:7:::\n" +) + +EXPECTED_PASSWD_LINE = f"iq:x:1000:1000:IQ.Pilot:{COMMA_HOME}:/bin/bash" + + +@pytest.fixture +def etc(tmp_path: Path) -> tuple[Path, Path]: + passwd = tmp_path / "passwd" + shadow = tmp_path / "shadow" + passwd.write_text(EXISTING_PASSWD, encoding="utf-8") + shadow.write_text(EXISTING_SHADOW, encoding="utf-8") + return passwd, shadow + + +@pytest.fixture +def fake_remount(): + calls: list[str] = [] + + def _remount(mode: str) -> None: + calls.append(mode) + + _remount.calls = calls + return _remount + + +def _frozen_now() -> float: + return 1779494400.0 + + +def test_fresh_install_adds_iq_user(etc, fake_remount): + passwd, shadow = etc + + changed = abb.ensure_iq_user( + passwd_path=passwd, + shadow_path=shadow, + remount=fake_remount, + root_opts="ro,relatime", + now=_frozen_now, + ) + + assert changed is True + passwd_text = passwd.read_text(encoding="utf-8") + shadow_text = shadow.read_text(encoding="utf-8") + + assert EXPECTED_PASSWD_LINE in passwd_text.splitlines() + assert any(l.startswith("iq:!:") and l.endswith(":0:99999:7:::") for l in shadow_text.splitlines()) + + for original_line in EXISTING_PASSWD.splitlines(): + assert original_line in passwd_text.splitlines() + for original_line in EXISTING_SHADOW.splitlines(): + assert original_line in shadow_text.splitlines() + + assert fake_remount.calls == ["remount,rw", "remount,ro,relatime"] + + +def test_second_boot_is_noop(etc, fake_remount): + passwd, shadow = etc + + abb.ensure_iq_user(passwd_path=passwd, shadow_path=shadow, remount=fake_remount, root_opts="ro", now=_frozen_now) + passwd_after_first = passwd.read_text(encoding="utf-8") + shadow_after_first = shadow.read_text(encoding="utf-8") + fake_remount.calls.clear() + + changed = abb.ensure_iq_user( + passwd_path=passwd, shadow_path=shadow, remount=fake_remount, root_opts="ro", now=_frozen_now, + ) + + assert changed is False + assert passwd.read_text(encoding="utf-8") == passwd_after_first + assert shadow.read_text(encoding="utf-8") == shadow_after_first + assert fake_remount.calls == [] # no remount when nothing to do + + +def test_partial_state_self_heals(etc, fake_remount): + passwd, shadow = etc + passwd.write_text(EXISTING_PASSWD + EXPECTED_PASSWD_LINE + "\n", encoding="utf-8") + # shadow lacks iq + + changed = abb.ensure_iq_user( + passwd_path=passwd, shadow_path=shadow, remount=fake_remount, root_opts="ro", now=_frozen_now, + ) + + assert changed is True + iq_passwd_lines = [l for l in passwd.read_text(encoding="utf-8").splitlines() if l.startswith("iq:")] + iq_shadow_lines = [l for l in shadow.read_text(encoding="utf-8").splitlines() if l.startswith("iq:")] + assert iq_passwd_lines == [EXPECTED_PASSWD_LINE] # not duplicated + assert len(iq_shadow_lines) == 1 + assert iq_shadow_lines[0].startswith("iq:!:") + + +def test_malformed_iq_passwd_line_replaced(etc, fake_remount): + passwd, shadow = etc + wrong = "iq:x:1000:1000:old:/root:/bin/sh" + passwd.write_text(EXISTING_PASSWD + wrong + "\n", encoding="utf-8") + + changed = abb.ensure_iq_user( + passwd_path=passwd, shadow_path=shadow, remount=fake_remount, root_opts="ro", now=_frozen_now, + ) + + assert changed is True + iq_lines = [l for l in passwd.read_text(encoding="utf-8").splitlines() if l.startswith("iq:")] + assert iq_lines == [EXPECTED_PASSWD_LINE] + + +def test_remount_ro_restored_on_write_failure(tmp_path, fake_remount): + passwd = tmp_path / "ro_passwd" + passwd.write_text(EXISTING_PASSWD, encoding="utf-8") + passwd.chmod(0o444) # force write to raise + + shadow = tmp_path / "shadow" + shadow.write_text(EXISTING_SHADOW, encoding="utf-8") + + with pytest.raises(PermissionError): + abb.ensure_iq_user( + passwd_path=passwd, shadow_path=shadow, remount=fake_remount, root_opts="ro", now=_frozen_now, + ) + + assert fake_remount.calls == ["remount,rw", "remount,ro"] + + +def test_does_not_alter_existing_branding_flow(monkeypatch, etc, fake_remount, capsys): + passwd, shadow = etc + + monkeypatch.setattr(abb, "PASSWD_PATH", passwd) + monkeypatch.setattr(abb, "SHADOW_PATH", shadow) + monkeypatch.setattr(abb, "remount_root", fake_remount) + monkeypatch.setattr(abb, "get_root_mount_options", lambda: "ro,relatime") + monkeypatch.setattr(abb.os, "geteuid", lambda: 0) + monkeypatch.setattr(abb, "apply_branding", lambda: False) # no assets → branding no-op + monkeypatch.setattr("sys.argv", ["apply_boot_branding.py"]) + + rc = abb.main() + out = capsys.readouterr().out.strip().splitlines() + + assert rc == 0 + assert out[-1] == "changed" # iq_changed flips combined result + assert EXPECTED_PASSWD_LINE in passwd.read_text(encoding="utf-8").splitlines() + + +def test_iq_home_tracks_commas_actual_home(tmp_path, fake_remount): + passwd = tmp_path / "passwd" + shadow = tmp_path / "shadow" + unusual_home = "/data/some/relocated/home/comma" + passwd.write_text( + "root:x:0:0:root:/root:/bin/bash\n" + f"comma:x:1000:1000:comma:{unusual_home}:/bin/zsh\n", + encoding="utf-8", + ) + shadow.write_text("comma:$6$x$y:19000:0:99999:7:::\n", encoding="utf-8") + + changed = abb.ensure_iq_user( + passwd_path=passwd, shadow_path=shadow, remount=fake_remount, root_opts="ro", now=_frozen_now, + ) + + assert changed is True + iq_line = [l for l in passwd.read_text(encoding="utf-8").splitlines() if l.startswith("iq:")][0] + # iq inherits comma's home, shell, UID, GID — not a hardcoded path + assert iq_line == f"iq:x:1000:1000:IQ.Pilot:{unusual_home}:/bin/zsh" + + +def test_skips_when_comma_user_absent(tmp_path, fake_remount): + passwd = tmp_path / "passwd" + shadow = tmp_path / "shadow" + passwd.write_text("root:x:0:0:root:/root:/bin/bash\n", encoding="utf-8") + shadow.write_text("root:*:19000:0:99999:7:::\n", encoding="utf-8") + + changed = abb.ensure_iq_user( + passwd_path=passwd, shadow_path=shadow, remount=fake_remount, root_opts="ro", now=_frozen_now, + ) + + assert changed is False + assert "iq:" not in passwd.read_text(encoding="utf-8") + assert "iq:" not in shadow.read_text(encoding="utf-8") + assert fake_remount.calls == [] + + +def test_ensure_iq_user_failure_is_swallowed_by_main(monkeypatch, capsys): + def boom(*a, **kw): + raise RuntimeError("simulated failure") + + monkeypatch.setattr(abb, "ensure_iq_user", boom) + monkeypatch.setattr(abb.os, "geteuid", lambda: 0) + monkeypatch.setattr(abb, "apply_branding", lambda: True) + monkeypatch.setattr("sys.argv", ["apply_boot_branding.py"]) + + rc = abb.main() + out = capsys.readouterr().out + + assert rc == 0 + assert "ensure_iq_user failed: simulated failure" in out + assert "changed" in out # branding ran, so combined result is still "changed" diff --git a/artifacts/runtime/verify_proprietary_bundle.py b/artifacts/runtime/verify_proprietary_bundle.py new file mode 100644 index 000000000..d0a97252c --- /dev/null +++ b/artifacts/runtime/verify_proprietary_bundle.py @@ -0,0 +1,70 @@ +#!/usr/bin/env python3 +import argparse +import hashlib +import json +import stat +from pathlib import Path + + +def parse_args() -> argparse.Namespace: + p = argparse.ArgumentParser(description="Verify IQ Pilot proprietary bundle manifest") + p.add_argument("bundle_root", help="Bundle directory containing manifest.json") + return p.parse_args() + + +def sha256(path: Path) -> str: + h = hashlib.sha256() + with path.open("rb") as f: + while True: + b = f.read(1024 * 1024) + if not b: + break + h.update(b) + return h.hexdigest() + + +def main() -> None: + args = parse_args() + root = Path(args.bundle_root).resolve() + manifest_path = root / "manifest.json" + if not manifest_path.exists(): + raise SystemExit(f"manifest not found: {manifest_path}") + + manifest = json.loads(manifest_path.read_text(encoding="utf-8")) + failures: list[str] = [] + + for rel, meta in manifest.items(): + if rel in {"signatures", "runtime"}: + continue + f = root / rel + if not f.exists(): + failures.append(f"missing: {rel}") + continue + + got = sha256(f) + exp = str(meta["sha256"]) + if got != exp: + failures.append(f"sha256 mismatch: {rel}") + + got_size = f.stat().st_size + exp_size = int(meta["size"]) + if got_size != exp_size: + failures.append(f"size mismatch: {rel} ({got_size} != {exp_size})") + + if "mode" in meta: + got_mode = stat.S_IMODE(f.stat().st_mode) + exp_mode = int(meta["mode"]) + if got_mode != exp_mode: + failures.append(f"mode mismatch: {rel} ({got_mode:o} != {exp_mode:o})") + + if failures: + print("FAILED") + for x in failures: + print(x) + raise SystemExit(1) + + print("OK") + + +if __name__ == "__main__": + main() diff --git a/cereal/README.md b/cereal/README.md new file mode 100644 index 000000000..45e859c09 --- /dev/null +++ b/cereal/README.md @@ -0,0 +1,95 @@ +# What is cereal? + +cereal is the messaging system for openpilot. It uses [msgq](https://github.com/commaai/msgq) as a pub/sub backend, and [Cap'n proto](https://capnproto.org/capnp-tool.html) for serialization of the structs. + + +## Messaging Spec + +You'll find the message types in [log.capnp](log.capnp). It uses [Cap'n proto](https://capnproto.org/capnp-tool.html) and defines one struct called `Event`. + +All `Events` have a `logMonoTime` and a `valid`. Then a big union defines the packet type. + +### Best Practices + +- **All fields must describe quantities in SI units**, unless otherwise specified in the field name. +- In the context of the message they are in, field names should be completely unambiguous. +- All values should be easy to plot and be human-readable with minimal parsing. + +### Maintaining backwards-compatibility + +When making changes to the messaging spec you want to maintain backwards-compatibility, such that old logs can +be parsed with a new version of cereal. Adding structs and adding members to structs is generally safe, most other +things are not. Read more details [here](https://capnproto.org/language.html). + +### Custom forks + +Forks of [openpilot](https://github.com/commaai/openpilot) might want to add things to the messaging +spec, however this could conflict with future changes made in mainline cereal/openpilot. Rebasing against mainline openpilot +then means breaking backwards-compatibility with all old logs of your fork. So we added reserved events in +[custom.capnp](custom.capnp) that we will leave empty in mainline cereal/openpilot. **If you only modify those, you can ensure your +fork will remain backwards-compatible with all versions of mainline openpilot and your fork.** + +An example of compatible changes: +```diff +diff --git a/cereal/custom.capnp b/cereal/custom.capnp +index 3348e859e..3365c7b98 100644 +--- a/cereal/custom.capnp ++++ b/cereal/custom.capnp +@@ -10,7 +10,11 @@ $Cxx.namespace("cereal"); + # DO rename the structs + # DON'T change the identifier (e.g. @0x81c2f05a394cf4af) + +-struct CustomReserved0 @0x81c2f05a394cf4af { ++struct SteeringInfo @0x81c2f05a394cf4af { ++ active @0 :Bool; ++ steeringAngleDeg @1 :Float32; ++ steeringRateDeg @2 :Float32; ++ steeringAccelDeg @3 :Float32; + } + + struct CustomReserved1 @0xaedffd8f31e7b55d { +diff --git a/cereal/log.capnp b/cereal/log.capnp +index 1209f3fd9..b189f58b6 100644 +--- a/cereal/log.capnp ++++ b/cereal/log.capnp +@@ -2558,14 +2558,14 @@ struct Event { + + # DO change the name of the field + # DON'T change anything after the "@" +- customReservedRawData0 @124 :Data; ++ rawCanData @124 :Data; + customReservedRawData1 @125 :Data; + customReservedRawData2 @126 :Data; + + # 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; ++ steeringInfo @107 :Custom.SteeringInfo; + customReserved1 @108 :Custom.CustomReserved1; + customReserved2 @109 :Custom.CustomReserved2; + customReserved3 @110 :Custom.CustomReserved3; +``` + +--- + +Example +--- +```python +import cereal.messaging as messaging + +# in subscriber +sm = messaging.SubMaster(['sensorEvents']) +while 1: + sm.update() + print(sm['sensorEvents']) + +``` + +```python +# in publisher +pm = messaging.PubMaster(['sensorEvents']) +dat = messaging.new_message('sensorEvents', size=1) +dat.sensorEvents[0] = {"gyro": {"v": [0.1, -0.1, 0.1]}} +pm.send('sensorEvents', dat) +``` diff --git a/cereal/__init__.py b/cereal/__init__.py new file mode 100644 index 000000000..93f4d7722 --- /dev/null +++ b/cereal/__init__.py @@ -0,0 +1,11 @@ +import os +import capnp +from importlib.resources import as_file, files + +capnp.remove_import_hook() + +with as_file(files("cereal")) as fspath: + CEREAL_PATH = fspath.as_posix() + log = capnp.load(os.path.join(CEREAL_PATH, "log.capnp")) + car = capnp.load(os.path.join(CEREAL_PATH, "car.capnp")) + custom = capnp.load(os.path.join(CEREAL_PATH, "custom.capnp")) diff --git a/cereal/car.capnp b/cereal/car.capnp new file mode 120000 index 000000000..658a09dfd --- /dev/null +++ b/cereal/car.capnp @@ -0,0 +1 @@ +../iqdbc_repo/iqdbc/car/car.capnp \ No newline at end of file diff --git a/cereal/custom.capnp b/cereal/custom.capnp new file mode 100644 index 000000000..a79382d38 --- /dev/null +++ b/cereal/custom.capnp @@ -0,0 +1,921 @@ +using Cxx = import "./include/c++.capnp"; +$Cxx.namespace("cereal"); + +@0xb526ba661d550a59; + +# Copyright © IQ.Lvbs, apart of Project Teal Lvbs, All Rights Reserved, licensed under https://konn3kt.com/tos/ + +# custom.capnp: a home for reserved structs used by IQ-specific extensions. + +struct AlwaysOnLateral { + state @0 :AlwaysOnLateralState; + enabled @1 :Bool; + active @2 :Bool; + available @3 :Bool; + + enum AlwaysOnLateralState { + disabled @0; + paused @1; + enabled @2; + softDisabling @3; + overriding @4; + } +} + +# Same struct as Log.RadarState.LeadData +struct LeadData { + dRel @0 :Float32; + yRel @1 :Float32; + vRel @2 :Float32; + aRel @3 :Float32; + vLead @4 :Float32; + dPath @6 :Float32; + vLat @7 :Float32; + vLeadK @8 :Float32; + aLeadK @9 :Float32; + fcw @10 :Bool; + status @11 :Bool; + aLeadTau @12 :Float32; + modelProb @13 :Float32; + radar @14 :Bool; + radarTrackId @15 :Int32 = -1; + + aLeadDEPRECATED @5 :Float32; +} + +struct IQState @0xfb0932cf1bde8c5a { + aol @0 :AlwaysOnLateral; + + enum AudibleAlert { + none @0; + + engage @1; + disengage @2; + refuse @3; + + warningSoft @4; + warningImmediate @5; + + prompt @6; + promptRepeat @7; + promptDistracted @8; + + # unused, these are reserved for upstream events so we don't collide + reserved9 @9; + reserved10 @10; + reserved11 @11; + reserved12 @12; + reserved13 @13; + reserved14 @14; + reserved15 @15; + reserved16 @16; + reserved17 @17; + reserved18 @18; + reserved19 @19; + reserved20 @20; + reserved21 @21; + reserved22 @22; + reserved23 @23; + reserved24 @24; + reserved25 @25; + reserved26 @26; + reserved27 @27; + reserved28 @28; + reserved29 @29; + reserved30 @30; + + promptSingleLow @31; + promptSingleHigh @32; + } +} + +struct IQModelManager @0xe91d6987759290bb { + activeBundle @0 :ModelBundle; + selectedBundle @1 :ModelBundle; + availableBundles @2 :List(ModelBundle); + + struct DownloadUri { + uri @0 :Text; + sha256 @1 :Text; + } + + enum DownloadStatus { + notDownloading @0; + downloading @1; + downloaded @2; + cached @3; + failed @4; + } + + struct DownloadProgress { + status @0 :DownloadStatus; + progress @1 :Float32; + eta @2 :UInt32; + } + + struct Artifact { + fileName @0 :Text; + downloadUri @1 :DownloadUri; + downloadProgress @2 :DownloadProgress; + } + + struct Model { + type @0 :Type; + artifact @1 :Artifact; # Main artifact + metadata @2 :Artifact; # Metadata artifact + + enum Type { + supercombo @0; + navigation @1; + vision @2; + policy @3; + offPolicy @4; + onPolicy @5; + } + } + + enum Runner { + snpe @0; + tinygrad @1; + stock @2; + } + + struct Override { + key @0 :Text; + value @1 :Text; + } + + struct ModelBundle { + index @0 :UInt32; + internalName @1 :Text; + displayName @2 :Text; + models @3 :List(Model); + status @4 :DownloadStatus; + generation @5 :UInt32; + environment @6 :Text; + runner @7 :Runner; + is20hz @8 :Bool; + ref @9 :Text; + minimumSelectorVersion @10 :UInt32; + overrides @11 :List(Override); + } +} + +struct IQPlan @0xda401323ae805f2b { + iqDynamic @0 :IQDynamicControl; + longitudinalPlanSource @1 :LongitudinalPlanSource; + iqNavState @2 :IQNavPlanState; + speedLimit @3 :SpeedLimit; + vTarget @4 :Float32; + aTarget @5 :Float32; + events @6 :List(IQOnroadEvent.Event); + e2eAlerts @7 :E2eAlerts; + + struct IQDynamicControl { + state @0 :IQDynamicControlState; + enabled @1 :Bool; + active @2 :Bool; + + enum IQDynamicControlState { + acc @0; + blended @1; + } + } + + struct IQNavPlanState { + nav @0 :Nav; + + struct Nav { + engaged @0 :Bool; + provider @1 :IQNavState.LongitudinalProvider; + state @2 :IQNavState.LongitudinalState; + speedTarget @3 :Float32; + accelTarget @4 :Float32; + valid @5 :Bool; + } + } + + struct SpeedLimit { + resolver @0 :Resolver; + assist @1 :Assist; + + struct Resolver { + speedLimit @0 :Float32; + distToSpeedLimit @1 :Float32; + source @2 :Source; + speedLimitOffset @3 :Float32; + speedLimitLast @4 :Float32; + speedLimitFinal @5 :Float32; + speedLimitFinalLast @6 :Float32; + speedLimitValid @7 :Bool; + speedLimitLastValid @8 :Bool; + } + + struct Assist { + state @0 :AssistState; + enabled @1 :Bool; + active @2 :Bool; + vTarget @3 :Float32; + aTarget @4 :Float32; + } + + enum Source { + none @0; + car @1; + map @2; + } + + enum AssistState { + disabled @0; + inactive @1; # No speed limit set or not enabled by parameter. + preActive @2; + pending @3; # Awaiting new speed limit. + adapting @4; # Reducing speed to match new speed limit. + active @5; # Cruising at speed limit. + } + } + + enum LongitudinalPlanSource { + cruise @0; + nav @1; + speedLimitAssist @2; + } + + struct E2eAlerts { + pathOpen @0 :Bool; + leadPullaway @1 :Bool; + } +} + +struct IQOnroadEvent @0xf4621d3ee9233bc9 { + events @0 :List(Event); + + struct Event { + name @0 :EventName; + + # event types + enable @1 :Bool; + noEntry @2 :Bool; + warning @3 :Bool; # alerts presented only when enabled or soft disabling + userDisable @4 :Bool; + softDisable @5 :Bool; + immediateDisable @6 :Bool; + preEnable @7 :Bool; + permanent @8 :Bool; # alerts presented regardless of openpilot state + overrideLateral @10 :Bool; + overrideLongitudinal @9 :Bool; + } + + # Grouped by IQ.Pilot subsystem. Ordinals are IQ-native and are not stable + # across schema revisions; all consumers reference members by name. + enum EventName { + # lateral / LKAS engagement core + alcEngaged @0; + alcDisengaged @1; + alcEngagedSilent @2; + alcDisengagedSilent @3; + steerManually @4; + speedManually @5; + latMismatch @6; + steeringOverrideReengageAlc @7; + + # silent pause conditions (gear / door / belt / brake) + gearNotDriveSilent @8; + reverseSilent @9; + doorAjarSilent @10; + seatbeltUnbuckledSilent @11; + parkBrakeSilent @12; + brakeHoldSilent @13; + + # alert-only notices + carModeMismatchNotice @14; + pedalHeldNotice @15; + + # lane-turn desires + modelTurnLeft @16; + modelTurnRight @17; + + # navigation maneuvers + navTurnLeft @18; + navTurnRight @19; + navExitLeft @20; + navExitRight @21; + + # speed limit / speed camera + speedLimitPreActive @22; + speedLimitActive @23; + speedLimitChanged @24; + speedLimitPending @25; + speedCameraAhead @26; + + # miscellaneous + hyundaiRadarTracksConfirmed @27; + experimentalToggled @28; + e2eChime @29; + + # construction zone assist + constructionZoneDetected @30; + + # model management + modelUpdating @31; + } +} + +struct IQCarParams @0xd4189b5c8aca9f78 { + # Ordinals are IQ-native; all consumers access by name. Live copies self-heal + # via CLEAR_ON_MANAGER_START on the "IQCarParams" param; the persistent cache + # is versioned separately (see IQCarParamsPersistentV2). + iqSafetyFlags @0 :Int16; # iqpilot custom safety flags (read in C++ panda_safety) + flags @1 :UInt32; # car-specific iqpilot quirks + pcmCruiseSpeed @2 :Bool; + enableGasInterceptor @3 :Bool; + + iqLateralNet @4 :LateralNet; + longitudinalStoppingSpeedOverride @5 :Float32; # m/s; zero keeps the upstream default + stoppingDecelRateOverride @6 :Float32; # m/s^3; zero keeps the upstream default + longActiveWithGasOverride @7 :Bool; # keep long control active while the driver is on the gas + + struct LateralNet { + fuzzyFingerprint @0 :Bool; + model @1 :Model; + + struct Model { + name @0 :Text; + path @1 :Text; + } + } +} + +struct IQCarControl @0xdc6c97009c7ba28f { + aol @0 :AlwaysOnLateral; + params @1 :List(Param); + leadOne @2 :LeadData; + leadTwo @3 :LeadData; + angleOffsetDeg @4 :Float32; + + radarBlendActive @5 :Bool; # feature enabled + PQ + alpha long active + radarEngageReq @6 :Bool; # want stock radar cruise engaged (RadarHandler sends SET on bus 2) + radarCancelReq @7 :Bool; # cancel stock radar cruise now (1kph stop / brake / teardown) + useRadarAccel @8 :Bool; # chill mode + radar active -> pass radar ACS_Sollbeschl as ACC_System payload + radarSetSpeedKph @9 :Float32; # OP set speed (km/h) to sync radar ACA_V_Wunsch toward via GRA_Up/Down + radarGapBars @10 :UInt8; # OP follow-distance bars to mirror to radar GRA_Zeitluecke + + struct Param { + key @0 :Text; + type @2 :ParamType; + value @3 :Data; + + valueDEPRECATED @1 :Text; # The data type change may cause issues with backwards compatibility. + } + + enum ParamType { + string @0; + bool @1; + int @2; + float @3; + time @4; + json @5; + bytes @6; + } +} + +# IQ.Pilot device backup/restore state. Ordinals are IQ-native and were +# renumbered/reordered from earlier revisions; persisted BackupInfo blobs are +# not backward compatible across this change and reset on first run. Every +# consumer accesses fields by name. +struct IQBackupManager @0x9f371a75483cf0a3 { + saveProgress @0 :Float32; + loadProgress @1 :Float32; + savePhase @2 :Phase; + loadPhase @3 :Phase; + activeSnapshot @4 :Snapshot; + snapshotLog @5 :List(Snapshot); + faultText @6 :Text; + + enum Phase { + idle @0; + completed @1; + inProgress @2; + failed @3; + } + + # nested struct names diverge from any upstream schema; field names below are the + # cloud backup JSON contract (to_dict keys) and MUST stay stable for restore. + struct BuildStamp { + build @0 :UInt16; + major @1 :UInt16; + minor @2 :UInt16; + patch @3 :UInt16; + branch @4 :Text; + } + + struct MetaField { + value @0 :Text; + key @1 :Text; + tags @2 :List(Text); + } + + struct Snapshot { + version @0 :UInt32; + isEncrypted @1 :Bool; + deviceId @2 :Text; + config @3 :Text; + createdAt @4 :Text; # ISO timestamp + updatedAt @5 :Text; # ISO timestamp + iqpilotVersion @6 :BuildStamp; + backupMetadata @7 :List(MetaField); + } +} + +struct IQCarState @0xb1c39318bb6bc2b3 { + speedLimit @0 :Float32; + accelPressed @1 :Bool; + decelPressed @2 :Bool; + alcOverrideAlert @3 :Bool; + + # VW PQ stock ACC radar feedback for the IQ.Dynamics radar_manager (Blend feature) + accRadarStaAdr @4 :UInt8; # ACC_System.ACS_Sta_ADR (0 not-active, 1 active, 2 passive, 3 irrev_Fehler) + accRadarFehler @5 :Bool; # ACC_System.ACS_Fehler (stored fault -> radar dead for the drive) +} + +struct IQLiveData @0xf2e2b608e51f4b0e { + speedLimitValid @0 :Bool; + speedLimit @1 :Float32; + speedLimitAheadValid @2 :Bool; + speedLimitAhead @3 :Float32; + speedLimitAheadDistance @4 :Float32; + roadName @5 :Text; +} + +struct IQLiveLocation @0xc04dbadb81776876 { + ecefPosition @0 :VectorSample; + geodeticPosition @1 :VectorSample; + ecefVelocity @2 :VectorSample; + nedVelocity @3 :VectorSample; + bodyVelocity @4 :VectorSample; + bodyAcceleration @5 :VectorSample; + + ecefOrientation @6 :VectorSample; + alignedOrientationEcef @7 :VectorSample; + nedOrientation @8 :VectorSample; + bodyAngularRate @9 :VectorSample; + alignedOrientationNed @10 :VectorSample; + alignedVelocity @11 :VectorSample; + alignedAcceleration @12 :VectorSample; + alignedAngularRate @13 :VectorSample; + + solutionState @14 :SolutionState; + unixTimestampMillis @15 :Int64; + inputsHealthy @16 :Bool = true; + visionHealthy @17 :Bool = true; + gpsHealthy @18 :Bool = true; + sensorsHealthy @19 :Bool = true; + deviceStable @20 :Bool = true; + secondsSinceReset @21 :Float64; + excessiveResets @22 :Bool; + timeToFirstFix @23 :Float32; + debugState @24 :VectorSample; + gpsWeek @25 :Int32; + gpsTimeOfWeek @26 :Float64; + + enum SolutionState { + booting @0; + coarse @1; + ready @2; + } + + struct VectorSample { + values @0 :List(Float64); + deviations @1 :List(Float64); + isValid @2 :Bool; + } +} + +enum IQTurnSignalDirection { + none @0; + turnLeft @1; + turnRight @2; +} + +struct IQDriveModelData @0xcdf0f7f14f46cb86 { + turnSignalDirection @0 :IQTurnSignalDirection; +} + +enum NavDirection { + none @0; + left @1; + right @2; +} + +struct IQNavState @0xaae9afb364368cd9 { + # Navigation state and guidance information + active @0 :Bool; # Whether navigation is currently active + destinationValid @1 :Bool; # Whether we have a valid destination + + # Current position and route info + distanceRemaining @2 :Float32; # Total distance remaining to destination (m) + timeRemaining @3 :Float32; # Estimated time remaining to destination (s) + currentSegmentIndex @4 :UInt32; # Index of current route segment + totalSegments @5 :UInt32; # Total number of segments in route + + # Next maneuver information + nextManeuverValid @6 :Bool; # Whether next maneuver data is valid + nextManeuverDistance @7 :Float32; # Distance to next maneuver (m) + nextManeuverType @8 :ManeuverType; # Type of next maneuver + nextManeuverDirection @9 :IQTurnSignalDirection; # Direction for next maneuver + nextManeuverDescription @10 :Text; # Human-readable maneuver description + nextManeuverAngle @21 :Float32; # Turn angle in degrees (for angle-adaptive enforcement) + + # Turn desire control for lateral planning + shouldSendTurnDesire @11 :Bool; # Whether to send turn desires to model + turnDesireDirection @12 :IQTurnSignalDirection; # Direction for turn desire + + # Lane change desire control for highway exits/ramps (>45 mph) + shouldSendLaneChangeDesire @22 :Bool; # Whether to send lane change desires for high-speed exits + laneChangeDesireDirection @23 :IQTurnSignalDirection; # Direction for lane change desire + + # Speed guidance for longitudinal planning + targetSpeed @13 :Float32; # Target speed for upcoming maneuver (m/s) + targetSpeedValid @14 :Bool; # Whether target speed is valid + + # Destination info + destinationLatitude @15 :Float64; + destinationLongitude @16 :Float64; + destinationName @17 :Text; + + # Lane positioning guidance for exits/turns + shouldSendLanePositioning @18 :Bool; # Whether to send lane positioning desires (keepLeft/keepRight) + lanePositioningDirection @19 :IQTurnSignalDirection; # Direction for lane positioning + + # Lane tracking debug info (model vs GPS comparison for testing) + laneDebugInfo @20 :LaneDebugInfo; + + # Navigation-specific UI event fields (separate from model/desire system) + navTurnDesireDirection @24 :NavDirection; # For "Navigation: Turning Left/Right" UI alerts + navLaneChangeDesireDirection @25 :NavDirection; # For "Navigation: Initiating Lane Change" UI alerts + navLanePositioningDirection @26 :NavDirection; # For future lane positioning UI alerts + navSpeedTargetActive @27 :Bool; # For "Navigation: Reducing Speed" UI alert + + # Second next maneuver information (for "Then" section in navigation banner UI) + secondNextManeuverValid @28 :Bool; # Whether second next maneuver data is valid + secondNextManeuverType @29 :ManeuverType; # Type of second next maneuver + secondNextManeuverDirection @30 :NavDirection; # Direction for second next maneuver + secondNextManeuverDistance @31 :Float32; # Distance to second next maneuver (m) + nextManeuverModifier @32 :Text; # Raw Mapbox modifier for next maneuver (slight_left, sharp_right, etc.) + secondNextManeuverModifier @33 :Text; # Raw Mapbox modifier for second next maneuver + longitudinalProvider @34 :LongitudinalProvider; # Active source of nav longitudinal influence + longitudinalState @35 :LongitudinalState; # Current nav longitudinal state machine output + longitudinalEngaged @36 :Bool; # Whether nav longitudinal influence is currently active + speedTarget @37 :Float32; # Nav longitudinal speed target (m/s) + accelTarget @38 :Float32; # Nav longitudinal accel target (m/s^2) + valid @39 :Bool; # Whether nav longitudinal target is valid + maneuverPhase @40 :ManeuverPhase; # IQ nav maneuver phase for desire/FSM integration + maneuverDirection @41 :NavDirection; # Direction of active IQ nav maneuver phase + command @42 :Command; # Short-lived IQ nav command trigger for desire/FSM integration + commandDirection @43 :NavDirection; # Direction associated with current IQ nav command + commandIndex @44 :UInt32; # Monotonic counter incremented when nav emits a new IQ command + cameraValid @45 :Bool; # Whether a speed camera ahead is currently detected + cameraType @46 :CameraType; # Type of the upcoming speed camera + cameraDistance @47 :Float32; # Distance to the upcoming camera (m) + cameraSpeedLimit @48 :Float32; # Enforced speed limit at the camera (m/s) + + enum CameraType { + none @0; + fixedSpeed @1; # Fixed speed camera + mobileSpeed @2; # Mobile/handheld speed camera + sectionStart @3; # Average-speed (section) zone start + sectionEnd @4; # Average-speed (section) zone end + averageZone @5; # Within an average-speed zone + redLight @6; # Red-light camera + bump @7; # Speed bump + alpr @8; # ALPR / Flock surveillance camera (DeFlock/OSM surveillance:type=ALPR) + } + + enum ManeuverType { + none @0; + turn @1; # Regular turn at intersection + exit @2; # Highway exit + merge @3; # Merge onto highway + fork @4; # Road fork + continueStraight @5; # Continue straight + arrive @6; # Arrive at destination + roundabout @7; # Enter/exit roundabout + } + + enum LongitudinalProvider { + none @0; + route @1; + mapbox @2; + vision @3; + offlineOsm @4; + camera @5; + } + + enum LongitudinalState { + disabled @0; + enabled @1; + entering @2; + active @3; + leaving @4; + overriding @5; + } + + enum ManeuverPhase { + none @0; + turnPrepare @1; + turnActive @2; + highwayPrepare @3; + highwayCommit @4; + } + + enum Command { + none @0; + laneChange @1; + } + + struct LaneDebugInfo { + modelLane @0 :Text; # "left", "middle", "right", "unknown" + modelConfidence @1 :Float32; # 0.0-1.0 + gpsLane @2 :Text; # "left", "middle", "right", "unknown" + gpsConfidence @3 :Float32; # 0.0-1.0 + lateralOffset @4 :Float32; # Meters from road centerline (negative=left, positive=right) + gpsAccuracy @5 :Float32; # GPS position accuracy (meters) + agreement @6 :Bool; # Do model and GPS agree? + } +} + +struct IQNavRenderState @0xf6e4a54ca6c92276 { + active @0 :Bool; + currentLatitude @1 :Float64; + currentLongitude @2 :Float64; + bearingDeg @3 :Float32; + routePolyline @4 :List(NavPoint); + routePolylineSimplified @5 :List(NavPoint); + nextManeuverLatitude @6 :Float64; + nextManeuverLongitude @7 :Float64; + nextManeuverType @8 :IQNavState.ManeuverType; + nextManeuverDirection @9 :NavDirection; + nextManeuverDistance @10 :Float32; + destinationLatitude @11 :Float64; + destinationLongitude @12 :Float64; + zoomHint @13 :Float32; + + struct NavPoint { + latitude @0 :Float64; + longitude @1 :Float64; + } +} + +struct IQPerfTrace @0xa8e2e4a8c6f4d3b2 { + process @0 :Text; + eventClass @1 :Text; + severity @2 :Severity; + frameId @3 :UInt32; + totalTimeUs @4 :UInt32; + rkRemainingUs @5 :Int32; + batchSize @6 :UInt16; + droppedFrames @7 :UInt16; + backlog @8 :UInt16; + flags @9 :UInt32; + samples @10 :List(Sample); + missingServices @11 :List(Text); + topProcesses @12 :List(Text); + detail @13 :Text; + + enum Severity { + info @0; + warning @1; + error @2; + critical @3; + } + + struct Sample { + frameId @0 :UInt32; + loopDtUs @1 :UInt32; + updateUs @2 :UInt32; + stateControlUs @3 :UInt32; + publishUs @4 :UInt32; + tailWorkUs @5 :UInt32; + rkRemainingUs @6 :Int32; + staleCarControlUs @7 :UInt32; + staleCarControlFrames @8 :UInt16; + sendcanGapUs @9 :UInt32; + modelEvalUs @10 :UInt32; + modelDroppedFrames @11 :UInt16; + modelBacklog @12 :UInt16; + textureDecodeUs @13 :UInt32; + textureUploadUs @14 :UInt32; + textureUnloadUs @15 :UInt32; + texturePruneUs @16 :UInt32; + textureConsumeUs @17 :UInt32; + textureBatchSize @18 :UInt16; + textureBytes @19 :UInt32; + textureCacheBefore @20 :UInt16; + textureCacheAfter @21 :UInt16; + textureUnloaded @22 :UInt16; + memoryUsagePercent @23 :UInt16; + gpuUsagePercent @24 :UInt16; + cpuUsagePercent @25 :UInt16; + flags @26 :UInt32; + } +} + +struct IQConstructionZone @0xb54d6e69da4ddc9f { + state @0 :State; + active @1 :Bool; + orangeFraction @2 :Float32; # hot-orange fraction of ROI chroma samples this analysis + secondsSinceHit @3 :Float32; # time since last frame that passed the hit threshold + + enum State { + inactive @0; + pending @1; # hits seen, not yet enough persistence to enter + active @2; + } +} + +struct IQVehicleTracks @0xb877ef4b20a4ae22 { + frameId @0 :UInt32; + frameWidth @1 :UInt16; + frameHeight @2 :UInt16; + processingMs @3 :Float32; + tracks @4 :List(Track); + wide @5 :Bool; + + struct Track { + # box corners normalized [0,1] in the road-camera frame + x1 @0 :Float32; + y1 @1 :Float32; + x2 @2 :Float32; + y2 @3 :Float32; + prob @4 :Float32; + label @5 :Label; + + enum Label { + car @0; + motorcycle @1; + bus @2; + truck @3; + person @4; + bicycle @5; + stopSign @6; + trafficLight @7; + } + } +} + +struct IQEnvironment @0xfd960244a79e2804 { + frameId @0 :UInt32; + offloaded @1 :Bool; + modelValid @2 :Bool; + objects @3 :List(Object); + + struct Object { + x @0 :Float32; + y @1 :Float32; + z @2 :Float32; + width @3 :Float32; + height @4 :Float32; + length @5 :Float32; + prob @6 :Float32; + label @7 :Label; + + enum Label { + car @0; + motorcycle @1; + bus @2; + truck @3; + person @4; + bicycle @5; + stopSign @6; + trafficLight @7; + } + } +} + +struct CustomReserved14 @0xa6e5a1ce8ca5258e { +} + +struct CustomReserved15 @0xdb8042111e62cc87 { +} + +struct CustomReserved16 @0xf59900ccf47b651a { +} + +# pfeiferj/mapd v2 output schema (struct ids must match the mapd binary exactly). +struct MapdDownloadLocationDetails @0xff889853e7b0987f { + location @0 :Text; + totalFiles @1 :UInt32; + downloadedFiles @2 :UInt32; +} + +struct MapdDownloadProgress @0xfaa35dcac85073a2 { + active @0 :Bool; + cancelled @1 :Bool; + totalFiles @2 :UInt32; + downloadedFiles @3 :UInt32; + locations @4 :List(Text); + locationDetails @5 :List(MapdDownloadLocationDetails); +} + +struct MapdPathPoint @0xd6f78acca1bc3939 { + latitude @0 :Float64; + longitude @1 :Float64; + curvature @2 :Float32; + targetVelocity @3 :Float32; +} + +enum MapdRoadContext { + freeway @0; + city @1; + unknown @2; +} + +enum MapdWaySelectionType { + current @0; + predicted @1; + possible @2; + extended @3; + fail @4; +} + +enum MapdInputType { + download @0; + setTargetLateralAccel @1; + setSpeedLimitOffset @2; + setSpeedLimitControl @3; + setMapCurveSpeedControl @4; + setVisionCurveSpeedControl @5; + setLogLevel @6; + setVisionCurveTargetLatA @7; + setVisionCurveMinTargetV @8; + reloadSettings @9; + saveSettings @10; + setEnableSpeed @11; + setVisionCurveUseEnableSpeed @12; + setMapCurveUseEnableSpeed @13; + setSpeedLimitUseEnableSpeed @14; + setHoldLastSeenSpeedLimit @15; + setTargetSpeedJerk @16; + setTargetSpeedAccel @17; + setTargetSpeedTimeOffset @18; + setDefaultLaneWidth @19; + setMapCurveTargetLatA @20; + loadDefaultSettings @21; + loadRecommendedSettings @22; + setSlowDownForNextSpeedLimit @23; + setSpeedUpForNextSpeedLimit @24; + setHoldSpeedLimitWhileChangingSetSpeed @25; + loadPersistentSettings @26; + cancelDownload @27; + setLogJson @28; + setLogSource @29; + setExternalSpeedLimitControl @30; + setExternalSpeedLimit @31; + setSpeedLimitPriority @32; + setSpeedLimitChangeRequiresAccept @33; + acceptSpeedLimit @34; + setPressGasToAcceptSpeedLimit @35; + setAdjustSetSpeedToAcceptSpeedLimit @36; + setAcceptSpeedLimitTimeout @37; + setPressGasToOverrideSpeedLimit @38; +} + +struct MapdExtendedOut @0x8d12e6a08c60a1a1 { + downloadProgress @0 :MapdDownloadProgress; + settings @1 :Text; + path @2 :List(MapdPathPoint); +} + +struct MapdIn @0xb9ceb3ea89cecc23 { + type @0 :MapdInputType; + float @1 :Float32; + str @2 :Text; + bool @3 :Bool; +} + +struct MapdOut @0xd615f9fe1608a3c0 { + wayName @0 :Text; + wayRef @1 :Text; + roadName @2 :Text; + speedLimit @3 :Float32; + nextSpeedLimit @4 :Float32; + nextSpeedLimitDistance @5 :Float32; + hazard @6 :Text; + nextHazard @7 :Text; + nextHazardDistance @8 :Float32; + advisorySpeed @9 :Float32; + nextAdvisorySpeed @10 :Float32; + nextAdvisorySpeedDistance @11 :Float32; + oneWay @12 :Bool; + lanes @13 :UInt8; + tileLoaded @14 :Bool; + speedLimitSuggestedSpeed @15 :Float32; + suggestedSpeed @16 :Float32; + estimatedRoadWidth @17 :Float32; + roadContext @18 :MapdRoadContext; + distanceFromWayCenter @19 :Float32; + visionCurveSpeed @20 :Float32; + mapCurveSpeed @21 :Float32; + waySelectionType @22 :MapdWaySelectionType; + speedLimitAccepted @23 :Bool; +} diff --git a/cereal/include/c++.capnp b/cereal/include/c++.capnp new file mode 100644 index 000000000..2bda54717 --- /dev/null +++ b/cereal/include/c++.capnp @@ -0,0 +1,26 @@ +# Copyright (c) 2013-2014 Sandstorm Development Group, Inc. and contributors +# Licensed under the MIT License: +# +# 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: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# 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. + +@0xbdf87d7bb8304e81; +$namespace("capnp::annotations"); + +annotation namespace(file): Text; +annotation name(field, enumerant, struct, enum, interface, method, param, group, union): Text; diff --git a/cereal/legacy.capnp b/cereal/legacy.capnp new file mode 100644 index 000000000..a8fa5e4a1 --- /dev/null +++ b/cereal/legacy.capnp @@ -0,0 +1,574 @@ +using Cxx = import "./include/c++.capnp"; +$Cxx.namespace("cereal"); + +@0x80ef1ec4889c2a63; + +# legacy.capnp: a home for deprecated structs + +struct LogRotate @0x9811e1f38f62f2d1 { + segmentNum @0 :Int32; + path @1 :Text; +} + +struct LiveUI @0xc08240f996aefced { + rearViewCam @0 :Bool; + alertText1 @1 :Text; + alertText2 @2 :Text; + awarenessStatus @3 :Float32; +} + +struct UiLayoutState @0x88dcce08ad29dda0 { + activeApp @0 :App; + sidebarCollapsed @1 :Bool; + mapEnabled @2 :Bool; + mockEngaged @3 :Bool; + + enum App @0x9917470acf94d285 { + home @0; + music @1; + nav @2; + settings @3; + none @4; + } +} + +struct OrbslamCorrection @0x8afd33dc9b35e1aa { + correctionMonoTime @0 :UInt64; + prePositionECEF @1 :List(Float64); + postPositionECEF @2 :List(Float64); + prePoseQuatECEF @3 :List(Float32); + postPoseQuatECEF @4 :List(Float32); + numInliers @5 :UInt32; +} + +struct EthernetPacket @0xa99a9d5b33cf5859 { + pkt @0 :Data; + ts @1 :Float32; +} + +struct CellInfo @0xcff7566681c277ce { + timestamp @0 :UInt64; + repr @1 :Text; # android toString() for now +} + +struct WifiScan @0xd4df5a192382ba0b { + bssid @0 :Text; + ssid @1 :Text; + capabilities @2 :Text; + frequency @3 :Int32; + level @4 :Int32; + timestamp @5 :Int64; + + centerFreq0 @6 :Int32; + centerFreq1 @7 :Int32; + channelWidth @8 :ChannelWidth; + operatorFriendlyName @9 :Text; + venueName @10 :Text; + is80211mcResponder @11 :Bool; + passpoint @12 :Bool; + + distanceCm @13 :Int32; + distanceSdCm @14 :Int32; + + enum ChannelWidth @0xcb6a279f015f6b51 { + w20Mhz @0; + w40Mhz @1; + w80Mhz @2; + w160Mhz @3; + w80Plus80Mhz @4; + } +} + +struct LiveEventData @0x94b7baa90c5c321e { + name @0 :Text; + value @1 :Int32; +} + +struct ModelData @0xb8aad62cffef28a9 { + frameId @0 :UInt32; + frameAge @12 :UInt32; + frameDropPerc @13 :Float32; + timestampEof @9 :UInt64; + modelExecutionTime @14 :Float32; + gpuExecutionTime @16 :Float32; + rawPred @15 :Data; + + path @1 :PathData; + leftLane @2 :PathData; + rightLane @3 :PathData; + lead @4 :LeadData; + freePath @6 :List(Float32); + + settings @5 :ModelSettings; + leadFuture @7 :LeadData; + speed @8 :List(Float32); + meta @10 :MetaData; + longitudinal @11 :LongitudinalData; + + struct PathData @0x8817eeea389e9f08 { + points @0 :List(Float32); + prob @1 :Float32; + std @2 :Float32; + stds @3 :List(Float32); + poly @4 :List(Float32); + validLen @5 :Float32; + } + + struct LeadData @0xd1c9bef96d26fa91 { + dist @0 :Float32; + prob @1 :Float32; + std @2 :Float32; + relVel @3 :Float32; + relVelStd @4 :Float32; + relY @5 :Float32; + relYStd @6 :Float32; + relA @7 :Float32; + relAStd @8 :Float32; + } + + struct ModelSettings @0xa26e3710efd3e914 { + bigBoxX @0 :UInt16; + bigBoxY @1 :UInt16; + bigBoxWidth @2 :UInt16; + bigBoxHeight @3 :UInt16; + boxProjection @4 :List(Float32); + yuvCorrection @5 :List(Float32); + inputTransform @6 :List(Float32); + } + + struct MetaData @0x9744f25fb60f2bf8 { + engagedProb @0 :Float32; + desirePrediction @1 :List(Float32); + brakeDisengageProb @2 :Float32; + gasDisengageProb @3 :Float32; + steerOverrideProb @4 :Float32; + desireState @5 :List(Float32); + } + + struct LongitudinalData @0xf98f999c6a071122 { + distances @2 :List(Float32); + speeds @0 :List(Float32); + accelerations @1 :List(Float32); + } +} + +struct ECEFPoint @0xc25bbbd524983447 { + x @0 :Float64; + y @1 :Float64; + z @2 :Float64; +} + +struct ECEFPointDEPRECATED @0xe10e21168db0c7f7 { + x @0 :Float32; + y @1 :Float32; + z @2 :Float32; +} + +struct GPSPlannerPoints @0xab54c59699f8f9f3 { + curPosDEPRECATED @0 :ECEFPointDEPRECATED; + pointsDEPRECATED @1 :List(ECEFPointDEPRECATED); + curPos @6 :ECEFPoint; + points @7 :List(ECEFPoint); + valid @2 :Bool; + trackName @3 :Text; + speedLimit @4 :Float32; + accelTarget @5 :Float32; +} + +struct GPSPlannerPlan @0xf5ad1d90cdc1dd6b { + valid @0 :Bool; + poly @1 :List(Float32); + trackName @2 :Text; + speed @3 :Float32; + acceleration @4 :Float32; + pointsDEPRECATED @5 :List(ECEFPointDEPRECATED); + points @6 :List(ECEFPoint); + xLookahead @7 :Float32; +} + +struct UiNavigationEvent @0x90c8426c3eaddd3b { + type @0: Type; + status @1: Status; + distanceTo @2: Float32; + endRoadPointDEPRECATED @3: ECEFPointDEPRECATED; + endRoadPoint @4: ECEFPoint; + + enum Type @0xe8db07dcf8fcea05 { + none @0; + laneChangeLeft @1; + laneChangeRight @2; + mergeLeft @3; + mergeRight @4; + turnLeft @5; + turnRight @6; + } + + enum Status @0xb9aa88c75ef99a1f { + none @0; + passive @1; + approaching @2; + active @3; + } +} + +struct LiveLocationData @0xb99b2bc7a57e8128 { + status @0 :UInt8; + + # 3D fix + lat @1 :Float64; + lon @2 :Float64; + alt @3 :Float32; # m + + # speed + speed @4 :Float32; # m/s + + # NED velocity components + vNED @5 :List(Float32); + + # roll, pitch, heading (x,y,z) + roll @6 :Float32; # WRT to center of earth? + pitch @7 :Float32; # WRT to center of earth? + heading @8 :Float32; # WRT to north? + + # what are these? + wanderAngle @9 :Float32; + trackAngle @10 :Float32; + + # car frame -- https://upload.wikimedia.org/wikipedia/commons/f/f5/RPY_angles_of_cars.png + + # gyro, in car frame, deg/s + gyro @11 :List(Float32); + + # accel, in car frame, m/s^2 + accel @12 :List(Float32); + + accuracy @13 :Accuracy; + + source @14 :SensorSource; + # if we are fixing a location in the past + fixMonoTime @15 :UInt64; + + gpsWeek @16 :Int32; + timeOfWeek @17 :Float64; + + positionECEF @18 :List(Float64); + poseQuatECEF @19 :List(Float32); + pitchCalibration @20 :Float32; + yawCalibration @21 :Float32; + imuFrame @22 :List(Float32); + + struct Accuracy @0x943dc4625473b03f { + pNEDError @0 :List(Float32); + vNEDError @1 :List(Float32); + rollError @2 :Float32; + pitchError @3 :Float32; + headingError @4 :Float32; + ellipsoidSemiMajorError @5 :Float32; + ellipsoidSemiMinorError @6 :Float32; + ellipsoidOrientationError @7 :Float32; + } + + enum SensorSource @0xc871d3cc252af657 { + applanix @0; + kalman @1; + orbslam @2; + timing @3; + dummy @4; + } +} + +struct OrbOdometry @0xd7700859ed1f5b76 { + # timing first + startMonoTime @0 :UInt64; + endMonoTime @1 :UInt64; + + # fundamental matrix and error + f @2: List(Float64); + err @3: Float64; + + # number of inlier points + inliers @4: Int32; + + # for debug only + # indexed by endMonoTime features + # value is startMonoTime feature match + # -1 if no match + matches @5: List(Int16); +} + +struct OrbFeatures @0xcd60164a8a0159ef { + timestampEof @0 :UInt64; + # transposed arrays of normalized image coordinates + # len(xs) == len(ys) == len(descriptors) * 32 + xs @1 :List(Float32); + ys @2 :List(Float32); + descriptors @3 :Data; + octaves @4 :List(Int8); + + # match index to last OrbFeatures + # -1 if no match + timestampLastEof @5 :UInt64; + matches @6: List(Int16); +} + +struct OrbFeaturesSummary @0xd500d30c5803fa4f { + timestampEof @0 :UInt64; + timestampLastEof @1 :UInt64; + + featureCount @2 :UInt16; + matchCount @3 :UInt16; + computeNs @4 :UInt64; +} + +struct OrbKeyFrame @0xc8233c0345e27e24 { + # this is a globally unique id for the KeyFrame + id @0: UInt64; + + # this is the location of the KeyFrame + pos @1: ECEFPoint; + + # these are the features in the world + # len(dpos) == len(descriptors) * 32 + dpos @2 :List(ECEFPoint); + descriptors @3 :Data; +} + +struct KalmanOdometry @0x92e21bb7ea38793a { + trans @0 :List(Float32); # m/s in device frame + rot @1 :List(Float32); # rad/s in device frame + transStd @2 :List(Float32); # std m/s in device frame + rotStd @3 :List(Float32); # std rad/s in device frame +} + +struct OrbObservation @0x9b326d4e436afec7 { + observationMonoTime @0 :UInt64; + normalizedCoordinates @1 :List(Float32); + locationECEF @2 :List(Float64); + matchDistance @3: UInt32; +} + +struct CalibrationFeatures @0x8fdfadb254ea867a { + frameId @0 :UInt32; + + p0 @1 :List(Float32); + p1 @2 :List(Float32); + status @3 :List(Int8); +} + +struct NavStatus @0xbd8822120928120c { + isNavigating @0 :Bool; + currentAddress @1 :Address; + + struct Address @0xce7cd672cacc7814 { + title @0 :Text; + lat @1 :Float64; + lng @2 :Float64; + house @3 :Text; + address @4 :Text; + street @5 :Text; + city @6 :Text; + state @7 :Text; + country @8 :Text; + } +} + +struct NavUpdate @0xdb98be6565516acb { + isNavigating @0 :Bool; + curSegment @1 :Int32; + segments @2 :List(Segment); + + struct LatLng @0x9eaef9187cadbb9b { + lat @0 :Float64; + lng @1 :Float64; + } + + struct Segment @0xa5b39b4fc4d7da3f { + from @0 :LatLng; + to @1 :LatLng; + updateTime @2 :Int32; + distance @3 :Int32; + crossTime @4 :Int32; + exitNo @5 :Int32; + instruction @6 :Instruction; + + parts @7 :List(LatLng); + + enum Instruction @0xc5417a637451246f { + turnLeft @0; + turnRight @1; + keepLeft @2; + keepRight @3; + straight @4; + roundaboutExitNumber @5; + roundaboutExit @6; + roundaboutTurnLeft @7; + unkn8 @8; + roundaboutStraight @9; + unkn10 @10; + roundaboutTurnRight @11; + unkn12 @12; + roundaboutUturn @13; + unkn14 @14; + arrive @15; + exitLeft @16; + exitRight @17; + unkn18 @18; + uturn @19; + # ... + } + } +} + +struct TrafficEvent @0xacfa74a094e62626 { + type @0 :Type; + distance @1 :Float32; + action @2 :Action; + resuming @3 :Bool; + + enum Type @0xd85d75253435bf4b { + stopSign @0; + lightRed @1; + lightYellow @2; + lightGreen @3; + stopLight @4; + } + + enum Action @0xa6f6ce72165ccb49 { + none @0; + yield @1; + stop @2; + resumeReady @3; + } + +} + + +struct AndroidGnss @0xdfdf30d03fc485bd { + union { + measurements @0 :Measurements; + navigationMessage @1 :NavigationMessage; + } + + struct Measurements @0xa20710d4f428d6cd { + clock @0 :Clock; + measurements @1 :List(Measurement); + + struct Clock @0xa0e27b453a38f450 { + timeNanos @0 :Int64; + hardwareClockDiscontinuityCount @1 :Int32; + + hasTimeUncertaintyNanos @2 :Bool; + timeUncertaintyNanos @3 :Float64; + + hasLeapSecond @4 :Bool; + leapSecond @5 :Int32; + + hasFullBiasNanos @6 :Bool; + fullBiasNanos @7 :Int64; + + hasBiasNanos @8 :Bool; + biasNanos @9 :Float64; + + hasBiasUncertaintyNanos @10 :Bool; + biasUncertaintyNanos @11 :Float64; + + hasDriftNanosPerSecond @12 :Bool; + driftNanosPerSecond @13 :Float64; + + hasDriftUncertaintyNanosPerSecond @14 :Bool; + driftUncertaintyNanosPerSecond @15 :Float64; + } + + struct Measurement @0xd949bf717d77614d { + svId @0 :Int32; + constellation @1 :Constellation; + + timeOffsetNanos @2 :Float64; + state @3 :Int32; + receivedSvTimeNanos @4 :Int64; + receivedSvTimeUncertaintyNanos @5 :Int64; + cn0DbHz @6 :Float64; + pseudorangeRateMetersPerSecond @7 :Float64; + pseudorangeRateUncertaintyMetersPerSecond @8 :Float64; + accumulatedDeltaRangeState @9 :Int32; + accumulatedDeltaRangeMeters @10 :Float64; + accumulatedDeltaRangeUncertaintyMeters @11 :Float64; + + hasCarrierFrequencyHz @12 :Bool; + carrierFrequencyHz @13 :Float32; + hasCarrierCycles @14 :Bool; + carrierCycles @15 :Int64; + hasCarrierPhase @16 :Bool; + carrierPhase @17 :Float64; + hasCarrierPhaseUncertainty @18 :Bool; + carrierPhaseUncertainty @19 :Float64; + hasSnrInDb @20 :Bool; + snrInDb @21 :Float64; + + multipathIndicator @22 :MultipathIndicator; + + enum Constellation @0x9ef1f3ff0deb5ffb { + unknown @0; + gps @1; + sbas @2; + glonass @3; + qzss @4; + beidou @5; + galileo @6; + } + + enum State @0xcbb9490adce12d72 { + unknown @0; + codeLock @1; + bitSync @2; + subframeSync @3; + towDecoded @4; + msecAmbiguous @5; + symbolSync @6; + gloStringSync @7; + gloTodDecoded @8; + bdsD2BitSync @9; + bdsD2SubframeSync @10; + galE1bcCodeLock @11; + galE1c2ndCodeLock @12; + galE1bPageSync @13; + sbasSync @14; + } + + enum MultipathIndicator @0xc04e7b6231d4caa8 { + unknown @0; + detected @1; + notDetected @2; + } + } + } + + struct NavigationMessage @0xe2517b083095fd4e { + type @0 :Int32; + svId @1 :Int32; + messageId @2 :Int32; + submessageId @3 :Int32; + data @4 :Data; + status @5 :Status; + + enum Status @0xec1ff7996b35366f { + unknown @0; + parityPassed @1; + parityRebuilt @2; + } + } +} + +struct LidarPts @0xe3d6685d4e9d8f7a { + r @0 :List(UInt16); # uint16 m*500.0 + theta @1 :List(UInt16); # uint16 deg*100.0 + reflect @2 :List(UInt8); # uint8 0-255 + + # For storing out of file. + idx @3 :UInt64; + + # For storing in file + pkt @4 :Data; +} + + diff --git a/cereal/log.capnp b/cereal/log.capnp new file mode 100644 index 000000000..056b9caf3 --- /dev/null +++ b/cereal/log.capnp @@ -0,0 +1,2738 @@ +using Cxx = import "./include/c++.capnp"; +$Cxx.namespace("cereal"); + +using Car = import "car.capnp"; +using Legacy = import "legacy.capnp"; +using Custom = import "custom.capnp"; + +@0xf3b1f17e25a4285b; + +# Copyright © IQ.Lvbs, apart of Project Teal Lvbs, All Rights Reserved, licensed under https://konn3kt.com/tos/ + +const logVersion :Int32 = 1; + +struct Map(Key, Value) { + entries @0 :List(Entry); + struct Entry { + key @0 :Key; + value @1 :Value; + } +} + +struct OnroadEvent @0xc4fa6047f024e718 { + name @0 :EventName; + + # event types + enable @1 :Bool; + noEntry @2 :Bool; + warning @3 :Bool; # alerts presented only when enabled or soft disabling + userDisable @4 :Bool; + softDisable @5 :Bool; + immediateDisable @6 :Bool; + preEnable @7 :Bool; + permanent @8 :Bool; # alerts presented regardless of openpilot state + overrideLateral @10 :Bool; + overrideLongitudinal @9 :Bool; + + enum EventName @0x91f1992a1f77fb03 { + canError @0; + steerUnavailable @1; + wrongGear @2; + doorOpen @3; + seatbeltNotLatched @4; + espDisabled @5; + wrongCarMode @6; + steerTempUnavailable @7; + reverseGear @8; + buttonCancel @9; + buttonEnable @10; + pedalPressed @11; # exits active state + preEnableStandstill @12; # added during pre-enable state with brake + gasPressedOverride @13; # added when user is pressing gas with no disengage on gas + steerOverride @14; + steerDisengage @94; # exits active state + cruiseDisabled @15; + speedTooLow @16; + outOfSpace @17; + overheat @18; + calibrationIncomplete @19; + calibrationInvalid @20; + calibrationRecalibrating @21; + controlsMismatch @22; + pcmEnable @23; + pcmDisable @24; + radarFault @25; + radarTempUnavailable @93; + brakeHold @26; + parkBrake @27; + manualRestart @28; + joystickDebug @29; + longitudinalManeuver @30; + steerTempUnavailableSilent @31; + resumeRequired @32; + preDriverDistracted @33; + promptDriverDistracted @34; + driverDistracted @35; + preDriverUnresponsive @36; + promptDriverUnresponsive @37; + driverUnresponsive @38; + belowSteerSpeed @39; + lowBattery @40; + accFaulted @41; + sensorDataInvalid @42; + commIssue @43; + commIssueAvgFreq @44; + tooDistracted @45; + posenetInvalid @46; + preLaneChangeLeft @48; + preLaneChangeRight @49; + laneChange @50; + lowMemory @51; + stockAeb @52; + stockLkas @98; + lateralManeuver @99; + cruiseFaultLateralAllowed @100; + ldw @53; + carUnrecognized @54; + invalidLkasSetting @55; + speedTooHigh @56; + laneChangeBlocked @57; + relayMalfunction @58; + stockFcw @59; + startup @60; + startupNoCar @61; + startupNoControl @62; + startupNoSecOcKey @63; + startupMaster @64; + fcw @65; + steerSaturated @66; + belowEngageSpeed @67; + noGps @68; + wrongCruiseMode @69; + modeldLagging @70; + deviceFalling @71; + fanMalfunction @72; + cameraMalfunction @73; + cameraFrameRate @74; + processNotRunning @75; + dashcamMode @76; + selfdriveInitializing @77; + usbError @78; + cruiseMismatch @79; + canBusMissing @80; + selfdrivedLagging @81; + resumeBlocked @82; + steerTimeLimit @83; + vehicleSensorsInvalid @84; + locationdTemporaryError @85; + locationdPermanentError @86; + paramsdTemporaryError @87; + paramsdPermanentError @88; + actuatorsApiUnavailable @89; + espActive @90; + personalityChanged @91; + aeb @92; + userBookmark @95; + excessiveActuation @96; + audioFeedback @97; + + soundsUnavailableDEPRECATED @47; + } +} + +enum LongitudinalPersonality { + aggressive @0; + standard @1; + relaxed @2; +} + +struct InitData { + kernelArgs @0 :List(Text); + kernelVersion @15 :Text; + osVersion @18 :Text; + + dongleId @2 :Text; + bootlogId @22 :Text; + + deviceType @3 :DeviceType; + version @4 :Text; + gitCommit @10 :Text; + gitCommitDate @21 :Text; + gitBranch @11 :Text; + gitRemote @13 :Text; + + # this is source commit for prebuilt branches + gitSrcCommit @23 :Text; + gitSrcCommitDate @24 :Text; + + androidProperties @16 :Map(Text, Text); + + pandaInfo @8 :PandaInfo; + + dirty @9 :Bool; + passive @12 :Bool; + params @17 :Map(Text, Data); + + commands @19 :Map(Text, Data); + + wallTimeNanos @20 :UInt64; + + ufsHealth @25 :UfsHealth; + + struct UfsHealth { + preEolInfo @0 :UInt8; + lifeTimeEstimateA @1 :UInt8; + lifeTimeEstimateB @2 :UInt8; + vendorHealthReport @3 :Data; + } + + enum DeviceType { + unknown @0; + neo @1; + chffrAndroid @2; + chffrIos @3; + tici @4; + pc @5; + tizi @6; + mici @7; + } + + struct PandaInfo { + hasPanda @0 :Bool; + dongleId @1 :Text; + stVersion @2 :Text; + espVersion @3 :Text; + } + + # ***** deprecated stuff ***** + gctxDEPRECATED @1 :Text; + androidBuildInfo @5 :AndroidBuildInfo; + androidSensorsDEPRECATED @6 :List(AndroidSensor); + chffrAndroidExtraDEPRECATED @7 :ChffrAndroidExtra; + iosBuildInfoDEPRECATED @14 :IosBuildInfo; + + struct AndroidBuildInfo { + board @0 :Text; + bootloader @1 :Text; + brand @2 :Text; + device @3 :Text; + display @4 :Text; + fingerprint @5 :Text; + hardware @6 :Text; + host @7 :Text; + id @8 :Text; + manufacturer @9 :Text; + model @10 :Text; + product @11 :Text; + radioVersion @12 :Text; + serial @13 :Text; + supportedAbis @14 :List(Text); + tags @15 :Text; + time @16 :Int64; + type @17 :Text; + user @18 :Text; + + versionCodename @19 :Text; + versionRelease @20 :Text; + versionSdk @21 :Int32; + versionSecurityPatch @22 :Text; + } + + struct AndroidSensor { + id @0 :Int32; + name @1 :Text; + vendor @2 :Text; + version @3 :Int32; + handle @4 :Int32; + type @5 :Int32; + maxRange @6 :Float32; + resolution @7 :Float32; + power @8 :Float32; + minDelay @9 :Int32; + fifoReservedEventCount @10 :UInt32; + fifoMaxEventCount @11 :UInt32; + stringType @12 :Text; + maxDelay @13 :Int32; + } + + struct ChffrAndroidExtra { + allCameraCharacteristics @0 :Map(Text, Text); + } + + struct IosBuildInfo { + appVersion @0 :Text; + appBuild @1 :UInt32; + osVersion @2 :Text; + deviceModel @3 :Text; + } +} + +struct FrameData { + frameId @0 :UInt32; + frameIdSensor @25 :UInt32; + requestId @28 :UInt32; + encodeId @1 :UInt32; + + # Timestamps + timestampEof @2 :UInt64; + timestampSof @8 :UInt64; + processingTime @23 :Float32; + + # Exposure + integLines @4 :Int32; + highConversionGain @20 :Bool; + gain @15 :Float32; # This includes highConversionGain if enabled + measuredGreyFraction @21 :Float32; + targetGreyFraction @22 :Float32; + exposureValPercent @27 :Float32; + + transform @10 :List(Float32); + + image @6 :Data; + + temperaturesC @24 :List(Float32); + + enum FrameTypeDEPRECATED { + unknown @0; + neo @1; + chffrAndroid @2; + front @3; + } + + sensor @26 :ImageSensor; + enum ImageSensor { + unknown @0; + ar0231 @1; + ox03c10 @2; + os04c10 @3; + } + + frameLengthDEPRECATED @3 :Int32; + globalGainDEPRECATED @5 :Int32; + frameTypeDEPRECATED @7 :FrameTypeDEPRECATED; + androidCaptureResultDEPRECATED @9 :AndroidCaptureResult; + lensPosDEPRECATED @11 :Int32; + lensSagDEPRECATED @12 :Float32; + lensErrDEPRECATED @13 :Float32; + lensTruePosDEPRECATED @14 :Float32; + focusValDEPRECATED @16 :List(Int16); + focusConfDEPRECATED @17 :List(UInt8); + sharpnessScoreDEPRECATED @18 :List(UInt16); + recoverStateDEPRECATED @19 :Int32; + struct AndroidCaptureResult { + sensitivity @0 :Int32; + frameDuration @1 :Int64; + exposureTime @2 :Int64; + rollingShutterSkew @3 :UInt64; + colorCorrectionTransform @4 :List(Int32); + colorCorrectionGains @5 :List(Float32); + displayRotation @6 :Int8; + } +} + +struct Thumbnail { + frameId @0 :UInt32; + timestampEof @1 :UInt64; + thumbnail @2 :Data; + encoding @3 :Encoding; + + enum Encoding { + unknown @0; + jpeg @1; + keyframe @2; + } +} + +struct GPSNMEAData { + timestamp @0 :Int64; + localWallTime @1 :UInt64; + nmea @2 :Text; +} + +# android sensor_event_t +struct SensorEventData { + version @0 :Int32; + sensor @1 :Int32; + type @2 :Int32; + timestamp @3 :Int64; + uncalibratedDEPRECATED @10 :Bool; + + union { + acceleration @4 :SensorVec; + magnetic @5 :SensorVec; + orientation @6 :SensorVec; + gyro @7 :SensorVec; + pressure @9 :SensorVec; + magneticUncalibrated @11 :SensorVec; + gyroUncalibrated @12 :SensorVec; + proximity @13: Float32; + light @14: Float32; + temperature @15: Float32; + } + source @8 :SensorSource; + + struct SensorVec { + v @0 :List(Float32); + status @1 :Int8; + } + + enum SensorSource { + android @0; + iOS @1; + fiber @2; + velodyne @3; # Velodyne IMU + bno055 @4; # Bosch accelerometer + lsm6ds3 @5; # includes LSM6DS3 and LSM6DS3TR, TR = tape reel + bmp280 @6; # barometer + mmc3416x @7; # magnetometer + bmx055 @8; + rpr0521 @9; + lsm6ds3trc @10; + mmc5603nj @11; + } +} + +# android struct GpsLocation +struct GpsLocationData { + # Contains module-specific flags. + flags @0 :UInt16; + + # Represents latitude in degrees. + latitude @1 :Float64; + + # Represents longitude in degrees. + longitude @2 :Float64; + + # Represents altitude in meters above the WGS 84 reference ellipsoid. + altitude @3 :Float64; + + # Represents speed in meters per second. + speed @4 :Float32; + + # Represents heading in degrees. + bearingDeg @5 :Float32; + + # Represents expected horizontal accuracy in meters. + horizontalAccuracy @6 :Float32; + + unixTimestampMillis @7 :Int64; + + source @8 :SensorSource; + + # Represents NED velocity in m/s. + vNED @9 :List(Float32); + + # Represents expected vertical accuracy in meters. (presumably 1 sigma?) + verticalAccuracy @10 :Float32; + + # Represents bearing accuracy in degrees. (presumably 1 sigma?) + bearingAccuracyDeg @11 :Float32; + + # Represents velocity accuracy in m/s. (presumably 1 sigma?) + speedAccuracy @12 :Float32; + + hasFix @13 :Bool; + satelliteCount @14 :Int8; + + enum SensorSource { + android @0; + iOS @1; + car @2; + velodyne @3; # Velodyne IMU + fusion @4; + external @5; + ublox @6; + trimble @7; + qcomdiag @8; + unicore @9; + } +} + +enum Desire { + none @0; + turnLeft @1; + turnRight @2; + laneChangeLeft @3; + laneChangeRight @4; + keepLeft @5; + keepRight @6; +} + +enum LaneChangeState { + off @0; + preLaneChange @1; + laneChangeStarting @2; + laneChangeFinishing @3; +} + +enum LaneChangeDirection { + none @0; + left @1; + right @2; +} + +struct CanData { + address @0 :UInt32; + dat @2 :Data; + src @3 :UInt8; + busTimeDEPRECATED @1 :UInt16; +} + +struct DeviceState @0xa4d8b5af2aa492eb { + deviceType @45 :InitData.DeviceType; + + networkType @22 :NetworkType; + networkInfo @31 :NetworkInfo; + networkStrength @24 :NetworkStrength; + networkStats @43 :NetworkStats; + networkMetered @41 :Bool; + lastAthenaPingTime @32 :UInt64; + + started @11 :Bool; + startedMonoTime @13 :UInt64; + + # system utilization + freeSpacePercent @7 :Float32; + memoryUsagePercent @19 :Int8; + gpuUsagePercent @33 :Int8; + cpuUsagePercent @34 :List(Int8); # per-core cpu usage + + # power + offroadPowerUsageUwh @23 :UInt32; + carBatteryCapacityUwh @25 :UInt32; + powerDrawW @40 :Float32; + somPowerDrawW @42 :Float32; + + # device thermals + cpuTempC @26 :List(Float32); + gpuTempC @27 :List(Float32); + dspTempC @49 :Float32; + memoryTempC @28 :Float32; + modemTempC @36 :List(Float32); + pmicTempC @39 :List(Float32); + intakeTempC @46 :Float32; + exhaustTempC @47 :Float32; + caseTempC @48 :Float32; + maxTempC @44 :Float32; # max of other temps, used to control fan + thermalZones @38 :List(ThermalZone); + thermalStatus @14 :ThermalStatus; + + fanSpeedPercentDesired @10 :UInt16; + screenBrightnessPercent @37 :Int8; + + struct ThermalZone { + name @0 :Text; + temp @1 :Float32; + } + + enum ThermalStatus { + green @0; + yellow @1; + red @2; + danger @3; + } + + enum NetworkType { + none @0; + wifi @1; + cell2G @2; + cell3G @3; + cell4G @4; + cell5G @5; + ethernet @6; + } + + enum NetworkStrength { + unknown @0; + poor @1; + moderate @2; + good @3; + great @4; + } + + struct NetworkInfo { + technology @0 :Text; + operator @1 :Text; + band @2 :Text; + channel @3 :UInt16; + extra @4 :Text; + state @5 :Text; + } + + struct NetworkStats { + wwanTx @0 :Int64; + wwanRx @1 :Int64; + } + + # deprecated + cpu0DEPRECATED @0 :UInt16; + cpu1DEPRECATED @1 :UInt16; + cpu2DEPRECATED @2 :UInt16; + cpu3DEPRECATED @3 :UInt16; + memDEPRECATED @4 :UInt16; + gpuDEPRECATED @5 :UInt16; + batDEPRECATED @6 :UInt32; + pa0DEPRECATED @21 :UInt16; + cpuUsagePercentDEPRECATED @20 :Int8; + batteryStatusDEPRECATED @9 :Text; + batteryVoltageDEPRECATED @16 :Int32; + batteryTempCDEPRECATED @29 :Float32; + batteryPercentDEPRECATED @8 :Int16; + batteryCurrentDEPRECATED @15 :Int32; + chargingErrorDEPRECATED @17 :Bool; + chargingDisabledDEPRECATED @18 :Bool; + usbOnlineDEPRECATED @12 :Bool; + ambientTempCDEPRECATED @30 :Float32; + nvmeTempCDEPRECATED @35 :List(Float32); +} + +struct PandaState @0xa7649e2575e4591e { + ignitionLine @2 :Bool; + rxBufferOverflow @7 :UInt32; + txBufferOverflow @8 :UInt32; + pandaType @10 :PandaType; + ignitionCan @13 :Bool; + faultStatus @15 :FaultStatus; + powerSaveEnabled @16 :Bool; + uptime @17 :UInt32; + faults @18 :List(FaultType); + heartbeatLost @22 :Bool; + interruptLoad @25 :Float32; + fanPower @28 :UInt8; + + spiErrorCount @33 :UInt16; + + harnessStatus @21 :HarnessStatus; + sbu1Voltage @35 :Float32; + sbu2Voltage @36 :Float32; + + # can health + canState0 @29 :PandaCanState; + canState1 @30 :PandaCanState; + canState2 @31 :PandaCanState; + + # safety stuff + controlsAllowed @3 :Bool; + safetyRxInvalid @19 :UInt32; + safetyTxBlocked @24 :UInt32; + safetyModel @14 :Car.CarParams.SafetyModel; + safetyParam @27 :UInt16; + alternativeExperience @23 :Int16; + safetyRxChecksInvalid @32 :Bool; + + voltage @0 :UInt32; + current @1 :UInt32; + + enum FaultStatus { + none @0; + faultTemp @1; + faultPerm @2; + } + + enum FaultType { + relayMalfunction @0; + unusedInterruptHandled @1; + interruptRateCan1 @2; + interruptRateCan2 @3; + interruptRateCan3 @4; + interruptRateTach @5; + interruptRateGmlanDEPRECATED @6; + interruptRateInterrupts @7; + interruptRateSpiDma @8; + interruptRateSpiCs @9; + interruptRateUart1 @10; + interruptRateUart2 @11; + interruptRateUart3 @12; + interruptRateUart5 @13; + interruptRateUartDma @14; + interruptRateUsb @15; + interruptRateTim1 @16; + interruptRateTim3 @17; + registerDivergent @18; + interruptRateKlineInit @19; + interruptRateClockSource @20; + interruptRateTick @21; + interruptRateExti @22; + interruptRateSpi @23; + interruptRateUart7 @24; + sirenMalfunction @25; + heartbeatLoopWatchdog @26; + # Update max fault type in boardd when adding faults + } + + enum PandaType @0x8a58adf93e5b3751 { + unknown @0; + whitePanda @1; + greyPanda @2; + blackPanda @3; + pedal @4; + uno @5; + dos @6; + redPanda @7; + redPandaV2 @8; + tres @9; + cuatro @10; + } + + enum HarnessStatus { + notConnected @0; + normal @1; + flipped @2; + } + + struct PandaCanState { + busOff @0 :Bool; + busOffCnt @1 :UInt32; + errorWarning @2 :Bool; + errorPassive @3 :Bool; + lastError @4 :LecErrorCode; + lastStoredError @5 :LecErrorCode; + lastDataError @6 :LecErrorCode; + lastDataStoredError @7 :LecErrorCode; + receiveErrorCnt @8 :UInt8; + transmitErrorCnt @9 :UInt8; + totalErrorCnt @10 :UInt32; + totalTxLostCnt @11 :UInt32; + totalRxLostCnt @12 :UInt32; + totalTxCnt @13 :UInt32; + totalRxCnt @14 :UInt32; + totalFwdCnt @15 :UInt32; + canSpeed @16 :UInt16; + canDataSpeed @17 :UInt16; + canfdEnabled @18 :Bool; + brsEnabled @19 :Bool; + canfdNonIso @20 :Bool; + irq0CallRate @21 :UInt32; + irq1CallRate @22 :UInt32; + irq2CallRate @23 :UInt32; + canCoreResetCnt @24 :UInt32; + + enum LecErrorCode { + noError @0; + stuffError @1; + formError @2; + ackError @3; + bit1Error @4; + bit0Error @5; + crcError @6; + noChange @7; + } + } + + gasInterceptorDetectedDEPRECATED @4 :Bool; + startedSignalDetectedDEPRECATED @5 :Bool; + hasGpsDEPRECATED @6 :Bool; + gmlanSendErrsDEPRECATED @9 :UInt32; + fanSpeedRpmDEPRECATED @11 :UInt16; + usbPowerModeDEPRECATED @12 :PeripheralState.UsbPowerModeDEPRECATED; + safetyParamDEPRECATED @20 :Int16; + safetyParam2DEPRECATED @26 :UInt32; + fanStallCountDEPRECATED @34 :UInt8; +} + +struct PeripheralState { + pandaType @0 :PandaState.PandaType; + voltage @1 :UInt32; + current @2 :UInt32; + fanSpeedRpm @3 :UInt16; + + usbPowerModeDEPRECATED @4 :UsbPowerModeDEPRECATED; + enum UsbPowerModeDEPRECATED @0xa8883583b32c9877 { + none @0; + client @1; + cdp @2; + dcp @3; + } +} + +struct RadarState @0x9a185389d6fdd05f { + mdMonoTime @6 :UInt64; + carStateMonoTime @11 :UInt64; + radarErrors @13 :Car.RadarData.Error; + + leadOne @3 :LeadData; + leadTwo @4 :LeadData; + + struct LeadData { + dRel @0 :Float32; + yRel @1 :Float32; + vRel @2 :Float32; + aRel @3 :Float32; + vLead @4 :Float32; + dPath @6 :Float32; + vLat @7 :Float32; + vLeadK @8 :Float32; + aLeadK @9 :Float32; + fcw @10 :Bool; + status @11 :Bool; + aLeadTau @12 :Float32; + modelProb @13 :Float32; + radar @14 :Bool; + radarTrackId @15 :Int32 = -1; + + aLeadDEPRECATED @5 :Float32; + } + + # deprecated + ftMonoTimeDEPRECATED @7 :UInt64; + warpMatrixDEPRECATED @0 :List(Float32); + angleOffsetDEPRECATED @1 :Float32; + calStatusDEPRECATED @2 :Int8; + calCycleDEPRECATED @8 :Int32; + calPercDEPRECATED @9 :Int8; + canMonoTimesDEPRECATED @10 :List(UInt64); + cumLagMsDEPRECATED @5 :Float32; + radarErrorsDEPRECATED @12 :List(Car.RadarData.ErrorDEPRECATED); +} + +struct LiveCalibrationData { + calStatus @11 :Status; + calCycle @2 :Int32; + calPerc @3 :Int8; + validBlocks @9 :Int32; + + # view_frame_from_road_frame + # ui's is inversed needs new + extrinsicMatrix @4 :List(Float32); + # the direction of travel vector in device frame + rpyCalib @7 :List(Float32); + rpyCalibSpread @8 :List(Float32); + wideFromDeviceEuler @10 :List(Float32); + height @12 :List(Float32); + + warpMatrixDEPRECATED @0 :List(Float32); + calStatusDEPRECATED @1 :Int8; + warpMatrix2DEPRECATED @5 :List(Float32); + warpMatrixBigDEPRECATED @6 :List(Float32); + + enum Status { + uncalibrated @0; + calibrated @1; + invalid @2; + recalibrating @3; + } +} + +struct LiveTracksDEPRECATED { + trackId @0 :Int32; + dRel @1 :Float32; + yRel @2 :Float32; + vRel @3 :Float32; + aRel @4 :Float32; + timeStamp @5 :Float32; + status @6 :Float32; + currentTime @7 :Float32; + stationary @8 :Bool; + oncoming @9 :Bool; +} + +struct SelfdriveState { + # high level system state + state @0 :OpenpilotState; + enabled @1 :Bool; + active @2 :Bool; + engageable @9 :Bool; # can OP be engaged? + + # UI alerts + alertText1 @3 :Text; + alertText2 @4 :Text; + alertStatus @5 :AlertStatus; + alertSize @6 :AlertSize; + alertType @7 :Text; + alertSound @8 :Car.CarControl.HUDControl.AudibleAlert; + alertHudVisual @12 :Car.CarControl.HUDControl.VisualAlert; + + # configurable driving settings + experimentalMode @10 :Bool; + personality @11 :LongitudinalPersonality; + + enum OpenpilotState @0xdbe58b96d2d1ac61 { + disabled @0; + preEnabled @1; + enabled @2; + softDisabling @3; + overriding @4; # superset of overriding with steering or accelerator + } + + enum AlertStatus @0xa0d0dcd113193c62 { + normal @0; + userPrompt @1; + critical @2; + } + + enum AlertSize @0xe98bb99d6e985f64 { + none @0; + small @1; + mid @2; + full @3; + } +} + +struct ControlsState @0x97ff69c53601abf1 { + longitudinalPlanMonoTime @28 :UInt64; + lateralPlanMonoTime @50 :UInt64; + + longControlState @30 :Car.CarControl.Actuators.LongControlState; + upAccelCmd @4 :Float32; + uiAccelCmd @5 :Float32; + ufAccelCmd @33 :Float32; + curvature @37 :Float32; # path curvature from vehicle model + desiredCurvature @61 :Float32; # lag adjusted curvatures used by lateral controllers + forceDecel @51 :Bool; + + lateralControlState :union { + pidState @53 :LateralPIDState; + angleState @58 :LateralAngleState; + debugState @59 :LateralDebugState; + torqueState @60 :LateralTorqueState; + + curvatureStateDEPRECATED @65 :LateralCurvatureState; + lqrStateDEPRECATED @55 :LateralLQRState; + indiStateDEPRECATED @52 :LateralINDIState; + } + + struct LateralINDIState { + active @0 :Bool; + steeringAngleDeg @1 :Float32; + steeringRateDeg @2 :Float32; + steeringAccelDeg @3 :Float32; + rateSetPoint @4 :Float32; + accelSetPoint @5 :Float32; + accelError @6 :Float32; + delayedOutput @7 :Float32; + delta @8 :Float32; + output @9 :Float32; + saturated @10 :Bool; + steeringAngleDesiredDeg @11 :Float32; + steeringRateDesiredDeg @12 :Float32; + } + + struct LateralPIDState { + active @0 :Bool; + steeringAngleDeg @1 :Float32; + steeringRateDeg @2 :Float32; + angleError @3 :Float32; + p @4 :Float32; + i @5 :Float32; + f @6 :Float32; + output @7 :Float32; + saturated @8 :Bool; + steeringAngleDesiredDeg @9 :Float32; + } + + struct LateralTorqueState { + active @0 :Bool; + error @1 :Float32; + errorRate @8 :Float32; + p @2 :Float32; + i @3 :Float32; + d @4 :Float32; + f @5 :Float32; + output @6 :Float32; + saturated @7 :Bool; + actualLateralAccel @9 :Float32; + desiredLateralAccel @10 :Float32; + desiredLateralJerk @11 :Float32; + version @12 :Int32; + } + + struct LateralLQRState { + active @0 :Bool; + steeringAngleDeg @1 :Float32; + i @2 :Float32; + output @3 :Float32; + lqrOutput @4 :Float32; + saturated @5 :Bool; + steeringAngleDesiredDeg @6 :Float32; + } + + struct LateralAngleState { + active @0 :Bool; + steeringAngleDeg @1 :Float32; + output @2 :Float32; + saturated @3 :Bool; + steeringAngleDesiredDeg @4 :Float32; + } + + struct LateralCurvatureState { + active @0 :Bool; + actualCurvature @1 :Float32; + desiredCurvature @2 :Float32; + error @3 :Float32; + p @4 :Float32; + i @5 :Float32; + f @6 :Float32; + output @7 :Float32; + saturated @8 :Bool; + } + + struct LateralDebugState { + active @0 :Bool; + steeringAngleDeg @1 :Float32; + output @2 :Float32; + saturated @3 :Bool; + } + + # deprecated + vEgoDEPRECATED @0 :Float32; + vEgoRawDEPRECATED @32 :Float32; + aEgoDEPRECATED @1 :Float32; + canMonoTimeDEPRECATED @16 :UInt64; + radarStateMonoTimeDEPRECATED @17 :UInt64; + mdMonoTimeDEPRECATED @18 :UInt64; + yActualDEPRECATED @6 :Float32; + yDesDEPRECATED @7 :Float32; + upSteerDEPRECATED @8 :Float32; + uiSteerDEPRECATED @9 :Float32; + ufSteerDEPRECATED @34 :Float32; + aTargetMinDEPRECATED @10 :Float32; + aTargetMaxDEPRECATED @11 :Float32; + rearViewCamDEPRECATED @23 :Bool; + driverMonitoringOnDEPRECATED @43 :Bool; + hudLeadDEPRECATED @14 :Int32; + alertSoundDEPRECATED @45 :Text; + angleModelBiasDEPRECATED @27 :Float32; + gpsPlannerActiveDEPRECATED @40 :Bool; + decelForTurnDEPRECATED @47 :Bool; + decelForModelDEPRECATED @54 :Bool; + awarenessStatusDEPRECATED @26 :Float32; + angleSteersDEPRECATED @13 :Float32; + vCurvatureDEPRECATED @46 :Float32; + mapValidDEPRECATED @49 :Bool; + jerkFactorDEPRECATED @12 :Float32; + steerOverrideDEPRECATED @20 :Bool; + steeringAngleDesiredDegDEPRECATED @29 :Float32; + canMonoTimesDEPRECATED @21 :List(UInt64); + desiredCurvatureRateDEPRECATED @62 :Float32; + canErrorCounterDEPRECATED @57 :UInt32; + vPidDEPRECATED @2 :Float32; + alertBlinkingRateDEPRECATED @42 :Float32; + alertText1DEPRECATED @24 :Text; + alertText2DEPRECATED @25 :Text; + alertStatusDEPRECATED @38 :SelfdriveState.AlertStatus; + alertSizeDEPRECATED @39 :SelfdriveState.AlertSize; + alertTypeDEPRECATED @44 :Text; + alertSound2DEPRECATED @56 :Car.CarControl.HUDControl.AudibleAlert; + engageableDEPRECATED @41 :Bool; # can OP be engaged? + stateDEPRECATED @31 :SelfdriveState.OpenpilotState; + enabledDEPRECATED @19 :Bool; + activeDEPRECATED @36 :Bool; + experimentalModeDEPRECATED @64 :Bool; + personalityDEPRECATED @66 :LongitudinalPersonality; + vCruiseDEPRECATED @22 :Float32; # actual set speed + vCruiseClusterDEPRECATED @63 :Float32; # set speed to display in the UI + startMonoTimeDEPRECATED @48 :UInt64; + cumLagMsDEPRECATED @15 :Float32; + aTargetDEPRECATED @35 :Float32; + vTargetLeadDEPRECATED @3 :Float32; +} + +struct DrivingModelData { + frameId @0 :UInt32; + frameIdExtra @1 :UInt32; + frameDropPerc @6 :Float32; + modelExecutionTime @7 :Float32; + + action @2 :ModelDataV2.Action; + + laneLineMeta @3 :LaneLineMeta; + meta @4 :MetaData; + + path @5 :PolyPath; + + struct PolyPath { + xCoefficients @0 :List(Float32); + yCoefficients @1 :List(Float32); + zCoefficients @2 :List(Float32); + } + + struct LaneLineMeta { + leftY @0 :Float32; + rightY @1 :Float32; + leftProb @2 :Float32; + rightProb @3 :Float32; + } + + struct MetaData { + laneChangeState @0 :LaneChangeState; + laneChangeDirection @1 :LaneChangeDirection; + } +} + +# All SI units and in device frame +struct XYZTData @0xc3cbae1fd505ae80 { + x @0 :List(Float32); + y @1 :List(Float32); + z @2 :List(Float32); + t @3 :List(Float32); + xStd @4 :List(Float32); + yStd @5 :List(Float32); + zStd @6 :List(Float32); +} + +struct ModelDataV2 { + frameId @0 :UInt32; + frameIdExtra @20 :UInt32; + frameAge @1 :UInt32; + frameDropPerc @2 :Float32; + timestampEof @3 :UInt64; + modelExecutionTime @15 :Float32; + rawPredictions @16 :Data; + + # predicted future position, orientation, etc.. + position @4 :XYZTData; + orientation @5 :XYZTData; + velocity @6 :XYZTData; + orientationRate @7 :XYZTData; + acceleration @19 :XYZTData; + + # prediction lanelines and road edges + laneLines @8 :List(XYZTData); + laneLineProbs @9 :List(Float32); + laneLineStds @13 :List(Float32); + roadEdges @10 :List(XYZTData); + roadEdgeStds @14 :List(Float32); + + # predicted lead cars + leads @11 :List(LeadDataV2); + leadsV3 @18 :List(LeadDataV3); + + meta @12 :MetaData; + confidence @23: ConfidenceClass; + + # Model perceived motion + temporalPoseDEPRECATED @21 :Pose; + + # e2e lateral planner + action @26: Action; + + gpuExecutionTimeDEPRECATED @17 :Float32; + navEnabledDEPRECATED @22 :Bool; + locationMonoTimeDEPRECATED @24 :UInt64; + lateralPlannerSolutionDEPRECATED @25: LateralPlannerSolution; + + struct LeadDataV2 { + prob @0 :Float32; # probability that car is your lead at time t + t @1 :Float32; + + # x and y are relative position in device frame + # v is norm relative speed + # a is norm relative acceleration + xyva @2 :List(Float32); + xyvaStd @3 :List(Float32); + } + + struct LeadDataV3 { + prob @0 :Float32; # probability that car is your lead at time t + probTime @1 :Float32; + t @2 :List(Float32); + + # x and y are relative position in device frame + # v absolute norm speed + # a is derivative of v + x @3 :List(Float32); + xStd @4 :List(Float32); + y @5 :List(Float32); + yStd @6 :List(Float32); + v @7 :List(Float32); + vStd @8 :List(Float32); + a @9 :List(Float32); + aStd @10 :List(Float32); + } + + + struct MetaData { + engagedProb @0 :Float32; + desirePrediction @1 :List(Float32); + desireState @5 :List(Float32); + disengagePredictions @6 :DisengagePredictions; + hardBrakePredicted @7 :Bool; + laneChangeState @8 :LaneChangeState; + laneChangeDirection @9 :LaneChangeDirection; + + + # deprecated + brakeDisengageProbDEPRECATED @2 :Float32; + gasDisengageProbDEPRECATED @3 :Float32; + steerOverrideProbDEPRECATED @4 :Float32; + } + + enum ConfidenceClass { + red @0; + yellow @1; + green @2; + } + + struct DisengagePredictions { + t @0 :List(Float32); + brakeDisengageProbs @1 :List(Float32); + gasDisengageProbs @2 :List(Float32); + steerOverrideProbs @3 :List(Float32); + brake3MetersPerSecondSquaredProbs @4 :List(Float32); + brake4MetersPerSecondSquaredProbs @5 :List(Float32); + brake5MetersPerSecondSquaredProbs @6 :List(Float32); + gasPressProbs @7 :List(Float32); + brakePressProbs @8 :List(Float32); + } + + struct Pose { + trans @0 :List(Float32); # m/s in device frame + rot @1 :List(Float32); # rad/s in device frame + transStd @2 :List(Float32); # std m/s in device frame + rotStd @3 :List(Float32); # std rad/s in device frame + } + + struct LateralPlannerSolution { + x @0 :List(Float32); + y @1 :List(Float32); + yaw @2 :List(Float32); + yawRate @3 :List(Float32); + xStd @4 :List(Float32); + yStd @5 :List(Float32); + yawStd @6 :List(Float32); + yawRateStd @7 :List(Float32); + } + + struct Action { + desiredCurvature @0 :Float32; + desiredAcceleration @1 :Float32; + shouldStop @2 :Bool; + } +} + +struct EncodeIndex { + # picture from camera + frameId @0 :UInt32; + type @1 :Type; + # index of encoder from start of route + encodeId @2 :UInt32; + # minute long segment this frame is in + segmentNum @3 :Int32; + # index into camera file in segment in presentation order + segmentId @4 :UInt32; + # index into camera file in segment in encode order + segmentIdEncode @5 :UInt32; + timestampSof @6 :UInt64; + timestampEof @7 :UInt64; + + # encoder metadata + flags @8 :UInt32; + len @9 :UInt32; + + enum Type { + bigBoxLossless @0; + fullHEVC @1; + qcameraH264 @6; + livestreamH264 @7; + + # deprecated + bigBoxHEVCDEPRECATED @2; + chffrAndroidH264DEPRECATED @3; + fullLosslessClipDEPRECATED @4; + frontDEPRECATED @5; + + } +} + +struct AndroidLogEntry { + id @0 :UInt8; + ts @1 :UInt64; + priority @2 :UInt8; + pid @3 :Int32; + tid @4 :Int32; + tag @5 :Text; + message @6 :Text; +} + +struct DriverAssistance { + # Lane Departure Warnings + leftLaneDeparture @0 :Bool; + rightLaneDeparture @1 :Bool; + + # FCW, AEB, etc. will go here +} + +struct LateralManeuverPlan { + desiredCurvature @0 :Float32; # 1/m +} + +struct LongitudinalPlan @0xe00b5b3eba12876c { + modelMonoTime @9 :UInt64; + hasLead @7 :Bool; + fcw @8 :Bool; + longitudinalPlanSource @15 :LongitudinalPlanSource; + processingDelay @29 :Float32; + + # desired speed/accel/jerk over next 2.5s + accels @32 :List(Float32); + speeds @33 :List(Float32); + jerks @34 :List(Float32); + aTarget @18 :Float32; + shouldStop @37: Bool; + allowThrottle @38: Bool; + allowBrake @39: Bool; + leadDistance @40 :Float32; + + + solverExecutionTime @35 :Float32; + + # lead trajectories the MPC solved against (13 points at T_IDXS) + leadTrajectoryX0 @41 :List(Float32); + leadTrajectoryV0 @42 :List(Float32); + leadTrajectoryX1 @43 :List(Float32); + leadTrajectoryV1 @44 :List(Float32); + + enum LongitudinalPlanSource { + cruise @0; + lead0 @1; + lead1 @2; + lead2 @3; + e2e @4; + } + + # deprecated + vCruiseDEPRECATED @16 :Float32; + aCruiseDEPRECATED @17 :Float32; + vTargetDEPRECATED @3 :Float32; + vTargetFutureDEPRECATED @14 :Float32; + vStartDEPRECATED @26 :Float32; + aStartDEPRECATED @27 :Float32; + vMaxDEPRECATED @20 :Float32; + radarStateMonoTimeDEPRECATED @10 :UInt64; + jerkFactorDEPRECATED @6 :Float32; + hasLeftLaneDEPRECATED @23 :Bool; + hasRightLaneDEPRECATED @24 :Bool; + aTargetMinDEPRECATED @4 :Float32; + aTargetMaxDEPRECATED @5 :Float32; + lateralValidDEPRECATED @0 :Bool; + longitudinalValidDEPRECATED @2 :Bool; + dPolyDEPRECATED @1 :List(Float32); + laneWidthDEPRECATED @11 :Float32; + vCurvatureDEPRECATED @21 :Float32; + decelForTurnDEPRECATED @22 :Bool; + mapValidDEPRECATED @25 :Bool; + radarValidDEPRECATED @28 :Bool; + radarCanErrorDEPRECATED @30 :Bool; + commIssueDEPRECATED @31 :Bool; + eventsDEPRECATED @13 :List(Car.OnroadEventDEPRECATED); + gpsTrajectoryDEPRECATED @12 :GpsTrajectory; + gpsPlannerActiveDEPRECATED @19 :Bool; + personalityDEPRECATED @36 :LongitudinalPersonality; + + struct GpsTrajectory { + x @0 :List(Float32); + y @1 :List(Float32); + } +} +struct UiPlan { + frameId @2 :UInt32; + position @0 :XYZTData; + accel @1 :List(Float32); +} + +struct LateralPlan @0xe1e9318e2ae8b51e { + modelMonoTime @31 :UInt64; + laneWidthDEPRECATED @0 :Float32; + lProbDEPRECATED @5 :Float32; + rProbDEPRECATED @7 :Float32; + dPathPoints @20 :List(Float32); + dProbDEPRECATED @21 :Float32; + + mpcSolutionValid @9 :Bool; + desire @17 :Desire; + laneChangeState @18 :LaneChangeState; + laneChangeDirection @19 :LaneChangeDirection; + useLaneLines @29 :Bool; + + # desired curvatures over next 2.5s in rad/m + psis @26 :List(Float32); + curvatures @27 :List(Float32); + curvatureRates @28 :List(Float32); + + solverExecutionTime @30 :Float32; + solverCost @32 :Float32; + solverState @33 :SolverState; + + struct SolverState { + x @0 :List(List(Float32)); + u @1 :List(Float32); + } + + # deprecated + curvatureDEPRECATED @22 :Float32; + curvatureRateDEPRECATED @23 :Float32; + rawCurvatureDEPRECATED @24 :Float32; + rawCurvatureRateDEPRECATED @25 :Float32; + cProbDEPRECATED @3 :Float32; + dPolyDEPRECATED @1 :List(Float32); + cPolyDEPRECATED @2 :List(Float32); + lPolyDEPRECATED @4 :List(Float32); + rPolyDEPRECATED @6 :List(Float32); + modelValidDEPRECATED @12 :Bool; + commIssueDEPRECATED @15 :Bool; + posenetValidDEPRECATED @16 :Bool; + sensorValidDEPRECATED @14 :Bool; + paramsValidDEPRECATED @10 :Bool; + steeringAngleDegDEPRECATED @8 :Float32; # deg + steeringRateDegDEPRECATED @13 :Float32; # deg/s + angleOffsetDegDEPRECATED @11 :Float32; +} + +struct LiveLocationKalman { + + # More info on reference frames: + # https://github.com/commaai/openpilot/tree/master/common/transformations + + positionECEF @0 : Measurement; + positionGeodetic @1 : Measurement; + velocityECEF @2 : Measurement; + velocityNED @3 : Measurement; + velocityDevice @4 : Measurement; + accelerationDevice @5: Measurement; + + + # These angles are all eulers and roll, pitch, yaw + # orientationECEF transforms to rot matrix: ecef_from_device + orientationECEF @6 : Measurement; + calibratedOrientationECEF @20 : Measurement; + orientationNED @7 : Measurement; + angularVelocityDevice @8 : Measurement; + + # orientationNEDCalibrated transforms to rot matrix: NED_from_calibrated + calibratedOrientationNED @9 : Measurement; + + # Calibrated frame is simply device frame + # aligned with the vehicle + velocityCalibrated @10 : Measurement; + accelerationCalibrated @11 : Measurement; + angularVelocityCalibrated @12 : Measurement; + + gpsWeek @13 :Int32; + gpsTimeOfWeek @14 :Float64; + status @15 :Status; + unixTimestampMillis @16 :Int64; + inputsOK @17 :Bool = true; + posenetOK @18 :Bool = true; + gpsOK @19 :Bool = true; + sensorsOK @21 :Bool = true; + deviceStable @22 :Bool = true; + timeSinceReset @23 :Float64; + excessiveResets @24 :Bool; + timeToFirstFix @25 :Float32; + + filterState @26 : Measurement; + + enum Status { + uninitialized @0; + uncalibrated @1; + valid @2; + } + + struct Measurement { + value @0 : List(Float64); + std @1 : List(Float64); + valid @2 : Bool; + } +} + + +struct LivePose { + # More info on reference frames: + # https://github.com/commaai/openpilot/tree/master/common/transformations + orientationNED @0 :XYZMeasurement; + velocityDevice @1 :XYZMeasurement; + accelerationDevice @2 :XYZMeasurement; + angularVelocityDevice @3 :XYZMeasurement; + + inputsOK @4 :Bool = false; + posenetOK @5 :Bool = false; + sensorsOK @6 :Bool = false; + + debugFilterState @7 :FilterState; + + struct XYZMeasurement { + x @0 :Float32; + y @1 :Float32; + z @2 :Float32; + xStd @3 :Float32; + yStd @4 :Float32; + zStd @5 :Float32; + valid @6 :Bool; + } + + struct FilterState { + value @0 : List(Float64); + std @1 : List(Float64); + valid @2 : Bool; + + observations @3 :List(Observation); + + struct Observation { + kind @0 :Int32; + value @1 :List(Float32); + error @2 :List(Float32); + } + } +} + +struct ProcLog { + cpuTimes @0 :List(CPUTimes); + mem @1 :Mem; + procs @2 :List(Process); + + struct Process { + pid @0 :Int32; + name @1 :Text; + state @2 :UInt8; + ppid @3 :Int32; + + cpuUser @4 :Float32; + cpuSystem @5 :Float32; + cpuChildrenUser @6 :Float32; + cpuChildrenSystem @7 :Float32; + priority @8 :Int64; + nice @9 :Int32; + numThreads @10 :Int32; + startTime @11 :Float64; + + memVms @12 :UInt64; + memRss @13 :UInt64; + + processor @14 :Int32; + + cmdline @15 :List(Text); + exe @16 :Text; + } + + struct CPUTimes { + cpuNum @0 :Int64; + user @1 :Float32; + nice @2 :Float32; + system @3 :Float32; + idle @4 :Float32; + iowait @5 :Float32; + irq @6 :Float32; + softirq @7 :Float32; + } + + struct Mem { + total @0 :UInt64; + free @1 :UInt64; + available @2 :UInt64; + buffers @3 :UInt64; + cached @4 :UInt64; + active @5 :UInt64; + inactive @6 :UInt64; + shared @7 :UInt64; + } +} + +struct GnssMeasurements { + measTime @0 :UInt64; + gpsWeek @1 :Int16; + gpsTimeOfWeek @2 :Float64; + + correctedMeasurements @3 :List(CorrectedMeasurement); + ephemerisStatuses @9 :List(EphemerisStatus); + + kalmanPositionECEF @4 :LiveLocationKalman.Measurement; + kalmanVelocityECEF @5 :LiveLocationKalman.Measurement; + positionECEF @6 :LiveLocationKalman.Measurement; + velocityECEF @7 :LiveLocationKalman.Measurement; + timeToFirstFix @8 :Float32; + # Todo sync this with timing pulse of ublox + + struct EphemerisStatus { + constellationId @0 :ConstellationId; + svId @1 :UInt8; + type @2 :EphemerisType; + source @3 :EphemerisSource; + gpsWeek @4 : UInt16; + tow @5 :Float64; + } + + struct CorrectedMeasurement { + constellationId @0 :ConstellationId; + svId @1 :UInt8; + # Is 0 when not Glonass constellation. + glonassFrequency @2 :Int8; + pseudorange @3 :Float64; + pseudorangeStd @4 :Float64; + pseudorangeRate @5 :Float64; + pseudorangeRateStd @6 :Float64; + # Satellite position and velocity [x,y,z] + satPos @7 :List(Float64); + satVel @8 :List(Float64); + ephemerisSourceDEPRECATED @9 :EphemerisSourceDEPRECATED; + } + + struct EphemerisSourceDEPRECATED { + type @0 :EphemerisType; + # first epoch in file: + gpsWeek @1 :Int16; # -1 if Nav + gpsTimeOfWeek @2 :Int32; # -1 if Nav. Integer for seconds is good enough for logs. + } + + enum ConstellationId { + # Satellite Constellation using the Ublox gnssid as index + gps @0; + sbas @1; + galileo @2; + beidou @3; + imes @4; + qznss @5; + glonass @6; + } + + enum EphemerisType { + nav @0; + # Different ultra-rapid files: + nasaUltraRapid @1; + glonassIacUltraRapid @2; + qcom @3; + } + + enum EphemerisSource { + gnssChip @0; + internet @1; + cache @2; + unknown @3; + } +} + +struct UbloxGnss { + union { + measurementReport @0 :MeasurementReport; + ephemeris @1 :Ephemeris; + ionoData @2 :IonoData; + hwStatus @3 :HwStatus; + hwStatus2 @4 :HwStatus2; + glonassEphemeris @5 :GlonassEphemeris; + satReport @6 :SatReport; + } + + struct SatReport { + #received time of week in gps time in seconds and gps week + iTow @0 :UInt32; + svs @1 :List(SatInfo); + + struct SatInfo { + svId @0 :UInt8; + gnssId @1 :UInt8; + flagsBitfield @2 :UInt32; + cno @3 :UInt8; + elevationDeg @4 :Int8; + azimuthDeg @5 :Int16; + pseudorangeResidual @6 :Float32; + } + } + + struct MeasurementReport { + #received time of week in gps time in seconds and gps week + rcvTow @0 :Float64; + gpsWeek @1 :UInt16; + # leap seconds in seconds + leapSeconds @2 :UInt16; + # receiver status + receiverStatus @3 :ReceiverStatus; + # num of measurements to follow + numMeas @4 :UInt8; + measurements @5 :List(Measurement); + + struct ReceiverStatus { + # leap seconds have been determined + leapSecValid @0 :Bool; + # Clock reset applied + clkReset @1 :Bool; + } + + struct Measurement { + svId @0 :UInt8; + trackingStatus @1 :TrackingStatus; + # pseudorange in meters + pseudorange @2 :Float64; + # carrier phase measurement in cycles + carrierCycles @3 :Float64; + # doppler measurement in Hz + doppler @4 :Float32; + # GNSS id, 0 is gps + gnssId @5 :UInt8; + glonassFrequencyIndex @6 :UInt8; + # carrier phase locktime counter in ms + locktime @7 :UInt16; + # Carrier-to-noise density ratio (signal strength) in dBHz + cno @8 :UInt8; + # pseudorange standard deviation in meters + pseudorangeStdev @9 :Float32; + # carrier phase standard deviation in cycles + carrierPhaseStdev @10 :Float32; + # doppler standard deviation in Hz + dopplerStdev @11 :Float32; + sigId @12 :UInt8; + + struct TrackingStatus { + # pseudorange valid + pseudorangeValid @0 :Bool; + # carrier phase valid + carrierPhaseValid @1 :Bool; + # half cycle valid + halfCycleValid @2 :Bool; + # half cycle subtracted from phase + halfCycleSubtracted @3 :Bool; + } + } + } + + struct Ephemeris { + # This is according to the rinex (2?) format + svId @0 :UInt16; + year @1 :UInt16; + month @2 :UInt16; + day @3 :UInt16; + hour @4 :UInt16; + minute @5 :UInt16; + second @6 :Float32; + af0 @7 :Float64; + af1 @8 :Float64; + af2 @9 :Float64; + + iode @10 :Float64; + crs @11 :Float64; + deltaN @12 :Float64; + m0 @13 :Float64; + + cuc @14 :Float64; + ecc @15 :Float64; + cus @16 :Float64; + a @17 :Float64; # note that this is not the root!! + + toe @18 :Float64; + cic @19 :Float64; + omega0 @20 :Float64; + cis @21 :Float64; + + i0 @22 :Float64; + crc @23 :Float64; + omega @24 :Float64; + omegaDot @25 :Float64; + + iDot @26 :Float64; + codesL2 @27 :Float64; + gpsWeekDEPRECATED @28 :Float64; + l2 @29 :Float64; + + svAcc @30 :Float64; + svHealth @31 :Float64; + tgd @32 :Float64; + iodc @33 :Float64; + + transmissionTime @34 :Float64; + fitInterval @35 :Float64; + + toc @36 :Float64; + + ionoCoeffsValid @37 :Bool; + ionoAlpha @38 :List(Float64); + ionoBeta @39 :List(Float64); + + towCount @40 :UInt32; + toeWeek @41 :UInt16; + tocWeek @42 :UInt16; + } + + struct IonoData { + svHealth @0 :UInt32; + tow @1 :Float64; + gpsWeek @2 :Float64; + + ionoAlpha @3 :List(Float64); + ionoBeta @4 :List(Float64); + + healthValid @5 :Bool; + ionoCoeffsValid @6 :Bool; + } + + struct HwStatus { + noisePerMS @0 :UInt16; + agcCnt @1 :UInt16; + aStatus @2 :AntennaSupervisorState; + aPower @3 :AntennaPowerStatus; + jamInd @4 :UInt8; + flags @5 :UInt8; + + enum AntennaSupervisorState { + init @0; + dontknow @1; + ok @2; + short @3; + open @4; + } + + enum AntennaPowerStatus { + off @0; + on @1; + dontknow @2; + } + } + + struct HwStatus2 { + ofsI @0 :Int8; + magI @1 :UInt8; + ofsQ @2 :Int8; + magQ @3 :UInt8; + cfgSource @4 :ConfigSource; + lowLevCfg @5 :UInt32; + postStatus @6 :UInt32; + + enum ConfigSource { + undefined @0; + rom @1; + otp @2; + configpins @3; + flash @4; + } + } + + struct GlonassEphemeris { + svId @0 :UInt16; + year @1 :UInt16; + dayInYear @2 :UInt16; + hour @3 :UInt16; + minute @4 :UInt16; + second @5 :Float32; + + x @6 :Float64; + xVel @7 :Float64; + xAccel @8 :Float64; + y @9 :Float64; + yVel @10 :Float64; + yAccel @11 :Float64; + z @12 :Float64; + zVel @13 :Float64; + zAccel @14 :Float64; + + svType @15 :UInt8; + svURA @16 :Float32; + age @17 :UInt8; + + svHealth @18 :UInt8; + tkDEPRECATED @19 :UInt16; + tb @20 :UInt16; + + tauN @21 :Float64; + deltaTauN @22 :Float64; + gammaN @23 :Float64; + + p1 @24 :UInt8; + p2 @25 :UInt8; + p3 @26 :UInt8; + p4 @27 :UInt8; + + freqNumDEPRECATED @28 :UInt32; + + n4 @29 :UInt8; + nt @30 :UInt16; + freqNum @31 :Int16; + tkSeconds @32 :UInt32; + } +} + +struct QcomGnss @0xde94674b07ae51c1 { + logTs @0 :UInt64; + union { + measurementReport @1 :MeasurementReport; + clockReport @2 :ClockReport; + drMeasurementReport @3 :DrMeasurementReport; + drSvPoly @4 :DrSvPolyReport; + rawLog @5 :Data; + } + + enum MeasurementSource @0xd71a12b6faada7ee { + gps @0; + glonass @1; + beidou @2; + unknown3 @3; + unknown4 @4; + unknown5 @5; + sbas @6; + } + + enum SVObservationState @0xe81e829a0d6c83e9 { + idle @0; + search @1; + searchVerify @2; + bitEdge @3; + trackVerify @4; + track @5; + restart @6; + dpo @7; + glo10msBe @8; + glo10msAt @9; + } + + struct MeasurementStatus @0xe501010e1bcae83b { + subMillisecondIsValid @0 :Bool; + subBitTimeIsKnown @1 :Bool; + satelliteTimeIsKnown @2 :Bool; + bitEdgeConfirmedFromSignal @3 :Bool; + measuredVelocity @4 :Bool; + fineOrCoarseVelocity @5 :Bool; + lockPointValid @6 :Bool; + lockPointPositive @7 :Bool; + lastUpdateFromDifference @8 :Bool; + lastUpdateFromVelocityDifference @9 :Bool; + strongIndicationOfCrossCorelation @10 :Bool; + tentativeMeasurement @11 :Bool; + measurementNotUsable @12 :Bool; + sirCheckIsNeeded @13 :Bool; + probationMode @14 :Bool; + + glonassMeanderBitEdgeValid @15 :Bool; + glonassTimeMarkValid @16 :Bool; + + gpsRoundRobinRxDiversity @17 :Bool; + gpsRxDiversity @18 :Bool; + gpsLowBandwidthRxDiversityCombined @19 :Bool; + gpsHighBandwidthNu4 @20 :Bool; + gpsHighBandwidthNu8 @21 :Bool; + gpsHighBandwidthUniform @22 :Bool; + multipathIndicator @23 :Bool; + + imdJammingIndicator @24 :Bool; + lteB13TxJammingIndicator @25 :Bool; + freshMeasurementIndicator @26 :Bool; + + multipathEstimateIsValid @27 :Bool; + directionIsValid @28 :Bool; + } + + struct MeasurementReport @0xf580d7d86b7b8692 { + source @0 :MeasurementSource; + + fCount @1 :UInt32; + + gpsWeek @2 :UInt16; + glonassCycleNumber @3 :UInt8; + glonassNumberOfDays @4 :UInt16; + + milliseconds @5 :UInt32; + timeBias @6 :Float32; + clockTimeUncertainty @7 :Float32; + clockFrequencyBias @8 :Float32; + clockFrequencyUncertainty @9 :Float32; + + sv @10 :List(SV); + + struct SV @0xf10c595ae7bb2c27 { + svId @0 :UInt8; + observationState @2 :SVObservationState; + observations @3 :UInt8; + goodObservations @4 :UInt8; + gpsParityErrorCount @5 :UInt16; + glonassFrequencyIndex @1 :Int8; + glonassHemmingErrorCount @6 :UInt8; + filterStages @7 :UInt8; + carrierNoise @8 :UInt16; + latency @9 :Int16; + predetectInterval @10 :UInt8; + postdetections @11 :UInt16; + + unfilteredMeasurementIntegral @12 :UInt32; + unfilteredMeasurementFraction @13 :Float32; + unfilteredTimeUncertainty @14 :Float32; + unfilteredSpeed @15 :Float32; + unfilteredSpeedUncertainty @16 :Float32; + measurementStatus @17 :MeasurementStatus; + multipathEstimate @18 :UInt32; + azimuth @19 :Float32; + elevation @20 :Float32; + carrierPhaseCyclesIntegral @21 :Int32; + carrierPhaseCyclesFraction @22 :UInt16; + fineSpeed @23 :Float32; + fineSpeedUncertainty @24 :Float32; + cycleSlipCount @25 :UInt8; + } + + } + + struct ClockReport @0xca965e4add8f4f0b { + hasFCount @0 :Bool; + fCount @1 :UInt32; + + hasGpsWeek @2 :Bool; + gpsWeek @3 :UInt16; + hasGpsMilliseconds @4 :Bool; + gpsMilliseconds @5 :UInt32; + gpsTimeBias @6 :Float32; + gpsClockTimeUncertainty @7 :Float32; + gpsClockSource @8 :UInt8; + + hasGlonassYear @9 :Bool; + glonassYear @10 :UInt8; + hasGlonassDay @11 :Bool; + glonassDay @12 :UInt16; + hasGlonassMilliseconds @13 :Bool; + glonassMilliseconds @14 :UInt32; + glonassTimeBias @15 :Float32; + glonassClockTimeUncertainty @16 :Float32; + glonassClockSource @17 :UInt8; + + bdsWeek @18 :UInt16; + bdsMilliseconds @19 :UInt32; + bdsTimeBias @20 :Float32; + bdsClockTimeUncertainty @21 :Float32; + bdsClockSource @22 :UInt8; + + galWeek @23 :UInt16; + galMilliseconds @24 :UInt32; + galTimeBias @25 :Float32; + galClockTimeUncertainty @26 :Float32; + galClockSource @27 :UInt8; + + clockFrequencyBias @28 :Float32; + clockFrequencyUncertainty @29 :Float32; + frequencySource @30 :UInt8; + gpsLeapSeconds @31 :UInt8; + gpsLeapSecondsUncertainty @32 :UInt8; + gpsLeapSecondsSource @33 :UInt8; + + gpsToGlonassTimeBiasMilliseconds @34 :Float32; + gpsToGlonassTimeBiasMillisecondsUncertainty @35 :Float32; + gpsToBdsTimeBiasMilliseconds @36 :Float32; + gpsToBdsTimeBiasMillisecondsUncertainty @37 :Float32; + bdsToGloTimeBiasMilliseconds @38 :Float32; + bdsToGloTimeBiasMillisecondsUncertainty @39 :Float32; + gpsToGalTimeBiasMilliseconds @40 :Float32; + gpsToGalTimeBiasMillisecondsUncertainty @41 :Float32; + galToGloTimeBiasMilliseconds @42 :Float32; + galToGloTimeBiasMillisecondsUncertainty @43 :Float32; + galToBdsTimeBiasMilliseconds @44 :Float32; + galToBdsTimeBiasMillisecondsUncertainty @45 :Float32; + + hasRtcTime @46 :Bool; + systemRtcTime @47 :UInt32; + fCountOffset @48 :UInt32; + lpmRtcCount @49 :UInt32; + clockResets @50 :UInt32; + } + + struct DrMeasurementReport @0x8053c39445c6c75c { + + reason @0 :UInt8; + seqNum @1 :UInt8; + seqMax @2 :UInt8; + rfLoss @3 :UInt16; + + systemRtcValid @4 :Bool; + fCount @5 :UInt32; + clockResets @6 :UInt32; + systemRtcTime @7 :UInt64; + + gpsLeapSeconds @8 :UInt8; + gpsLeapSecondsUncertainty @9 :UInt8; + gpsToGlonassTimeBiasMilliseconds @10 :Float32; + gpsToGlonassTimeBiasMillisecondsUncertainty @11 :Float32; + + gpsWeek @12 :UInt16; + gpsMilliseconds @13 :UInt32; + gpsTimeBiasMs @14 :UInt32; + gpsClockTimeUncertaintyMs @15 :UInt32; + gpsClockSource @16 :UInt8; + + glonassClockSource @17 :UInt8; + glonassYear @18 :UInt8; + glonassDay @19 :UInt16; + glonassMilliseconds @20 :UInt32; + glonassTimeBias @21 :Float32; + glonassClockTimeUncertainty @22 :Float32; + + clockFrequencyBias @23 :Float32; + clockFrequencyUncertainty @24 :Float32; + frequencySource @25 :UInt8; + + source @26 :MeasurementSource; + + sv @27 :List(SV); + + struct SV @0xf08b81df8cbf459c { + svId @0 :UInt8; + glonassFrequencyIndex @1 :Int8; + observationState @2 :SVObservationState; + observations @3 :UInt8; + goodObservations @4 :UInt8; + filterStages @5 :UInt8; + predetectInterval @6 :UInt8; + cycleSlipCount @7 :UInt8; + postdetections @8 :UInt16; + + measurementStatus @9 :MeasurementStatus; + + carrierNoise @10 :UInt16; + rfLoss @11 :UInt16; + latency @12 :Int16; + + filteredMeasurementFraction @13 :Float32; + filteredMeasurementIntegral @14 :UInt32; + filteredTimeUncertainty @15 :Float32; + filteredSpeed @16 :Float32; + filteredSpeedUncertainty @17 :Float32; + + unfilteredMeasurementFraction @18 :Float32; + unfilteredMeasurementIntegral @19 :UInt32; + unfilteredTimeUncertainty @20 :Float32; + unfilteredSpeed @21 :Float32; + unfilteredSpeedUncertainty @22 :Float32; + + multipathEstimate @23 :UInt32; + azimuth @24 :Float32; + elevation @25 :Float32; + dopplerAcceleration @26 :Float32; + fineSpeed @27 :Float32; + fineSpeedUncertainty @28 :Float32; + + carrierPhase @29 :Float64; + fCount @30 :UInt32; + + parityErrorCount @31 :UInt16; + goodParity @32 :Bool; + } + } + + struct DrSvPolyReport @0xb1fb80811a673270 { + svId @0 :UInt16; + frequencyIndex @1 :Int8; + + hasPosition @2 :Bool; + hasIono @3 :Bool; + hasTropo @4 :Bool; + hasElevation @5 :Bool; + polyFromXtra @6 :Bool; + hasSbasIono @7 :Bool; + + iode @8 :UInt16; + t0 @9 :Float64; + xyz0 @10 :List(Float64); + xyzN @11 :List(Float64); + other @12 :List(Float32); + + positionUncertainty @13 :Float32; + ionoDelay @14 :Float32; + ionoDot @15 :Float32; + sbasIonoDelay @16 :Float32; + sbasIonoDot @17 :Float32; + tropoDelay @18 :Float32; + elevation @19 :Float32; + elevationDot @20 :Float32; + elevationUncertainty @21 :Float32; + velocityCoeff @22 :List(Float64); + + gpsWeek @23 :UInt16; + gpsTow @24 :Float64; + } +} + +struct Clocks { + wallTimeNanos @3 :UInt64; # unix epoch time + + bootTimeNanosDEPRECATED @0 :UInt64; + monotonicNanosDEPRECATED @1 :UInt64; + monotonicRawNanosDEPRECATD @2 :UInt64; + modemUptimeMillisDEPRECATED @4 :UInt64; +} + +struct LiveMpcData { + x @0 :List(Float32); + y @1 :List(Float32); + psi @2 :List(Float32); + curvature @3 :List(Float32); + qpIterations @4 :UInt32; + calculationTime @5 :UInt64; + cost @6 :Float64; +} + +struct LiveLongitudinalMpcData { + xEgo @0 :List(Float32); + vEgo @1 :List(Float32); + aEgo @2 :List(Float32); + xLead @3 :List(Float32); + vLead @4 :List(Float32); + aLead @5 :List(Float32); + aLeadTau @6 :Float32; # lead accel time constant + qpIterations @7 :UInt32; + mpcId @8 :UInt32; + calculationTime @9 :UInt64; + cost @10 :Float64; +} + +struct Joystick { + # convenient for debug and live tuning + axes @0: List(Float32); + buttons @1: List(Bool); +} + +struct DriverStateV2 { + frameId @0 :UInt32; + modelExecutionTime @1 :Float32; + gpuExecutionTime @8 :Float32; + rawPredictions @3 :Data; + + wheelOnRightProb @5 :Float32; + leftDriverData @6 :DriverData; + rightDriverData @7 :DriverData; + + struct DriverData { + faceOrientation @0 :List(Float32); + faceOrientationStd @1 :List(Float32); + facePosition @2 :List(Float32); + facePositionStd @3 :List(Float32); + faceProb @4 :Float32; + leftEyeProb @5 :Float32; + rightEyeProb @6 :Float32; + leftBlinkProb @7 :Float32; + rightBlinkProb @8 :Float32; + sunglassesProb @9 :Float32; + phoneProb @13 :Float32; + notReadyProbDEPRECATED @12 :List(Float32); + occludedProbDEPRECATED @10 :Float32; + readyProbDEPRECATED @11 :List(Float32); + } + + dspExecutionTimeDEPRECATED @2 :Float32; + poorVisionProbDEPRECATED @4 :Float32; +} + +struct DriverStateDEPRECATED @0xb83c6cc593ed0a00 { + frameId @0 :UInt32; + modelExecutionTime @14 :Float32; + dspExecutionTime @16 :Float32; + rawPredictions @15 :Data; + + faceOrientation @3 :List(Float32); + facePosition @4 :List(Float32); + faceProb @5 :Float32; + leftEyeProb @6 :Float32; + rightEyeProb @7 :Float32; + leftBlinkProb @8 :Float32; + rightBlinkProb @9 :Float32; + faceOrientationStd @11 :List(Float32); + facePositionStd @12 :List(Float32); + sunglassesProb @13 :Float32; + poorVision @17 :Float32; + partialFace @18 :Float32; + distractedPose @19 :Float32; + distractedEyes @20 :Float32; + eyesOnRoad @21 :Float32; + phoneUse @22 :Float32; + occludedProb @23 :Float32; + + readyProb @24 :List(Float32); + notReadyProb @25 :List(Float32); + + irPwrDEPRECATED @10 :Float32; + descriptorDEPRECATED @1 :List(Float32); + stdDEPRECATED @2 :Float32; +} + +struct DriverMonitoringState @0xb83cda094a1da284 { + events @18 :List(OnroadEvent); + faceDetected @1 :Bool; + isDistracted @2 :Bool; + distractedType @17 :UInt32; + awarenessStatus @3 :Float32; + posePitchOffset @6 :Float32; + posePitchValidCount @7 :UInt32; + poseYawOffset @8 :Float32; + poseYawValidCount @9 :UInt32; + stepChange @10 :Float32; + awarenessActive @11 :Float32; + awarenessPassive @12 :Float32; + isLowStd @13 :Bool; + hiStdCount @14 :UInt32; + isActiveMode @16 :Bool; + isRHD @4 :Bool; + uncertainCount @19 :UInt32; + phoneProbOffset @20 :Float32; + phoneProbValidCount @21 :UInt32; + + isPreviewDEPRECATED @15 :Bool; + rhdCheckedDEPRECATED @5 :Bool; + eventsDEPRECATED @0 :List(Car.OnroadEventDEPRECATED); +} + +struct Boot { + wallTimeNanos @0 :UInt64; + pstore @4 :Map(Text, Data); + commands @5 :Map(Text, Data); + launchLog @3 :Text; + + lastKmsgDEPRECATED @1 :Data; + lastPmsgDEPRECATED @2 :Data; +} + +struct LiveParametersData { + valid @0 :Bool; + gyroBias @1 :Float32; + angleOffsetDeg @2 :Float32; + angleOffsetAverageDeg @3 :Float32; + stiffnessFactor @4 :Float32; + steerRatio @5 :Float32; + sensorValid @6 :Bool; + posenetSpeed @8 :Float32; + posenetValid @9 :Bool; + angleOffsetFastStd @10 :Float32; + angleOffsetAverageStd @11 :Float32; + stiffnessFactorStd @12 :Float32; + steerRatioStd @13 :Float32; + roll @14 :Float32; + debugFilterState @16 :FilterState; + + angleOffsetValid @17 :Bool = true; + angleOffsetAverageValid @18 :Bool = true; + steerRatioValid @19 :Bool = true; + stiffnessFactorValid @20 :Bool = true; + + yawRateDEPRECATED @7 :Float32; + filterStateDEPRECATED @15 :LiveLocationKalman.Measurement; + + struct FilterState { + value @0 : List(Float64); + std @1 : List(Float64); + } +} + +struct LiveTorqueParametersData { + liveValid @0 :Bool; + latAccelFactorRaw @1 :Float32; + latAccelOffsetRaw @2 :Float32; + frictionCoefficientRaw @3 :Float32; + latAccelFactorFiltered @4 :Float32; + latAccelOffsetFiltered @5 :Float32; + frictionCoefficientFiltered @6 :Float32; + totalBucketPoints @7 :Float32; + decay @8 :Float32; + maxResets @9 :Float32; + points @10 :List(List(Float32)); + version @11 :Int32; + useParams @12 :Bool; + calPerc @13 :Int8; +} + +struct LiveDelayData { + lateralDelay @0 :Float32; + validBlocks @1 :Int32; + status @2 :Status; + + lateralDelayEstimate @3 :Float32; + lateralDelayEstimateStd @5 :Float32; + points @4 :List(Float32); + calPerc @6 :Int8; + version @7 :Int32; + + enum Status { + unestimated @0; + estimated @1; + invalid @2; + } +} + +struct LiveMapDataDEPRECATED { + speedLimitValid @0 :Bool; + speedLimit @1 :Float32; + speedAdvisoryValid @12 :Bool; + speedAdvisory @13 :Float32; + speedLimitAheadValid @14 :Bool; + speedLimitAhead @15 :Float32; + speedLimitAheadDistance @16 :Float32; + curvatureValid @2 :Bool; + curvature @3 :Float32; + wayId @4 :UInt64; + roadX @5 :List(Float32); + roadY @6 :List(Float32); + lastGps @7: GpsLocationData; + roadCurvatureX @8 :List(Float32); + roadCurvature @9 :List(Float32); + distToTurn @10 :Float32; + mapValid @11 :Bool; +} + +struct CameraOdometry { + frameId @4 :UInt32; + timestampEof @5 :UInt64; + trans @0 :List(Float32); # m/s in device frame + rot @1 :List(Float32); # rad/s in device frame + transStd @2 :List(Float32); # std m/s in device frame + rotStd @3 :List(Float32); # std rad/s in device frame + wideFromDeviceEuler @6 :List(Float32); + wideFromDeviceEulerStd @7 :List(Float32); + roadTransformTrans @8 :List(Float32); + roadTransformTransStd @9 :List(Float32); +} + +struct Sentinel { + enum SentinelType { + endOfSegment @0; + endOfRoute @1; + startOfSegment @2; + startOfRoute @3; + } + type @0 :SentinelType; + signal @1 :Int32; +} + +struct UIDebug { + drawTimeMillis @0 :Float32; +} + +struct ManagerState { + processes @0 :List(ProcessState); + + struct ProcessState { + name @0 :Text; + pid @1 :Int32; + running @2 :Bool; + shouldBeRunning @4 :Bool; + exitCode @3 :Int32; + } +} + +struct UploaderState { + immediateQueueSize @0 :UInt32; + immediateQueueCount @1 :UInt32; + rawQueueSize @2 :UInt32; + rawQueueCount @3 :UInt32; + + # stats for last successfully uploaded file + lastTime @4 :Float32; # s + lastSpeed @5 :Float32; # MB/s + lastFilename @6 :Text; +} + +struct NavInstruction { + maneuverPrimaryText @0 :Text; + maneuverSecondaryText @1 :Text; + maneuverDistance @2 :Float32; # m + maneuverType @3 :Text; # TODO: Make Enum + maneuverModifier @4 :Text; # TODO: Make Enum + + distanceRemaining @5 :Float32; # m + timeRemaining @6 :Float32; # s + timeRemainingTypical @7 :Float32; # s + + lanes @8 :List(Lane); + showFull @9 :Bool; + + speedLimit @10 :Float32; # m/s + speedLimitSign @11 :SpeedLimitSign; + + allManeuvers @12 :List(Maneuver); + + struct Lane { + directions @0 :List(Direction); + active @1 :Bool; + activeDirection @2 :Direction; + } + + enum Direction { + none @0; + left @1; + right @2; + straight @3; + slightLeft @4; + slightRight @5; + } + + enum SpeedLimitSign { + mutcd @0; # US Style + vienna @1; # EU Style + } + + struct Maneuver { + distance @0 :Float32; + type @1 :Text; + modifier @2 :Text; + } +} + +struct NavRoute { + coordinates @0 :List(Coordinate); + + struct Coordinate { + latitude @0 :Float32; + longitude @1 :Float32; + } +} + +struct MapRenderState { + locationMonoTime @0 :UInt64; + renderTime @1 :Float32; + frameId @2: UInt32; +} + +struct NavModelData { + frameId @0 :UInt32; + locationMonoTime @6 :UInt64; + modelExecutionTime @1 :Float32; + dspExecutionTime @2 :Float32; + features @3 :List(Float32); + # predicted future position + position @4 :XYData; + desirePrediction @5 :List(Float32); + + # All SI units and in device frame + struct XYData { + x @0 :List(Float32); + y @1 :List(Float32); + xStd @2 :List(Float32); + yStd @3 :List(Float32); + } +} + +struct EncodeData { + idx @0 :EncodeIndex; + data @1 :Data; + header @2 :Data; + unixTimestampNanos @3 :UInt64; + width @4 :UInt32; + height @5 :UInt32; +} + +struct DebugAlert { + alertText1 @0 :Text; + alertText2 @1 :Text; +} + +struct UserBookmark @0xfe346a9de48d9b50 { +} + +struct SoundPressure @0xdc24138990726023 { + soundPressure @0 :Float32; + + # uncalibrated, A-weighted + soundPressureWeighted @3 :Float32; + soundPressureWeightedDb @1 :Float32; + + filteredSoundPressureWeightedDbDEPRECATED @2 :Float32; +} + +struct AudioData { + data @0 :Data; + sampleRate @1 :UInt32; +} + +struct AudioFeedback { + audio @0 :AudioData; + blockNum @1 :UInt16; +} + +struct Touch { + sec @0 :Int64; + usec @1 :Int64; + type @2 :UInt8; + code @3 :Int32; + value @4 :Int32; +} + +struct Event { + logMonoTime @0 :UInt64; # nanoseconds + valid @67 :Bool = true; + + union { + # *********** log metadata *********** + initData @1 :InitData; + sentinel @73 :Sentinel; + + # *********** bootlog *********** + boot @60 :Boot; + + # ********** openpilot daemon msgs ********** + gpsNMEA @3 :GPSNMEAData; + can @5 :List(CanData); + controlsState @7 :ControlsState; + selfdriveState @130 :SelfdriveState; + gyroscope @99 :SensorEventData; + accelerometer @98 :SensorEventData; + magnetometer @95 :SensorEventData; + lightSensor @96 :SensorEventData; + temperatureSensor @97 :SensorEventData; + pandaStates @81 :List(PandaState); + peripheralState @80 :PeripheralState; + radarState @13 :RadarState; + liveTracks @131 :Car.RadarData; + sendcan @17 :List(CanData); + liveCalibration @19 :LiveCalibrationData; + carState @22 :Car.CarState; + carControl @23 :Car.CarControl; + carOutput @127 :Car.CarOutput; + longitudinalPlan @24 :LongitudinalPlan; + lateralManeuverPlan @162 :LateralManeuverPlan; + driverAssistance @132 :DriverAssistance; + ubloxGnss @34 :UbloxGnss; + ubloxRaw @39 :Data; + qcomGnss @31 :QcomGnss; + gpsLocationExternal @48 :GpsLocationData; + gpsLocation @21 :GpsLocationData; + gnssMeasurements @91 :GnssMeasurements; + liveParameters @61 :LiveParametersData; + liveTorqueParameters @94 :LiveTorqueParametersData; + liveDelay @146 : LiveDelayData; + cameraOdometry @63 :CameraOdometry; + thumbnail @66: Thumbnail; + onroadEvents @134: List(OnroadEvent); + carParams @69: Car.CarParams; + driverMonitoringState @71: DriverMonitoringState; + livePose @129 :LivePose; + modelV2 @75 :ModelDataV2; + drivingModelData @128 :DrivingModelData; + driverStateV2 @92 :DriverStateV2; + + # camera stuff, each camera state has a matching encode idx + roadCameraState @2 :FrameData; + driverCameraState @70: FrameData; + wideRoadCameraState @74: FrameData; + roadEncodeIdx @15 :EncodeIndex; + driverEncodeIdx @76 :EncodeIndex; + wideRoadEncodeIdx @77 :EncodeIndex; + qRoadEncodeIdx @90 :EncodeIndex; + + livestreamRoadEncodeIdx @117 :EncodeIndex; + livestreamWideRoadEncodeIdx @118 :EncodeIndex; + livestreamDriverEncodeIdx @119 :EncodeIndex; + + # microphone data + soundPressure @103 :SoundPressure; + rawAudioData @147 :AudioData; + webrtcAudioData @152 :AudioData; + + # systems stuff + androidLog @20 :AndroidLogEntry; + managerState @78 :ManagerState; + uploaderState @79 :UploaderState; + procLog @33 :ProcLog; + clocks @35 :Clocks; + deviceState @6 :DeviceState; + logMessage @18 :Text; + errorLogMessage @85 :Text; + + # touch frame + touch @135 :List(Touch); + + # navigation + navInstruction @82 :NavInstruction; + navRoute @83 :NavRoute; + navThumbnail @84: Thumbnail; + mapRenderState @105: MapRenderState; + + # UI services + uiDebug @102 :UIDebug; + + # driving feedback + userBookmark @93 :UserBookmark; + bookmarkButton @148 :UserBookmark; + audioFeedback @149 :AudioFeedback; + + # *********** debug *********** + testJoystick @52 :Joystick; + roadEncodeData @86 :EncodeData; + driverEncodeData @87 :EncodeData; + wideRoadEncodeData @88 :EncodeData; + qRoadEncodeData @89 :EncodeData; + alertDebug @133 :DebugAlert; + + livestreamRoadEncodeData @120 :EncodeData; + livestreamWideRoadEncodeData @121 :EncodeData; + livestreamDriverEncodeData @122 :EncodeData; + + # *********** Custom: reserved for forks *********** + + # DO change the name of the field + # DON'T change anything after the "@" + customReservedRawData0 @124 :Data; + customReservedRawData1 @125 :Data; + customReservedRawData2 @126 :Data; + + # 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 + customReservedState107 @107 :Custom.IQState; + customReservedModelManager108 @108 :Custom.IQModelManager; + customReservedPlan109 @109 :Custom.IQPlan; + customReservedEvents110 @110 :Custom.IQOnroadEvent; + customReservedCarParams111 @111 :Custom.IQCarParams; + customReservedCarControl112 @112 :Custom.IQCarControl; + customReservedBackupManager113 @113 :Custom.IQBackupManager; + customReservedCarState114 @114 :Custom.IQCarState; + customReservedLiveData115 @115 :Custom.IQLiveData; + customReservedDriveModel116 @116 :Custom.IQDriveModelData; + backupManagerK3 @151 :Custom.IQBackupManager; + iqNavState @150 :Custom.IQNavState; + iqState @153 :Custom.IQState; + iqModelManager @154 :Custom.IQModelManager; + iqPlan @155 :Custom.IQPlan; + iqOnroadEvents @156 :Custom.IQOnroadEvent; + iqCarParams @157 :Custom.IQCarParams; + iqCarControl @158 :Custom.IQCarControl; + iqCarState @159 :Custom.IQCarState; + iqLiveData @160 :Custom.IQLiveData; + iqDriveModelData @161 :Custom.IQDriveModelData; + iqNavRenderState @163 :Custom.IQNavRenderState; + iqLiveLocation @164 :Custom.IQLiveLocation; + iqPerfTrace @136 :Custom.IQPerfTrace; + iqConstructionZone @137 :Custom.IQConstructionZone; + iqVehicleTracks @138 :Custom.IQVehicleTracks; + iqEnvironment @139 :Custom.IQEnvironment; + customReserved14 @140 :Custom.CustomReserved14; + customReserved15 @141 :Custom.CustomReserved15; + customReserved16 @142 :Custom.CustomReserved16; + mapdExtendedOut @143 :Custom.MapdExtendedOut; + mapdIn @144 :Custom.MapdIn; + mapdOut @145 :Custom.MapdOut; + + # *********** legacy + deprecated *********** + model @9 :Legacy.ModelData; # TODO: rename modelV2 and mark this as deprecated + liveMpcDEPRECATED @36 :LiveMpcData; + liveLongitudinalMpcDEPRECATED @37 :LiveLongitudinalMpcData; + liveLocationKalmanLegacyDEPRECATED @51 :Legacy.LiveLocationData; + orbslamCorrectionDEPRECATED @45 :Legacy.OrbslamCorrection; + liveUIDEPRECATED @14 :Legacy.LiveUI; + sensorEventDEPRECATED @4 :SensorEventData; + liveEventDEPRECATED @8 :List(Legacy.LiveEventData); + liveLocationDEPRECATED @25 :Legacy.LiveLocationData; + ethernetDataDEPRECATED @26 :List(Legacy.EthernetPacket); + cellInfoDEPRECATED @28 :List(Legacy.CellInfo); + wifiScanDEPRECATED @29 :List(Legacy.WifiScan); + uiNavigationEventDEPRECATED @50 :Legacy.UiNavigationEvent; + liveMapDataDEPRECATED @62 :LiveMapDataDEPRECATED; + gpsPlannerPointsDEPRECATED @40 :Legacy.GPSPlannerPoints; + gpsPlannerPlanDEPRECATED @41 :Legacy.GPSPlannerPlan; + applanixRawDEPRECATED @42 :Data; + androidGnssDEPRECATED @30 :Legacy.AndroidGnss; + lidarPtsDEPRECATED @32 :Legacy.LidarPts; + navStatusDEPRECATED @38 :Legacy.NavStatus; + trafficEventsDEPRECATED @43 :List(Legacy.TrafficEvent); + liveLocationTimingDEPRECATED @44 :Legacy.LiveLocationData; + liveLocationCorrectedDEPRECATED @46 :Legacy.LiveLocationData; + navUpdateDEPRECATED @27 :Legacy.NavUpdate; + orbObservationDEPRECATED @47 :List(Legacy.OrbObservation); + locationDEPRECATED @49 :Legacy.LiveLocationData; + orbOdometryDEPRECATED @53 :Legacy.OrbOdometry; + orbFeaturesDEPRECATED @54 :Legacy.OrbFeatures; + applanixLocationDEPRECATED @55 :Legacy.LiveLocationData; + orbKeyFrameDEPRECATED @56 :Legacy.OrbKeyFrame; + orbFeaturesSummaryDEPRECATED @58 :Legacy.OrbFeaturesSummary; + featuresDEPRECATED @10 :Legacy.CalibrationFeatures; + kalmanOdometryDEPRECATED @65 :Legacy.KalmanOdometry; + uiLayoutStateDEPRECATED @57 :Legacy.UiLayoutState; + pandaStateDEPRECATED @12 :PandaState; + driverStateDEPRECATED @59 :DriverStateDEPRECATED; + sensorEventsDEPRECATED @11 :List(SensorEventData); + lateralPlanDEPRECATED @64 :LateralPlan; + navModelDEPRECATED @104 :NavModelData; + uiPlanDEPRECATED @106 :UiPlan; + liveLocationKalman @72 :LiveLocationKalman; + liveTracksDEPRECATED @16 :List(LiveTracksDEPRECATED); + onroadEventsDEPRECATED @68: List(Car.OnroadEventDEPRECATED); + gyroscope2DEPRECATED @100 :SensorEventData; + accelerometer2DEPRECATED @101 :SensorEventData; + temperatureSensor2DEPRECATED @123 :SensorEventData; + } +} diff --git a/cereal/messaging/__init__.py b/cereal/messaging/__init__.py new file mode 100644 index 000000000..2c925b4cc --- /dev/null +++ b/cereal/messaging/__init__.py @@ -0,0 +1,269 @@ +# must be built with scons +from msgq import fake_event_handle, drain_sock_raw, MultiplePublishersError, IpcError, \ + Context, Poller, SubSocket, PubSocket, SocketEventHandle, toggle_fake_events, \ + set_fake_prefix, get_fake_prefix, delete_fake_prefix, wait_for_one_event +import msgq +import os +import capnp +import time + +from typing import Optional, List, Union, Dict + +from cereal import log +from cereal.services import SERVICE_LIST +from openpilot.common.utils import MovingAverage + +NO_TRAVERSAL_LIMIT = 2**64-1 + + +def pub_sock(endpoint: str) -> PubSocket: + service = SERVICE_LIST.get(endpoint) + segment_size = service.queue_size if service else 0 + return msgq.pub_sock(endpoint, segment_size) + + +def sub_sock(endpoint: str, poller: Optional[Poller] = None, addr: str = "127.0.0.1", + conflate: bool = False, timeout: Optional[int] = None) -> SubSocket: + service = SERVICE_LIST.get(endpoint) + segment_size = service.queue_size if service else 0 + return msgq.sub_sock(endpoint, poller=poller, addr=addr, conflate=conflate, + timeout=timeout, segment_size=segment_size) + + +def reset_context(): + msgq.context = Context() + + +def log_from_bytes(dat: bytes, struct: capnp.lib.capnp._StructModule = log.Event) -> capnp.lib.capnp._DynamicStructReader: + with struct.from_bytes(dat, traversal_limit_in_words=NO_TRAVERSAL_LIMIT) as msg: + return msg + + +def new_message(service: Optional[str], size: Optional[int] = None, **kwargs) -> capnp.lib.capnp._DynamicStructBuilder: + args = { + 'valid': False, + 'logMonoTime': int(time.monotonic() * 1e9), + **kwargs + } + dat = log.Event.new_message(**args) + if service is not None: + if size is None: + dat.init(service) + else: + dat.init(service, size) + return dat + + +def drain_sock(sock: SubSocket, wait_for_one: bool = False) -> List[capnp.lib.capnp._DynamicStructReader]: + """Receive all message currently available on the queue""" + msgs = drain_sock_raw(sock, wait_for_one=wait_for_one) + return [log_from_bytes(m) for m in msgs] + + +# TODO: print when we drop packets? +def recv_sock(sock: SubSocket, wait: bool = False) -> Optional[capnp.lib.capnp._DynamicStructReader]: + """Same as drain sock, but only returns latest message. Consider using conflate instead.""" + dat = None + + while 1: + if wait and dat is None: + recv = sock.receive() + else: + recv = sock.receive(non_blocking=True) + + if recv is None: # Timeout hit + break + + dat = recv + + if dat is not None: + dat = log_from_bytes(dat) + + return dat + + +def recv_one(sock: SubSocket) -> Optional[capnp.lib.capnp._DynamicStructReader]: + dat = sock.receive() + if dat is not None: + dat = log_from_bytes(dat) + return dat + + +def recv_one_or_none(sock: SubSocket) -> Optional[capnp.lib.capnp._DynamicStructReader]: + dat = sock.receive(non_blocking=True) + if dat is not None: + dat = log_from_bytes(dat) + return dat + + +def recv_one_retry(sock: SubSocket) -> capnp.lib.capnp._DynamicStructReader: + """Keep receiving until we get a message""" + while True: + dat = sock.receive() + if dat is not None: + return log_from_bytes(dat) + + +class FrequencyTracker: + def __init__(self, service_freq: float, update_freq: float, is_poll: bool): + freq = max(min(service_freq, update_freq), 1.) + if is_poll: + min_freq = max_freq = freq + else: + max_freq = min(freq, update_freq) + if service_freq >= 2 * update_freq: + min_freq = update_freq + elif update_freq >= 2* service_freq: + min_freq = freq + else: + min_freq = min(freq, freq / 2.) + + self.min_freq = min_freq * 0.8 + self.max_freq = max_freq * 1.2 + self.avg_dt = MovingAverage(int(10 * freq)) + self.recent_avg_dt = MovingAverage(int(freq)) + self.prev_time = 0.0 + + def record_recv_time(self, cur_time: float) -> None: + # TODO: Handle case where cur_time is less than prev_time + if self.prev_time > 1e-5: + dt = cur_time - self.prev_time + + self.avg_dt.add_value(dt) + self.recent_avg_dt.add_value(dt) + + self.prev_time = cur_time + + @property + def valid(self) -> bool: + if self.avg_dt.count == 0: + return False + + avg_freq = 1.0 / self.avg_dt.get_average() + if self.min_freq <= avg_freq <= self.max_freq: + return True + + avg_freq_recent = 1.0 / self.recent_avg_dt.get_average() + return self.min_freq <= avg_freq_recent <= self.max_freq + + +class SubMaster: + def __init__(self, services: List[str], poll: Optional[str] = None, + ignore_alive: Optional[List[str]] = None, ignore_avg_freq: Optional[List[str]] = None, + ignore_valid: Optional[List[str]] = None, addr: str = "127.0.0.1", frequency: Optional[float] = None): + self.frame = -1 + self.services = services + self.seen = {s: False for s in services} + self.updated = {s: False for s in services} + self.recv_time = {s: 0. for s in services} + self.recv_frame = {s: 0 for s in services} + self.sock = {} + self.data = {} + self.logMonoTime = {s: 0 for s in services} + + # zero-frequency / on-demand services are always alive and presumed valid; all others must pass checks + on_demand = {s: SERVICE_LIST[s].frequency <= 1e-5 for s in services} + self.static_freq_services = set(s for s in services if not on_demand[s]) + self.alive = {s: on_demand[s] for s in services} + self.freq_ok = {s: on_demand[s] for s in services} + self.valid = {s: on_demand[s] for s in services} + + self.freq_tracker: Dict[str, FrequencyTracker] = {} + self.poller = Poller() + polled_services = set([poll, ] if poll is not None else services) + self.non_polled_services = set(services) - polled_services + + self.ignore_average_freq = [] if ignore_avg_freq is None else ignore_avg_freq + self.ignore_alive = [] if ignore_alive is None else ignore_alive + self.ignore_valid = [] if ignore_valid is None else ignore_valid + + self.simulation = bool(int(os.getenv("SIMULATION", "0"))) + + # if freq and poll aren't specified, assume the max to be conservative + assert frequency is None or poll is None, "Do not specify 'frequency' - frequency of the polled service will be used." + self.update_freq = frequency or max([SERVICE_LIST[s].frequency for s in polled_services]) + + for s in services: + p = self.poller if s not in self.non_polled_services else None + self.sock[s] = sub_sock(s, poller=p, addr=addr, conflate=True) + + try: + data = new_message(s) + except capnp.lib.capnp.KjException: + data = new_message(s, 0) # lists + + self.data[s] = getattr(data.as_reader(), s) + self.freq_tracker[s] = FrequencyTracker(SERVICE_LIST[s].frequency, self.update_freq, s == poll) + + def __getitem__(self, s: str) -> capnp.lib.capnp._DynamicStructReader: + return self.data[s] + + def _check_avg_freq(self, s: str) -> bool: + return SERVICE_LIST[s].frequency > 0.99 and (s not in self.ignore_average_freq) and (s not in self.ignore_alive) + + def update(self, timeout: int = 100) -> None: + msgs = [] + for sock in self.poller.poll(timeout): + msgs.append(recv_one_or_none(sock)) + + # non-blocking receive for non-polled sockets + for s in self.non_polled_services: + msgs.append(recv_one_or_none(self.sock[s])) + self.update_msgs(time.monotonic(), msgs) + + def update_msgs(self, cur_time: float, msgs: List[capnp.lib.capnp._DynamicStructReader]) -> None: + self.frame += 1 + self.updated = dict.fromkeys(self.services, False) + for msg in msgs: + if msg is None: + continue + + s = msg.which() + self.seen[s] = True + self.updated[s] = True + + self.freq_tracker[s].record_recv_time(cur_time) + self.recv_time[s] = cur_time + self.recv_frame[s] = self.frame + self.data[s] = getattr(msg, s) + self.logMonoTime[s] = msg.logMonoTime + self.valid[s] = msg.valid + + for s in self.static_freq_services: + # alive if delay is within 10x the expected frequency; checks relaxed in simulator + self.alive[s] = (cur_time - self.recv_time[s]) < (10. / SERVICE_LIST[s].frequency) or (self.seen[s] and self.simulation) + self.freq_ok[s] = self.freq_tracker[s].valid or self.simulation + + def all_alive(self, service_list: Optional[List[str]] = None) -> bool: + return all(self.alive[s] for s in (service_list or self.services) if s not in self.ignore_alive) + + def all_freq_ok(self, service_list: Optional[List[str]] = None) -> bool: + return all(self.freq_ok[s] for s in (service_list or self.services) if self._check_avg_freq(s)) + + def all_valid(self, service_list: Optional[List[str]] = None) -> bool: + return all(self.valid[s] for s in (service_list or self.services) if s not in self.ignore_valid) + + def all_checks(self, service_list: Optional[List[str]] = None) -> bool: + return self.all_alive(service_list) and self.all_freq_ok(service_list) and self.all_valid(service_list) + + +class PubMaster: + def __init__(self, services: List[str]): + self.sock = {} + for s in services: + self.sock[s] = pub_sock(s) + + def send(self, s: str, dat: Union[bytes, capnp.lib.capnp._DynamicStructBuilder]) -> None: + if not isinstance(dat, bytes): + dat = dat.to_bytes() + self.sock[s].send(dat) + + def wait_for_readers_to_update(self, s: str, timeout: int, dt: float = 0.05) -> bool: + for _ in range(int(timeout*(1./dt))): + if self.sock[s].all_readers_updated(): + return True + time.sleep(dt) + return False + + def all_readers_updated(self, s: str) -> bool: + return self.sock[s].all_readers_updated() # type: ignore diff --git a/cereal/messaging/bridge b/cereal/messaging/bridge new file mode 100755 index 000000000..ea2573d19 Binary files /dev/null and b/cereal/messaging/bridge differ diff --git a/cereal/messaging/tests/__init__.py b/cereal/messaging/tests/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/cereal/messaging/tests/test_messaging.py b/cereal/messaging/tests/test_messaging.py new file mode 100644 index 000000000..583eb8b0d --- /dev/null +++ b/cereal/messaging/tests/test_messaging.py @@ -0,0 +1,185 @@ +import os +import capnp +import multiprocessing +import numbers +import random +import threading +import time +from parameterized import parameterized +import pytest + +from cereal import log, car +import cereal.messaging as messaging +from cereal.services import SERVICE_LIST + +events = [evt for evt in log.Event.schema.union_fields if evt in SERVICE_LIST.keys()] + +def random_sock(): + return random.choice(events) + +def random_socks(num_socks=10): + return list({random_sock() for _ in range(num_socks)}) + +def random_bytes(length=1000): + return bytes([random.randrange(0xFF) for _ in range(length)]) + +def zmq_sleep(t=1): + if "ZMQ" in os.environ: + time.sleep(t) + + +# TODO: this should take any capnp struct and returrn a msg with random populated data +def random_carstate(): + fields = ["vEgo", "aEgo", "brake", "steeringAngleDeg"] + msg = messaging.new_message("carState") + cs = msg.carState + for f in fields: + setattr(cs, f, random.random() * 10) + return msg + +# TODO: this should compare any capnp structs +def assert_carstate(cs1, cs2): + for f in car.CarState.schema.non_union_fields: + # TODO: check all types + val1, val2 = getattr(cs1, f), getattr(cs2, f) + if isinstance(val1, numbers.Number): + assert val1 == val2, f"{f}: sent '{val1}' vs recvd '{val2}'" + +def delayed_send(delay, sock, dat): + def send_func(): + sock.send(dat) + threading.Timer(delay, send_func).start() + + +class TestMessaging: + def setUp(self): + # TODO: ZMQ tests are too slow; all sleeps will need to be + # replaced with logic to block on the necessary condition + if "ZMQ" in os.environ: + pytest.skip() + + # ZMQ pub socket takes too long to die + # sleep to prevent multiple publishers error between tests + zmq_sleep() + + @parameterized.expand(events) + def test_new_message(self, evt): + try: + msg = messaging.new_message(evt) + except capnp.lib.capnp.KjException: + msg = messaging.new_message(evt, random.randrange(200)) + assert (time.monotonic() - msg.logMonoTime) < 0.1 + assert not msg.valid + assert evt == msg.which() + + @parameterized.expand(events) + def test_pub_sock(self, evt): + messaging.pub_sock(evt) + + @parameterized.expand(events) + def test_sub_sock(self, evt): + messaging.sub_sock(evt) + + @parameterized.expand([ + (messaging.drain_sock, capnp._DynamicStructReader), + (messaging.drain_sock_raw, bytes), + ]) + def test_drain_sock(self, func, expected_type): + sock = "carState" + pub_sock = messaging.pub_sock(sock) + sub_sock = messaging.sub_sock(sock, timeout=1000) + zmq_sleep() + + # no wait and no msgs in queue + msgs = func(sub_sock) + assert isinstance(msgs, list) + assert len(msgs) == 0 + + # no wait but msgs are queued up + num_msgs = random.randrange(3, 10) + for _ in range(num_msgs): + pub_sock.send(messaging.new_message(sock).to_bytes()) + time.sleep(0.1) + msgs = func(sub_sock) + assert isinstance(msgs, list) + assert all(isinstance(msg, expected_type) for msg in msgs) + assert len(msgs) == num_msgs + + def test_recv_sock(self): + sock = "carState" + pub_sock = messaging.pub_sock(sock) + sub_sock = messaging.sub_sock(sock, timeout=100) + zmq_sleep() + + # no wait and no msg in queue, socket should timeout + recvd = messaging.recv_sock(sub_sock) + assert recvd is None + + # no wait and one msg in queue + msg = random_carstate() + pub_sock.send(msg.to_bytes()) + time.sleep(0.01) + recvd = messaging.recv_sock(sub_sock) + assert isinstance(recvd, capnp._DynamicStructReader) + # https://github.com/python/mypy/issues/13038 + assert_carstate(msg.carState, recvd.carState) + + def test_recv_one(self): + sock = "carState" + pub_sock = messaging.pub_sock(sock) + sub_sock = messaging.sub_sock(sock, timeout=1000) + zmq_sleep() + + # no msg in queue, socket should timeout + recvd = messaging.recv_one(sub_sock) + assert recvd is None + + # one msg in queue + msg = random_carstate() + pub_sock.send(msg.to_bytes()) + recvd = messaging.recv_one(sub_sock) + assert isinstance(recvd, capnp._DynamicStructReader) + assert_carstate(msg.carState, recvd.carState) + + @pytest.mark.xfail(condition="ZMQ" in os.environ, reason='ZMQ detected') + def test_recv_one_or_none(self): + sock = "carState" + pub_sock = messaging.pub_sock(sock) + sub_sock = messaging.sub_sock(sock) + zmq_sleep() + + # no msg in queue, socket shouldn't block + recvd = messaging.recv_one_or_none(sub_sock) + assert recvd is None + + # one msg in queue + msg = random_carstate() + pub_sock.send(msg.to_bytes()) + recvd = messaging.recv_one_or_none(sub_sock) + assert isinstance(recvd, capnp._DynamicStructReader) + assert_carstate(msg.carState, recvd.carState) + + def test_recv_one_retry(self): + sock = "carState" + sock_timeout = 0.1 + pub_sock = messaging.pub_sock(sock) + sub_sock = messaging.sub_sock(sock, timeout=round(sock_timeout*1000)) + zmq_sleep() + + # this test doesn't work with ZMQ since multiprocessing interrupts it + if "ZMQ" not in os.environ: + # wait 5 socket timeouts and make sure it's still retrying + p = multiprocessing.Process(target=messaging.recv_one_retry, args=(sub_sock,)) + p.start() + time.sleep(sock_timeout*5) + assert p.is_alive() + p.terminate() + + # wait 5 socket timeouts before sending + msg = random_carstate() + start_time = time.monotonic() + delayed_send(sock_timeout*5, pub_sock, msg.to_bytes()) + recvd = messaging.recv_one_retry(sub_sock) + assert (time.monotonic() - start_time) >= sock_timeout*5 + assert isinstance(recvd, capnp._DynamicStructReader) + assert_carstate(msg.carState, recvd.carState) diff --git a/cereal/messaging/tests/test_pub_sub_master.py b/cereal/messaging/tests/test_pub_sub_master.py new file mode 100644 index 000000000..5e26b4970 --- /dev/null +++ b/cereal/messaging/tests/test_pub_sub_master.py @@ -0,0 +1,160 @@ +import random +import time +from typing import Sized, cast + +import cereal.messaging as messaging +from cereal.messaging.tests.test_messaging import events, random_sock, random_socks, \ + random_bytes, random_carstate, assert_carstate, \ + zmq_sleep +from cereal.services import SERVICE_LIST + + +class TestSubMaster: + + def setup_method(self): + # ZMQ pub socket takes too long to die + # sleep to prevent multiple publishers error between tests + zmq_sleep(3) + + def test_init(self): + sm = messaging.SubMaster(events) + for p in [sm.updated, sm.recv_time, sm.recv_frame, sm.alive, + sm.sock, sm.data, sm.logMonoTime, sm.valid]: + assert len(cast(Sized, p)) == len(events) + + def test_init_state(self): + socks = random_socks() + sm = messaging.SubMaster(socks) + assert sm.frame == -1 + assert not any(sm.updated.values()) + assert not any(sm.seen.values()) + on_demand = {s: SERVICE_LIST[s].frequency <= 1e-5 for s in sm.services} + assert all(sm.alive[s] == sm.valid[s] == sm.freq_ok[s] == on_demand[s] for s in sm.services) + assert all(t == 0. for t in sm.recv_time.values()) + assert all(f == 0 for f in sm.recv_frame.values()) + assert all(t == 0 for t in sm.logMonoTime.values()) + + for p in [sm.updated, sm.recv_time, sm.recv_frame, sm.alive, + sm.sock, sm.data, sm.logMonoTime, sm.valid]: + assert len(cast(Sized, p)) == len(socks) + + def test_getitem(self): + sock = "carState" + pub_sock = messaging.pub_sock(sock) + sm = messaging.SubMaster([sock,]) + zmq_sleep() + + msg = random_carstate() + pub_sock.send(msg.to_bytes()) + sm.update(1000) + assert_carstate(msg.carState, sm[sock]) + + # TODO: break this test up to individually test SubMaster.update and SubMaster.update_msgs + def test_update(self): + sock = "carState" + pub_sock = messaging.pub_sock(sock) + sm = messaging.SubMaster([sock,]) + zmq_sleep() + + for i in range(10): + msg = messaging.new_message(sock) + pub_sock.send(msg.to_bytes()) + sm.update(1000) + assert sm.frame == i + assert all(sm.updated.values()) + + def test_update_timeout(self): + sock = random_sock() + sm = messaging.SubMaster([sock,]) + timeout = random.randrange(1000, 3000) + start_time = time.monotonic() + sm.update(timeout) + t = time.monotonic() - start_time + assert t >= timeout/1000. + assert t < 3 + assert not any(sm.updated.values()) + + def test_avg_frequency_checks(self): + for poll in (True, False): + sm = messaging.SubMaster(["modelV2", "carParams", "carState", "cameraOdometry", "liveCalibration"], + poll=("modelV2" if poll else None), + frequency=(20. if not poll else None)) + + checks = { + "carState": (20, 20), + "modelV2": (20, 20 if poll else 10), + "cameraOdometry": (20, 10), + "liveCalibration": (4, 4), + "carParams": (None, None), + "userBookmark": (None, None), + } + + for service, (max_freq, min_freq) in checks.items(): + if max_freq is not None: + assert sm._check_avg_freq(service) + assert sm.freq_tracker[service].max_freq == max_freq*1.2 + assert sm.freq_tracker[service].min_freq == min_freq*0.8 + else: + assert not sm._check_avg_freq(service) + + def test_alive(self): + pass + + def test_ignore_alive(self): + pass + + def test_valid(self): + pass + + # SubMaster should always conflate + def test_conflate(self): + sock = "carState" + pub_sock = messaging.pub_sock(sock) + sm = messaging.SubMaster([sock,]) + + n = 10 + for i in range(n+1): + msg = messaging.new_message(sock) + msg.carState.vEgo = i + pub_sock.send(msg.to_bytes()) + time.sleep(0.01) + sm.update(1000) + assert sm[sock].vEgo == n + + +class TestPubMaster: + + def setup_method(self): + # ZMQ pub socket takes too long to die + # sleep to prevent multiple publishers error between tests + zmq_sleep(3) + + def test_init(self): + messaging.PubMaster(events) + + def test_send(self): + socks = random_socks() + pm = messaging.PubMaster(socks) + sub_socks = {s: messaging.sub_sock(s, conflate=True, timeout=1000) for s in socks} + zmq_sleep() + + # PubMaster accepts either a capnp msg builder or bytes + for capnp in [True, False]: + for i in range(100): + sock = socks[i % len(socks)] + + if capnp: + try: + msg = messaging.new_message(sock) + except Exception: + msg = messaging.new_message(sock, random.randrange(50)) + else: + msg = random_bytes() + + pm.send(sock, msg) + recvd = sub_socks[sock].receive() + + if capnp: + msg.clear_write_flag() + msg = msg.to_bytes() + assert msg == recvd, i diff --git a/cereal/messaging/tests/test_services.py b/cereal/messaging/tests/test_services.py new file mode 100644 index 000000000..d35826755 --- /dev/null +++ b/cereal/messaging/tests/test_services.py @@ -0,0 +1,27 @@ +import os +import tempfile +from typing import Dict +from parameterized import parameterized + +from cereal import log +import cereal.services as services +from cereal.services import SERVICE_LIST + + +class TestServices: + + @parameterized.expand(SERVICE_LIST.keys()) + def test_services(self, s): + service = SERVICE_LIST[s] + assert service.frequency <= 104 + assert service.decimation != 0 + + def test_generated_header(self): + with tempfile.NamedTemporaryFile(suffix=".h") as f: + ret = os.system(f"python3 {services.__file__} > {f.name} && clang++ {f.name} -std=c++11") + assert ret == 0, "generated services header is not valid C" + + def test_all_services_exist_in_log_union(self): + event_fields = set(log.Event.schema.union_fields) + missing = sorted(s for s in SERVICE_LIST if s not in event_fields) + assert not missing, f"services missing from log.capnp Event union: {missing}" diff --git a/cereal/messaging/tests/validate_sp_cereal_upstream.py b/cereal/messaging/tests/validate_sp_cereal_upstream.py new file mode 100755 index 000000000..9ccd6533c --- /dev/null +++ b/cereal/messaging/tests/validate_sp_cereal_upstream.py @@ -0,0 +1,222 @@ +#!/usr/bin/env python3 +import argparse +import sys +from typing import Any, List, Tuple + +DEBUG = False + + +def print_debug(string: str) -> None: + if DEBUG: + print(string) + + +def create_schema_instance(struct: Any, prop: Tuple[str, Any]) -> Any: + """ + Create a new instance of a schema type, handling different field types. + + Args: + struct: The Cap'n Proto schema structure + prop: A tuple containing the field name and field metadata + + Returns: + A new initialized schema instance + """ + struct_instance = struct.new_message() + field_name, field_metadata = prop + + try: + field_type = field_metadata.proto.slot.type.which() + + # Initialize different types of fields + if field_type in ('list', 'text', 'data'): + struct_instance.init(field_name, 1) + print_debug(f"Initialized list/text/data field: {field_name}") + elif field_type in ('struct', 'object'): + struct_instance.init(field_name) + print_debug(f"Initialized struct/object field: {field_name}") + + return struct_instance + + except Exception as e: + print(f"Error creating instance for {field_name}: {e}") + return None + + +def get_schema_fields(schema_struct: Any) -> List[Tuple[str, Any]]: + """ + Retrieve all fields from a given schema structure. + + Args: + schema_struct: The Cap'n Proto schema structure + + Returns: + A list of field names and their metadata + """ + try: + # Get all fields from the schema + schema_fields = list(schema_struct.schema.fields.items()) + + print_debug("Discovered schema fields:") + for field_name, field_metadata in schema_fields: + print_debug(f"- {field_name}") + + return schema_fields + + except Exception as e: + print(f"Error retrieving schema fields: {e}") + return [] + + +def generate_schema_instances(schema_struct: Any) -> List[Any]: + """ + Generate instances for all fields in a given schema. + + Args: + schema_struct: The Cap'n Proto schema structure + + Returns: + A list of schema instances + """ + schema_fields = get_schema_fields(schema_struct) + instances = [] + + for field_prop in schema_fields: + try: + instance = create_schema_instance(schema_struct, field_prop) + if instance is not None: + instances.append(instance) + except Exception as e: + print(f"Skipping field due to error: {e}") + + print(f"Generated {len(instances)} schema instances") + return instances + + +def persist_instances(instances: List[Any], filename: str) -> None: + """ + Write schema instances to a binary file. + + Args: + instances: List of schema instances + filename: Output file path + """ + try: + with open(filename, 'wb') as f: + for instance in instances: + f.write(instance.to_bytes()) + + print(f"Successfully wrote {len(instances)} instances to {filename}") + + except Exception as e: + print(f"Error persisting instances: {e}") + sys.exit(1) + + +def read_instances(filename: str, schema_type: Any) -> List[Any]: + """ + Read schema instances from a binary file. + + Args: + filename: Input file path + schema_type: The schema type to use for reading + + Returns: + A list of read schema instances + """ + try: + with open(filename, 'rb') as f: + data = f.read() + + instances = list(schema_type.read_multiple_bytes(data)) + + print(f"Read {len(instances)} instances from {filename}") + return instances + + except Exception as e: + print(f"Error reading instances: {e}") + sys.exit(1) + + +def compare_schemas(original_instances: List[Any], read_instances: List[Any]) -> bool: + """ + Compare original and read-back instances to detect potential breaking changes. + + Args: + original_instances: List of originally generated instances + read_instances: List of instances read back from file + + Returns: + Boolean indicating whether schemas appear compatible + """ + if len(original_instances) != len(read_instances): + print("❌ Schema Compatibility Warning: Instance count mismatch") + return False + + compatible = True + for struct in read_instances: + try: + getattr(struct, struct.which()) # Attempting to access the field to validate readability + except Exception as e: + print(f"❌ Structural change detected: {struct.which()} is not readable.\nFull error: {e}") + compatible = False + + return compatible + + +def main(): + """ + CLI entry point for schema compatibility testing. + """ + # Setup argument parser + parser = argparse.ArgumentParser( + description='Cap\'n Proto Schema Compatibility Testing Tool', + epilog='Test schema compatibility by generating and reading back instances.' + ) + + # Add mutually exclusive group for generation or reading mode + mode_group = parser.add_mutually_exclusive_group(required=True) + mode_group.add_argument('-g', '--generate', action='store_true', + help='Generate schema instances') + mode_group.add_argument('-r', '--read', action='store_true', + help='Read and validate schema instances') + + # Common arguments + parser.add_argument('-f', '--file', + default='schema_instances.bin', + help='Output/input binary file (default: schema_instances.bin)') + + # Parse arguments + args = parser.parse_args() + + # Import the schema dynamically + try: + from cereal import log + schema_type = log.Event + except ImportError: + print("Error: Unable to import schema. Ensure 'cereal' is installed.") + sys.exit(1) + + # Execute based on mode + if args.generate: + print("🔧 Generating Schema Instances") + instances = generate_schema_instances(schema_type) + persist_instances(instances, args.file) + print("✅ Instance generation complete") + + elif args.read: + print("🔍 Reading and Validating Schema Instances") + generated_instances = generate_schema_instances(schema_type) + read_back_instances = read_instances(args.file, schema_type) + + # Compare schemas + if compare_schemas(generated_instances, read_back_instances): + print("✅ Schema Compatibility: No breaking changes detected") + sys.exit(0) + else: + print("❌ Potential Schema Breaking Changes Detected") + sys.exit(1) + + +if __name__ == "__main__": + main() diff --git a/cereal/services.py b/cereal/services.py new file mode 100755 index 000000000..50f8e8063 --- /dev/null +++ b/cereal/services.py @@ -0,0 +1,158 @@ +#!/usr/bin/env python3 +from enum import IntEnum +from typing import Optional + + +# TODO: this should be automatically determined using the capnp schema +class QueueSize(IntEnum): + BIG = 10 * 1024 * 1024 # 10MB - video frames, large AI outputs + MEDIUM = 2 * 1024 * 1024 # 2MB - high freq (CAN), livestream + SMALL = 250 * 1024 # 250KB - most services + + +class Service: + def __init__(self, should_log: bool, frequency: float, decimation: Optional[int] = None, + queue_size: QueueSize = QueueSize.SMALL): + self.should_log = should_log + self.frequency = frequency + self.decimation = decimation + self.queue_size = queue_size + + +_services: dict[str, tuple] = { + # service: (should_log, frequency, qlog decimation (optional)) + # note: the "EncodeIdx" packets will still be in the log + "gyroscope": (True, 104., 104), + "accelerometer": (True, 104., 104), + "magnetometer": (True, 25.), + "lightSensor": (True, 100., 100), + "temperatureSensor": (True, 2., 200), + "gpsNMEA": (True, 9.), + "deviceState": (True, 2., 1), + "touch": (True, 20., 1), + "can": (True, 100., 2053, QueueSize.BIG), # decimation gives ~3 msgs in a full segment + "controlsState": (True, 100., 10, QueueSize.MEDIUM), + "selfdriveState": (True, 100., 10), + "pandaStates": (True, 10., 1), + "peripheralState": (True, 2., 1), + "radarState": (True, 20., 5), + "roadEncodeIdx": (False, 20., 1), + "liveTracks": (True, 20.), + "sendcan": (True, 100., 139, QueueSize.MEDIUM), + "logMessage": (True, 0., None, QueueSize.MEDIUM), + "errorLogMessage": (True, 0., 1, QueueSize.MEDIUM), + "liveCalibration": (True, 4., 4), + "liveTorqueParameters": (True, 4., 1), + "liveDelay": (True, 4., 1), + "androidLog": (True, 0.), + "carState": (True, 100., 10), + "carControl": (True, 100., 10), + "carOutput": (True, 100., 10), + "longitudinalPlan": (True, 20., 10), + "lateralManeuverPlan": (True, 20.), + "driverAssistance": (True, 20., 20), + "procLog": (True, 0.5, 15, QueueSize.MEDIUM), + "gpsLocationExternal": (True, 10., 10), + "gpsLocation": (True, 1., 1), + "ubloxGnss": (True, 10.), + "qcomGnss": (True, 2.), + "gnssMeasurements": (True, 10., 10), + "clocks": (True, 0.1, 1), + "ubloxRaw": (True, 20.), + "livePose": (True, 20., 4), + "liveParameters": (True, 20., 5), + "cameraOdometry": (True, 20., 10), + "thumbnail": (True, 1 / 60., 1), + "onroadEvents": (True, 1., 1), + "carParams": (True, 0.02, 1), + "roadCameraState": (True, 20., 20), + "driverCameraState": (True, 20., 20), + "driverEncodeIdx": (False, 20., 1), + "driverStateV2": (True, 20., 10), + "driverMonitoringState": (True, 20., 10), + "wideRoadEncodeIdx": (False, 20., 1), + "wideRoadCameraState": (True, 20., 20), + "drivingModelData": (True, 20., 10), + "modelV2": (True, 20., None, QueueSize.MEDIUM), + "managerState": (True, 2., 1), + "uploaderState": (True, 0., 1), + "navInstruction": (True, 1., 10), + "navRoute": (True, 0.), + "navThumbnail": (True, 0.), + "qRoadEncodeIdx": (False, 20.), + "userBookmark": (True, 0., 1), + "soundPressure": (True, 10., 10), + "rawAudioData": (False, 20.), + "webrtcAudioData": (False, 50.), + "bookmarkButton": (True, 0., 1), + "audioFeedback": (True, 0., 1), + "roadEncodeData": (False, 20., None, QueueSize.BIG), + "driverEncodeData": (False, 20., None, QueueSize.BIG), + "wideRoadEncodeData": (False, 20., None, QueueSize.BIG), + "qRoadEncodeData": (False, 20., None, QueueSize.BIG), + + # iqpilot + "iqModelManager": (False, 1., 1, QueueSize.MEDIUM), + "backupManagerK3": (False, 1., 1), + "iqCarParams": (True, 0.02, 1), + "iqCarControl": (True, 100., 10), + "iqCarState": (True, 100., 10), + "iqLiveData": (True, 1., 1), + "iqConstructionZone": (True, 2., 2), + "iqVehicleTracks": (True, 4., 4), + "iqEnvironment": (True, 4., 4), + "mapdOut": (True, 20., 20, QueueSize.MEDIUM), + "mapdExtendedOut": (False, 1., -1, QueueSize.MEDIUM), + "mapdIn": (False, 1., -1, QueueSize.MEDIUM), + "iqNavState": (True, 5., 10), + "iqNavRenderState": (True, 5., 10, QueueSize.MEDIUM), + "iqState": (True, 100., 10), + "iqPlan": (True, 20., 10), + "iqOnroadEvents": (True, 1., 1), + "iqDriveModelData": (True, 20., None, QueueSize.MEDIUM), + "iqLiveLocation": (True, 20.), + "liveLocationKalman": (True, 20.), + "iqPerfTrace": (True, 0., 1, QueueSize.SMALL), + + # debug + "uiDebug": (True, 0., 1), + "testJoystick": (True, 0.), + "alertDebug": (True, 20., 5), + "livestreamWideRoadEncodeIdx": (False, 20.), + "livestreamRoadEncodeIdx": (False, 20.), + "livestreamDriverEncodeIdx": (False, 20.), + "livestreamWideRoadEncodeData": (False, 20., None, QueueSize.MEDIUM), + "livestreamRoadEncodeData": (False, 20., None, QueueSize.MEDIUM), + "livestreamDriverEncodeData": (False, 20., None, QueueSize.MEDIUM), + "customReservedRawData0": (True, 0.), + "customReservedRawData1": (True, 0.), + "customReservedRawData2": (True, 0.), +} +SERVICE_LIST = {name: Service(*vals) for + idx, (name, vals) in enumerate(_services.items())} + + +def build_header(): + h = "" + h += "/* THIS IS AN AUTOGENERATED FILE, PLEASE EDIT services.py */\n" + h += "#ifndef __SERVICES_H\n" + h += "#define __SERVICES_H\n" + + h += "#include \n" + h += "#include \n" + + h += "struct service { std::string name; bool should_log; float frequency; int decimation; size_t queue_size; };\n" + h += "static std::map services = {\n" + for k, v in SERVICE_LIST.items(): + should_log = "true" if v.should_log else "false" + decimation = -1 if v.decimation is None else v.decimation + h += ' { "%s", {"%s", %s, %f, %d, %d}},\n' % \ + (k, k, should_log, v.frequency, decimation, v.queue_size) + h += "};\n" + + h += "#endif\n" + return h + + +if __name__ == "__main__": + print(build_header()) diff --git a/common/.gitignore b/common/.gitignore new file mode 100644 index 000000000..ce1da4c53 --- /dev/null +++ b/common/.gitignore @@ -0,0 +1 @@ +*.cpp diff --git a/common/__init__.py b/common/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/common/api/__init__.py b/common/api/__init__.py new file mode 100644 index 000000000..865b2dba9 --- /dev/null +++ b/common/api/__init__.py @@ -0,0 +1,26 @@ +import openpilot.common.api.comma_connect + + +class Api: + def __init__(self, dongle_id): + self.service = openpilot.common.api.comma_connect.CommaConnectApi(dongle_id) + + def request(self, method, endpoint, **params): + return self.service.request(method, endpoint, **params) + + def get(self, *args, **kwargs): + return self.service.get(*args, **kwargs) + + def post(self, *args, **kwargs): + return self.service.post(*args, **kwargs) + + def get_token(self, payload_extra=None, expiry_hours=1): + return self.service.get_token(payload_extra, expiry_hours) + + +def api_get(endpoint, method='GET', timeout=None, access_token=None, session=None, **params): + return openpilot.common.api.comma_connect.CommaConnectApi(None).api_get(endpoint, method, timeout, access_token, session, **params) + + +def get_key_pair() -> tuple[str, str, str] | tuple[None, None, None]: + return openpilot.common.api.comma_connect.CommaConnectApi(None).get_key_pair() diff --git a/common/api/base.py b/common/api/base.py new file mode 100644 index 000000000..7e5332901 --- /dev/null +++ b/common/api/base.py @@ -0,0 +1,84 @@ +import jwt +import os +import requests +import unicodedata +from datetime import datetime, timedelta, UTC +from functools import lru_cache +from openpilot.system.hardware.hw import Paths +from openpilot.system.version import get_version + +# name: jwt signature algorithm +KEYS = {"id_rsa": "RS256", + "id_ecdsa": "ES256"} + + +@lru_cache(maxsize=4) +def load_signing_key(private_key: str): + # PyJWT re-parses a PEM string on every encode; an RSA parse is ~40ms, so cache the key object + try: + from cryptography.hazmat.primitives.serialization import load_pem_private_key + return load_pem_private_key(private_key.encode(), password=None) + except Exception: + return private_key + + +class BaseApi: + def __init__(self, dongle_id, api_host, user_agent="openpilot-"): + self.dongle_id = dongle_id + self.api_host = api_host + self.user_agent = user_agent + self.jwt_algorithm, self.private_key, _ = self.get_key_pair() + + def get(self, *args, **kwargs): + return self.request('GET', *args, **kwargs) + + def post(self, *args, **kwargs): + return self.request('POST', *args, **kwargs) + + def request(self, method, endpoint, timeout=None, access_token=None, **params): + return self.api_get(endpoint, method=method, timeout=timeout, access_token=access_token, **params) + + def _get_token(self, payload_extra=None, expiry_hours=1, **extra_payload): + now = datetime.now(UTC).replace(tzinfo=None) + payload = { + 'identity': self.dongle_id, + 'nbf': now, + 'iat': now, + 'exp': now + timedelta(hours=expiry_hours), + **extra_payload + } + if payload_extra is not None: + payload.update(payload_extra) + key = load_signing_key(self.private_key) if self.private_key else self.private_key + token = jwt.encode(payload, key, algorithm=self.jwt_algorithm) + if isinstance(token, bytes): + token = token.decode('utf8') + return token + + def get_token(self, payload_extra=None, expiry_hours=1): + return self._get_token(payload_extra, expiry_hours) + + def remove_non_ascii_chars(self, text): + normalized_text = unicodedata.normalize('NFD', text) + ascii_encoded_text = normalized_text.encode('ascii', 'ignore') + return ascii_encoded_text.decode() + + def api_get(self, endpoint, method='GET', timeout=None, access_token=None, session=None, json=None, **params): + headers = {} + if access_token is not None: + headers['Authorization'] = "JWT " + access_token + + version = self.remove_non_ascii_chars(get_version()) + headers['User-Agent'] = self.user_agent + version + + # TODO: add session to Api + req = requests if session is None else session + return req.request(method, f"{self.api_host}/{endpoint}", timeout=timeout, headers=headers, json=json, params=params) + + @staticmethod + def get_key_pair() -> tuple[str, str, str] | tuple[None, None, None]: + for key in KEYS: + if os.path.isfile(Paths.persist_root() + f'/comma/{key}') and os.path.isfile(Paths.persist_root() + f'/comma/{key}.pub'): + with open(Paths.persist_root() + f'/comma/{key}') as private, open(Paths.persist_root() + f'/comma/{key}.pub') as public: + return KEYS[key], private.read(), public.read() + return None, None, None diff --git a/common/api/comma_connect.py b/common/api/comma_connect.py new file mode 100644 index 000000000..04935ad52 --- /dev/null +++ b/common/api/comma_connect.py @@ -0,0 +1,11 @@ +import os + +from openpilot.common.api.base import BaseApi + +API_HOST = os.getenv('API_HOST', 'https://api-iqlabs.konn3kt.com') + + +class CommaConnectApi(BaseApi): + def __init__(self, dongle_id): + super().__init__(dongle_id, API_HOST) + self.user_agent = "openpilot-" diff --git a/common/basedir.py b/common/basedir.py new file mode 100644 index 000000000..6b4811e53 --- /dev/null +++ b/common/basedir.py @@ -0,0 +1,4 @@ +import os + + +BASEDIR = os.path.abspath(os.path.join(os.path.dirname(os.path.realpath(__file__)), "../")) diff --git a/common/constants.py b/common/constants.py new file mode 100644 index 000000000..7ca425c4b --- /dev/null +++ b/common/constants.py @@ -0,0 +1,23 @@ +import numpy as np + +# conversions +class CV: + # Speed + MPH_TO_KPH = 1.609344 + KPH_TO_MPH = 1. / MPH_TO_KPH + MS_TO_KPH = 3.6 + KPH_TO_MS = 1. / MS_TO_KPH + MS_TO_MPH = MS_TO_KPH * KPH_TO_MPH + MPH_TO_MS = MPH_TO_KPH * KPH_TO_MS + MS_TO_KNOTS = 1.9438 + KNOTS_TO_MS = 1. / MS_TO_KNOTS + + # Angle + DEG_TO_RAD = np.pi / 180. + RAD_TO_DEG = 1. / DEG_TO_RAD + + # Mass + LB_TO_KG = 0.453592 + + +ACCELERATION_DUE_TO_GRAVITY = 9.81 # m/s^2 diff --git a/common/file_chunker.py b/common/file_chunker.py new file mode 100644 index 000000000..57dfc3553 --- /dev/null +++ b/common/file_chunker.py @@ -0,0 +1,55 @@ +#!/usr/bin/env python3 +import sys +import math +import os +from pathlib import Path + +CHUNK_SIZE = 45 * 1024 * 1024 # 45MB, under GitHub's 50MB limit + +def get_chunk_name(name, idx, num_chunks): + return f"{name}.chunk{idx+1:02d}of{num_chunks:02d}" + +def get_manifest_path(name): + return f"{name}.chunkmanifest" + +def _chunk_paths(path, num_chunks): + return [get_manifest_path(path)] + [get_chunk_name(path, i, num_chunks) for i in range(num_chunks)] + +def get_chunk_targets(path, file_size): + num_chunks = math.ceil(file_size / CHUNK_SIZE) + return _chunk_paths(path, num_chunks) + +def chunk_file(path, targets): + manifest_path, *chunk_paths = targets + with open(path, 'rb') as f: + data = f.read() + actual_num_chunks = max(1, math.ceil(len(data) / CHUNK_SIZE)) + assert len(chunk_paths) >= actual_num_chunks, f"Allowed {len(chunk_paths)} chunks but needs at least {actual_num_chunks}, for path {path}" + for i, chunk_path in enumerate(chunk_paths): + with open(chunk_path, 'wb') as f: + f.write(data[i * CHUNK_SIZE:(i + 1) * CHUNK_SIZE]) + Path(manifest_path).write_text(str(len(chunk_paths))) + os.remove(path) + +def get_existing_chunks(path): + if os.path.isfile(path): + return [path] + if os.path.isfile(manifest := get_manifest_path(path)): + num_chunks = int(Path(manifest).read_text().strip()) + return _chunk_paths(path, num_chunks) + raise FileNotFoundError(path) + +def read_file_chunked(path): + manifest_path = get_manifest_path(path) + if os.path.isfile(manifest_path): + num_chunks = int(Path(manifest_path).read_text().strip()) + return b''.join(Path(get_chunk_name(path, i, num_chunks)).read_bytes() for i in range(num_chunks)) + if os.path.isfile(path): + return Path(path).read_bytes() + raise FileNotFoundError(path) + + +if __name__ == "__main__": + path = sys.argv[1] + chunk_paths = get_chunk_targets(path, os.path.getsize(path)) + chunk_file(path, chunk_paths) diff --git a/common/file_helpers.py b/common/file_helpers.py new file mode 100644 index 000000000..b7857fbc5 --- /dev/null +++ b/common/file_helpers.py @@ -0,0 +1 @@ +from openpilot.common.utils import CallbackReader, get_upload_stream diff --git a/common/filter_simple.py b/common/filter_simple.py new file mode 100644 index 000000000..30839f66e --- /dev/null +++ b/common/filter_simple.py @@ -0,0 +1,71 @@ +from collections import deque + +import numpy as np + + +class FirstOrderFilter: + def __init__(self, x0, rc, dt, initialized=True): + self.x = x0 + self.dt = dt + self.update_alpha(rc) + self.initialized = initialized + + def update_alpha(self, rc): + self.alpha = self.dt / (rc + self.dt) + + def update(self, x): + if self.initialized: + self.x = (1. - self.alpha) * self.x + self.alpha * x + else: + self.initialized = True + self.x = x + return self.x + + +class BounceFilter(FirstOrderFilter): + def __init__(self, x0, rc, dt, initialized=True, bounce=2): + self.velocity = FirstOrderFilter(0.0, 0.15, dt) + self.bounce = bounce + super().__init__(x0, rc, dt, initialized) + + def update(self, x): + super().update(x) + scale = self.dt / (1.0 / 60.0) # tuned at 60 fps + self.velocity.x += (x - self.x) * self.bounce * scale * self.dt + self.velocity.update(0.0) + if abs(self.velocity.x) < 1e-5: + self.velocity.x = 0.0 + self.x += self.velocity.x + return self.x + + +class MyMovingAverage: + def __init__(self, window_size, value=None): + self.window_size = window_size + if value is not None: + self.values = deque([value] * window_size, maxlen=window_size) + self.sum = value * window_size + self.result = value + else: + self.values = deque(maxlen=window_size) + self.sum = 0 + self.result = 0 + + def set(self, value): + self.values.clear() + self.values.append(value) + self.sum = value + self.result = value + return value + + def set_all(self, value): + self.values = deque([value] * self.window_size, maxlen=self.window_size) + self.sum = value * self.window_size + self.result = value + return value + + def process(self, value, median=False): + self.values.append(value) + self.sum = sum(self.values) + self.result = float(np.median(self.values)) if median else float(self.sum) / len(self.values) + return self.result diff --git a/common/git.py b/common/git.py new file mode 100644 index 000000000..6b662e571 --- /dev/null +++ b/common/git.py @@ -0,0 +1,42 @@ +from functools import cache +import subprocess +from openpilot.common.utils import run_cmd, run_cmd_default + + +@cache +def get_commit(cwd: str | None = None, branch: str = "HEAD") -> str: + return run_cmd_default(["git", "rev-parse", branch], cwd=cwd) + + +@cache +def get_commit_date(cwd: str | None = None, commit: str = "HEAD") -> str: + return run_cmd_default(["git", "show", "--no-patch", "--format='%ct %ci'", commit], cwd=cwd) + + +@cache +def get_short_branch(cwd: str | None = None) -> str: + return run_cmd_default(["git", "rev-parse", "--abbrev-ref", "HEAD"], cwd=cwd) + + +@cache +def get_branch(cwd: str | None = None) -> str: + return run_cmd_default(["git", "rev-parse", "--abbrev-ref", "--symbolic-full-name", "@{u}"], cwd=cwd) + + +@cache +def get_origin(cwd: str | None = None) -> str: + try: + local_branch = run_cmd(["git", "name-rev", "--name-only", "HEAD"], cwd=cwd) + tracking_remote = run_cmd(["git", "config", "branch." + local_branch + ".remote"], cwd=cwd) + return run_cmd(["git", "config", "remote." + tracking_remote + ".url"], cwd=cwd) + except subprocess.CalledProcessError: # Not on a branch, fallback + return run_cmd_default(["git", "config", "--get", "remote.origin.url"], cwd=cwd) + + +@cache +def get_normalized_origin(cwd: str | None = None) -> str: + return get_origin(cwd) \ + .replace("git@", "", 1) \ + .replace(".git", "", 1) \ + .replace("https://", "", 1) \ + .replace(":", "/", 1) diff --git a/common/git_creds.py b/common/git_creds.py new file mode 100644 index 000000000..d9a669d15 --- /dev/null +++ b/common/git_creds.py @@ -0,0 +1,209 @@ +import base64 +import json +import os +import subprocess + +from openpilot.common.params import Params + +PARAM = "GitAuthBlob" +KEY_DIR = "/data/konn3kt" +KEY_PATH = os.path.join(KEY_DIR, "git_auth.key") +HELPER_PATH = os.path.join(KEY_DIR, "git_credential_helper.py") +DEFAULT_REPO_DIR = "/data/openpilot" + +_HELPER_SCRIPT = '''#!/usr/bin/env python3 +import json +import os +import sys + +KEY_PATH = "{key_path}" + + +def main() -> None: + if len(sys.argv) < 2 or sys.argv[1] != "get": + return + # drain git's request on stdin (terminated by a blank line) + for line in sys.stdin: + if not line.strip(): + break + params_dir = os.environ.get("PARAMS_DIR", "/data/params/d") + blob_path = os.path.join(params_dir, "GitAuthBlob") + try: + with open(KEY_PATH, "rb") as f: + key = f.read().strip() + with open(blob_path, "rb") as f: + blob = f.read() + if not blob: + return + from cryptography.fernet import Fernet + data = json.loads(Fernet(key).decrypt(blob).decode()) + username = data.get("u", "") + token = data.get("t", "") + if username and token: + sys.stdout.write("username=%s\\npassword=%s\\n" % (username, token)) + except Exception: + return + + +if __name__ == "__main__": + main() +''' + + +def _load_or_create_key() -> bytes: + from cryptography.fernet import Fernet + try: + with open(KEY_PATH, "rb") as f: + return f.read().strip() + except FileNotFoundError: + pass + key = Fernet.generate_key() + os.makedirs(KEY_DIR, exist_ok=True) + # write atomically with restrictive perms + tmp = KEY_PATH + ".tmp" + fd = os.open(tmp, os.O_WRONLY | os.O_CREAT | os.O_TRUNC, 0o600) + with os.fdopen(fd, "wb") as f: + f.write(key) + os.replace(tmp, KEY_PATH) + return key + + +def set_credentials(username: str, token: str) -> None: + """Encrypt and store credentials. Empty username AND token clears them.""" + username = (username or "").strip() + token = (token or "").strip() + if not username and not token: + clear_credentials() + return + from cryptography.fernet import Fernet + blob = Fernet(_load_or_create_key()).encrypt( + json.dumps({"u": username, "t": token}).encode() + ) + Params().put(PARAM, blob) + try: + install_credential_helper(DEFAULT_REPO_DIR) + except Exception: + pass + + +def get_credentials() -> tuple[str, str] | None: + """Return (username, token), or None if unset / unreadable.""" + blob = Params().get(PARAM) + if not blob: + return None + try: + from cryptography.fernet import Fernet + data = json.loads(Fernet(_load_or_create_key()).decrypt(blob).decode()) + return data.get("u", ""), data.get("t", "") + except Exception: + return None + + +def clear_credentials() -> None: + Params().remove(PARAM) + + +def has_credentials() -> bool: + return get_credentials() is not None + + +def _auth_header(username: str, token: str) -> str: + return "Authorization: Basic " + base64.b64encode(f"{username}:{token}".encode()).decode() + + +def ssh_to_https(url: str) -> str: + """Convert an SSH git URL to its HTTPS equivalent. Returns url unchanged if it + is not an SSH URL. A leading ssh. host label is dropped (ssh.host -> host).""" + url = url.strip() + host = path = "" + if url.startswith("ssh://"): + rest = url[len("ssh://"):] + rest = rest.split("@", 1)[-1] # drop user@ + hostport, _, path = rest.partition("/") + host = hostport.split(":", 1)[0] # drop :port + elif url.startswith("git@") or ("@" in url and ":" in url.split("@", 1)[-1] and "://" not in url): + rest = url.split("@", 1)[-1] # host:owner/repo.git + host, _, path = rest.partition(":") + else: + return url # already https/http or unrecognised + if host.startswith("ssh."): + host = host[len("ssh."):] + return f"https://{host}/{path}" + + +def install_credential_helper(repo_dir: str = DEFAULT_REPO_DIR) -> None: + if get_credentials() is None: + return + + origin = subprocess.run( + ["git", "-C", repo_dir, "config", "--get", "remote.origin.url"], + capture_output=True, text=True, check=False, + ).stdout.strip() + if not origin: + return + https = ssh_to_https(origin) + if not https.startswith("https://"): + return + + from urllib.parse import urlsplit + parts = urlsplit(https) + if not parts.hostname: + return + scope = f"{parts.scheme}://{parts.hostname}" + + try: + os.makedirs(KEY_DIR, exist_ok=True) + tmp = HELPER_PATH + ".tmp" + fd = os.open(tmp, os.O_WRONLY | os.O_CREAT | os.O_TRUNC, 0o755) + with os.fdopen(fd, "w") as f: + f.write(_HELPER_SCRIPT.format(key_path=KEY_PATH)) + os.replace(tmp, HELPER_PATH) + except Exception: + return + + helper_cmd = f"!/usr/bin/env python3 {HELPER_PATH}" + subprocess.run( + ["git", "-C", repo_dir, "config", f"credential.{scope}.helper", helper_cmd], + check=False, capture_output=True, + ) + + +def configure(repo_dir: str) -> None: + """Apply on-device credentials to the git repo at repo_dir before a remote op. + + No-op when no credentials are stored. If the origin is an SSH URL it is + rewritten in-place to the HTTPS equivalent so the Basic-auth header applies. + The header is injected via GIT_CONFIG_* env (never persisted to .git/config). + Idempotent.""" + creds = get_credentials() + if creds is None: + return + username, token = creds + + try: + install_credential_helper(repo_dir) + except Exception: + pass + + origin = subprocess.run( + ["git", "-C", repo_dir, "config", "--get", "remote.origin.url"], + capture_output=True, text=True, check=False, + ).stdout.strip() + if not origin: + return + + https = ssh_to_https(origin) + if https != origin and https.startswith("https://"): + subprocess.run( + ["git", "-C", repo_dir, "config", "remote.origin.url", https], + check=False, capture_output=True, + ) + + if not https.startswith("https://"): + return # header auth only works over https + + # scope to this exact repo URL prefix (trailing slash => component boundary) + key = https if https.endswith("/") else https + "/" + os.environ["GIT_CONFIG_COUNT"] = "1" + os.environ["GIT_CONFIG_KEY_0"] = f"http.{key}.extraHeader" + os.environ["GIT_CONFIG_VALUE_0"] = _auth_header(username, token) diff --git a/common/gpio.py b/common/gpio.py new file mode 100644 index 000000000..8f025a2da --- /dev/null +++ b/common/gpio.py @@ -0,0 +1,89 @@ +import os +import fcntl +import ctypes +from functools import cache + +def gpio_init(pin: int, output: bool) -> None: + try: + with open(f"/sys/class/gpio/gpio{pin}/direction", 'wb') as f: + f.write(b"out" if output else b"in") + except Exception as e: + print(f"Failed to set gpio {pin} direction: {e}") + +def gpio_set(pin: int, high: bool) -> None: + try: + with open(f"/sys/class/gpio/gpio{pin}/value", 'wb') as f: + f.write(b"1" if high else b"0") + except Exception as e: + print(f"Failed to set gpio {pin} value: {e}") + +def gpio_read(pin: int) -> bool | None: + val = None + try: + with open(f"/sys/class/gpio/gpio{pin}/value", 'rb') as f: + val = bool(int(f.read().strip())) + except Exception as e: + print(f"Failed to set gpio {pin} value: {e}") + + return val + +def gpio_export(pin: int) -> None: + if os.path.isdir(f"/sys/class/gpio/gpio{pin}"): + return + + try: + with open("/sys/class/gpio/export", 'w') as f: + f.write(str(pin)) + except Exception: + print(f"Failed to export gpio {pin}") + +@cache +def get_irq_action(irq: int) -> list[str]: + try: + with open(f"/sys/kernel/irq/{irq}/actions") as f: + actions = f.read().strip().split(',') + return actions + except FileNotFoundError: + return [] + +def get_irqs_for_action(action: str) -> list[str]: + ret = [] + with open("/proc/interrupts") as f: + for l in f.readlines(): + irq = l.split(':')[0].strip() + if irq.isdigit() and action in get_irq_action(irq): + ret.append(irq) + return ret + +# *** gpiochip *** + +class gpioevent_data(ctypes.Structure): + _fields_ = [ + ("timestamp", ctypes.c_uint64), + ("id", ctypes.c_uint32), + ] + +class gpioevent_request(ctypes.Structure): + _fields_ = [ + ("lineoffset", ctypes.c_uint32), + ("handleflags", ctypes.c_uint32), + ("eventflags", ctypes.c_uint32), + ("label", ctypes.c_char * 32), + ("fd", ctypes.c_int) + ] + +def gpiochip_get_ro_value_fd(label: str, gpiochip_id: int, pin: int) -> int: + GPIOEVENT_REQUEST_BOTH_EDGES = 0x3 + GPIOHANDLE_REQUEST_INPUT = 0x1 + GPIO_GET_LINEEVENT_IOCTL = 0xc030b404 + + rq = gpioevent_request() + rq.lineoffset = pin + rq.handleflags = GPIOHANDLE_REQUEST_INPUT + rq.eventflags = GPIOEVENT_REQUEST_BOTH_EDGES + rq.label = label.encode('utf-8')[:31] + b'\0' + + fd = os.open(f"/dev/gpiochip{gpiochip_id}", os.O_RDONLY) + fcntl.ioctl(fd, GPIO_GET_LINEEVENT_IOCTL, rq) + os.close(fd) + return int(rq.fd) diff --git a/common/gps.py b/common/gps.py new file mode 100644 index 000000000..6f96d72e9 --- /dev/null +++ b/common/gps.py @@ -0,0 +1,8 @@ +from openpilot.common.params import Params + + +def get_gps_location_service(params: Params) -> str: + if params.get_bool("UbloxAvailable"): + return "gpsLocationExternal" + else: + return "gpsLocation" diff --git a/common/i2c.py b/common/i2c.py new file mode 100644 index 000000000..1dfaa659a --- /dev/null +++ b/common/i2c.py @@ -0,0 +1,81 @@ +import os +import fcntl +import ctypes + +# I2C constants from /usr/include/linux/i2c-dev.h +I2C_SLAVE = 0x0703 +I2C_SLAVE_FORCE = 0x0706 +I2C_SMBUS = 0x0720 + +# SMBus transfer types +I2C_SMBUS_READ = 1 +I2C_SMBUS_WRITE = 0 +I2C_SMBUS_BYTE_DATA = 2 +I2C_SMBUS_I2C_BLOCK_DATA = 8 + +I2C_SMBUS_BLOCK_MAX = 32 + + +class _I2cSmbusData(ctypes.Union): + _fields_ = [ + ("byte", ctypes.c_uint8), + ("word", ctypes.c_uint16), + ("block", ctypes.c_uint8 * (I2C_SMBUS_BLOCK_MAX + 2)), + ] + + +class _I2cSmbusIoctlData(ctypes.Structure): + _fields_ = [ + ("read_write", ctypes.c_uint8), + ("command", ctypes.c_uint8), + ("size", ctypes.c_uint32), + ("data", ctypes.POINTER(_I2cSmbusData)), + ] + + +class SMBus: + def __init__(self, bus: int): + self._fd = os.open(f'/dev/i2c-{bus}', os.O_RDWR) + + def __enter__(self) -> 'SMBus': + return self + + def __exit__(self, *args) -> None: + self.close() + + def close(self) -> None: + if hasattr(self, '_fd') and self._fd >= 0: + os.close(self._fd) + self._fd = -1 + + def _set_address(self, addr: int, force: bool = False) -> None: + ioctl_arg = I2C_SLAVE_FORCE if force else I2C_SLAVE + fcntl.ioctl(self._fd, ioctl_arg, addr) + + def _smbus_access(self, read_write: int, command: int, size: int, data: _I2cSmbusData) -> None: + ioctl_data = _I2cSmbusIoctlData(read_write, command, size, ctypes.pointer(data)) + fcntl.ioctl(self._fd, I2C_SMBUS, ioctl_data) + + def read_byte_data(self, addr: int, register: int, force: bool = False) -> int: + self._set_address(addr, force) + data = _I2cSmbusData() + self._smbus_access(I2C_SMBUS_READ, register, I2C_SMBUS_BYTE_DATA, data) + return int(data.byte) + + def write_byte_data(self, addr: int, register: int, value: int, force: bool = False) -> None: + self._set_address(addr, force) + data = _I2cSmbusData() + data.byte = value & 0xFF + self._smbus_access(I2C_SMBUS_WRITE, register, I2C_SMBUS_BYTE_DATA, data) + + def read_i2c_block_data(self, addr: int, register: int, length: int, force: bool = False) -> list[int]: + self._set_address(addr, force) + if not (0 <= length <= I2C_SMBUS_BLOCK_MAX): + raise ValueError(f"length must be 0..{I2C_SMBUS_BLOCK_MAX}") + + data = _I2cSmbusData() + data.block[0] = length + self._smbus_access(I2C_SMBUS_READ, register, I2C_SMBUS_I2C_BLOCK_DATA, data) + read_len = int(data.block[0]) or length + read_len = min(read_len, length) + return [int(b) for b in data.block[1 : read_len + 1]] diff --git a/common/iq_perf.py b/common/iq_perf.py new file mode 100644 index 000000000..e81bbdfdc --- /dev/null +++ b/common/iq_perf.py @@ -0,0 +1,187 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import time +from collections import deque +from dataclasses import dataclass +from typing import Any + +import cereal.messaging as messaging +from cereal import custom +from openpilot.common.swaglog import cloudlog + + +TRACE_SERVICE = "iqPerfTrace" +MAX_TRACE_SAMPLES = 16 +_SHARED_PM: messaging.PubMaster | None = None + + +@dataclass(slots=True) +class PerfSample: + frame_id: int = 0 + loop_dt_us: int = 0 + update_us: int = 0 + state_control_us: int = 0 + publish_us: int = 0 + tail_work_us: int = 0 + rk_remaining_us: int = 0 + stale_carcontrol_us: int = 0 + stale_carcontrol_frames: int = 0 + sendcan_gap_us: int = 0 + model_eval_us: int = 0 + model_dropped_frames: int = 0 + model_backlog: int = 0 + texture_decode_us: int = 0 + texture_upload_us: int = 0 + texture_unload_us: int = 0 + texture_prune_us: int = 0 + texture_consume_us: int = 0 + texture_batch_size: int = 0 + texture_bytes: int = 0 + texture_cache_before: int = 0 + texture_cache_after: int = 0 + texture_unloaded: int = 0 + memory_usage_percent: int = 0 + gpu_usage_percent: int = 0 + cpu_usage_percent: int = 0 + flags: int = 0 + + +class PerfTraceRing: + def __init__(self, size: int = MAX_TRACE_SAMPLES): + self._samples: deque[PerfSample] = deque(maxlen=size) + + def push(self, sample: PerfSample) -> None: + self._samples.append(sample) + + def snapshot(self) -> list[PerfSample]: + return list(self._samples) + + +class PerfTraceEmitter: + _SEVERITY_MAP = { + "info": custom.IQPerfTrace.Severity.info, + "warning": custom.IQPerfTrace.Severity.warning, + "error": custom.IQPerfTrace.Severity.error, + "critical": custom.IQPerfTrace.Severity.critical, + } + + def __init__(self, process_name: str, pubmaster: messaging.PubMaster | None = None): + self.process_name = process_name + self._pm: messaging.PubMaster | None = pubmaster + self._last_emit_mono: dict[str, float] = {} + self._disabled = False + + def _pubmaster(self) -> messaging.PubMaster: + global _SHARED_PM + if self._pm is not None: + return self._pm + if _SHARED_PM is None: + _SHARED_PM = messaging.PubMaster([TRACE_SERVICE]) + self._pm = _SHARED_PM + return self._pm + + @staticmethod + def _clamp_uint(value: int, bits: int) -> int: + return max(0, min(value, (1 << bits) - 1)) + + @staticmethod + def _clamp_int(value: int, bits: int) -> int: + lo = -(1 << (bits - 1)) + hi = (1 << (bits - 1)) - 1 + return max(lo, min(value, hi)) + + def emit(self, event_class: str, *, + severity: str = "warning", + frame_id: int = 0, + total_time_us: int = 0, + rk_remaining_us: int = 0, + batch_size: int = 0, + dropped_frames: int = 0, + backlog: int = 0, + flags: int = 0, + samples: list[PerfSample] | None = None, + missing_services: list[str] | None = None, + top_processes: list[str] | None = None, + detail: str = "", + min_interval_s: float = 0.0, + mirror_cloudlog: bool = True) -> bool: + if self._disabled: + return False + now = time.monotonic() + last_emit = self._last_emit_mono.get(event_class, 0.0) + if min_interval_s > 0.0 and (now - last_emit) < min_interval_s: + return False + self._last_emit_mono[event_class] = now + + msg = messaging.new_message(TRACE_SERVICE) + trace = msg.iqPerfTrace + trace.process = self.process_name + trace.eventClass = event_class + trace.severity = self._SEVERITY_MAP.get(severity, custom.IQPerfTrace.Severity.warning) + trace.frameId = self._clamp_uint(int(frame_id), 32) + trace.totalTimeUs = self._clamp_uint(int(total_time_us), 32) + trace.rkRemainingUs = self._clamp_int(int(rk_remaining_us), 32) + trace.batchSize = self._clamp_uint(int(batch_size), 16) + trace.droppedFrames = self._clamp_uint(int(dropped_frames), 16) + trace.backlog = self._clamp_uint(int(backlog), 16) + trace.flags = self._clamp_uint(int(flags), 32) + trace.missingServices = list(missing_services or []) + trace.topProcesses = list(top_processes or []) + trace.detail = detail + + trace_samples = samples or [] + samples_builder = trace.init("samples", len(trace_samples)) + for i, sample in enumerate(trace_samples): + builder = samples_builder[i] + builder.frameId = self._clamp_uint(int(sample.frame_id), 32) + builder.loopDtUs = self._clamp_uint(int(sample.loop_dt_us), 32) + builder.updateUs = self._clamp_uint(int(sample.update_us), 32) + builder.stateControlUs = self._clamp_uint(int(sample.state_control_us), 32) + builder.publishUs = self._clamp_uint(int(sample.publish_us), 32) + builder.tailWorkUs = self._clamp_uint(int(sample.tail_work_us), 32) + builder.rkRemainingUs = self._clamp_int(int(sample.rk_remaining_us), 32) + builder.staleCarControlUs = self._clamp_uint(int(sample.stale_carcontrol_us), 32) + builder.staleCarControlFrames = self._clamp_uint(int(sample.stale_carcontrol_frames), 16) + builder.sendcanGapUs = self._clamp_uint(int(sample.sendcan_gap_us), 32) + builder.modelEvalUs = self._clamp_uint(int(sample.model_eval_us), 32) + builder.modelDroppedFrames = self._clamp_uint(int(sample.model_dropped_frames), 16) + builder.modelBacklog = self._clamp_uint(int(sample.model_backlog), 16) + builder.textureDecodeUs = self._clamp_uint(int(sample.texture_decode_us), 32) + builder.textureUploadUs = self._clamp_uint(int(sample.texture_upload_us), 32) + builder.textureUnloadUs = self._clamp_uint(int(sample.texture_unload_us), 32) + builder.texturePruneUs = self._clamp_uint(int(sample.texture_prune_us), 32) + builder.textureConsumeUs = self._clamp_uint(int(sample.texture_consume_us), 32) + builder.textureBatchSize = self._clamp_uint(int(sample.texture_batch_size), 16) + builder.textureBytes = self._clamp_uint(int(sample.texture_bytes), 32) + builder.textureCacheBefore = self._clamp_uint(int(sample.texture_cache_before), 16) + builder.textureCacheAfter = self._clamp_uint(int(sample.texture_cache_after), 16) + builder.textureUnloaded = self._clamp_uint(int(sample.texture_unloaded), 16) + builder.memoryUsagePercent = self._clamp_uint(int(sample.memory_usage_percent), 16) + builder.gpuUsagePercent = self._clamp_uint(int(sample.gpu_usage_percent), 16) + builder.cpuUsagePercent = self._clamp_uint(int(sample.cpu_usage_percent), 16) + builder.flags = self._clamp_uint(int(sample.flags), 32) + + try: + self._pubmaster().send(TRACE_SERVICE, msg) + except messaging.MultiplePublishersError: + self._disabled = True + cloudlog.error(f"iq_perf_trace disabled for {self.process_name}: duplicate publisher for {TRACE_SERVICE}") + return False + except Exception: + cloudlog.exception(f"iq_perf_trace publish failed for {self.process_name}") + return False + + if mirror_cloudlog: + cloudlog.event( + "iq_perf_trace", + process=self.process_name, + event_class=event_class, + severity=severity, + frame_id=int(frame_id), + total_time_us=int(total_time_us), + dropped_frames=int(dropped_frames), + flags=int(flags), + detail=detail, + ) + return True diff --git a/common/issue_debug.py b/common/issue_debug.py new file mode 100644 index 000000000..3a8b437fc --- /dev/null +++ b/common/issue_debug.py @@ -0,0 +1,44 @@ +import os +import threading +import time +from datetime import datetime +from pathlib import Path + +from openpilot.system.hardware import PC +from openpilot.system.hardware.hw import Paths + + +DEBUG_FILENAME = "iqpilot_issue_debug.txt" +DEBUG_PATH = Path(Paths.comma_home()) / "community" / DEBUG_FILENAME if PC else Path("/data/community") / DEBUG_FILENAME + +_lock = threading.Lock() +_last_log_times: dict[str, float] = {} + + +def log_issue(tag: str, message: str) -> None: + try: + DEBUG_PATH.parent.mkdir(parents=True, exist_ok=True) + with _lock: + with open(DEBUG_PATH, "a", encoding="utf-8") as f: + timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f")[:-3] + f.write(f"[{timestamp}] [{tag}] {message}\n") + except OSError: + pass + + +def log_issue_limited(key: str, tag: str, message: str, interval_sec: float = 1.0) -> None: + now = time.monotonic() + with _lock: + last = _last_log_times.get(key, 0.0) + if now - last < interval_sec: + return + _last_log_times[key] = now + + log_issue(tag, message) + + +def clear_issue_debug_log() -> None: + try: + os.remove(DEBUG_PATH) + except OSError: + pass diff --git a/common/logging_extra.py b/common/logging_extra.py new file mode 100644 index 000000000..ceaf083cd --- /dev/null +++ b/common/logging_extra.py @@ -0,0 +1,249 @@ +import io +import os +import sys +import copy +import json +import time +import uuid +import socket +import logging +import traceback +import numpy as np +from threading import local +from collections import OrderedDict +from contextlib import contextmanager + +LOG_TIMESTAMPS = "LOG_TIMESTAMPS" in os.environ + +def json_handler(obj): + if isinstance(obj, np.bool_): + return bool(obj) + # if isinstance(obj, (datetime.date, datetime.time)): + # return obj.isoformat() + return repr(obj) + +def json_robust_dumps(obj): + return json.dumps(obj, default=json_handler) + +class NiceOrderedDict(OrderedDict): + def __str__(self): + return json_robust_dumps(self) + +class SwagFormatter(logging.Formatter): + def __init__(self, swaglogger): + logging.Formatter.__init__(self, None, '%a %b %d %H:%M:%S %Z %Y') + + self.swaglogger = swaglogger + self.host = socket.gethostname() + + def format_dict(self, record): + record_dict = NiceOrderedDict() + + if isinstance(record.msg, dict): + record_dict['msg'] = record.msg + else: + try: + record_dict['msg'] = record.getMessage() + except (ValueError, TypeError): + record_dict['msg'] = [record.msg]+record.args + + record_dict['ctx'] = self.swaglogger.get_ctx() + + if record.exc_info: + record_dict['exc_info'] = self.formatException(record.exc_info) + + record_dict['level'] = record.levelname + record_dict['levelnum'] = record.levelno + record_dict['name'] = record.name + record_dict['filename'] = record.filename + record_dict['lineno'] = record.lineno + record_dict['pathname'] = record.pathname + record_dict['module'] = record.module + record_dict['funcName'] = record.funcName + record_dict['host'] = self.host + record_dict['process'] = record.process + record_dict['thread'] = record.thread + record_dict['threadName'] = record.threadName + record_dict['created'] = record.created + + return record_dict + + def format(self, record): + if self.swaglogger is None: + raise Exception("must set swaglogger before calling format()") + return json_robust_dumps(self.format_dict(record)) + +class SwagLogFileFormatter(SwagFormatter): + def fix_kv(self, k, v): + # append type to names to preserve legacy naming in logs + # avoids overlapping key namespaces with different types + # e.g. log.info() creates 'msg' -> 'msg$s' + # log.event() creates 'msg.health.logMonoTime' -> 'msg.health.logMonoTime$i' + # because overlapping namespace 'msg' caused problems + if isinstance(v, (str, bytes)): + k += "$s" + elif isinstance(v, float): + k += "$f" + elif isinstance(v, bool): + k += "$b" + elif isinstance(v, int): + k += "$i" + elif isinstance(v, dict): + nv = {} + for ik, iv in v.items(): + ik, iv = self.fix_kv(ik, iv) + nv[ik] = iv + v = nv + elif isinstance(v, list): + k += "$a" + return k, v + + def format(self, record): + if isinstance(record, str): + v = json.loads(record) + else: + v = self.format_dict(record) + + mk, mv = self.fix_kv('msg', v['msg']) + del v['msg'] + v[mk] = mv + v['id'] = uuid.uuid4().hex + + return json_robust_dumps(v) + +class SwagErrorFilter(logging.Filter): + def filter(self, record): + return record.levelno < logging.ERROR + +def _tmpfunc(): + return 0 + +def _srcfile(): + return os.path.normcase(_tmpfunc.__code__.co_filename) + +class SwagLogger(logging.Logger): + def __init__(self): + logging.Logger.__init__(self, "swaglog") + + self.global_ctx = {} + + self.log_local = local() + self.log_local.ctx = {} + + def local_ctx(self): + try: + return self.log_local.ctx + except AttributeError: + self.log_local.ctx = {} + return self.log_local.ctx + + def get_ctx(self): + return dict(self.local_ctx(), **self.global_ctx) + + @contextmanager + def ctx(self, **kwargs): + old_ctx = self.local_ctx() + self.log_local.ctx = copy.copy(old_ctx) or {} + self.log_local.ctx.update(kwargs) + try: + yield + finally: + self.log_local.ctx = old_ctx + + def bind(self, **kwargs): + self.local_ctx().update(kwargs) + + def bind_global(self, **kwargs): + self.global_ctx.update(kwargs) + + def event(self, event, *args, **kwargs): + evt = NiceOrderedDict() + evt['event'] = event + if args: + evt['args'] = args + evt.update(kwargs) + if 'error' in kwargs: + self.error(evt) + elif 'debug' in kwargs: + self.debug(evt) + else: + self.info(evt) + + def timestamp(self, event_name): + if LOG_TIMESTAMPS: + t = time.monotonic() + tstp = NiceOrderedDict() + tstp['timestamp'] = NiceOrderedDict() + tstp['timestamp']["event"] = event_name + tstp['timestamp']["time"] = t*1e9 + self.debug(tstp) + + def findCaller(self, stack_info=False, stacklevel=1): + """ + Find the stack frame of the caller so that we can note the source + file name, line number and function name. + """ + f = sys._getframe(3) + #On some versions of IronPython, currentframe() returns None if + #IronPython isn't run with -X:Frames. + if f is not None: + f = f.f_back + orig_f = f + while f and stacklevel > 1: + f = f.f_back + stacklevel -= 1 + if not f: + f = orig_f + rv = "(unknown file)", 0, "(unknown function)", None + while hasattr(f, "f_code"): + co = f.f_code + filename = os.path.normcase(co.co_filename) + + if filename == _srcfile: + f = f.f_back + continue + sinfo = None + if stack_info: + sio = io.StringIO() + sio.write('Stack (most recent call last):\n') + traceback.print_stack(f, file=sio) + sinfo = sio.getvalue() + if sinfo[-1] == '\n': + sinfo = sinfo[:-1] + sio.close() + rv = (co.co_filename, f.f_lineno, co.co_name, sinfo) + break + return rv + +if __name__ == "__main__": + log = SwagLogger() + + stdout_handler = logging.StreamHandler(sys.stdout) + stdout_handler.setLevel(logging.INFO) + stdout_handler.addFilter(SwagErrorFilter()) + log.addHandler(stdout_handler) + + stderr_handler = logging.StreamHandler(sys.stderr) + stderr_handler.setLevel(logging.ERROR) + log.addHandler(stderr_handler) + + log.info("asdasd %s", "a") + log.info({'wut': 1}) + log.warning("warning") + log.error("error") + log.critical("critical") + log.event("test", x="y") + + with log.ctx(): + stdout_handler.setFormatter(SwagFormatter(log)) + stderr_handler.setFormatter(SwagFormatter(log)) + log.bind(user="some user") + log.info("in req") + print("") + log.warning("warning") + print("") + log.error("error") + print("") + log.critical("critical") + print("") + log.event("do_req", a=1, b="c") diff --git a/common/markdown.py b/common/markdown.py new file mode 100644 index 000000000..f0f056d96 --- /dev/null +++ b/common/markdown.py @@ -0,0 +1,45 @@ +HTML_REPLACEMENTS = [ + (r'&', r'&'), + (r'"', r'"'), +] + +def parse_markdown(text: str, tab_length: int = 2) -> str: + lines = text.split("\n") + output: list[str] = [] + list_level = 0 + + def end_outstanding_lists(level: int, end_level: int) -> int: + while level > end_level: + level -= 1 + output.append("") + if level > 0: + output.append("") + return end_level + + for i, line in enumerate(lines): + if i + 1 < len(lines) and lines[i + 1].startswith("==="): # heading + output.append(f"

{line}

") + elif line.startswith("==="): + pass + elif line.lstrip().startswith("* "): # list + line_level = 1 + line.count(" " * tab_length, 0, line.index("*")) + if list_level >= line_level: + list_level = end_outstanding_lists(list_level, line_level) + else: + list_level += 1 + if list_level > 1: + output[-1] = output[-1].replace("", "") + output.append("