From e016d20edf1333a4996ba100b6805fe215dc6769 Mon Sep 17 00:00:00 2001 From: Sherin Joseph Roy Date: Thu, 24 Sep 2026 00:55:56 +0530 Subject: [PATCH] feat: openpilot logs open, with the schema bundled and the GPS as a reference openpilot rlog import was listed as a limitation because it raised unless openpilot's cereal schema happened to be on the machine. It also could not have worked when the schema was there: - Logs are distributed as rlog.bz2 or rlog.zst and were read as raw capnp. They are now decompressed (zstd needs the zstandard module, and says so). - The schema was loaded without an import path, which its absolute imports need. It is now vendored under core/data/cereal (comma.ai, MIT; NOTICE beside it), with tools/fetch_cereal.py to refresh it. - Only files ending .rlog or .qlog were routed to the reader; openpilot names them rlog, rlog.bz2, qlog.zst. All of those route now, from the loader and the Open openpilot Log dialog. - A panda echoes the frames it transmits with src 128 + bus. Those were mixed into the car's traffic; they are kept apart and counted. gps_reference returns the log's GPS as reference series on the capture's clock, ready for the calibrator. On a public 2021 Toyota RAV4 drive from openpilot's CI routes: 122,282 received frames from 158 IDs on three buses, 78,896 panda transmissions kept apart, the two summing to pycapnp's own count. Calibrated against the log's own GPS, 0x0B4 bytes 5-6 come back as 0.01 km/h per bit, the figure in openpilot's DBC, and decode within 0.87% of openpilot's vEgo. Both are now phases of acceptance_new_sources.py. pycapnp is the new openpilot extra; CI installs it, the binary build installs it with mdf, and the spec collects it. --- .github/workflows/ci.yml | 4 +- canlab.spec | 11 +- canlab/core/data/cereal/NOTICE.txt | 30 + canlab/core/data/cereal/car.capnp | 760 ++++++ canlab/core/data/cereal/custom.capnp | 71 + canlab/core/data/cereal/deprecated.capnp | 787 ++++++ canlab/core/data/cereal/include/c++.capnp | 26 + canlab/core/data/cereal/log.capnp | 2716 +++++++++++++++++++++ canlab/core/log_parser.py | 13 +- canlab/core/openpilot_parser.py | 205 +- canlab/mainwindow.py | 2 +- pyproject.toml | 4 +- tests/real_data/acceptance_new_sources.py | 83 +- tests/test_openpilot_rlog.py | 102 + tools/fetch_cereal.py | 79 + 15 files changed, 4808 insertions(+), 85 deletions(-) create mode 100644 canlab/core/data/cereal/NOTICE.txt create mode 100644 canlab/core/data/cereal/car.capnp create mode 100644 canlab/core/data/cereal/custom.capnp create mode 100644 canlab/core/data/cereal/deprecated.capnp create mode 100644 canlab/core/data/cereal/include/c++.capnp create mode 100644 canlab/core/data/cereal/log.capnp create mode 100644 tests/test_openpilot_rlog.py create mode 100644 tools/fetch_cereal.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 05b9b4a..98f750b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -23,7 +23,7 @@ jobs: - name: Install package with extras run: | python -m pip install --upgrade pip - pip install -e ".[dev,ai,rest,mcp,mdf,adapters]" + pip install -e ".[dev,ai,rest,mcp,mdf,adapters,openpilot]" - name: Lint run: ruff check canlab tests @@ -53,7 +53,7 @@ jobs: - name: Install package run: | python -m pip install --upgrade pip - pip install -e ".[dev,ai,rest,mcp,adapters]" + pip install -e ".[dev,ai,rest,mcp,mdf,adapters,openpilot]" # Catches bundling gaps such as a missing Qt submodule in canlab.spec # (e.g. QtMultimedia) that only surface in the packaged binary. diff --git a/canlab.spec b/canlab.spec index 4fce780..8c427ae 100644 --- a/canlab.spec +++ b/canlab.spec @@ -12,10 +12,17 @@ ROOT = Path("canlab") # _imaging.so or the manylinux-bundled libtiff/libjpeg/etc. without collect_all. pil_datas, pil_binaries, pil_hiddenimports = collect_all("PIL") +# pycapnp reads openpilot logs. It is a compiled extension with schema files of +# its own, so it is collected whole when it is installed. +try: + capnp_datas, capnp_binaries, capnp_hiddenimports = collect_all("capnp") +except Exception: + capnp_datas, capnp_binaries, capnp_hiddenimports = [], [], [] + a = Analysis( [str(ROOT / "main.py")], pathex=["."], - binaries=pil_binaries, + binaries=pil_binaries + capnp_binaries, datas=[ (str(ROOT / "canlab.png"), "canlab" ), (str(ROOT / "assets"), "canlab/assets" ), @@ -23,6 +30,7 @@ a = Analysis( # the NMEA 2000 table distilled from canboat, and its licence notice (str(ROOT / "core" / "data"), "canlab/core/data" ), *pil_datas, + *capnp_datas, ], hiddenimports=[ *collect_submodules("canlab"), @@ -63,6 +71,7 @@ a = Analysis( "pydantic", "pydantic.v1", "pydantic_core", "anthropic", "anthropic.types", "anthropic._models", *pil_hiddenimports, + *capnp_hiddenimports, ], excludes=[ "tkinter", diff --git a/canlab/core/data/cereal/NOTICE.txt b/canlab/core/data/cereal/NOTICE.txt new file mode 100644 index 0000000..7feba59 --- /dev/null +++ b/canlab/core/data/cereal/NOTICE.txt @@ -0,0 +1,30 @@ +The capnp schema in this directory is copied from comma.ai's repositories: + + log.capnp, custom.capnp, deprecated.capnp, include/c++.capnp + https://github.com/commaai/openpilot (openpilot/cereal) + Copyright (c) 2018, Comma.ai, Inc. + car.capnp + https://github.com/commaai/opendbc (opendbc/car) + Copyright (c) 2020, Comma.ai, Inc. + +Both are released 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. + +The files are unmodified. diff --git a/canlab/core/data/cereal/car.capnp b/canlab/core/data/cereal/car.capnp new file mode 100644 index 0000000..3df6ba7 --- /dev/null +++ b/canlab/core/data/cereal/car.capnp @@ -0,0 +1,760 @@ +using Cxx = import "/include/c++.capnp"; +$Cxx.namespace("cereal"); + +@0x8e2af1e708af8b8d; + +# ******* events causing controls state machine transition ******* + +# IMPORTANT: This struct is to not be modified so old logs can be parsed +struct OnroadEventDEPRECATED @0x9b1657f34caf3ad3 { + 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 @0xbaa8c5d505f727de { + canError @0; + steerUnavailable @1; + wrongGear @4; + doorOpen @5; + seatbeltNotLatched @6; + espDisabled @7; + wrongCarMode @8; + steerTempUnavailable @9; + reverseGear @10; + buttonCancel @11; + buttonEnable @12; + pedalPressed @13; # exits active state + preEnableStandstill @73; # added during pre-enable state with brake + gasPressedOverride @108; # added when user is pressing gas with no disengage on gas + steerOverride @114; + cruiseDisabled @14; + speedTooLow @17; + outOfSpace @18; + overheat @19; + calibrationIncomplete @20; + calibrationInvalid @21; + calibrationRecalibrating @117; + controlsMismatch @22; + pcmEnable @23; + pcmDisable @24; + radarFault @26; + brakeHold @28; + parkBrake @29; + manualRestart @30; + joystickDebug @34; + longitudinalManeuver @124; + steerTempUnavailableSilent @35; + resumeRequired @36; + preDriverDistracted @37; + promptDriverDistracted @38; + driverDistracted @39; + preDriverUnresponsive @43; + promptDriverUnresponsive @44; + driverUnresponsive @45; + belowSteerSpeed @46; + lowBattery @48; + accFaulted @51; + sensorDataInvalid @52; + commIssue @53; + commIssueAvgFreq @109; + tooDistracted @54; + posenetInvalid @55; + preLaneChangeLeft @57; + preLaneChangeRight @58; + laneChange @59; + lowMemory @63; + stockAeb @64; + ldw @65; + carUnrecognized @66; + invalidLkasSetting @69; + speedTooHigh @70; + laneChangeBlocked @71; + relayMalfunction @72; + stockFcw @74; + startup @75; + startupNoCar @76; + startupNoControl @77; + startupNoSecOcKey @125; + startupMaster @78; + fcw @79; + steerSaturated @80; + belowEngageSpeed @84; + noGps @85; + wrongCruiseMode @87; + modeldLagging @89; + deviceFalling @90; + fanMalfunction @91; + cameraMalfunction @92; + cameraFrameRate @110; + processNotRunning @95; + dashcamMode @96; + selfdriveInitializing @98; + usbError @99; + cruiseMismatch @106; + canBusMissing @111; + selfdrivedLagging @112; + resumeBlocked @113; + steerTimeLimit @115; + vehicleSensorsInvalid @116; + locationdTemporaryError @103; + locationdPermanentError @118; + paramsdTemporaryError @50; + paramsdPermanentError @119; + actuatorsApiUnavailable @120; + espActive @121; + personalityChanged @122; + aeb @123; + + radarCanErrorDEPRECATED @15; + communityFeatureDisallowedDEPRECATED @62; + radarCommIssueDEPRECATED @67; + driverMonitorLowAccDEPRECATED @68; + gasUnavailableDEPRECATED @3; + dataNeededDEPRECATED @16; + modelCommIssueDEPRECATED @27; + ipasOverrideDEPRECATED @33; + geofenceDEPRECATED @40; + driverMonitorOnDEPRECATED @41; + driverMonitorOffDEPRECATED @42; + calibrationProgressDEPRECATED @47; + invalidGiraffeHondaDEPRECATED @49; + invalidGiraffeToyotaDEPRECATED @60; + internetConnectivityNeededDEPRECATED @61; + whitePandaUnsupportedDEPRECATED @81; + commIssueWarningDEPRECATED @83; + focusRecoverActiveDEPRECATED @86; + neosUpdateRequiredDEPRECATED @88; + modelLagWarningDEPRECATED @93; + startupOneplusDEPRECATED @82; + startupFuzzyFingerprintDEPRECATED @97; + noTargetDEPRECATED @25; + brakeUnavailableDEPRECATED @2; + plannerErrorDEPRECATED @32; + gpsMalfunctionDEPRECATED @94; + roadCameraErrorDEPRECATED @100; + driverCameraErrorDEPRECATED @101; + wideRoadCameraErrorDEPRECATED @102; + highCpuUsageDEPRECATED @105; + startupNoFwDEPRECATED @104; + lowSpeedLockoutDEPRECATED @31; + lkasDisabledDEPRECATED @107; + soundsUnavailableDEPRECATED @56; + } +} + +struct CarState { + # CAN health + canValid @26 :Bool; # invalid counter/checksums + canTimeout @40 :Bool; # CAN bus dropped out + canErrorCounter @48 :UInt32; + + # process meta + cumLagMs @50 :Float32; + + # car speed + vEgo @1 :Float32; # best estimate of speed + aEgo @16 :Float32; # best estimate of aCAN cceleration + vEgoRaw @17 :Float32; # unfiltered speed from wheel speed sensors + vEgoCluster @44 :Float32; # best estimate of speed shown on car's instrument cluster, used for UI + + vCruise @53 :Float32; # actual set speed + vCruiseCluster @54 :Float32; # set speed to display in the UI + + yawRate @22 :Float32; # best estimate of yaw rate + standstill @18 :Bool; + wheelSpeeds @2 :WheelSpeeds; + + gasPressed @4 :Bool; # this is user pedal only + + brakePressed @6 :Bool; # this is user pedal only + regenBraking @45 :Bool; # this is user pedal only + parkingBrake @39 :Bool; + brakeHoldActive @38 :Bool; + + # steering wheel + steeringAngleDeg @7 :Float32; + steeringAngleOffsetDeg @37 :Float32; # Offset between sensors in case there multiple + steeringRateDeg @15 :Float32; # optional + steeringTorque @8 :Float32; # Native CAN units, only needed on cars where it's used for control + steeringTorqueEps @27 :Float32; # Native CAN units, only needed on cars where it's used for control + steeringPressed @9 :Bool; # is the user overring the steering wheel? + steeringDisengage @58 :Bool; # more force than steeringPressed, disengages for applicable brands + steerFaultTemporary @35 :Bool; + steerFaultPermanent @36 :Bool; + + invalidLkasSetting @55 :Bool; # stock LKAS is incorrectly configured (i.e. on or off) + stockAeb @30 :Bool; + stockLkas @59 :Bool; + stockFcw @31 :Bool; + espDisabled @32 :Bool; + accFaulted @42 :Bool; + carFaultedNonCritical @47 :Bool; # some ECU is faulted, but car remains controllable + espActive @51 :Bool; + vehicleSensorsInvalid @52 :Bool; # invalid steering angle readings, etc. + lowSpeedAlert @56 :Bool; # lost steering control due to a dynamic min steering speed + blockPcmEnable @60 :Bool; # whether to allow PCM to enable this frame + carNotReady @61 :Bool; # car is transiently refusing engagement, used to prevent a fault if engaged + + # cruise state + cruiseState @10 :CruiseState; + + # gear + gearShifter @14 :GearShifter; + + # button presses + buttonEvents @11 :List(ButtonEvent); + buttonEnable @57 :Bool; # user is requesting enable, usually one frame. set if pcmCruise=False + leftBlinker @20 :Bool; + rightBlinker @21 :Bool; + genericToggle @23 :Bool; + + # lock info + doorOpen @24 :Bool; # ideally includes all doors + seatbeltUnlatched @25 :Bool; # driver seatbelt + + # blindspot sensors + leftBlindspot @33 :Bool; # Is there something blocking the left lane change + rightBlindspot @34 :Bool; # Is there something blocking the right lane change + + fuelGauge @41 :Float32; # battery or fuel tank level from [0.0, 1.0] + charging @43 :Bool; + + struct WheelSpeeds { + # optional wheel speeds + fl @0 :Float32; + fr @1 :Float32; + rl @2 :Float32; + rr @3 :Float32; + } + + struct CruiseState { + enabled @0 :Bool; + speed @1 :Float32; + speedCluster @6 :Float32; # Set speed as shown on instrument cluster + available @2 :Bool; + standstill @4 :Bool; + nonAdaptive @5 :Bool; + + deprecated :group { + speedOffset @3 :Float32; + } + } + + enum GearShifter { + unknown @0; + park @1; + drive @2; + neutral @3; + reverse @4; + sport @5; + low @6; + brake @7; + eco @8; + manumatic @9; + } + + # send on change + struct ButtonEvent { + pressed @0 :Bool; + type @1 :Type; + + enum Type { + unknown @0; + leftBlinker @1; + rightBlinker @2; + accelCruise @3; + decelCruise @4; + cancel @5; + lkas @6; + altButton2 @7; + mainCruise @8; + setCruise @9; + resumeCruise @10; + gapAdjustCruise @11; + } + } + + deprecated :group { + errors @0 :List(OnroadEventDEPRECATED.EventName); + gas @3 :Float32; + brake @5 :Float32; + brakeLights @19 :Bool; + steeringRateLimited @29 :Bool; + canMonoTimes @12: List(UInt64); + canRcvTimeout @49 :Bool; + events @13 :List(OnroadEventDEPRECATED); + clutchPressed @28 :Bool; + engineRpm @46 :Float32; + } +} + +# ******* radar state @ 20hz ******* + +struct RadarData @0x888ad6581cf0aacb { + errors @3 :Error; + points @1 :List(RadarPoint); + + struct Error { + canError @0 :Bool; + radarFault @1 :Bool; + wrongConfig @2 :Bool; + radarUnavailableTemporary @3 :Bool; # radar data is temporarily unavailable due to conditions the car sets + } + + # similar to LiveTracks + struct RadarPoint { + # all fields required + trackId @0 :UInt64; # no trackId reuse + dRel @1 :Float32; # m from the front bumper of the car + yRel @2 :Float32; # m + vRel @3 :Float32; # m/s + + deprecated :group { + aRel @4 :Float32; # m/s^2 + yvRel @5 :Float32; # m/s + measured @6 :Bool; # measurement VS estimate flag + } + } + + enum ErrorDEPRECATED { + canError @0; + fault @1; + wrongConfig @2; + } + + deprecated :group { + canMonoTimes @2 :List(UInt64); + errors @0 :List(ErrorDEPRECATED); + } +} + +# ******* car controls @ 100hz ******* + +struct CarControl { + # must be true for any actuator commands to work + enabled @0 :Bool; + latActive @11: Bool; + longActive @12: Bool; + + # Final actuator commands + actuators @6 :Actuators; + + # Blinker controls + leftBlinker @15: Bool; + rightBlinker @16: Bool; + + orientationNED @13 :List(Float32); + angularVelocity @14 :List(Float32); + currentCurvature @17 :Float32; # From vehicle model + + driverMonitoringEscalation @18 :Bool; # trigger the car's stock driver monitoring escalation + + cruiseControl @4 :CruiseControl; + hudControl @5 :HUDControl; + + struct Actuators { + # lateral commands, mutually exclusive + torque @2: Float32; # [0.0, 1.0] + steeringAngleDeg @3: Float32; + curvature @7: Float32; + + # longitudinal commands + accel @4: Float32; # m/s^2 + longControlState @5: LongControlState; + + # these are only for logging the actual values sent to the car over CAN + gas @0: Float32; # [0.0, 1.0] + brake @1: Float32; # [0.0, 1.0] + torqueOutputCan @8: Float32; # value sent over can to the car + speed @6: Float32; # m/s + + enum LongControlState @0xe40f3a917d908282{ + off @0; + pid @1; + stopping @2; + starting @3; + } + } + + struct CruiseControl { + cancel @0: Bool; + resume @1: Bool; + override @4: Bool; + + deprecated :group { + speedOverride @2: Float32; + accelOverride @3: Float32; + } + } + + struct HUDControl { + speedVisible @0: Bool; + setSpeed @1: Float32; + lanesVisible @2: Bool; + leadVisible @3: Bool; + visualAlert @4: VisualAlert; + rightLaneVisible @6: Bool; + leftLaneVisible @7: Bool; + rightLaneDepart @8: Bool; + leftLaneDepart @9: Bool; + leadDistanceBars @10: Int8; # 1-3: 1 is closest, 3 is farthest. some ports may utilize 2-4 bars instead + + # not used with the dash, TODO: separate structs for dash UI and device UI + audibleAlert @5: AudibleAlert; + + enum VisualAlert { + # these are the choices from the Honda + # map as good as you can for your car + none @0; + fcw @1; + steerRequired @2; + brakePressed @3; + wrongGear @4; + seatbeltUnbuckled @5; + speedTooHigh @6; + ldw @7; + } + + enum AudibleAlert { + none @0; + + engage @1; + disengage @2; + refuse @3; + + warningSoft @4; + warningImmediate @5; + + prompt @6; + promptRepeat @7; + promptDistracted @8; + } + } + + deprecated :group { + gas @1 :Float32; + brake @2 :Float32; + steeringTorque @3 :Float32; + active @7 :Bool; + roll @8 :Float32; + pitch @9 :Float32; + actuatorsOutput @10 :Actuators; + } +} + +struct CarOutput { + # Any car specific rate limits or quirks applied by + # the CarController are reflected in actuatorsOutput + # and matches what is sent to the car + actuatorsOutput @0 :CarControl.Actuators; +} + +# ****** car param ****** + +struct CarParams { + brand @0 :Text; # Designates which group a platform falls under. Each folder in opendbc/car is assigned one brand string + carFingerprint @1 :Text; + fuzzyFingerprint @55 :Bool; + + notCar @66 :Bool; # flag for non-car robotics platforms + + pcmCruise @3 :Bool; # is openpilot's state tied to the PCM's cruise state? + flags @64 :UInt32; # flags for car specific quirks + alphaLongitudinalAvailable @71 :Bool; + + minEnableSpeed @7 :Float32; + minSteerSpeed @8 :Float32; + steerAtStandstill @77 :Bool; # is steering available at standstill? just check if it faults + safetyConfigs @62 :List(SafetyConfig); + alternativeExperience @65 :Int16; # panda flag for features like no disengage on gas + + # Car docs fields, not used for control + maxLateralAccel @68 :Float32; + autoResumeSng @69 :Bool; # describes whether car can resume from a stop automatically + + # things about the car in the manual + mass @17 :Float32; # [kg] curb weight: all fluids no cargo + wheelbase @18 :Float32; # [m] distance from rear axle to front axle + centerToFront @19 :Float32; # [m] distance from center of mass to front axle + steerRatio @20 :Float32; # [] ratio of steering wheel angle to front wheel angle + steerRatioRear @21 :Float32; # [] ratio of steering wheel angle to rear wheel angle (usually 0) + + # things we can derive + rotationalInertia @22 :Float32; # [kg*m2] body rotational inertia + tireStiffnessFactor @72 :Float32; # scaling factor used in calculating tireStiffness[Front,Rear] + tireStiffnessFront @23 :Float32; # [N/rad] front tire coeff of stiff + tireStiffnessRear @24 :Float32; # [N/rad] rear tire coeff of stiff + + longitudinalTuning @25 :LongitudinalPIDTuning; + lateralTuning :union { + pid @26 :LateralPIDTuning; + indiDEPRECATED @27 :LateralINDITuningDEPRECATED; + lqrDEPRECATED @40 :LateralLQRTuningDEPRECATED; + torque @67 :LateralTorqueTuning; + } + + steerLimitTimer @47 :Float32; # time before steerLimitAlert is issued + + steerControlType @34 :SteerControlType; + radarUnavailable @35 :Bool; # True when radar objects aren't visible on CAN or aren't parsed out + stopAccel @60 :Float32; # Required acceleration to keep vehicle stationary + + steerActuatorDelay @36 :Float32; # Steering wheel actuator delay in seconds + longitudinalActuatorDelay @58 :Float32; # Gas/Brake actuator delay in seconds + openpilotLongitudinalControl @37 :Bool; # is openpilot doing the longitudinal control? + carVin @38 :Text; # VIN number queried during fingerprinting + dashcamOnly @41: Bool; + passive @73: Bool; # is openpilot in control? + transmissionType @43 :TransmissionType; + carFw @44 :List(CarFw); + + radarDelay @74 :Float32; + fingerprintSource @49: FingerprintSource; + networkLocation @50 :NetworkLocation; # Where Panda/C2 is integrated into the car's CAN network + + wheelSpeedFactor @63 :Float32; # Multiplier on wheels speeds to computer actual speeds + + secOcRequired @75 :Bool; # Car requires SecOC message authentication to operate + secOcKeyAvailable @76 :Bool; # Stored SecOC key loaded from params + + struct SafetyConfig { + safetyModel @0 :SafetyModel; + safetyParam @3 :UInt16; + + deprecated :group { + safetyParam @1 :Int16; + safetyParam2 @2 :UInt32; + } + } + + struct LateralParams { + torqueBP @0 :List(Int32); + torqueV @1 :List(Int32); + } + + struct LateralPIDTuning { + kpBP @0 :List(Float32); + kpV @1 :List(Float32); + kiBP @2 :List(Float32); + kiV @3 :List(Float32); + kf @4 :Float32; + } + + struct LateralTorqueTuning { + friction @3 :Float32; + steeringAngleDeadzoneDeg @5 :Float32; + latAccelFactor @6 :Float32; + latAccelOffset @7 :Float32; + + deprecated :group { + useSteeringAngle @0 :Bool; + kp @1 :Float32; + ki @2 :Float32; + kf @4 :Float32; + kd @8 : Float32; + } + } + + struct LongitudinalPIDTuning { + kiBP @2 :List(Float32); + kiV @3 :List(Float32); + + deprecated :group { + kf @6 :Float32; + deadzoneBP @4 :List(Float32); + deadzoneV @5 :List(Float32); + kpBP @0 :List(Float32); + kpV @1 :List(Float32); + } + } + + struct LateralINDITuningDEPRECATED { + outerLoopGainBP @4 :List(Float32); + outerLoopGainV @5 :List(Float32); + innerLoopGainBP @6 :List(Float32); + innerLoopGainV @7 :List(Float32); + timeConstantBP @8 :List(Float32); + timeConstantV @9 :List(Float32); + actuatorEffectivenessBP @10 :List(Float32); + actuatorEffectivenessV @11 :List(Float32); + + deprecated :group { + outerLoopGain @0 :Float32; + innerLoopGain @1 :Float32; + timeConstant @2 :Float32; + actuatorEffectiveness @3 :Float32; + } + } + + struct LateralLQRTuningDEPRECATED { + scale @0 :Float32; + ki @1 :Float32; + dcGain @2 :Float32; + + # State space system + a @3 :List(Float32); + b @4 :List(Float32); + c @5 :List(Float32); + + k @6 :List(Float32); # LQR gain + l @7 :List(Float32); # Kalman gain + } + + enum SafetyModel { + silent @0; + hondaNidec @1; + toyota @2; + elm327 @3; + gm @4; + hondaBoschGiraffe @5; + ford @6; + cadillac @7; + hyundai @8; + chrysler @9; + tesla @10; + subaru @11; + gmPassive @12; + mazda @13; + nissan @14; + volkswagen @15; + toyotaIpas @16; + allOutput @17; + gmAscm @18; + noOutput @19; # like silent but without silent CAN TXs + hondaBosch @20; + volkswagenPq @21; + subaruPreglobal @22; # pre-Global platform + hyundaiLegacy @23; + hyundaiCommunity @24; + volkswagenMlb @25; + hongqi @26; + body @27; + hyundaiCanfd @28; + volkswagenMqbEvo @29; + chryslerCusw @30; + psa @31; + fcaGiorgio @32; + rivian @33; + volkswagenMeb @34; + byd @35; + volvo @36; + bmw @37; + mg @38; + } + + enum SteerControlType { + torque @0; + angle @1; + curvature @2; + } + + enum TransmissionType { + unknown @0; + automatic @1; # Traditional auto, including DSG + manual @2; # True "stick shift" only + direct @3; # Electric vehicle or other direct drive + cvt @4; + } + + struct CarFw { + ecu @0 :Ecu; + fwVersion @1 :Data; + address @2 :UInt32; + subAddress @3 :UInt8; + responseAddress @4 :UInt32; + request @5 :List(Data); + brand @6 :Text; + bus @7 :UInt8; + logging @8 :Bool; + obdMultiplexing @9 :Bool; + } + + enum Ecu { + eps @0; + abs @1; + fwdRadar @2; + fwdCamera @3; + engine @4; + unknown @5; + transmission @8; # Transmission Control Module + hybrid @18; # hybrid control unit, e.g. Chrysler's HCP, Honda's IMA Control Unit, Toyota's hybrid control computer + srs @9; # airbag + gateway @10; # can gateway + hud @11; # heads up display + combinationMeter @12; # instrument cluster + electricBrakeBooster @15; + shiftByWire @16; + adas @19; + cornerRadar @21; + hvac @20; + parkingAdas @7; # parking assist system ECU, e.g. Toyota's IPAS, Hyundai's RSPA, etc. + epb @22; # electronic parking brake + telematics @23; + body @24; # body control module + + # Toyota only + dsu @6; + + # Honda only + vsa @13; # Vehicle Stability Assist + programmedFuelInjection @14; + + debug @17; + } + + enum FingerprintSource { + can @0; + fw @1; + fixed @2; + } + + enum NetworkLocation { + fwdCamera @0; # Standard/default integration at LKAS camera + gateway @1; # Integration at vehicle's CAN gateway + } + + deprecated :group { + lateralParams @48 :LateralParams; + enableGasInterceptor @2 :Bool; + enableCamera @4 :Bool; + enableApgs @6 :Bool; + steerLimitAlert @28 :Bool; + steerRateCost @33 :Float32; + isPandaBlack @39 :Bool; + hasStockCamera @57 :Bool; + safetyParam @10 :Int16; + safetyModel @9 :SafetyModel; + safetyModelPassive @42 :SafetyModel = silent; + minSpeedCan @51 :Float32; + communityFeature @46: Bool; + startingAccelRate @53 :Float32; + steerMaxBP @11 :List(Float32); + steerMaxV @12 :List(Float32); + gasMaxBP @13 :List(Float32); + gasMaxV @14 :List(Float32); + brakeMaxBP @15 :List(Float32); + brakeMaxV @16 :List(Float32); + directAccelControl @30 :Bool; + maxSteeringAngleDeg @54 :Float32; + longitudinalActuatorDelayLowerBound @61 :Float32; + stoppingControl @31 :Bool; # Does the car allow full control even at lows speeds when stopping + radarTimeStep @45: Float32 = 0.05; # time delta between radar updates, 20Hz is very standard + enableDsu @5 :Bool; # driving support unit + enableBsm @56 :Bool; # blind spot monitoring + vEgoStarting @59 :Float32; # Speed at which the car goes into starting state + startAccel @32 :Float32; # Required acceleration to get car moving + startingState @70 :Bool; # Does this car make use of special starting state + vEgoStopping @29 :Float32; # Speed at which the car goes into stopping state + stoppingDecelRate @52 :Float32; # m/s^2/s while trying to stop + } +} diff --git a/canlab/core/data/cereal/custom.capnp b/canlab/core/data/cereal/custom.capnp new file mode 100644 index 0000000..42804e0 --- /dev/null +++ b/canlab/core/data/cereal/custom.capnp @@ -0,0 +1,71 @@ +using Cxx = import "/include/c++.capnp"; +$Cxx.namespace("cereal"); + +@0xb526ba661d550a59; + +# custom.capnp: a home for empty structs reserved for custom forks +# These structs are guaranteed to remain reserved and empty in mainline +# cereal, so use these if you want custom events in your fork. + +# DO rename the structs +# DON'T change the identifier (e.g. @0x81c2f05a394cf4af) + +struct CustomReserved0 @0x81c2f05a394cf4af { +} + +struct CustomReserved1 @0xaedffd8f31e7b55d { +} + +struct CustomReserved2 @0xf35cc4560bbf6ec2 { +} + +struct CustomReserved3 @0xda96579883444c35 { +} + +struct CustomReserved4 @0x80ae746ee2596b11 { +} + +struct CustomReserved5 @0xa5cd762cd951a455 { +} + +struct CustomReserved6 @0xf98d843bfd7004a3 { +} + +struct CustomReserved7 @0xb86e6369214c01c8 { +} + +struct CustomReserved8 @0xf416ec09499d9d19 { +} + +struct CustomReserved9 @0xa1680744031fdb2d { +} + +struct CustomReserved10 @0xcb9fd56c7057593a { +} + +struct CustomReserved11 @0xc2243c65e0340384 { +} + +struct CustomReserved12 @0x9ccdc8676701b412 { +} + +struct CustomReserved13 @0xcd96dafb67a082d0 { +} + +struct CustomReserved14 @0xb057204d7deadf3f { +} + +struct CustomReserved15 @0xbd443b539493bc68 { +} + +struct CustomReserved16 @0xfc6241ed8877b611 { +} + +struct CustomReserved17 @0xa30662f84033036c { +} + +struct CustomReserved18 @0xc86a3d38d13eb3ef { +} + +struct CustomReserved19 @0xa4f1eb3323f5f582 { +} diff --git a/canlab/core/data/cereal/deprecated.capnp b/canlab/core/data/cereal/deprecated.capnp new file mode 100644 index 0000000..b119e70 --- /dev/null +++ b/canlab/core/data/cereal/deprecated.capnp @@ -0,0 +1,787 @@ +using Cxx = import "/include/c++.capnp"; +$Cxx.namespace("cereal"); + +@0x80ef1ec4889c2a63; + +# deprecated.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; +} + +struct LiveTracksDEPRECATED @0xb16f60103159415a { + 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 LiveMpcData @0x92a5e332a85f32a0 { + 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 @0xe7e17c434f865ae2 { + 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 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 NavModelData @0xac3de5c437be057a { + 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 @0xbe09e615b2507e26 { + x @0 :List(Float32); + y @1 :List(Float32); + xStd @2 :List(Float32); + yStd @3 :List(Float32); + } +} + +struct AndroidBuildInfo @0xfe2919d5c21f426c { + 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 @0x9b513b93a887dbcd { + 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 IosBuildInfo @0xd97e3b28239f5580 { + appVersion @0 :Text; + appBuild @1 :UInt32; + osVersion @2 :Text; + deviceModel @3 :Text; +} + +enum FrameTypeDEPRECATED @0xa37f0d8558e193fd { + unknown @0; + neo @1; + chffrAndroid @2; + front @3; +} + +struct AndroidCaptureResult @0xbcc3efbac41d2048 { + sensitivity @0 :Int32; + frameDuration @1 :Int64; + exposureTime @2 :Int64; + rollingShutterSkew @3 :UInt64; + colorCorrectionTransform @4 :List(Int32); + colorCorrectionGains @5 :List(Float32); + displayRotation @6 :Int8; +} + +enum UsbPowerModeDEPRECATED @0xa8883583b32c9877 { + none @0; + client @1; + cdp @2; + dcp @3; +} + +struct LateralINDIState @0x939463348632375e { + 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 LateralLQRState @0x9024e2d790c82ade { + active @0 :Bool; + steeringAngleDeg @1 :Float32; + i @2 :Float32; + output @3 :Float32; + lqrOutput @4 :Float32; + saturated @5 :Bool; + steeringAngleDesiredDeg @6 :Float32; +} + +struct LateralPlannerSolution @0x84caeca5a6b4acfe { + 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 GpsTrajectory @0x8cfeb072f5301000 { + x @0 :List(Float32); + y @1 :List(Float32); +} + +struct AudioFeedbackDEPRECATED @0xed47e3c075be372a { + audio @0 :AudioData; + blockNum @1 :UInt16; + + struct AudioData { + data @0 :Data; + sampleRate @1 :UInt32; + } +} diff --git a/canlab/core/data/cereal/include/c++.capnp b/canlab/core/data/cereal/include/c++.capnp new file mode 100644 index 0000000..2bda547 --- /dev/null +++ b/canlab/core/data/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/canlab/core/data/cereal/log.capnp b/canlab/core/data/cereal/log.capnp new file mode 100644 index 0000000..ab53884 --- /dev/null +++ b/canlab/core/data/cereal/log.capnp @@ -0,0 +1,2716 @@ +using Cxx = import "/include/c++.capnp"; +$Cxx.namespace("cereal"); + +using Car = import "/car.capnp"; +using Deprecated = import "deprecated.capnp"; +using Custom = import "custom.capnp"; + +@0xf3b1f17e25a4285b; + +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; + driverDistracted1 @33; + driverDistracted2 @34; + driverDistracted3 @35; + driverUnresponsive1 @36; + driverUnresponsive2 @37; + driverUnresponsive3 @38; + belowSteerSpeed @39; + 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; + 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; + fanMalfunction @72; + cameraMalfunction @73; + cameraFrameRate @74; + processNotRunning @75; + dashcamMode @76; + selfdriveInitializing @77; + cruiseMismatch @79; + canBusMissing @80; + selfdrivedLagging @81; + resumeBlocked @82; + carNotReady @103; + steerTimeLimit @83; + vehicleSensorsInvalid @84; + locationdTemporaryError @85; + locationdPermanentError @86; + paramsdTemporaryError @87; + paramsdPermanentError @88; + actuatorsApiUnavailable @89; + espActive @90; + personalityChanged @91; + aeb @92; + userBookmark @95; + userBookmarkNotPaired @104; + excessiveActuation @96; + bigModelLoading @100; + bigModelFailed @102; + + lowBatteryDEPRECATED @40; + soundsUnavailableDEPRECATED @47; + deviceFallingDEPRECATED @71; + usbErrorDEPRECATED @78; + audioFeedbackDEPRECATED @97; + bigModelReadyDEPRECATED @101; + } +} + +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; + + enum DeviceType { + unknown @0; + neo @1; # NEO, EON, & comma two + chffrAndroid @2; + chffrIos @3; + tici @4; # comma three + pc @5; + tizi @6; # comma 3X + mici @7; # comma four + } + + struct PandaInfo { + hasPanda @0 :Bool; + dongleId @1 :Text; + stVersion @2 :Text; + espVersion @3 :Text; + } + + struct ChffrAndroidExtra { + allCameraCharacteristics @0 :Map(Text, Text); + } + + deprecated :group { + gctx @1 :Text; + androidBuildInfo @5 :Deprecated.AndroidBuildInfo; + androidSensors @6 :List(Deprecated.AndroidSensor); + chffrAndroidExtra @7 :ChffrAndroidExtra; + iosBuildInfo @14 :Deprecated.IosBuildInfo; + } +} + +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); + + sensor @26 :ImageSensor; + enum ImageSensor { + unknown @0; + ar0231 @1; + ox03c10 @2; + os04c10 @3; + } + + deprecated :group { + frameLength @3 :Int32; + globalGain @5 :Int32; + frameType @7 :Deprecated.FrameTypeDEPRECATED; + androidCaptureResult @9 :Deprecated.AndroidCaptureResult; + lensPos @11 :Int32; + lensSag @12 :Float32; + lensErr @13 :Float32; + lensTruePos @14 :Float32; + focusVal @16 :List(Int16); + focusConf @17 :List(UInt8); + sharpnessScore @18 :List(UInt16); + recoverState @19 :Int32; + } +} + +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; +} + +struct SensorEventData { + timestamp @3 :Int64; + + 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); + + deprecated :group { + 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; + } + + # formerly based on android sensor_event_t + deprecated :group { + version @0 :Int32; + sensor @1 :Int32; + type @2 :Int32; + uncalibrated @10 :Bool; + } +} + +# 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; + + deprecated :group { + busTime @1 :UInt16; + } +} + +struct DeviceState @0xa4d8b5af2aa492eb { + deviceType @45 :InitData.DeviceType; + + # usb + chestnutPresent @51 :Bool; + usbState @52 :UsbState; + + 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; + gnssTempC @48 :Float32; + bottomSocTempC @50 :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 { + ok @0; + warmDEPRECATED @1; + overheated @2; + critical @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 :group { + cpu0 @0 :UInt16; + cpu1 @1 :UInt16; + cpu2 @2 :UInt16; + cpu3 @3 :UInt16; + mem @4 :UInt16; + gpu @5 :UInt16; + bat @6 :UInt32; + pa0 @21 :UInt16; + cpuUsagePercent @20 :Int8; + batteryStatus @9 :Text; + batteryVoltage @16 :Int32; + batteryTempC @29 :Float32; + batteryPercent @8 :Int16; + batteryCurrent @15 :Int32; + chargingError @17 :Bool; + chargingDisabled @18 :Bool; + usbOnline @12 :Bool; + ambientTempC @30 :Float32; + nvmeTempC @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; + soundOutputLevel @37 :UInt16; + + # 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; + + # these fields are not used by openpilot, but they're + # reserved for forks building alternate experiences. + controlsAllowedRESERVED1 @38 :Bool; + controlsAllowedRESERVED2 @39 :Bool; + + 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; + } + } + + deprecated :group { + gasInterceptorDetected @4 :Bool; + startedSignalDetected @5 :Bool; + hasGps @6 :Bool; + gmlanSendErrs @9 :UInt32; + fanSpeedRpm @11 :UInt16; + usbPowerMode @12 :Deprecated.UsbPowerModeDEPRECATED; + safetyParam @20 :Int16; + safetyParam2 @26 :UInt32; + fanStallCount @34 :UInt8; + } +} + +struct PeripheralState { + pandaType @0 :PandaState.PandaType; + voltage @1 :UInt32; + current @2 :UInt32; + fanSpeedRpm @3 :UInt16; + + deprecated :group { + usbPowerMode @4 :Deprecated.UsbPowerModeDEPRECATED; + } +} + +struct UsbState { + devices @0 :List(Device); + + struct Device { + busnum @0 :UInt8; + devnum @1 :UInt8; + vendorId @2 :UInt16; + productId @3 :UInt16; + speedMbps @4 :UInt16; + manufacturer @6 :Text; + product @5 :Text; + linkErrorCount @7 :UInt16; + usb3Lane @8 :Usb3Lane; + + enum Usb3Lane { + unknown @0; + a @1; + b @2; + } + } +} + +struct ChestnutState { + tempC @0 :Float32; + memoryTempC @1 :Float32; + powerDrawW @2 :Float32; + powerLimitW @3 :Float32; + gpuUsagePercent @4 :UInt8; + gpuClockMhz @5 :UInt16; + fanSpeedRpm @6 :UInt16; + pcieLtssm @7 :UInt8; + supplyVoltage @8 :UInt16; # mV + supplyCurrent @9 :Int16; # mA + supplyFault @10 :Bool; +} + +struct RadarState @0x9a185389d6fdd05f { + mdMonoTime @6 :UInt64; # for debugging + radarErrors @13 :Car.RadarData.Error; + + leadOne @3 :LeadData; + leadTwo @4 :LeadData; + + struct LeadData { + dRel @0 :Float32; # m from the front bumper of the car + yRel @1 :Float32; # m in car frame, left positive + vRel @2 :Float32; # m/s relative longitudinal speed + vLead @4 :Float32; # m/s absolute lead speed + vLeadK @8 :Float32; # kalman-filtered lead speed + aLeadK @9 :Float32; # kalman-filtered lead accel + present @11 :Bool; # true if a lead is present + aLeadTau @12 :Float32; # lead accel time constant + modelProb @13 :Float32; # vision model lead probability + radar @14 :Bool; # true if lead is radar-matched (vs vision-only) + radarTrackId @15 :Int32 = -1; # for debugging + + deprecated :group { + aRel @3 :Float32; + aLead @5 :Float32; + dPath @6 :Float32; + vLat @7 :Float32; + fcw @10 :Bool; + } + } + + deprecated :group { + ftMonoTime @7 :UInt64; + warpMatrix @0 :List(Float32); + angleOffset @1 :Float32; + calStatus @2 :Int8; + calCycle @8 :Int32; + calPerc @9 :Int8; + canMonoTimes @10 :List(UInt64); + cumLagMs @5 :Float32; + carStateMonoTime @11 :UInt64; + radarErrors @12 :List(Car.RadarData.ErrorDEPRECATED); + } +} + +struct ExtrinsicsCalibration @0x96df70754d8390bc { + 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); + + + enum Status { + uncalibrated @0; + calibrated @1; + invalid @2; + recalibrating @3; + } + + deprecated :group { + warpMatrix @0 :List(Float32); + calStatus @1 :Int8; + warpMatrix2 @5 :List(Float32); + warpMatrixBig @6 :List(Float32); + } +} + +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 @13 :AudibleAlert; + alertHudVisual @12 :Car.CarControl.HUDControl.VisualAlert; + + # configurable driving settings + experimentalMode @10 :Bool; + personality @11 :LongitudinalPersonality; + + enum AudibleAlert { + none @0; + + engage @1; + disengage @2; + refuse @3; + + warningSoft @4; + warningImmediate @5; + + prompt @6; + promptRepeat @7; + promptDistracted @8; + + preAlert @9; + complete @10; + } + + 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; + } + + deprecated :group { + alertSound @8 :Car.CarControl.HUDControl.AudibleAlert; + } +} + +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; + + curvatureState @65 :LateralCurvatureState; + lqrStateDEPRECATED @55 :Deprecated.LateralLQRState; + indiStateDEPRECATED @52 :Deprecated.LateralINDIState; + } + + 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 LateralAngleState { + active @0 :Bool; + steeringAngleDeg @1 :Float32; + output @2 :Float32; + saturated @3 :Bool; + steeringAngleDesiredDeg @4 :Float32; + } + + struct LateralDebugState { + active @0 :Bool; + steeringAngleDeg @1 :Float32; + output @2 :Float32; + saturated @3 :Bool; + } + + struct LateralCurvatureState @0xad9d8095c06f7c61 { + 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; + } + + deprecated :group { + vEgo @0 :Float32; + vEgoRaw @32 :Float32; + aEgo @1 :Float32; + canMonoTime @16 :UInt64; + radarStateMonoTime @17 :UInt64; + mdMonoTime @18 :UInt64; + yActual @6 :Float32; + yDes @7 :Float32; + upSteer @8 :Float32; + uiSteer @9 :Float32; + ufSteer @34 :Float32; + aTargetMin @10 :Float32; + aTargetMax @11 :Float32; + rearViewCam @23 :Bool; + driverMonitoringOn @43 :Bool; + hudLead @14 :Int32; + alertSound @45 :Text; + angleModelBias @27 :Float32; + gpsPlannerActive @40 :Bool; + decelForTurn @47 :Bool; + decelForModel @54 :Bool; + awarenessStatus @26 :Float32; + angleSteers @13 :Float32; + vCurvature @46 :Float32; + mapValid @49 :Bool; + jerkFactor @12 :Float32; + steerOverride @20 :Bool; + steeringAngleDesiredDeg @29 :Float32; + canMonoTimes @21 :List(UInt64); + desiredCurvatureRate @62 :Float32; + canErrorCounter @57 :UInt32; + vPid @2 :Float32; + alertBlinkingRate @42 :Float32; + alertText1 @24 :Text; + alertText2 @25 :Text; + alertStatus @38 :SelfdriveState.AlertStatus; + alertSize @39 :SelfdriveState.AlertSize; + alertType @44 :Text; + alertSound2 @56 :SelfdriveState.AudibleAlert; + engageable @41 :Bool; # can OP be engaged? + state @31 :SelfdriveState.OpenpilotState; + enabled @19 :Bool; + active @36 :Bool; + experimentalMode @64 :Bool; + personality @66 :LongitudinalPersonality; + vCruise @22 :Float32; # actual set speed + vCruiseCluster @63 :Float32; # set speed to display in the UI + startMonoTime @48 :UInt64; + cumLagMs @15 :Float32; + aTarget @35 :Float32; + vTargetLead @3 :Float32; + } +} + +struct DrivingModelData { + frameId @0 :UInt32; + frameIdExtra @1 :UInt32; + frameDropPerc @6 :Float32; + modelExecutionTime @7 :Float32; + big @8 :Bool; + + 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; + big @27 :Bool; + + # 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 + leadsV3 @18 :List(LeadDataV3); + + meta @12 :MetaData; + confidence @23: ConfidenceClass; + + # e2e lateral planner + action @26: Action; + + lateralPlannerSolutionDEPRECATED @25: Deprecated.LateralPlannerSolution; + leadsDEPRECATED @11 :List(LeadDataV2DEPRECATED); + + struct LeadDataV2DEPRECATED { + 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 :group { + brakeDisengageProb @2 :Float32; + gasDisengageProb @3 :Float32; + steerOverrideProb @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 Action { + desiredCurvature @0 :Float32; + desiredAcceleration @1 :Float32; + shouldStop @2 :Bool; + } + + deprecated :group { + temporalPose @21 :Pose; + gpuExecutionTime @17 :Float32; + navEnabled @22 :Bool; + locationMonoTime @24 :UInt64; + } +} + +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 OperatingSystemLogEntry { + 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; + + + solverExecutionTime @35 :Float32; + + enum LongitudinalPlanSource { + cruise @0; + lead0 @1; + lead1 @2; + lead2 @3; + e2e @4; + } + + + deprecated :group { + vCruise @16 :Float32; + aCruise @17 :Float32; + vTarget @3 :Float32; + vTargetFuture @14 :Float32; + vStart @26 :Float32; + aStart @27 :Float32; + vMax @20 :Float32; + radarStateMonoTime @10 :UInt64; + jerkFactor @6 :Float32; + hasLeftLane @23 :Bool; + hasRightLane @24 :Bool; + aTargetMin @4 :Float32; + aTargetMax @5 :Float32; + lateralValid @0 :Bool; + longitudinalValid @2 :Bool; + dPoly @1 :List(Float32); + laneWidth @11 :Float32; + vCurvature @21 :Float32; + decelForTurn @22 :Bool; + mapValid @25 :Bool; + radarValid @28 :Bool; + radarCanError @30 :Bool; + commIssue @31 :Bool; + events @13 :List(Car.OnroadEventDEPRECATED); + gpsTrajectory @12 :Deprecated.GpsTrajectory; + gpsPlannerActive @19 :Bool; + personality @36 :LongitudinalPersonality; + } +} +struct UiPlan { + frameId @2 :UInt32; + position @0 :XYZTData; + accel @1 :List(Float32); +} + +struct LateralPlan @0xe1e9318e2ae8b51e { + modelMonoTime @31 :UInt64; + dPathPoints @20 :List(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 :group { + laneWidth @0 :Float32; + lProb @5 :Float32; + rProb @7 :Float32; + dProb @21 :Float32; + curvature @22 :Float32; + curvatureRate @23 :Float32; + rawCurvature @24 :Float32; + rawCurvatureRate @25 :Float32; + cProb @3 :Float32; + dPoly @1 :List(Float32); + cPoly @2 :List(Float32); + lPoly @4 :List(Float32); + rPoly @6 :List(Float32); + modelValid @12 :Bool; + commIssue @15 :Bool; + posenetValid @16 :Bool; + sensorValid @14 :Bool; + paramsValid @10 :Bool; + steeringAngleDeg @8 :Float32; # deg + steeringRateDeg @13 :Float32; # deg/s + angleOffsetDeg @11 :Float32; + } +} + +struct LiveLocationKalman { + + # More info on reference frames: + # https://github.com/commaai/openpilot/tree/master/openpilot/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 DeviceMotion @0xc24ca2b57206b44d { + # More info on reference frames: + # https://github.com/commaai/openpilot/tree/master/openpilot/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; + + timestamp @8 :UInt64; + + 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; + + # from /proc//smaps_rollup (proportional/private memory) + memPss @17 :UInt64; # Pss — shared pages split by mapper count + memPssAnon @18 :UInt64; # Pss_Anon — private anonymous (heap, stack) + memPssShmem @19 :UInt64; # Pss_Shmem — proportional MSGQ/tmpfs share + } + + 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); + + deprecated :group { + ephemerisSource @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; + 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; + + deprecated :group { + gpsWeek @28 :Float64; + } + } + + 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; + tb @20 :UInt16; + + tauN @21 :Float64; + deltaTauN @22 :Float64; + gammaN @23 :Float64; + + p1 @24 :UInt8; + p2 @25 :UInt8; + p3 @26 :UInt8; + p4 @27 :UInt8; + + + n4 @29 :UInt8; + nt @30 :UInt16; + freqNum @31 :Int16; + tkSeconds @32 :UInt32; + + deprecated :group { + tk @19 :UInt16; + freqNum @28 :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 + + deprecated :group { + bootTimeNanos @0 :UInt64; + monotonicNanos @1 :UInt64; + monotonicRawNanos @2 :UInt64; + modemUptimeMillis @4 :UInt64; + } +} + +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; + sleepProb @14 :Float32; + + deprecated :group { + notReadyProb @12 :List(Float32); + occludedProb @10 :Float32; + readyProb @11 :List(Float32); + } + } + + deprecated :group { + dspExecutionTime @2 :Float32; + poorVisionProb @4 :Float32; + } +} + +struct DriverMonitoringStateDEPRECATED @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; + + deprecated :group { + phoneProbOffset @20 :Float32; + phoneProbValidCount @21 :UInt32; + isPreview @15 :Bool; + rhdChecked @5 :Bool; + events @0 :List(Car.OnroadEventDEPRECATED); + } +} + +struct DriverMonitoringState { + lockout @0 :Bool; + lockoutCount @15 :Int8; + lockoutMinutesRemaining @11 :Int8; + alert3Count @12 :Int8; + noResponseCount @13 :Int8; + noResponseForceDecel @14 :Bool; + + alwaysOn @3 :Bool; + alwaysOnLockout @4 :Bool; + + alertLevel @5 :AlertLevel; + activePolicy @6 :MonitoringPolicy; + isRHD @7 :Bool; + rhdCalibration @8 :CalibrationState; + + visionPolicyState @9 :VisionPolicyState; + wheeltouchPolicyState @10 :WheeltouchPolicyState; + + enum AlertLevel { + # ordinal must match the name to prevent bugs + # comparing against the raw ordinal value + none @0; + one @1; + two @2; + three @3; + } + + enum MonitoringPolicy { + wheeltouch @0; + vision @1; + } + + struct VisionPolicyState { + awarenessPercent @0 :Int8; + awarenessStep @1 :Float32; + isDistracted @2 :Bool; + distractedTypes @3 :DistractedTypes; + + faceDetected @4 :Bool; + pose @5 :Pose; + wheeltouchFallbackPercent @6 :Int8; + uncertainOffroadAlertPercent @7 :Int8; + + struct DistractedTypes { + pose @0: Bool; + eye @1: Bool; + phone @2: Bool; + sleep @3: Bool; + } + + struct Pose { + pitch @0 :Float32; + yaw @1 :Float32; + pitchCalib @2 :CalibrationState; + yawCalib @3 :CalibrationState; + calibrated @4 :Bool; + uncertainty @5 :Float32; + } + } + + struct WheeltouchPolicyState { + awarenessPercent @0 :Int8; + awarenessStep @1 :Float32; + driverInteracting @2 :Bool; + } + + struct CalibrationState { + calibratedPercent @0 :Int8; + offset @1 :Float32; + } + + deprecated :group { + alertCountLockoutPercent @1 :Int8; + alertTimeLockoutPercent @2 :Int8; + } +} + +struct Boot { + wallTimeNanos @0 :UInt64; + pstore @4 :Map(Text, Data); + commands @5 :Map(Text, Data); + launchLog @3 :Text; + + deprecated :group { + lastKmsg @1 :Data; + lastPmsg @2 :Data; + } +} + +struct VehicleParameters @0xd9058dcb967c2753 { + 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; + + + struct FilterState { + value @0 : List(Float64); + std @1 : List(Float64); + } + + deprecated :group { + yawRate @7 :Float32; + filterState @15 :LiveLocationKalman.Measurement; + } +} + +struct LateralTorqueParameters @0xe61690eb0b091692 { + valid @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 LateralDelay @0x98dfdb22c44df8d4 { + 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 { + cpuTimeMillis @0 :Float32; + frameTimeMillis @1 :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 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; + + deprecated :group { + filteredSoundPressureWeightedDb @2 :Float32; + } +} + +struct AudioData { + data @0 :Data; + sampleRate @1 :UInt32; +} + +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 ********** + 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; + radarTracks @131 :Car.RadarData; + sendcan @17 :List(CanData); + extrinsicsCalibration @19 :ExtrinsicsCalibration; + carState @22 :Car.CarState; + carControl @23 :Car.CarControl; + carOutput @127 :Car.CarOutput; + longitudinalPlan @24 :LongitudinalPlan; + driverAssistance @132 :DriverAssistance; + ubloxGnss @34 :UbloxGnss; + ubloxRaw @39 :Data; + qcomGnss @31 :QcomGnss; + gpsLocationExternal @48 :GpsLocationData; + gpsLocation @21 :GpsLocationData; + vehicleParameters @61 :VehicleParameters; + lateralTorqueParameters @94 :LateralTorqueParameters; + lateralDelay @146 : LateralDelay; + cameraOdometry @63 :CameraOdometry; + thumbnail @66: Thumbnail; + onroadEvents @134: List(OnroadEvent); + carParams @69: Car.CarParams; + driverMonitoringState @151 :DriverMonitoringState; + deviceMotion @129 :DeviceMotion; + modelV2 @75 :ModelDataV2; + drivingModelData @128 :DrivingModelData; + driverStateV2 @92 :DriverStateV2; + + # camera stuff, each camera state has a matching encode idx + narrowRoadCameraState @2 :FrameData; + cabinCameraState @70: FrameData; + wideRoadCameraState @74: FrameData; + narrowRoadEncodeIdx @15 :EncodeIndex; + cabinEncodeIdx @76 :EncodeIndex; + wideRoadEncodeIdx @77 :EncodeIndex; + qNarrowRoadEncodeIdx @90 :EncodeIndex; + + livestreamNarrowRoadEncodeIdx @117 :EncodeIndex; + livestreamWideRoadEncodeIdx @118 :EncodeIndex; + livestreamCabinEncodeIdx @119 :EncodeIndex; + + # microphone data + soundPressure @103 :SoundPressure; + rawAudioData @147 :AudioData; + + # systems stuff + operatingSystemLog @20 :OperatingSystemLogEntry; + managerState @78 :ManagerState; + procLog @33 :ProcLog; + clocks @35 :Clocks; + deviceState @6 :DeviceState; + chestnutState @152 :ChestnutState; + chestnutGpuState @153 :ChestnutState; + logMessage @18 :Text; + errorLogMessage @85 :Text; + + # touch frame + touch @135 :List(Touch); + + # UI services + uiDebug @102 :UIDebug; + + # driving feedback + userBookmark @93 :UserBookmark; + bookmarkButton @148 :UserBookmark; + + lateralManeuverPlan @150 :LateralManeuverPlan; + + # *********** debug *********** + testJoystick @52 :Joystick; + narrowRoadEncodeData @86 :EncodeData; + cabinEncodeData @87 :EncodeData; + wideRoadEncodeData @88 :EncodeData; + qNarrowRoadEncodeData @89 :EncodeData; + alertDebug @133 :DebugAlert; + + livestreamNarrowRoadEncodeData @120 :EncodeData; + livestreamWideRoadEncodeData @121 :EncodeData; + livestreamCabinEncodeData @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 + customReserved0 @107 :Custom.CustomReserved0; + customReserved1 @108 :Custom.CustomReserved1; + customReserved2 @109 :Custom.CustomReserved2; + customReserved3 @110 :Custom.CustomReserved3; + customReserved4 @111 :Custom.CustomReserved4; + customReserved5 @112 :Custom.CustomReserved5; + customReserved6 @113 :Custom.CustomReserved6; + customReserved7 @114 :Custom.CustomReserved7; + customReserved8 @115 :Custom.CustomReserved8; + customReserved9 @116 :Custom.CustomReserved9; + customReserved10 @136 :Custom.CustomReserved10; + customReserved11 @137 :Custom.CustomReserved11; + customReserved12 @138 :Custom.CustomReserved12; + customReserved13 @139 :Custom.CustomReserved13; + customReserved14 @140 :Custom.CustomReserved14; + customReserved15 @141 :Custom.CustomReserved15; + customReserved16 @142 :Custom.CustomReserved16; + customReserved17 @143 :Custom.CustomReserved17; + customReserved18 @144 :Custom.CustomReserved18; + customReserved19 @145 :Custom.CustomReserved19; + + # *********** legacy + deprecated *********** + model @9 :Deprecated.ModelData; # TODO: rename modelV2 and mark this as deprecated + audioFeedbackDEPRECATED @149 :Deprecated.AudioFeedbackDEPRECATED; + liveMpcDEPRECATED @36 :Deprecated.LiveMpcData; + liveLongitudinalMpcDEPRECATED @37 :Deprecated.LiveLongitudinalMpcData; + liveLocationKalmanDeprecatedDEPRECATED @51 :Deprecated.LiveLocationData; + orbslamCorrectionDEPRECATED @45 :Deprecated.OrbslamCorrection; + liveUIDEPRECATED @14 :Deprecated.LiveUI; + sensorEventDEPRECATED @4 :SensorEventData; + liveEventDEPRECATED @8 :List(Deprecated.LiveEventData); + liveLocationDEPRECATED @25 :Deprecated.LiveLocationData; + ethernetDataDEPRECATED @26 :List(Deprecated.EthernetPacket); + cellInfoDEPRECATED @28 :List(Deprecated.CellInfo); + wifiScanDEPRECATED @29 :List(Deprecated.WifiScan); + uiNavigationEventDEPRECATED @50 :Deprecated.UiNavigationEvent; + liveMapDataDEPRECATED @62 :LiveMapDataDEPRECATED; + gpsPlannerPointsDEPRECATED @40 :Deprecated.GPSPlannerPoints; + gpsPlannerPlanDEPRECATED @41 :Deprecated.GPSPlannerPlan; + applanixRawDEPRECATED @42 :Data; + androidGnssDEPRECATED @30 :Deprecated.AndroidGnss; + lidarPtsDEPRECATED @32 :Deprecated.LidarPts; + navStatusDEPRECATED @38 :Deprecated.NavStatus; + trafficEventsDEPRECATED @43 :List(Deprecated.TrafficEvent); + liveLocationTimingDEPRECATED @44 :Deprecated.LiveLocationData; + liveLocationCorrectedDEPRECATED @46 :Deprecated.LiveLocationData; + navUpdateDEPRECATED @27 :Deprecated.NavUpdate; + orbObservationDEPRECATED @47 :List(Deprecated.OrbObservation); + locationDEPRECATED @49 :Deprecated.LiveLocationData; + orbOdometryDEPRECATED @53 :Deprecated.OrbOdometry; + orbFeaturesDEPRECATED @54 :Deprecated.OrbFeatures; + applanixLocationDEPRECATED @55 :Deprecated.LiveLocationData; + orbKeyFrameDEPRECATED @56 :Deprecated.OrbKeyFrame; + orbFeaturesSummaryDEPRECATED @58 :Deprecated.OrbFeaturesSummary; + featuresDEPRECATED @10 :Deprecated.CalibrationFeatures; + kalmanOdometryDEPRECATED @65 :Deprecated.KalmanOdometry; + uiLayoutStateDEPRECATED @57 :Deprecated.UiLayoutState; + pandaStateDEPRECATED @12 :PandaState; + driverStateDEPRECATED @59 :Deprecated.DriverStateDEPRECATED; + sensorEventsDEPRECATED @11 :List(SensorEventData); + lateralPlanDEPRECATED @64 :LateralPlan; + navModelDEPRECATED @104 :Deprecated.NavModelData; + uiPlanDEPRECATED @106 :UiPlan; + liveLocationKalmanDEPRECATED @72 :LiveLocationKalman; + liveTracksDEPRECATED @16 :List(Deprecated.LiveTracksDEPRECATED); + onroadEventsDEPRECATED @68: List(Car.OnroadEventDEPRECATED); + gyroscope2DEPRECATED @100 :SensorEventData; + accelerometer2DEPRECATED @101 :SensorEventData; + temperatureSensor2DEPRECATED @123 :SensorEventData; + driverMonitoringStateDEPRECATED @71 :DriverMonitoringStateDEPRECATED; + gpsNMEADEPRECATED @3 :GPSNMEAData; + uploaderStateDEPRECATED @79 :UploaderState; + navInstructionDEPRECATED @82 :NavInstruction; + navRouteDEPRECATED @83 :NavRoute; + navThumbnailDEPRECATED @84 :Thumbnail; + gnssMeasurementsDEPRECATED @91 :GnssMeasurements; + mapRenderStateDEPRECATED @105: MapRenderState; + } +} diff --git a/canlab/core/log_parser.py b/canlab/core/log_parser.py index 4f17501..de56989 100644 --- a/canlab/core/log_parser.py +++ b/canlab/core/log_parser.py @@ -400,12 +400,23 @@ def _get(mdf, name): # ── dispatch ───────────────────────────────────────────────────────────────── +def is_openpilot_log(path) -> bool: + """openpilot names its logs rlog and qlog, usually as rlog.bz2 or rlog.zst, + so the suffix alone (".bz2", or none) says nothing.""" + p = Path(path) + name = p.name.lower() + stem = name.split(".", 1)[0] + return (p.suffix.lower() in (".rlog", ".qlog") + or stem in ("rlog", "qlog") + or name.endswith((".rlog.bz2", ".qlog.bz2", ".rlog.zst", ".qlog.zst"))) + + def parse_log_file(filepath: str) -> pd.DataFrame: """Auto-detect the capture format from the suffix/header and parse it.""" path = Path(filepath) suffix = path.suffix.lower() try: - if suffix in (".rlog", ".qlog"): + if is_openpilot_log(path): from canlab.core.openpilot_parser import parse_rlog return parse_rlog(filepath) if suffix in (".pcap", ".pcapng"): diff --git a/canlab/core/openpilot_parser.py b/canlab/core/openpilot_parser.py index 66c0369..b420316 100644 --- a/canlab/core/openpilot_parser.py +++ b/canlab/core/openpilot_parser.py @@ -1,107 +1,156 @@ """ -Parse openpilot .rlog / .qlog files into a standard CAN frames DataFrame. - -Requires pycapnp. Gracefully returns empty DataFrame if missing. - -openpilot log format (simplified): - Each log entry is a capnp-encoded Event with a union field. - CAN frames live in Event.can[], each entry has: - address, busTime, dat (bytes), src (bus index). +Read openpilot logs (rlog, qlog; plain, .bz2 or .zst) into CanLab frames. + +An openpilot log is a stream of capnp ``Event`` messages. CAN traffic is the +``can`` event: a list of frames with an address, the payload and ``src``, the +panda's bus number. The schema is comma.ai's cereal, vendored under +``core/data/cereal`` (MIT, see the NOTICE there), so only pycapnp is needed. + +Three things the previous reader got wrong, each of which stopped a real log +from opening: + +- Logs are distributed compressed (``rlog.bz2``, ``rlog.zst``) and were read + as raw capnp. +- The schema was looked for in places nobody has it, and when found was + loaded without an import path, which its absolute imports need. +- A panda echoes the frames openpilot itself sends with ``src`` 128 plus the + bus. Those are openpilot's transmissions, not the car's traffic; they are + now left out of the capture unless asked for, and counted. + +The same log carries the car's GPS, which is a ready reference for the +calibrator: ``gps_reference`` returns it as ``ReferenceSeries``. """ +from __future__ import annotations + +import bz2 +import logging from pathlib import Path + import pandas as pd -import logging log = logging.getLogger(__name__) +SCHEMA_DIR = Path(__file__).resolve().parent / "data" / "cereal" +#: panda marks a frame it transmitted with 128 added to the bus number +SENT_FLAG = 0x80 + _CAPNP_AVAILABLE = False try: import capnp # noqa: F401 _CAPNP_AVAILABLE = True except ImportError: - log.debug("suppressed exception", exc_info=True) + log.debug("pycapnp not installed", exc_info=True) def is_available() -> bool: return _CAPNP_AVAILABLE -def _normalize_id(val: int) -> str: - return format(val, "03X") +def _schema(): + import capnp + capnp.remove_import_hook() + path = SCHEMA_DIR / "log.capnp" + if not path.is_file(): + raise FileNotFoundError(f"cereal schema missing at {path}") + return capnp.load(str(path), imports=[str(SCHEMA_DIR)]) -def parse_rlog(filepath: str) -> pd.DataFrame: - """ - Parse an openpilot .rlog or .qlog file → standard CAN DataFrame. +_LOG = None - Raises RuntimeError if pycapnp is not installed, the cereal schema is - missing, or the log cannot be decoded. Returns an empty DataFrame only when - the log genuinely contains no CAN events. - """ - if not _CAPNP_AVAILABLE: - raise RuntimeError( - "pycapnp not installed. " - "Run: pip install pycapnp --break-system-packages" - ) - path = Path(filepath) +def schema(): + """The loaded cereal ``log`` module, cached.""" + global _LOG + if _LOG is None: + _LOG = _schema() + return _LOG - # openpilot logs are a concatenated stream of capnp-encoded Event messages. - # Decoding requires the cereal `log.capnp` schema. If parsing fails we raise - # rather than fabricating frames from arbitrary bytes (the old heuristic - # fallback emitted garbage "frames" that looked real). - try: - rows = _parse_with_cereal(path) - except FileNotFoundError as e: - raise RuntimeError( - "openpilot cereal schema (log.capnp) not found. Install openpilot's " - "cereal or place log.capnp under canlab/resources/. An rlog cannot be " - "decoded without it." - ) from e - except Exception as e: - raise RuntimeError(f"Failed to parse openpilot rlog: {e}") from e - from canlab.core.log_parser import _finish - return _finish(rows) +def read_bytes(path) -> bytes: + """The raw event stream of a log, decompressed if it needs to be.""" + p = Path(path) + data = p.read_bytes() + if data[:3] == b"BZh": + return bz2.decompress(data) + if data[:4] == b"\x28\xb5\x2f\xfd": # zstd frame magic + try: + import zstandard + except ImportError as e: + raise RuntimeError("This log is zstd-compressed; pip install zstandard " + "to read it, or decompress it first.") from e + return zstandard.ZstdDecompressor().decompressobj().decompress(data) + return data -def _parse_with_cereal(path: Path) -> list: - """Try to read using cereal capnp schemas bundled with openpilot.""" - import capnp - # Look for cereal schema in common openpilot locations - import os - schema_candidates = [ - os.path.expanduser("~/openpilot/cereal/log.capnp"), - "/opt/openpilot/cereal/log.capnp", - str(Path(__file__).parent.parent / "resources" / "log.capnp"), - ] - schema_path = next((p for p in schema_candidates if os.path.exists(p)), None) - if not schema_path: - raise FileNotFoundError("cereal schema (log.capnp) not found") - - log_capnp = capnp.load(schema_path) - rows = [] - ts_base = 0.0 - - # Use pycapnp's streaming reader over the concatenated message stream rather - # than a hand-rolled length-prefix framing (which did not match the real - # format). - with open(path, "rb") as f: - for event in log_capnp.Event.read_multiple(f): - try: - if event.which() != "can": - continue - ts = event.logMonoTime / 1e9 - if ts_base == 0.0: - ts_base = ts - for frame in event.can: - from canlab.core.log_parser import make_row - dat = bytes(frame.dat)[:64] - rows.append(make_row(ts - ts_base, frame.address, - int(frame.address) > 0x7FF, frame.src, dat)) - except Exception: - continue +def events(path): + """Every Event in a log, in order.""" + return schema().Event.read_multiple_bytes(read_bytes(path)) - return rows +def parse_rlog(filepath: str, include_sent: bool = False) -> pd.DataFrame: + """ + An openpilot log's CAN traffic as a standard frames DataFrame. + + Timestamps are seconds from the first event. ``Bus`` is the panda bus. + Frames openpilot transmitted (``src`` >= 128) are dropped unless + ``include_sent``; how many were dropped is in ``df.attrs["sent_frames"]``. + Raises RuntimeError if pycapnp is missing or the file is not a log. + """ + if not _CAPNP_AVAILABLE: + raise RuntimeError("Reading openpilot logs needs pycapnp: " + "pip install canlab[openpilot]") + from canlab.core.log_parser import _finish, make_row + path = Path(filepath) + try: + stream = events(path) + rows, sent, t0 = [], 0, None + for event in stream: + if event.which() != "can": + continue + ts = event.logMonoTime / 1e9 + if t0 is None: + t0 = ts + for frame in event.can: + src = int(frame.src) + if src & SENT_FLAG and not include_sent: + sent += 1 + continue + address = int(frame.address) + rows.append(make_row(ts - t0, address, address > 0x7FF, src, + bytes(frame.dat)[:64])) + except RuntimeError: + raise + except Exception as e: + raise RuntimeError(f"{path.name} is not an openpilot log this schema can read: {e}") from e + df = _finish(rows) + df.attrs["sent_frames"] = sent + df.attrs["t0_mono"] = t0 + return df + + +def gps_reference(filepath: str): + """The log's GPS as reference series on the same clock as ``parse_rlog``: + speed (m/s), latitude, longitude and altitude, from gpsLocationExternal + (the external receiver) or, failing that, gpsLocation.""" + from canlab.core.reference_series import ReferenceSeries + t0, rows = None, {"gpsLocationExternal": [], "gpsLocation": []} + for event in events(filepath): + kind = event.which() + if t0 is None and kind == "can": + t0 = event.logMonoTime / 1e9 + if kind in rows: + g = getattr(event, kind) + rows[kind].append((event.logMonoTime / 1e9, g.speed, g.latitude, + g.longitude, g.altitude)) + source = "gpsLocationExternal" if rows["gpsLocationExternal"] else "gpsLocation" + fixes = rows[source] + if not fixes or t0 is None: + return [] + ts = [r[0] - t0 for r in fixes] + name = Path(filepath).name + return [ReferenceSeries("speed", ts, [r[1] for r in fixes], unit="m/s", source=name, + meta={"from": source}), + ReferenceSeries("latitude", ts, [r[2] for r in fixes], unit="deg", source=name), + ReferenceSeries("longitude", ts, [r[3] for r in fixes], unit="deg", source=name), + ReferenceSeries("altitude", ts, [r[4] for r in fixes], unit="m", source=name)] diff --git a/canlab/mainwindow.py b/canlab/mainwindow.py index 732d25f..b0b7873 100644 --- a/canlab/mainwindow.py +++ b/canlab/mainwindow.py @@ -1485,7 +1485,7 @@ def _open_settings(self, tab: str = ""): def _open_rlog(self): path, _ = QFileDialog.getOpenFileName( self, "Open openpilot Log", "", - "openpilot Logs (*.rlog *.qlog);;All Files (*)" + "openpilot Logs (rlog qlog *.rlog *.qlog *.bz2 *.zst);;All Files (*)" ) if path: self._load_log_file(path) diff --git a/pyproject.toml b/pyproject.toml index fab5036..58c96ac 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -31,6 +31,8 @@ ai = ["anthropic==0.116.0", "groq==1.5.0", "openai==3.13.0"] mdf = ["asammdf>=7.0.0"] rest = ["fastapi==0.139.0", "uvicorn==0.51.0", "pydantic==2.13.4"] mcp = ["mcp==1.28.1"] +# openpilot rlog/qlog import; the schema ships with CanLab +openpilot = ["pycapnp==2.2.4"] # Serial (slcan) and candleLight (gs_usb) adapters; the other backends need vendor drivers. adapters = ["pyserial==3.5", "gs_usb==0.3.1"] dev = ["pytest==9.1.1", "ruff==0.16.7", "httpx==0.28.1", "pyinstaller", "lupa"] @@ -51,7 +53,7 @@ canlab-cli = "canlab.cli:main" include = ["canlab*"] [tool.setuptools.package-data] -canlab = ["canlab.png", "assets/*.svg", "sample_data/*.csv", "examples/capture-kit/*", "core/data/*"] +canlab = ["canlab.png", "assets/*.svg", "sample_data/*.csv", "examples/capture-kit/*", "core/data/*", "core/data/cereal/*", "core/data/cereal/include/*"] [tool.ruff] line-length = 120 diff --git a/tests/real_data/acceptance_new_sources.py b/tests/real_data/acceptance_new_sources.py index d7d864b..5617af3 100644 --- a/tests/real_data/acceptance_new_sources.py +++ b/tests/real_data/acceptance_new_sources.py @@ -774,6 +774,87 @@ def decodes(): check("the signal it writes decodes the car", decodes) +OPENPILOT_RLOG = ("https://commadataci.blob.core.windows.net/openpilotci/" + "0982d79ebb0de295/2021-01-03--20-03-36/6/rlog.bz2") + + +def phase_openpilot(): + """A real openpilot log: a 2021 Toyota RAV4 drive from openpilot's own CI + test routes, publicly downloadable. It carries the car's CAN traffic, an + external GPS receiver, and openpilot's decoded carState, so the reader and + the calibrator can both be checked against it.""" + section("AN OPENPILOT LOG FROM A REAL DRIVE") + import urllib.request + + import numpy as np + from canlab.core.dbc_manager import decode_frame + try: + from canlab.core import openpilot_parser as op + if not op.is_available(): + raise ImportError + except ImportError: + check("openpilot rlog", lambda: (None, "pycapnp is not installed")) + return + from canlab.core.reference_calibrate import calibrate_with_lag_search, candidate_to_signal_def + + path = DATA / "openpilot" / "rlog.bz2" + if not path.is_file(): + path.parent.mkdir(parents=True, exist_ok=True) + try: + with urllib.request.urlopen(OPENPILOT_RLOG, timeout=120) as r: + path.write_bytes(r.read()) + except OSError as e: + reason = f"could not fetch it: {e}" + check("openpilot rlog", lambda: (None, reason)) + return + state = {} + + def reads(): + t0 = time.perf_counter() + df = op.parse_rlog(str(path)) + took = time.perf_counter() - t0 + direct = sum(len(e.can) for e in op.events(path) if e.which() == "can") + state["df"] = df + ok = (len(df) + df.attrs["sent_frames"] == direct and df["ID"].nunique() > 100 + and sorted(df["Bus"].unique()) == [0, 1, 2]) + return ok, (f"{len(df):,} received frames from {df['ID'].nunique()} IDs on buses " + f"0, 1 and 2 in {took:.1f} s; {df.attrs['sent_frames']:,} frames the " + f"panda sent itself kept apart; the two add up to pycapnp's own count") + check("a real rlog opens with the bundled schema", reads) + + def calibrates(): + df = state.get("df") + if df is None: + return False, "no frames" + speed = op.gps_reference(str(path))[0] + t0 = df.attrs["t0_mono"] + vt, v = [], [] + for ev in op.events(path): + if ev.which() == "carState": + vt.append(ev.logMonoTime / 1e9 - t0) + v.append(ev.carState.vEgo) + vt, v = np.array(vt), np.array(v) + cands = calibrate_with_lag_search(df[df["Bus"] == 0], speed, window_s=5.0, top_k=8) + hit = [c for c in cands if c["id"] == "0B4" and c["start_bit"] == 40 + and c["length"] == 16 and c["byte_order"] == "big"] + if not hit: + return False, "0x0B4 bytes 5-6 not among the candidates" + sig = candidate_to_signal_def(hit[0], "SPEED") + errs = [] + for r in df[(df["ID"] == "0B4") & (df["Bus"] == 0)].iloc[::20].itertuples(): + frame = bytes(int(getattr(r, f"B{i}")) for i in range(int(r.DLC))) + got = decode_frame([sig], "0B4", frame)["SPEED"] + got = got / 3.6 if sig["unit"] == "km/h" else got + want = float(np.interp(r.Timestamp, vt, v)) + errs.append(abs(got - want) / want) + med = float(np.median(errs)) + ok = hit[0].get("native_unit") == "km/h" and hit[0].get("native_scale") == 0.01 and med < 0.02 + return ok, (f"from the log's own GPS, 0x0B4 bytes 5-6 at {hit[0].get('native_scale')} " + f"{hit[0].get('native_unit')} per bit (openpilot's DBC says 0.01 km/h), " + f"R2 {hit[0]['r2']}; decodes within {100 * med:.2f}% of openpilot's vEgo") + check("the calibrator finds the speed in it from its own GPS", calibrates) + + def phase_safety(): section("NOTHING TRANSMITTED") @@ -878,7 +959,7 @@ def main() -> int: print(f"{len(have)} file(s): {', '.join(have)}") for phase in (phase_mdf4, phase_native_binary, phase_cross_format, - phase_analysis, phase_new_features, phase_exports, phase_multiframe, phase_blocks, phase_reference, phase_safety): + phase_analysis, phase_new_features, phase_exports, phase_multiframe, phase_blocks, phase_reference, phase_openpilot, phase_safety): phase() print("\n" + "=" * 70) diff --git a/tests/test_openpilot_rlog.py b/tests/test_openpilot_rlog.py new file mode 100644 index 0000000..d64be37 --- /dev/null +++ b/tests/test_openpilot_rlog.py @@ -0,0 +1,102 @@ +"""openpilot logs: built here with the vendored cereal schema, then read back. + +A real log is checked in the acceptance run (a public 2021 drive from +openpilot's CI). These pin the reader's behaviour on logs small enough to +write by hand: compression, openpilot's own transmissions kept apart from the +car's traffic, the GPS as a reference, and the names openpilot gives its files. +""" +import bz2 + +import pytest + +capnp = pytest.importorskip("capnp") + +from canlab.core import openpilot_parser as op # noqa: E402 +from canlab.core.log_parser import is_openpilot_log, parse_log_file # noqa: E402 + + +def _log(tmp_path, name="rlog.bz2", compress=True): + log = op.schema() + msgs = [] + t0 = 20_000_000_000_000 # logMonoTime, ns + for i in range(10): + ev = log.Event.new_message(logMonoTime=t0 + i * 10_000_000) + frames = ev.init("can", 3) + frames[0].address, frames[0].src = 0x0B4, 0 + frames[0].dat = bytes([0, 0, 0, 0, 0, 0x0A, i, 0]) + frames[1].address, frames[1].src = 0x18FEF100, 1 + frames[1].dat = bytes([0xC3, 0, 0x40, 0, 0, 0, 0, 0x30]) + frames[2].address, frames[2].src = 0x2E4, 0x80 # openpilot's own send + frames[2].dat = bytes(8) + msgs.append(ev) + gps = log.Event.new_message(logMonoTime=t0 + 50_000_000) + g = gps.init("gpsLocationExternal") + g.speed, g.latitude, g.longitude, g.altitude = 25.5, 32.71, -117.15, -10.0 + msgs.append(gps) + raw = b"".join(m.to_bytes() for m in msgs) + path = tmp_path / name + path.write_bytes(bz2.compress(raw) if compress else raw) + return path + + +def test_a_compressed_rlog_reads_as_frames(tmp_path): + df = op.parse_rlog(str(_log(tmp_path))) + assert len(df) == 20 and df.attrs["sent_frames"] == 10 + assert sorted(df["ID"].unique()) == ["0B4", "18FEF100"] + assert sorted(df["Bus"].unique()) == [0, 1] + assert df["Timestamp"].iloc[0] == 0.0 + assert df["Timestamp"].max() == pytest.approx(0.09) + ext = df.drop_duplicates("ID").set_index("ID")["Extended"] + assert bool(ext["18FEF100"]) and not bool(ext["0B4"]) + + +def test_openpilots_own_transmissions_are_kept_apart(tmp_path): + df = op.parse_rlog(str(_log(tmp_path)), include_sent=True) + assert len(df) == 30 and 0x80 in set(df["Bus"]) + + +def test_an_uncompressed_log_reads_the_same(tmp_path): + assert len(op.parse_rlog(str(_log(tmp_path, "rlog", compress=False)))) == 20 + + +def test_the_gps_is_a_reference_on_the_same_clock(tmp_path): + path = _log(tmp_path) + speed, lat, lon, alt = op.gps_reference(str(path)) + assert speed.unit == "m/s" and speed.values.tolist() == [25.5] + assert speed.ts.tolist() == pytest.approx([0.05]) + assert lat.values[0] == pytest.approx(32.71) and alt.unit == "m" + + +def test_openpilots_file_names_route_to_this_reader(tmp_path): + for name in ("rlog", "qlog", "rlog.bz2", "qlog.zst", "a.rlog", "b.qlog.bz2"): + assert is_openpilot_log(name), name + for name in ("capture.csv", "data.bz2", "trace.log"): + assert not is_openpilot_log(name), name + assert len(parse_log_file(str(_log(tmp_path, "rlog.bz2")))) == 20 + + +def test_a_zstd_log_without_zstandard_says_what_to_install(tmp_path, monkeypatch): + path = tmp_path / "rlog.zst" + path.write_bytes(b"\x28\xb5\x2f\xfd" + bytes(20)) + import builtins + real_import = builtins.__import__ + + def no_zstd(name, *a, **k): + if name == "zstandard": + raise ImportError("no zstandard") + return real_import(name, *a, **k) + monkeypatch.setattr(builtins, "__import__", no_zstd) + with pytest.raises(RuntimeError, match="zstandard"): + op.parse_rlog(str(path)) + + +def test_without_pycapnp_the_error_names_the_extra(monkeypatch, tmp_path): + monkeypatch.setattr(op, "_CAPNP_AVAILABLE", False) + with pytest.raises(RuntimeError, match="canlab\\[openpilot\\]"): + op.parse_rlog(str(tmp_path / "rlog")) + + +def test_the_schema_ships_with_its_licence(): + assert (op.SCHEMA_DIR / "log.capnp").is_file() and (op.SCHEMA_DIR / "car.capnp").is_file() + notice = (op.SCHEMA_DIR / "NOTICE.txt").read_text() + assert "Comma.ai" in notice and "MIT License" in notice diff --git a/tools/fetch_cereal.py b/tools/fetch_cereal.py new file mode 100644 index 0000000..5ec1658 --- /dev/null +++ b/tools/fetch_cereal.py @@ -0,0 +1,79 @@ +"""Vendor the capnp schema openpilot logs are written in. + +An openpilot rlog is a stream of capnp Event messages; reading one needs the +schema. It lives in two comma.ai repositories, both MIT licensed: cereal's +log.capnp and its neighbours in openpilot, and car.capnp in opendbc. This +copies them into canlab/core/data/cereal/ so an rlog opens with nothing +else installed but pycapnp. + + python tools/fetch_cereal.py [--ref master] + +capnp is backward compatible for added fields, so a current schema reads +older logs; the acceptance run checks that on a 2021 drive. +""" +from __future__ import annotations + +import argparse +import urllib.request +from pathlib import Path + +REPO = Path(__file__).resolve().parents[1] +OUT = REPO / "canlab" / "core" / "data" / "cereal" +FILES = { + "log.capnp": "openpilot/{ref}/openpilot/cereal/log.capnp", + "custom.capnp": "openpilot/{ref}/openpilot/cereal/custom.capnp", + "deprecated.capnp": "openpilot/{ref}/openpilot/cereal/deprecated.capnp", + "include/c++.capnp": "openpilot/{ref}/openpilot/cereal/include/c++.capnp", + "car.capnp": "opendbc/{ref}/opendbc/car/car.capnp", +} +NOTICE = """The capnp schema in this directory is copied from comma.ai's repositories: + + log.capnp, custom.capnp, deprecated.capnp, include/c++.capnp + https://github.com/commaai/openpilot (openpilot/cereal) + Copyright (c) 2018, Comma.ai, Inc. + car.capnp + https://github.com/commaai/opendbc (opendbc/car) + Copyright (c) 2020, Comma.ai, Inc. + +Both are released 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. + +The files are unmodified. +""" + + +def main() -> int: + ap = argparse.ArgumentParser(description=__doc__) + ap.add_argument("--ref", default="master") + args = ap.parse_args() + for name, path in FILES.items(): + url = "https://raw.githubusercontent.com/commaai/" + path.format(ref=args.ref) + dest = OUT / name + dest.parent.mkdir(parents=True, exist_ok=True) + with urllib.request.urlopen(url, timeout=60) as r: + dest.write_bytes(r.read()) + print(f" {name:<22} {dest.stat().st_size:>7} bytes") + (OUT / "NOTICE.txt").write_text(NOTICE) + print(f"schema in {OUT}") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main())