diff --git a/.gitignore b/.gitignore index 83ccbb478..e1ff5d500 100644 --- a/.gitignore +++ b/.gitignore @@ -41,16 +41,14 @@ board/obj/ selfdrive/boardd/boardd selfdrive/logcatd/logcatd selfdrive/mapd/default_speeds_by_region.json -selfdrive/proclogd/proclogd +system/proclogd/proclogd selfdrive/ui/_ui selfdrive/test/longitudinal_maneuvers/out selfdrive/visiond/visiond -selfdrive/loggerd/loggerd -selfdrive/loggerd/bootlog selfdrive/sensord/_gpsd selfdrive/sensord/_sensord -selfdrive/camerad/camerad -selfdrive/camerad/test/ae_gray_test +system/camerad/camerad +system/camerad/test/ae_gray_test selfdrive/modeld/_modeld selfdrive/modeld/_dmonitoringmodeld /src/ diff --git a/Jenkinsfile b/Jenkinsfile index 3b134d7c0..4e1371785 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -10,6 +10,7 @@ export TEST_DIR=${env.TEST_DIR} export SOURCE_DIR=${env.SOURCE_DIR} export GIT_BRANCH=${env.GIT_BRANCH} export GIT_COMMIT=${env.GIT_COMMIT} +export AZURE_TOKEN='${env.AZURE_TOKEN}' source ~/.bash_profile if [ -f /TICI ]; then @@ -42,8 +43,10 @@ def phone_steps(String device_type, steps) { pipeline { agent none environment { + CI = "1" TEST_DIR = "/data/openpilot" SOURCE_DIR = "/data/openpilot_source/" + AZURE_TOKEN = credentials('azure_token') } options { timeout(time: 4, unit: 'HOURS') @@ -74,72 +77,88 @@ pipeline { } } - stages { - stage('On-device Tests') { - agent { docker { image 'ghcr.io/commaai/alpine-ssh'; args '--user=root' } } - stages { - stage('parallel tests') { - parallel { - stage('build') { - environment { - R3_PUSH = "${env.BRANCH_NAME == 'master' ? '1' : ' '}" - } - steps { - phone_steps("tici", [ - ["build master-ci", "cd $SOURCE_DIR/release && TARGET_DIR=$TEST_DIR EXTRA_FILES='tools/' ./build_devel.sh"], - ["build openpilot", "cd selfdrive/manager && ./build.py"], - ["test manager", "python selfdrive/manager/test/test_manager.py"], - ["onroad tests", "cd selfdrive/test/ && ./test_onroad.py"], - ["test car interfaces", "cd selfdrive/car/tests/ && ./test_car_interfaces.py"], - ]) - } - } + parallel { - stage('HW + Unit Tests') { - steps { - phone_steps("tici2", [ - ["build", "cd selfdrive/manager && ./build.py"], - ["test power draw", "python selfdrive/hardware/tici/test_power_draw.py"], - ["test boardd loopback", "python selfdrive/boardd/tests/test_boardd_loopback.py"], - ["test loggerd", "python selfdrive/loggerd/tests/test_loggerd.py"], - ["test encoder", "LD_LIBRARY_PATH=/usr/local/lib python selfdrive/loggerd/tests/test_encoder.py"], - ["test sensord", "python selfdrive/sensord/test/test_sensord.py"], - ]) - } - } - - stage('camerad') { - steps { - phone_steps("tici-party", [ - ["build", "cd selfdrive/manager && ./build.py"], - ["test camerad", "python selfdrive/camerad/test/test_camerad.py"], - ["test exposure", "python selfdrive/camerad/test/test_exposure.py"], - ]) - } - } - - stage('replay') { - steps { - phone_steps("tici3", [ - ["build", "cd selfdrive/manager && ./build.py"], - ["model replay", "cd selfdrive/test/process_replay && ./model_replay.py"], - ]) - } - } - - } + stage('simulator') { + agent { + dockerfile { + filename 'Dockerfile.sim_nvidia' + dir 'tools/sim' + args '--user=root' + } + } + steps { + sh "git config --global --add safe.directory ${WORKSPACE}" + sh "git lfs pull" + lock(resource: "", label: "simulator", inversePrecedence: true, quantity: 1) { + sh "${WORKSPACE}/tools/sim/build_container.sh" + sh "DETACH=1 ${WORKSPACE}/tools/sim/start_carla.sh" + sh "${WORKSPACE}/tools/sim/start_openpilot_docker.sh" } } post { always { - cleanWs() + sh "docker kill carla_sim || true" + sh "rm -rf ${WORKSPACE}/* || true" + sh "rm -rf .* || true" } } - } + stage('build') { + agent { docker { image 'ghcr.io/commaai/alpine-ssh'; args '--user=root' } } + environment { + R3_PUSH = "${env.BRANCH_NAME == 'master' ? '1' : ' '}" + } + steps { + phone_steps("tici", [ + ["build master-ci", "cd $SOURCE_DIR/release && TARGET_DIR=$TEST_DIR EXTRA_FILES='tools/' ./build_devel.sh"], + ["build openpilot", "cd selfdrive/manager && ./build.py"], + ["check dirty", "release/check-dirty.sh"], + ["test manager", "python selfdrive/manager/test/test_manager.py"], + ["onroad tests", "cd selfdrive/test/ && ./test_onroad.py"], + ["test car interfaces", "cd selfdrive/car/tests/ && ./test_car_interfaces.py"], + ]) + } + } + + stage('HW + Unit Tests') { + agent { docker { image 'ghcr.io/commaai/alpine-ssh'; args '--user=root' } } + steps { + phone_steps("tici2", [ + ["build", "cd selfdrive/manager && ./build.py"], + ["test power draw", "python system/hardware/tici/test_power_draw.py"], + ["test boardd loopback", "python selfdrive/boardd/tests/test_boardd_loopback.py"], + ["test loggerd", "python selfdrive/loggerd/tests/test_loggerd.py"], + ["test encoder", "LD_LIBRARY_PATH=/usr/local/lib python selfdrive/loggerd/tests/test_encoder.py"], + ["test sensord", "python selfdrive/sensord/test/test_sensord.py"], + ]) + } + } + + stage('camerad') { + agent { docker { image 'ghcr.io/commaai/alpine-ssh'; args '--user=root' } } + steps { + phone_steps("tici-party", [ + ["build", "cd selfdrive/manager && ./build.py"], + ["test camerad", "python system/camerad/test/test_camerad.py"], + ["test exposure", "python system/camerad/test/test_exposure.py"], + ]) + } + } + + stage('replay') { + agent { docker { image 'ghcr.io/commaai/alpine-ssh'; args '--user=root' } } + steps { + phone_steps("tici3", [ + ["build", "cd selfdrive/manager && ./build.py"], + ["model replay", "cd selfdrive/test/process_replay && ./model_replay.py"], + ]) + } + } } + } } } diff --git a/README.md b/README.md index 5af02de8d..3dce9d447 100755 --- a/README.md +++ b/README.md @@ -35,16 +35,17 @@ What is openpilot? -Running in a car +Running on a dedicated device in a car ------ To use openpilot in a car, you need four things -* This software. It's free and available right here. +* A supported device to run this software: a [comma three](https://comma.ai/shop/products/three). +* This software. The setup procedure of the comma three allows the user to enter a url for custom software. +The url, openpilot.comma.ai will install the release version of openpilot. To install openpilot master, you can use installer.comma.ai/commaai/master, and replacing commaai with another github username can install a fork. * One of [the 150+ supported cars](docs/CARS.md). We support Honda, Toyota, Hyundai, Nissan, Kia, Chrysler, Lexus, Acura, Audi, VW, and more. If your car is not supported, but has adaptive cruise control and lane keeping assist, it's likely able to run openpilot. -* A supported device to run this software: a [comma three](https://comma.ai/shop/products/three), or if you like to experiment, a [Ubuntu computer with webcams](https://github.com/commaai/openpilot/tree/master/tools/webcam). -* A way to connect to your car. With a comma three, you need only a [car harness](https://comma.ai/shop/products/car-harness). With a PC, you also need a [black panda](https://comma.ai/shop/products/panda). +* A [car harness](https://comma.ai/shop/products/car-harness) to connect to your car. -We have detailed instructions for [how to install the device in a car](https://comma.ai/setup). +We have detailed instructions for [how to mount the device in a car](https://comma.ai/setup). Running on PC ------ @@ -55,6 +56,7 @@ With openpilot's tools you can plot logs, replay drives and watch the full-res c You can also run openpilot in simulation [with the CARLA simulator](tools/sim/README.md). This allows openpilot to drive around a virtual car on your Ubuntu machine. The whole setup should only take a few minutes, but does require a decent GPU. +A PC running openpilot can also control your vehicle if it is connected to a [a webcam](https://github.com/commaai/openpilot/tree/master/tools/webcam), a [black panda](https://comma.ai/shop/products/panda), and [a harness](https://comma.ai/shop/products/car-harness). Community and Contributing ------ @@ -102,20 +104,25 @@ Directory Structure ├── panda # Code used to communicate on CAN ├── third_party # External libraries ├── pyextra # Extra python packages + └── system # Generic services + ├── camerad # Driver to capture images from the camera sensors + ├── clocksd # Broadcasts current time + ├── hardware # Hardware abstraction classes + ├── logcatd # systemd journal as a service + └── proclogd # Logs information from /proc └── selfdrive # Code needed to drive the car ├── assets # Fonts, images, and sounds for UI ├── athena # Allows communication with the app ├── boardd # Daemon to talk to the board - ├── camerad # Driver to capture images from the camera sensors ├── car # Car specific code to read states and control actuators - ├── common # Shared C/C++ code for the daemons ├── controls # Planning and controls ├── debug # Tools to help you debug and do car ports ├── locationd # Precise localization and vehicle parameter estimation - ├── logcatd # Android logcat as a service ├── loggerd # Logger and uploader of car data + ├── manager # Deamon that starts/stops all other daemons as needed ├── modeld # Driving and monitoring model runners - ├── proclogd # Logs information from proc + ├── monitoring # Daemon to determine driver attention + ├── navd # Turn-by-turn navigation ├── sensord # IMU interface code ├── test # Unit tests, system tests, and a car simulator └── ui # The UI diff --git a/RELEASES.md b/RELEASES.md index 2c7ff17de..b87bd2ee7 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,3 +1,38 @@ +Version 0.8.15 (2022-07-20) +======================== +* New driving model + * Path planning uses end-to-end output instead of lane lines at all times + * Reduced ping pong + * Improved lane centering +* New lateral controller based on physical wheel torque model + * Much smoother control that's consistent across the speed range + * Effective feedforward that uses road roll + * Simplified tuning, all car-specific parameters can be derived from data + * Used on select Toyota and Hyundai models at first + * Significantly improved control on TSS-P Prius +* New driver monitoring model + * Bigger model, covering full interior view from driver camera + * Works with a wider variety of mounting angles + * 3x more unique comma three training data than previous +* Navigation improvements + * Speed limits shown while navigating + * Faster position fix by using raw GPS measurements +* UI updates + * Multilanguage support for settings and home screen + * New font + * Refreshed max speed design + * More consistent camera view perspective across cars +* Reduced power usage: device runs cooler and fan spins less +* AGNOS 5 + * Support VSCode remote SSH target + * Support for delta updates to reduce data usage on future OS updates +* Chrysler ECU firmware fingerprinting thanks to realfast! +* Honda Civic 2022 support +* Hyundai Tucson 2021 support thanks to bluesforte! +* Kia EV6 2022 support +* Lexus NX Hybrid 2020 support thanks to AlexandreSato! +* Ram 1500 2019-21 support thanks to realfast! + Version 0.8.14 (2022-06-01) ======================== * New driving model diff --git a/SConstruct b/SConstruct index d9d05f794..1209894ba 100644 --- a/SConstruct +++ b/SConstruct @@ -1,5 +1,4 @@ import os -import shutil import subprocess import sys import sysconfig @@ -7,6 +6,8 @@ import platform import numpy as np TICI = os.path.isfile('/TICI') +AGNOS = TICI + Decider('MD5-timestamp') AddOption('--test', @@ -56,7 +57,7 @@ real_arch = arch = subprocess.check_output(["uname", "-m"], encoding='utf8').rst if platform.system() == "Darwin": arch = "Darwin" -if arch == "aarch64" and TICI: +if arch == "aarch64" and AGNOS: arch = "larch64" USE_WEBCAM = os.getenv("USE_WEBCAM") is not None @@ -93,7 +94,7 @@ if arch == "larch64": "/usr/lib/aarch64-linux-gnu" ] cpppath += [ - "#selfdrive/camerad/include", + "#system/camerad/include", ] cflags = ["-DQCOM2", "-mcpu=cortex-a57"] cxxflags = ["-DQCOM2", "-mcpu=cortex-a57"] @@ -226,7 +227,7 @@ if GetOption('compile_db'): env.CompilationDatabase('compile_commands.json') # Setup cache dir -cache_dir = '/data/scons_cache' if TICI else '/tmp/scons_cache' +cache_dir = '/data/scons_cache' if AGNOS else '/tmp/scons_cache' CacheDir(cache_dir) Clean(["."], cache_dir) @@ -355,27 +356,41 @@ Export('cereal', 'messaging', 'visionipc') # Build rednose library and ekf models +rednose_deps = [ + "#selfdrive/locationd/models/constants.py", + "#selfdrive/locationd/models/gnss_helpers.py", +] + rednose_config = { 'generated_folder': '#selfdrive/locationd/models/generated', 'to_build': { - 'live': ('#selfdrive/locationd/models/live_kf.py', True, ['live_kf_constants.h']), - 'car': ('#selfdrive/locationd/models/car_kf.py', True, []), + 'gnss': ('#selfdrive/locationd/models/gnss_kf.py', True, [], rednose_deps), + 'live': ('#selfdrive/locationd/models/live_kf.py', True, ['live_kf_constants.h'], rednose_deps), + 'car': ('#selfdrive/locationd/models/car_kf.py', True, [], rednose_deps), }, } if arch != "larch64": rednose_config['to_build'].update({ - 'gnss': ('#selfdrive/locationd/models/gnss_kf.py', True, []), - 'loc_4': ('#selfdrive/locationd/models/loc_kf.py', True, []), - 'pos_computer_4': ('#rednose/helpers/lst_sq_computer.py', False, []), - 'pos_computer_5': ('#rednose/helpers/lst_sq_computer.py', False, []), - 'feature_handler_5': ('#rednose/helpers/feature_handler.py', False, []), - 'lane': ('#xx/pipeline/lib/ekf/lane_kf.py', True, []), + 'loc_4': ('#selfdrive/locationd/models/loc_kf.py', True, [], rednose_deps), + 'pos_computer_4': ('#rednose/helpers/lst_sq_computer.py', False, [], []), + 'pos_computer_5': ('#rednose/helpers/lst_sq_computer.py', False, [], []), + 'feature_handler_5': ('#rednose/helpers/feature_handler.py', False, [], []), + 'lane': ('#xx/pipeline/lib/ekf/lane_kf.py', True, [], rednose_deps), }) Export('rednose_config') SConscript(['rednose/SConscript']) +# Build system services +SConscript([ + 'system/camerad/SConscript', + 'system/clocksd/SConscript', + 'system/proclogd/SConscript', +]) +if arch != "Darwin": + SConscript(['system/logcatd/SConscript']) + # Build openpilot SConscript(['cereal/SConscript']) @@ -387,7 +402,6 @@ SConscript(['third_party/SConscript']) SConscript(['common/kalman/SConscript']) SConscript(['common/transformations/SConscript']) -SConscript(['selfdrive/camerad/SConscript']) SConscript(['selfdrive/modeld/SConscript']) SConscript(['selfdrive/controls/lib/cluster/SConscript']) @@ -395,8 +409,6 @@ SConscript(['selfdrive/controls/lib/lateral_mpc_lib/SConscript']) SConscript(['selfdrive/controls/lib/longitudinal_mpc_lib/SConscript']) SConscript(['selfdrive/boardd/SConscript']) -SConscript(['selfdrive/proclogd/SConscript']) -SConscript(['selfdrive/clocksd/SConscript']) SConscript(['selfdrive/loggerd/SConscript']) @@ -404,8 +416,7 @@ SConscript(['selfdrive/locationd/SConscript']) SConscript(['selfdrive/sensord/SConscript']) SConscript(['selfdrive/ui/SConscript']) -if arch != "Darwin": - SConscript(['selfdrive/logcatd/SConscript']) +SConscript(['tools/replay/SConscript']) if GetOption('test'): SConscript('panda/tests/safety/SConscript') diff --git a/cereal/car.capnp b/cereal/car.capnp index 5a4e256ad..c361ed60e 100644 --- a/cereal/car.capnp +++ b/cereal/car.capnp @@ -344,9 +344,9 @@ struct CarControl { struct CruiseControl { cancel @0: Bool; - override @1: Bool; - speedOverride @2: Float32; - accelOverride @3: Float32; + resume @1: Bool; + speedOverrideDEPRECATED @2: Float32; + accelOverrideDEPRECATED @3: Float32; } struct HUDControl { @@ -419,6 +419,7 @@ struct CarParams { maxSteeringAngleDeg @54 :Float32; safetyConfigs @62 :List(SafetyConfig); alternativeExperience @65 :Int16; # panda flag for features like no disengage on gas + maxLateralAccel @68 :Float32; steerMaxBPDEPRECATED @11 :List(Float32); steerMaxVDEPRECATED @12 :List(Float32); @@ -456,7 +457,6 @@ struct CarParams { directAccelControl @30 :Bool; # Does the car have direct accel control or just gas/brake stoppingControl @31 :Bool; # Does the car allows full control even at lows speeds when stopping stopAccel @60 :Float32; # Required acceleraton to keep vehicle stationary - steerRateCost @33 :Float32; # Lateral MPC cost on steering rate steerControlType @34 :SteerControlType; radarOffCan @35 :Bool; # True when radar objects aren't visible on CAN stoppingDecelRate @52 :Float32; # m/s^2/s while trying to stop @@ -502,6 +502,7 @@ struct CarParams { ki @2 :Float32; friction @3 :Float32; kf @4 :Float32; + steeringAngleDeadzoneDeg @5 :Float32; } struct LongitudinalPIDTuning { @@ -592,8 +593,11 @@ struct CarParams { struct CarFw { ecu @0 :Ecu; fwVersion @1 :Data; - address @2: UInt32; - subAddress @3: UInt8; + address @2 :UInt32; + subAddress @3 :UInt8; + responseAddress @4 :UInt32; + request @5 :List(Data); + brand @6 :Text; } enum Ecu { @@ -634,6 +638,7 @@ struct CarParams { } enableCameraDEPRECATED @4 :Bool; + steerRateCostDEPRECATED @33 :Float32; isPandaBlackDEPRECATED @39 :Bool; hasStockCameraDEPRECATED @57 :Bool; safetyParamDEPRECATED @10 :Int16; diff --git a/cereal/log.capnp b/cereal/log.capnp index 3b53c01e5..dce40bf1b 100644 --- a/cereal/log.capnp +++ b/cereal/log.capnp @@ -314,6 +314,7 @@ struct DeviceState @0xa4d8b5af2aa492eb { offroadPowerUsageUwh @23 :UInt32; carBatteryCapacityUwh @25 :UInt32; powerDrawW @40 :Float32; + somPowerDrawW @42 :Float32; # device thermals cpuTempC @26 :List(Float32); @@ -1078,13 +1079,15 @@ struct ProcLog { struct GnssMeasurements { ubloxMonoTime @0 :UInt64; - correctedMeasurements @1 :List(CorrectedMeasurement); + gpsWeek @1 :Int16; + gpsTimeOfWeek @2 :Float64; - positionECEF @2 :Measurement; - velocityECEF @3 :Measurement; - # todo add accuracy of position? - # Represents heading in degrees. - bearingDeg @4 :Measurement; + correctedMeasurements @3 :List(CorrectedMeasurement); + + positionECEF @4 :LiveLocationKalman.Measurement; + velocityECEF @5 :LiveLocationKalman.Measurement; + # Used for debugging: + positionFixECEF @6 :LiveLocationKalman.Measurement; # Todo sync this with timing pulse of ublox struct CorrectedMeasurement { @@ -1099,6 +1102,14 @@ struct GnssMeasurements { # Satellite position and velocity [x,y,z] satPos @7 :List(Float64); satVel @8 :List(Float64); + ephemerisSource @9 :EphemerisSource; + } + + struct EphemerisSource { + type @0 :EphemerisSourceType; + # 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 { @@ -1112,10 +1123,11 @@ struct GnssMeasurements { glonass @6; } - struct Measurement { - value @0 : List(Float64); - std @1 : Float64; - valid @2 : Bool; + enum EphemerisSourceType { + nav @0; + # Different ultra-rapid files: + nasaUltraRapid @1; + glonassIacUltraRapid @2; } } @@ -1621,7 +1633,36 @@ struct Joystick { buttons @1: List(Bool); } -struct DriverState { +struct DriverStateV2 { + frameId @0 :UInt32; + modelExecutionTime @1 :Float32; + dspExecutionTime @2 :Float32; + rawPredictions @3 :Data; + + poorVisionProb @4 :Float32; + 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; + occludedProb @10 :Float32; + readyProb @11 :List(Float32); + notReadyProb @12 :List(Float32); + } +} + +struct DriverStateDEPRECATED @0xb83c6cc593ed0a00 { frameId @0 :UInt32; modelExecutionTime @14 :Float32; dspExecutionTime @16 :Float32; @@ -1669,8 +1710,8 @@ struct DriverMonitoringState @0xb83cda094a1da284 { isLowStd @13 :Bool; hiStdCount @14 :UInt32; isActiveMode @16 :Bool; + isRHD @4 :Bool; - isRHDDEPRECATED @4 :Bool; isPreviewDEPRECATED @15 :Bool; rhdCheckedDEPRECATED @5 :Bool; } @@ -1781,6 +1822,9 @@ struct NavInstruction { lanes @8 :List(Lane); showFull @9 :Bool; + speedLimit @10 :Float32; # m/s + speedLimitSign @11 :SpeedLimitSign; + struct Lane { directions @0 :List(Direction); active @1 :Bool; @@ -1794,6 +1838,10 @@ struct NavInstruction { straight @3; } + enum SpeedLimitSign { + mutcd @0; # US Style + vienna @1; # EU Style + } } struct NavRoute { @@ -1844,7 +1892,6 @@ struct Event { qcomGnss @31 :QcomGnss; gpsLocationExternal @48 :GpsLocationData; gnssMeasurements @91 :GnssMeasurements; - driverState @59 :DriverState; liveParameters @61 :LiveParametersData; cameraOdometry @63 :CameraOdometry; thumbnail @66: Thumbnail; @@ -1853,6 +1900,7 @@ struct Event { driverMonitoringState @71: DriverMonitoringState; liveLocationKalman @72 :LiveLocationKalman; modelV2 @75 :ModelDataV2; + driverStateV2 @92 :DriverStateV2; # camera stuff, each camera state has a matching encode idx roadCameraState @2 :FrameData; @@ -1922,5 +1970,6 @@ struct Event { gpsLocationDEPRECATED @21 :GpsLocationData; uiLayoutStateDEPRECATED @57 :Legacy.UiLayoutState; pandaStateDEPRECATED @12 :PandaState; + driverStateDEPRECATED @59 :DriverStateDEPRECATED; } } diff --git a/cereal/messaging/bridge.cc b/cereal/messaging/bridge.cc index 844f6d10e..0b72e067f 100644 --- a/cereal/messaging/bridge.cc +++ b/cereal/messaging/bridge.cc @@ -79,9 +79,13 @@ int main(int argc, char** argv) { Message * msg = sub_sock->receive(); if (msg == NULL) continue; int ret; - do { ret = sub2pub[sub_sock]->sendMessage(msg); } while (ret == -1 && errno == EINTR); - assert(ret >= 0); + do { + ret = sub2pub[sub_sock]->sendMessage(msg); + } while (ret == -1 && errno == EINTR && !do_exit); + assert(ret >= 0 || do_exit); delete msg; + + if (do_exit) break; } } return 0; diff --git a/cereal/services.py b/cereal/services.py index f895d883d..a1411f5c2 100755 --- a/cereal/services.py +++ b/cereal/services.py @@ -1,8 +1,6 @@ #!/usr/bin/env python3 -import os from typing import Optional -TICI = os.path.isfile('/TICI') RESERVED_PORT = 8022 # sshd STARTING_PORT = 8001 @@ -19,7 +17,6 @@ class Service: self.frequency = frequency self.decimation = decimation -DCAM_FREQ = 10. if not TICI else 20. services = { # service: (should_log, frequency, qlog decimation (optional)) @@ -46,7 +43,7 @@ services = { "gpsLocationExternal": (True, 10., 10), "ubloxGnss": (True, 10.), "qcomGnss": (True, 2.), - "gnssMeasurements": (True, 10.), + "gnssMeasurements": (True, 10., 10), "clocks": (True, 1., 1), "ubloxRaw": (True, 20.), "liveLocationKalman": (True, 20., 5), @@ -57,16 +54,16 @@ services = { "carEvents": (True, 1., 1), "carParams": (True, 0.02, 1), "roadCameraState": (True, 20., 20), - "driverCameraState": (True, DCAM_FREQ, DCAM_FREQ), - "driverEncodeIdx": (False, DCAM_FREQ, 1), - "driverState": (True, DCAM_FREQ, DCAM_FREQ / 2), - "driverMonitoringState": (True, DCAM_FREQ, DCAM_FREQ / 2), + "driverCameraState": (True, 20., 20), + "driverEncodeIdx": (False, 20., 1), + "driverStateV2": (True, 20., 10), + "driverMonitoringState": (True, 20., 10), "wideRoadEncodeIdx": (False, 20., 1), "wideRoadCameraState": (True, 20., 20), "modelV2": (True, 20., 40), "managerState": (True, 2., 1), "uploaderState": (True, 0., 1), - "navInstruction": (True, 0., 10), + "navInstruction": (True, 1., 10), "navRoute": (True, 0.), "navThumbnail": (True, 0.), "qRoadEncodeIdx": (False, 20.), @@ -74,7 +71,7 @@ services = { # debug "testJoystick": (True, 0.), "roadEncodeData": (False, 20.), - "driverEncodeData": (False, DCAM_FREQ), + "driverEncodeData": (False, 20.), "wideRoadEncodeData": (False, 20.), "qRoadEncodeData": (False, 20.), } diff --git a/cereal/visionipc/__init__.py b/cereal/visionipc/__init__.py index e69de29bb..89915b8ef 100644 --- a/cereal/visionipc/__init__.py +++ b/cereal/visionipc/__init__.py @@ -0,0 +1,4 @@ +from cereal.visionipc.visionipc_pyx import VisionIpcClient, VisionIpcServer, VisionStreamType # pylint: disable=no-name-in-module, import-error +assert VisionIpcClient +assert VisionIpcServer +assert VisionStreamType diff --git a/cereal/visionipc/visionbuf.cc b/cereal/visionipc/visionbuf.cc index 276b4fb4f..3480e4037 100644 --- a/cereal/visionipc/visionbuf.cc +++ b/cereal/visionipc/visionbuf.cc @@ -14,14 +14,15 @@ void VisionBuf::init_rgb(size_t init_width, size_t init_height, size_t init_stri this->stride = init_stride; } -void VisionBuf::init_yuv(size_t init_width, size_t init_height){ +void VisionBuf::init_yuv(size_t init_width, size_t init_height, size_t init_stride, size_t init_uv_offset){ this->rgb = false; this->width = init_width; this->height = init_height; + this->stride = init_stride; + this->uv_offset = init_uv_offset; this->y = (uint8_t *)this->addr; - this->u = this->y + (this->width * this->height); - this->v = this->u + (this->width / 2 * this->height / 2); + this->uv = this->y + this->uv_offset; } diff --git a/cereal/visionipc/visionbuf.h b/cereal/visionipc/visionbuf.h index 864fac97d..fe546a64e 100644 --- a/cereal/visionipc/visionbuf.h +++ b/cereal/visionipc/visionbuf.h @@ -36,11 +36,11 @@ class VisionBuf { size_t width = 0; size_t height = 0; size_t stride = 0; + size_t uv_offset = 0; // YUV uint8_t * y = nullptr; - uint8_t * u = nullptr; - uint8_t * v = nullptr; + uint8_t * uv = nullptr; // Visionipc uint64_t server_id = 0; @@ -58,7 +58,7 @@ class VisionBuf { void import(); void init_cl(cl_device_id device_id, cl_context ctx); void init_rgb(size_t width, size_t height, size_t stride); - void init_yuv(size_t width, size_t height); + void init_yuv(size_t width, size_t height, size_t stride, size_t uv_offset); int sync(int dir); int free(); diff --git a/cereal/visionipc/visionbuf_cl.cc b/cereal/visionipc/visionbuf_cl.cc index 0286d28fd..8de25fcb6 100644 --- a/cereal/visionipc/visionbuf_cl.cc +++ b/cereal/visionipc/visionbuf_cl.cc @@ -34,8 +34,8 @@ static void *malloc_with_fd(size_t len, int *fd) { void VisionBuf::allocate(size_t length) { this->len = length; - this->mmap_len = this->len; - this->addr = malloc_with_fd(this->len, &this->fd); + this->mmap_len = this->len + sizeof(uint64_t); + this->addr = malloc_with_fd(this->mmap_len, &this->fd); this->frame_id = (uint64_t*)((uint8_t*)this->addr + this->len); } diff --git a/cereal/visionipc/visionipc.pxd b/cereal/visionipc/visionipc.pxd index db45aee80..5c2d6f079 100644 --- a/cereal/visionipc/visionipc.pxd +++ b/cereal/visionipc/visionipc.pxd @@ -16,6 +16,7 @@ cdef extern from "visionbuf.h": size_t width size_t height size_t stride + size_t uv_offset void set_frame_id(uint64_t id) cdef extern from "visionipc.h": @@ -28,6 +29,7 @@ cdef extern from "visionipc_server.h": cdef cppclass VisionIpcServer: VisionIpcServer(string, void*, void*) void create_buffers(VisionStreamType, size_t, bool, size_t, size_t) + void create_buffers_with_sizes(VisionStreamType, size_t, bool, size_t, size_t, size_t, size_t, size_t) VisionBuf * get_buffer(VisionStreamType) void send(VisionBuf *, VisionIpcBufExtra *, bool) void start_listener() diff --git a/cereal/visionipc/visionipc_client.cc b/cereal/visionipc/visionipc_client.cc index ece239356..573a47d09 100644 --- a/cereal/visionipc/visionipc_client.cc +++ b/cereal/visionipc/visionipc_client.cc @@ -66,7 +66,7 @@ bool VisionIpcClient::connect(bool blocking){ if (buffers[i].rgb) { buffers[i].init_rgb(buffers[i].width, buffers[i].height, buffers[i].stride); } else { - buffers[i].init_yuv(buffers[i].width, buffers[i].height); + buffers[i].init_yuv(buffers[i].width, buffers[i].height, buffers[i].stride, buffers[i].uv_offset); } if (device_id) buffers[i].init_cl(device_id, ctx); diff --git a/cereal/visionipc/visionipc_pyx.pyx b/cereal/visionipc/visionipc_pyx.pyx index 9db6e1f7b..151a33431 100644 --- a/cereal/visionipc/visionipc_pyx.pyx +++ b/cereal/visionipc/visionipc_pyx.pyx @@ -31,9 +31,12 @@ cdef class VisionIpcServer: def __init__(self, string name): self.server = new cppVisionIpcServer(name, NULL, NULL) - def create_buffers(self, VisionStreamType tp, size_t num_buffers, bool rgb, size_t width, size_t height): + def create_buffers(self, VisionStreamType tp, size_t num_buffers, bool rgb, size_t width, size_t height): self.server.create_buffers(tp, num_buffers, rgb, width, height) + def create_buffers_with_sizes(self, VisionStreamType tp, size_t num_buffers, bool rgb, size_t width, size_t height, size_t size, size_t stride, size_t uv_offset): + self.server.create_buffers_with_sizes(tp, num_buffers, rgb, width, height, size, stride, uv_offset) + def send(self, VisionStreamType tp, const unsigned char[:] data, uint32_t frame_id=0, uint64_t timestamp_sof=0, uint64_t timestamp_eof=0): cdef cppVisionBuf * buf = self.server.get_buffer(tp) @@ -79,6 +82,10 @@ cdef class VisionIpcClient: def stride(self): return None if not self.buf else self.buf.stride + @property + def uv_offset(self): + return None if not self.buf else self.buf.uv_offset + def recv(self, int timeout_ms=100): self.buf = self.client.recv(NULL, timeout_ms) if not self.buf: diff --git a/cereal/visionipc/visionipc_server.cc b/cereal/visionipc/visionipc_server.cc index aa424709c..cbe36663a 100644 --- a/cereal/visionipc/visionipc_server.cc +++ b/cereal/visionipc/visionipc_server.cc @@ -35,7 +35,8 @@ void VisionIpcServer::create_buffers(VisionStreamType type, size_t num_buffers, int aligned_w = 0, aligned_h = 0; size_t size = 0; - size_t stride = 0; // Only used for RGB + size_t stride = 0; + size_t uv_offset = 0; if (rgb) { visionbuf_compute_aligned_width_and_height(width, height, &aligned_w, &aligned_h); @@ -43,8 +44,14 @@ void VisionIpcServer::create_buffers(VisionStreamType type, size_t num_buffers, stride = aligned_w * 3; } else { size = width * height * 3 / 2; + stride = width; + uv_offset = width * height; } + create_buffers_with_sizes(type, num_buffers, rgb, width, height, size, stride, uv_offset); +} + +void VisionIpcServer::create_buffers_with_sizes(VisionStreamType type, size_t num_buffers, bool rgb, size_t width, size_t height, size_t size, size_t stride, size_t uv_offset) { // Create map + alloc requested buffers for (size_t i = 0; i < num_buffers; i++){ VisionBuf* buf = new VisionBuf(); @@ -54,7 +61,7 @@ void VisionIpcServer::create_buffers(VisionStreamType type, size_t num_buffers, if (device_id) buf->init_cl(device_id, ctx); - rgb ? buf->init_rgb(width, height, stride) : buf->init_yuv(width, height); + rgb ? buf->init_rgb(width, height, stride) : buf->init_yuv(width, height, stride, uv_offset); buffers[type].push_back(buf); } diff --git a/cereal/visionipc/visionipc_server.h b/cereal/visionipc/visionipc_server.h index 01409f78d..5c644023b 100644 --- a/cereal/visionipc/visionipc_server.h +++ b/cereal/visionipc/visionipc_server.h @@ -37,6 +37,7 @@ class VisionIpcServer { VisionBuf * get_buffer(VisionStreamType type); void create_buffers(VisionStreamType type, size_t num_buffers, bool rgb, size_t width, size_t height); + void create_buffers_with_sizes(VisionStreamType type, size_t num_buffers, bool rgb, size_t width, size_t height, size_t size, size_t stride, size_t uv_offset); void send(VisionBuf * buf, VisionIpcBufExtra * extra, bool sync=true); void start_listener(); }; diff --git a/common/SConscript b/common/SConscript index ebc0ec79e..8aee6f42a 100644 --- a/common/SConscript +++ b/common/SConscript @@ -19,7 +19,6 @@ _common = fxn('common', common_libs, LIBS="json11") files = [ 'clutil.cc', - 'visionimg.cc', ] _gpucommon = fxn('gpucommon', files) diff --git a/common/api/__init__.py b/common/api/__init__.py index 8b83dfc64..c1fa635bd 100644 --- a/common/api/__init__.py +++ b/common/api/__init__.py @@ -3,7 +3,7 @@ import os import requests from datetime import datetime, timedelta from common.basedir import PERSIST -from selfdrive.version import get_version +from system.version import get_version API_HOST = os.getenv('API_HOST', 'https://api.commadotai.com') @@ -22,13 +22,13 @@ class Api(): def request(self, method, endpoint, timeout=None, access_token=None, **params): return api_get(endpoint, method=method, timeout=timeout, access_token=access_token, **params) - def get_token(self): + def get_token(self, expiry_hours=1): now = datetime.utcnow() payload = { 'identity': self.dongle_id, 'nbf': now, 'iat': now, - 'exp': now + timedelta(hours=1) + 'exp': now + timedelta(hours=expiry_hours) } token = jwt.encode(payload, self.private_key, algorithm='RS256') if isinstance(token, bytes): diff --git a/common/basedir.py b/common/basedir.py index 8be1cf6af..371b54d3e 100644 --- a/common/basedir.py +++ b/common/basedir.py @@ -1,7 +1,7 @@ import os from pathlib import Path -from selfdrive.hardware import PC +from system.hardware import PC BASEDIR = os.path.abspath(os.path.join(os.path.dirname(os.path.realpath(__file__)), "../")) diff --git a/common/modeldata.h b/common/modeldata.h index 4b776ef94..b19384141 100644 --- a/common/modeldata.h +++ b/common/modeldata.h @@ -2,7 +2,7 @@ #include #include "common/mat.h" -#include "selfdrive/hardware/hw.h" +#include "system/hardware/hw.h" const int TRAJECTORY_SIZE = 33; const int LAT_MPC_N = 16; @@ -24,14 +24,6 @@ constexpr auto T_IDXS_FLOAT = build_idxs(10.0); constexpr auto X_IDXS = build_idxs(192.0); constexpr auto X_IDXS_FLOAT = build_idxs(192.0); -const int TICI_CAM_WIDTH = 1928; - -namespace tici_dm_crop { - const int x_offset = -72; - const int y_offset = -144; - const int width = 954; -}; - const mat3 fcam_intrinsic_matrix = (mat3){{2648.0, 0.0, 1928.0 / 2, 0.0, 2648.0, 1208.0 / 2, 0.0, 0.0, 1.0}}; diff --git a/common/params.cc b/common/params.cc index 4b62fc97a..c4f65a9e0 100644 --- a/common/params.cc +++ b/common/params.cc @@ -8,7 +8,7 @@ #include "common/swaglog.h" #include "common/util.h" -#include "selfdrive/hardware/hw.h" +#include "system/hardware/hw.h" namespace { @@ -94,6 +94,7 @@ std::unordered_map keys = { {"CompletedTrainingVersion", PERSISTENT}, {"ControlsReady", CLEAR_ON_MANAGER_START | CLEAR_ON_IGNITION_ON}, {"CurrentRoute", CLEAR_ON_MANAGER_START | CLEAR_ON_IGNITION_ON}, + {"DashcamOverride", PERSISTENT}, {"DisableLogging", CLEAR_ON_MANAGER_START | CLEAR_ON_IGNITION_ON}, {"DisablePowerDown", PERSISTENT}, {"DisableRadar_Allow", PERSISTENT}, @@ -104,8 +105,6 @@ std::unordered_map keys = { {"DoReboot", CLEAR_ON_MANAGER_START}, {"DoShutdown", CLEAR_ON_MANAGER_START}, {"DoUninstall", CLEAR_ON_MANAGER_START}, - {"EnableWideCamera", CLEAR_ON_MANAGER_START}, - {"EndToEndToggle", PERSISTENT}, {"ForcePowerDown", CLEAR_ON_MANAGER_START}, {"GitBranch", PERSISTENT}, {"GitCommit", PERSISTENT}, @@ -129,6 +128,8 @@ std::unordered_map keys = { {"IsTakingSnapshot", CLEAR_ON_MANAGER_START}, {"IsUpdateAvailable", CLEAR_ON_MANAGER_START}, {"JoystickDebugMode", CLEAR_ON_MANAGER_START | CLEAR_ON_IGNITION_OFF}, + {"LaikadEphemeris", PERSISTENT | DONT_LOG}, + {"LanguageSetting", PERSISTENT}, {"LastAthenaPingTime", CLEAR_ON_MANAGER_START}, {"LastGPSPosition", PERSISTENT}, {"LastManagerExitReason", CLEAR_ON_MANAGER_START}, @@ -144,7 +145,6 @@ std::unordered_map keys = { {"PandaHeartbeatLost", CLEAR_ON_MANAGER_START | CLEAR_ON_IGNITION_OFF}, {"PandaSignatures", CLEAR_ON_MANAGER_START}, {"Passive", PERSISTENT}, - {"PrimeRedirected", PERSISTENT}, {"PrimeType", PERSISTENT}, {"RecordFront", PERSISTENT}, {"RecordFrontLock", PERSISTENT}, // for the internal fleet @@ -160,6 +160,7 @@ std::unordered_map keys = { {"UpdateFailedCount", CLEAR_ON_MANAGER_START}, {"Version", PERSISTENT}, {"VisionRadarToggle", PERSISTENT}, + {"WideCameraOnly", PERSISTENT}, {"ApiCache_Device", PERSISTENT}, {"ApiCache_DriveStats", PERSISTENT}, {"ApiCache_NavDestinations", PERSISTENT}, diff --git a/common/realtime.py b/common/realtime.py index 897683214..8a79d8d39 100644 --- a/common/realtime.py +++ b/common/realtime.py @@ -8,19 +8,14 @@ from typing import Optional, List, Union from setproctitle import getproctitle # pylint: disable=no-name-in-module from common.clock import sec_since_boot # pylint: disable=no-name-in-module, import-error -from selfdrive.hardware import PC, TICI +from system.hardware import PC # time step for each process DT_CTRL = 0.01 # controlsd DT_MDL = 0.05 # model DT_TRML = 0.5 # thermald and manager - -# driver monitoring -if TICI: - DT_DMON = 0.05 -else: - DT_DMON = 0.1 +DT_DMON = 0.05 # driver monitoring class Priority: diff --git a/common/swaglog.cc b/common/swaglog.cc index f0d0f0f50..22682dc54 100644 --- a/common/swaglog.cc +++ b/common/swaglog.cc @@ -15,7 +15,7 @@ #include "common/util.h" #include "common/version.h" -#include "selfdrive/hardware/hw.h" +#include "system/hardware/hw.h" class SwaglogState : public LogState { public: @@ -50,11 +50,7 @@ class SwaglogState : public LogState { ctx_j["dirty"] = !getenv("CLEAN"); // device type - if (Hardware::TICI()) { - ctx_j["device"] = "tici"; - } else { - ctx_j["device"] = "pc"; - } + ctx_j["device"] = Hardware::get_name(); LogState::initialize(); } }; @@ -70,8 +66,9 @@ static void log(int levelnum, const char* filename, int lineno, const char* func char levelnum_c = levelnum; zmq_send(s.sock, (levelnum_c + log_s).c_str(), log_s.length() + 1, ZMQ_NOBLOCK); } + static void cloudlog_common(int levelnum, const char* filename, int lineno, const char* func, - char* msg_buf, json11::Json::object msg_j={}) { + char* msg_buf, const json11::Json::object &msg_j={}) { std::lock_guard lk(s.lock); if (!s.initialized) s.initialize(); diff --git a/common/transformations/camera.py b/common/transformations/camera.py index 7573877a3..5d100d104 100644 --- a/common/transformations/camera.py +++ b/common/transformations/camera.py @@ -1,7 +1,6 @@ import numpy as np import common.transformations.orientation as orient -from selfdrive.hardware import TICI ## -- hardcoded hardware params -- eon_f_focal_length = 910.0 @@ -45,14 +44,9 @@ tici_fcam_intrinsics_inv = np.linalg.inv(tici_fcam_intrinsics) tici_ecam_intrinsics_inv = np.linalg.inv(tici_ecam_intrinsics) -if not TICI: - FULL_FRAME_SIZE = eon_f_frame_size - FOCAL = eon_f_focal_length - fcam_intrinsics = eon_fcam_intrinsics -else: - FULL_FRAME_SIZE = tici_f_frame_size - FOCAL = tici_f_focal_length - fcam_intrinsics = tici_fcam_intrinsics +FULL_FRAME_SIZE = tici_f_frame_size +FOCAL = tici_f_focal_length +fcam_intrinsics = tici_fcam_intrinsics W, H = FULL_FRAME_SIZE[0], FULL_FRAME_SIZE[1] diff --git a/common/version.h b/common/version.h index 1fcbdac27..de550d6be 100644 --- a/common/version.h +++ b/common/version.h @@ -1 +1 @@ -#define COMMA_VERSION "0.8.14" +#define COMMA_VERSION "0.8.15" diff --git a/common/visionimg.cc b/common/visionimg.cc deleted file mode 100644 index 1009767e0..000000000 --- a/common/visionimg.cc +++ /dev/null @@ -1,14 +0,0 @@ -#include "common/visionimg.h" - -#include - -EGLImageTexture::EGLImageTexture(const VisionBuf *buf) { - glGenTextures(1, &frame_tex); - glBindTexture(GL_TEXTURE_2D, frame_tex); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, buf->width, buf->height, 0, GL_RGB, GL_UNSIGNED_BYTE, nullptr); - glGenerateMipmap(GL_TEXTURE_2D); -} - -EGLImageTexture::~EGLImageTexture() { - glDeleteTextures(1, &frame_tex); -} diff --git a/common/visionimg.h b/common/visionimg.h deleted file mode 100644 index 0cb41a32b..000000000 --- a/common/visionimg.h +++ /dev/null @@ -1,16 +0,0 @@ -#pragma once - -#include "cereal/visionipc/visionbuf.h" - -#ifdef __APPLE__ -#include -#else -#include -#endif - -class EGLImageTexture { - public: - EGLImageTexture(const VisionBuf *buf); - ~EGLImageTexture(); - GLuint frame_tex = 0; -}; diff --git a/common/watchdog.cc b/common/watchdog.cc index 5a1020782..920df4030 100644 --- a/common/watchdog.cc +++ b/common/watchdog.cc @@ -1,12 +1,9 @@ #include "common/watchdog.h" -#include "common/timing.h" #include "common/util.h" const std::string watchdog_fn_prefix = "/dev/shm/wd_"; // + -bool watchdog_kick() { +bool watchdog_kick(uint64_t ts) { static std::string fn = watchdog_fn_prefix + std::to_string(getpid()); - - uint64_t ts = nanos_since_boot(); return util::write_file(fn.c_str(), &ts, sizeof(ts), O_WRONLY | O_CREAT) > 0; } diff --git a/common/watchdog.h b/common/watchdog.h index 7ed23aa0d..12dd2ca03 100644 --- a/common/watchdog.h +++ b/common/watchdog.h @@ -1,3 +1,5 @@ #pragma once -bool watchdog_kick(); +#include + +bool watchdog_kick(uint64_t ts); diff --git a/docs/CARS.md b/docs/CARS.md index 5100a8306..754052085 100644 --- a/docs/CARS.md +++ b/docs/CARS.md @@ -25,7 +25,8 @@ How We Rate The Cars - - No steering control below certain speeds. ### Steering Torque -- - Car has enough steering torque for comfortable highway driving. +- - Car has enough steering torque to take tighter turns. +- - Car has enough steering torque for comfortable highway driving. - - Limited ability to make turns. ### Actively Maintained @@ -34,7 +35,7 @@ How We Rate The Cars **All supported cars can move between the tiers as support changes.** -## Gold Cars +# Gold - 30 cars |Make|Model|Supported Package|openpilot ACC|Stop and Go|Steer to 0|Steering Torque|Actively Maintained| |---|---|---|:---:|:---:|:---:|:---:|:---:| @@ -48,14 +49,12 @@ How We Rate The Cars |Kia|Niro Electric 2021|All|||||| |Kia|Niro Electric 2022|All|||||| |Kia|Telluride 2020|SCC + LKAS|||||| -|Lexus|ES 2019-21|All|||||| +|Lexus|ES 2019-22|All|||||| |Lexus|ES Hybrid 2019-22|All|||||| -|Lexus|NX 2020|All|||||| +|Lexus|NX 2020-21|All|||||| +|Lexus|NX Hybrid 2020-21|All|||||| |Lexus|RX 2020-22|All|||||| -|Lexus|RX Hybrid 2020-21|All|||||| -|Lexus|UX Hybrid 2019-21|All|||||| -|Toyota|Alphard 2019-20|All|||||| -|Toyota|Alphard Hybrid 2021|All|||||| +|Lexus|UX Hybrid 2019-22|All|||||| |Toyota|Avalon 2022|All|||||| |Toyota|Avalon Hybrid 2022|All|||||| |Toyota|Camry 2021-22|All||[4](#footnotes)|||| @@ -71,18 +70,17 @@ How We Rate The Cars |Toyota|RAV4 2019-21|All|||||| |Toyota|RAV4 Hybrid 2019-21|All|||||| -## Silver Cars +# Silver - 70 cars |Make|Model|Supported Package|openpilot ACC|Stop and Go|Steer to 0|Steering Torque|Actively Maintained| |---|---|---|:---:|:---:|:---:|:---:|:---:| |Audi|A3 2014-19|ACC + Lane Assist|||||| |Audi|A3 Sportback e-tron 2017-18|ACC + Lane Assist|||||| -|Audi|Q2 2018|ACC + Lane Assist|||||| -|Audi|Q3 2020-21|ACC + Lane Assist|||||| |Audi|RS3 2018|ACC + Lane Assist|||||| |Audi|S3 2015-17|ACC + Lane Assist|||||| -|Genesis|G70 2018|All|||||| -|Genesis|G80 2018|All|||||| +|Chevrolet|Volt 2017-18[1](#footnotes)|Adaptive Cruise|||||| +|Genesis|G70 2018-19|All|||||| +|Genesis|G80 2017-19|All|||||| |Hyundai|Elantra 2021-22|SCC + LKAS|||||| |Hyundai|Elantra Hybrid 2021-22|SCC + LKAS|||||| |Hyundai|Ioniq Electric 2020|SCC + LKAS|||||| @@ -94,52 +92,48 @@ How We Rate The Cars |Hyundai|Santa Fe 2021-22|All|||||| |Hyundai|Santa Fe Hybrid 2022|All|||||| |Hyundai|Santa Fe Plug-in Hybrid 2022|All|||||| -|Hyundai|Sonata 2018-19|SCC + LKAS|||||| |Hyundai|Tucson Diesel 2019|SCC + LKAS|||||| |Kia|Ceed 2019|SCC + LKAS|||||| +|Kia|EV6 2022|All|||||| |Kia|Forte 2018|SCC + LKAS|||||| |Kia|Forte 2019-21|SCC + LKAS|||||| -|Kia|K5 2021-22|SCC + LFA|||||| +|Kia|K5 2021-22|SCC|||||| |Kia|Niro Hybrid 2021|SCC + LKAS|||||| |Kia|Niro Hybrid 2022|SCC + LKAS|||||| |Kia|Optima 2019|SCC + LKAS|||||| |Kia|Seltos 2021|SCC + LKAS|||||| |Kia|Sorento 2018|SCC + LKAS|||||| |Kia|Sorento 2019|SCC + LKAS|||||| -|Kia|Stinger 2018|SCC + LKAS|||||| +|Kia|Stinger 2018-20|SCC + LKAS|||||| |Lexus|CT Hybrid 2017-18|LSS|[3](#footnotes)||||| |Lexus|ES Hybrid 2017-18|LSS|[3](#footnotes)||||| |Lexus|NX 2018-19|All|[3](#footnotes)||||| |Lexus|NX Hybrid 2018-19|All|[3](#footnotes)||||| -|Lexus|RX 2016-18|All|[3](#footnotes)||||| -|Lexus|RX Hybrid 2016-19|All|[3](#footnotes)||||| -|Mazda|CX-5 2022|All|||||| +|Lexus|RX Hybrid 2020-21|All|||||| +|Nissan|Altima 2019-20|ProPILOT|||||| +|Nissan|Leaf 2018-22|ProPILOT|||||| +|Nissan|Rogue 2018-20|ProPILOT|||||| +|Nissan|X-Trail 2017|ProPILOT|||||| |SEAT|Ateca 2018|Driver Assistance|||||| |SEAT|Leon 2014-20|Driver Assistance|||||| -|Subaru|Crosstrek 2018-19|EyeSight|||||| -|Subaru|Forester 2019-21|EyeSight|||||| -|Subaru|Impreza 2017-19|EyeSight|||||| -|Škoda|Kamiq 2021[6](#footnotes)|Driver Assistance|||||| -|Škoda|Karoq 2019|Driver Assistance|||||| -|Škoda|Kodiaq 2018-19|Driver Assistance|||||| -|Škoda|Octavia 2015, 2018-19|Driver Assistance|||||| -|Škoda|Octavia RS 2016|Driver Assistance|||||| -|Škoda|Scala 2020|Driver Assistance|||||| -|Škoda|Superb 2015-18|Driver Assistance|||||| -|Toyota|Avalon 2019-21|TSS-P|[3](#footnotes)||||| -|Toyota|Avalon Hybrid 2019-21|TSS-P|[3](#footnotes)||||| -|Toyota|C-HR 2017-21|All|||||| -|Toyota|C-HR Hybrid 2017-19|All|||||| +|Subaru|Ascent 2019-21|All|||||| +|Subaru|Crosstrek 2020-21|EyeSight|||||| +|Subaru|Forester 2019-22|All|||||| +|Subaru|Impreza 2020-22|EyeSight|||||| +|Subaru|XV 2020-21|EyeSight|||||| +|Toyota|Alphard 2019-20|All|||||| +|Toyota|Alphard Hybrid 2021|All|||||| |Toyota|Camry 2018-20|All||[4](#footnotes)|||| |Toyota|Camry Hybrid 2018-20|All||[4](#footnotes)|||| +|Toyota|Corolla Cross 2020-21 (Non-US only)|All|||||| |Toyota|Highlander 2017-19|All|[3](#footnotes)||||| |Toyota|Highlander Hybrid 2017-19|All|[3](#footnotes)||||| +|Toyota|Prius 2016-20|TSS-P|[3](#footnotes)||||| +|Toyota|Prius Prime 2017-20|All|[3](#footnotes)||||| |Toyota|RAV4 2022|All|||||| |Toyota|RAV4 Hybrid 2016-18|TSS-P|[3](#footnotes)||||| |Toyota|RAV4 Hybrid 2022|All|||||| -|Toyota|Sienna 2018-20|All|[3](#footnotes)||||| -|Volkswagen|Arteon 2018, 2021[8](#footnotes)|Driver Assistance|||||| -|Volkswagen|Atlas 2018-19, 2022[8](#footnotes)|Driver Assistance|||||| +|Volkswagen|Atlas 2018-19, 2022[7](#footnotes)|Driver Assistance|||||| |Volkswagen|e-Golf 2014, 2018-20|Driver Assistance|||||| |Volkswagen|Golf 2015-20|Driver Assistance|||||| |Volkswagen|Golf Alltrack 2017-18|Driver Assistance|||||| @@ -148,77 +142,93 @@ How We Rate The Cars |Volkswagen|Golf R 2016-19|Driver Assistance|||||| |Volkswagen|Golf SportsVan 2016|Driver Assistance|||||| |Volkswagen|Golf SportWagen 2015|Driver Assistance|||||| -|Volkswagen|Jetta 2018-21|Driver Assistance|||||| -|Volkswagen|Jetta GLI 2021|Driver Assistance|||||| -|Volkswagen|Passat 2016-18[7](#footnotes)|Driver Assistance|||||| +|Volkswagen|Passat 2015-19[6](#footnotes)|Driver Assistance|||||| |Volkswagen|Polo 2020|Driver Assistance|||||| -|Volkswagen|T-Cross 2021[8](#footnotes)|Driver Assistance|||||| -|Volkswagen|T-Roc 2021[8](#footnotes)|Driver Assistance|||||| -|Volkswagen|Taos 2022[8](#footnotes)|Driver Assistance|||||| -|Volkswagen|Tiguan 2019-22[8](#footnotes)|Driver Assistance|||||| -|Volkswagen|Touran 2017|Driver Assistance|||||| -## Bronze Cars +# Bronze - 80 cars |Make|Model|Supported Package|openpilot ACC|Stop and Go|Steer to 0|Steering Torque|Actively Maintained| |---|---|---|:---:|:---:|:---:|:---:|:---:| |Acura|ILX 2016-19|AcuraWatch Plus|||||| |Acura|RDX 2016-18|AcuraWatch Plus|||||| -|Acura|RDX 2019-21|All|||||| -|Cadillac|Escalade ESV 2016[1](#footnotes)|ACC + LKAS|||||| -|Chevrolet|Volt 2017-18[1](#footnotes)|Adaptive Cruise|||||| -|Chrysler|Pacifica 2017-18|Adaptive Cruise|||||| -|Chrysler|Pacifica 2020|Adaptive Cruise|||||| -|Chrysler|Pacifica Hybrid 2017-18|Adaptive Cruise|||||| -|Chrysler|Pacifica Hybrid 2019-21|Adaptive Cruise|||||| -|Genesis|G90 2018|All|||||| -|GMC|Acadia 2018[1](#footnotes)|Adaptive Cruise|||||| -|Honda|Accord 2018-21|All|||||| -|Honda|Accord Hybrid 2018-21|All|||||| +|Acura|RDX 2019-22|All|||||| +|Audi|Q2 2018|ACC + Lane Assist|||||| +|Audi|Q3 2020-21|ACC + Lane Assist|||||| +|Cadillac|Escalade ESV 2016[1](#footnotes)|ACC + LKAS|||||| +|Chrysler|Pacifica 2017-18|Adaptive Cruise|||||| +|Chrysler|Pacifica 2019-20|Adaptive Cruise|||||| +|Chrysler|Pacifica Hybrid 2017-18|Adaptive Cruise|||||| +|Chrysler|Pacifica Hybrid 2019-22|Adaptive Cruise|||||| +|Genesis|G90 2017-18|All|||||| +|GMC|Acadia 2018[1](#footnotes)|Adaptive Cruise|||||| +|Honda|Accord 2018-22|All|||||| +|Honda|Accord Hybrid 2018-22|All|||||| |Honda|Civic 2016-18|Honda Sensing|||||| -|Honda|Civic 2019-20|All|||[2](#footnotes)||| +|Honda|Civic 2019-21|All|||[2](#footnotes)||| +|Honda|Civic 2022|All|||||| |Honda|Civic Hatchback 2017-21|Honda Sensing|||||| +|Honda|Civic Hatchback 2022|All|||||| |Honda|CR-V 2015-16|Touring|||||| -|Honda|CR-V 2017-21|Honda Sensing|||||| +|Honda|CR-V 2017-22|Honda Sensing|||||| |Honda|CR-V Hybrid 2017-19|Honda Sensing|||||| |Honda|e 2020|All|||||| -|Honda|Fit 2018-19|Honda Sensing|||||| +|Honda|Fit 2018-20|Honda Sensing|||||| |Honda|Freed 2020|Honda Sensing|||||| -|Honda|HR-V 2019-20|Honda Sensing|||||| -|Honda|Insight 2019-21|All|||||| +|Honda|HR-V 2019-22|Honda Sensing|||||| +|Honda|Insight 2019-22|All|||||| |Honda|Inspire 2018|All|||||| -|Honda|Odyssey 2018-20|Honda Sensing|||||| +|Honda|Odyssey 2018-22|Honda Sensing|||||| |Honda|Passport 2019-21|All|||||| -|Honda|Pilot 2016-21|Honda Sensing|||||| +|Honda|Pilot 2016-22|Honda Sensing|||||| |Honda|Ridgeline 2017-22|Honda Sensing|||||| -|Hyundai|Elantra 2017-19|SCC + LKAS|||||| +|Hyundai|Elantra 2017-19|SCC + LKAS|||||| |Hyundai|Genesis 2015-16|SCC + LKAS|||||| |Hyundai|Ioniq Electric 2019|SCC + LKAS|||||| |Hyundai|Ioniq Hybrid 2017-19|SCC + LKAS|||||| |Hyundai|Ioniq Plug-in Hybrid 2019|SCC + LKAS|||||| -|Hyundai|Veloster 2019-20|SCC + LKAS|||||| -|Jeep|Grand Cherokee 2016-18|Adaptive Cruise|||||| -|Jeep|Grand Cherokee 2019-20|Adaptive Cruise|||||| +|Hyundai|Sonata 2018-19|SCC + LKAS|||||| +|Hyundai|Tucson 2021|SCC + LKAS|||||| +|Hyundai|Veloster 2019-20|SCC + LKAS|||||| +|Jeep|Grand Cherokee 2016-18|Adaptive Cruise|||||| +|Jeep|Grand Cherokee 2019-21|Adaptive Cruise|||||| |Kia|Niro Plug-in Hybrid 2019|SCC + LKAS|||||| |Kia|Optima 2017|SCC + LKAS|||||| |Lexus|IS 2017-19|All|||||| -|Lexus|RC 2020|All|||||| -|Mazda|CX-9 2021|All|||||| -|Nissan|Altima 2019-20|ProPILOT|||||| -|Nissan|Leaf 2018-22|ProPILOT|||||| -|Nissan|Rogue 2018-20|ProPILOT|||||| -|Nissan|X-Trail 2017|ProPILOT|||||| -|Subaru|Ascent 2019-20|EyeSight|||||| -|Subaru|Crosstrek 2020-21|EyeSight|||||| -|Subaru|Impreza 2020-21|EyeSight|||||| +|Lexus|RC 2017-2020|All|||||| +|Lexus|RX 2016-18|All|[3](#footnotes)||||| +|Lexus|RX Hybrid 2016-19|All|[3](#footnotes)||||| +|Mazda|CX-5 2022|All|||||| +|Mazda|CX-9 2021-22|All|||||| +|Ram|1500 2019-22|Adaptive Cruise|||||| +|Subaru|Crosstrek 2018-19|EyeSight|||||| +|Subaru|Impreza 2017-19|EyeSight|||||| +|Subaru|XV 2018-19|EyeSight|||||| +|Škoda|Kamiq 2021[5](#footnotes)|Driver Assistance|||||| +|Škoda|Karoq 2019|Driver Assistance|||||| +|Škoda|Kodiaq 2018-19|Driver Assistance|||||| +|Škoda|Octavia 2015, 2018-19|Driver Assistance|||||| +|Škoda|Octavia RS 2016|Driver Assistance|||||| +|Škoda|Scala 2020|Driver Assistance|||||| +|Škoda|Superb 2015-18|Driver Assistance|||||| |Toyota|Avalon 2016-18|TSS-P|[3](#footnotes)||||| +|Toyota|Avalon 2019-21|TSS-P|[3](#footnotes)||||| +|Toyota|Avalon Hybrid 2019-21|TSS-P|[3](#footnotes)||||| +|Toyota|C-HR 2017-21|All|||||| +|Toyota|C-HR Hybrid 2017-19|All|||||| |Toyota|Corolla 2017-19|All|[3](#footnotes)||||| -|Toyota|Prius 2016-20|TSS-P|[3](#footnotes)|||[5](#footnotes)|| -|Toyota|Prius Prime 2017-20|All|[3](#footnotes)|||[5](#footnotes)|| -|Toyota|Prius v 2017|TSS-P|[3](#footnotes)|||[5](#footnotes)|| +|Toyota|Prius v 2017|TSS-P|[3](#footnotes)||||| |Toyota|RAV4 2016-18|TSS-P|[3](#footnotes)||||| -|Volkswagen|California 2021[8](#footnotes)|Driver Assistance|||||| -|Volkswagen|Caravelle 2020[8](#footnotes)|Driver Assistance|||||| +|Toyota|Sienna 2018-20|All|[3](#footnotes)||||| +|Volkswagen|Arteon 2018, 2021[7](#footnotes)|Driver Assistance|||||| +|Volkswagen|California 2021[7](#footnotes)|Driver Assistance|||||| +|Volkswagen|Caravelle 2020[7](#footnotes)|Driver Assistance|||||| +|Volkswagen|Jetta 2018-21|Driver Assistance|||||| +|Volkswagen|Jetta GLI 2021|Driver Assistance|||||| +|Volkswagen|T-Cross 2021[7](#footnotes)|Driver Assistance|||||| +|Volkswagen|T-Roc 2021[7](#footnotes)|Driver Assistance|||||| +|Volkswagen|Taos 2022[7](#footnotes)|Driver Assistance|||||| +|Volkswagen|Tiguan 2019-22[7](#footnotes)|Driver Assistance|||||| +|Volkswagen|Touran 2017|Driver Assistance|||||| @@ -226,10 +236,9 @@ How We Rate The Cars 22019 Honda Civic 1.6L Diesel Sedan does not have ALC below 12mph.
3When disconnecting the Driver Support Unit (DSU), openpilot Adaptive Cruise Control (ACC) will replace stock Adaptive Cruise Control (ACC). NOTE: disconnecting the DSU disables Automatic Emergency Braking (AEB).
428mph for Camry 4CYL L, 4CYL LE and 4CYL SE which don't have Full-Speed Range Dynamic Radar Cruise Control.
-5An inaccurate steering wheel angle sensor makes precise control difficult.
-6Not including the China market Kamiq, which is based on the (currently) unsupported PQ34 platform.
-7Not including the USA/China market Passat, which is based on the (currently) unsupported PQ35/NMS platform.
-8Model-years 2021 and beyond may have a new camera harness design, which isn't yet available from the comma store. Before ordering, remove the Lane Assist camera cover and check to see if the connector is black (older design) or light brown (newer design). For the newer design, in the interim, choose "VW J533 Development" from the vehicle drop-down for a harness that integrates at the CAN gateway inside the dashboard.
+5Not including the China market Kamiq, which is based on the (currently) unsupported PQ34 platform.
+6Not including the USA/China market Passat, which is based on the (currently) unsupported PQ35/NMS platform.
+7Model-years 2021 and beyond may have a new camera harness design, which isn't yet available from the comma store. Before ordering, remove the Lane Assist camera cover and check to see if the connector is black (older design) or light brown (newer design). For the newer design, in the interim, choose "VW J533 Development" from the vehicle drop-down for a harness that integrates at the CAN gateway inside the dashboard.
## Community Maintained Cars Although they're not upstream, the community has openpilot running on other makes and models. See the 'Community Supported Models' section of each make [on our wiki](https://wiki.comma.ai/). \ No newline at end of file diff --git a/laika/__init__.py b/laika/__init__.py new file mode 100644 index 000000000..661c4584d --- /dev/null +++ b/laika/__init__.py @@ -0,0 +1,2 @@ +from .astro_dog import AstroDog +assert AstroDog diff --git a/laika/astro_dog.py b/laika/astro_dog.py new file mode 100644 index 000000000..ea32e350d --- /dev/null +++ b/laika/astro_dog.py @@ -0,0 +1,367 @@ +from collections import defaultdict +from concurrent.futures import ThreadPoolExecutor +from typing import DefaultDict, Dict, Iterable, List, Optional, Union + +from .constants import SECS_IN_DAY, SECS_IN_HR +from .helpers import ConstellationId, get_constellation, get_closest, get_el_az, TimeRangeHolder +from .ephemeris import Ephemeris, EphemerisType, GLONASSEphemeris, GPSEphemeris, PolyEphemeris, parse_sp3_orbits, parse_rinex_nav_msg_gps, \ + parse_rinex_nav_msg_glonass +from .downloader import download_orbits_gps, download_orbits_russia_src, download_nav, download_ionex, download_dcb, download_prediction_orbits_russia_src +from .downloader import download_cors_station +from .trop import saast +from .iono import IonexMap, parse_ionex +from .dcb import DCB, parse_dcbs +from .gps_time import GPSTime +from .dgps import get_closest_station_names, parse_dgps +from . import constants + +MAX_DGPS_DISTANCE = 100_000 # in meters, because we're not barbarians + + +class AstroDog: + ''' + auto_update: flag indicating whether laika should fetch files from web automatically + cache_dir: directory where data files are downloaded to and cached + dgps: flag indicating whether laika should use dgps (CORS) + data to calculate pseudorange corrections + valid_const: list of constellation identifiers laika will try process + valid_ephem_types: set of ephemeris types that are allowed to use and download. + Default is set to use all orbit ephemeris types + clear_old_ephemeris: flag indicating if ephemeris for an individual satellite should be overwritten when new ephemeris is added. + ''' + + def __init__(self, auto_update=True, + cache_dir='/tmp/gnss/', + dgps=False, + valid_const=('GPS', 'GLONASS'), + valid_ephem_types=EphemerisType.all_orbits(), + clear_old_ephemeris=False): + self.auto_update = auto_update + self.cache_dir = cache_dir + self.clear_old_ephemeris = clear_old_ephemeris + self.dgps = dgps + if not isinstance(valid_ephem_types, Iterable): + valid_ephem_types = [valid_ephem_types] + self.pull_orbit = len(set(EphemerisType.all_orbits()) & set(valid_ephem_types)) > 0 + self.pull_nav = EphemerisType.NAV in valid_ephem_types + self.valid_const = valid_const + self.valid_ephem_types = valid_ephem_types + + self.orbit_fetched_times = TimeRangeHolder() + self.nav_fetched_times = TimeRangeHolder() + self.dcbs_fetched_times = TimeRangeHolder() + + self.dgps_delays = [] + self.ionex_maps: List[IonexMap] = [] + self.orbits: DefaultDict[str, List[PolyEphemeris]] = defaultdict(list) + self.nav: DefaultDict[str, List[Union[GPSEphemeris, GLONASSEphemeris]]] = defaultdict(list) + self.dcbs: DefaultDict[str, List[DCB]] = defaultdict(list) + + self.cached_ionex: Optional[IonexMap] = None + self.cached_dgps = None + self.cached_orbit: DefaultDict[str, Optional[PolyEphemeris]] = defaultdict(lambda: None) + self.cached_nav: DefaultDict[str, Union[GPSEphemeris, GLONASSEphemeris, None]] = defaultdict(lambda: None) + self.cached_dcb: DefaultDict[str, Optional[DCB]] = defaultdict(lambda: None) + + def get_ionex(self, time) -> Optional[IonexMap]: + ionex: Optional[IonexMap] = self._get_latest_valid_data(self.ionex_maps, self.cached_ionex, self.get_ionex_data, time) + if ionex is None: + if self.auto_update: + raise RuntimeError("Pulled ionex, but still can't get valid for time " + str(time)) + else: + self.cached_ionex = ionex + return ionex + + def get_nav(self, prn, time): + skip_download = time in self.nav_fetched_times + nav = self._get_latest_valid_data(self.nav[prn], self.cached_nav[prn], self.get_nav_data, time, skip_download) + if nav is not None: + self.cached_nav[prn] = nav + return nav + + @staticmethod + def _select_valid_temporal_items(item_dict, time, cache): + '''Returns only valid temporal item for specific time from currently fetched + data.''' + result = {} + for prn, temporal_objects in item_dict.items(): + cached = cache[prn] + if cached is not None and cached.valid(time): + obj = cached + else: + obj = get_closest(time, temporal_objects) + if obj is None or not obj.valid(time): + continue + cache[prn] = obj + result[prn] = obj + return result + + def get_navs(self, time): + if time not in self.nav_fetched_times and self.auto_update: + self.get_nav_data(time) + return AstroDog._select_valid_temporal_items(self.nav, time, self.cached_nav) + + def get_orbit(self, prn: str, time: GPSTime): + skip_download = time in self.orbit_fetched_times + orbit = self._get_latest_valid_data(self.orbits[prn], self.cached_orbit[prn], self.get_orbit_data, time, skip_download) + if orbit is not None: + self.cached_orbit[prn] = orbit + return orbit + + def get_orbits(self, time): + if time not in self.orbit_fetched_times: + self.get_orbit_data(time) + return AstroDog._select_valid_temporal_items(self.orbits, time, self.cached_orbit) + + def get_dcb(self, prn, time): + skip_download = time in self.dcbs_fetched_times + dcb = self._get_latest_valid_data(self.dcbs[prn], self.cached_dcb[prn], self.get_dcb_data, time, skip_download) + if dcb is not None: + self.cached_dcb[prn] = dcb + return dcb + + def get_dgps_corrections(self, time, recv_pos): + latest_data = self._get_latest_valid_data(self.dgps_delays, self.cached_dgps, self.get_dgps_data, time, recv_pos=recv_pos) + if latest_data is None: + if self.auto_update: + raise RuntimeError("Pulled dgps, but still can't get valid for time " + str(time)) + else: + self.cached_dgps = latest_data + return latest_data + + def add_orbits(self, new_ephems: Dict[str, List[Ephemeris]]): + self._add_ephems(new_ephems, self.orbits, self.orbit_fetched_times) + + def add_navs(self, new_ephems: Dict[str, List[Ephemeris]]): + self._add_ephems(new_ephems, self.nav, self.nav_fetched_times) + + def _add_ephems(self, new_ephems: Dict[str, List[Ephemeris]], ephems_dict, fetched_times): + for k, v in new_ephems.items(): + if len(v) > 0: + if self.clear_old_ephemeris: + ephems_dict[k] = v + else: + ephems_dict[k].extend(v) + + min_epochs = [] + max_epochs = [] + for v in new_ephems.values(): + if len(v) > 0: + min_ephem, max_ephem = self.get_epoch_range(v) + min_epochs.append(min_ephem) + max_epochs.append(max_ephem) + if len(min_epochs) > 0: + min_epoch = min(min_epochs) + max_epoch = max(max_epochs) + fetched_times.add(min_epoch, max_epoch) + + def get_nav_data(self, time): + def download_and_parse(constellation, parse_rinex_nav_func): + file_path = download_nav(time, cache_dir=self.cache_dir, constellation=constellation) + return parse_rinex_nav_func(file_path) if file_path else {} + + fetched_ephems = {} + + if 'GPS' in self.valid_const: + fetched_ephems = download_and_parse(ConstellationId.GPS, parse_rinex_nav_msg_gps) + if 'GLONASS' in self.valid_const: + for k, v in download_and_parse(ConstellationId.GLONASS, parse_rinex_nav_msg_glonass).items(): + fetched_ephems.setdefault(k, []).extend(v) + self.add_navs(fetched_ephems) + + if sum([len(v) for v in fetched_ephems.values()]) == 0: + begin_day = GPSTime(time.week, SECS_IN_DAY * (time.tow // SECS_IN_DAY)) + end_day = GPSTime(time.week, SECS_IN_DAY * (1 + (time.tow // SECS_IN_DAY))) + self.nav_fetched_times.add(begin_day, end_day) + + def download_parse_orbit(self, gps_time: GPSTime, skip_before_epoch=None) -> Dict[str, List[PolyEphemeris]]: + # Download multiple days to be able to polyfit at the start-end of the day + time_steps = [gps_time - SECS_IN_DAY, gps_time, gps_time + SECS_IN_DAY] + with ThreadPoolExecutor() as executor: + futures_other = [executor.submit(download_orbits_russia_src, t, self.cache_dir, self.valid_ephem_types) for t in time_steps] + futures_gps = None + if "GPS" in self.valid_const: + futures_gps = [executor.submit(download_orbits_gps, t, self.cache_dir, self.valid_ephem_types) for t in time_steps] + + ephems_other = parse_sp3_orbits([f.result() for f in futures_other if f.result()], self.valid_const, skip_before_epoch) + ephems_us = parse_sp3_orbits([f.result() for f in futures_gps if f.result()], self.valid_const, skip_before_epoch) if futures_gps else {} + + return {k: ephems_other.get(k, []) + ephems_us.get(k, []) for k in set(list(ephems_other.keys()) + list(ephems_us.keys()))} + + def download_parse_prediction_orbit(self, gps_time: GPSTime): + assert EphemerisType.ULTRA_RAPID_ORBIT in self.valid_ephem_types + skip_until_epoch = gps_time - 2 * SECS_IN_HR + + result = download_prediction_orbits_russia_src(gps_time, self.cache_dir) + if result is not None: + result = [result] + elif "GPS" in self.valid_const: + # Slower fallback. Russia src prediction orbits are published from 2022 + result = [download_orbits_gps(t, self.cache_dir, self.valid_ephem_types) for t in [gps_time - SECS_IN_DAY, gps_time]] + if result is None: + return {} + return parse_sp3_orbits(result, self.valid_const, skip_until_epoch=skip_until_epoch) + + def get_orbit_data(self, time: GPSTime, only_predictions=False): + if only_predictions: + ephems_sp3 = self.download_parse_prediction_orbit(time) + else: + ephems_sp3 = self.download_parse_orbit(time) + if sum([len(v) for v in ephems_sp3.values()]) < 5: + raise RuntimeError(f'No orbit data found. For Time {time.as_datetime()} constellations {self.valid_const} valid ephem types {self.valid_ephem_types}') + + self.add_orbits(ephems_sp3) + + def get_dcb_data(self, time): + file_path_dcb = download_dcb(time, cache_dir=self.cache_dir) + dcbs = parse_dcbs(file_path_dcb, self.valid_const) + for dcb in dcbs: + self.dcbs[dcb.prn].append(dcb) + + if len(dcbs) != 0: + min_epoch, max_epoch = self.get_epoch_range(dcbs) + self.dcbs_fetched_times.add(min_epoch, max_epoch) + + def get_epoch_range(self, new_ephems): + min_ephem = min(new_ephems, key=lambda e: e.epoch) + max_ephem = max(new_ephems, key=lambda e: e.epoch) + min_epoch = min_ephem.epoch - min_ephem.max_time_diff + max_epoch = max_ephem.epoch + max_ephem.max_time_diff + return min_epoch, max_epoch + + def get_ionex_data(self, time): + file_path_ionex = download_ionex(time, cache_dir=self.cache_dir) + ionex_maps = parse_ionex(file_path_ionex) + for im in ionex_maps: + self.ionex_maps.append(im) + + def get_dgps_data(self, time, recv_pos): + station_names = get_closest_station_names(recv_pos, k=8, max_distance=MAX_DGPS_DISTANCE, cache_dir=self.cache_dir) + for station_name in station_names: + file_path_station = download_cors_station(time, station_name, cache_dir=self.cache_dir) + if file_path_station: + dgps = parse_dgps(station_name, file_path_station, + self, max_distance=MAX_DGPS_DISTANCE, + required_constellations=self.valid_const) + if dgps is not None: + self.dgps_delays.append(dgps) + break + + def get_tgd_from_nav(self, prn, time): + if get_constellation(prn) not in self.valid_const: + return None + + eph = self.get_nav(prn, time) + + if eph: + return eph.get_tgd() + return None + + def get_sat_info(self, prn, time): + if get_constellation(prn) not in self.valid_const: + return None + eph = None + if self.pull_orbit: + eph = self.get_orbit(prn, time) + if not eph and self.pull_nav: + eph = self.get_nav(prn, time) + + if eph: + return eph.get_sat_info(time) + return None + + def get_all_sat_info(self, time): + ephs = {} + if self.pull_orbit: + ephs = self.get_orbits(time) + if len(ephs) == 0 and self.pull_nav: + ephs = self.get_navs(time) + + return {prn: eph.get_sat_info(time) for prn, eph in ephs.items()} + + def get_glonass_channel(self, prn, time): + nav = self.get_nav(prn, time) + if nav: + return nav.channel + return None + + def get_frequency(self, prn, time, signal='C1C'): + if get_constellation(prn) == 'GPS': + switch = {'1': constants.GPS_L1, + '2': constants.GPS_L2, + '5': constants.GPS_L5, + '6': constants.GALILEO_E6, + '7': constants.GALILEO_E5B, + '8': constants.GALILEO_E5AB} + freq = switch.get(signal[1]) + if freq: + return freq + raise NotImplementedError("Dont know this GPS frequency: ", signal, prn) + elif get_constellation(prn) == 'GLONASS': + n = self.get_glonass_channel(prn, time) + if n is None: + return None + switch = {'1': constants.GLONASS_L1 + n * constants.GLONASS_L1_DELTA, + '2': constants.GLONASS_L2 + n * constants.GLONASS_L2_DELTA, + '5': constants.GLONASS_L5 + n * constants.GLONASS_L5_DELTA, + '6': constants.GALILEO_E6, + '7': constants.GALILEO_E5B, + '8': constants.GALILEO_E5AB} + freq = switch.get(signal[1]) + if freq: + return freq + raise NotImplementedError("Dont know this GLONASS frequency: ", signal, prn) + + def get_delay(self, prn, time, rcv_pos, no_dgps=False, signal='C1C', freq=None): + sat_info = self.get_sat_info(prn, time) + if sat_info is None: + return None + sat_pos = sat_info[0] + el, az = get_el_az(rcv_pos, sat_pos) + if el < 0.2: + return None + + if self.dgps and not no_dgps: + return self._get_delay_dgps(prn, rcv_pos, time) + + ionex = self.get_ionex(time) + if not freq and ionex is not None: + freq = self.get_frequency(prn, time, signal) + dcb = self.get_dcb(prn, time) + # When using internet we expect all data or return None + if self.auto_update and (ionex is None or dcb is None or freq is None): + return None + iono_delay = ionex.get_delay(rcv_pos, az, el, sat_pos, time, freq) if ionex is not None else 0. + trop_delay = saast(rcv_pos, el) + code_bias = dcb.get_delay(signal) if dcb is not None else 0. + return iono_delay + trop_delay + code_bias + + def _get_delay_dgps(self, prn, rcv_pos, time): + dgps_corrections = self.get_dgps_corrections(time, rcv_pos) + if dgps_corrections is None: + return None + return dgps_corrections.get_delay(prn, time) + + def _get_latest_valid_data(self, data, latest_data, download_data_func, time, skip_download=False, recv_pos=None): + def is_valid(latest_data): + if recv_pos is None: + return latest_data is not None and latest_data.valid(time) + else: + return latest_data is not None and latest_data.valid(time, recv_pos) + + if is_valid(latest_data): + return latest_data + + latest_data = get_closest(time, data, recv_pos=recv_pos) + if is_valid(latest_data): + return latest_data + if skip_download or not self.auto_update: + return None + if recv_pos is not None: + download_data_func(time, recv_pos) + else: + download_data_func(time) + latest_data = get_closest(time, data, recv_pos=recv_pos) + if is_valid(latest_data): + return latest_data + return None diff --git a/laika/constants.py b/laika/constants.py new file mode 100644 index 000000000..6b6d33110 --- /dev/null +++ b/laika/constants.py @@ -0,0 +1,34 @@ +# These are all from IS-GPS-200G unless otherwise noted + +SPEED_OF_LIGHT = 2.99792458e8 # m/s + +# Physical parameters of the Earth +EARTH_GM = 3.986005e14 # m^3/s^2 (gravitational constant * mass of earth) +EARTH_RADIUS = 6.3781e6 # m +EARTH_ROTATION_RATE = 7.2921151467e-005 # rad/s (WGS84 earth rotation rate) + +# GPS system parameters: +GPS_L1 = l1 = 1.57542e9 # Hz +GPS_L2 = l2 = 1.22760e9 # Hz +GPS_L5 = l5 = 1.17645e9 # Hz Also E5 + +#GLONASS system parameters +#TODO this is old convention +GLONASS_L1 = 1.602e9 +GLONASS_L1_DELTA = 0.5625e6 +GLONASS_L2 = 1.246e9 +GLONASS_L2_DELTA = 0.4375e6 +GLONASS_L5 = 1.201e9 +GLONASS_L5_DELTA = 0.4375e6 + +#Galileo system parameters: # Has additional frequencies on E6 +#Source RINEX 2.11 document +GALILEO_E5B = 1.207140e9 # Hz +GALILEO_E5AB = 1.191795e9 # Hz +GALILEO_E6 = 1.27875e9 # Hz + +SECS_IN_MIN = 60 +SECS_IN_HR = 60*SECS_IN_MIN +SECS_IN_DAY = 24*SECS_IN_HR +SECS_IN_WEEK = 7*SECS_IN_DAY +SECS_IN_YEAR = 365*SECS_IN_DAY diff --git a/laika/dcb.py b/laika/dcb.py new file mode 100644 index 000000000..06017ff4c --- /dev/null +++ b/laika/dcb.py @@ -0,0 +1,84 @@ +from datetime import datetime +from .constants import SECS_IN_HR, SECS_IN_WEEK, \ + SPEED_OF_LIGHT, GPS_L1, GPS_L2 +from .gps_time import GPSTime +from .helpers import get_constellation +import warnings + + +class DCB: + def __init__(self, prn, data): + self.max_time_diff = 2*SECS_IN_WEEK + self.prn = prn + self.epoch = data['epoch'] + self.healthy = True + if 'C1W_C2W' in data: + self.C1W_C2W = data['C1W_C2W'] + elif 'C1P_C2P' in data: + self.C1W_C2W = data['C1P_C2P'] + else: + self.healthy = False + if 'C1C_C1W' in data: + self.C1C_C1W = data['C1C_C1W'] + elif 'C1C_C1P' in data: + self.C1C_C1W = data['C1C_C1P'] + else: + self.healthy = False + + def valid(self, time): + return abs(time - self.epoch) <= self.max_time_diff and self.healthy + + def get_delay(self, signal): + if signal == 'C1C': + return (- SPEED_OF_LIGHT*1e-9*self.C1W_C2W*GPS_L2**2/(GPS_L1**2 - GPS_L2**2) + + SPEED_OF_LIGHT*1e-9*self.C1C_C1W) + if signal == 'C2P': + return (- SPEED_OF_LIGHT*1e-9*self.C1W_C2W*GPS_L1**2/(GPS_L1**2 - GPS_L2**2)) + if signal == 'C1P': + return (SPEED_OF_LIGHT*1e-9*self.C1C_C1W) + ## Todo: update dcb database and get delay to include additional signals + if signal == 'C2C': + warnings.warn("Differential code bias not implemented for signal C2C", UserWarning) + return 0 + if signal == 'C5C': + warnings.warn("Differential code bias not implemented for signal C5C", UserWarning) + return 0 + if signal == 'C6C': + warnings.warn("Differential code bias not implemented for signal C6C", UserWarning) + return 0 + if signal == 'C7C': + warnings.warn("Differential code bias not implemented for signal C7C", UserWarning) + return 0 + if signal == 'C8C': + warnings.warn("Differential code bias not implemented for signal C8C", UserWarning) + return 0 + + +def parse_dcbs(file_name, SUPPORTED_CONSTELLATIONS): + with open(file_name, 'r+') as DCB_file: + contents = DCB_file.readlines() + data_started = False + dcbs_dict = {} + for line in contents: + if not data_started: + if line[1:4] == 'DSB': + data_started = True + else: + continue + line_components = line.split() + if len(line_components[2]) < 3: + break + prn = line_components[2] + if get_constellation(prn) not in SUPPORTED_CONSTELLATIONS: + continue + dcb_type = line_components[3] + '_' + line_components[4] + epoch = GPSTime.from_datetime(datetime.strptime(line_components[5], '%Y:%j:%f')) + 12*SECS_IN_HR + if prn not in dcbs_dict: + dcbs_dict[prn] = {} + dcbs_dict[prn][dcb_type] = float(line_components[8]) + dcbs_dict[prn]['epoch'] = epoch + + dcbs = [] + for prn in dcbs_dict: + dcbs.append(DCB(prn, dcbs_dict[prn])) + return dcbs diff --git a/laika/dgps.py b/laika/dgps.py new file mode 100644 index 000000000..54c13d8cf --- /dev/null +++ b/laika/dgps.py @@ -0,0 +1,160 @@ +import os +import numpy as np +from datetime import datetime + +from .gps_time import GPSTime +from .constants import SECS_IN_YEAR +from . import raw_gnss as raw +from .rinex_file import RINEXFile +from .downloader import download_cors_coords +from .helpers import get_constellation + + +def mean_filter(delay): + d2 = delay.copy() + max_step = 10 + for i in range(max_step, len(delay) - max_step): + finite_idxs = np.where(np.isfinite(delay[i - max_step:i + max_step])) + if max_step in finite_idxs[0]: + step = min([max_step, finite_idxs[0][-1] - max_step, max_step - finite_idxs[0][0]]) + d2[i] = np.nanmean(delay[i - step:i + step + 1]) + return d2 + + +def download_and_parse_station_postions(cors_station_positions_path, cache_dir): + if not os.path.isfile(cors_station_positions_path): + cors_stations = {} + coord_file_paths = download_cors_coords(cache_dir=cache_dir) + for coord_file_path in coord_file_paths: + try: + station_id = coord_file_path.split('/')[-1][:4] + with open(coord_file_path, 'r+') as coord_file: + contents = coord_file.readlines() + phase_center = False + for line_number in range(len(contents)): + if 'L1 Phase Center' in contents[line_number]: + phase_center = True + if not phase_center and 'ITRF2014 POSITION' in contents[line_number]: + velocity = [float(contents[line_number+8].split()[3]), + float(contents[line_number+9].split()[3]), + float(contents[line_number+10].split()[3])] + if phase_center and 'ITRF2014 POSITION' in contents[line_number]: + epoch = GPSTime.from_datetime(datetime(2005,1,1)) + position = [float(contents[line_number+2].split()[3]), + float(contents[line_number+3].split()[3]), + float(contents[line_number+4].split()[3])] + cors_stations[station_id] = [epoch, position, velocity] + break + except ValueError: + pass + cors_station_positions_file = open(cors_station_positions_path, 'wb') + np.save(cors_station_positions_file, cors_stations) + cors_station_positions_file.close() + + +def get_closest_station_names(pos, k=5, max_distance=100000, cache_dir='/tmp/gnss/'): + from scipy.spatial import cKDTree + + cors_station_positions_dict = load_cors_station_positions(cache_dir) + station_ids = list(cors_station_positions_dict.keys()) + station_positions = [] + for station_id in station_ids: + station_positions.append(cors_station_positions_dict[station_id][1]) + tree = cKDTree(station_positions) + distances, idxs = tree.query(pos, k=k, distance_upper_bound=max_distance) + return np.array(station_ids)[idxs] + + +def load_cors_station_positions(cache_dir): + cors_station_positions_path = cache_dir + 'cors_coord/cors_station_positions' + download_and_parse_station_postions(cors_station_positions_path, cache_dir) + with open(cors_station_positions_path, 'rb') as f: + return np.load(f, allow_pickle=True).item() # pylint: disable=unexpected-keyword-arg + + +def get_station_position(station_id, cache_dir='/tmp/gnss/', time=GPSTime.from_datetime(datetime.utcnow())): + cors_station_positions_dict = load_cors_station_positions(cache_dir) + epoch, pos, vel = cors_station_positions_dict[station_id] + return ((time - epoch)/SECS_IN_YEAR)*np.array(vel) + np.array(pos) + + +def parse_dgps(station_id, station_obs_file_path, dog, max_distance=100000, required_constellations=['GPS']): + station_pos = get_station_position(station_id, cache_dir=dog.cache_dir) + obsdata = RINEXFile(station_obs_file_path) + measurements = raw.read_rinex_obs(obsdata) + + # if not all constellations in first 100 epochs bail + detected_constellations = set() + for m in sum(measurements[:100],[]): + detected_constellations.add(get_constellation(m.prn)) + for constellation in required_constellations: + if constellation not in detected_constellations: + return None + + proc_measurements = [] + for measurement in measurements: + proc_measurements.append(raw.process_measurements(measurement, dog=dog)) + # sample at 30s + if len(proc_measurements) > 2880: + proc_measurements = proc_measurements[::int(len(proc_measurements)/2880)] + if len(proc_measurements) != 2880: + return None + + station_delays = {} + n = len(proc_measurements) + for signal in ['C1C', 'C2P']: + times = [] + station_delays[signal] = {} + for i, proc_measurement in enumerate(proc_measurements): + times.append(proc_measurement[0].recv_time) + Fx_pos = raw.pr_residual(proc_measurement, signal=signal) + residual = -np.array(Fx_pos(list(station_pos) + [0, 0])) + for j, m in enumerate(proc_measurement): + prn = m.prn + if prn not in station_delays[signal]: + station_delays[signal][prn] = np.nan*np.ones(n) + station_delays[signal][prn][i] = residual[j] + assert len(times) == n + + # TODO crude way to get dgps station's clock errors, + # could this be biased? Only use GPS for convenience. + model_delays = {} + for prn in station_delays['C1C']: + if get_constellation(prn) == 'GPS': + model_delays[prn] = np.nan*np.zeros(n) + for i in range(n): + model_delays[prn][i] = dog.get_delay(prn, times[i], station_pos, no_dgps=True) + station_clock_errs = np.zeros(n) + for i in range(n): + station_clock_errs[i] = np.nanmean([(station_delays['C1C'][prn][i] - model_delays[prn][i]) for prn in model_delays]) + + # remove clock errors and smooth out signal + for prn in station_delays['C1C']: + station_delays['C1C'][prn] = mean_filter(station_delays['C1C'][prn] - station_clock_errs) + for prn in station_delays['C2P']: + station_delays['C2P'][prn] = station_delays['C2P'][prn] - station_clock_errs + + return DGPSDelay(station_id, station_pos, station_delays, + times, max_distance) + + +class DGPSDelay: + def __init__(self, station_id, station_pos, + station_delays, station_delays_t, max_distance): + self.id = station_id + self.pos = station_pos + self.delays = station_delays + self.delays_t = station_delays_t + self.max_distance = max_distance + + def get_delay(self, prn, time, signal='C1C'): + time_index = int((time - self.delays_t[0])/30) + assert abs(self.delays_t[time_index] - time) < 30 + if prn in self.delays[signal] and np.isfinite(self.delays[signal][prn][time_index]): + return self.delays[signal][prn][time_index] + return None + + def valid(self, time, recv_pos): + return (np.linalg.norm(recv_pos - self.pos) <= self.max_distance and + time - self.delays_t[0] > -30 and + self.delays_t[-1] - time > -30) diff --git a/laika/downloader.py b/laika/downloader.py new file mode 100644 index 000000000..eac84d33d --- /dev/null +++ b/laika/downloader.py @@ -0,0 +1,454 @@ +import certifi +import ftplib +import hatanaka +import os +import urllib.request +import pycurl +import re +import time +import tempfile +import socket + +from datetime import datetime, timedelta +from urllib.parse import urlparse +from io import BytesIO + +from atomicwrites import atomic_write + +from laika.ephemeris import EphemerisType +from .constants import SECS_IN_HR, SECS_IN_DAY, SECS_IN_WEEK +from .gps_time import GPSTime +from .helpers import ConstellationId + +dir_path = os.path.dirname(os.path.realpath(__file__)) + + +def retryable(f): + """ + Decorator to allow us to pass multiple URLs from which to download. + Automatically retry the request with the next URL on failure + """ + def wrapped(url_bases, *args, **kwargs): + if isinstance(url_bases, str): + # only one url passed, don't do the retry thing + return f(url_bases, *args, **kwargs) + + # not a string, must be a list of url_bases + for url_base in url_bases: + try: + return f(url_base, *args, **kwargs) + except IOError as e: + print(e) + # none of them succeeded + raise IOError("Multiple URL failures attempting to pull file(s)") + return wrapped + + +def ftp_connect(url): + parsed = urlparse(url) + assert parsed.scheme == 'ftp' + try: + domain = parsed.netloc + ftp = ftplib.FTP(domain, timeout=10) + ftp.login() + except (OSError, ftplib.error_perm): + raise IOError("Could not connect/auth to: " + domain) + try: + ftp.cwd(parsed.path) + except ftplib.error_perm: + raise IOError("Permission failure with folder: " + url) + return ftp + + +@retryable +def list_dir(url): + parsed = urlparse(url) + if parsed.scheme == 'ftp': + try: + ftp = ftp_connect(url) + return ftp.nlst() + except ftplib.error_perm: + raise IOError("Permission failure listing folder: " + url) + else: + # just connect and do simple url parsing + listing = https_download_file(url) + urls = re.findall(b"", listing) + # decode the urls to normal strings. If they are complicated paths, ignore them + return [name.decode("latin1") for name in urls if name and b"/" not in name[1:]] + + +def ftp_download_files(url_base, folder_path, cacheDir, filenames): + """ + Like download file, but more of them. Keeps a persistent FTP connection open + to be more efficient. + """ + folder_path_abs = os.path.join(cacheDir, folder_path) + + ftp = ftp_connect(url_base + folder_path) + + filepaths = [] + for filename in filenames: + # os.path.join will be dumb if filename has a leading / + # if there is a / in the filename, then it's using a different folder + filename = filename.lstrip("/") + if "/" in filename: + continue + filepath = os.path.join(folder_path_abs, filename) + print("pulling from", url_base, "to", filepath) + + if not os.path.isfile(filepath): + os.makedirs(folder_path_abs, exist_ok=True) + try: + ftp.retrbinary('RETR ' + filename, open(filepath, 'wb').write) + except (ftplib.error_perm): + raise IOError("Could not download file from: " + url_base + folder_path + filename) + except (socket.timeout): + raise IOError("Read timed out from: " + url_base + folder_path + filename) + filepaths.append(filepath) + else: + filepaths.append(filepath) + return filepaths + + +def http_download_files(url_base, folder_path, cacheDir, filenames): + """ + Similar to ftp_download_files, attempt to download multiple files faster than + just downloading them one-by-one. + Returns a list of filepaths instead of the raw data + """ + folder_path_abs = os.path.join(cacheDir, folder_path) + + def write_function(disk_path, handle): + def do_write(data): + open(disk_path, "wb").write(data) + + return do_write + + fetcher = pycurl.CurlMulti() + fetcher.setopt(pycurl.M_PIPELINING, 3) + fetcher.setopt(pycurl.M_MAX_HOST_CONNECTIONS, 64) + fetcher.setopt(pycurl.M_MAX_TOTAL_CONNECTIONS, 64) + filepaths = [] + for filename in filenames: + # os.path.join will be dumb if filename has a leading / + # if there is a / in the filename, then it's using a different folder + filename = filename.lstrip("/") + if "/" in filename: + continue + filepath = os.path.join(folder_path_abs, filename) + if not os.path.isfile(filepath): + print("pulling from", url_base, "to", filepath) + os.makedirs(folder_path_abs, exist_ok=True) + url_path = url_base + folder_path + filename + handle = pycurl.Curl() + handle.setopt(pycurl.URL, url_path) + handle.setopt(pycurl.CONNECTTIMEOUT, 10) + handle.setopt(pycurl.WRITEFUNCTION, write_function(filepath, handle)) + fetcher.add_handle(handle) + filepaths.append(filepath) + + requests_processing = len(filepaths) + timeout = 10.0 # after 10 seconds of nothing happening, restart + deadline = time.time() + timeout + while requests_processing and time.time() < deadline: + while True: + ret, cur_requests_processing = fetcher.perform() + if ret != pycurl.E_CALL_MULTI_PERFORM: + _, success, failed = fetcher.info_read() + break + if requests_processing > cur_requests_processing: + deadline = time.time() + timeout + requests_processing = cur_requests_processing + + if fetcher.select(1) < 0: + continue + + # if there are downloads left to be done, repeat, and don't overwrite + _, requests_processing = fetcher.perform() + if requests_processing > 0: + print("some requests stalled, retrying them") + return http_download_files(url_base, folder_path, cacheDir, filenames) + + return filepaths + + +def https_download_file(url): + if 'nasa.gov/' not in url: + netrc_path = None + f = None + elif os.path.isfile(dir_path + '/.netrc'): + netrc_path = dir_path + '/.netrc' + f = None + else: + try: + username = os.environ['NASA_USERNAME'] + password = os.environ['NASA_PASSWORD'] + f = tempfile.NamedTemporaryFile() + netrc = f"machine urs.earthdata.nasa.gov login {username} password {password}" + f.write(netrc.encode()) + f.flush() + netrc_path = f.name + except KeyError: + raise IOError('Could not find .netrc file and no NASA_USERNAME and NASA_PASSWORD in enviroment for urs.earthdata.nasa.gov authentication') + + crl = pycurl.Curl() + crl.setopt(crl.CAINFO, certifi.where()) + crl.setopt(crl.URL, url) + crl.setopt(crl.FOLLOWLOCATION, True) + crl.setopt(crl.SSL_CIPHER_LIST, 'DEFAULT@SECLEVEL=1') + crl.setopt(crl.COOKIEJAR, '/tmp/cddis_cookies') + crl.setopt(pycurl.CONNECTTIMEOUT, 10) + if netrc_path is not None: + crl.setopt(crl.NETRC_FILE, netrc_path) + crl.setopt(crl.NETRC, 2) + + buf = BytesIO() + crl.setopt(crl.WRITEDATA, buf) + crl.perform() + response = crl.getinfo(pycurl.RESPONSE_CODE) + crl.close() + if f is not None: + f.close() + + if response != 200: + raise IOError('HTTPS error ' + str(response)) + return buf.getvalue() + + +def ftp_download_file(url): + urlf = urllib.request.urlopen(url, timeout=10) + data_zipped = urlf.read() + urlf.close() + return data_zipped + + +@retryable +def download_files(url_base, folder_path, cacheDir, filenames): + parsed = urlparse(url_base) + if parsed.scheme == 'ftp': + return ftp_download_files(url_base, folder_path, cacheDir, filenames) + else: + return http_download_files(url_base, folder_path, cacheDir, filenames) + + +@retryable +def download_file(url_base, folder_path, filename_zipped): + url = url_base + folder_path + filename_zipped + print('Downloading ' + url) + if url.startswith('https'): + return https_download_file(url) + if url.startswith('ftp'): + return ftp_download_file(url) + raise NotImplementedError('Did find ftp or https preamble') + + +def download_and_cache_file_return_first_success(url_bases, folder_and_file_names, cache_dir, compression='', overwrite=False, raise_error=False): + last_error = None + for folder_path, filename in folder_and_file_names: + try: + file = download_and_cache_file(url_bases, folder_path, cache_dir, filename, compression, overwrite) + return file + except IOError as e: + last_error = e + + if last_error and raise_error: + raise last_error + + +def download_and_cache_file(url_base, folder_path: str, cache_dir: str, filename: str, compression='', overwrite=False): + filename_zipped = filename + compression + folder_path_abs = os.path.join(cache_dir, folder_path) + filepath = str(hatanaka.get_decompressed_path(os.path.join(folder_path_abs, filename))) + + filepath_attempt = filepath + '.attempt_time' + + if os.path.exists(filepath_attempt): + with open(filepath_attempt, 'r') as rf: + last_attempt_time = float(rf.read()) + if time.time() - last_attempt_time < SECS_IN_HR: + raise IOError(f"Too soon to try downloading {folder_path + filename_zipped} from {url_base} again since last attempt") + if not os.path.isfile(filepath) or overwrite: + try: + data_zipped = download_file(url_base, folder_path, filename_zipped) + except (IOError, pycurl.error, socket.timeout): + unix_time = time.time() + with atomic_write(filepath_attempt, mode='w') as wf: + wf.write(str(unix_time)) + raise IOError(f"Could not download {folder_path + filename_zipped} from {url_base} ") + + os.makedirs(folder_path_abs, exist_ok=True) + ephem_bytes = hatanaka.decompress(data_zipped) + try: + with atomic_write(filepath, mode='wb', overwrite=overwrite) as f: + f.write(ephem_bytes) + except FileExistsError: + # Only happens when same file is downloaded in parallel by another process. + pass + return filepath + + +# Currently, only GPS and Glonass are supported for daily and hourly data. +CONSTELLATION_NASA_CHAR = {ConstellationId.GPS: 'n', ConstellationId.GLONASS: 'g'} + + +def download_nav(time: GPSTime, cache_dir, constellation: ConstellationId): + t = time.as_datetime() + try: + if constellation not in CONSTELLATION_NASA_CHAR: + return None + c = CONSTELLATION_NASA_CHAR[constellation] + if GPSTime.from_datetime(datetime.utcnow()) - time > SECS_IN_DAY: + url_bases = ( + 'https://github.com/commaai/gnss-data/raw/master/gnss/data/daily/', + 'https://cddis.nasa.gov/archive/gnss/data/daily/', + ) + filename = t.strftime(f"brdc%j0.%y{c}") + folder_path = t.strftime(f'%Y/%j/%y{c}/') + compression = '.gz' if folder_path >= '2020/335/' else '.Z' + return download_and_cache_file(url_bases, folder_path, cache_dir+'daily_nav/', filename, compression) + elif constellation == ConstellationId.GPS: + url_base = 'https://cddis.nasa.gov/archive/gnss/data/hourly/' + filename = t.strftime(f"hour%j0.%y{c}") + folder_path = t.strftime('%Y/%j/') + compression = '.gz' if folder_path >= '2020/336/' else '.Z' + return download_and_cache_file(url_base, folder_path, cache_dir+'hourly_nav/', filename, compression) + except IOError: + pass + + +def download_orbits_gps(time, cache_dir, ephem_types): + url_bases = ( + 'https://github.com/commaai/gnss-data/raw/master/gnss/products/', + 'https://cddis.nasa.gov/archive/gnss/products/', + 'ftp://igs.ign.fr/pub/igs/products/', + ) + folder_path = "%i/" % time.week + filenames = [] + time_str = "%i%i" % (time.week, time.day) + # Download filenames in order of quality. Final -> Rapid -> Ultra-Rapid(newest first) + if EphemerisType.FINAL_ORBIT in ephem_types and GPSTime.from_datetime(datetime.utcnow()) - time > 3 * SECS_IN_WEEK: + filenames.append(f"igs{time_str}.sp3") + if EphemerisType.RAPID_ORBIT in ephem_types: + filenames.append(f"igr{time_str}.sp3") + if EphemerisType.ULTRA_RAPID_ORBIT in ephem_types: + filenames.extend([f"igu{time_str}_18.sp3", + f"igu{time_str}_12.sp3", + f"igu{time_str}_06.sp3", + f"igu{time_str}_00.sp3"]) + folder_file_names = [(folder_path, filename) for filename in filenames] + return download_and_cache_file_return_first_success(url_bases, folder_file_names, cache_dir+'cddis_products/', compression='.Z') + + +def download_prediction_orbits_russia_src(gps_time, cache_dir): + # Download single file that contains Ultra_Rapid predictions for GPS, GLONASS and other constellations + t = gps_time.as_datetime() + # Files exist starting at 29-01-2022 + if t < datetime(2022, 1, 29): + return None + url_bases = 'https://github.com/commaai/gnss-data-alt/raw/master/MCC/PRODUCTS/' + folder_path = t.strftime('%y%j/ultra/') + file_prefix = "Stark_1D_" + t.strftime('%y%m%d') + + # Predictions are 24H so previous day can also be used. + prev_day = (t - timedelta(days=1)) + file_prefix_prev = "Stark_1D_" + prev_day.strftime('%y%m%d') + folder_path_prev = prev_day.strftime('%y%j/ultra/') + + current_day = GPSTime.from_datetime(datetime(t.year, t.month, t.day)) + # Ultra-Orbit is published in gnss-data-alt every 10th minute past the 5,11,17,23 hour. + # Predictions published are delayed by around 10 hours. + # Download latest file that includes gps_time with 20 minutes margin.: + if gps_time > current_day + 23.5 * SECS_IN_HR: + prev_day, current_day = [], [6, 12] + elif gps_time > current_day + 17.5 * SECS_IN_HR: + prev_day, current_day = [], [0, 6] + elif gps_time > current_day + 11.5 * SECS_IN_HR: + prev_day, current_day = [18], [0] + elif gps_time > current_day + 5.5 * SECS_IN_HR: + prev_day, current_day = [12, 18], [] + else: + prev_day, current_day = [6, 12], [] + # Example: Stark_1D_22060100.sp3 + folder_and_file_names = [(folder_path, file_prefix + f"{h:02}.sp3") for h in reversed(current_day)] + \ + [(folder_path_prev, file_prefix_prev + f"{h:02}.sp3") for h in reversed(prev_day)] + return download_and_cache_file_return_first_success(url_bases, folder_and_file_names, cache_dir+'russian_products/', raise_error=True) + + +def download_orbits_russia_src(time, cache_dir, ephem_types): + # Orbits from russian source. Contains GPS, GLONASS, GALILEO, BEIDOU + url_bases = ( + 'https://github.com/commaai/gnss-data-alt/raw/master/MCC/PRODUCTS/', + 'ftp://ftp.glonass-iac.ru/MCC/PRODUCTS/', + ) + t = time.as_datetime() + folder_paths = [] + current_gps_time = GPSTime.from_datetime(datetime.utcnow()) + filename = "Sta%i%i.sp3" % (time.week, time.day) + if EphemerisType.FINAL_ORBIT in ephem_types and current_gps_time - time > 2 * SECS_IN_WEEK: + folder_paths.append(t.strftime('%y%j/final/')) + if EphemerisType.RAPID_ORBIT in ephem_types: + folder_paths.append(t.strftime('%y%j/rapid/')) + if EphemerisType.ULTRA_RAPID_ORBIT in ephem_types: + folder_paths.append(t.strftime('%y%j/ultra/')) + folder_file_names = [(folder_path, filename) for folder_path in folder_paths] + return download_and_cache_file_return_first_success(url_bases, folder_file_names, cache_dir+'russian_products/') + + +def download_ionex(time, cache_dir): + t = time.as_datetime() + url_bases = ( + 'https://github.com/commaai/gnss-data/raw/master/gnss/products/ionex/', + 'https://cddis.nasa.gov/archive/gnss/products/ionex/', + 'ftp://igs.ensg.ign.fr/pub/igs/products/ionosphere/', + 'ftp://gssc.esa.int/gnss/products/ionex/', + ) + folder_path = t.strftime('%Y/%j/') + filenames = [t.strftime("codg%j0.%yi"), t.strftime("c1pg%j0.%yi"), t.strftime("c2pg%j0.%yi")] + + folder_file_names = [(folder_path, f) for f in filenames] + return download_and_cache_file_return_first_success(url_bases, folder_file_names, cache_dir+'ionex/', compression='.Z', raise_error=True) + + +def download_dcb(time, cache_dir): + filenames = [] + folder_paths = [] + url_bases = ( + 'https://github.com/commaai/gnss-data/raw/master/gnss/products/bias/', + 'https://cddis.nasa.gov/archive/gnss/products/bias/', + 'ftp://igs.ign.fr/pub/igs/products/mgex/dcb/', + ) + # seem to be a lot of data missing, so try many days + for time_step in [time - i * SECS_IN_DAY for i in range(14)]: + t = time_step.as_datetime() + folder_paths.append(t.strftime('%Y/')) + filenames.append(t.strftime("CAS0MGXRAP_%Y%j0000_01D_01D_DCB.BSX")) + + return download_and_cache_file_return_first_success(url_bases, list(zip(folder_paths, filenames)), cache_dir+'dcb/', compression='.gz', raise_error=True) + + +def download_cors_coords(cache_dir): + cache_subdir = cache_dir + 'cors_coord/' + url_bases = ( + 'https://geodesy.noaa.gov/corsdata/coord/coord_14/', + 'https://alt.ngs.noaa.gov/corsdata/coord/coord_14/', + ) + file_names = list_dir(url_bases) + file_names = [file_name for file_name in file_names if file_name.endswith('coord.txt')] + filepaths = download_files(url_bases, '', cache_subdir, file_names) + return filepaths + + +def download_cors_station(time, station_name, cache_dir): + t = time.as_datetime() + folder_path = t.strftime('%Y/%j/') + station_name + '/' + filename = station_name + t.strftime("%j0.%yd") + url_bases = ( + 'https://geodesy.noaa.gov/corsdata/rinex/', + 'https://alt.ngs.noaa.gov/corsdata/rinex/', + ) + try: + filepath = download_and_cache_file(url_bases, folder_path, cache_dir+'cors_obs/', filename, compression='.gz') + return filepath + except IOError: + print("File not downloaded, check availability on server.") + return None diff --git a/laika/ephemeris.py b/laika/ephemeris.py new file mode 100644 index 000000000..91caef2a4 --- /dev/null +++ b/laika/ephemeris.py @@ -0,0 +1,557 @@ +import json +import warnings +from abc import ABC, abstractmethod +from collections import defaultdict +from enum import IntEnum +from typing import Dict, List, Optional + +import numpy as np +import numpy.polynomial.polynomial as poly +from datetime import datetime +from math import sin, cos, sqrt, fabs, atan2 + +from .gps_time import GPSTime, utc_to_gpst +from .constants import SPEED_OF_LIGHT, SECS_IN_MIN, SECS_IN_HR, SECS_IN_DAY, EARTH_ROTATION_RATE, EARTH_GM +from .helpers import get_constellation + + +def read4(f, rinex_ver): + line = f.readline()[:-1] + if rinex_ver == 2: + line = ' ' + line # Shift 1 char to the right + line = line.replace('D', 'E') # Handle bizarro float format + return float(line[4:23]), float(line[23:42]), float(line[42:61]), float(line[61:80]) + + +def convert_ublox_ephem(ublox_ephem, current_time: Optional[datetime] = None): + # Week time of ephemeris gps msg has a roll-over period of 10 bits (19.6 years) + # The latest roll-over was on 2019-04-07 + week = ublox_ephem.gpsWeek + if current_time is None: + # Each message is incremented to be greater or equal than week 1877 (2015-12-27). + # To skip this use the current_time argument + week += 1024 + if week < 1877: + week += 1024 + else: + roll_overs = GPSTime.from_datetime(current_time).week // 1024 + week += (roll_overs - (week // 1024)) * 1024 + + ephem = {} + ephem['sv_id'] = ublox_ephem.svId + ephem['toe'] = GPSTime(week, ublox_ephem.toe) + ephem['toc'] = GPSTime(week, ublox_ephem.toc) + ephem['af0'] = ublox_ephem.af0 + ephem['af1'] = ublox_ephem.af1 + ephem['af2'] = ublox_ephem.af2 + ephem['tgd'] = ublox_ephem.tgd + + ephem['sqrta'] = np.sqrt(ublox_ephem.a) + ephem['dn'] = ublox_ephem.deltaN + ephem['m0'] = ublox_ephem.m0 + + ephem['ecc'] = ublox_ephem.ecc + ephem['w'] = ublox_ephem.omega + ephem['cus'] = ublox_ephem.cus + ephem['cuc'] = ublox_ephem.cuc + ephem['crc'] = ublox_ephem.crc + ephem['crs'] = ublox_ephem.crs + ephem['cic'] = ublox_ephem.cic + ephem['cis'] = ublox_ephem.cis + + ephem['inc'] = ublox_ephem.i0 + ephem['inc_dot'] = ublox_ephem.iDot + ephem['omegadot'] = ublox_ephem.omegaDot + ephem['omega0'] = ublox_ephem.omega0 + + epoch = ephem['toe'] + return GPSEphemeris(ephem, epoch) + + +class EphemerisType(IntEnum): + # Matches the enum in log.capnp + NAV = 0 + FINAL_ORBIT = 1 + RAPID_ORBIT = 2 + ULTRA_RAPID_ORBIT = 3 + QCOM_POLY = 4 # Currently not supported + + @staticmethod + def all_orbits(): + return EphemerisType.FINAL_ORBIT, EphemerisType.RAPID_ORBIT, EphemerisType.ULTRA_RAPID_ORBIT + + @classmethod + def from_file_name(cls, file_name: str): + if "/final" in file_name or "/igs" in file_name: + return EphemerisType.FINAL_ORBIT + if "/rapid" in file_name or "/igr" in file_name: + return EphemerisType.RAPID_ORBIT + if "/ultra" in file_name or "/igu" in file_name: + return EphemerisType.ULTRA_RAPID_ORBIT + raise RuntimeError(f"Ephemeris type not found in filename: {file_name}") + + +class Ephemeris(ABC): + + def __init__(self, prn: str, data, epoch: GPSTime, eph_type: EphemerisType, healthy: bool, max_time_diff: float, + file_epoch: Optional[GPSTime] = None, file_name=None): + self.prn = prn + self.data = data + self.epoch = epoch + self.eph_type = eph_type + self.healthy = healthy + self.max_time_diff = max_time_diff + self.file_epoch = file_epoch + self.file_source = '' if file_name is None else file_name.split('/')[-1][:3] # File source for the ephemeris (e.g. igu, igr, Sta) + self.__json = None + + def valid(self, time): + return abs(time - self.epoch) <= self.max_time_diff + + def __repr__(self): + time = self.epoch.as_datetime().strftime('%Y-%m-%dT%H:%M:%S.%f') + return f"<{self.__class__.__name__} from {self.prn} at {time}>" + + def get_sat_info(self, time: GPSTime): + """ + Returns: (pos, vel, clock_err, clock_rate_err, ephemeris) + """ + if not self.healthy: + return None + return list(self._get_sat_info(time)) + [self] + + @abstractmethod + def _get_sat_info(self, time): + pass + + def to_json(self): + if self.__json is None: + dict = self.__dict__ + dict['ephemeris_class'] = self.__class__.__name__ + self.__json = {'ephemeris': json.dumps(dict, cls=EphemerisSerializer)} + return self.__json + + @classmethod + def from_json(cls, json_dct): + dct = json.loads(json_dct['ephemeris'], object_hook=ephemeris_deserialize_hook) + obj = cls.__new__(globals()[dct['ephemeris_class']]) + obj.__dict__.update(dct) + obj.__json = json_dct + return obj + + +def ephemeris_deserialize_hook(dct): + if 'week' in dct: + return GPSTime(dct['week'], dct['tow']) + return dct + + +class EphemerisSerializer(json.JSONEncoder): + + def default(self, o): + if isinstance(o, GPSTime): + return o.__dict__ + if isinstance(o, np.ndarray): + return o.tolist() + return json.JSONEncoder.default(self, o) + + +class GLONASSEphemeris(Ephemeris): + def __init__(self, data, epoch, healthy=True, file_name=None): + super().__init__(data['prn'], data, epoch, EphemerisType.NAV, healthy, max_time_diff=25*SECS_IN_MIN, file_name=file_name) + self.channel = data['freq_num'] + self.to_json() + + def _get_sat_info(self, time: GPSTime): + # see the russian doc for this: + # http://gauss.gge.unb.ca/GLONASS.ICD.pdf + + eph = self.data + # TODO should handle leap seconds better + toc_gps_time = utc_to_gpst(eph['toc']) + tdiff = time - toc_gps_time + + # Clock correction (except for general relativity which is applied later) + clock_err = eph['min_tauN'] + tdiff * (eph['GammaN']) + clock_rate_err = eph['GammaN'] + + def glonass_diff_eq(state, acc): + J2 = 1.0826257e-3 + mu = 3.9860044e14 + omega = 7.292115e-5 + ae = 6378136.0 + r = np.sqrt(state[0]**2 + state[1]**2 + state[2]**2) + ders = np.zeros(6) + if r**2 < 0: + return ders + a = 1.5 * J2 * mu * (ae**2)/ (r**5) + b = 5 * (state[2]**2) / (r**2) + c = -mu/(r**3) - a*(1-b) + + ders[0:3] = state[3:6] + ders[3] = (c + omega**2)*state[0] + 2*omega*state[4] + acc[0] + ders[4] = (c + omega**2)*state[1] - 2*omega*state[3] + acc[1] + ders[5] = (c - 2*a)*state[2] + acc[2] + return ders + + init_state = np.empty(6) + init_state[0] = eph['x'] + init_state[1] = eph['y'] + init_state[2] = eph['z'] + init_state[3] = eph['x_vel'] + init_state[4] = eph['y_vel'] + init_state[5] = eph['z_vel'] + init_state = 1000*init_state + acc = 1000*np.array([eph['x_acc'], eph['y_acc'], eph['z_acc']]) + state = init_state + tstep = 90 + if tdiff < 0: + tt = -tstep + elif tdiff > 0: + tt = tstep + while abs(tdiff) > 1e-9: + if abs(tdiff) < tstep: + tt = tdiff + k1 = glonass_diff_eq(state, acc) + k2 = glonass_diff_eq(state + k1*tt/2, -acc) + k3 = glonass_diff_eq(state + k2*tt/2, -acc) + k4 = glonass_diff_eq(state + k3*tt, -acc) + state += (k1 + 2*k2 + 2*k3 + k4)*tt/6.0 + tdiff -= tt + + pos = state[0:3] + vel = state[3:6] + return pos, vel, clock_err, clock_rate_err + + +class PolyEphemeris(Ephemeris): + def __init__(self, prn: str, data, epoch: GPSTime, ephem_type: EphemerisType, file_epoch: GPSTime, file_name: str, healthy=True, tgd=0): + super().__init__(prn, data, epoch, ephem_type, healthy, max_time_diff=SECS_IN_HR, file_epoch=file_epoch, file_name=file_name) + self.tgd = tgd + self.to_json() + + def _get_sat_info(self, time: GPSTime): + dt = time - self.data['t0'] + deg = self.data['deg'] + deg_t = self.data['deg_t'] + indices = np.arange(deg+1)[:,np.newaxis] + sat_pos = np.sum((dt**indices)*self.data['xyz'], axis=0) + indices = indices[1:] + sat_vel = np.sum(indices*(dt**(indices-1)*self.data['xyz'][1:]), axis=0) + time_err = sum((dt**p)*self.data['clock'][deg_t-p] for p in range(deg_t+1)) + time_err_rate = sum(p*(dt**(p-1))*self.data['clock'][deg_t-p] for p in range(1,deg_t+1)) + time_err_with_rel = time_err - 2*np.inner(sat_pos, sat_vel)/SPEED_OF_LIGHT**2 + return sat_pos, sat_vel, time_err_with_rel, time_err_rate + + +class GPSEphemeris(Ephemeris): + def __init__(self, data, epoch, healthy=True, file_name=None): + super().__init__('G%02i' % data['sv_id'], data, epoch, EphemerisType.NAV, healthy, max_time_diff=2*SECS_IN_HR, file_name=file_name) + self.max_time_diff_tgd = SECS_IN_DAY + self.to_json() + + def get_tgd(self): + return self.data['tgd'] + + def _get_sat_info(self, time: GPSTime): + eph = self.data + tdiff = time - eph['toc'] # Time of clock + clock_err = eph['af0'] + tdiff * (eph['af1'] + tdiff * eph['af2']) + clock_rate_err = eph['af1'] + 2 * tdiff * eph['af2'] + + # Orbit propagation + tdiff = time - eph['toe'] # Time of ephemeris (might be different from time of clock) + + # Calculate position per IS-GPS-200D p 97 Table 20-IV + a = eph['sqrta'] * eph['sqrta'] # [m] Semi-major axis + ma_dot = sqrt(EARTH_GM / (a * a * a)) + eph['dn'] # [rad/sec] Corrected mean motion + ma = eph['m0'] + ma_dot * tdiff # [rad] Corrected mean anomaly + + # Iteratively solve for the Eccentric Anomaly (from Keith Alter and David Johnston) + ea = ma # Starting value for E + + ea_old = 2222 + while fabs(ea - ea_old) > 1.0E-14: + ea_old = ea + tempd1 = 1.0 - eph['ecc'] * cos(ea_old) + ea = ea + (ma - ea_old + eph['ecc'] * sin(ea_old)) / tempd1 + ea_dot = ma_dot / tempd1 + + # Relativistic correction term + einstein = -4.442807633E-10 * eph['ecc'] * eph['sqrta'] * sin(ea) + + # Begin calc for True Anomaly and Argument of Latitude + tempd2 = sqrt(1.0 - eph['ecc'] * eph['ecc']) + # [rad] Argument of Latitude = True Anomaly + Argument of Perigee + al = atan2(tempd2 * sin(ea), cos(ea) - eph['ecc']) + eph['w'] + al_dot = tempd2 * ea_dot / tempd1 + + # Calculate corrected argument of latitude based on position + cal = al + eph['cus'] * sin(2.0 * al) + eph['cuc'] * cos(2.0 * al) + cal_dot = al_dot * (1.0 + 2.0 * (eph['cus'] * cos(2.0 * al) - + eph['cuc'] * sin(2.0 * al))) + + # Calculate corrected radius based on argument of latitude + r = a * tempd1 + eph['crc'] * cos(2.0 * al) + eph['crs'] * sin(2.0 * al) + r_dot = (a * eph['ecc'] * sin(ea) * ea_dot + + 2.0 * al_dot * (eph['crs'] * cos(2.0 * al) - + eph['crc'] * sin(2.0 * al))) + + # Calculate inclination based on argument of latitude + inc = (eph['inc'] + eph['inc_dot'] * tdiff + + eph['cic'] * cos(2.0 * al) + + eph['cis'] * sin(2.0 * al)) + inc_dot = (eph['inc_dot'] + + 2.0 * al_dot * (eph['cis'] * cos(2.0 * al) - + eph['cic'] * sin(2.0 * al))) + + # Calculate position and velocity in orbital plane + x = r * cos(cal) + y = r * sin(cal) + x_dot = r_dot * cos(cal) - y * cal_dot + y_dot = r_dot * sin(cal) + x * cal_dot + + # Corrected longitude of ascending node + om_dot = eph['omegadot'] - EARTH_ROTATION_RATE + om = eph['omega0'] + tdiff * om_dot - EARTH_ROTATION_RATE * eph['toe'].tow + + # Compute the satellite's position in Earth-Centered Earth-Fixed coordinates + pos = np.empty(3) + pos[0] = x * cos(om) - y * cos(inc) * sin(om) + pos[1] = x * sin(om) + y * cos(inc) * cos(om) + pos[2] = y * sin(inc) + + tempd3 = y_dot * cos(inc) - y * sin(inc) * inc_dot + + # Compute the satellite's velocity in Earth-Centered Earth-Fixed coordinates + vel = np.empty(3) + vel[0] = -om_dot * pos[1] + x_dot * cos(om) - tempd3 * sin(om) + vel[1] = om_dot * pos[0] + x_dot * sin(om) + tempd3 * cos(om) + vel[2] = y * cos(inc) * inc_dot + y_dot * sin(inc) + + clock_err += einstein + + return pos, vel, clock_err, clock_rate_err + + +def parse_sp3_orbits(file_names, supported_constellations, skip_until_epoch: Optional[GPSTime] = None) -> Dict[str, List[PolyEphemeris]]: + if skip_until_epoch is None: + skip_until_epoch = GPSTime(0, 0) + data: Dict[str, List] = {} + for file_name in file_names: + if file_name is None: + continue + with open(file_name) as f: + ephem_type = EphemerisType.from_file_name(file_name) + file_epoch = None + while True: + line = f.readline()[:-1] + if not line: + break + # epoch header + if line[0:2] == '* ': + year = int(line[3:7]) + month = int(line[8:10]) + day = int(line[11:13]) + hour = int(line[14:16]) + minute = int(line[17:19]) + second = int(float(line[20:31])) + epoch = GPSTime.from_datetime(datetime(year, month, day, hour, minute, second)) + if file_epoch is None: + file_epoch = epoch + # pos line + elif line[0] == 'P': + # Skipping data can reduce the time significantly when parsing the ephemeris + if epoch < skip_until_epoch: + continue + prn = line[1:4].replace(' ', '0') + # In old SP3 files vehicle ID doesn't contain constellation + # identifier. We assume that constellation is GPS when missing. + if prn[0] == '0': + prn = 'G' + prn[1:] + if get_constellation(prn) not in supported_constellations: + continue + if prn not in data: + data[prn] = [] + #TODO this is a crappy way to deal with overlapping ultra rapid + if len(data[prn]) < 1 or epoch - data[prn][-1][1] > 0: + parsed = [(ephem_type, file_epoch, file_name), + epoch, + 1e3 * float(line[4:18]), + 1e3 * float(line[18:32]), + 1e3 * float(line[32:46]), + 1e-6 * float(line[46:60])] + if (np.array(parsed[2:]) != 0).all(): + data[prn].append(parsed) + ephems = {} + for prn in data: + ephems[prn] = read_prn_data(data, prn) + return ephems + + +def read_prn_data(data, prn, deg=16, deg_t=1): + np_data_prn = np.array(data[prn], dtype=object) + # Currently, don't even bother with satellites that have unhealthy times + if len(np_data_prn) == 0 or (np_data_prn[:, 5] > .99).any(): + return [] + ephems = [] + for i in range(len(np_data_prn) - deg): + epoch_index = i + deg // 2 + epoch = np_data_prn[epoch_index][1] + measurements = np_data_prn[i:i + deg + 1, 1:5] + + times = (measurements[:, 0] - epoch).astype(float) + if (np.diff(times) != 900).any(): + continue + + poly_data = {} + poly_data['t0'] = epoch + with warnings.catch_warnings(): + warnings.simplefilter("ignore") # Ignores: UserWarning: The value of the smallest subnormal for type is zero. + poly_data['xyz'] = poly.polyfit(times, measurements[:, 1:].astype(float), deg) + poly_data['clock'] = [(np_data_prn[epoch_index + 1][5] - np_data_prn[epoch_index - 1][5]) / 1800, np_data_prn[epoch_index][5]] + poly_data['deg'] = deg + poly_data['deg_t'] = deg_t + # It can happen that a mix of orbit ephemeris types are used in the polyfit. + ephem_type, file_epoch, file_name = np_data_prn[epoch_index][0] + ephems.append(PolyEphemeris(prn, poly_data, epoch, ephem_type, file_epoch, file_name, healthy=True)) + return ephems + + +def parse_rinex_nav_msg_gps(file_name): + ephems = defaultdict(list) + got_header = False + rinex_ver = None + #ion_alpha = None + #ion_beta = None + f = open(file_name) + while True: + line = f.readline()[:-1] + if not line: + break + if not got_header: + if rinex_ver is None: + if line[60:80] != "RINEX VERSION / TYPE": + raise RuntimeError("Doesn't appear to be a RINEX file") + rinex_ver = int(float(line[0:9])) + if line[20] != "N": + raise RuntimeError("Doesn't appear to be a Navigation Message file") + #if line[60:69] == "ION ALPHA": + # line = line.replace('D', 'E') # Handle bizarro float format + # ion_alpha= [float(line[3:14]), float(line[15:26]), float(line[27:38]), float(line[39:50])] + #if line[60:68] == "ION BETA": + # line = line.replace('D', 'E') # Handle bizarro float format + # ion_beta= [float(line[3:14]), float(line[15:26]), float(line[27:38]), float(line[39:50])] + if line[60:73] == "END OF HEADER": + #ion = ion_alpha + ion_beta + got_header = True + continue + if rinex_ver == 3: + if line[0] != 'G': + continue + if rinex_ver == 3: + sv_id = int(line[1:3]) + epoch = GPSTime.from_datetime(datetime.strptime(line[4:23], "%y %m %d %H %M %S")) + elif rinex_ver == 2: + sv_id = int(line[0:2]) + # 2000 year is in RINEX file as 0, but Python requires two digit year: 00 + epoch_str = line[3:20] + if epoch_str[0] == ' ': + epoch_str = '0' + epoch_str[1:] + epoch = GPSTime.from_datetime(datetime.strptime(epoch_str, "%y %m %d %H %M %S")) + line = ' ' + line # Shift 1 char to the right + + line = line.replace('D', 'E') # Handle bizarro float format + e = {'epoch': epoch, 'sv_id': sv_id} + e['toc'] = epoch + e['af0'] = float(line[23:42]) + e['af1'] = float(line[42:61]) + e['af2'] = float(line[61:80]) + + e['iode'], e['crs'], e['dn'], e['m0'] = read4(f, rinex_ver) + e['cuc'], e['ecc'], e['cus'], e['sqrta'] = read4(f, rinex_ver) + toe_tow, e['cic'], e['omega0'], e['cis'] = read4(f, rinex_ver) + e['inc'], e['crc'], e['w'], e['omegadot'] = read4(f, rinex_ver) + e['inc_dot'], e['l2_codes'], toe_week, e['l2_pflag'] = read4(f, rinex_ver) + e['sv_accuracy'], e['health'], e['tgd'], e['iodc'] = read4(f, rinex_ver) + f.readline() # Discard last row + + e['toe'] = GPSTime(toe_week, toe_tow) + e['healthy'] = (e['health'] == 0.0) + + ephem = GPSEphemeris(e, epoch, file_name=file_name) + ephems[ephem.prn].append(ephem) + f.close() + return ephems + + +def parse_rinex_nav_msg_glonass(file_name): + ephems = defaultdict(list) + f = open(file_name) + got_header = False + rinex_ver = None + while True: + line = f.readline()[:-1] + if not line: + break + if not got_header: + if rinex_ver is None: + if line[60:80] != "RINEX VERSION / TYPE": + raise RuntimeError("Doesn't appear to be a RINEX file") + rinex_ver = int(float(line[0:9])) + if line[20] != "G": + raise RuntimeError("Doesn't appear to be a Navigation Message file") + if line[60:73] == "END OF HEADER": + got_header = True + continue + if rinex_ver == 3: + prn = line[:3] + epoch = GPSTime.from_datetime(datetime.strptime(line[4:23], "%y %m %d %H %M %S")) + elif rinex_ver == 2: + prn = 'R%02i' % int(line[0:2]) + epoch = GPSTime.from_datetime(datetime.strptime(line[3:20], "%y %m %d %H %M %S")) + line = ' ' + line # Shift 1 char to the right + + line = line.replace('D', 'E') # Handle bizarro float format + e = {'epoch': epoch, 'prn': prn} + e['toc'] = epoch + e['min_tauN'] = float(line[23:42]) + e['GammaN'] = float(line[42:61]) + e['tk'] = float(line[61:80]) + + e['x'], e['x_vel'], e['x_acc'], e['health'] = read4(f, rinex_ver) + e['y'], e['y_vel'], e['y_acc'], e['freq_num'] = read4(f, rinex_ver) + e['z'], e['z_vel'], e['z_acc'], e['age'] = read4(f, rinex_ver) + + e['healthy'] = (e['health'] == 0.0) + + ephems[prn].append(GLONASSEphemeris(e, epoch, file_name=file_name)) + f.close() + return ephems + + +''' + +def parse_qcom_ephems(qcom_polys, current_week): + ephems = [] + for qcom_poly in qcom_polys: + svId = qcom_poly.qcomGnss.drSvPoly.svId + data = qcom_poly.qcomGnss.drSvPoly + t0 = data.t0 + # fix glonass time + if get_constellation(svId) == 'GLONASS': + # TODO should handle leap seconds better + epoch = GPSTime(current_week, (t0 + 3*SECS_IN_WEEK) % (SECS_IN_WEEK) + 18) + else: + epoch = GPSTime(current_week, t0) + poly_data = {} + poly_data['t0'] = epoch + poly_data['x'] = [data.xyzN[2], data.xyzN[1], data.xyzN[0], data.xyz0[0]] + poly_data['y'] = [data.xyzN[5], data.xyzN[4], data.xyzN[3], data.xyz0[1]] + poly_data['z'] = [data.xyzN[8], data.xyzN[7], data.xyzN[6], data.xyz0[2]] + poly_data['clock'] = [1e-3*data.other[3], 1e-3*data.other[2], 1e-3*data.other[1], 1e-3*data.other[0]] + poly_data['deg'] = 3 + poly_data['deg_t'] = 3 + ephems.append(PolyEphemeris(svId, poly_data, epoch, eph_type=EphemerisType.QCOM_POLY)) + return ephems +''' diff --git a/laika/gps_time.py b/laika/gps_time.py new file mode 100644 index 000000000..83d65b748 --- /dev/null +++ b/laika/gps_time.py @@ -0,0 +1,188 @@ +import datetime + + +def datetime_to_tow(t): + """ + Convert a Python datetime object to GPS Week and Time Of Week. + Does *not* convert from UTC to GPST. + Fractional seconds are supported. + + Parameters + ---------- + t : datetime + A time to be converted, on the GPST timescale. + mod1024 : bool, optional + If True (default), the week number will be output in 10-bit form. + + Returns + ------- + week, tow : tuple (int, float) + The GPS week number and time-of-week. + """ + # DateTime to GPS week and TOW + wk_ref = datetime.datetime(2014, 2, 16, 0, 0, 0, 0, None) + refwk = 1780 + wk = (t - wk_ref).days // 7 + refwk + tow = ((t - wk_ref) - datetime.timedelta((wk - refwk) * 7.0)).total_seconds() + return wk, tow + + +def tow_to_datetime(tow, week): + """ + Convert a GPS Week and Time Of Week to Python datetime object. + Does *not* convert from GPST to UTC. + Fractional seconds are supported. + + Parameters + ---------- + tow : time of week in seconds + + weeks : gps week + + + Returns + ------- + t : datetime + Python datetime + """ + # GPS week and TOW to DateTime + t = datetime.datetime(1980, 1, 6, 0, 0, 0, 0, None) + t += datetime.timedelta(seconds=tow) + t += datetime.timedelta(weeks=week) + return t + + +def get_leap_seconds(time): + if time <= GPSTime.from_datetime(datetime.datetime(2006, 1, 1)): + raise ValueError("Don't know how many leap seconds to use before 2006") + elif time <= GPSTime.from_datetime(datetime.datetime(2009, 1, 1)): + return 14 + elif time <= GPSTime.from_datetime(datetime.datetime(2012, 7, 1)): + return 15 + elif time <= GPSTime.from_datetime(datetime.datetime(2015, 7, 1)): + return 16 + elif time <= GPSTime.from_datetime(datetime.datetime(2017, 7, 1)): + return 17 + else: + return 18 + + +def gpst_to_utc(t_gpst): + t_utc = t_gpst - get_leap_seconds(t_gpst) + if utc_to_gpst(t_utc) - t_gpst != 0: + return t_utc + 1 + else: + return t_utc + + +def utc_to_gpst(t_utc): + t_gpst = t_utc + get_leap_seconds(t_utc) + return t_gpst + + +class GPSTime: + """ + GPS time class to add and subtract [week, tow] + """ + def __init__(self, week, tow): + self.week = week + self.tow = tow + self.seconds_in_week = 604800 + + @classmethod + def from_datetime(cls, datetime): + week, tow = datetime_to_tow(datetime) + return cls(week, tow) + + @classmethod + def from_glonass(cls, cycle, days, tow): + # https://en.wikipedia.org/wiki/GLONASS + # Day number (1 to 1461) within a four-year interval + # starting on 1 January of the last leap year + t = datetime.datetime(1992, 1, 1, 0, 0, 0, 0, None) + t += datetime.timedelta(days=cycle*(365*4+1)+(days-1)) + # according to Moscow decree time. + t -= datetime.timedelta(hours=3) + t += datetime.timedelta(seconds=tow) + ret = cls.from_datetime(t) + return utc_to_gpst(ret) + + @classmethod + def from_meas(cls, meas): + return cls(meas[1], meas[2]) + + def __sub__(self, other): + if isinstance(other, type(self)): + return (self.week - other.week)*self.seconds_in_week + self.tow - other.tow + elif isinstance(other, float) or isinstance(other, int): + new_week = self.week + new_tow = self.tow - other + while new_tow < 0: + new_tow += self.seconds_in_week + new_week -= 1 + return GPSTime(new_week, new_tow) + raise NotImplementedError(f"subtracting {other} from {self}") + + def __add__(self, other): + if isinstance(other, float) or isinstance(other, int): + new_week = self.week + new_tow = self.tow + other + while new_tow >= self.seconds_in_week: + new_tow -= self.seconds_in_week + new_week += 1 + return GPSTime(new_week, new_tow) + raise NotImplementedError(f"adding {other} from {self}") + + def __lt__(self, other): + return self - other < 0 + + def __gt__(self, other): + return self - other > 0 + + def __le__(self, other): + return self - other <= 0 + + def __ge__(self, other): + return self - other >= 0 + + def __eq__(self, other): + return self - other == 0 + + def as_datetime(self): + return tow_to_datetime(self.tow, self.week) + + @property + def day(self): + return int(self.tow/(24*3600)) + + def __repr__(self): + return f"GPSTime(week={self.week}, tow={self.tow})" + + +class TimeSyncer: + """ + Converts logmonotime to gps_time and vice versa + """ + def __init__(self, mono_time, gps_time): + self.ref_mono_time = mono_time + self.ref_gps_time = gps_time + + @classmethod + def from_datetime(cls, datetime): + week, tow = datetime_to_tow(datetime) + return cls(week, tow) + + @classmethod + def from_logs(cls, raw_qcom_measurement_report, clocks): + #TODO + #return cls(week, mono_time, gps_time) + return None + + def mono2gps(self, mono_time): + return self.ref_gps_time + mono_time - self.ref_mono_time + + def gps2mono(self, gps_time): + return gps_time - self.ref_gps_time + self.ref_mono_time + + def __str__(self): + return f"Reference mono time: {self.ref_mono_time} \n Reference gps time: {self.ref_gps_time}" diff --git a/laika/helpers.py b/laika/helpers.py new file mode 100644 index 000000000..4ebba76c1 --- /dev/null +++ b/laika/helpers.py @@ -0,0 +1,224 @@ +from enum import IntEnum +from typing import Dict + +import numpy as np +from .lib.coordinates import LocalCoord + + +class ConstellationId(IntEnum): + # Int values match Ublox gnssid version 8 + GPS = 0 + SBAS = 1 + GALILEO = 2 + BEIDOU = 3 + IMES = 4 + QZNSS = 5 + GLONASS = 6 + # Not supported by Ublox: + IRNSS = 7 + + def to_rinex_char(self) -> str: + # returns single character id + return RINEX_CONSTELLATION_TO_ID[self] + + @classmethod + def from_rinex_char(cls, c: str): + if c in RINEX_ID_TO_CONSTELLATION: + return RINEX_ID_TO_CONSTELLATION[c] + else: + return None + + @classmethod + def from_qcom_source(cls, report_source: int): + if report_source == 0: + return ConstellationId.GPS + if report_source == 6: + return ConstellationId.SBAS + elif report_source == 1: + return ConstellationId.GLONASS + raise NotImplementedError('Only GPS (0), SBAS (1) and GLONASS (6) are supported from qcom, not:', {report_source}) + + +# From https://gpsd.gitlab.io/gpsd/NMEA.html#_satellite_ids +# NmeaId is the unique 3 digits id for every satellite globally. (Example: 001, 201) +# SvId is the 2 digits satellite id that is unique within a constellation. (Get the unique satellite with the constellation id. Examples: G01, R01) +CONSTELLATION_TO_NMEA_RANGES = { + # NmeaId ranges for each constellation with its svId offset. + # constellation: [(start, end, svIdOffset)] + # svId = nmeaId + offset + ConstellationId.GPS: [(1, 32, 0)], # svId [1,32] + ConstellationId.SBAS: [(33, 64, -32), (120, 158, -87)], # svId [1,71] + ConstellationId.GLONASS: [(65, 96, -64)], # svId [1,31] + ConstellationId.IMES: [(173, 182, -172)], # svId [1,9] + ConstellationId.QZNSS: [(193, 200, -192)], # svId [1,28] # todo should be QZSS + ConstellationId.BEIDOU: [(201, 235, -200), (401, 437, -365)], # svId 1-72 + ConstellationId.GALILEO: [(301, 336, -300)] # svId 1-36 +} +# +# # Source: RINEX 3.04 +RINEX_CONSTELLATION_TO_ID: Dict[ConstellationId, str] = { + ConstellationId.GPS: 'G', + ConstellationId.GLONASS: 'R', + ConstellationId.SBAS: 'S', + ConstellationId.GALILEO: 'E', + ConstellationId.BEIDOU: 'C', + ConstellationId.QZNSS: 'J', + ConstellationId.IRNSS: 'I' +} + +# Make above dictionary bidirectional map: +# Now you can ask for constellation using: +# >>> RINEX_CONSTELLATION_IDENTIFIERS['R'] +# "GLONASS" +RINEX_ID_TO_CONSTELLATION: Dict[str, ConstellationId] = {id: con for con, id in RINEX_CONSTELLATION_TO_ID.items()} + + +def get_el_az(pos, sat_pos): + converter = LocalCoord.from_ecef(pos) + sat_ned = converter.ecef2ned(sat_pos) + sat_range = np.linalg.norm(sat_ned) + + el = np.arcsin(-sat_ned[2] / sat_range) # pylint: disable=unsubscriptable-object + az = np.arctan2(sat_ned[1], sat_ned[0]) # pylint: disable=unsubscriptable-object + return el, az + + +def get_closest(time, candidates, recv_pos=None): + if recv_pos is None: + # Takes a list of object that have an epoch(GPSTime) value + # and return the one that is closest the given time (GPSTime) + return min(candidates, key=lambda candidate: abs(time - candidate.epoch), default=None) + + return min( + (candidate for candidate in candidates if candidate.valid(time, recv_pos)), + key=lambda candidate: np.linalg.norm(recv_pos - candidate.pos), + default=None, + ) + + +def get_constellation(prn: str): + identifier = prn[0] + constellation = ConstellationId.from_rinex_char(identifier) + if constellation is not None: + return constellation.name + return None + + +def get_constellation_and_sv_id(nmea_id): + for c, ranges in CONSTELLATION_TO_NMEA_RANGES.items(): + for (start, end, sv_id_offset) in ranges: + if start <= nmea_id <= end: + sv_id = nmea_id + sv_id_offset + return c, sv_id + + raise ValueError(f"constellation not found for nmeaid {nmea_id}") + + +def get_prn_from_nmea_id(nmea_id: int): + c_id, sv_id = get_constellation_and_sv_id(nmea_id) + return "%s%02d" % (c_id.to_rinex_char(), sv_id) + + +def get_nmea_id_from_prn(prn: str): + constellation = get_constellation(prn) + if constellation is None: + raise ValueError(f"Constellation not found for prn {prn}") + + sv_id = int(prn[1:]) # satellite id + return get_nmea_id_from_constellation_and_svid(ConstellationId[constellation], sv_id) + + +def get_nmea_id_from_constellation_and_svid(constellation: ConstellationId, sv_id: int): + ranges = CONSTELLATION_TO_NMEA_RANGES[constellation] + for (start, end, sv_id_offset) in ranges: + new_nmea_id = sv_id - sv_id_offset + if start <= new_nmea_id <= end: + return new_nmea_id + + raise ValueError(f"NMEA ID not found for constellation {constellation.name} with satellite id {sv_id}") + + +def rinex3_obs_from_rinex2_obs(observable): + if observable == 'P2': + return 'C2P' + if len(observable) == 2: + return observable + 'C' + raise NotImplementedError("Don't know this: " + observable) + + +class TimeRangeHolder: + '''Class to support test if date is in any of the multiple, sparse ranges''' + + def __init__(self): + # Sorted list + self._ranges = [] + + def _previous_and_contains_index(self, time): + prev = None + current = None + + for idx, (start, end) in enumerate(self._ranges): + # Time may be in next range + if time > end: + continue + + # Time isn't in any next range + if time < start: + prev = idx - 1 + current = None + # Time is in current range + else: + prev = idx - 1 + current = idx + break + + # Break in last loop + if prev is None: + prev = len(self._ranges) - 1 + + return prev, current + + def add(self, start_time, end_time): + prev_start, current_start = self._previous_and_contains_index(start_time) + _, current_end = self._previous_and_contains_index(end_time) + + # Merge ranges + if current_start is not None and current_end is not None: + # If ranges are different then merge + if current_start != current_end: + new_start, _ = self._ranges[current_start] + _, new_end = self._ranges[current_end] + new_range = (new_start, new_end) + # Required reversed order to correct remove + del self._ranges[current_end] + del self._ranges[current_start] + self._ranges.insert(current_start, new_range) + # Extend range - left + elif current_start is not None: + new_start, _ = self._ranges[current_start] + new_range = (new_start, end_time) + del self._ranges[current_start] + self._ranges.insert(current_start, new_range) + # Extend range - right + elif current_end is not None: + _, new_end = self._ranges[current_end] + new_range = (start_time, new_end) + del self._ranges[current_end] + self._ranges.insert(prev_start + 1, new_range) + # Create new range + else: + new_range = (start_time, end_time) + self._ranges.insert(prev_start + 1, new_range) + + def __contains__(self, time): + for start, end in self._ranges: + # Time may be in next range + if time > end: + continue + + # Time isn't in any next range + if time < start: + return False + # Time is in current range + return True + return False diff --git a/laika/iono.py b/laika/iono.py new file mode 100644 index 000000000..16bad87fe --- /dev/null +++ b/laika/iono.py @@ -0,0 +1,246 @@ +import datetime as dt +import numpy as np +import re +from math import cos, sin, pi, floor +from .constants import SECS_IN_MIN, SECS_IN_HR, EARTH_RADIUS +from .lib.coordinates import LocalCoord +from .gps_time import GPSTime + +# Altitude of Ionospheric-pierce-point +IPP_ALT = 6821000 + + +def closest_in_list(lst, val, num=2): + """ + Returns two (`num` in general) closest values of `val` in list `lst` + """ + idxs = sorted(lst, key=lambda x: abs(x - val))[:num] + return sorted(list(lst).index(x) for x in idxs) + + +def get_header_line(headr, proprty): + """ + :param headr: the header of the RINEX-file + :param proprty: string-like property to search for (e.g. 'delta-utc') + :return: the string of the ``headr`` containing ``property`` + """ + pattern = re.compile(proprty, re.IGNORECASE) + for d in headr: + if pattern.search(d): + return d + + +def get_header_body(file_path): + """ + Opens `file_path`, reads file and returns header and body + separated with "END OF HEADER" + :param file_path: path to RINEX-like file + :return: header, body (arrays of lines) + """ + with open(file_path) as fd: + data = fd.readlines() + for j, d in enumerate(data): + if "END OF HEADER" in d: + header_end = j + break + return data[:header_end], data[header_end + 1:] + + +def get_int_from_header(hdr, seq): + """ + Returns the first int from the line that contains `seq` of lines `hdr`. + In fact, _header_ here may not be header of RINEX/IONEX, just some set of lines. + """ + return int(get_header_line(hdr, seq).split()[0]) + +def compute_grid_lats_lons(data): + grid = np.array([], dtype='uint16') + lats = np.array([]) + for j, line in enumerate(data[1:]): + if "LAT" in line: + lat, lon1, lon2, dlon, h = (float(line[x:x + 6]) for x in range(2, 32, 6)) + lats = np.append(lats, lat) + row_length = (lon2 - lon1) / dlon + 1 # total number of values of longitudes + next_lines_with_numbers = int(np.ceil(row_length / 16)) + elems_in_row = [ + min(16, int(row_length - i * 16)) for i in range(next_lines_with_numbers) + ] + row = np.array([], dtype='int16') + for i, elem in enumerate(elems_in_row): + row = np.append( + row, + np.array( + [int(data[j + 2 + i][5 * x:5 * x + 5]) for x in range(elem)], + dtype='int16', + ), + ) + if len(grid) > 0: + grid = np.vstack((grid, row)) + else: + grid = np.append(grid, row) + lons = np.linspace(lon1, lon2, int(row_length)) + return (grid, lats, lons) + + +class IonexMap: + def __init__(self, exp, data1, data2): + self.exp = exp + self.t1 = GPSTime.from_datetime(dt.datetime(*[int(d) for d in data1[0].split()[:6]])) + self.t2 = GPSTime.from_datetime(dt.datetime(*[int(d) for d in data2[0].split()[:6]])) + assert self.t2 - self.t1 == SECS_IN_HR + assert len(data1) == len(data2) + + self.max_time_diff = SECS_IN_MIN*30 + self.epoch = self.t1 + self.max_time_diff + + self.grid_TEC1, self.lats, self.lons = compute_grid_lats_lons(data1) + self.grid_TEC2, self.lats, self.lons = compute_grid_lats_lons(data2) + + def valid(self, time): + return abs(time - self.epoch) <= self.max_time_diff + + @staticmethod + def find_nearest(lst, val): + return (np.abs(lst - val)).argmin() + + def get_TEC(self, pos, time): + """ + Returns TEC in a position `pos` of ionosphere + :param pos: (lat, lon) [deg, deg] + :return: + """ + if pos[0] in self.lats and pos[1] in self.lons: + lat = self.find_nearest(self.lats, pos[0]) + lon = self.find_nearest(self.lons, pos[1]) + E = self.grid_TEC1[lat][lon] + self.grid_TEC2[lat][lon] + return E + lat_idxs = closest_in_list(self.lats, pos[0]) + lon_idxs = closest_in_list(self.lons, pos[1]) + lat0, lat1 = self.lats[lat_idxs[0]], self.lats[lat_idxs[1]] + lon0, lon1 = self.lons[lon_idxs[0]], self.lons[lon_idxs[1]] + dlat = lat1 - lat0 + dlon = lon1 - lon0 + p = float(pos[0] - lat0) / dlat + q = float(pos[1] - lon0) / dlon + + (E00, E10), (E01, E11) = self.grid_TEC1[lat_idxs[0]:lat_idxs[1] + 1, lon_idxs[0]:lon_idxs[1] + 1] + TEC_1 = ((1 - p) * (1 - q) * E00 + p * (1 - q) * E01 + (1 - p) * q * E10 + p * q * E11) + (E00, E10), (E01, E11) = self.grid_TEC2[lat_idxs[0]:lat_idxs[1] + 1, lon_idxs[0]:lon_idxs[1] + 1] + TEC_2 = ((1 - p) * (1 - q) * E00 + p * (1 - q) * E01 + (1 - p) * q * E10 + p * q * E11) + + return (1 - (time - self.t1)/SECS_IN_HR)*TEC_1 + ((time - self.t1)/SECS_IN_HR)*TEC_2 + + def get_delay(self, rcv_pos, az, el, sat_pos, time, freq): + # To get a delay from a TEC map, we need to calculate + # the ionospheric pierce point, geometry described here + # https://en.wikipedia.org/wiki/Ionospheric_pierce_point + conv = LocalCoord.from_ecef(rcv_pos) + geocentric_alt = np.linalg.norm(rcv_pos) + alpha = np.pi/2 + el + beta = np.arcsin(geocentric_alt*np.sin(alpha)/IPP_ALT) + gamma = np.pi - alpha - beta + ipp_dist = geocentric_alt*np.sin(gamma)/np.sin(beta) + ipp_ned = conv.ecef2ned(sat_pos)*(ipp_dist)/np.linalg.norm(sat_pos) + ipp_geo = conv.ned2geodetic(ipp_ned) + factor = 40.30E16 / (freq**2) * 10**(self.exp) + vertical_delay = self.get_TEC(ipp_geo, time) * factor + slant_delay = vertical_delay * ((1 - ((EARTH_RADIUS * np.sin(beta)) / + (EARTH_RADIUS + 3.5e5))**2)**(-0.5)) + return slant_delay + + @staticmethod + def round_to_grid(number, base): + return int(base * round(float(number) / base)) + + +def parse_ionex(ionex_file): + """ + :param ionex_file: path to the IONEX file + :return: TEC interpolation function `f( (lat,lon), datetime )` + """ + header, body = get_header_body(ionex_file) + + exponent = get_int_from_header(header, "EXPONENT") + maps_count = get_int_from_header(header, "MAPS IN FILE") + # ============= + # Separate maps + # ============= + map_start_idx = [] + map_end_idx = [] + + for j, line in enumerate(body): + if "START OF TEC MAP" in line: + map_start_idx += [j] + elif "END OF TEC MAP" in line: + map_end_idx += [j] + if maps_count != len(map_start_idx): + raise LookupError("Parsing error: the number of maps in the header " + "is not equal to the number of maps in the body.") + if len(map_start_idx) != len(map_end_idx): + raise IndexError("Starts end ends numbers are not equal.") + map_dates = [] + for i in range(maps_count): + date_components = body[map_start_idx[i] + 1].split()[:6] + map_dates.append(dt.datetime(*[int(d) for d in date_components])) + + maps = [] + iono_map = iono_map_prev = None + for m in range(maps_count): + iono_map_prev = iono_map + iono_map = body[map_start_idx[m] + 1:map_end_idx[m]] + if iono_map and iono_map_prev: + maps += [IonexMap(exponent, iono_map_prev, iono_map)] + return maps + + +def klobuchar(pos, az, el, time, iono_coeffs): + """ + Details are taken from [5]: IS-GPS-200H, Fig. 20-4 + Note: result is referred to the GPS L₁ frequency; + if the user is operating on the GPS L₂ frequency, the correction term must + be multiplied by γ = f₂²/f₁¹ = 0.6071850227694382 + :param pos: [lat, lon, alt] in radians and meters + """ + + tow = time.tow + if pos[2] < -1E3 or el < 0: + return 0.0 + if len(iono_coeffs) < 8: + return None + + # earth centered angle (semi-circle) + psi = 0.0137 / (el / pi + 0.11) - 0.022 + + # subionospheric latitude/longitude (semi-circle) + phi = pos[0] / pi + psi * cos(az) + if phi > 0.416: + phi = 0.416 + elif phi < -0.416: + phi = -0.416 + lam = pos[1] / pi + psi * sin(az) / cos(phi * pi) + + # geomagnetic latitude (semi-circle) */ + phi += 0.064 * cos((lam - 1.617) * pi) + + # local time (s) + tt = 43200.0 * lam + tow + tt -= floor(tt / 86400.0) * 86400.0 # 0<=tt<86400 + + # slant factor + f = 1.0 + 16.0 * pow(0.53 - el / pi, 3.0) + + # ionospheric delay + amp = iono_coeffs[0] + phi * (iono_coeffs[1] + phi * + (iono_coeffs[2] + phi * iono_coeffs[3])) + per = iono_coeffs[4] + phi * (iono_coeffs[5] + phi * + (iono_coeffs[6] + phi * iono_coeffs[7])) + if amp < 0.0: + amp = 0. + if per < 72000.0: + per = 72000.0 + x = 2.0 * pi * (tt - 50400.0) / per + + mul = 5E-9 + if abs(x) < 1.57: + mul = (5E-9 + amp * (1.0 + x * x * (-0.5 + x * x / 24.0))) + return 2.99792458E8 * f * mul diff --git a/selfdrive/camerad/snapshot/__init__.py b/laika/lib/__init__.py similarity index 100% rename from selfdrive/camerad/snapshot/__init__.py rename to laika/lib/__init__.py diff --git a/laika/lib/coordinates.py b/laika/lib/coordinates.py new file mode 100644 index 000000000..c6e584f83 --- /dev/null +++ b/laika/lib/coordinates.py @@ -0,0 +1,106 @@ +import numpy as np +""" +Coordinate transformation module. All methods accept arrays as input +with each row as a position. +""" + + +a = 6378137 +b = 6356752.3142 +esq = 6.69437999014 * 0.001 +e1sq = 6.73949674228 * 0.001 + + +def geodetic2ecef(geodetic, radians=False): + geodetic = np.array(geodetic) + input_shape = geodetic.shape + geodetic = np.atleast_2d(geodetic) + + ratio = 1.0 if radians else (np.pi / 180.0) + lat = ratio*geodetic[:,0] + lon = ratio*geodetic[:,1] + alt = geodetic[:,2] + + xi = np.sqrt(1 - esq * np.sin(lat)**2) + x = (a / xi + alt) * np.cos(lat) * np.cos(lon) + y = (a / xi + alt) * np.cos(lat) * np.sin(lon) + z = (a / xi * (1 - esq) + alt) * np.sin(lat) + ecef = np.array([x, y, z]).T + return ecef.reshape(input_shape) + + +def ecef2geodetic(ecef, radians=False): + """ + Convert ECEF coordinates to geodetic using ferrari's method + """ + # Save shape and export column + ecef = np.atleast_1d(ecef) + input_shape = ecef.shape + ecef = np.atleast_2d(ecef) + x, y, z = ecef[:, 0], ecef[:, 1], ecef[:, 2] + + ratio = 1.0 if radians else (180.0 / np.pi) + + # Conver from ECEF to geodetic using Ferrari's methods + # https://en.wikipedia.org/wiki/Geographic_coordinate_conversion#Ferrari.27s_solution + r = np.sqrt(x * x + y * y) + Esq = a * a - b * b + F = 54 * b * b * z * z + G = r * r + (1 - esq) * z * z - esq * Esq + C = (esq * esq * F * r * r) / (pow(G, 3)) + S = np.cbrt(1 + C + np.sqrt(C * C + 2 * C)) + P = F / (3 * pow((S + 1 / S + 1), 2) * G * G) + Q = np.sqrt(1 + 2 * esq * esq * P) + r_0 = -(P * esq * r) / (1 + Q) + np.sqrt(0.5 * a * a*(1 + 1.0 / Q) - + P * (1 - esq) * z * z / (Q * (1 + Q)) - 0.5 * P * r * r) + U = np.sqrt(pow((r - esq * r_0), 2) + z * z) + V = np.sqrt(pow((r - esq * r_0), 2) + (1 - esq) * z * z) + Z_0 = b * b * z / (a * V) + h = U * (1 - b * b / (a * V)) + lat = ratio*np.arctan((z + e1sq * Z_0) / r) + lon = ratio*np.arctan2(y, x) + + # stack the new columns and return to the original shape + geodetic = np.column_stack((lat, lon, h)) + return geodetic.reshape(input_shape) + +class LocalCoord: + """ + Allows conversions to local frames. In this case NED. + That is: North East Down from the start position in + meters. + """ + def __init__(self, init_geodetic, init_ecef): + self.init_ecef = init_ecef + lat, lon, _ = (np.pi/180)*np.array(init_geodetic) + self.ned2ecef_matrix = np.array([[-np.sin(lat)*np.cos(lon), -np.sin(lon), -np.cos(lat)*np.cos(lon)], + [-np.sin(lat)*np.sin(lon), np.cos(lon), -np.cos(lat)*np.sin(lon)], + [np.cos(lat), 0, -np.sin(lat)]]) + self.ecef2ned_matrix = self.ned2ecef_matrix.T + + @classmethod + def from_geodetic(cls, init_geodetic): + init_ecef = geodetic2ecef(init_geodetic) + return LocalCoord(init_geodetic, init_ecef) + + @classmethod + def from_ecef(cls, init_ecef): + init_geodetic = ecef2geodetic(init_ecef) + return LocalCoord(init_geodetic, init_ecef) + + def ecef2ned(self, ecef): + ecef = np.array(ecef) + return np.dot(self.ecef2ned_matrix, (ecef - self.init_ecef).T).T + + def ned2ecef(self, ned): + ned = np.array(ned) + # Transpose so that init_ecef will broadcast correctly for 1d or 2d ned. + return (np.dot(self.ned2ecef_matrix, ned.T).T + self.init_ecef) + + def geodetic2ned(self, geodetic): + ecef = geodetic2ecef(geodetic) + return self.ecef2ned(ecef) + + def ned2geodetic(self, ned): + ecef = self.ned2ecef(ned) + return ecef2geodetic(ecef) diff --git a/laika/lib/orientation.py b/laika/lib/orientation.py new file mode 100644 index 000000000..5f8d5d2b3 --- /dev/null +++ b/laika/lib/orientation.py @@ -0,0 +1,291 @@ +import numpy as np +from numpy import dot, inner, array, linalg +from .coordinates import LocalCoord + + +''' +Vectorized functions that transform between +rotation matrices, euler angles and quaternions. +All support lists, array or array of arrays as inputs. +Supports both x2y and y_from_x format (y_from_x preferred!). +''' + +def euler2quat(eulers): + eulers = array(eulers) + if len(eulers.shape) > 1: + output_shape = (-1,4) + else: + output_shape = (4,) + eulers = np.atleast_2d(eulers) + gamma, theta, psi = eulers[:,0], eulers[:,1], eulers[:,2] + + cos_half_gamma = np.cos(gamma / 2) + cos_half_theta = np.cos(theta / 2) + cos_half_psi = np.cos(psi / 2) + sin_half_gamma = np.sin(gamma / 2) + sin_half_theta = np.sin(theta / 2) + sin_half_psi = np.sin(psi / 2) + q0 = cos_half_gamma * cos_half_theta * cos_half_psi + sin_half_gamma * sin_half_theta * sin_half_psi + q1 = sin_half_gamma * cos_half_theta * cos_half_psi - cos_half_gamma * sin_half_theta * sin_half_psi + q2 = cos_half_gamma * sin_half_theta * cos_half_psi + sin_half_gamma * cos_half_theta * sin_half_psi + q3 = cos_half_gamma * cos_half_theta * sin_half_psi - sin_half_gamma * sin_half_theta * cos_half_psi + + quats = array([q0, q1, q2, q3]).T + for i in range(len(quats)): + if quats[i,0] < 0: + quats[i] = -quats[i] + return quats.reshape(output_shape) + + +def quat2euler(quats): + quats = array(quats) + if len(quats.shape) > 1: + output_shape = (-1,3) + else: + output_shape = (3,) + quats = np.atleast_2d(quats) + q0, q1, q2, q3 = quats[:,0], quats[:,1], quats[:,2], quats[:,3] + + gamma = np.arctan2(2 * (q0 * q1 + q2 * q3), 1 - 2 * (q1**2 + q2**2)) + theta = np.arcsin(2 * (q0 * q2 - q3 * q1)) + psi = np.arctan2(2 * (q0 * q3 + q1 * q2), 1 - 2 * (q2**2 + q3**2)) + + eulers = array([gamma, theta, psi]).T + return eulers.reshape(output_shape) + + +def quat2rot(quats): + quats = array(quats) + input_shape = quats.shape + quats = np.atleast_2d(quats) + Rs = np.zeros((quats.shape[0], 3, 3)) + q0 = quats[:, 0] + q1 = quats[:, 1] + q2 = quats[:, 2] + q3 = quats[:, 3] + Rs[:, 0, 0] = q0 * q0 + q1 * q1 - q2 * q2 - q3 * q3 + Rs[:, 0, 1] = 2 * (q1 * q2 - q0 * q3) + Rs[:, 0, 2] = 2 * (q0 * q2 + q1 * q3) + Rs[:, 1, 0] = 2 * (q1 * q2 + q0 * q3) + Rs[:, 1, 1] = q0 * q0 - q1 * q1 + q2 * q2 - q3 * q3 + Rs[:, 1, 2] = 2 * (q2 * q3 - q0 * q1) + Rs[:, 2, 0] = 2 * (q1 * q3 - q0 * q2) + Rs[:, 2, 1] = 2 * (q0 * q1 + q2 * q3) + Rs[:, 2, 2] = q0 * q0 - q1 * q1 - q2 * q2 + q3 * q3 + + if len(input_shape) < 2: + return Rs[0] + return Rs + + +def rot2quat(rots): + input_shape = rots.shape + if len(input_shape) < 3: + rots = array([rots]) + K3 = np.empty((len(rots), 4, 4)) + K3[:, 0, 0] = (rots[:, 0, 0] - rots[:, 1, 1] - rots[:, 2, 2]) / 3.0 + K3[:, 0, 1] = (rots[:, 1, 0] + rots[:, 0, 1]) / 3.0 + K3[:, 0, 2] = (rots[:, 2, 0] + rots[:, 0, 2]) / 3.0 + K3[:, 0, 3] = (rots[:, 1, 2] - rots[:, 2, 1]) / 3.0 + K3[:, 1, 0] = K3[:, 0, 1] + K3[:, 1, 1] = (rots[:, 1, 1] - rots[:, 0, 0] - rots[:, 2, 2]) / 3.0 + K3[:, 1, 2] = (rots[:, 2, 1] + rots[:, 1, 2]) / 3.0 + K3[:, 1, 3] = (rots[:, 2, 0] - rots[:, 0, 2]) / 3.0 + K3[:, 2, 0] = K3[:, 0, 2] + K3[:, 2, 1] = K3[:, 1, 2] + K3[:, 2, 2] = (rots[:, 2, 2] - rots[:, 0, 0] - rots[:, 1, 1]) / 3.0 + K3[:, 2, 3] = (rots[:, 0, 1] - rots[:, 1, 0]) / 3.0 + K3[:, 3, 0] = K3[:, 0, 3] + K3[:, 3, 1] = K3[:, 1, 3] + K3[:, 3, 2] = K3[:, 2, 3] + K3[:, 3, 3] = (rots[:, 0, 0] + rots[:, 1, 1] + rots[:, 2, 2]) / 3.0 + q = np.empty((len(rots), 4)) + for i in range(len(rots)): + _, eigvecs = linalg.eigh(K3[i].T) + eigvecs = eigvecs[:,3:] + q[i, 0] = eigvecs[-1] + q[i, 1:] = -eigvecs[:-1].flatten() + if q[i, 0] < 0: + q[i] = -q[i] + + if len(input_shape) < 3: + return q[0] + return q + + +def euler2rot(eulers): + return rotations_from_quats(euler2quat(eulers)) + + +def rot2euler(rots): + return quat2euler(quats_from_rotations(rots)) + + +quats_from_rotations = rot2quat +quat_from_rot = rot2quat +rotations_from_quats = quat2rot +rot_from_quat= quat2rot +rot_from_quat= quat2rot +euler_from_rot = rot2euler +euler_from_quat = quat2euler +rot_from_euler = euler2rot +quat_from_euler = euler2quat + + +''' +Random helpers below +''' + + +def quat_product(q, r): + t = np.zeros(4) + t[0] = r[0] * q[0] - r[1] * q[1] - r[2] * q[2] - r[3] * q[3] + t[1] = r[0] * q[1] + r[1] * q[0] - r[2] * q[3] + r[3] * q[2] + t[2] = r[0] * q[2] + r[1] * q[3] + r[2] * q[0] - r[3] * q[1] + t[3] = r[0] * q[3] - r[1] * q[2] + r[2] * q[1] + r[3] * q[0] + return t + + +def rot_matrix(roll, pitch, yaw): + cr, sr = np.cos(roll), np.sin(roll) + cp, sp = np.cos(pitch), np.sin(pitch) + cy, sy = np.cos(yaw), np.sin(yaw) + rr = array([[1,0,0],[0, cr,-sr],[0, sr, cr]]) + rp = array([[cp,0,sp],[0, 1,0],[-sp, 0, cp]]) + ry = array([[cy,-sy,0],[sy, cy,0],[0, 0, 1]]) + return ry.dot(rp.dot(rr)) + + +def rot(axis, angle): + # Rotates around an arbitrary axis + ret_1 = (1 - np.cos(angle)) * array([[axis[0]**2, axis[0] * axis[1], axis[0] * axis[2]], [ + axis[1] * axis[0], axis[1]**2, axis[1] * axis[2] + ], [axis[2] * axis[0], axis[2] * axis[1], axis[2]**2]]) + ret_2 = np.cos(angle) * np.eye(3) + ret_3 = np.sin(angle) * array([[0, -axis[2], axis[1]], [axis[2], 0, -axis[0]], + [-axis[1], axis[0], 0]]) + return ret_1 + ret_2 + ret_3 + + +def ecef_euler_from_ned(ned_ecef_init, ned_pose): + ''' + Got it from here: + Using Rotations to Build Aerospace Coordinate Systems + -Don Koks + ''' + converter = LocalCoord.from_ecef(ned_ecef_init) + x0 = converter.ned2ecef([1, 0, 0]) - converter.ned2ecef([0, 0, 0]) + y0 = converter.ned2ecef([0, 1, 0]) - converter.ned2ecef([0, 0, 0]) + z0 = converter.ned2ecef([0, 0, 1]) - converter.ned2ecef([0, 0, 0]) + + x1 = rot(z0, ned_pose[2]).dot(x0) + y1 = rot(z0, ned_pose[2]).dot(y0) + z1 = rot(z0, ned_pose[2]).dot(z0) + + x2 = rot(y1, ned_pose[1]).dot(x1) + y2 = rot(y1, ned_pose[1]).dot(y1) + z2 = rot(y1, ned_pose[1]).dot(z1) + + x3 = rot(x2, ned_pose[0]).dot(x2) + y3 = rot(x2, ned_pose[0]).dot(y2) + #z3 = rot(x2, ned_pose[0]).dot(z2) + + x0 = array([1, 0, 0]) + y0 = array([0, 1, 0]) + z0 = array([0, 0, 1]) + + psi = np.arctan2(inner(x3, y0), inner(x3, x0)) + theta = np.arctan2(-inner(x3, z0), np.sqrt(inner(x3, x0)**2 + inner(x3, y0)**2)) + y2 = rot(z0, psi).dot(y0) + z2 = rot(y2, theta).dot(z0) + phi = np.arctan2(inner(y3, z2), inner(y3, y2)) + + ret = array([phi, theta, psi]) + return ret + + +def ned_euler_from_ecef(ned_ecef_init, ecef_poses): + ''' + Got the math from here: + Using Rotations to Build Aerospace Coordinate Systems + -Don Koks + + Also accepts array of ecef_poses and array of ned_ecef_inits. + Where each row is a pose and an ecef_init. + ''' + ned_ecef_init = array(ned_ecef_init) + ecef_poses = array(ecef_poses) + output_shape = ecef_poses.shape + ned_ecef_init = np.atleast_2d(ned_ecef_init) + if ned_ecef_init.shape[0] == 1: + ned_ecef_init = np.tile(ned_ecef_init[0], (output_shape[0], 1)) + ecef_poses = np.atleast_2d(ecef_poses) + + ned_poses = np.zeros(ecef_poses.shape) + for i, ecef_pose in enumerate(ecef_poses): + converter = LocalCoord.from_ecef(ned_ecef_init[i]) + x0 = array([1, 0, 0]) + y0 = array([0, 1, 0]) + z0 = array([0, 0, 1]) + + x1 = rot(z0, ecef_pose[2]).dot(x0) + y1 = rot(z0, ecef_pose[2]).dot(y0) + z1 = rot(z0, ecef_pose[2]).dot(z0) + + x2 = rot(y1, ecef_pose[1]).dot(x1) + y2 = rot(y1, ecef_pose[1]).dot(y1) + z2 = rot(y1, ecef_pose[1]).dot(z1) + + x3 = rot(x2, ecef_pose[0]).dot(x2) + y3 = rot(x2, ecef_pose[0]).dot(y2) + #z3 = rot(x2, ecef_pose[0]).dot(z2) + + x0 = converter.ned2ecef([1, 0, 0]) - converter.ned2ecef([0, 0, 0]) + y0 = converter.ned2ecef([0, 1, 0]) - converter.ned2ecef([0, 0, 0]) + z0 = converter.ned2ecef([0, 0, 1]) - converter.ned2ecef([0, 0, 0]) + + psi = np.arctan2(inner(x3, y0), inner(x3, x0)) + theta = np.arctan2(-inner(x3, z0), np.sqrt(inner(x3, x0)**2 + inner(x3, y0)**2)) + y2 = rot(z0, psi).dot(y0) + z2 = rot(y2, theta).dot(z0) + phi = np.arctan2(inner(y3, z2), inner(y3, y2)) + ned_poses[i] = array([phi, theta, psi]) + + return ned_poses.reshape(output_shape) + + +def ecef2car(car_ecef, psi, theta, points_ecef, ned_converter): + """ + TODO: add roll rotation + Converts an array of points in ecef coordinates into + x-forward, y-left, z-up coordinates + Parameters + ---------- + psi: yaw, radian + theta: pitch, radian + Returns + ------- + [x, y, z] coordinates in car frame + """ + + # input is an array of points in ecef cocrdinates + # output is an array of points in car's coordinate (x-front, y-left, z-up) + + # convert points to NED + points_ned = [] + for p in points_ecef: + points_ned.append(ned_converter.ecef2ned_matrix.dot(array(p) - car_ecef)) + + points_ned = np.vstack(points_ned).T + + # n, e, d -> x, y, z + # Calculate relative positions and rotate wrt to heading and pitch of car + invert_R = array([[1., 0., 0.], [0., -1., 0.], [0., 0., -1.]]) + + c, s = np.cos(psi), np.sin(psi) + yaw_R = array([[c, s, 0.], [-s, c, 0.], [0., 0., 1.]]) + + c, s = np.cos(theta), np.sin(theta) + pitch_R = array([[c, 0., -s], [0., 1., 0.], [s, 0., c]]) + + return dot(pitch_R, dot(yaw_R, dot(invert_R, points_ned))) diff --git a/laika/raw_gnss.py b/laika/raw_gnss.py new file mode 100644 index 000000000..39eabda45 --- /dev/null +++ b/laika/raw_gnss.py @@ -0,0 +1,433 @@ +from math import sqrt +from typing import Dict, List, Optional, Union + +import numpy as np +import datetime +import struct + +from . import constants +from .ephemeris import Ephemeris +from .lib.coordinates import LocalCoord +from .gps_time import GPSTime +from .helpers import ConstellationId, get_constellation_and_sv_id, get_nmea_id_from_constellation_and_svid, \ + rinex3_obs_from_rinex2_obs + + +def array_from_normal_meas(meas): + return np.concatenate(([meas.get_nmea_id()], + [meas.recv_time_week], + [meas.recv_time_sec], + [meas.glonass_freq], + [meas.observables['C1C']], + [meas.observables_std['C1C']], + [meas.observables['D1C']], + [meas.observables_std['D1C']], + [meas.observables['S1C']], + [meas.observables['L1C']])) + + +def normal_meas_from_array(arr): + observables, observables_std = {}, {} + observables['C1C'] = arr[4] + observables_std['C1C'] = arr[5] + observables['D1C'] = arr[6] + observables_std['D1C'] = arr[7] + observables['S1C'] = arr[8] + observables['L1C'] = arr[9] + constellation_id, sv_id = get_constellation_and_sv_id(nmea_id=arr[0]) + return GNSSMeasurement(constellation_id, sv_id, arr[1], arr[2], + observables, observables_std, arr[3]) + + +class GNSSMeasurement: + PRN = 0 + RECV_TIME_WEEK = 1 + RECV_TIME_SEC = 2 + GLONASS_FREQ = 3 + + PR = 4 + PR_STD = 5 + PRR = 6 + PRR_STD = 7 + + SAT_POS = slice(8, 11) + SAT_VEL = slice(11, 14) + + def __init__(self, constellation_id: ConstellationId, sv_id: int, recv_time_week: int, recv_time_sec: float, observables: Dict[str, float], observables_std: Dict[str, float], + glonass_freq: Union[int, float] = None): + # Metadata + # prn: unique satellite id + self.prn = "%s%02d" % (constellation_id.to_rinex_char(), sv_id) # satellite ID in rinex convention + self.constellation_id = constellation_id + self.sv_id = sv_id # satellite id per constellation + + self.recv_time_week = recv_time_week + self.recv_time_sec = recv_time_sec + self.recv_time = GPSTime(recv_time_week, recv_time_sec) + self.glonass_freq = glonass_freq # glonass channel + + # Measurements + self.observables = observables + self.observables_std = observables_std + + # flags + self.processed = False + self.corrected = False + + # sat info + self.sat_pos = np.array([np.nan, np.nan, np.nan]) + self.sat_vel = np.array([np.nan, np.nan, np.nan]) + self.sat_clock_err = np.nan + self.sat_ephemeris: Optional[Ephemeris] = None + + self.sat_pos_final = np.array([np.nan, np.nan, np.nan]) # sat_pos in receiver time's ECEF frame instead of satellite time's ECEF frame + self.observables_final: Dict[str, float] = {} + + def process(self, dog): + sat_time = self.recv_time - self.observables['C1C']/constants.SPEED_OF_LIGHT + sat_info = dog.get_sat_info(self.prn, sat_time) + if sat_info is None: + return False + self.sat_pos, self.sat_vel, self.sat_clock_err, _, self.sat_ephemeris = sat_info + self.processed = True + return True + + def correct(self, est_pos, dog, allow_incomplete_delay=False): + for obs in self.observables: + if obs[0] == 'C': # or obs[0] == 'L': + delay = dog.get_delay(self.prn, self.recv_time, est_pos, signal=obs) + if delay is not None and (allow_incomplete_delay or delay != 0): + self.observables_final[obs] = (self.observables[obs] + + self.sat_clock_err*constants.SPEED_OF_LIGHT - + delay) + else: + self.observables_final[obs] = self.observables[obs] + if 'C1C' in self.observables_final and 'C2P' in self.observables_final: + self.observables_final['IOF'] = (((constants.GPS_L1**2)*self.observables_final['C1C'] - + (constants.GPS_L2**2)*self.observables_final['C2P'])/ + (constants.GPS_L1**2 - constants.GPS_L2**2)) + + geometric_range = np.linalg.norm(self.sat_pos - est_pos) + theta_1 = constants.EARTH_ROTATION_RATE * geometric_range / constants.SPEED_OF_LIGHT + self.sat_pos_final = np.array([self.sat_pos[0] * np.cos(theta_1) + self.sat_pos[1] * np.sin(theta_1), + self.sat_pos[1] * np.cos(theta_1) - self.sat_pos[0] * np.sin(theta_1), + self.sat_pos[2]]) + if 'C1C' in self.observables_final and np.isfinite(self.observables_final['C1C']): + self.corrected = True + return True + return False + + def as_array(self, only_corrected=True): + observables = self.observables_final + sat_pos = self.sat_pos_final + if not self.corrected: + if only_corrected: + raise NotImplementedError('Only corrected measurements can be put into arrays') + else: + observables = self.observables + sat_pos = self.sat_pos + ret = np.array([self.get_nmea_id(), self.recv_time_week, self.recv_time_sec, self.glonass_freq, + observables['C1C'], self.observables_std['C1C'], + observables['D1C'], self.observables_std['D1C']]) + return np.concatenate((ret, sat_pos, self.sat_vel)) + + def __repr__(self): + time = self.recv_time.as_datetime().strftime('%Y-%m-%dT%H:%M:%S.%f') + return f"" + + def get_nmea_id(self): + return get_nmea_id_from_constellation_and_svid(self.constellation_id, self.sv_id) + + +def process_measurements(measurements: List[GNSSMeasurement], dog) -> List[GNSSMeasurement]: + proc_measurements = [] + for meas in measurements: + if meas.process(dog): + proc_measurements.append(meas) + return proc_measurements + + +def correct_measurements(measurements: List[GNSSMeasurement], est_pos, dog, allow_incomplete_delay=False) -> List[GNSSMeasurement]: + corrected_measurements = [] + for meas in measurements: + if meas.correct(est_pos, dog, allow_incomplete_delay): + corrected_measurements.append(meas) + return corrected_measurements + + +def group_measurements_by_epoch(measurements): + meas_filt_by_t = [[measurements[0]]] + for m in measurements[1:]: + if abs(m.recv_time - meas_filt_by_t[-1][-1].recv_time) > 1e-9: + meas_filt_by_t.append([]) + meas_filt_by_t[-1].append(m) + return meas_filt_by_t + + +def group_measurements_by_sat(measurements): + measurements_by_sat = {} + sats = {m.prn for m in measurements} + for sat in sats: + measurements_by_sat[sat] = [m for m in measurements if m.prn == sat] + return measurements_by_sat + + +def read_raw_qcom(report): + dr = 'DrMeasurementReport' in str(report.schema) + # Only gps/sbas and glonass are supported + constellation_id = ConstellationId.from_qcom_source(report.source) + if constellation_id in [ConstellationId.GPS, ConstellationId.SBAS]: # gps/sbas + if dr: + recv_tow = report.gpsMilliseconds / 1000.0 # seconds + time_bias_ms = struct.unpack("f", struct.pack("I", report.gpsTimeBiasMs))[0] + else: + recv_tow = report.milliseconds / 1000.0 # seconds + time_bias_ms = report.timeBias + recv_time = GPSTime(report.gpsWeek, recv_tow) + elif constellation_id == ConstellationId.GLONASS: + if dr: + recv_tow = report.glonassMilliseconds / 1000.0 # seconds + recv_time = GPSTime.from_glonass(report.glonassYear, report.glonassDay, recv_tow) + time_bias_ms = report.glonassTimeBias + else: + recv_tow = report.milliseconds / 1000.0 # seconds + recv_time = GPSTime.from_glonass(report.glonassCycleNumber, report.glonassNumberOfDays, recv_tow) + time_bias_ms = report.timeBias + else: + raise NotImplementedError('Only GPS (0), SBAS (1) and GLONASS (6) are supported from qcom, not:', {report.source}) + #print(recv_time, report.source, time_bias_ms, dr) + measurements = [] + for i in report.sv: + nmea_id = i.svId # todo change svId to nmea_id in cereal message. Or better: change the publisher to publish correct svId's, since constellation id is also given + if nmea_id == 255: + # todo nmea_id is not valid. Fix publisher + continue + _, sv_id = get_constellation_and_sv_id(nmea_id) + if not i.measurementStatus.measurementNotUsable and i.measurementStatus.satelliteTimeIsKnown: + sat_tow = (i.unfilteredMeasurementIntegral + i.unfilteredMeasurementFraction + i.latency + time_bias_ms) / 1000 + observables, observables_std = {}, {} + observables['C1C'] = (recv_tow - sat_tow)*constants.SPEED_OF_LIGHT + observables_std['C1C'] = i.unfilteredTimeUncertainty * 1e-3 * constants.SPEED_OF_LIGHT + if i.measurementStatus.fineOrCoarseVelocity: + # about 10x better, perhaps filtered with carrier phase? + observables['D1C'] = i.fineSpeed + observables_std['D1C'] = i.fineSpeedUncertainty + else: + observables['D1C'] = i.unfilteredSpeed + observables_std['D1C'] = i.unfilteredSpeedUncertainty + observables['S1C'] = (i.carrierNoise/100.) if i.carrierNoise != 0 else np.nan + observables['L1C'] = np.nan + #print(" %.5f %3d %10.2f %7.2f %7.2f %.2f %d" % (recv_time.tow, nmea_id, + # observables['C1C'], observables_std['C1C'], + # observables_std['D1C'], observables['S1C'], i.latency), i.observationState, i.measurementStatus.fineOrCoarseVelocity) + glonass_freq = (i.glonassFrequencyIndex - 7) if constellation_id == ConstellationId.GLONASS else np.nan + measurements.append(GNSSMeasurement(constellation_id, sv_id, + recv_time.week, + recv_time.tow, + observables, + observables_std, + glonass_freq)) + return measurements + + +def read_raw_ublox(report) -> List[GNSSMeasurement]: + recv_tow = report.rcvTow # seconds + recv_week = report.gpsWeek + measurements = [] + for i in report.measurements: + # only add Gps and Glonass fixes + if i.gnssId in [ConstellationId.GPS, ConstellationId.GLONASS]: + if i.svId > 32 or i.pseudorange > 2**32: + continue + observables = {} + observables_std = {} + if i.trackingStatus.pseudorangeValid and i.sigId == 0: + observables['C1C'] = i.pseudorange + # Empirically it seems obvious ublox's std is + # actually a variation + observables_std['C1C'] = sqrt(i.pseudorangeStdev)*10 + if i.gnssId == ConstellationId.GLONASS: + glonass_freq = i.glonassFrequencyIndex - 7 + observables['D1C'] = -(constants.SPEED_OF_LIGHT / (constants.GLONASS_L1 + glonass_freq * constants.GLONASS_L1_DELTA)) * i.doppler + else: # GPS + glonass_freq = np.nan + observables['D1C'] = -(constants.SPEED_OF_LIGHT / constants.GPS_L1) * i.doppler + observables_std['D1C'] = (constants.SPEED_OF_LIGHT / constants.GPS_L1) * i.dopplerStdev + observables['S1C'] = i.cno + if i.trackingStatus.carrierPhaseValid: + observables['L1C'] = i.carrierCycles + else: + observables['L1C'] = np.nan + + measurements.append(GNSSMeasurement(ConstellationId(i.gnssId), i.svId, recv_week, recv_tow, + observables, observables_std, glonass_freq)) + return measurements + + +def read_rinex_obs(obsdata) -> List[List[GNSSMeasurement]]: + measurements: List[List[GNSSMeasurement]] = [] + obsdata_keys = list(obsdata.data.keys()) + first_sat = obsdata_keys[0] + n = len(obsdata.data[first_sat]['Epochs']) + for i in range(n): + recv_time_datetime = obsdata.data[first_sat]['Epochs'][i] + recv_time_datetime = recv_time_datetime.astype(datetime.datetime) + recv_time = GPSTime.from_datetime(recv_time_datetime) + measurements.append([]) + for sat_str in obsdata_keys: + if np.isnan(obsdata.data[sat_str]['C1'][i]): + continue + observables, observables_std = {}, {} + for obs in obsdata.data[sat_str]: + if obs == 'Epochs': + continue + rinex3_obs_key = rinex3_obs_from_rinex2_obs(obs) + observables[rinex3_obs_key] = obsdata.data[sat_str][obs][i] + observables_std[rinex3_obs_key] = 1. + + constellation_id, sv_id = get_constellation_and_sv_id(int(sat_str)) + measurements[-1].append(GNSSMeasurement(constellation_id, sv_id, + recv_time.week, recv_time.tow, + observables, observables_std)) + return measurements + + +def calc_pos_fix(measurements, x0=[0, 0, 0, 0, 0], no_weight=False, signal='C1C', min_measurements=6): + ''' + Calculates gps fix with WLS optimizer + + returns: + 0 -> list with positions + 1 -> pseudorange errs + ''' + import scipy.optimize as opt # Only use scipy here + + n = len(measurements) + if n < min_measurements: + return [] + + Fx_pos = pr_residual(measurements, signal=signal, no_weight=no_weight, no_nans=True) + opt_pos = opt.least_squares(Fx_pos, x0).x + return opt_pos, Fx_pos(opt_pos, no_weight=True) + + +def calc_vel_fix(measurements, est_pos, v0=[0, 0, 0, 0], no_weight=False, signal='D1C'): + ''' + Calculates gps velocity fix with WLS optimizer + + returns: + 0 -> list with velocities + 1 -> pseudorange_rate errs + ''' + import scipy.optimize as opt # Only use scipy here + + n = len(measurements) + if n < 6: + return [] + + Fx_vel = prr_residual(measurements, est_pos, signal=signal, no_weight=no_weight, no_nans=True) + opt_vel = opt.least_squares(Fx_vel, v0).x + return opt_vel, Fx_vel(opt_vel, no_weight=True) + + +def pr_residual(measurements: List[GNSSMeasurement], signal='C1C', no_weight=False, no_nans=False): + # solve for pos + def Fx_pos(xxx_todo_changeme, no_weight=no_weight): + (x, y, z, bc, bg) = xxx_todo_changeme + rows = [] + + for meas in measurements: + if signal in meas.observables_final and np.isfinite(meas.observables_final[signal]): + pr = meas.observables_final[signal] + sat_pos = meas.sat_pos_final + theta = 0 + elif signal in meas.observables and np.isfinite(meas.observables[signal]) and meas.processed: + pr = meas.observables[signal] + pr += meas.sat_clock_err * constants.SPEED_OF_LIGHT + sat_pos = meas.sat_pos + theta = constants.EARTH_ROTATION_RATE * (pr - bc) / constants.SPEED_OF_LIGHT + else: + if not no_nans: + rows.append(np.nan) + continue + if no_weight: + weight = 1 + else: + weight = (1 / meas.observables_std[signal]) + + val = np.sqrt( + (sat_pos[0] * np.cos(theta) + sat_pos[1] * np.sin(theta) - x) ** 2 + + (sat_pos[1] * np.cos(theta) - sat_pos[0] * np.sin(theta) - y) ** 2 + + (sat_pos[2] - z) ** 2 + ) + if meas.constellation_id == ConstellationId.GLONASS: + rows.append(weight * (val - (pr - bc - bg))) + elif meas.constellation_id == ConstellationId.GPS: + rows.append(weight * (val - (pr - bc))) + return rows + return Fx_pos + + +def prr_residual(measurements, est_pos, signal='D1C', no_weight=False, no_nans=False): + # solve for vel + def Fx_vel(vel, no_weight=no_weight): + rows = [] + for meas in measurements: + if signal not in meas.observables or not np.isfinite(meas.observables[signal]): + if not no_nans: + rows.append(np.nan) + continue + if meas.corrected: + sat_pos = meas.sat_pos_final + else: + sat_pos = meas.sat_pos + if no_weight: + weight = 1 + else: + weight = (1 / meas.observables[signal]) + los_vector = (sat_pos - est_pos[0:3] + ) / np.linalg.norm(sat_pos - est_pos[0:3]) + rows.append( + weight * ((meas.sat_vel - vel[0:3]).dot(los_vector) - + (meas.observables[signal] - vel[3]))) + return rows + return Fx_vel + + +def get_Q(recv_pos, sat_positions): + local = LocalCoord.from_ecef(recv_pos) + sat_positions_rel = local.ecef2ned(sat_positions) + sat_distances = np.linalg.norm(sat_positions_rel, axis=1) + A = np.column_stack((sat_positions_rel[:,0]/sat_distances, # pylint: disable=unsubscriptable-object + sat_positions_rel[:,1]/sat_distances, # pylint: disable=unsubscriptable-object + sat_positions_rel[:,2]/sat_distances, # pylint: disable=unsubscriptable-object + -np.ones(len(sat_distances)))) + if A.shape[0] < 4 or np.linalg.matrix_rank(A) < 4: + return np.inf*np.ones((4,4)) + Q = np.linalg.inv(A.T.dot(A)) + return Q + + +def get_DOP(recv_pos, sat_positions): + Q = get_Q(recv_pos, sat_positions) + return np.sqrt(np.trace(Q)) + + +def get_HDOP(recv_pos, sat_positions): + Q = get_Q(recv_pos, sat_positions) + return np.sqrt(np.trace(Q[:2,:2])) + + +def get_VDOP(recv_pos, sat_positions): + Q = get_Q(recv_pos, sat_positions) + return np.sqrt(Q[2,2]) + + +def get_TDOP(recv_pos, sat_positions): + Q = get_Q(recv_pos, sat_positions) + return np.sqrt(Q[3,3]) + + +def get_PDOP(recv_pos, sat_positions): + Q = get_Q(recv_pos, sat_positions) + return np.sqrt(np.trace(Q[:3,:3])) diff --git a/laika/rinex_file.py b/laika/rinex_file.py new file mode 100644 index 000000000..438506191 --- /dev/null +++ b/laika/rinex_file.py @@ -0,0 +1,252 @@ +#!/usr/bin/env python + +# Copyright (C) 2014 Swift Navigation Inc. +# +# This source is subject to the license found in the file 'LICENSE' which must +# be be distributed together with this source. All other rights reserved. +# +# THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, +# EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE. + +import datetime +import numpy as np + + +def floatornan(x): + if x == '' or x[-1] == ' ': + return np.NaN + return float(x) + + +def digitorzero(x): + if x == ' ' or x == '': + return 0 + return int(x) + + +def padline(l, n=16): + x = len(l) + x_ = n * ((x + n - 1) // n) + padded = l + ' ' * (x_ - x) + while len(padded) < 70: + padded += ' ' * 16 + return padded + + +TOTAL_SATS = 132 # Increased to support Galileo + + +class DownloadError(Exception): + pass + + +class RINEXFile: + def __init__(self, filename, rate=None): + self.rate = rate + try: + with open(filename) as f: + self._read_header(f) + self._read_data(f) + except TypeError: + print("TypeError, file likely not downloaded.") + raise DownloadError("file download failure") + except FileNotFoundError: + print("File not found in directory.") + raise DownloadError("file missing in download cache") + def _read_header(self, f): + version_line = padline(f.readline(), 80) + + self.version = float(version_line[0:9]) + if (self.version > 2.11): + raise ValueError( + f"RINEX file versions > 2.11 not supported (file version {self.version:f})") + + self.filetype = version_line[20] + if self.filetype not in "ONGM": # Check valid file type + raise ValueError(f"RINEX file type '{self.filetype}' not supported") + if self.filetype != 'O': + raise ValueError("Only 'OBSERVATION DATA' RINEX files are currently supported") + + self.gnss = version_line[40] + if self.gnss not in " GRSEM": # Check valid satellite system + raise ValueError(f"Satellite system '{self.filetype}' not supported") + if self.gnss == ' ': + self.gnss = 'G' + if self.gnss != 'G': + #raise ValueError("Only GPS data currently supported") + pass + + self.comment = "" + while True: # Read the rest of the header + line = padline(f.readline(), 80) + label = line[60:80].rstrip() + if label == "END OF HEADER": + break + if label == "COMMENT": + self.comment += line[:60] + '\n' + if label == "MARKER NAME": + self.marker_name = line[:60].rstrip() + if self.marker_name == '': + self.marker_name = 'UNKNOWN' + if label == "# / TYPES OF OBSERV": + # RINEX files can have multiple line headers + # This code handles the case + try: + n_obs = int(line[0:6]) + self.obs_types = [] + except ValueError: + pass + + if n_obs <= 9: + for i in range(0, n_obs): + self.obs_types.append(line[10 + 6 * i:12 + 6 * i]) + if n_obs > 9: + for i in range(0, 9): + self.obs_types.append(line[10 + 6 * i:12 + 6 * i]) + n_obs -= 9 + + def _read_next_non_comment(self, f): + line = f.readline() + while line and line.find('COMMENT') != -1: + line = f.readline() + return line + + def _read_epoch_header(self, f): + epoch_hdr = self._read_next_non_comment(f) + if epoch_hdr == '': + return None + # ignore any line with these three strings + skippable = ('0.0000000 4 5', 'MARKER NUMBER', ' 4 1') + while any(skip in epoch_hdr for skip in skippable): + epoch_hdr = self._read_next_non_comment(f) + + if epoch_hdr == '': + return None + + year = int(epoch_hdr[1:3]) + if year >= 80: + year += 1900 + else: + year += 2000 + month = int(epoch_hdr[4:6]) + day = int(epoch_hdr[7:9]) + hour = int(epoch_hdr[10:12]) + minute = int(epoch_hdr[13:15]) + second = int(epoch_hdr[15:18]) + microsecond = int( + epoch_hdr[19:25]) # Discard the least sig. fig. (use microseconds only). + epoch = datetime.datetime(year, month, day, hour, minute, second, microsecond) + + flag = int(epoch_hdr[28]) + allowed_flags = {0, 3, 4} + if flag not in allowed_flags: + raise ValueError("Don't know how to handle epoch flag %d in epoch header:\n%s" % + (flag, epoch_hdr)) + + n_sats = int(epoch_hdr[29:32]) + if flag > 1: # event flag: nsats is number of records + for i in range(n_sats): + f.readline() + return None + + sats = [] + for i in range(0, n_sats): + if ((i % 12) == 0) and (i > 0): + epoch_hdr = f.readline() + sats.append(epoch_hdr[(32 + (i % 12) * 3):(35 + (i % 12) * 3)]) + + return epoch, flag, sats + + def _read_obs(self, f, n_sat, sat_map): + obs = np.empty((TOTAL_SATS, len(self.obs_types)), dtype=np.float64) * np.NaN + lli = np.zeros((TOTAL_SATS, len(self.obs_types)), dtype=np.uint8) + signal_strength = np.zeros((TOTAL_SATS, len(self.obs_types)), dtype=np.uint8) + + for i in range(n_sat): + # Join together observations for a single satellite if split across lines. + obs_line = ''.join( + padline(f.readline()[:-1], 16) for _ in range((len(self.obs_types) + 4) // 5)) + for j in range(len(self.obs_types)): + obs_record = obs_line[16 * j:16 * (j + 1)] + obs[int(sat_map[i]), j] = floatornan(obs_record[0:14]) + lli[int(sat_map[i]), j] = digitorzero(obs_record[14:15]) + signal_strength[int(sat_map[i]), j] = digitorzero(obs_record[15:16]) + + return obs, lli, signal_strength + + def _skip_obs(self, f, n_sat): + for i in range(n_sat): + for _ in range((len(self.obs_types) + 4) // 5): + f.readline() + + def _read_data_chunk(self, f, CHUNK_SIZE=10000): + obss = np.empty( + (CHUNK_SIZE, TOTAL_SATS, len(self.obs_types)), dtype=np.float64) * np.NaN + llis = np.zeros((CHUNK_SIZE, TOTAL_SATS, len(self.obs_types)), dtype=np.uint8) + signal_strengths = np.zeros( + (CHUNK_SIZE, TOTAL_SATS, len(self.obs_types)), dtype=np.uint8) + epochs = np.zeros(CHUNK_SIZE, dtype='datetime64[us]') + flags = np.zeros(CHUNK_SIZE, dtype=np.uint8) + + i = 0 + while True: + hdr = self._read_epoch_header(f) + if hdr is None: + break + # data faster than desired rate: ignore it + if self.rate and (hdr[0].microsecond or hdr[0].second % self.rate != 0): + self._skip_obs(f, len(hdr[2])) + continue + epoch, flags[i], sats = hdr + epochs[i] = np.datetime64(epoch) + sat_map = np.ones(len(sats)) * -1 + for n, sat in enumerate(sats): + if sat[0] == 'G': + sat_map[n] = int(sat[1:]) - 1 + if sat[0] == 'R': + sat_map[n] = int(sat[1:]) - 1 + 64 + obss[i], llis[i], signal_strengths[i] = self._read_obs(f, len(sats), sat_map) + i += 1 + if i >= CHUNK_SIZE: + break + + return obss[:i], llis[:i], signal_strengths[:i], epochs[:i], flags[:i] + + def _read_data(self, f): + self.data = {} + while True: + obss, llis, signal_strengths, epochs, flags = self._read_data_chunk(f) + if obss.shape[0] == 0: + break + + for i, sv in enumerate(['%02d' % d for d in range(1, TOTAL_SATS+1)]): + if sv not in self.data: + self.data[sv] = {} + for j, obs_type in enumerate(self.obs_types): + if obs_type in self.data[sv]: + self.data[sv][obs_type] = np.append(self.data[sv][obs_type], obss[:, i, j]) + else: + self.data[sv][obs_type] = obss[:, i, j] + if 'Epochs' in self.data[sv]: + self.data[sv]['Epochs'] = np.append(self.data[sv]['Epochs'], epochs) + else: + self.data[sv]['Epochs'] = epochs + for sat in list(self.data.keys()): + if np.all(np.isnan(self.data[sat]['C1'])): + del self.data[sat] + + + + + + + + + + + + + + + diff --git a/laika/trop.py b/laika/trop.py new file mode 100644 index 000000000..8315e91bb --- /dev/null +++ b/laika/trop.py @@ -0,0 +1,34 @@ +#!/usr/bin/python + +from numpy import cos, exp, pi +from .lib.coordinates import ecef2geodetic + + +def saast(pos, el, humi=0.75, temp0=15.0): + """ + Function from RTKlib: https://github.com/tomojitakasu/RTKLIB/blob/master/src/rtkcmn.c#L3362-3362 + with no changes + :param time: time + :param pos: receiver position {ecef} m) + :param el: azimuth/elevation angle {az,el} (rad) -- we do not use az + :param humi: relative humidity + :param temp0: temperature (Celsius) + :return: tropospheric delay (m) + """ + pos_rad = ecef2geodetic(pos, radians=True) + if pos_rad[2] < -100.0 or 1E4 < pos_rad[2] or el <= 0: + return 0.0 + + # /* standard atmosphere */ + hgt = 0.0 if pos_rad[2] < 0.0 else pos_rad[2] + + pres = 1013.25 * pow(1.0 - 2.2557E-5 * hgt, 5.2568) + temp = temp0 - 6.5E-3 * hgt + 273.16 + e = 6.108 * humi * exp((17.15 * temp - 4684.0) / (temp - 38.45)) + + # /* saastamoninen model */ + z = pi / 2.0 - el + trph = 0.0022768 * pres / ( + 1.0 - 0.00266 * cos(2.0 * pos_rad[0]) - 0.00028 * hgt / 1E3) / cos(z) + trpw = 0.002277 * (1255.0 / temp + 0.05) * e / cos(z) + return trph + trpw diff --git a/launch_chffrplus.sh b/launch_chffrplus.sh index 503ebb5d4..911774a4e 100755 --- a/launch_chffrplus.sh +++ b/launch_chffrplus.sh @@ -8,7 +8,7 @@ source "$BASEDIR/launch_env.sh" DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )" -function tici_init { +function agnos_init { # wait longer for weston to come up if [ -f "$BASEDIR/prebuilt" ]; then sleep 3 @@ -22,12 +22,12 @@ function tici_init { # Check if AGNOS update is required if [ $(< /VERSION) != "$AGNOS_VERSION" ]; then - AGNOS_PY="$DIR/selfdrive/hardware/tici/agnos.py" - MANIFEST="$DIR/selfdrive/hardware/tici/agnos.json" + AGNOS_PY="$DIR/system/hardware/tici/agnos.py" + MANIFEST="$DIR/system/hardware/tici/agnos.json" if $AGNOS_PY --verify $MANIFEST; then sudo reboot fi - $DIR/selfdrive/hardware/tici/updater $AGNOS_PY $MANIFEST + $DIR/system/hardware/tici/updater $AGNOS_PY $MANIFEST fi } @@ -77,9 +77,7 @@ function launch { export PYTHONPATH="$PWD:$PWD/pyextra" # hardware specific init - if [ -f /TICI ]; then - tici_init - fi + agnos_init # write tmux scrollback to a file tmux capture-pane -pq -S-1000 > /tmp/launch_log diff --git a/launch_env.sh b/launch_env.sh index d4b57c909..ac84d6dcb 100755 --- a/launch_env.sh +++ b/launch_env.sh @@ -7,7 +7,7 @@ export OPENBLAS_NUM_THREADS=1 export VECLIB_MAXIMUM_THREADS=1 if [ -z "$AGNOS_VERSION" ]; then - export AGNOS_VERSION="4" + export AGNOS_VERSION="5.2" fi if [ -z "$PASSIVE" ]; then diff --git a/opendbc/acura_ilx_2016_can_generated.dbc b/opendbc/acura_ilx_2016_can_generated.dbc index 12d42511d..854c36ee0 100644 --- a/opendbc/acura_ilx_2016_can_generated.dbc +++ b/opendbc/acura_ilx_2016_can_generated.dbc @@ -94,6 +94,43 @@ BO_ 773 SEATBELT_STATUS: 7 BDY SG_ COUNTER : 53|2@0+ (1,0) [0|3] "" EON SG_ CHECKSUM : 51|4@0+ (1,0) [0|3] "" EON +BO_ 777 CAR_SPEED: 8 PCM + SG_ ROUGH_CAR_SPEED : 23|8@0+ (1,0) [0|255] "mph" XXX + SG_ CAR_SPEED : 7|16@0+ (0.01,0) [0|65535] "kph" XXX + SG_ ROUGH_CAR_SPEED_3 : 39|16@0+ (0.01,0) [0|65535] "kph" XXX + SG_ ROUGH_CAR_SPEED_2 : 31|8@0+ (1,0) [0|255] "mph" XXX + SG_ LOCK_STATUS : 55|2@0+ (1,0) [0|255] "" XXX + SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" XXX + SG_ CHECKSUM : 59|4@0+ (1,0) [0|15] "" XXX + SG_ IMPERIAL_UNIT : 63|1@0+ (1,0) [0|1] "" XXX + +BO_ 780 ACC_HUD: 8 ADAS + SG_ PCM_SPEED : 7|16@0+ (0.01,0) [0|250] "kph" BDY + SG_ PCM_GAS : 23|8@0+ (1,0) [0|127] "" BDY + SG_ CRUISE_SPEED : 31|8@0+ (1,0) [0|255] "kph" BDY + SG_ DTC_MODE : 39|1@0+ (1,0) [0|1] "" BDY + SG_ BOH : 38|1@0+ (1,0) [0|1] "" BDY + SG_ ACC_PROBLEM : 37|1@0+ (1,0) [0|1] "" BDY + SG_ FCM_OFF : 35|1@0+ (1,0) [0|1] "" BDY + SG_ FCM_OFF_2 : 36|1@0+ (1,0) [0|1] "" BDY + SG_ FCM_PROBLEM : 34|1@0+ (1,0) [0|1] "" BDY + SG_ RADAR_OBSTRUCTED : 33|1@0+ (1,0) [0|1] "" BDY + SG_ ENABLE_MINI_CAR : 32|1@0+ (1,0) [0|1] "" BDY + SG_ HUD_DISTANCE : 47|2@0+ (1,0) [0|3] "" BDY + SG_ HUD_LEAD : 45|2@0+ (1,0) [0|3] "" BDY + SG_ BOH_3 : 43|1@0+ (1,0) [0|3] "" BDY + SG_ BOH_4 : 42|1@0+ (1,0) [0|3] "" BDY + SG_ BOH_5 : 41|1@0+ (1,0) [0|3] "" BDY + SG_ CRUISE_CONTROL_LABEL : 40|1@0+ (1,0) [0|3] "" BDY + SG_ SET_ME_X01_2 : 55|1@0+ (1,0) [0|1] "" BDY + SG_ IMPERIAL_UNIT : 54|1@0+ (1,0) [0|1] "" BDY + SG_ ACC_ON : 52|1@0+ (1,0) [0|1] "" BDY + SG_ CHIME : 51|3@0+ (1,0) [0|1] "" BDY + SG_ SET_ME_X01 : 48|1@0+ (1,0) [0|1] "" BDY + SG_ ICONS : 63|2@0+ (1,0) [0|1] "" BDY + SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" BDY + SG_ CHECKSUM : 59|4@0+ (1,0) [0|3] "" BDY + BO_ 804 CRUISE: 8 PCM SG_ HUD_SPEED_KPH : 7|8@0+ (1,0) [0|255] "kph" EON SG_ HUD_SPEED_MPH : 15|8@0+ (1,0) [0|255] "mph" EON @@ -104,27 +141,6 @@ BO_ 804 CRUISE: 8 PCM SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" EON SG_ CHECKSUM : 59|4@0+ (1,0) [0|15] "" EON -BO_ 829 LKAS_HUD: 5 ADAS - SG_ CAM_TEMP_HIGH : 7|1@0+ (1,0) [0|255] "" BDY - SG_ SET_ME_X41 : 6|7@0+ (1,0) [0|127] "" BDY - SG_ BOH : 6|7@0+ (1,0) [0|127] "" BDY - SG_ DASHED_LANES : 14|1@0+ (1,0) [0|1] "" BDY - SG_ DTC : 13|1@0+ (1,0) [0|1] "" BDY - SG_ LKAS_PROBLEM : 12|1@0+ (1,0) [0|1] "" BDY - SG_ LKAS_OFF : 11|1@0+ (1,0) [0|1] "" BDY - SG_ SOLID_LANES : 10|1@0+ (1,0) [0|1] "" BDY - SG_ LDW_RIGHT : 9|1@0+ (1,0) [0|1] "" BDY - SG_ STEERING_REQUIRED : 8|1@0+ (1,0) [0|1] "" BDY - SG_ BOH : 23|2@0+ (1,0) [0|4] "" BDY - SG_ LDW_PROBLEM : 21|1@0+ (1,0) [0|1] "" BDY - SG_ BEEP : 17|2@0+ (1,0) [0|1] "" BDY - SG_ LDW_ON : 28|1@0+ (1,0) [0|1] "" BDY - SG_ LDW_OFF : 27|1@0+ (1,0) [0|1] "" BDY - SG_ CLEAN_WINDSHIELD : 26|1@0+ (1,0) [0|1] "" BDY - SG_ SET_ME_X48 : 31|8@0+ (1,0) [0|255] "" BDY - SG_ COUNTER : 37|2@0+ (1,0) [0|3] "" BDY - SG_ CHECKSUM : 35|4@0+ (1,0) [0|15] "" BDY - BO_ 884 STALK_STATUS: 8 XXX SG_ DASHBOARD_ALERT : 39|8@0+ (1,0) [0|255] "" EON SG_ AUTO_HEADLIGHTS : 46|1@0+ (1,0) [0|1] "" EON @@ -158,15 +174,18 @@ CM_ SG_ 420 BRAKE_HOLD_RELATED "On when Brake Hold engaged"; CM_ SG_ 490 LONG_ACCEL "wheel speed derivative, noisy and zero snapping"; CM_ SG_ 773 PASS_AIRBAG_ON "Might just be indicator light"; CM_ SG_ 773 PASS_AIRBAG_OFF "Might just be indicator light"; +CM_ SG_ 780 CRUISE_SPEED "255 = no speed"; +CM_ SG_ 780 PCM_SPEED "Used by Nidec"; +CM_ SG_ 780 PCM_GAS "Used by Nidec"; CM_ SG_ 804 CRUISE_SPEED_PCM "255 = no speed"; -CM_ SG_ 829 BEEP "beeps are pleasant, chimes are for warnngs etc..."; -VAL_ 829 BEEP 3 "single_beep" 2 "triple_beep" 1 "repeated_beep" 0 "no_beep"; +VAL_ 780 CRUISE_SPEED 255 "no_speed" 252 "stopped"; +VAL_ 780 HUD_LEAD 3 "acc_off" 2 "solid_car" 1 "dashed_car" 0 "no_car"; VAL_ 884 DASHBOARD_ALERT 0 "none" 51 "acc_problem" 55 "cmbs_problem" 75 "key_not_detected" 79 "fasten_seatbelt" 111 "lkas_problem" 131 "brake_system_problem" 132 "brake_hold_problem" 139 "tbd" 161 "door_open"; VAL_ 891 WIPERS 4 "High" 2 "Low" 0 "Off"; -CM_ "Imported file _nidec_2017.dbc starts here"; +CM_ "Imported file _nidec_common.dbc starts here"; BO_ 145 KINEMATICS: 8 XXX SG_ LAT_ACCEL : 7|10@0+ (0.02,-512) [-20|20] "m/s2" EON SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" EON @@ -194,11 +213,11 @@ BO_ 487 BRAKE_PRESSURE: 4 VSA BO_ 506 BRAKE_COMMAND: 8 ADAS SG_ COMPUTER_BRAKE : 7|10@0+ (1,0) [0|1] "" EBCM - SG_ SET_ME_X00 : 13|5@0+ (1,0) [0|1] "" EBCM SG_ BRAKE_PUMP_REQUEST : 8|1@0+ (1,0) [0|1] "" EBCM - SG_ SET_ME_X00_2 : 23|3@0+ (1,0) [0|1] "" EBCM + SG_ BRAKE_PUMP_REQUEST_ALT : 11|1@0+ (1,0) [0|1] "" EBCM + SG_ SET_ME_X00 : 23|3@0+ (1,0) [0|1] "" EBCM SG_ CRUISE_OVERRIDE : 20|1@0+ (1,0) [0|1] "" EBCM - SG_ SET_ME_X00_3 : 19|1@0+ (1,0) [0|1] "" EBCM + SG_ SET_ME_X00_2 : 19|1@0+ (1,0) [0|1] "" EBCM SG_ CRUISE_FAULT_CMD : 18|1@0+ (1,0) [0|1] "" EBCM SG_ CRUISE_CANCEL_CMD : 17|1@0+ (1,0) [0|1] "" EBCM SG_ COMPUTER_BRAKE_REQUEST : 16|1@0+ (1,0) [0|1] "" EBCM @@ -208,10 +227,10 @@ BO_ 506 BRAKE_COMMAND: 8 ADAS SG_ BRAKE_LIGHTS : 39|1@0+ (1,0) [0|1] "" EBCM SG_ CRUISE_STATES : 38|7@0+ (1,0) [0|1] "" EBCM SG_ CHIME : 47|3@0+ (1,0) [0|7] "" EBCM - SG_ SET_ME_X00_4 : 44|1@0+ (1,0) [0|1] "" EBCM + SG_ SET_ME_X00_3 : 44|1@0+ (1,0) [0|1] "" EBCM SG_ FCW : 43|2@0+ (1,0) [0|3] "" EBCM SG_ AEB_STATUS : 41|2@0+ (1,0) [0|3] "" XXX - SG_ SET_ME_X00_5 : 55|8@0+ (1,0) [0|0] "" EBCM + SG_ COMPUTER_BRAKE_ALT : 55|10@0+ (1,0) [0|0] "" EBCM SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" EBCM SG_ CHECKSUM : 59|4@0+ (1,0) [0|15] "" EBCM @@ -225,38 +244,26 @@ BO_ 597 ROUGH_WHEEL_SPEED: 8 VSA SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" EON SG_ CHECKSUM : 59|4@0+ (1,0) [0|15] "" EON -BO_ 777 LOCK_STATUS: 8 XXX - SG_ DOORS_UNLOCKED : 54|1@0+ (1,0) [0|1] "" EON - SG_ DOORS_LOCKED : 55|1@0+ (1,0) [0|1] "" EON - SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" EON - SG_ CHECKSUM : 59|4@0+ (1,0) [0|3] "" EON - -BO_ 780 ACC_HUD: 8 ADAS - SG_ PCM_SPEED : 7|16@0+ (0.01,0) [0|250] "kph" BDY - SG_ PCM_GAS : 23|8@0+ (1,0) [0|127] "" BDY - SG_ CRUISE_SPEED : 31|8@0+ (1,0) [0|255] "" BDY - SG_ DTC_MODE : 39|1@0+ (1,0) [0|1] "" BDY - SG_ BOH : 38|1@0+ (1,0) [0|1] "" BDY - SG_ ACC_PROBLEM : 37|1@0+ (1,0) [0|1] "" BDY - SG_ FCM_OFF : 36|1@0+ (1,0) [0|1] "" BDY - SG_ FCM_OFF_2 : 35|1@0+ (1,0) [0|1] "" BDY - SG_ FCM_PROBLEM : 34|1@0+ (1,0) [0|1] "" BDY - SG_ RADAR_OBSTRUCTED : 33|1@0+ (1,0) [0|1] "" BDY - SG_ ENABLE_MINI_CAR : 32|1@0+ (1,0) [0|1] "" BDY - SG_ HUD_DISTANCE : 47|2@0+ (1,0) [0|3] "" BDY - SG_ HUD_LEAD : 45|2@0+ (1,0) [0|3] "" BDY - SG_ BOH_3 : 43|1@0+ (1,0) [0|3] "" BDY - SG_ BOH_4 : 42|1@0+ (1,0) [0|3] "" BDY - SG_ BOH_5 : 41|1@0+ (1,0) [0|3] "" BDY - SG_ CRUISE_CONTROL_LABEL : 40|1@0+ (1,0) [0|3] "" BDY - SG_ SET_ME_X01_2 : 55|1@0+ (1,0) [0|1] "" BDY - SG_ IMPERIAL_UNIT : 54|1@0+ (1,0) [0|1] "" BDY - SG_ HUD_DISTANCE_3 : 52|1@0+ (1,0) [0|1] "" BDY - SG_ CHIME : 51|3@0+ (1,0) [0|1] "" BDY - SG_ SET_ME_X01 : 48|1@0+ (1,0) [0|1] "" BDY - SG_ ICONS : 63|2@0+ (1,0) [0|1] "" BDY - SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" BDY - SG_ CHECKSUM : 59|4@0+ (1,0) [0|3] "" BDY +BO_ 829 LKAS_HUD: 5 ADAS + SG_ CAM_TEMP_HIGH : 7|1@0+ (1,0) [0|255] "" BDY + SG_ SET_ME_X41 : 6|7@0+ (1,0) [0|127] "" BDY + SG_ BOH : 6|7@0+ (1,0) [0|127] "" BDY + SG_ DASHED_LANES : 14|1@0+ (1,0) [0|1] "" BDY + SG_ DTC : 13|1@0+ (1,0) [0|1] "" BDY + SG_ LKAS_PROBLEM : 12|1@0+ (1,0) [0|1] "" BDY + SG_ LKAS_OFF : 11|1@0+ (1,0) [0|1] "" BDY + SG_ SOLID_LANES : 10|1@0+ (1,0) [0|1] "" BDY + SG_ LDW_RIGHT : 9|1@0+ (1,0) [0|1] "" BDY + SG_ STEERING_REQUIRED : 8|1@0+ (1,0) [0|1] "" BDY + SG_ BOH : 23|2@0+ (1,0) [0|4] "" BDY + SG_ LDW_PROBLEM : 21|1@0+ (1,0) [0|1] "" BDY + SG_ BEEP : 17|2@0+ (1,0) [0|1] "" BDY + SG_ LDW_ON : 28|1@0+ (1,0) [0|1] "" BDY + SG_ LDW_OFF : 27|1@0+ (1,0) [0|1] "" BDY + SG_ CLEAN_WINDSHIELD : 26|1@0+ (1,0) [0|1] "" BDY + SG_ SET_ME_X48 : 31|8@0+ (1,0) [0|255] "" BDY + SG_ COUNTER : 37|2@0+ (1,0) [0|3] "" BDY + SG_ CHECKSUM : 35|4@0+ (1,0) [0|15] "" BDY BO_ 892 CRUISE_PARAMS: 8 PCM SG_ CRUISE_SPEED_OFFSET : 31|8@0- (0.1,0) [-128|127] "kph" EON @@ -264,13 +271,22 @@ BO_ 892 CRUISE_PARAMS: 8 PCM SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" EON CM_ SG_ 506 AEB_REQ_1 "set for duration of suspected AEB event"; -CM_ SG_ 780 CRUISE_SPEED "255 = no speed"; +CM_ SG_ 506 COMPUTER_BRAKE_ALT "Used by dual-can Nidec"; +CM_ SG_ 506 BRAKE_PUMP_REQUEST_ALT "Used by dual-can Nidec"; +CM_ SG_ 829 BEEP "beeps are pleasant, chimes are for warnngs etc..."; VAL_ 506 FCW 3 "fcw" 2 "fcw" 1 "fcw" 0 "no_fcw"; VAL_ 506 CHIME 4 "double_chime" 3 "single_chime" 2 "continuous_chime" 1 "repeating_chime" 0 "no_chime"; VAL_ 506 AEB_STATUS 3 "aeb_prepare" 2 "aeb_ready" 1 "aeb_braking" 0 "no_aeb"; -VAL_ 780 CRUISE_SPEED 255 "no_speed" 252 "stopped"; -VAL_ 780 HUD_LEAD 3 "acc_off" 2 "solid_car" 1 "dashed_car" 0 "no_car"; +VAL_ 829 BEEP 3 "single_beep" 2 "triple_beep" 1 "repeated_beep" 0 "no_beep"; + + +CM_ "Imported file _steering_sensors_b.dbc starts here"; +BO_ 342 STEERING_SENSORS: 6 EPS + SG_ STEER_ANGLE : 7|16@0- (-0.1,0) [-500|500] "deg" EON + SG_ STEER_ANGLE_RATE : 23|16@0- (-1,0) [-3000|3000] "deg/s" EON + SG_ COUNTER : 45|2@0+ (1,0) [0|3] "" EON + SG_ CHECKSUM : 43|4@0+ (1,0) [0|15] "" EON CM_ "acura_ilx_2016_can.dbc starts here"; @@ -281,12 +297,6 @@ BO_ 228 STEERING_CONTROL: 5 ADAS SG_ COUNTER : 37|2@0+ (1,0) [0|3] "" EPS SG_ CHECKSUM : 35|4@0+ (1,0) [0|3] "" EPS -BO_ 342 STEERING_SENSORS: 6 EPS - SG_ STEER_ANGLE : 7|16@0- (-0.1,0) [-500|500] "deg" EON - SG_ STEER_ANGLE_RATE : 23|16@0- (-1,0) [-3000|3000] "deg/s" EON - SG_ COUNTER : 45|2@0+ (1,0) [0|3] "" EON - SG_ CHECKSUM : 43|4@0+ (1,0) [0|3] "" EON - BO_ 399 STEER_STATUS: 7 EPS SG_ STEER_TORQUE_SENSOR : 7|16@0- (-1,0) [-31000|31000] "tbd" EON SG_ STEER_ANGLE_RATE : 23|16@0- (-0.1,0) [-31000|31000] "deg/s" EON diff --git a/opendbc/acura_rdx_2018_can_generated.dbc b/opendbc/acura_rdx_2018_can_generated.dbc index de3ecbfc3..c9a8dd6d7 100644 --- a/opendbc/acura_rdx_2018_can_generated.dbc +++ b/opendbc/acura_rdx_2018_can_generated.dbc @@ -94,6 +94,43 @@ BO_ 773 SEATBELT_STATUS: 7 BDY SG_ COUNTER : 53|2@0+ (1,0) [0|3] "" EON SG_ CHECKSUM : 51|4@0+ (1,0) [0|3] "" EON +BO_ 777 CAR_SPEED: 8 PCM + SG_ ROUGH_CAR_SPEED : 23|8@0+ (1,0) [0|255] "mph" XXX + SG_ CAR_SPEED : 7|16@0+ (0.01,0) [0|65535] "kph" XXX + SG_ ROUGH_CAR_SPEED_3 : 39|16@0+ (0.01,0) [0|65535] "kph" XXX + SG_ ROUGH_CAR_SPEED_2 : 31|8@0+ (1,0) [0|255] "mph" XXX + SG_ LOCK_STATUS : 55|2@0+ (1,0) [0|255] "" XXX + SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" XXX + SG_ CHECKSUM : 59|4@0+ (1,0) [0|15] "" XXX + SG_ IMPERIAL_UNIT : 63|1@0+ (1,0) [0|1] "" XXX + +BO_ 780 ACC_HUD: 8 ADAS + SG_ PCM_SPEED : 7|16@0+ (0.01,0) [0|250] "kph" BDY + SG_ PCM_GAS : 23|8@0+ (1,0) [0|127] "" BDY + SG_ CRUISE_SPEED : 31|8@0+ (1,0) [0|255] "kph" BDY + SG_ DTC_MODE : 39|1@0+ (1,0) [0|1] "" BDY + SG_ BOH : 38|1@0+ (1,0) [0|1] "" BDY + SG_ ACC_PROBLEM : 37|1@0+ (1,0) [0|1] "" BDY + SG_ FCM_OFF : 35|1@0+ (1,0) [0|1] "" BDY + SG_ FCM_OFF_2 : 36|1@0+ (1,0) [0|1] "" BDY + SG_ FCM_PROBLEM : 34|1@0+ (1,0) [0|1] "" BDY + SG_ RADAR_OBSTRUCTED : 33|1@0+ (1,0) [0|1] "" BDY + SG_ ENABLE_MINI_CAR : 32|1@0+ (1,0) [0|1] "" BDY + SG_ HUD_DISTANCE : 47|2@0+ (1,0) [0|3] "" BDY + SG_ HUD_LEAD : 45|2@0+ (1,0) [0|3] "" BDY + SG_ BOH_3 : 43|1@0+ (1,0) [0|3] "" BDY + SG_ BOH_4 : 42|1@0+ (1,0) [0|3] "" BDY + SG_ BOH_5 : 41|1@0+ (1,0) [0|3] "" BDY + SG_ CRUISE_CONTROL_LABEL : 40|1@0+ (1,0) [0|3] "" BDY + SG_ SET_ME_X01_2 : 55|1@0+ (1,0) [0|1] "" BDY + SG_ IMPERIAL_UNIT : 54|1@0+ (1,0) [0|1] "" BDY + SG_ ACC_ON : 52|1@0+ (1,0) [0|1] "" BDY + SG_ CHIME : 51|3@0+ (1,0) [0|1] "" BDY + SG_ SET_ME_X01 : 48|1@0+ (1,0) [0|1] "" BDY + SG_ ICONS : 63|2@0+ (1,0) [0|1] "" BDY + SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" BDY + SG_ CHECKSUM : 59|4@0+ (1,0) [0|3] "" BDY + BO_ 804 CRUISE: 8 PCM SG_ HUD_SPEED_KPH : 7|8@0+ (1,0) [0|255] "kph" EON SG_ HUD_SPEED_MPH : 15|8@0+ (1,0) [0|255] "mph" EON @@ -104,27 +141,6 @@ BO_ 804 CRUISE: 8 PCM SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" EON SG_ CHECKSUM : 59|4@0+ (1,0) [0|15] "" EON -BO_ 829 LKAS_HUD: 5 ADAS - SG_ CAM_TEMP_HIGH : 7|1@0+ (1,0) [0|255] "" BDY - SG_ SET_ME_X41 : 6|7@0+ (1,0) [0|127] "" BDY - SG_ BOH : 6|7@0+ (1,0) [0|127] "" BDY - SG_ DASHED_LANES : 14|1@0+ (1,0) [0|1] "" BDY - SG_ DTC : 13|1@0+ (1,0) [0|1] "" BDY - SG_ LKAS_PROBLEM : 12|1@0+ (1,0) [0|1] "" BDY - SG_ LKAS_OFF : 11|1@0+ (1,0) [0|1] "" BDY - SG_ SOLID_LANES : 10|1@0+ (1,0) [0|1] "" BDY - SG_ LDW_RIGHT : 9|1@0+ (1,0) [0|1] "" BDY - SG_ STEERING_REQUIRED : 8|1@0+ (1,0) [0|1] "" BDY - SG_ BOH : 23|2@0+ (1,0) [0|4] "" BDY - SG_ LDW_PROBLEM : 21|1@0+ (1,0) [0|1] "" BDY - SG_ BEEP : 17|2@0+ (1,0) [0|1] "" BDY - SG_ LDW_ON : 28|1@0+ (1,0) [0|1] "" BDY - SG_ LDW_OFF : 27|1@0+ (1,0) [0|1] "" BDY - SG_ CLEAN_WINDSHIELD : 26|1@0+ (1,0) [0|1] "" BDY - SG_ SET_ME_X48 : 31|8@0+ (1,0) [0|255] "" BDY - SG_ COUNTER : 37|2@0+ (1,0) [0|3] "" BDY - SG_ CHECKSUM : 35|4@0+ (1,0) [0|15] "" BDY - BO_ 884 STALK_STATUS: 8 XXX SG_ DASHBOARD_ALERT : 39|8@0+ (1,0) [0|255] "" EON SG_ AUTO_HEADLIGHTS : 46|1@0+ (1,0) [0|1] "" EON @@ -158,15 +174,18 @@ CM_ SG_ 420 BRAKE_HOLD_RELATED "On when Brake Hold engaged"; CM_ SG_ 490 LONG_ACCEL "wheel speed derivative, noisy and zero snapping"; CM_ SG_ 773 PASS_AIRBAG_ON "Might just be indicator light"; CM_ SG_ 773 PASS_AIRBAG_OFF "Might just be indicator light"; +CM_ SG_ 780 CRUISE_SPEED "255 = no speed"; +CM_ SG_ 780 PCM_SPEED "Used by Nidec"; +CM_ SG_ 780 PCM_GAS "Used by Nidec"; CM_ SG_ 804 CRUISE_SPEED_PCM "255 = no speed"; -CM_ SG_ 829 BEEP "beeps are pleasant, chimes are for warnngs etc..."; -VAL_ 829 BEEP 3 "single_beep" 2 "triple_beep" 1 "repeated_beep" 0 "no_beep"; +VAL_ 780 CRUISE_SPEED 255 "no_speed" 252 "stopped"; +VAL_ 780 HUD_LEAD 3 "acc_off" 2 "solid_car" 1 "dashed_car" 0 "no_car"; VAL_ 884 DASHBOARD_ALERT 0 "none" 51 "acc_problem" 55 "cmbs_problem" 75 "key_not_detected" 79 "fasten_seatbelt" 111 "lkas_problem" 131 "brake_system_problem" 132 "brake_hold_problem" 139 "tbd" 161 "door_open"; VAL_ 891 WIPERS 4 "High" 2 "Low" 0 "Off"; -CM_ "Imported file _nidec_2017.dbc starts here"; +CM_ "Imported file _nidec_common.dbc starts here"; BO_ 145 KINEMATICS: 8 XXX SG_ LAT_ACCEL : 7|10@0+ (0.02,-512) [-20|20] "m/s2" EON SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" EON @@ -194,11 +213,11 @@ BO_ 487 BRAKE_PRESSURE: 4 VSA BO_ 506 BRAKE_COMMAND: 8 ADAS SG_ COMPUTER_BRAKE : 7|10@0+ (1,0) [0|1] "" EBCM - SG_ SET_ME_X00 : 13|5@0+ (1,0) [0|1] "" EBCM SG_ BRAKE_PUMP_REQUEST : 8|1@0+ (1,0) [0|1] "" EBCM - SG_ SET_ME_X00_2 : 23|3@0+ (1,0) [0|1] "" EBCM + SG_ BRAKE_PUMP_REQUEST_ALT : 11|1@0+ (1,0) [0|1] "" EBCM + SG_ SET_ME_X00 : 23|3@0+ (1,0) [0|1] "" EBCM SG_ CRUISE_OVERRIDE : 20|1@0+ (1,0) [0|1] "" EBCM - SG_ SET_ME_X00_3 : 19|1@0+ (1,0) [0|1] "" EBCM + SG_ SET_ME_X00_2 : 19|1@0+ (1,0) [0|1] "" EBCM SG_ CRUISE_FAULT_CMD : 18|1@0+ (1,0) [0|1] "" EBCM SG_ CRUISE_CANCEL_CMD : 17|1@0+ (1,0) [0|1] "" EBCM SG_ COMPUTER_BRAKE_REQUEST : 16|1@0+ (1,0) [0|1] "" EBCM @@ -208,10 +227,10 @@ BO_ 506 BRAKE_COMMAND: 8 ADAS SG_ BRAKE_LIGHTS : 39|1@0+ (1,0) [0|1] "" EBCM SG_ CRUISE_STATES : 38|7@0+ (1,0) [0|1] "" EBCM SG_ CHIME : 47|3@0+ (1,0) [0|7] "" EBCM - SG_ SET_ME_X00_4 : 44|1@0+ (1,0) [0|1] "" EBCM + SG_ SET_ME_X00_3 : 44|1@0+ (1,0) [0|1] "" EBCM SG_ FCW : 43|2@0+ (1,0) [0|3] "" EBCM SG_ AEB_STATUS : 41|2@0+ (1,0) [0|3] "" XXX - SG_ SET_ME_X00_5 : 55|8@0+ (1,0) [0|0] "" EBCM + SG_ COMPUTER_BRAKE_ALT : 55|10@0+ (1,0) [0|0] "" EBCM SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" EBCM SG_ CHECKSUM : 59|4@0+ (1,0) [0|15] "" EBCM @@ -225,38 +244,26 @@ BO_ 597 ROUGH_WHEEL_SPEED: 8 VSA SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" EON SG_ CHECKSUM : 59|4@0+ (1,0) [0|15] "" EON -BO_ 777 LOCK_STATUS: 8 XXX - SG_ DOORS_UNLOCKED : 54|1@0+ (1,0) [0|1] "" EON - SG_ DOORS_LOCKED : 55|1@0+ (1,0) [0|1] "" EON - SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" EON - SG_ CHECKSUM : 59|4@0+ (1,0) [0|3] "" EON - -BO_ 780 ACC_HUD: 8 ADAS - SG_ PCM_SPEED : 7|16@0+ (0.01,0) [0|250] "kph" BDY - SG_ PCM_GAS : 23|8@0+ (1,0) [0|127] "" BDY - SG_ CRUISE_SPEED : 31|8@0+ (1,0) [0|255] "" BDY - SG_ DTC_MODE : 39|1@0+ (1,0) [0|1] "" BDY - SG_ BOH : 38|1@0+ (1,0) [0|1] "" BDY - SG_ ACC_PROBLEM : 37|1@0+ (1,0) [0|1] "" BDY - SG_ FCM_OFF : 36|1@0+ (1,0) [0|1] "" BDY - SG_ FCM_OFF_2 : 35|1@0+ (1,0) [0|1] "" BDY - SG_ FCM_PROBLEM : 34|1@0+ (1,0) [0|1] "" BDY - SG_ RADAR_OBSTRUCTED : 33|1@0+ (1,0) [0|1] "" BDY - SG_ ENABLE_MINI_CAR : 32|1@0+ (1,0) [0|1] "" BDY - SG_ HUD_DISTANCE : 47|2@0+ (1,0) [0|3] "" BDY - SG_ HUD_LEAD : 45|2@0+ (1,0) [0|3] "" BDY - SG_ BOH_3 : 43|1@0+ (1,0) [0|3] "" BDY - SG_ BOH_4 : 42|1@0+ (1,0) [0|3] "" BDY - SG_ BOH_5 : 41|1@0+ (1,0) [0|3] "" BDY - SG_ CRUISE_CONTROL_LABEL : 40|1@0+ (1,0) [0|3] "" BDY - SG_ SET_ME_X01_2 : 55|1@0+ (1,0) [0|1] "" BDY - SG_ IMPERIAL_UNIT : 54|1@0+ (1,0) [0|1] "" BDY - SG_ HUD_DISTANCE_3 : 52|1@0+ (1,0) [0|1] "" BDY - SG_ CHIME : 51|3@0+ (1,0) [0|1] "" BDY - SG_ SET_ME_X01 : 48|1@0+ (1,0) [0|1] "" BDY - SG_ ICONS : 63|2@0+ (1,0) [0|1] "" BDY - SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" BDY - SG_ CHECKSUM : 59|4@0+ (1,0) [0|3] "" BDY +BO_ 829 LKAS_HUD: 5 ADAS + SG_ CAM_TEMP_HIGH : 7|1@0+ (1,0) [0|255] "" BDY + SG_ SET_ME_X41 : 6|7@0+ (1,0) [0|127] "" BDY + SG_ BOH : 6|7@0+ (1,0) [0|127] "" BDY + SG_ DASHED_LANES : 14|1@0+ (1,0) [0|1] "" BDY + SG_ DTC : 13|1@0+ (1,0) [0|1] "" BDY + SG_ LKAS_PROBLEM : 12|1@0+ (1,0) [0|1] "" BDY + SG_ LKAS_OFF : 11|1@0+ (1,0) [0|1] "" BDY + SG_ SOLID_LANES : 10|1@0+ (1,0) [0|1] "" BDY + SG_ LDW_RIGHT : 9|1@0+ (1,0) [0|1] "" BDY + SG_ STEERING_REQUIRED : 8|1@0+ (1,0) [0|1] "" BDY + SG_ BOH : 23|2@0+ (1,0) [0|4] "" BDY + SG_ LDW_PROBLEM : 21|1@0+ (1,0) [0|1] "" BDY + SG_ BEEP : 17|2@0+ (1,0) [0|1] "" BDY + SG_ LDW_ON : 28|1@0+ (1,0) [0|1] "" BDY + SG_ LDW_OFF : 27|1@0+ (1,0) [0|1] "" BDY + SG_ CLEAN_WINDSHIELD : 26|1@0+ (1,0) [0|1] "" BDY + SG_ SET_ME_X48 : 31|8@0+ (1,0) [0|255] "" BDY + SG_ COUNTER : 37|2@0+ (1,0) [0|3] "" BDY + SG_ CHECKSUM : 35|4@0+ (1,0) [0|15] "" BDY BO_ 892 CRUISE_PARAMS: 8 PCM SG_ CRUISE_SPEED_OFFSET : 31|8@0- (0.1,0) [-128|127] "kph" EON @@ -264,22 +271,25 @@ BO_ 892 CRUISE_PARAMS: 8 PCM SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" EON CM_ SG_ 506 AEB_REQ_1 "set for duration of suspected AEB event"; -CM_ SG_ 780 CRUISE_SPEED "255 = no speed"; +CM_ SG_ 506 COMPUTER_BRAKE_ALT "Used by dual-can Nidec"; +CM_ SG_ 506 BRAKE_PUMP_REQUEST_ALT "Used by dual-can Nidec"; +CM_ SG_ 829 BEEP "beeps are pleasant, chimes are for warnngs etc..."; VAL_ 506 FCW 3 "fcw" 2 "fcw" 1 "fcw" 0 "no_fcw"; VAL_ 506 CHIME 4 "double_chime" 3 "single_chime" 2 "continuous_chime" 1 "repeating_chime" 0 "no_chime"; VAL_ 506 AEB_STATUS 3 "aeb_prepare" 2 "aeb_ready" 1 "aeb_braking" 0 "no_aeb"; -VAL_ 780 CRUISE_SPEED 255 "no_speed" 252 "stopped"; -VAL_ 780 HUD_LEAD 3 "acc_off" 2 "solid_car" 1 "dashed_car" 0 "no_car"; +VAL_ 829 BEEP 3 "single_beep" 2 "triple_beep" 1 "repeated_beep" 0 "no_beep"; -CM_ "acura_rdx_2018_can.dbc starts here"; +CM_ "Imported file _steering_sensors_b.dbc starts here"; BO_ 342 STEERING_SENSORS: 6 EPS SG_ STEER_ANGLE : 7|16@0- (-0.1,0) [-500|500] "deg" EON SG_ STEER_ANGLE_RATE : 23|16@0- (-1,0) [-3000|3000] "deg/s" EON SG_ COUNTER : 45|2@0+ (1,0) [0|3] "" EON SG_ CHECKSUM : 43|4@0+ (1,0) [0|15] "" EON +CM_ "acura_rdx_2018_can.dbc starts here"; + BO_ 392 GEARBOX: 6 XXX SG_ CHECKSUM : 43|4@0+ (1,0) [0|15] "" XXX SG_ COUNTER : 45|2@0+ (1,0) [0|3] "" XXX diff --git a/opendbc/acura_rdx_2020_can_generated.dbc b/opendbc/acura_rdx_2020_can_generated.dbc index 7d0f2188f..0b8162f05 100644 --- a/opendbc/acura_rdx_2020_can_generated.dbc +++ b/opendbc/acura_rdx_2020_can_generated.dbc @@ -76,6 +76,43 @@ BO_ 773 SEATBELT_STATUS: 7 BDY SG_ COUNTER : 53|2@0+ (1,0) [0|3] "" EON SG_ CHECKSUM : 51|4@0+ (1,0) [0|3] "" EON +BO_ 777 CAR_SPEED: 8 PCM + SG_ ROUGH_CAR_SPEED : 23|8@0+ (1,0) [0|255] "mph" XXX + SG_ CAR_SPEED : 7|16@0+ (0.01,0) [0|65535] "kph" XXX + SG_ ROUGH_CAR_SPEED_3 : 39|16@0+ (0.01,0) [0|65535] "kph" XXX + SG_ ROUGH_CAR_SPEED_2 : 31|8@0+ (1,0) [0|255] "mph" XXX + SG_ LOCK_STATUS : 55|2@0+ (1,0) [0|255] "" XXX + SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" XXX + SG_ CHECKSUM : 59|4@0+ (1,0) [0|15] "" XXX + SG_ IMPERIAL_UNIT : 63|1@0+ (1,0) [0|1] "" XXX + +BO_ 780 ACC_HUD: 8 ADAS + SG_ PCM_SPEED : 7|16@0+ (0.01,0) [0|250] "kph" BDY + SG_ PCM_GAS : 23|8@0+ (1,0) [0|127] "" BDY + SG_ CRUISE_SPEED : 31|8@0+ (1,0) [0|255] "kph" BDY + SG_ DTC_MODE : 39|1@0+ (1,0) [0|1] "" BDY + SG_ BOH : 38|1@0+ (1,0) [0|1] "" BDY + SG_ ACC_PROBLEM : 37|1@0+ (1,0) [0|1] "" BDY + SG_ FCM_OFF : 35|1@0+ (1,0) [0|1] "" BDY + SG_ FCM_OFF_2 : 36|1@0+ (1,0) [0|1] "" BDY + SG_ FCM_PROBLEM : 34|1@0+ (1,0) [0|1] "" BDY + SG_ RADAR_OBSTRUCTED : 33|1@0+ (1,0) [0|1] "" BDY + SG_ ENABLE_MINI_CAR : 32|1@0+ (1,0) [0|1] "" BDY + SG_ HUD_DISTANCE : 47|2@0+ (1,0) [0|3] "" BDY + SG_ HUD_LEAD : 45|2@0+ (1,0) [0|3] "" BDY + SG_ BOH_3 : 43|1@0+ (1,0) [0|3] "" BDY + SG_ BOH_4 : 42|1@0+ (1,0) [0|3] "" BDY + SG_ BOH_5 : 41|1@0+ (1,0) [0|3] "" BDY + SG_ CRUISE_CONTROL_LABEL : 40|1@0+ (1,0) [0|3] "" BDY + SG_ SET_ME_X01_2 : 55|1@0+ (1,0) [0|1] "" BDY + SG_ IMPERIAL_UNIT : 54|1@0+ (1,0) [0|1] "" BDY + SG_ ACC_ON : 52|1@0+ (1,0) [0|1] "" BDY + SG_ CHIME : 51|3@0+ (1,0) [0|1] "" BDY + SG_ SET_ME_X01 : 48|1@0+ (1,0) [0|1] "" BDY + SG_ ICONS : 63|2@0+ (1,0) [0|1] "" BDY + SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" BDY + SG_ CHECKSUM : 59|4@0+ (1,0) [0|3] "" BDY + BO_ 804 CRUISE: 8 PCM SG_ HUD_SPEED_KPH : 7|8@0+ (1,0) [0|255] "kph" EON SG_ HUD_SPEED_MPH : 15|8@0+ (1,0) [0|255] "mph" EON @@ -86,27 +123,6 @@ BO_ 804 CRUISE: 8 PCM SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" EON SG_ CHECKSUM : 59|4@0+ (1,0) [0|15] "" EON -BO_ 829 LKAS_HUD: 5 ADAS - SG_ CAM_TEMP_HIGH : 7|1@0+ (1,0) [0|255] "" BDY - SG_ SET_ME_X41 : 6|7@0+ (1,0) [0|127] "" BDY - SG_ BOH : 6|7@0+ (1,0) [0|127] "" BDY - SG_ DASHED_LANES : 14|1@0+ (1,0) [0|1] "" BDY - SG_ DTC : 13|1@0+ (1,0) [0|1] "" BDY - SG_ LKAS_PROBLEM : 12|1@0+ (1,0) [0|1] "" BDY - SG_ LKAS_OFF : 11|1@0+ (1,0) [0|1] "" BDY - SG_ SOLID_LANES : 10|1@0+ (1,0) [0|1] "" BDY - SG_ LDW_RIGHT : 9|1@0+ (1,0) [0|1] "" BDY - SG_ STEERING_REQUIRED : 8|1@0+ (1,0) [0|1] "" BDY - SG_ BOH : 23|2@0+ (1,0) [0|4] "" BDY - SG_ LDW_PROBLEM : 21|1@0+ (1,0) [0|1] "" BDY - SG_ BEEP : 17|2@0+ (1,0) [0|1] "" BDY - SG_ LDW_ON : 28|1@0+ (1,0) [0|1] "" BDY - SG_ LDW_OFF : 27|1@0+ (1,0) [0|1] "" BDY - SG_ CLEAN_WINDSHIELD : 26|1@0+ (1,0) [0|1] "" BDY - SG_ SET_ME_X48 : 31|8@0+ (1,0) [0|255] "" BDY - SG_ COUNTER : 37|2@0+ (1,0) [0|3] "" BDY - SG_ CHECKSUM : 35|4@0+ (1,0) [0|15] "" BDY - BO_ 884 STALK_STATUS: 8 XXX SG_ DASHBOARD_ALERT : 39|8@0+ (1,0) [0|255] "" EON SG_ AUTO_HEADLIGHTS : 46|1@0+ (1,0) [0|1] "" EON @@ -140,10 +156,13 @@ CM_ SG_ 420 BRAKE_HOLD_RELATED "On when Brake Hold engaged"; CM_ SG_ 490 LONG_ACCEL "wheel speed derivative, noisy and zero snapping"; CM_ SG_ 773 PASS_AIRBAG_ON "Might just be indicator light"; CM_ SG_ 773 PASS_AIRBAG_OFF "Might just be indicator light"; +CM_ SG_ 780 CRUISE_SPEED "255 = no speed"; +CM_ SG_ 780 PCM_SPEED "Used by Nidec"; +CM_ SG_ 780 PCM_GAS "Used by Nidec"; CM_ SG_ 804 CRUISE_SPEED_PCM "255 = no speed"; -CM_ SG_ 829 BEEP "beeps are pleasant, chimes are for warnngs etc..."; -VAL_ 829 BEEP 3 "single_beep" 2 "triple_beep" 1 "repeated_beep" 0 "no_beep"; +VAL_ 780 CRUISE_SPEED 255 "no_speed" 252 "stopped"; +VAL_ 780 HUD_LEAD 3 "acc_off" 2 "solid_car" 1 "dashed_car" 0 "no_car"; VAL_ 884 DASHBOARD_ALERT 0 "none" 51 "acc_problem" 55 "cmbs_problem" 75 "key_not_detected" 79 "fasten_seatbelt" 111 "lkas_problem" 131 "brake_system_problem" 132 "brake_hold_problem" 139 "tbd" 161 "door_open"; VAL_ 891 WIPERS 4 "High" 2 "Low" 0 "Off"; @@ -194,32 +213,6 @@ BO_ 450 EPB_STATUS: 8 EPB SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" XXX SG_ CHECKSUM : 59|4@0+ (1,0) [0|15] "" XXX -BO_ 479 ACC_CONTROL: 8 EON - SG_ SET_TO_0 : 20|5@0+ (1,0) [0|1] "" XXX - SG_ CONTROL_ON : 23|3@0+ (1,0) [0|5] "" XXX - SG_ GAS_COMMAND : 7|16@0- (1,0) [0|0] "" XXX - SG_ ACCEL_COMMAND : 31|11@0- (0.01,0) [0|0] "m/s2" XXX - SG_ BRAKE_LIGHTS : 62|1@0+ (1,0) [0|1] "" XXX - SG_ BRAKE_REQUEST : 34|1@0+ (1,0) [0|1] "" XXX - SG_ STANDSTILL : 35|1@0+ (1,0) [0|1] "" XXX - SG_ STANDSTILL_RELEASE : 36|1@0+ (1,0) [0|1] "" XXX - SG_ AEB_STATUS : 33|1@0+ (1,0) [0|1] "" XXX - SG_ AEB_BRAKING : 47|1@0+ (1,0) [0|1] "" XXX - SG_ AEB_PREPARE : 43|1@0+ (1,0) [0|1] "" XXX - SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" XXX - SG_ CHECKSUM : 59|4@0+ (1,0) [0|15] "" XXX - -BO_ 495 ACC_CONTROL_ON: 8 XXX - SG_ SET_TO_75 : 31|8@0+ (1,0) [0|255] "" XXX - SG_ SET_TO_30 : 39|8@0+ (1,0) [0|255] "" XXX - SG_ ZEROS_BOH : 23|8@0+ (1,0) [0|255] "" XXX - SG_ ZEROS_BOH2 : 47|16@0+ (1,0) [0|255] "" XXX - SG_ SET_TO_FF : 15|8@0+ (1,0) [0|255] "" XXX - SG_ SET_TO_3 : 6|7@0+ (1,0) [0|4095] "" XXX - SG_ CONTROL_ON : 7|1@0+ (1,0) [0|1] "" XXX - SG_ CHECKSUM : 59|4@0+ (1,0) [0|15] "" XXX - SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" XXX - BO_ 545 XXX_16: 6 SCM SG_ ECON_ON : 23|1@0+ (1,0) [0|1] "" XXX SG_ DRIVE_MODE : 37|2@0+ (1,0) [0|3] "" XXX @@ -319,40 +312,6 @@ BO_ 662 SCM_BUTTONS: 4 SCM SG_ COUNTER : 29|2@0+ (1,0) [0|3] "" EON SG_ CHECKSUM : 27|4@0+ (1,0) [0|15] "" EON -BO_ 777 CAR_SPEED: 8 PCM - SG_ ROUGH_CAR_SPEED : 23|8@0+ (1,0) [0|255] "mph" XXX - SG_ CAR_SPEED : 7|16@0+ (0.01,0) [0|65535] "kph" XXX - SG_ ROUGH_CAR_SPEED_3 : 39|16@0+ (0.01,0) [0|65535] "kph" XXX - SG_ ROUGH_CAR_SPEED_2 : 31|8@0+ (1,0) [0|255] "mph" XXX - SG_ LOCK_STATUS : 55|2@0+ (1,0) [0|255] "" XXX - SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" XXX - SG_ CHECKSUM : 59|4@0+ (1,0) [0|15] "" XXX - SG_ IMPERIAL_UNIT : 63|1@0+ (1,0) [0|1] "" XXX - -BO_ 780 ACC_HUD: 8 ADAS - SG_ CRUISE_SPEED : 31|8@0+ (1,0) [0|255] "kph" BDY - SG_ DTC_MODE : 39|1@0+ (1,0) [0|1] "" BDY - SG_ BOH : 38|1@0+ (1,0) [0|1] "" BDY - SG_ FCM_PROBLEM : 34|1@0+ (1,0) [0|1] "" BDY - SG_ RADAR_OBSTRUCTED : 33|1@0+ (1,0) [0|1] "" BDY - SG_ ENABLE_MINI_CAR : 32|1@0+ (1,0) [0|1] "" BDY - SG_ BOH_3 : 43|1@0+ (1,0) [0|3] "" BDY - SG_ BOH_4 : 42|1@0+ (1,0) [0|3] "" BDY - SG_ BOH_5 : 41|1@0+ (1,0) [0|3] "" BDY - SG_ CRUISE_CONTROL_LABEL : 40|1@0+ (1,0) [0|3] "" BDY - SG_ ZEROS_BOH : 7|24@0+ (0.002759506,0) [0|100] "m/s" BDY - SG_ FCM_OFF : 35|1@0+ (1,0) [0|1] "" BDY - SG_ SET_TO_1 : 36|1@0+ (1,0) [0|1] "" XXX - SG_ HUD_DISTANCE : 47|2@0+ (1,0) [0|3] "" BDY - SG_ HUD_LEAD : 45|2@0+ (1,0) [0|3] "" BDY - SG_ ACC_PROBLEM : 37|1@0+ (1,0) [0|1] "" BDY - SG_ ACC_ON : 52|1@0+ (1,0) [0|1] "" XXX - SG_ BOH_6 : 51|4@0+ (1,0) [0|15] "" XXX - SG_ SET_TO_X1 : 55|1@0+ (1,0) [0|1] "" XXX - SG_ IMPERIAL_UNIT : 54|1@0+ (1,0) [0|1] "" XXX - SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" XXX - SG_ CHECKSUM : 59|4@0+ (1,0) [0|15] "" XXX - BO_ 806 SCM_FEEDBACK: 8 SCM SG_ DRIVERS_DOOR_OPEN : 17|1@0+ (1,0) [0|1] "" XXX SG_ MAIN_ON : 28|1@0+ (1,0) [0|1] "" EON @@ -370,6 +329,23 @@ BO_ 862 CAMERA_MESSAGES: 8 CAM SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" XXX SG_ CHECKSUM : 59|4@0+ (1,0) [0|15] "" XXX +BO_ 927 RADAR_HUD: 8 RADAR + SG_ ZEROS_BOH : 7|10@0+ (1,0) [0|127] "" BDY + SG_ CMBS_OFF : 12|1@0+ (1,0) [0|1] "" BDY + SG_ RESUME_INSTRUCTION : 21|1@0+ (1,0) [0|1] "" XXX + SG_ SET_TO_1 : 13|1@0+ (1,0) [0|1] "" BDY + SG_ ZEROS_BOH2 : 11|4@0+ (1,0) [0|1] "" XXX + SG_ APPLY_BRAKES_FOR_CANC : 23|1@0+ (1,0) [0|1] "" XXX + SG_ ACC_ALERTS : 20|5@0+ (1,0) [0|1] "" BDY + SG_ SET_TO_0 : 22|1@0+ (1,0) [0|1] "" XXX + SG_ HUD_LEAD : 40|1@0+ (1,0) [0|1] "" XXX + SG_ SET_TO_64 : 31|8@0+ (1,0) [0|255] "" XXX + SG_ LEAD_DISTANCE : 39|8@0+ (1,0) [0|255] "" XXX + SG_ ZEROS_BOH3 : 47|7@0+ (1,0) [0|127] "" XXX + SG_ ZEROS_BOH4 : 55|8@0+ (1,0) [0|255] "" XXX + SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" XXX + SG_ CHECKSUM : 59|4@0+ (1,0) [0|15] "" XXX + BO_ 13274 LKAS_HUD_A: 5 ADAS SG_ CAM_TEMP_HIGH : 7|1@0+ (1,0) [0|255] "" BDY SG_ SET_ME_X41 : 6|7@0+ (1,0) [0|127] "" BDY @@ -413,10 +389,6 @@ BO_ 13275 LKAS_HUD_B: 8 ADAS SG_ CHECKSUM : 59|4@0+ (1,0) [0|15] "" BDY CM_ SG_ 450 EPB_STATE "3: On, 2: Disengaging, 1: Engaging, 0: Off"; -CM_ SG_ 479 CONTROL_ON "Set to 5 when car is being controlled"; -CM_ SG_ 479 AEB_STATUS "set for the duration of AEB event"; -CM_ SG_ 479 AEB_BRAKING "set when braking is commanded during AEB event"; -CM_ SG_ 479 AEB_PREPARE "set 1s before AEB"; CM_ SG_ 576 LINE_DISTANCE_VISIBLE "Length of line visible, undecoded"; CM_ SG_ 577 LINE_FAR_EDGE_POSITION "Appears to be a measure of line thickness, indicates location of the portion of the line furthest from the car, undecoded"; CM_ SG_ 577 LINE_PARAMETER "Unclear if this is low quality line curvature rate or if this is something else, but it is correlated with line curvature, undecoded"; @@ -425,14 +397,73 @@ CM_ SG_ 577 LINE_SOLID "1 = line is solid"; VAL_ 399 STEER_STATUS 6 "tmp_fault" 5 "fault_1" 4 "no_torque_alert_2" 3 "low_speed_lockout" 2 "no_torque_alert_1" 0 "normal"; -CM_ "acura_rdx_2020_can.dbc starts here"; +CM_ "Imported file _bosch_adas_2018.dbc starts here"; +BO_ 479 ACC_CONTROL: 8 EON + SG_ SET_TO_0 : 20|5@0+ (1,0) [0|1] "" XXX + SG_ CONTROL_ON : 23|3@0+ (1,0) [0|5] "" XXX + SG_ GAS_COMMAND : 7|16@0- (1,0) [0|0] "" XXX + SG_ ACCEL_COMMAND : 31|11@0- (0.01,0) [0|0] "m/s2" XXX + SG_ BRAKE_LIGHTS : 62|1@0+ (1,0) [0|1] "" XXX + SG_ BRAKE_REQUEST : 34|1@0+ (1,0) [0|1] "" XXX + SG_ STANDSTILL : 35|1@0+ (1,0) [0|1] "" XXX + SG_ STANDSTILL_RELEASE : 36|1@0+ (1,0) [0|1] "" XXX + SG_ AEB_STATUS : 33|1@0+ (1,0) [0|1] "" XXX + SG_ AEB_BRAKING : 47|1@0+ (1,0) [0|1] "" XXX + SG_ AEB_PREPARE : 43|1@0+ (1,0) [0|1] "" XXX + SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" XXX + SG_ CHECKSUM : 59|4@0+ (1,0) [0|15] "" XXX + +BO_ 495 ACC_CONTROL_ON: 8 XXX + SG_ SET_TO_75 : 31|8@0+ (1,0) [0|255] "" XXX + SG_ SET_TO_30 : 39|8@0+ (1,0) [0|255] "" XXX + SG_ ZEROS_BOH : 23|8@0+ (1,0) [0|255] "" XXX + SG_ ZEROS_BOH2 : 47|16@0+ (1,0) [0|255] "" XXX + SG_ SET_TO_FF : 15|8@0+ (1,0) [0|255] "" XXX + SG_ SET_TO_3 : 6|7@0+ (1,0) [0|4095] "" XXX + SG_ CONTROL_ON : 7|1@0+ (1,0) [0|1] "" XXX + SG_ CHECKSUM : 59|4@0+ (1,0) [0|15] "" XXX + SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" XXX + +BO_ 829 LKAS_HUD: 5 ADAS + SG_ CAM_TEMP_HIGH : 7|1@0+ (1,0) [0|255] "" BDY + SG_ SET_ME_X41 : 6|7@0+ (1,0) [0|127] "" BDY + SG_ BOH : 6|7@0+ (1,0) [0|127] "" BDY + SG_ DASHED_LANES : 14|1@0+ (1,0) [0|1] "" BDY + SG_ DTC : 13|1@0+ (1,0) [0|1] "" BDY + SG_ LKAS_PROBLEM : 12|1@0+ (1,0) [0|1] "" BDY + SG_ LKAS_OFF : 11|1@0+ (1,0) [0|1] "" BDY + SG_ SOLID_LANES : 10|1@0+ (1,0) [0|1] "" BDY + SG_ LDW_RIGHT : 9|1@0+ (1,0) [0|1] "" BDY + SG_ STEERING_REQUIRED : 8|1@0+ (1,0) [0|1] "" BDY + SG_ BOH : 23|2@0+ (1,0) [0|4] "" BDY + SG_ LDW_PROBLEM : 21|1@0+ (1,0) [0|1] "" BDY + SG_ BEEP : 17|2@0+ (1,0) [0|1] "" BDY + SG_ LDW_ON : 28|1@0+ (1,0) [0|1] "" BDY + SG_ LDW_OFF : 27|1@0+ (1,0) [0|1] "" BDY + SG_ CLEAN_WINDSHIELD : 26|1@0+ (1,0) [0|1] "" BDY + SG_ SET_ME_X48 : 31|8@0+ (1,0) [0|255] "" BDY + SG_ COUNTER : 37|2@0+ (1,0) [0|3] "" BDY + SG_ CHECKSUM : 35|4@0+ (1,0) [0|15] "" BDY + +CM_ SG_ 479 CONTROL_ON "Set to 5 when car is being controlled"; +CM_ SG_ 479 AEB_STATUS "set for the duration of AEB event"; +CM_ SG_ 479 AEB_BRAKING "set when braking is commanded during AEB event"; +CM_ SG_ 479 AEB_PREPARE "set 1s before AEB"; +CM_ SG_ 829 BEEP "beeps are pleasant, chimes are for warnngs etc..."; + +VAL_ 829 BEEP 3 "single_beep" 2 "triple_beep" 1 "repeated_beep" 0 "no_beep"; + + +CM_ "Imported file _steering_sensors_b.dbc starts here"; BO_ 342 STEERING_SENSORS: 6 EPS SG_ STEER_ANGLE : 7|16@0- (-0.1,0) [-500|500] "deg" EON SG_ STEER_ANGLE_RATE : 23|16@0- (-1,0) [-3000|3000] "deg/s" EON SG_ COUNTER : 45|2@0+ (1,0) [0|3] "" EON SG_ CHECKSUM : 43|4@0+ (1,0) [0|15] "" EON +CM_ "acura_rdx_2020_can.dbc starts here"; + BO_ 419 GEARBOX: 8 PCM SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" EON SG_ CHECKSUM : 59|4@0+ (1,0) [0|3] "" EON @@ -450,21 +481,6 @@ BO_ 446 BRAKE_MODULE: 3 VSA SG_ COUNTER : 21|2@0+ (1,0) [0|3] "" XXX SG_ CHECKSUM : 19|4@0+ (1,0) [0|15] "" XXX -BO_ 927 RADAR_HUD: 8 RADAR - SG_ ZEROS_BOH : 7|10@0+ (1,0) [0|127] "" BDY - SG_ CMBS_OFF : 12|1@0+ (1,0) [0|1] "" BDY - SG_ RESUME_INSTRUCTION : 21|1@0+ (1,0) [0|1] "" XXX - SG_ SET_TO_1 : 13|1@0+ (1,0) [0|1] "" BDY - SG_ ZEROS_BOH2 : 11|4@0+ (1,0) [0|1] "" XXX - SG_ APPLY_BRAKES_FOR_CANC : 23|1@0+ (1,0) [0|1] "" XXX - SG_ ACC_ALERTS : 20|5@0+ (1,0) [0|1] "" BDY - SG_ SET_TO_0 : 22|1@0+ (1,0) [0|1] "" XXX - SG_ LEAD_DISTANCE : 39|8@0+ (1,0) [0|255] "" XXX - SG_ BOH : 40|1@0+ (1,0) [0|1] "" XXX - SG_ BOH_2 : 30|1@0+ (1,0) [0|1] "" XXX - SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" XXX - SG_ CHECKSUM : 59|4@0+ (1,0) [0|15] "" XXX - BO_ 1302 ODOMETER: 8 XXX SG_ ODOMETER : 7|24@0+ (1,0) [0|16777215] "km" EON SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" EON diff --git a/opendbc/can/common.cc b/opendbc/can/common.cc index e9abbedb3..d237fa359 100644 --- a/opendbc/can/common.cc +++ b/opendbc/can/common.cc @@ -1,6 +1,6 @@ #include "common.h" -unsigned int honda_checksum(uint32_t address, const std::vector &d) { +unsigned int honda_checksum(uint32_t address, const Signal &sig, const std::vector &d) { int s = 0; bool extended = address > 0x7FF; while (address) { s += (address & 0xF); address >>= 4; } @@ -15,7 +15,7 @@ unsigned int honda_checksum(uint32_t address, const std::vector &d) { return s & 0xF; } -unsigned int toyota_checksum(uint32_t address, const std::vector &d) { +unsigned int toyota_checksum(uint32_t address, const Signal &sig, const std::vector &d) { unsigned int s = d.size(); while (address) { s += address & 0xFF; address >>= 8; } for (int i = 0; i < d.size() - 1; i++) { s += d[i]; } @@ -23,7 +23,7 @@ unsigned int toyota_checksum(uint32_t address, const std::vector &d) { return s & 0xFF; } -unsigned int subaru_checksum(uint32_t address, const std::vector &d) { +unsigned int subaru_checksum(uint32_t address, const Signal &sig, const std::vector &d) { unsigned int s = 0; while (address) { s += address & 0xFF; address >>= 8; } @@ -33,7 +33,7 @@ unsigned int subaru_checksum(uint32_t address, const std::vector &d) { return s & 0xFF; } -unsigned int chrysler_checksum(uint32_t address, const std::vector &d) { +unsigned int chrysler_checksum(uint32_t address, const Signal &sig, const std::vector &d) { /* jeep chrysler canbus checksum from http://illmatics.com/Remote%20Car%20Hacking.pdf */ uint8_t checksum = 0xFF; for (int j = 0; j < (d.size() - 1); j++) { @@ -107,7 +107,7 @@ void init_crc_lookup_tables() { gen_crc_lookup_table_16(0x1021, crc16_lut_xmodem); // CRC-16 XMODEM for HKG CAN FD } -unsigned int volkswagen_crc(uint32_t address, const std::vector &d) { +unsigned int volkswagen_mqb_checksum(uint32_t address, const Signal &sig, const std::vector &d) { // Volkswagen uses standard CRC8 8H2F/AUTOSAR, but they compute it with // a magic variable padding byte tacked onto the end of the payload. // https://www.autosar.org/fileadmin/user_upload/standards/classic/4-3/AUTOSAR_SWS_CRCLibrary.pdf @@ -188,7 +188,7 @@ unsigned int volkswagen_crc(uint32_t address, const std::vector &d) { return crc ^ 0xFF; // Return after standard final XOR for CRC8 8H2F/AUTOSAR } -unsigned int pedal_checksum(const std::vector &d) { +unsigned int pedal_checksum(uint32_t address, const Signal &sig, const std::vector &d) { uint8_t crc = 0xFF; uint8_t poly = 0xD5; // standard crc8 @@ -206,7 +206,7 @@ unsigned int pedal_checksum(const std::vector &d) { return crc; } -unsigned int hkg_can_fd_checksum(uint32_t address, const std::vector &d) { +unsigned int hkg_can_fd_checksum(uint32_t address, const Signal &sig, const std::vector &d) { uint16_t crc = 0; diff --git a/opendbc/can/common.h b/opendbc/can/common.h index 7e35e06a3..f7ab3202b 100644 --- a/opendbc/can/common.h +++ b/opendbc/can/common.h @@ -22,13 +22,13 @@ void init_crc_lookup_tables(); // Car specific functions -unsigned int honda_checksum(uint32_t address, const std::vector &d); -unsigned int toyota_checksum(uint32_t address, const std::vector &d); -unsigned int subaru_checksum(uint32_t address, const std::vector &d); -unsigned int chrysler_checksum(uint32_t address, const std::vector &d); -unsigned int volkswagen_crc(uint32_t address, const std::vector &d); -unsigned int hkg_can_fd_checksum(uint32_t address, const std::vector &d); -unsigned int pedal_checksum(const std::vector &d); +unsigned int honda_checksum(uint32_t address, const Signal &sig, const std::vector &d); +unsigned int toyota_checksum(uint32_t address, const Signal &sig, const std::vector &d); +unsigned int subaru_checksum(uint32_t address, const Signal &sig, const std::vector &d); +unsigned int chrysler_checksum(uint32_t address, const Signal &sig, const std::vector &d); +unsigned int volkswagen_mqb_checksum(uint32_t address, const Signal &sig, const std::vector &d); +unsigned int hkg_can_fd_checksum(uint32_t address, const Signal &sig, const std::vector &d); +unsigned int pedal_checksum(uint32_t address, const Signal &sig, const std::vector &d); class MessageState { public: @@ -39,7 +39,7 @@ public: std::vector vals; std::vector> all_vals; - uint64_t seen; + uint64_t last_seen_nanos; uint64_t check_threshold; uint8_t counter; diff --git a/opendbc/can/common.pxd b/opendbc/can/common.pxd index 9d97a81bb..c45eb8184 100644 --- a/opendbc/can/common.pxd +++ b/opendbc/can/common.pxd @@ -9,20 +9,19 @@ from libcpp.vector cimport vector from libcpp.unordered_set cimport unordered_set +ctypedef unsigned int (*calc_checksum_type)(uint32_t, const Signal&, const vector[uint8_t] &) + cdef extern from "common_dbc.h": ctypedef enum SignalType: DEFAULT, + COUNTER, HONDA_CHECKSUM, - HONDA_COUNTER, TOYOTA_CHECKSUM, PEDAL_CHECKSUM, - PEDAL_COUNTER, - VOLKSWAGEN_CHECKSUM, - VOLKSWAGEN_COUNTER, + VOLKSWAGEN_MQB_CHECKSUM, SUBARU_CHECKSUM, CHRYSLER_CHECKSUM HKG_CAN_FD_CHECKSUM, - HKG_CAN_FD_COUNTER, cdef struct Signal: string name @@ -31,6 +30,7 @@ cdef extern from "common_dbc.h": double factor, offset bool is_little_endian SignalType type + calc_checksum_type calc_checksum cdef struct Msg: string name diff --git a/opendbc/can/common_dbc.h b/opendbc/can/common_dbc.h index 9bd47c25e..a44221d55 100644 --- a/opendbc/can/common_dbc.h +++ b/opendbc/can/common_dbc.h @@ -31,17 +31,14 @@ struct SignalValue { enum SignalType { DEFAULT, + COUNTER, HONDA_CHECKSUM, - HONDA_COUNTER, TOYOTA_CHECKSUM, PEDAL_CHECKSUM, - PEDAL_COUNTER, - VOLKSWAGEN_CHECKSUM, - VOLKSWAGEN_COUNTER, + VOLKSWAGEN_MQB_CHECKSUM, SUBARU_CHECKSUM, CHRYSLER_CHECKSUM, HKG_CAN_FD_CHECKSUM, - HKG_CAN_FD_COUNTER, }; struct Signal { @@ -51,6 +48,7 @@ struct Signal { double factor, offset; bool is_little_endian; SignalType type; + unsigned int (*calc_checksum)(uint32_t address, const Signal &sig, const std::vector &d); }; struct Msg { @@ -73,6 +71,6 @@ struct DBC { std::vector vals; }; -DBC* dbc_parse(const std::string& dbc_name, const std::string& dbc_file_path); +DBC* dbc_parse(const std::string& dbc_path); const DBC* dbc_lookup(const std::string& dbc_name); std::vector get_dbc_names(); diff --git a/opendbc/can/dbc.cc b/opendbc/can/dbc.cc index 94ac4082d..38e4d1213 100644 --- a/opendbc/can/dbc.cc +++ b/opendbc/can/dbc.cc @@ -9,6 +9,7 @@ #include #include +#include "common.h" #include "common_dbc.h" std::regex bo_regexp(R"(^BO_ (\w+) (\w+) *: (\w+) (\w+))"); @@ -54,35 +55,39 @@ typedef struct ChecksumState { bool little_endian; SignalType checksum_type; SignalType counter_type; + unsigned int (*calc_checksum)(uint32_t address, const Signal &sig, const std::vector &d); } ChecksumState; ChecksumState* get_checksum(const std::string& dbc_name) { ChecksumState* s = nullptr; if (startswith(dbc_name, {"honda_", "acura_"})) { - s = new ChecksumState({4, 2, 3, 5, false, HONDA_CHECKSUM, HONDA_COUNTER}); + s = new ChecksumState({4, 2, 3, 5, false, HONDA_CHECKSUM, COUNTER, &honda_checksum}); } else if (startswith(dbc_name, {"toyota_", "lexus_"})) { - s = new ChecksumState({8, -1, 7, -1, false, TOYOTA_CHECKSUM}); + s = new ChecksumState({8, -1, 7, -1, false, TOYOTA_CHECKSUM, DEFAULT, &toyota_checksum}); } else if (startswith(dbc_name, "kia_ev6")) { - s = new ChecksumState({16, 8, 0, 0, true, HKG_CAN_FD_CHECKSUM, HKG_CAN_FD_COUNTER}); - } else if (startswith(dbc_name, {"vw_", "volkswagen_", "audi_", "seat_", "skoda_"})) { - s = new ChecksumState({8, 4, 0, 0, true, VOLKSWAGEN_CHECKSUM, VOLKSWAGEN_COUNTER}); + s = new ChecksumState({16, -1, 0, -1, true, HKG_CAN_FD_CHECKSUM, COUNTER, &hkg_can_fd_checksum}); + } else if (startswith(dbc_name, {"vw_mqb_2010"})) { + s = new ChecksumState({8, 4, 0, 0, true, VOLKSWAGEN_MQB_CHECKSUM, COUNTER, &volkswagen_mqb_checksum}); } else if (startswith(dbc_name, "subaru_global_")) { - s = new ChecksumState({8, -1, 0, -1, true, SUBARU_CHECKSUM}); + s = new ChecksumState({8, -1, 0, -1, true, SUBARU_CHECKSUM, DEFAULT, &subaru_checksum}); } else if (startswith(dbc_name, "chrysler_")) { - s = new ChecksumState({8, -1, 7, -1, false, CHRYSLER_CHECKSUM}); + s = new ChecksumState({8, -1, 7, -1, false, CHRYSLER_CHECKSUM, DEFAULT, &chrysler_checksum}); } else if (startswith(dbc_name, "comma_body")) { - s = new ChecksumState({8, 4, 7, 3, false, PEDAL_CHECKSUM, PEDAL_COUNTER}); + s = new ChecksumState({8, 4, 7, 3, false, PEDAL_CHECKSUM, COUNTER, &pedal_checksum}); } return s; } -void set_signal_type(Signal& s, uint32_t address, ChecksumState* chk, const std::string& dbc_name, int line_num) { +void set_signal_type(Signal& s, ChecksumState* chk, const std::string& dbc_name, int line_num) { + s.calc_checksum = nullptr; if (chk) { if (s.name == "CHECKSUM") { - DBC_ASSERT(s.size == chk->checksum_size, "CHECKSUM is not " << chk->checksum_size << " bits long"); - DBC_ASSERT((s.start_bit % 8) == chk->checksum_start_bit, " CHECKSUM starts at wrong bit"); + DBC_ASSERT(chk->checksum_size == -1 || s.size == chk->checksum_size, "CHECKSUM is not " << chk->checksum_size << " bits long"); + DBC_ASSERT(chk->checksum_start_bit == -1 || (s.start_bit % 8) == chk->checksum_start_bit, " CHECKSUM starts at wrong bit"); DBC_ASSERT(s.is_little_endian == chk->little_endian, "CHECKSUM has wrong endianness"); + DBC_ASSERT(chk->calc_checksum != nullptr, "CHECKSUM calculate function not supplied"); s.type = chk->checksum_type; + s.calc_checksum = chk->calc_checksum; } else if (s.name == "COUNTER") { DBC_ASSERT(chk->counter_size == -1 || s.size == chk->counter_size, "COUNTER is not " << chk->counter_size << " bits long"); DBC_ASSERT(chk->counter_start_bit == -1 || (s.start_bit % 8) == chk->counter_start_bit, "COUNTER starts at wrong bit"); @@ -90,22 +95,23 @@ void set_signal_type(Signal& s, uint32_t address, ChecksumState* chk, const std: s.type = chk->counter_type; } } - // TODO: replace hardcoded addresses with signal names. prefix with COMMA_PEDAL_? - if (address == 0x200 || address == 0x201) { - if (s.name == "CHECKSUM_PEDAL") { - DBC_ASSERT(s.size == 8, "PEDAL CHECKSUM is not 8 bits long"); - s.type = PEDAL_CHECKSUM; - } else if (s.name == "COUNTER_PEDAL") { - DBC_ASSERT(s.size == 4, "PEDAL COUNTER is not 4 bits long"); - s.type = PEDAL_COUNTER; - } + + // TODO: CAN packer/parser shouldn't know anything about interceptors or pedals + if (s.name == "CHECKSUM_PEDAL") { + DBC_ASSERT(s.size == 8, "INTERCEPTOR CHECKSUM is not 8 bits long"); + s.type = PEDAL_CHECKSUM; + } else if (s.name == "COUNTER_PEDAL") { + DBC_ASSERT(s.size == 4, "INTERCEPTOR COUNTER is not 4 bits long"); + s.type = COUNTER; } } -DBC* dbc_parse(const std::string& dbc_name, const std::string& dbc_file_path) { - std::ifstream infile(dbc_file_path + "/" + dbc_name + ".dbc"); +DBC* dbc_parse(const std::string& dbc_path) { + std::ifstream infile(dbc_path); if (!infile) return nullptr; + const std::string dbc_name = std::filesystem::path(dbc_path).filename(); + std::unique_ptr checksum(get_checksum(dbc_name)); uint32_t address = 0; @@ -161,7 +167,7 @@ DBC* dbc_parse(const std::string& dbc_name, const std::string& dbc_file_path) { sig.is_signed = match[offset + 5].str() == "-"; sig.factor = std::stod(match[offset + 6].str()); sig.offset = std::stod(match[offset + 7].str()); - set_signal_type(sig, address, checksum.get(), dbc_name, line_num); + set_signal_type(sig, checksum.get(), dbc_name, line_num); if (sig.is_little_endian) { sig.lsb = sig.start_bit; sig.msb = sig.start_bit + sig.size - 1; @@ -206,7 +212,7 @@ DBC* dbc_parse(const std::string& dbc_name, const std::string& dbc_file_path) { return dbc; } -const std::string get_dbc_file_path() { +const std::string get_dbc_root_path() { char *basedir = std::getenv("BASEDIR"); if (basedir != NULL) { return std::string(basedir) + "/opendbc"; @@ -218,18 +224,22 @@ const std::string get_dbc_file_path() { const DBC* dbc_lookup(const std::string& dbc_name) { static std::mutex lock; static std::map dbcs; - static const std::string& dbc_file_path = get_dbc_file_path(); + + std::string dbc_file_path = dbc_name; + if (!std::filesystem::exists(dbc_file_path)) { + dbc_file_path = get_dbc_root_path() + "/" + dbc_name + ".dbc"; + } std::unique_lock lk(lock); auto it = dbcs.find(dbc_name); if (it == dbcs.end()) { - it = dbcs.insert(it, {dbc_name, dbc_parse(dbc_name, dbc_file_path)}); + it = dbcs.insert(it, {dbc_name, dbc_parse(dbc_file_path)}); } return it->second; } std::vector get_dbc_names() { - static const std::string& dbc_file_path = get_dbc_file_path(); + static const std::string& dbc_file_path = get_dbc_root_path(); std::vector dbcs; for (std::filesystem::directory_iterator i(dbc_file_path), end; i != end; i++) { if (!is_directory(i->path())) { diff --git a/opendbc/can/packer.cc b/opendbc/can/packer.cc index 27e002148..96c04b6df 100644 --- a/opendbc/can/packer.cc +++ b/opendbc/can/packer.cc @@ -69,11 +69,6 @@ std::vector CANPacker::pack(uint32_t address, const std::vectorsecond; - - if ((sig.type != SignalType::HONDA_COUNTER) && (sig.type != SignalType::VOLKSWAGEN_COUNTER)) { - //WARN("COUNTER signal type not valid\n"); - } - set_value(ret, sig, counter); } @@ -81,29 +76,9 @@ std::vector CANPacker::pack(uint32_t address, const std::vectorsecond; - if (sig.type == SignalType::HONDA_CHECKSUM) { - unsigned int chksm = honda_checksum(address, ret); - set_value(ret, sig, chksm); - } else if (sig.type == SignalType::TOYOTA_CHECKSUM) { - unsigned int chksm = toyota_checksum(address, ret); - set_value(ret, sig, chksm); - } else if (sig.type == SignalType::VOLKSWAGEN_CHECKSUM) { - unsigned int chksm = volkswagen_crc(address, ret); - set_value(ret, sig, chksm); - } else if (sig.type == SignalType::SUBARU_CHECKSUM) { - unsigned int chksm = subaru_checksum(address, ret); - set_value(ret, sig, chksm); - } else if (sig.type == SignalType::CHRYSLER_CHECKSUM) { - unsigned int chksm = chrysler_checksum(address, ret); - set_value(ret, sig, chksm); - } else if (sig.type == SignalType::PEDAL_CHECKSUM) { - unsigned int chksm = pedal_checksum(ret); - set_value(ret, sig, chksm); - } else if (sig.type == SignalType::HKG_CAN_FD_CHECKSUM) { - unsigned int chksm = hkg_can_fd_checksum(address, ret); - set_value(ret, sig, chksm); - } else { - //WARN("CHECKSUM signal type not valid\n"); + if (sig.calc_checksum != nullptr) { + unsigned int checksum = sig.calc_checksum(address, sig, ret); + set_value(ret, sig, checksum); } } diff --git a/opendbc/can/parser.cc b/opendbc/can/parser.cc index 7e6d60445..3a1b77c18 100644 --- a/opendbc/can/parser.cc +++ b/opendbc/can/parser.cc @@ -45,26 +45,14 @@ bool MessageState::parse(uint64_t sec, const std::vector &dat) { bool checksum_failed = false; if (!ignore_checksum) { - if (sig.type == SignalType::HONDA_CHECKSUM && honda_checksum(address, dat) != tmp) { - checksum_failed = true; - } else if (sig.type == SignalType::TOYOTA_CHECKSUM && toyota_checksum(address, dat) != tmp) { - checksum_failed = true; - } else if (sig.type == SignalType::VOLKSWAGEN_CHECKSUM && volkswagen_crc(address, dat) != tmp) { - checksum_failed = true; - } else if (sig.type == SignalType::SUBARU_CHECKSUM && subaru_checksum(address, dat) != tmp) { - checksum_failed = true; - } else if (sig.type == SignalType::CHRYSLER_CHECKSUM && chrysler_checksum(address, dat) != tmp) { - checksum_failed = true; - } else if (sig.type == SignalType::HKG_CAN_FD_CHECKSUM && hkg_can_fd_checksum(address, dat) != tmp) { - checksum_failed = true; - } else if (sig.type == SignalType::PEDAL_CHECKSUM && pedal_checksum(dat) != tmp) { + if (sig.calc_checksum != nullptr && sig.calc_checksum(address, sig, dat) != tmp) { checksum_failed = true; } } bool counter_failed = false; if (!ignore_counter) { - if (sig.type == SignalType::HONDA_COUNTER || sig.type == SignalType::VOLKSWAGEN_COUNTER || sig.type == SignalType::PEDAL_COUNTER) { + if (sig.type == SignalType::COUNTER) { counter_failed = !update_counter_generic(tmp, sig.size); } } @@ -78,7 +66,7 @@ bool MessageState::parse(uint64_t sec, const std::vector &dat) { vals[i] = tmp * sig.factor + sig.offset; all_vals[i].push_back(vals[i]); } - seen = sec; + last_seen_nanos = sec; return true; } @@ -285,16 +273,19 @@ void CANParser::UpdateCans(uint64_t sec, const capnp::DynamicStruct::Reader& cms } void CANParser::UpdateValid(uint64_t sec) { - const bool show_missing = (last_sec - first_sec) > 2e9; + const bool show_missing = (last_sec - first_sec) > 8e9; can_valid = true; for (const auto& kv : message_states) { const auto& state = kv.second; - if (state.check_threshold > 0 && (sec - state.seen) > state.check_threshold) { - if (state.seen > 0) { - LOGE("0x%X TIMEOUT", state.address); - } else if (show_missing) { + + const bool missing = state.last_seen_nanos == 0; + const bool timed_out = (sec - state.last_seen_nanos) > state.check_threshold; + if (state.check_threshold > 0 && (missing || timed_out)) { + if (missing) { LOGE("0x%X MISSING", state.address); + } else if (show_missing) { + LOGE("0x%X TIMEOUT", state.address); } can_valid = false; } @@ -306,7 +297,7 @@ std::vector CANParser::query_latest() { for (auto& kv : message_states) { auto& state = kv.second; - if (last_sec != 0 && state.seen != last_sec) continue; + if (last_sec != 0 && state.last_seen_nanos != last_sec) continue; for (int i = 0; i < state.parse_sigs.size(); i++) { const Signal &sig = state.parse_sigs[i]; diff --git a/opendbc/chrysler_pacifica_2017_hybrid.dbc b/opendbc/chrysler_pacifica_2017_hybrid_generated.dbc similarity index 61% rename from opendbc/chrysler_pacifica_2017_hybrid.dbc rename to opendbc/chrysler_pacifica_2017_hybrid_generated.dbc index f8a053892..d5293c761 100644 --- a/opendbc/chrysler_pacifica_2017_hybrid.dbc +++ b/opendbc/chrysler_pacifica_2017_hybrid_generated.dbc @@ -1,47 +1,114 @@ -VERSION "" - - -NS_ : - NS_DESC_ - CM_ - BA_DEF_ - BA_ - VAL_ - CAT_DEF_ - CAT_ - FILTER - BA_DEF_DEF_ - EV_DATA_ - ENVVAR_DATA_ - SGTYPE_ - SGTYPE_VAL_ - BA_DEF_SGTYPE_ - BA_SGTYPE_ - SIG_TYPE_REF_ - VAL_TABLE_ - SIG_GROUP_ - SIG_VALTYPE_ - SIGTYPE_VALTYPE_ - BO_TX_BU_ - BA_DEF_REL_ - BA_REL_ - BA_DEF_DEF_REL_ - BU_SG_REL_ - BU_EV_REL_ - BU_BO_REL_ - SG_MUL_VAL_ - -BS_: - -BU_: XXX +CM_ "AUTOGENERATED FILE, DO NOT EDIT"; +CM_ "Imported file _stellantis_common.dbc starts here"; BO_ 258 STEERING: 8 XXX + SG_ STEER_ANGLE : 5|14@0+ (0.5,-2048) [-2048|2047] "deg" XXX + SG_ STEERING_RATE : 21|14@0+ (0.5,-2048) [-2048|2047] "deg/s" XXX + SG_ UNKNOWN_STEERING : 50|3@0+ (1,0) [0|15] "" XXX SG_ COUNTER : 55|4@0+ (1,0) [0|15] "" XXX SG_ CHECKSUM : 63|8@0+ (1,0) [0|255] "" XXX - SG_ UNKNOWN_STEERING : 50|3@0+ (1,0) [0|15] "" XXX - SG_ STEERING_RATE : 20|13@0+ (0.3187251,-1305.498) [0|8191] "deg/s" XXX - SG_ STEER_ANGLE : 4|13@0+ (0.3187251,-1307.888) [-360|360] "deg" XXX + +BO_ 284 ESP_8: 8 XXX + SG_ BRK_PRESSURE : 3|12@0+ (1,0) [0|1] "" XXX + SG_ BRAKE_PEDAL : 19|12@0+ (1,0) [0|1] "" XXX + SG_ Vehicle_Speed : 39|16@0+ (0.0078125,0) [0|511.984375] "km/h" XXX + SG_ COUNTER : 55|4@0+ (1,0) [0|15] "" XXX + SG_ CHECKSUM : 63|8@0+ (1,0) [0|255] "" XXX + +BO_ 320 ESP_1: 8 XXX + SG_ Brake_Pedal_State : 2|2@1+ (1,0) [0|0] "" XXX + SG_ Vehicle_Speed : 33|10@0+ (0.5,0) [0|511] "km/h" XXX + SG_ COUNTER : 55|4@0+ (1,0) [0|15] "" XXX + SG_ CHECKSUM : 63|8@0+ (1,0) [0|255] "" XXX + SG_ BRAKE_PRESSED_ACC : 6|1@0+ (1,0) [0|3] "" XXX + +BO_ 344 ESP_6: 8 XXX + SG_ WHEEL_SPEED_FL : 5|14@0+ (0.5,0) [0|8191] "rpm" XXX + SG_ WHEEL_SPEED_FR : 21|14@0+ (0.5,0) [0|8191] "rpm" XXX + SG_ WHEEL_SPEED_RL : 37|14@0+ (0.5,0) [0|8191] "rpm" XXX + SG_ WHEEL_SPEED_RR : 53|14@0+ (0.5,0) [0|8191] "rpm" XXX + +BO_ 464 ORC_1: 8 XXX + SG_ SEATBELT_DRIVER_UNLATCHED : 13|1@0+ (1,0) [0|1] "" XXX + +BO_ 500 DAS_3: 8 XXX + SG_ ENGINE_TORQUE_REQUEST : 4|13@0+ (0.25,-500) [-500|1547.5] "Nm" XXX + SG_ ENGINE_TORQUE_REQUEST_MAX : 7|1@0+ (1,0) [0|1] "" XXX + SG_ ACC_STANDSTILL : 5|1@0+ (1,0) [0|1] "" XXX + SG_ ACC_AVAILABLE : 20|1@0+ (1,0) [0|1] "" XXX + SG_ ACC_ACTIVE : 21|1@0+ (1,0) [0|1] "" XXX + SG_ ACC_FAULTED : 47|1@0+ (1,0) [0|1] "" XXX + SG_ BRAKE_MAYBE : 18|11@0+ (1,0) [0|255] "" XXX + SG_ BRAKE_BOOL_1 : 36|1@0+ (1,0) [0|3] "" XXX + SG_ COUNTER : 55|4@0+ (1,0) [0|15] "" XXX + SG_ CHECKSUM : 63|8@0+ (1,0) [0|255] "" XXX + +BO_ 501 DAS_4: 8 XXX + SG_ ACC_SET_SPEED_KPH : 15|8@0+ (1,0) [0|3] "km/h" XXX + SG_ ACC_SET_SPEED_MPH : 23|8@0+ (1,0) [0|3] "mph" XXX + SG_ ACC_DISTANCE_CONFIG_1 : 1|2@0+ (1,0) [0|3] "" XXX + SG_ ACC_DISTANCE_CONFIG_2 : 41|2@0+ (1,0) [0|3] "" XXX + SG_ SPEED_DIGITAL : 63|8@0+ (1,0) [0|255] "mph" XXX + SG_ ACC_STATE : 38|3@0+ (1,0) [0|7] "" XXX + +BO_ 544 EPS_2: 8 XXX + SG_ LKAS_STATE : 23|4@0+ (1,0) [0|15] "" XXX + SG_ COLUMN_TORQUE : 2|11@0+ (1,-1024) [-1024|1023] "" XXX + SG_ TORQUE_OVERLAY_STATUS : 6|4@0+ (1,0) [0|15] "" XXX + SG_ EPS_TORQUE_MOTOR_RAW : 19|12@0+ (1,-2048) [-2048|2047] "" XXX + SG_ EPS_TORQUE_MOTOR : 34|11@0+ (1,-1024) [-1024|1023] "" XXX + SG_ AUTO_PARK_HAS_CONTROL_2 : 51|1@0+ (1,0) [0|1] "" XXX + SG_ COUNTER : 55|4@0+ (1,0) [0|15] "" XXX + SG_ CHECKSUM : 63|8@0+ (1,0) [0|255] "" XXX + +BO_ 559 ECM_5: 8 XXX + SG_ Accelerator_Position : 0|8@1+ (0.4,0) [0|100] "%" XXX + SG_ COUNTER : 55|4@0+ (1,0) [0|15] "" XXX + SG_ CHECKSUM : 63|8@0+ (1,0) [0|255] "" XXX + +BO_ 571 CRUISE_BUTTONS: 3 XXX + SG_ ACC_Cancel : 0|1@1+ (1,0) [0|0] "" XXX + SG_ ACC_Distance_Dec : 1|1@1+ (1,0) [0|0] "" XXX + SG_ ACC_Accel : 2|1@1+ (1,0) [0|0] "" XXX + SG_ ACC_Decel : 3|1@1+ (1,0) [0|0] "" XXX + SG_ ACC_Resume : 4|1@0+ (1,0) [0|1] "" XXX + SG_ Cruise_OnOff : 6|1@1+ (1,0) [0|0] "" XXX + SG_ ACC_OnOff : 7|1@1+ (1,0) [0|0] "" XXX + SG_ ACC_Distance_Inc : 8|1@1+ (1,0) [0|0] "" XXX + SG_ COUNTER : 15|4@0+ (1,0) [0|15] "" XXX + SG_ CHECKSUM : 23|8@0+ (1,0) [0|255] "" XXX + +BO_ 678 DAS_6: 8 XXX + SG_ LKAS_ICON_COLOR : 1|2@0+ (1,0) [0|3] "" XXX + SG_ LKAS_LANE_LINES : 19|4@0+ (1,0) [0|1] "" XXX + SG_ LKAS_ALERTS : 27|4@0+ (1,0) [0|1] "" XXX + SG_ CAR_MODEL : 15|8@0+ (1,0) [0|255] "" XXX + SG_ AUTO_HIGH_BEAM_ON : 47|1@1+ (1,0) [0|0] "" XXX + +BO_ 720 BSM_1: 6 XXX + SG_ RIGHT_STATUS : 5|1@0+ (1,0) [0|1] "" XXX + SG_ LEFT_STATUS : 2|1@0+ (1,0) [0|1] "" XXX + +BO_ 792 STEERING_LEVERS: 8 XXX + SG_ TURN_SIGNALS : 0|2@1+ (1,0) [0|3] "" XXX + SG_ HIGH_BEAM_PRESSED : 2|1@0+ (1,0) [0|3] "" XXX + +BO_ 820 BCM_1: 8 XXX + SG_ DOOR_OPEN_FL : 17|1@0+ (1,0) [0|1] "" XXX + SG_ DOOR_OPEN_FR : 18|1@0+ (1,0) [0|1] "" XXX + SG_ DOOR_OPEN_RL : 19|1@0+ (1,0) [0|1] "" XXX + SG_ DOOR_OPEN_RR : 20|1@0+ (1,0) [0|1] "" XXX + SG_ DOOR_OPEN_TRUNK : 22|1@0+ (1,0) [0|1] "" XXX + SG_ TURN_LIGHT_LEFT : 31|1@0+ (1,0) [0|1] "" XXX + SG_ TURN_LIGHT_RIGHT : 30|1@0+ (1,0) [0|1] "" XXX + SG_ HIGH_BEAM_DISPLAY : 58|1@0+ (1,0) [0|1] "" XXX + +CM_ SG_ 678 LKAS_ICON_COLOR "3 is yellow, 2 is green, 1 is white, 0 is null"; +CM_ SG_ 678 LKAS_LANE_LINES "0x01 transparent lines, 0x02 left white, 0x03 right white, 0x04 left yellow with car on top, 0x05 left yellow with car on top, 0x06 both white, 0x07 left yellow, 0x08 left yellow right white, 0x09 right yellow, 0x0a right yellow left white, 0x0b left yellow with car on top right white, 0x0c right yellow with car on top left white, (0x00, 0x0d, 0x0e, 0x0f) null"; +CM_ SG_ 678 LKAS_ALERTS "(0x01, 0x02) lane sense off, (0x03, 0x04, 0x06) place hands on steering wheel, 0x07 lane departure detected + place hands on steering wheel, (0x08, 0x09) lane sense unavailable + clean front windshield, 0x0b lane sense and auto high beam unavailable + clean front windshield, 0x0c lane sense unavailable + service required, (0x00, 0x05, 0x0a, 0x0d, 0x0e, 0x0f) null"; + +CM_ "chrysler_pacifica_2017_hybrid.dbc starts here"; BO_ 514 SPEED_1: 8 XXX SG_ SPEED_LEFT : 7|12@0+ (0.071028,0) [0|65535] "m/s" XXX @@ -51,78 +118,25 @@ BO_ 653 BRAKE_MODULE: 2 XXX SG_ BRAKE_PRESSURE : 15|8@0+ (1,0) [0|255] "" XXX SG_ BRAKE_PRESSED : 4|1@0+ (1,0) [0|4] "" XXX -BO_ 820 DOORS: 8 XXX - SG_ DOOR_OPEN_FR : 18|1@0+ (1,0) [0|1] "" XXX - SG_ DOOR_OPEN_RL : 19|1@0+ (1,0) [0|1] "" XXX - SG_ DOOR_OPEN_RR : 20|1@0+ (1,0) [0|1] "" XXX - SG_ DOOR_OPEN_TRUNK : 22|1@0+ (1,0) [0|1] "" XXX - SG_ DOOR_OPEN_FL : 17|1@0+ (1,0) [0|1] "" XXX - SG_ TURN_LIGHT_LEFT : 31|1@0+ (1,0) [0|1] "" XXX - SG_ TURN_LIGHT_RIGHT : 30|1@0+ (1,0) [0|1] "" XXX - SG_ HIGH_BEAM_DISPLAY : 58|1@0+ (1,0) [0|1] "" XXX - BO_ 746 GEAR: 5 XXX SG_ PRNDL : 2|3@0+ (1,0) [0|7] "" XXX SG_ GEAR_CHECKSUM : 39|8@0+ (1,0) [0|255] "" XXX SG_ COUNTER : 31|4@0+ (1,0) [0|15] "" XXX -BO_ 284 BRAKE_1: 8 XXX - SG_ SPEED_RELATED_1 : 37|14@0+ (1,0) [0|255] "" XXX - SG_ BRAKE_RELATED_1_2 : 18|11@0+ (1,0) [0|255] "" XXX - SG_ BRAKE_RELATED_1_1 : 3|12@0+ (1,0) [0|255] "" XXX - SG_ COUNTER : 55|4@0+ (1,0) [0|15] "" XXX - SG_ CHECKSUM : 63|8@0+ (1,0) [0|255] "" XXX - -BO_ 320 BRAKE_2: 8 XXX - SG_ SPEED_RELATED_2 : 47|8@0+ (1,0) [0|63] "" XXX - SG_ COUNTER : 55|4@0+ (1,0) [0|15] "" XXX - SG_ CHECKSUM : 63|8@0+ (1,0) [0|255] "" XXX - SG_ BRAKE_PRESSED_2 : 2|3@0+ (1,0) [0|7] "" XXX - SG_ BRAKE_PRESSED_ACC : 6|1@0+ (1,0) [0|3] "" XXX - BO_ 736 TRIP: 8 XXX SG_ COUNTER : 7|16@0+ (1,0) [0|65535] "Meters" XXX SG_ COUNTER_2 : 23|16@0+ (1,0) [0|65535] "Meters" XXX -BO_ 344 WHEEL_SPEEDS: 8 XXX - SG_ WHEEL_SPEED_FL : 3|12@0+ (0.0189408,0) [0|65535] "m/s" XXX - SG_ WHEEL_SPEED_RR : 51|12@0+ (0.0189408,0) [0|255] "m/s" XXX - SG_ WHEEL_SPEED_RL : 35|12@0+ (0.0189408,0) [0|3] "m/s" XXX - SG_ WHEEL_SPEED_FR : 19|12@0+ (0.0189408,0) [0|255] "m/s" XXX - -BO_ 792 STEERING_LEVERS: 8 XXX - SG_ HIGH_BEAM_PUSHED_IN : 2|1@0+ (1,0) [0|3] "" XXX - SG_ TURN_SIGNALS : 1|2@0+ (1,0) [0|3] "" XXX - SG_ HIGH_BEAM_FLASH : 3|1@0+ (1,0) [0|3] "" XXX - BO_ 264 ACCEL_PEDAL_MSG: 8 XXX SG_ COUNTER : 55|4@0+ (1,0) [0|15] "" XXX SG_ CHECKSUM : 63|8@0+ (1,0) [0|255] "" XXX SG_ ACCEL_PEDAL : 35|1@0+ (1,0) [0|1] "" XXX -BO_ 464 SEATBELT_STATUS: 8 XXX - SG_ SEATBELT_DRIVER_UNLATCHED : 13|1@0+ (1,0) [0|1] "" XXX - -BO_ 544 EPS_STATUS: 8 XXX - SG_ LKAS_STATE : 23|4@0+ (1,0) [0|15] "" XXX - SG_ TORQUE_DRIVER : 2|11@0+ (1,-1024) [-1024|1023] "" XXX - SG_ TORQUE_MOTOR_RAW : 19|12@0+ (1,-2048) [-2048|2047] "" XXX - SG_ TORQUE_MOTOR : 34|11@0+ (1,-1024) [-1024|1023] "" XXX - SG_ AUTO_PARK_HAS_CONTROL_2 : 51|1@0+ (1,0) [0|1] "" XXX - SG_ COUNTER : 55|4@0+ (1,0) [0|15] "" XXX - SG_ CHECKSUM : 63|8@0+ (1,0) [0|255] "" XXX - BO_ 658 LKAS_COMMAND: 6 XXX SG_ COUNTER : 39|4@0+ (1,0) [0|15] "" XXX SG_ CHECKSUM : 47|8@0+ (1,0) [0|255] "" XXX - SG_ LKAS_STEERING_TORQUE : 2|11@0+ (1,-1024) [0|1] "" XXX - SG_ LKAS_HIGH_TORQUE : 4|1@0+ (1,0) [0|1] "" XXX - -BO_ 678 LKAS_HUD: 8 XXX - SG_ LKAS_ICON_COLOR : 1|2@0+ (1,0) [0|3] "" XXX - SG_ LKAS_LANE_LINES : 19|4@0+ (1,0) [0|1] "" XXX - SG_ LKAS_ALERTS : 27|4@0+ (1,0) [0|1] "" XXX - SG_ CAR_MODEL : 15|8@0+ (1,0) [0|255] "" XXX + SG_ STEERING_TORQUE : 2|11@0+ (1,-1024) [0|1] "" XXX + SG_ LKAS_CONTROL_BIT : 4|1@0+ (1,0) [0|1] "" XXX BO_ 705 AUTO_PARK_BUTTON: 8 XXX SG_ AUTO_PARK_TOGGLE_2 : 8|1@0+ (1,0) [0|1] "" XXX @@ -155,10 +169,6 @@ BO_ 332 STEERING_2: 8 XXX SG_ ENERGY_RELATED : 39|16@0+ (1,0) [0|65535] "" XXX SG_ STEER_ANGLE_2 : 7|13@0+ (0.3187251,-1307.888) [-360|360] "deg" XXX -BO_ 720 BLIND_SPOT_WARNINGS: 6 XXX - SG_ BLIND_SPOT_RIGHT : 5|1@0+ (1,0) [0|1] "" XXX - SG_ BLIND_SPOT_LEFT : 2|1@0+ (1,0) [0|1] "" XXX - BO_ 331 BRAKE_3: 8 XXX SG_ BRAKE_RELATED_3 : 7|16@0+ (1,0) [0|65535] "" XXX SG_ COUNTER : 55|4@0+ (1,0) [0|15] "" XXX @@ -175,12 +185,6 @@ BO_ 608 PARKSENSE_SIGNAL: 8 XXX BO_ 729 LKAS_HEARTBIT: 5 XXX SG_ LKAS_STATUS_OK : 31|16@0+ (1,0) [0|65535] "" XXX -BO_ 274 NEW_MSG_112: 2 XXX - -BO_ 290 NEW_MSG_122: 6 XXX - -BO_ 376 NEW_MSG_178: 3 XXX - BO_ 288 ACCEL_RELATED_120: 7 XXX SG_ COUNTER : 47|4@0+ (1,0) [0|15] "" XXX SG_ CHECKSUM : 55|8@0+ (1,0) [0|255] "" XXX @@ -190,49 +194,9 @@ BO_ 288 ACCEL_RELATED_120: 7 XXX BO_ 257 ACCEL_RELATED_101: 5 XXX SG_ ENERGY_OR_RPM : 31|8@0+ (1,0) [0|255] "" XXX -BO_ 388 NEW_MSG_184: 4 XXX - -BO_ 448 NEW_MSG_1c0: 6 XXX - -BO_ 456 NEW_MSG_1c8: 4 XXX - -BO_ 560 NEW_MSG_230: 4 XXX - -BO_ 564 NEW_MSG_234: 4 XXX - -BO_ 571 WHEEL_BUTTONS: 3 XXX - SG_ CHECKSUM : 23|8@0+ (1,0) [0|255] "" XXX - SG_ ACC_FOLLOW_DEC : 1|1@0+ (1,0) [0|3] "" XXX - SG_ ACC_SPEED_INC : 2|1@0+ (1,0) [0|255] "" XXX - SG_ ACC_SPEED_DEC : 3|1@0+ (1,0) [0|3] "" XXX - SG_ ACC_FOLLOW_INC : 8|1@0+ (1,0) [0|15] "" XXX - SG_ ACC_CANCEL : 0|1@0+ (1,0) [0|15] "" XXX - SG_ COUNTER : 15|4@0+ (1,0) [0|15] "" XXX - SG_ ACC_RESUME : 4|1@0+ (1,0) [0|15] "" XXX - -BO_ 669 NEW_MSG_29d: 3 XXX - SG_ COUNTER : 15|4@0+ (1,0) [0|15] "" XXX - SG_ CHECKSUM : 23|8@0+ (1,0) [0|255] "" XXX - BO_ 825 AUDIBLE_BEEP_339: 2 XXX SG_ BEEP_339 : 7|16@0+ (1,0) [0|65535] "" XXX -BO_ 856 NEW_MSG_358: 4 XXX - -BO_ 860 NEW_MSG_35c: 6 XXX - -BO_ 924 NEW_MSG_39c: 3 XXX - -BO_ 969 NEW_MSG_3c9: 4 XXX - -BO_ 974 NEW_MSG_3ce: 5 XXX - -BO_ 993 NEW_MSG_3e1: 7 XXX - -BO_ 838 NEW_MSG_346: 2 XXX - -BO_ 926 NEW_MSG_39e: 3 XXX - BO_ 168 ACCEL_RELATED_a8: 8 XXX SG_ ACCEL_RELATED : 23|16@0+ (1,0) [0|65535] "" XXX SG_ COUNTER : 55|4@0+ (1,0) [0|15] "" XXX @@ -257,8 +221,6 @@ BO_ 294 ENERGY_RELATED_126: 8 XXX SG_ UNKNOWN_126_2 : 35|12@0+ (1,0) [0|4095] "" XXX SG_ ENERGY_GAIN_LOSS_NOISY : 19|12@0+ (1,0) [0|2047] "" XXX -BO_ 300 NEW_MSG_12C: 8 XXX - BO_ 308 ACCEL_GAS_134: 8 XXX SG_ COUNTER : 55|4@0+ (1,0) [0|15] "" XXX SG_ ACCEL_134 : 46|7@0+ (1,0) [0|127] "" XXX @@ -267,11 +229,6 @@ BO_ 532 ENERGY_RELATED_214: 8 XXX SG_ NOISY_SLOWLY_DECREASING : 16|9@0+ (1,0) [0|255] "" XXX SG_ ENERGY_RELATED : 0|9@0+ (1,0) [0|255] "" XXX -BO_ 559 ACCEL_GAS_22F: 8 XXX - SG_ ACCEL_22F : 3|4@0+ (1,0) [0|255] "" XXX - SG_ COUNTER : 55|4@0+ (1,0) [0|15] "" XXX - SG_ CHECKSUM : 63|8@0+ (1,0) [0|255] "" XXX - BO_ 655 CHARGING_MAYBE_28F: 8 XXX SG_ CHARGING : 1|2@0+ (1,0) [0|3] "" XXX @@ -294,52 +251,9 @@ BO_ 878 ACCEL_RELATED_36E: 8 XXX BO_ 324 SPEED_2: 8 XXX SG_ SPEED_2 : 31|16@0+ (0.01,0) [0|255] "m/s" XXX -BO_ 501 DASHBOARD: 8 XXX - SG_ ACC_SPEED_CONFIG_KPH : 15|8@0+ (1,0) [0|3] "km/h" XXX - SG_ ACC_SPEED_CONFIG_MPH : 23|8@0+ (1,0) [0|3] "mph" XXX - SG_ ACC_DISTANCE_CONFIG_1 : 1|2@0+ (1,0) [0|3] "" XXX - SG_ ACC_DISTANCE_CONFIG_2 : 41|2@0+ (1,0) [0|3] "" XXX - SG_ SPEED_DIGITAL : 63|8@0+ (1,0) [0|255] "mph" XXX - SG_ CRUISE_STATE : 38|3@0+ (1,0) [0|7] "" XXX - -BO_ 639 NEW_MSG_27f: 8 XXX - SG_ INCREASING : 47|8@0+ (1,0) [0|255] "" XXX - -BO_ 701 NEW_MSG_2bd: 8 XXX - SG_ unknown_1 : 39|8@0+ (1,0) [0|255] "" XXX - BO_ 832 UNKNOWN_340: 8 XXX SG_ SPEED_DIGITAL : 63|8@0+ (1,0) [0|255] "mph" XXX -BO_ 848 UNKNOWN_350: 8 XXX - SG_ INCREASING_LSB : 5|6@0+ (1,0) [0|255] "" XXX - SG_ INCREASING_MSB : 12|5@0+ (1,0) [0|31] "" XXX - -BO_ 908 NEW_MSG_38c: 8 XXX - SG_ INCREASING_MSB : 44|5@0+ (1,0) [0|31] "" XXX - SG_ INCREASING_LSB : 61|6@0+ (1,0) [0|255] "" XXX - -BO_ 938 NEW_MSG_3aa: 8 XXX - SG_ INCREASING_UNKNOWN_1 : 39|8@0+ (1,0) [0|255] "" XXX - SG_ INCREASING_UNKNOWN_2 : 63|8@0+ (1,0) [0|255] "" XXX - -BO_ 940 NEW_MSG_3ac: 8 XXX - SG_ INCREASING_1 : 35|4@0+ (1,0) [0|15] "" XXX - SG_ INCREASING_2 : 63|8@0+ (1,0) [0|255] "" XXX - -BO_ 941 NEW_MSG_3ad: 8 XXX - SG_ INCREASING_1 : 36|5@0+ (1,0) [0|31] "" XXX - SG_ INCREASING_2 : 63|8@0+ (1,0) [0|255] "" XXX - -BO_ 500 ACC_2: 8 XXX - SG_ ACC_STATUS_1 : 7|3@0+ (1,0) [0|255] "" XXX - SG_ BRAKE_MAYBE : 18|11@0+ (1,0) [0|255] "" XXX - SG_ ACC_STATUS_2 : 21|3@0+ (1,0) [0|255] "" XXX - SG_ BRAKE_BOOL_1 : 36|1@0+ (1,0) [0|3] "" XXX - SG_ ACC_FAULTED : 47|1@0+ (1,0) [0|1] "" XXX - SG_ COUNTER : 55|4@0+ (1,0) [0|15] "" XXX - SG_ CHECKSUM : 63|8@0+ (1,0) [0|255] "" XXX - BO_ 625 ACC_1: 8 XXX SG_ SPEED : 31|8@0+ (1,0) [0|255] "km/h" XXX SG_ ACCEL_PERHAPS : 39|16@0+ (1,0) [0|255] "" XXX @@ -351,60 +265,26 @@ BO_ 268 ACC_10c: 8 XXX SG_ COUNTER : 55|4@0+ (1,0) [0|15] "" XXX SG_ CHECKSUM : 63|8@0+ (1,0) [0|255] "" XXX -BO_ 384 NEW_MSG_180: 8 XXX - SG_ NEW_SIGNAL_2 : 15|8@0+ (1,0) [0|255] "" XXX - SG_ NEW_SIGNAL_1 : 7|8@0+ (1,0) [0|255] "" XXX - SG_ NEW_SIGNAL_3 : 39|8@0+ (1,0) [0|3] "" XXX - -BO_ 853 NEW_MSG_355: 8 XXX - -BO_ 939 NEW_MSG_3ab: 8 XXX - -BO_ 512 NEW_MSG_200: 8 XXX - SG_ NEW_SIGNAL_1 : 23|8@0+ (1,0) [0|255] "" XXX - SG_ INCREASING : 27|12@0+ (1,0) [0|127] "" XXX - SG_ COUNTER : 51|4@0+ (1,0) [0|15] "" XXX - SG_ CHECKSUM : 63|8@0+ (1,0) [0|255] "" XXX - -CM_ SG_ 258 UNKNOWN_STEERING "never goes above 4. see if human-applied torque"; -CM_ SG_ 258 STEER_ANGLE "positive is left (counter-clockwise)"; -CM_ SG_ 514 SPEED_LEFT "TODO find upper limit"; CM_ SG_ 653 BRAKE_PRESSURE "max seems to be 148"; -CM_ SG_ 820 TURN_LIGHT_LEFT "oscillates with the light blinking"; -CM_ SG_ 820 TURN_LIGHT_RIGHT "hazard blinks both right and left lights"; CM_ SG_ 746 PRNDL "5=L, 4=D, 3=N, 2=R, 1=P"; -CM_ SG_ 746 GEAR_CHECKSUM "different than the LKAS checksum. unknown non-simple algorithm. just build a lookup table for it."; -CM_ SG_ 284 SPEED_RELATED_1 "Another Speed Signal, Maybe RPMs?"; -CM_ SG_ 284 BRAKE_RELATED_1_1 "Correlates with braking"; CM_ SG_ 320 BRAKE_PRESSED_2 "Value is 5 when brake is pressed by human, 1 when ACC brake"; CM_ SG_ 320 BRAKE_PRESSED_ACC "set when ACC brakes"; CM_ SG_ 792 TURN_SIGNALS "1=Left, 2=Right"; -CM_ SG_ 792 HIGH_BEAM_FLASH "use this for genericToggle"; CM_ SG_ 264 ACCEL_PEDAL "not in ACC so seems to be actual pedal. Use for gasPressed"; CM_ SG_ 544 LKAS_STATE "2 when autopark has control, 8 when is actuatable by lkas, 4 when there is a fault"; -CM_ SG_ 544 TORQUE_MOTOR_RAW "has larger range than TORQUE_MOTOR but ut seems biased"; -CM_ SG_ 658 COUNTER "each message increments, 0..f"; -CM_ SG_ 658 CHECKSUM "checksum calculated with https://gist.github.com/adhintz/94bf8d19b9075539f50172ab0fb24ba1"; CM_ SG_ 658 LKAS_STEERING_TORQUE "Most sent by stock system is 1024+-230. + is left. typically changes by 2 or 3 each 0.01s"; CM_ SG_ 678 LKAS_ICON_COLOR "3 is yellow, 2 is green, 1 is white, 0 is null"; CM_ SG_ 678 LKAS_LANE_LINES "0x01 transparent lines, 0x02 left white, 0x03 right white, 0x04 left yellow with car on top, 0x05 left yellow with car on top, 0x06 both white, 0x07 left yellow, 0x08 left yellow right white, 0x09 right yellow, 0x0a right yellow left white, 0x0b left yellow with car on top right white, 0x0c right yellow with car on top left white, (0x00, 0x0d, 0x0e, 0x0f) null"; CM_ SG_ 678 LKAS_ALERTS "(0x01, 0x02) lane sense off, (0x03, 0x04, 0x06) place hands on steering wheel, 0x07 lane departure detected + place hands on steering wheel, (0x08, 0x09) lane sense unavailable + clean front windshield, 0x0b lane sense and auto high beam unavailable + clean front windshield, 0x0c lane sense unavailable + service required, (0x00, 0x05, 0x0a, 0x0d, 0x0e, 0x0f) null"; CM_ SG_ 705 AUTO_PARK_TOGGLE_1 "set briefly when turning on or off self-parking"; -CM_ SG_ 705 INCREASING_UNKNOWN "sometimes decreasing"; CM_ SG_ 671 AUTO_PARK_CMD "Request Appears to be in NM"; CM_ SG_ 671 AUTO_PARK_STATUS "1 = IDLE / NO REQUEST 9 = START REQUEST 10 = REQUEST MODE 11 = REQUEST MODE"; -CM_ SG_ 784 INCREASING_UNKNOWN "perhaps distance traveled"; CM_ SG_ 826 AUTO_PARK_GEAR_1 "Reverse=0, Forward=f"; CM_ SG_ 826 AUTO_PARK_GEAR_2 "Reverse=0, Forward=f"; CM_ SG_ 826 AUTO_PARK_GEAR_3 "Reverse=0, Forward=f"; CM_ SG_ 332 STEER_ANGLE_2 "slightly lags the other steer_angle signal. also more noisy."; -CM_ SG_ 720 BLIND_SPOT_RIGHT "yellow triangle alert on side view mirror when a car is in your blind spot"; CM_ SG_ 608 PARKSENSE_DISABLED "set if parksense is disabled"; CM_ SG_ 729 LKAS_STATUS_OK "Set to 0x0820 when LKAS system is plugged in."; -CM_ SG_ 288 UNKNOWN_CHECKSUM "not the LKAS checksum"; -CM_ SG_ 288 GAS_ENGINE_RPM_MAYBE "lags acceleration, perhaps gas engine"; -CM_ SG_ 257 ENERGY_OR_RPM "perhaps energy consumption or RPMs"; -CM_ SG_ 571 CHECKSUM "standard checksum"; CM_ SG_ 825 BEEP_339 "sent every 0.5s. 0050 is no beep. To beep send 4355 or 4155. Used by ParkSense warning."; CM_ SG_ 270 ELECTRIC_MOTOR "0x7fff indicates electric motor not in use"; CM_ SG_ 291 ENERGY_GAIN_LOSS "unsure what this actually is"; @@ -416,19 +296,8 @@ CM_ SG_ 816 TOGGLE_PARKSENSE "sending 3000071ec0ff9000 enables or disables parks CM_ SG_ 324 SPEED_2 "signal is approx half other speeds"; CM_ SG_ 501 ACC_SPEED_CONFIG_KPH "speed configured for ACC"; CM_ SG_ 501 ACC_SPEED_CONFIG_MPH "speed configured for ACC"; -CM_ SG_ 639 INCREASING "perhaps number of seconds divided by two for this drive"; -CM_ SG_ 848 INCREASING_LSB "lower part of time counter"; -CM_ SG_ 848 INCREASING_MSB "upper part of time counter"; -CM_ SG_ 908 INCREASING_MSB "time based"; -CM_ SG_ 500 ACC_STATUS_1 "2 briefly (9 packets) when ACC goes to green, 1 help when ACC coming to a stop and at a stop"; -CM_ SG_ 500 BRAKE_MAYBE "2046 in non-ACC and non-decel. Signal on deceleration. 818 for already stopped break."; -CM_ SG_ 500 ACC_STATUS_2 "set to 1 when ACC not available, 3 when ACC available (white icon), and 7 when ACC active (green icon)"; -CM_ SG_ 500 BRAKE_BOOL_1 "set to 1 when ACC decel. 0 on non-ACC and accel."; CM_ SG_ 501 CRUISE_STATE "may just be an icon, but seems to indicate different cruise modes: ACC and Non-ACC and engaged state for both."; CM_ SG_ 625 SPEED "zero on non-acc drives"; -CM_ SG_ 625 ACCEL_PERHAPS "set to 7767 on non-ACC drives. ACC drive 40k is constant speed, 42k is accelerating"; -CM_ SG_ 268 BRAKE_PERHAPS "triggers only on ACC braking"; -CM_ SG_ 384 NEW_SIGNAL_1 "set in ACC gas driving. not set in electric human. not sure about gas human driving."; VAL_ 501 CRUISE_STATE 0 "Off" 1 "CC On" 2 "CC Engaged" 3 "ACC On" 4 "ACC Engaged"; VAL_ 746 PRNDL 5 "L" 4 "D" 3 "N" 2 "R" 1 "P"; diff --git a/opendbc/chrysler_ram_dt_generated.dbc b/opendbc/chrysler_ram_dt_generated.dbc new file mode 100644 index 000000000..71105b733 --- /dev/null +++ b/opendbc/chrysler_ram_dt_generated.dbc @@ -0,0 +1,177 @@ +CM_ "AUTOGENERATED FILE, DO NOT EDIT"; + + +CM_ "Imported file _stellantis_common_ram.dbc starts here"; +CM_ "Generated from _stellantis_common.dbc" + +BO_ 35 STEERING: 8 XXX + SG_ STEER_ANGLE : 5|14@0+ (0.5,-2048) [-2048|2047] "deg" XXX + SG_ STEERING_RATE : 21|14@0+ (0.5,-2048) [-2048|2047] "deg/s" XXX + SG_ UNKNOWN_STEERING : 50|3@0+ (1,0) [0|15] "" XXX + SG_ COUNTER : 55|4@0+ (1,0) [0|15] "" XXX + SG_ CHECKSUM : 63|8@0+ (1,0) [0|255] "" XXX + +BO_ 121 ESP_8: 8 XXX + SG_ BRK_PRESSURE : 3|12@0+ (1,0) [0|1] "" XXX + SG_ BRAKE_PEDAL : 19|12@0+ (1,0) [0|1] "" XXX + SG_ Vehicle_Speed : 39|16@0+ (0.0078125,0) [0|511.984375] "km/h" XXX + SG_ COUNTER : 55|4@0+ (1,0) [0|15] "" XXX + SG_ CHECKSUM : 63|8@0+ (1,0) [0|255] "" XXX + +BO_ 131 ESP_1: 8 XXX + SG_ Brake_Pedal_State : 2|2@1+ (1,0) [0|0] "" XXX + SG_ Vehicle_Speed : 33|10@0+ (0.5,0) [0|511] "km/h" XXX + SG_ COUNTER : 55|4@0+ (1,0) [0|15] "" XXX + SG_ CHECKSUM : 63|8@0+ (1,0) [0|255] "" XXX + SG_ BRAKE_PRESSED_ACC : 6|1@0+ (1,0) [0|3] "" XXX + +BO_ 139 ESP_6: 8 XXX + SG_ WHEEL_SPEED_FL : 5|14@0+ (0.5,0) [0|8191] "rpm" XXX + SG_ WHEEL_SPEED_FR : 21|14@0+ (0.5,0) [0|8191] "rpm" XXX + SG_ WHEEL_SPEED_RL : 37|14@0+ (0.5,0) [0|8191] "rpm" XXX + SG_ WHEEL_SPEED_RR : 53|14@0+ (0.5,0) [0|8191] "rpm" XXX + +BO_ 464 ORC_1: 8 XXX + SG_ SEATBELT_DRIVER_UNLATCHED : 13|1@0+ (1,0) [0|1] "" XXX + +BO_ 153 DAS_3: 8 XXX + SG_ ENGINE_TORQUE_REQUEST : 4|13@0+ (0.25,-500) [-500|1547.5] "Nm" XXX + SG_ ENGINE_TORQUE_REQUEST_MAX : 7|1@0+ (1,0) [0|1] "" XXX + SG_ ACC_STANDSTILL : 5|1@0+ (1,0) [0|1] "" XXX + SG_ ACC_AVAILABLE : 20|1@0+ (1,0) [0|1] "" XXX + SG_ ACC_ACTIVE : 21|1@0+ (1,0) [0|1] "" XXX + SG_ ACC_FAULTED : 47|1@0+ (1,0) [0|1] "" XXX + SG_ BRAKE_MAYBE : 18|11@0+ (1,0) [0|255] "" XXX + SG_ BRAKE_BOOL_1 : 36|1@0+ (1,0) [0|3] "" XXX + SG_ COUNTER : 55|4@0+ (1,0) [0|15] "" XXX + SG_ CHECKSUM : 63|8@0+ (1,0) [0|255] "" XXX + +BO_ 232 DAS_4: 8 XXX + SG_ ACC_SET_SPEED_KPH : 15|8@0+ (1,0) [0|3] "km/h" XXX + SG_ ACC_SET_SPEED_MPH : 23|8@0+ (1,0) [0|3] "mph" XXX + SG_ ACC_DISTANCE_CONFIG_1 : 1|2@0+ (1,0) [0|3] "" XXX + SG_ ACC_DISTANCE_CONFIG_2 : 41|2@0+ (1,0) [0|3] "" XXX + SG_ SPEED_DIGITAL : 63|8@0+ (1,0) [0|255] "mph" XXX + SG_ ACC_STATE : 38|3@0+ (1,0) [0|7] "" XXX + +BO_ 49 EPS_2: 8 XXX + SG_ LKAS_STATE : 23|4@0+ (1,0) [0|15] "" XXX + SG_ COLUMN_TORQUE : 2|11@0+ (1,-1024) [-1024|1023] "" XXX + SG_ TORQUE_OVERLAY_STATUS : 6|4@0+ (1,0) [0|15] "" XXX + SG_ EPS_TORQUE_MOTOR_RAW : 19|12@0+ (1,-2048) [-2048|2047] "" XXX + SG_ EPS_TORQUE_MOTOR : 34|11@0+ (1,-1024) [-1024|1023] "" XXX + SG_ AUTO_PARK_HAS_CONTROL_2 : 51|1@0+ (1,0) [0|1] "" XXX + SG_ COUNTER : 55|4@0+ (1,0) [0|15] "" XXX + SG_ CHECKSUM : 63|8@0+ (1,0) [0|255] "" XXX + +BO_ 157 ECM_5: 8 XXX + SG_ Accelerator_Position : 0|8@1+ (0.4,0) [0|100] "%" XXX + SG_ COUNTER : 55|4@0+ (1,0) [0|15] "" XXX + SG_ CHECKSUM : 63|8@0+ (1,0) [0|255] "" XXX + +BO_ 177 CRUISE_BUTTONS: 3 XXX + SG_ ACC_Cancel : 0|1@1+ (1,0) [0|0] "" XXX + SG_ ACC_Distance_Dec : 1|1@1+ (1,0) [0|0] "" XXX + SG_ ACC_Accel : 2|1@1+ (1,0) [0|0] "" XXX + SG_ ACC_Decel : 3|1@1+ (1,0) [0|0] "" XXX + SG_ ACC_Resume : 4|1@0+ (1,0) [0|1] "" XXX + SG_ Cruise_OnOff : 6|1@1+ (1,0) [0|0] "" XXX + SG_ ACC_OnOff : 7|1@1+ (1,0) [0|0] "" XXX + SG_ ACC_Distance_Inc : 8|1@1+ (1,0) [0|0] "" XXX + SG_ COUNTER : 15|4@0+ (1,0) [0|15] "" XXX + SG_ CHECKSUM : 23|8@0+ (1,0) [0|255] "" XXX + +BO_ 250 DAS_6: 8 XXX + SG_ LKAS_ICON_COLOR : 1|2@0+ (1,0) [0|3] "" XXX + SG_ LKAS_LANE_LINES : 19|4@0+ (1,0) [0|1] "" XXX + SG_ LKAS_ALERTS : 27|4@0+ (1,0) [0|1] "" XXX + SG_ CAR_MODEL : 15|8@0+ (1,0) [0|255] "" XXX + SG_ AUTO_HIGH_BEAM_ON : 47|1@1+ (1,0) [0|0] "" XXX + +BO_ 720 BSM_1: 6 XXX + SG_ RIGHT_STATUS : 5|1@0+ (1,0) [0|1] "" XXX + SG_ LEFT_STATUS : 2|1@0+ (1,0) [0|1] "" XXX + +BO_ 792 STEERING_LEVERS: 8 XXX + SG_ TURN_SIGNALS : 0|2@1+ (1,0) [0|3] "" XXX + SG_ HIGH_BEAM_PRESSED : 2|1@0+ (1,0) [0|3] "" XXX + +BO_ 657 BCM_1: 8 XXX + SG_ DOOR_OPEN_FL : 17|1@0+ (1,0) [0|1] "" XXX + SG_ DOOR_OPEN_FR : 18|1@0+ (1,0) [0|1] "" XXX + SG_ DOOR_OPEN_RL : 19|1@0+ (1,0) [0|1] "" XXX + SG_ DOOR_OPEN_RR : 20|1@0+ (1,0) [0|1] "" XXX + SG_ DOOR_OPEN_TRUNK : 22|1@0+ (1,0) [0|1] "" XXX + SG_ TURN_LIGHT_LEFT : 31|1@0+ (1,0) [0|1] "" XXX + SG_ TURN_LIGHT_RIGHT : 30|1@0+ (1,0) [0|1] "" XXX + SG_ HIGH_BEAM_DISPLAY : 58|1@0+ (1,0) [0|1] "" XXX + +CM_ SG_ 678 LKAS_ICON_COLOR "3 is yellow, 2 is green, 1 is white, 0 is null"; +CM_ SG_ 678 LKAS_LANE_LINES "0x01 transparent lines, 0x02 left white, 0x03 right white, 0x04 left yellow with car on top, 0x05 left yellow with car on top, 0x06 both white, 0x07 left yellow, 0x08 left yellow right white, 0x09 right yellow, 0x0a right yellow left white, 0x0b left yellow with car on top right white, 0x0c right yellow with car on top left white, (0x00, 0x0d, 0x0e, 0x0f) null"; +CM_ SG_ 678 LKAS_ALERTS "(0x01, 0x02) lane sense off, (0x03, 0x04, 0x06) place hands on steering wheel, 0x07 lane departure detected + place hands on steering wheel, (0x08, 0x09) lane sense unavailable + clean front windshield, 0x0b lane sense and auto high beam unavailable + clean front windshield, 0x0c lane sense unavailable + service required, (0x00, 0x05, 0x0a, 0x0d, 0x0e, 0x0f) null"; + +CM_ "chrysler_ram_dt.dbc starts here"; + +BO_ 37 PCM_1: 8 XXX + SG_ ENGINE_RPM : 7|16@0+ (1,0) [0|1] "" XXX + SG_ ENGINE_TRQ_REQ : 23|12@0+ (1,0) [0|1] "" XXX + SG_ COUNTER : 55|4@0+ (1,0) [0|15] "" XXX + SG_ CHECKSUM : 63|8@0+ (1,0) [0|255] "" XXX + +BO_ 53 PCM_2: 8 XXX + SG_ ENG_TORQUE_REQ : 3|12@0+ (1,0) [0|7] "" XXX + SG_ ENG_TORQUE_OUT : 19|12@0+ (1,0) [0|1] "" XXX + SG_ CHECKSUM : 63|8@0+ (1,0) [0|255] "" XXX + SG_ COUNTER : 55|4@0+ (1,0) [0|15] "" XXX + +BO_ 133 TCM_1: 8 XXX + SG_ DESIRED_GEAR : 15|4@0+ (1,0) [0|1] "" XXX + SG_ SHIFT_PENDING : 2|1@0+ (1,0) [0|1] "" XXX + SG_ SPEED_TURBINE : 27|12@0+ (0.04,0) [0|1] "km/h" XXX + SG_ ACTUAL_GEAR : 11|4@0+ (1,0) [0|15] "" XXX + +BO_ 135 ABS_2: 8 XXX + SG_ COUNTER : 55|4@0+ (1,0) [0|1] "" XXX + SG_ CHECKSUM : 63|8@0+ (1,0) [0|1] "" XXX + SG_ DRIVER_BRAKE : 15|8@0+ (1,0) [0|1] "" XXX + +BO_ 137 ESP_4: 8 XXX + SG_ Yaw_Rate : 7|16@0+ (0.01,-327.68) [-327.68|327.66] "deg/s" XXX + SG_ Acceleration : 32|8@1+ (0.08,-10.24) [-10.24|10.08] "m/s2" XXX + +BO_ 147 Transmission_Status: 8 XXX + SG_ Gear_State : 2|3@1+ (1,0) [0|15] "" XXX + SG_ COUNTER : 55|4@0+ (1,0) [0|15] "" XXX + SG_ CHECKSUM : 63|8@0+ (1,0) [0|255] "" XXX + +BO_ 164 EPS_3: 8 XXX + SG_ DASM_FAULT : 34|1@0+ (1,0) [0|1] "" XXX + SG_ Activation_Status : 48|3@1+ (1,0) [0|1] "" XXX + SG_ Driver_Override : 35|1@0+ (1,0) [0|1] "" XXX + SG_ Hands_on_Wheel : 51|1@0+ (1,0) [0|1] "" XXX + SG_ COUNTER : 55|4@0+ (1,0) [0|15] "" XXX + SG_ CHECKSUM : 63|8@0+ (1,0) [0|255] "" XXX + +BO_ 166 LKAS_COMMAND: 8 XXX + SG_ STEERING_TORQUE : 10|11@0+ (1,-1024) [0|1] "" XXX + SG_ LKAS_CONTROL_BIT : 24|3@1+ (1,0) [0|1] "" XXX + SG_ DASM_FAULT : 51|1@0+ (1,0) [0|1] "" XXX + SG_ COUNTER : 55|4@0+ (1,0) [0|15] "" XXX + SG_ CHECKSUM : 63|8@0+ (1,0) [0|255] "" XXX + +BO_ 221 Center_Stack_1: 8 XXX + SG_ LKAS_Button : 53|1@1+ (1,0) [0|0] "" XXX + SG_ Traction_Button : 54|1@0+ (1,0) [0|1] "" XXX + +BO_ 650 Center_Stack_2: 8 XXX + SG_ LKAS_Button : 57|1@1+ (1,0) [0|0] "" XXX + + +CM_ SG_ 133 ACTUAL_GEAR "0xd = P, 0x1-8 = D (actual gear), 0xb = R or N?? TODO find R vs N"; +CM_ SG_ 153 ACC_Engaged "SENT BY FORWARD CAMERA 1 = ACTIVE, 3 = ENGAGED, 0 = DISENGAGED/OFF"; +CM_ SG_ 166 LKAS_CONTROL_BIT "0=IDLE, 1=HAS 2=LKAS 3=ABSD, 4=TJA, 7=SNA"; +CM_ SG_ 250 Auto_High_Beam "1 = HIGH BEAMS OK 0 = HIGH BEAMS OFF "; +CM_ SG_ 250 LKAS_LANE_LINES "9 = LEFT CAUTION, 11 = VERY LEFT CAUTION 10 = RIGHT CAUTION, 14 = VERY RIGHT, 4 = NO LINES DETECTED, 3 = LINES DETECTED, SYSTEM ACTIVE"; +CM_ SG_ 464 Driver_Seatbelt_Status "1 unbuckled 0 buckled"; +CM_ SG_ 792 High_Beam_Lever_Status "1 is high beam, 0 reg"; +VAL_ 147 Gear_State 4 "D" 2 "N" 1 "R" 0 "P" ; diff --git a/opendbc/ford_fusion_2018_adas.dbc b/opendbc/ford_fusion_2018_adas.dbc deleted file mode 100644 index 2e1647b3d..000000000 --- a/opendbc/ford_fusion_2018_adas.dbc +++ /dev/null @@ -1,421 +0,0 @@ -VERSION "" - - -NS_ : - NS_DESC_ - CM_ - BA_DEF_ - BA_ - VAL_ - CAT_DEF_ - CAT_ - FILTER - BA_DEF_DEF_ - EV_DATA_ - ENVVAR_DATA_ - SGTYPE_ - SGTYPE_VAL_ - BA_DEF_SGTYPE_ - BA_SGTYPE_ - SIG_TYPE_REF_ - VAL_TABLE_ - SIG_GROUP_ - SIG_VALTYPE_ - SIGTYPE_VALTYPE_ - BO_TX_BU_ - BA_DEF_REL_ - BA_REL_ - BA_DEF_DEF_REL_ - BU_SG_REL_ - BU_EV_REL_ - BU_BO_REL_ - SG_MUL_VAL_ - -BS_: - -BU_: XXX - -BO_ 1280 Object_00: 8 XXX - SG_ X_Rel : 18|11@0+ (0.1,0) [0|0] "m" XXX - SG_ V_Rel : 52|13@0- (0.01,0) [0|0] "m/s" XXX - SG_ A_Rel : 33|10@0- (0.05,0) [0|0] "m/s2" XXX - SG_ Angle : 12|10@0- (-0.1,0) [0|0] "deg" XXX - -BO_ 1281 Object_01: 8 XXX - SG_ X_Rel : 18|11@0+ (0.1,0) [0|0] "m" XXX - SG_ V_Rel : 52|13@0- (0.01,0) [0|0] "m/s" XXX - SG_ A_Rel : 33|10@0- (0.05,0) [0|0] "m/s2" XXX - SG_ Angle : 12|10@0- (-0.1,0) [0|0] "deg" XXX - -BO_ 1282 Object_02: 8 XXX - SG_ X_Rel : 18|11@0+ (0.1,0) [0|0] "m" XXX - SG_ V_Rel : 52|13@0- (0.01,0) [0|0] "m/s" XXX - SG_ A_Rel : 33|10@0- (0.05,0) [0|0] "m/s2" XXX - SG_ Angle : 12|10@0- (-0.1,0) [0|0] "deg" XXX - -BO_ 1283 Object_03: 8 XXX - SG_ X_Rel : 18|11@0+ (0.1,0) [0|0] "m" XXX - SG_ V_Rel : 52|13@0- (0.01,0) [0|0] "m/s" XXX - SG_ A_Rel : 33|10@0- (0.05,0) [0|0] "m/s2" XXX - SG_ Angle : 12|10@0- (-0.1,0) [0|0] "deg" XXX - -BO_ 1284 Object_04: 8 XXX - SG_ X_Rel : 18|11@0+ (0.1,0) [0|0] "m" XXX - SG_ V_Rel : 52|13@0- (0.01,0) [0|0] "m/s" XXX - SG_ A_Rel : 33|10@0- (0.05,0) [0|0] "m/s2" XXX - SG_ Angle : 12|10@0- (-0.1,0) [0|0] "deg" XXX - -BO_ 1285 Object_05: 8 XXX - SG_ X_Rel : 18|11@0+ (0.1,0) [0|0] "m" XXX - SG_ V_Rel : 52|13@0- (0.01,0) [0|0] "m/s" XXX - SG_ A_Rel : 33|10@0- (0.05,0) [0|0] "m/s2" XXX - SG_ Angle : 12|10@0- (-0.1,0) [0|0] "deg" XXX - -BO_ 1286 Object_06: 8 XXX - SG_ X_Rel : 18|11@0+ (0.1,0) [0|0] "m" XXX - SG_ V_Rel : 52|13@0- (0.01,0) [0|0] "m/s" XXX - SG_ A_Rel : 33|10@0- (0.05,0) [0|0] "m/s2" XXX - SG_ Angle : 12|10@0- (-0.1,0) [0|0] "deg" XXX - -BO_ 1287 Object_07: 8 XXX - SG_ X_Rel : 18|11@0+ (0.1,0) [0|0] "m" XXX - SG_ V_Rel : 52|13@0- (0.01,0) [0|0] "m/s" XXX - SG_ A_Rel : 33|10@0- (0.05,0) [0|0] "m/s2" XXX - SG_ Angle : 12|10@0- (-0.1,0) [0|0] "deg" XXX - -BO_ 1288 Object_08: 8 XXX - SG_ X_Rel : 18|11@0+ (0.1,0) [0|0] "m" XXX - SG_ V_Rel : 52|13@0- (0.01,0) [0|0] "m/s" XXX - SG_ A_Rel : 33|10@0- (0.05,0) [0|0] "m/s2" XXX - SG_ Angle : 12|10@0- (-0.1,0) [0|0] "deg" XXX - -BO_ 1289 Object_09: 8 XXX - SG_ X_Rel : 18|11@0+ (0.1,0) [0|0] "m" XXX - SG_ V_Rel : 52|13@0- (0.01,0) [0|0] "m/s" XXX - SG_ A_Rel : 33|10@0- (0.05,0) [0|0] "m/s2" XXX - SG_ Angle : 12|10@0- (-0.1,0) [0|0] "deg" XXX - -BO_ 1290 Object_10: 8 XXX - SG_ X_Rel : 18|11@0+ (0.1,0) [0|0] "m" XXX - SG_ V_Rel : 52|13@0- (0.01,0) [0|0] "m/s" XXX - SG_ A_Rel : 33|10@0- (0.05,0) [0|0] "m/s2" XXX - SG_ Angle : 12|10@0- (-0.1,0) [0|0] "deg" XXX - -BO_ 1291 Object_11: 8 XXX - SG_ X_Rel : 18|11@0+ (0.1,0) [0|0] "m" XXX - SG_ V_Rel : 52|13@0- (0.01,0) [0|0] "m/s" XXX - SG_ A_Rel : 33|10@0- (0.05,0) [0|0] "m/s2" XXX - SG_ Angle : 12|10@0- (-0.1,0) [0|0] "deg" XXX - -BO_ 1292 Object_12: 8 XXX - SG_ X_Rel : 18|11@0+ (0.1,0) [0|0] "m" XXX - SG_ V_Rel : 52|13@0- (0.01,0) [0|0] "m/s" XXX - SG_ A_Rel : 33|10@0- (0.05,0) [0|0] "m/s2" XXX - SG_ Angle : 12|10@0- (-0.1,0) [0|0] "deg" XXX - -BO_ 1293 Object_13: 8 XXX - SG_ X_Rel : 18|11@0+ (0.1,0) [0|0] "m" XXX - SG_ V_Rel : 52|13@0- (0.01,0) [0|0] "m/s" XXX - SG_ A_Rel : 33|10@0- (0.05,0) [0|0] "m/s2" XXX - SG_ Angle : 12|10@0- (-0.1,0) [0|0] "deg" XXX - -BO_ 1294 Object_14: 8 XXX - SG_ X_Rel : 18|11@0+ (0.1,0) [0|0] "m" XXX - SG_ V_Rel : 52|13@0- (0.01,0) [0|0] "m/s" XXX - SG_ A_Rel : 33|10@0- (0.05,0) [0|0] "m/s2" XXX - SG_ Angle : 12|10@0- (-0.1,0) [0|0] "deg" XXX - -BO_ 1295 Object_15: 8 XXX - SG_ X_Rel : 18|11@0+ (0.1,0) [0|0] "m" XXX - SG_ V_Rel : 52|13@0- (0.01,0) [0|0] "m/s" XXX - SG_ A_Rel : 33|10@0- (0.05,0) [0|0] "m/s2" XXX - SG_ Angle : 12|10@0- (-0.1,0) [0|0] "deg" XXX - -BO_ 1296 Object_16: 8 XXX - SG_ X_Rel : 18|11@0+ (0.1,0) [0|0] "m" XXX - SG_ V_Rel : 52|13@0- (0.01,0) [0|0] "m/s" XXX - SG_ A_Rel : 33|10@0- (0.05,0) [0|0] "m/s2" XXX - SG_ Angle : 12|10@0- (-0.1,0) [0|0] "deg" XXX - -BO_ 1297 Object_17: 8 XXX - SG_ X_Rel : 18|11@0+ (0.1,0) [0|0] "m" XXX - SG_ V_Rel : 52|13@0- (0.01,0) [0|0] "m/s" XXX - SG_ A_Rel : 33|10@0- (0.05,0) [0|0] "m/s2" XXX - SG_ Angle : 12|10@0- (-0.1,0) [0|0] "deg" XXX - -BO_ 1298 Object_18: 8 XXX - SG_ X_Rel : 18|11@0+ (0.1,0) [0|0] "m" XXX - SG_ V_Rel : 52|13@0- (0.01,0) [0|0] "m/s" XXX - SG_ A_Rel : 33|10@0- (0.05,0) [0|0] "m/s2" XXX - SG_ Angle : 12|10@0- (-0.1,0) [0|0] "deg" XXX - -BO_ 1299 Object_19: 8 XXX - SG_ X_Rel : 18|11@0+ (0.1,0) [0|0] "m" XXX - SG_ V_Rel : 52|13@0- (0.01,0) [0|0] "m/s" XXX - SG_ A_Rel : 33|10@0- (0.05,0) [0|0] "m/s2" XXX - SG_ Angle : 12|10@0- (-0.1,0) [0|0] "deg" XXX - -BO_ 1300 Object_20: 8 XXX - SG_ X_Rel : 18|11@0+ (0.1,0) [0|0] "m" XXX - SG_ V_Rel : 52|13@0- (0.01,0) [0|0] "m/s" XXX - SG_ A_Rel : 33|10@0- (0.05,0) [0|0] "m/s2" XXX - SG_ Angle : 12|10@0- (-0.1,0) [0|0] "deg" XXX - -BO_ 1301 Object_21: 8 XXX - SG_ X_Rel : 18|11@0+ (0.1,0) [0|0] "m" XXX - SG_ V_Rel : 52|13@0- (0.01,0) [0|0] "m/s" XXX - SG_ A_Rel : 33|10@0- (0.05,0) [0|0] "m/s2" XXX - SG_ Angle : 12|10@0- (-0.1,0) [0|0] "deg" XXX - -BO_ 1302 Object_22: 8 XXX - SG_ X_Rel : 18|11@0+ (0.1,0) [0|0] "m" XXX - SG_ V_Rel : 52|13@0- (0.01,0) [0|0] "m/s" XXX - SG_ A_Rel : 33|10@0- (0.05,0) [0|0] "m/s2" XXX - SG_ Angle : 12|10@0- (-0.1,0) [0|0] "deg" XXX - -BO_ 1303 Object_23: 8 XXX - SG_ X_Rel : 18|11@0+ (0.1,0) [0|0] "m" XXX - SG_ V_Rel : 52|13@0- (0.01,0) [0|0] "m/s" XXX - SG_ A_Rel : 33|10@0- (0.05,0) [0|0] "m/s2" XXX - SG_ Angle : 12|10@0- (-0.1,0) [0|0] "deg" XXX - -BO_ 1304 Object_24: 8 XXX - SG_ X_Rel : 18|11@0+ (0.1,0) [0|0] "m" XXX - SG_ V_Rel : 52|13@0- (0.01,0) [0|0] "m/s" XXX - SG_ A_Rel : 33|10@0- (0.05,0) [0|0] "m/s2" XXX - SG_ Angle : 12|10@0- (-0.1,0) [0|0] "deg" XXX - -BO_ 1305 Object_25: 8 XXX - SG_ X_Rel : 18|11@0+ (0.1,0) [0|0] "m" XXX - SG_ V_Rel : 52|13@0- (0.01,0) [0|0] "m/s" XXX - SG_ A_Rel : 33|10@0- (0.05,0) [0|0] "m/s2" XXX - SG_ Angle : 12|10@0- (-0.1,0) [0|0] "deg" XXX - -BO_ 1306 Object_26: 8 XXX - SG_ X_Rel : 18|11@0+ (0.1,0) [0|0] "m" XXX - SG_ V_Rel : 52|13@0- (0.01,0) [0|0] "m/s" XXX - SG_ A_Rel : 33|10@0- (0.05,0) [0|0] "m/s2" XXX - SG_ Angle : 12|10@0- (-0.1,0) [0|0] "deg" XXX - -BO_ 1307 Object_27: 8 XXX - SG_ X_Rel : 18|11@0+ (0.1,0) [0|0] "m" XXX - SG_ V_Rel : 52|13@0- (0.01,0) [0|0] "m/s" XXX - SG_ A_Rel : 33|10@0- (0.05,0) [0|0] "m/s2" XXX - SG_ Angle : 12|10@0- (-0.1,0) [0|0] "deg" XXX - -BO_ 1308 Object_28: 8 XXX - SG_ X_Rel : 18|11@0+ (0.1,0) [0|0] "m" XXX - SG_ V_Rel : 52|13@0- (0.01,0) [0|0] "m/s" XXX - SG_ A_Rel : 33|10@0- (0.05,0) [0|0] "m/s2" XXX - SG_ Angle : 12|10@0- (-0.1,0) [0|0] "deg" XXX - -BO_ 1309 Object_29: 8 XXX - SG_ X_Rel : 18|11@0+ (0.1,0) [0|0] "m" XXX - SG_ V_Rel : 52|13@0- (0.01,0) [0|0] "m/s" XXX - SG_ A_Rel : 33|10@0- (0.05,0) [0|0] "m/s2" XXX - SG_ Angle : 12|10@0- (-0.1,0) [0|0] "deg" XXX - -BO_ 1310 Object_30: 8 XXX - SG_ X_Rel : 18|11@0+ (0.1,0) [0|0] "m" XXX - SG_ V_Rel : 52|13@0- (0.01,0) [0|0] "m/s" XXX - SG_ A_Rel : 33|10@0- (0.05,0) [0|0] "m/s2" XXX - SG_ Angle : 12|10@0- (-0.1,0) [0|0] "deg" XXX - -BO_ 1311 Object_31: 8 XXX - SG_ X_Rel : 18|11@0+ (0.1,0) [0|0] "m" XXX - SG_ V_Rel : 52|13@0- (0.01,0) [0|0] "m/s" XXX - SG_ A_Rel : 33|10@0- (0.05,0) [0|0] "m/s2" XXX - SG_ Angle : 12|10@0- (-0.1,0) [0|0] "deg" XXX - -BO_ 1312 Object_32: 8 XXX - SG_ X_Rel : 18|11@0+ (0.1,0) [0|0] "m" XXX - SG_ V_Rel : 52|13@0- (0.01,0) [0|0] "m/s" XXX - SG_ A_Rel : 33|10@0- (0.05,0) [0|0] "m/s2" XXX - SG_ Angle : 12|10@0- (-0.1,0) [0|0] "deg" XXX - -BO_ 1313 Object_33: 8 XXX - SG_ X_Rel : 18|11@0+ (0.1,0) [0|0] "m" XXX - SG_ V_Rel : 52|13@0- (0.01,0) [0|0] "m/s" XXX - SG_ A_Rel : 33|10@0- (0.05,0) [0|0] "m/s2" XXX - SG_ Angle : 12|10@0- (-0.1,0) [0|0] "deg" XXX - -BO_ 1314 Object_34: 8 XXX - SG_ X_Rel : 18|11@0+ (0.1,0) [0|0] "m" XXX - SG_ V_Rel : 52|13@0- (0.01,0) [0|0] "m/s" XXX - SG_ A_Rel : 33|10@0- (0.05,0) [0|0] "m/s2" XXX - SG_ Angle : 12|10@0- (-0.1,0) [0|0] "deg" XXX - -BO_ 1315 Object_35: 8 XXX - SG_ X_Rel : 18|11@0+ (0.1,0) [0|0] "m" XXX - SG_ V_Rel : 52|13@0- (0.01,0) [0|0] "m/s" XXX - SG_ A_Rel : 33|10@0- (0.05,0) [0|0] "m/s2" XXX - SG_ Angle : 12|10@0- (-0.1,0) [0|0] "deg" XXX - -BO_ 1316 Object_36: 8 XXX - SG_ X_Rel : 18|11@0+ (0.1,0) [0|0] "m" XXX - SG_ V_Rel : 52|13@0- (0.01,0) [0|0] "m/s" XXX - SG_ A_Rel : 33|10@0- (0.05,0) [0|0] "m/s2" XXX - SG_ Angle : 12|10@0- (-0.1,0) [0|0] "deg" XXX - -BO_ 1317 Object_37: 8 XXX - SG_ X_Rel : 18|11@0+ (0.1,0) [0|0] "m" XXX - SG_ V_Rel : 52|13@0- (0.01,0) [0|0] "m/s" XXX - SG_ A_Rel : 33|10@0- (0.05,0) [0|0] "m/s2" XXX - SG_ Angle : 12|10@0- (-0.1,0) [0|0] "deg" XXX - -BO_ 1318 Object_38: 8 XXX - SG_ X_Rel : 18|11@0+ (0.1,0) [0|0] "m" XXX - SG_ V_Rel : 52|13@0- (0.01,0) [0|0] "m/s" XXX - SG_ A_Rel : 33|10@0- (0.05,0) [0|0] "m/s2" XXX - SG_ Angle : 12|10@0- (-0.1,0) [0|0] "deg" XXX - -BO_ 1319 Object_39: 8 XXX - SG_ X_Rel : 18|11@0+ (0.1,0) [0|0] "m" XXX - SG_ V_Rel : 52|13@0- (0.01,0) [0|0] "m/s" XXX - SG_ A_Rel : 33|10@0- (0.05,0) [0|0] "m/s2" XXX - SG_ Angle : 12|10@0- (-0.1,0) [0|0] "deg" XXX - -BO_ 1320 Object_40: 8 XXX - SG_ X_Rel : 18|11@0+ (0.1,0) [0|0] "m" XXX - SG_ V_Rel : 52|13@0- (0.01,0) [0|0] "m/s" XXX - SG_ A_Rel : 33|10@0- (0.05,0) [0|0] "m/s2" XXX - SG_ Angle : 12|10@0- (-0.1,0) [0|0] "deg" XXX - -BO_ 1321 Object_41: 8 XXX - SG_ X_Rel : 18|11@0+ (0.1,0) [0|0] "m" XXX - SG_ V_Rel : 52|13@0- (0.01,0) [0|0] "m/s" XXX - SG_ A_Rel : 33|10@0- (0.05,0) [0|0] "m/s2" XXX - SG_ Angle : 12|10@0- (-0.1,0) [0|0] "deg" XXX - -BO_ 1322 Object_42: 8 XXX - SG_ X_Rel : 18|11@0+ (0.1,0) [0|0] "m" XXX - SG_ V_Rel : 52|13@0- (0.01,0) [0|0] "m/s" XXX - SG_ A_Rel : 33|10@0- (0.05,0) [0|0] "m/s2" XXX - SG_ Angle : 12|10@0- (-0.1,0) [0|0] "deg" XXX - -BO_ 1323 Object_43: 8 XXX - SG_ X_Rel : 18|11@0+ (0.1,0) [0|0] "m" XXX - SG_ V_Rel : 52|13@0- (0.01,0) [0|0] "m/s" XXX - SG_ A_Rel : 33|10@0- (0.05,0) [0|0] "m/s2" XXX - SG_ Angle : 12|10@0- (-0.1,0) [0|0] "deg" XXX - -BO_ 1324 Object_44: 8 XXX - SG_ X_Rel : 18|11@0+ (0.1,0) [0|0] "m" XXX - SG_ V_Rel : 52|13@0- (0.01,0) [0|0] "m/s" XXX - SG_ A_Rel : 33|10@0- (0.05,0) [0|0] "m/s2" XXX - SG_ Angle : 12|10@0- (-0.1,0) [0|0] "deg" XXX - -BO_ 1325 Object_45: 8 XXX - SG_ X_Rel : 18|11@0+ (0.1,0) [0|0] "m" XXX - SG_ V_Rel : 52|13@0- (0.01,0) [0|0] "m/s" XXX - SG_ A_Rel : 33|10@0- (0.05,0) [0|0] "m/s2" XXX - SG_ Angle : 12|10@0- (-0.1,0) [0|0] "deg" XXX - -BO_ 1326 Object_46: 8 XXX - SG_ X_Rel : 18|11@0+ (0.1,0) [0|0] "m" XXX - SG_ V_Rel : 52|13@0- (0.01,0) [0|0] "m/s" XXX - SG_ A_Rel : 33|10@0- (0.05,0) [0|0] "m/s2" XXX - SG_ Angle : 12|10@0- (-0.1,0) [0|0] "deg" XXX - -BO_ 1327 Object_47: 8 XXX - SG_ X_Rel : 18|11@0+ (0.1,0) [0|0] "m" XXX - SG_ V_Rel : 52|13@0- (0.01,0) [0|0] "m/s" XXX - SG_ A_Rel : 33|10@0- (0.05,0) [0|0] "m/s2" XXX - SG_ Angle : 12|10@0- (-0.1,0) [0|0] "deg" XXX - -BO_ 1328 Object_48: 8 XXX - SG_ X_Rel : 18|11@0+ (0.1,0) [0|0] "m" XXX - SG_ V_Rel : 52|13@0- (0.01,0) [0|0] "m/s" XXX - SG_ A_Rel : 33|10@0- (0.05,0) [0|0] "m/s2" XXX - SG_ Angle : 12|10@0- (-0.1,0) [0|0] "deg" XXX - -BO_ 1329 Object_49: 8 XXX - SG_ X_Rel : 18|11@0+ (0.1,0) [0|0] "m" XXX - SG_ V_Rel : 52|13@0- (0.01,0) [0|0] "m/s" XXX - SG_ A_Rel : 33|10@0- (0.05,0) [0|0] "m/s2" XXX - SG_ Angle : 12|10@0- (-0.1,0) [0|0] "deg" XXX - -BO_ 1330 Object_50: 8 XXX - SG_ X_Rel : 18|11@0+ (0.1,0) [0|0] "m" XXX - SG_ V_Rel : 52|13@0- (0.01,0) [0|0] "m/s" XXX - SG_ A_Rel : 33|10@0- (0.05,0) [0|0] "m/s2" XXX - SG_ Angle : 12|10@0- (-0.1,0) [0|0] "deg" XXX - -BO_ 1331 Object_51: 8 XXX - SG_ X_Rel : 18|11@0+ (0.1,0) [0|0] "m" XXX - SG_ V_Rel : 52|13@0- (0.01,0) [0|0] "m/s" XXX - SG_ A_Rel : 33|10@0- (0.05,0) [0|0] "m/s2" XXX - SG_ Angle : 12|10@0- (-0.1,0) [0|0] "deg" XXX - -BO_ 1332 Object_52: 8 XXX - SG_ X_Rel : 18|11@0+ (0.1,0) [0|0] "m" XXX - SG_ V_Rel : 52|13@0- (0.01,0) [0|0] "m/s" XXX - SG_ A_Rel : 33|10@0- (0.05,0) [0|0] "m/s2" XXX - SG_ Angle : 12|10@0- (-0.1,0) [0|0] "deg" XXX - -BO_ 1333 Object_53: 8 XXX - SG_ X_Rel : 18|11@0+ (0.1,0) [0|0] "m" XXX - SG_ V_Rel : 52|13@0- (0.01,0) [0|0] "m/s" XXX - SG_ A_Rel : 33|10@0- (0.05,0) [0|0] "m/s2" XXX - SG_ Angle : 12|10@0- (-0.1,0) [0|0] "deg" XXX - -BO_ 1334 Object_54: 8 XXX - SG_ X_Rel : 18|11@0+ (0.1,0) [0|0] "m" XXX - SG_ V_Rel : 52|13@0- (0.01,0) [0|0] "m/s" XXX - SG_ A_Rel : 33|10@0- (0.05,0) [0|0] "m/s2" XXX - SG_ Angle : 12|10@0- (-0.1,0) [0|0] "deg" XXX - -BO_ 1335 Object_55: 8 XXX - SG_ X_Rel : 18|11@0+ (0.1,0) [0|0] "m" XXX - SG_ V_Rel : 52|13@0- (0.01,0) [0|0] "m/s" XXX - SG_ A_Rel : 33|10@0- (0.05,0) [0|0] "m/s2" XXX - SG_ Angle : 12|10@0- (-0.1,0) [0|0] "deg" XXX - -BO_ 1336 Object_56: 8 XXX - SG_ X_Rel : 18|11@0+ (0.1,0) [0|0] "m" XXX - SG_ V_Rel : 52|13@0- (0.01,0) [0|0] "m/s" XXX - SG_ A_Rel : 33|10@0- (0.05,0) [0|0] "m/s2" XXX - SG_ Angle : 12|10@0- (-0.1,0) [0|0] "deg" XXX - -BO_ 1337 Object_57: 8 XXX - SG_ X_Rel : 18|11@0+ (0.1,0) [0|0] "m" XXX - SG_ V_Rel : 52|13@0- (0.01,0) [0|0] "m/s" XXX - SG_ A_Rel : 33|10@0- (0.05,0) [0|0] "m/s2" XXX - SG_ Angle : 12|10@0- (-0.1,0) [0|0] "deg" XXX - -BO_ 1338 Object_58: 8 XXX - SG_ X_Rel : 18|11@0+ (0.1,0) [0|0] "m" XXX - SG_ V_Rel : 52|13@0- (0.01,0) [0|0] "m/s" XXX - SG_ A_Rel : 33|10@0- (0.05,0) [0|0] "m/s2" XXX - SG_ Angle : 12|10@0- (-0.1,0) [0|0] "deg" XXX - -BO_ 1339 Object_59: 8 XXX - SG_ X_Rel : 18|11@0+ (0.1,0) [0|0] "m" XXX - SG_ V_Rel : 52|13@0- (0.01,0) [0|0] "m/s" XXX - SG_ A_Rel : 33|10@0- (0.05,0) [0|0] "m/s2" XXX - SG_ Angle : 12|10@0- (-0.1,0) [0|0] "deg" XXX - -BO_ 1340 Object_60: 8 XXX - SG_ X_Rel : 18|11@0+ (0.1,0) [0|0] "m" XXX - SG_ V_Rel : 52|13@0- (0.01,0) [0|0] "m/s" XXX - SG_ A_Rel : 33|10@0- (0.05,0) [0|0] "m/s2" XXX - SG_ Angle : 12|10@0- (-0.1,0) [0|0] "deg" XXX - -BO_ 1341 Object_61: 8 XXX - SG_ X_Rel : 18|11@0+ (0.1,0) [0|0] "m" XXX - SG_ V_Rel : 52|13@0- (0.01,0) [0|0] "m/s" XXX - SG_ A_Rel : 33|10@0- (0.05,0) [0|0] "m/s2" XXX - SG_ Angle : 12|10@0- (-0.1,0) [0|0] "deg" XXX - -BO_ 1342 Object_62: 8 XXX - SG_ X_Rel : 18|11@0+ (0.1,0) [0|0] "m" XXX - SG_ V_Rel : 52|13@0- (0.01,0) [0|0] "m/s" XXX - SG_ A_Rel : 33|10@0- (0.05,0) [0|0] "m/s2" XXX - SG_ Angle : 12|10@0- (-0.1,0) [0|0] "deg" XXX - -BO_ 1343 Object_63: 8 XXX - SG_ X_Rel : 18|11@0+ (0.1,0) [0|0] "m" XXX - SG_ V_Rel : 52|13@0- (0.01,0) [0|0] "m/s" XXX - SG_ A_Rel : 33|10@0- (0.05,0) [0|0] "m/s2" XXX - SG_ Angle : 12|10@0- (-0.1,0) [0|0] "deg" XXX - diff --git a/opendbc/honda_accord_2018_can_generated.dbc b/opendbc/honda_accord_2018_can_generated.dbc index 12b66c6ec..d1a84ac80 100644 --- a/opendbc/honda_accord_2018_can_generated.dbc +++ b/opendbc/honda_accord_2018_can_generated.dbc @@ -76,6 +76,43 @@ BO_ 773 SEATBELT_STATUS: 7 BDY SG_ COUNTER : 53|2@0+ (1,0) [0|3] "" EON SG_ CHECKSUM : 51|4@0+ (1,0) [0|3] "" EON +BO_ 777 CAR_SPEED: 8 PCM + SG_ ROUGH_CAR_SPEED : 23|8@0+ (1,0) [0|255] "mph" XXX + SG_ CAR_SPEED : 7|16@0+ (0.01,0) [0|65535] "kph" XXX + SG_ ROUGH_CAR_SPEED_3 : 39|16@0+ (0.01,0) [0|65535] "kph" XXX + SG_ ROUGH_CAR_SPEED_2 : 31|8@0+ (1,0) [0|255] "mph" XXX + SG_ LOCK_STATUS : 55|2@0+ (1,0) [0|255] "" XXX + SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" XXX + SG_ CHECKSUM : 59|4@0+ (1,0) [0|15] "" XXX + SG_ IMPERIAL_UNIT : 63|1@0+ (1,0) [0|1] "" XXX + +BO_ 780 ACC_HUD: 8 ADAS + SG_ PCM_SPEED : 7|16@0+ (0.01,0) [0|250] "kph" BDY + SG_ PCM_GAS : 23|8@0+ (1,0) [0|127] "" BDY + SG_ CRUISE_SPEED : 31|8@0+ (1,0) [0|255] "kph" BDY + SG_ DTC_MODE : 39|1@0+ (1,0) [0|1] "" BDY + SG_ BOH : 38|1@0+ (1,0) [0|1] "" BDY + SG_ ACC_PROBLEM : 37|1@0+ (1,0) [0|1] "" BDY + SG_ FCM_OFF : 35|1@0+ (1,0) [0|1] "" BDY + SG_ FCM_OFF_2 : 36|1@0+ (1,0) [0|1] "" BDY + SG_ FCM_PROBLEM : 34|1@0+ (1,0) [0|1] "" BDY + SG_ RADAR_OBSTRUCTED : 33|1@0+ (1,0) [0|1] "" BDY + SG_ ENABLE_MINI_CAR : 32|1@0+ (1,0) [0|1] "" BDY + SG_ HUD_DISTANCE : 47|2@0+ (1,0) [0|3] "" BDY + SG_ HUD_LEAD : 45|2@0+ (1,0) [0|3] "" BDY + SG_ BOH_3 : 43|1@0+ (1,0) [0|3] "" BDY + SG_ BOH_4 : 42|1@0+ (1,0) [0|3] "" BDY + SG_ BOH_5 : 41|1@0+ (1,0) [0|3] "" BDY + SG_ CRUISE_CONTROL_LABEL : 40|1@0+ (1,0) [0|3] "" BDY + SG_ SET_ME_X01_2 : 55|1@0+ (1,0) [0|1] "" BDY + SG_ IMPERIAL_UNIT : 54|1@0+ (1,0) [0|1] "" BDY + SG_ ACC_ON : 52|1@0+ (1,0) [0|1] "" BDY + SG_ CHIME : 51|3@0+ (1,0) [0|1] "" BDY + SG_ SET_ME_X01 : 48|1@0+ (1,0) [0|1] "" BDY + SG_ ICONS : 63|2@0+ (1,0) [0|1] "" BDY + SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" BDY + SG_ CHECKSUM : 59|4@0+ (1,0) [0|3] "" BDY + BO_ 804 CRUISE: 8 PCM SG_ HUD_SPEED_KPH : 7|8@0+ (1,0) [0|255] "kph" EON SG_ HUD_SPEED_MPH : 15|8@0+ (1,0) [0|255] "mph" EON @@ -86,27 +123,6 @@ BO_ 804 CRUISE: 8 PCM SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" EON SG_ CHECKSUM : 59|4@0+ (1,0) [0|15] "" EON -BO_ 829 LKAS_HUD: 5 ADAS - SG_ CAM_TEMP_HIGH : 7|1@0+ (1,0) [0|255] "" BDY - SG_ SET_ME_X41 : 6|7@0+ (1,0) [0|127] "" BDY - SG_ BOH : 6|7@0+ (1,0) [0|127] "" BDY - SG_ DASHED_LANES : 14|1@0+ (1,0) [0|1] "" BDY - SG_ DTC : 13|1@0+ (1,0) [0|1] "" BDY - SG_ LKAS_PROBLEM : 12|1@0+ (1,0) [0|1] "" BDY - SG_ LKAS_OFF : 11|1@0+ (1,0) [0|1] "" BDY - SG_ SOLID_LANES : 10|1@0+ (1,0) [0|1] "" BDY - SG_ LDW_RIGHT : 9|1@0+ (1,0) [0|1] "" BDY - SG_ STEERING_REQUIRED : 8|1@0+ (1,0) [0|1] "" BDY - SG_ BOH : 23|2@0+ (1,0) [0|4] "" BDY - SG_ LDW_PROBLEM : 21|1@0+ (1,0) [0|1] "" BDY - SG_ BEEP : 17|2@0+ (1,0) [0|1] "" BDY - SG_ LDW_ON : 28|1@0+ (1,0) [0|1] "" BDY - SG_ LDW_OFF : 27|1@0+ (1,0) [0|1] "" BDY - SG_ CLEAN_WINDSHIELD : 26|1@0+ (1,0) [0|1] "" BDY - SG_ SET_ME_X48 : 31|8@0+ (1,0) [0|255] "" BDY - SG_ COUNTER : 37|2@0+ (1,0) [0|3] "" BDY - SG_ CHECKSUM : 35|4@0+ (1,0) [0|15] "" BDY - BO_ 884 STALK_STATUS: 8 XXX SG_ DASHBOARD_ALERT : 39|8@0+ (1,0) [0|255] "" EON SG_ AUTO_HEADLIGHTS : 46|1@0+ (1,0) [0|1] "" EON @@ -140,10 +156,13 @@ CM_ SG_ 420 BRAKE_HOLD_RELATED "On when Brake Hold engaged"; CM_ SG_ 490 LONG_ACCEL "wheel speed derivative, noisy and zero snapping"; CM_ SG_ 773 PASS_AIRBAG_ON "Might just be indicator light"; CM_ SG_ 773 PASS_AIRBAG_OFF "Might just be indicator light"; +CM_ SG_ 780 CRUISE_SPEED "255 = no speed"; +CM_ SG_ 780 PCM_SPEED "Used by Nidec"; +CM_ SG_ 780 PCM_GAS "Used by Nidec"; CM_ SG_ 804 CRUISE_SPEED_PCM "255 = no speed"; -CM_ SG_ 829 BEEP "beeps are pleasant, chimes are for warnngs etc..."; -VAL_ 829 BEEP 3 "single_beep" 2 "triple_beep" 1 "repeated_beep" 0 "no_beep"; +VAL_ 780 CRUISE_SPEED 255 "no_speed" 252 "stopped"; +VAL_ 780 HUD_LEAD 3 "acc_off" 2 "solid_car" 1 "dashed_car" 0 "no_car"; VAL_ 884 DASHBOARD_ALERT 0 "none" 51 "acc_problem" 55 "cmbs_problem" 75 "key_not_detected" 79 "fasten_seatbelt" 111 "lkas_problem" 131 "brake_system_problem" 132 "brake_hold_problem" 139 "tbd" 161 "door_open"; VAL_ 891 WIPERS 4 "High" 2 "Low" 0 "Off"; @@ -194,32 +213,6 @@ BO_ 450 EPB_STATUS: 8 EPB SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" XXX SG_ CHECKSUM : 59|4@0+ (1,0) [0|15] "" XXX -BO_ 479 ACC_CONTROL: 8 EON - SG_ SET_TO_0 : 20|5@0+ (1,0) [0|1] "" XXX - SG_ CONTROL_ON : 23|3@0+ (1,0) [0|5] "" XXX - SG_ GAS_COMMAND : 7|16@0- (1,0) [0|0] "" XXX - SG_ ACCEL_COMMAND : 31|11@0- (0.01,0) [0|0] "m/s2" XXX - SG_ BRAKE_LIGHTS : 62|1@0+ (1,0) [0|1] "" XXX - SG_ BRAKE_REQUEST : 34|1@0+ (1,0) [0|1] "" XXX - SG_ STANDSTILL : 35|1@0+ (1,0) [0|1] "" XXX - SG_ STANDSTILL_RELEASE : 36|1@0+ (1,0) [0|1] "" XXX - SG_ AEB_STATUS : 33|1@0+ (1,0) [0|1] "" XXX - SG_ AEB_BRAKING : 47|1@0+ (1,0) [0|1] "" XXX - SG_ AEB_PREPARE : 43|1@0+ (1,0) [0|1] "" XXX - SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" XXX - SG_ CHECKSUM : 59|4@0+ (1,0) [0|15] "" XXX - -BO_ 495 ACC_CONTROL_ON: 8 XXX - SG_ SET_TO_75 : 31|8@0+ (1,0) [0|255] "" XXX - SG_ SET_TO_30 : 39|8@0+ (1,0) [0|255] "" XXX - SG_ ZEROS_BOH : 23|8@0+ (1,0) [0|255] "" XXX - SG_ ZEROS_BOH2 : 47|16@0+ (1,0) [0|255] "" XXX - SG_ SET_TO_FF : 15|8@0+ (1,0) [0|255] "" XXX - SG_ SET_TO_3 : 6|7@0+ (1,0) [0|4095] "" XXX - SG_ CONTROL_ON : 7|1@0+ (1,0) [0|1] "" XXX - SG_ CHECKSUM : 59|4@0+ (1,0) [0|15] "" XXX - SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" XXX - BO_ 545 XXX_16: 6 SCM SG_ ECON_ON : 23|1@0+ (1,0) [0|1] "" XXX SG_ DRIVE_MODE : 37|2@0+ (1,0) [0|3] "" XXX @@ -319,40 +312,6 @@ BO_ 662 SCM_BUTTONS: 4 SCM SG_ COUNTER : 29|2@0+ (1,0) [0|3] "" EON SG_ CHECKSUM : 27|4@0+ (1,0) [0|15] "" EON -BO_ 777 CAR_SPEED: 8 PCM - SG_ ROUGH_CAR_SPEED : 23|8@0+ (1,0) [0|255] "mph" XXX - SG_ CAR_SPEED : 7|16@0+ (0.01,0) [0|65535] "kph" XXX - SG_ ROUGH_CAR_SPEED_3 : 39|16@0+ (0.01,0) [0|65535] "kph" XXX - SG_ ROUGH_CAR_SPEED_2 : 31|8@0+ (1,0) [0|255] "mph" XXX - SG_ LOCK_STATUS : 55|2@0+ (1,0) [0|255] "" XXX - SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" XXX - SG_ CHECKSUM : 59|4@0+ (1,0) [0|15] "" XXX - SG_ IMPERIAL_UNIT : 63|1@0+ (1,0) [0|1] "" XXX - -BO_ 780 ACC_HUD: 8 ADAS - SG_ CRUISE_SPEED : 31|8@0+ (1,0) [0|255] "kph" BDY - SG_ DTC_MODE : 39|1@0+ (1,0) [0|1] "" BDY - SG_ BOH : 38|1@0+ (1,0) [0|1] "" BDY - SG_ FCM_PROBLEM : 34|1@0+ (1,0) [0|1] "" BDY - SG_ RADAR_OBSTRUCTED : 33|1@0+ (1,0) [0|1] "" BDY - SG_ ENABLE_MINI_CAR : 32|1@0+ (1,0) [0|1] "" BDY - SG_ BOH_3 : 43|1@0+ (1,0) [0|3] "" BDY - SG_ BOH_4 : 42|1@0+ (1,0) [0|3] "" BDY - SG_ BOH_5 : 41|1@0+ (1,0) [0|3] "" BDY - SG_ CRUISE_CONTROL_LABEL : 40|1@0+ (1,0) [0|3] "" BDY - SG_ ZEROS_BOH : 7|24@0+ (0.002759506,0) [0|100] "m/s" BDY - SG_ FCM_OFF : 35|1@0+ (1,0) [0|1] "" BDY - SG_ SET_TO_1 : 36|1@0+ (1,0) [0|1] "" XXX - SG_ HUD_DISTANCE : 47|2@0+ (1,0) [0|3] "" BDY - SG_ HUD_LEAD : 45|2@0+ (1,0) [0|3] "" BDY - SG_ ACC_PROBLEM : 37|1@0+ (1,0) [0|1] "" BDY - SG_ ACC_ON : 52|1@0+ (1,0) [0|1] "" XXX - SG_ BOH_6 : 51|4@0+ (1,0) [0|15] "" XXX - SG_ SET_TO_X1 : 55|1@0+ (1,0) [0|1] "" XXX - SG_ IMPERIAL_UNIT : 54|1@0+ (1,0) [0|1] "" XXX - SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" XXX - SG_ CHECKSUM : 59|4@0+ (1,0) [0|15] "" XXX - BO_ 806 SCM_FEEDBACK: 8 SCM SG_ DRIVERS_DOOR_OPEN : 17|1@0+ (1,0) [0|1] "" XXX SG_ MAIN_ON : 28|1@0+ (1,0) [0|1] "" EON @@ -370,6 +329,23 @@ BO_ 862 CAMERA_MESSAGES: 8 CAM SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" XXX SG_ CHECKSUM : 59|4@0+ (1,0) [0|15] "" XXX +BO_ 927 RADAR_HUD: 8 RADAR + SG_ ZEROS_BOH : 7|10@0+ (1,0) [0|127] "" BDY + SG_ CMBS_OFF : 12|1@0+ (1,0) [0|1] "" BDY + SG_ RESUME_INSTRUCTION : 21|1@0+ (1,0) [0|1] "" XXX + SG_ SET_TO_1 : 13|1@0+ (1,0) [0|1] "" BDY + SG_ ZEROS_BOH2 : 11|4@0+ (1,0) [0|1] "" XXX + SG_ APPLY_BRAKES_FOR_CANC : 23|1@0+ (1,0) [0|1] "" XXX + SG_ ACC_ALERTS : 20|5@0+ (1,0) [0|1] "" BDY + SG_ SET_TO_0 : 22|1@0+ (1,0) [0|1] "" XXX + SG_ HUD_LEAD : 40|1@0+ (1,0) [0|1] "" XXX + SG_ SET_TO_64 : 31|8@0+ (1,0) [0|255] "" XXX + SG_ LEAD_DISTANCE : 39|8@0+ (1,0) [0|255] "" XXX + SG_ ZEROS_BOH3 : 47|7@0+ (1,0) [0|127] "" XXX + SG_ ZEROS_BOH4 : 55|8@0+ (1,0) [0|255] "" XXX + SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" XXX + SG_ CHECKSUM : 59|4@0+ (1,0) [0|15] "" XXX + BO_ 13274 LKAS_HUD_A: 5 ADAS SG_ CAM_TEMP_HIGH : 7|1@0+ (1,0) [0|255] "" BDY SG_ SET_ME_X41 : 6|7@0+ (1,0) [0|127] "" BDY @@ -413,10 +389,6 @@ BO_ 13275 LKAS_HUD_B: 8 ADAS SG_ CHECKSUM : 59|4@0+ (1,0) [0|15] "" BDY CM_ SG_ 450 EPB_STATE "3: On, 2: Disengaging, 1: Engaging, 0: Off"; -CM_ SG_ 479 CONTROL_ON "Set to 5 when car is being controlled"; -CM_ SG_ 479 AEB_STATUS "set for the duration of AEB event"; -CM_ SG_ 479 AEB_BRAKING "set when braking is commanded during AEB event"; -CM_ SG_ 479 AEB_PREPARE "set 1s before AEB"; CM_ SG_ 576 LINE_DISTANCE_VISIBLE "Length of line visible, undecoded"; CM_ SG_ 577 LINE_FAR_EDGE_POSITION "Appears to be a measure of line thickness, indicates location of the portion of the line furthest from the car, undecoded"; CM_ SG_ 577 LINE_PARAMETER "Unclear if this is low quality line curvature rate or if this is something else, but it is correlated with line curvature, undecoded"; @@ -425,8 +397,65 @@ CM_ SG_ 577 LINE_SOLID "1 = line is solid"; VAL_ 399 STEER_STATUS 6 "tmp_fault" 5 "fault_1" 4 "no_torque_alert_2" 3 "low_speed_lockout" 2 "no_torque_alert_1" 0 "normal"; -CM_ "honda_accord_2018_can.dbc starts here"; +CM_ "Imported file _bosch_adas_2018.dbc starts here"; +BO_ 479 ACC_CONTROL: 8 EON + SG_ SET_TO_0 : 20|5@0+ (1,0) [0|1] "" XXX + SG_ CONTROL_ON : 23|3@0+ (1,0) [0|5] "" XXX + SG_ GAS_COMMAND : 7|16@0- (1,0) [0|0] "" XXX + SG_ ACCEL_COMMAND : 31|11@0- (0.01,0) [0|0] "m/s2" XXX + SG_ BRAKE_LIGHTS : 62|1@0+ (1,0) [0|1] "" XXX + SG_ BRAKE_REQUEST : 34|1@0+ (1,0) [0|1] "" XXX + SG_ STANDSTILL : 35|1@0+ (1,0) [0|1] "" XXX + SG_ STANDSTILL_RELEASE : 36|1@0+ (1,0) [0|1] "" XXX + SG_ AEB_STATUS : 33|1@0+ (1,0) [0|1] "" XXX + SG_ AEB_BRAKING : 47|1@0+ (1,0) [0|1] "" XXX + SG_ AEB_PREPARE : 43|1@0+ (1,0) [0|1] "" XXX + SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" XXX + SG_ CHECKSUM : 59|4@0+ (1,0) [0|15] "" XXX + +BO_ 495 ACC_CONTROL_ON: 8 XXX + SG_ SET_TO_75 : 31|8@0+ (1,0) [0|255] "" XXX + SG_ SET_TO_30 : 39|8@0+ (1,0) [0|255] "" XXX + SG_ ZEROS_BOH : 23|8@0+ (1,0) [0|255] "" XXX + SG_ ZEROS_BOH2 : 47|16@0+ (1,0) [0|255] "" XXX + SG_ SET_TO_FF : 15|8@0+ (1,0) [0|255] "" XXX + SG_ SET_TO_3 : 6|7@0+ (1,0) [0|4095] "" XXX + SG_ CONTROL_ON : 7|1@0+ (1,0) [0|1] "" XXX + SG_ CHECKSUM : 59|4@0+ (1,0) [0|15] "" XXX + SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" XXX + +BO_ 829 LKAS_HUD: 5 ADAS + SG_ CAM_TEMP_HIGH : 7|1@0+ (1,0) [0|255] "" BDY + SG_ SET_ME_X41 : 6|7@0+ (1,0) [0|127] "" BDY + SG_ BOH : 6|7@0+ (1,0) [0|127] "" BDY + SG_ DASHED_LANES : 14|1@0+ (1,0) [0|1] "" BDY + SG_ DTC : 13|1@0+ (1,0) [0|1] "" BDY + SG_ LKAS_PROBLEM : 12|1@0+ (1,0) [0|1] "" BDY + SG_ LKAS_OFF : 11|1@0+ (1,0) [0|1] "" BDY + SG_ SOLID_LANES : 10|1@0+ (1,0) [0|1] "" BDY + SG_ LDW_RIGHT : 9|1@0+ (1,0) [0|1] "" BDY + SG_ STEERING_REQUIRED : 8|1@0+ (1,0) [0|1] "" BDY + SG_ BOH : 23|2@0+ (1,0) [0|4] "" BDY + SG_ LDW_PROBLEM : 21|1@0+ (1,0) [0|1] "" BDY + SG_ BEEP : 17|2@0+ (1,0) [0|1] "" BDY + SG_ LDW_ON : 28|1@0+ (1,0) [0|1] "" BDY + SG_ LDW_OFF : 27|1@0+ (1,0) [0|1] "" BDY + SG_ CLEAN_WINDSHIELD : 26|1@0+ (1,0) [0|1] "" BDY + SG_ SET_ME_X48 : 31|8@0+ (1,0) [0|255] "" BDY + SG_ COUNTER : 37|2@0+ (1,0) [0|3] "" BDY + SG_ CHECKSUM : 35|4@0+ (1,0) [0|15] "" BDY + +CM_ SG_ 479 CONTROL_ON "Set to 5 when car is being controlled"; +CM_ SG_ 479 AEB_STATUS "set for the duration of AEB event"; +CM_ SG_ 479 AEB_BRAKING "set when braking is commanded during AEB event"; +CM_ SG_ 479 AEB_PREPARE "set 1s before AEB"; +CM_ SG_ 829 BEEP "beeps are pleasant, chimes are for warnngs etc..."; + +VAL_ 829 BEEP 3 "single_beep" 2 "triple_beep" 1 "repeated_beep" 0 "no_beep"; + + +CM_ "Imported file _steering_sensors_a.dbc starts here"; BO_ 330 STEERING_SENSORS: 8 EPS SG_ STEER_ANGLE : 7|16@0- (-0.1,0) [-500|500] "deg" EON SG_ STEER_ANGLE_RATE : 23|16@0- (-1,0) [-3000|3000] "deg/s" EON @@ -437,6 +466,8 @@ BO_ 330 STEERING_SENSORS: 8 EPS SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" EON SG_ CHECKSUM : 59|4@0+ (1,0) [0|15] "" EON +CM_ "honda_accord_2018_can.dbc starts here"; + BO_ 401 GEARBOX_15T: 8 PCM SG_ GEAR_SHIFTER : 5|6@0+ (1,0) [0|63] "" EON SG_ BOH : 45|6@0+ (1,0) [0|63] "" XXX @@ -474,22 +505,6 @@ BO_ 892 CRUISE_PARAMS: 8 PCM SG_ CHECKSUM : 59|4@0+ (1,0) [0|3] "" EON SG_ COUNTER : 61|2@0+ (1,0) [0|15] "" EON -BO_ 927 RADAR_HUD: 8 RADAR - SG_ ZEROS_BOH : 7|10@0+ (1,0) [0|127] "" BDY - SG_ CMBS_OFF : 12|1@0+ (1,0) [0|1] "" BDY - SG_ ZEROS_BOH3 : 31|32@0+ (1,0) [0|4294967295] "" XXX - SG_ RESUME_INSTRUCTION : 21|1@0+ (1,0) [0|1] "" XXX - SG_ SET_TO_1 : 13|1@0+ (1,0) [0|1] "" BDY - SG_ ZEROS_BOH2 : 11|4@0+ (1,0) [0|1] "" XXX - SG_ APPLY_BRAKES_FOR_CANC : 23|1@0+ (1,0) [0|1] "" XXX - SG_ ACC_ALERTS : 20|5@0+ (1,0) [0|1] "" BDY - SG_ SET_TO_0 : 22|1@0+ (1,0) [0|1] "" XXX - SG_ LEAD_DISTANCE : 39|8@0+ (1,0) [0|255] "" XXX - SG_ BOH : 40|1@0+ (1,0) [0|1] "" XXX - SG_ BOH_2 : 30|1@0+ (1,0) [0|1] "" XXX - SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" XXX - SG_ CHECKSUM : 59|4@0+ (1,0) [0|15] "" XXX - BO_ 1302 ODOMETER: 8 XXX SG_ ODOMETER : 7|24@0+ (1,0) [0|16777215] "km" EON SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" EON diff --git a/opendbc/honda_civic_ex_2022_can_generated.dbc b/opendbc/honda_civic_ex_2022_can_generated.dbc new file mode 100644 index 000000000..0b52c793c --- /dev/null +++ b/opendbc/honda_civic_ex_2022_can_generated.dbc @@ -0,0 +1,497 @@ +CM_ "AUTOGENERATED FILE, DO NOT EDIT"; + + +CM_ "Imported file _comma.dbc starts here"; +BO_ 512 GAS_COMMAND: 6 EON + SG_ GAS_COMMAND : 7|16@0+ (0.253984064,-83.3) [0|1] "" INTERCEPTOR + SG_ GAS_COMMAND2 : 23|16@0+ (0.126992032,-83.3) [0|1] "" INTERCEPTOR + SG_ ENABLE : 39|1@0+ (1,0) [0|1] "" INTERCEPTOR + SG_ COUNTER_PEDAL : 35|4@0+ (1,0) [0|15] "" INTERCEPTOR + SG_ CHECKSUM_PEDAL : 47|8@0+ (1,0) [0|255] "" INTERCEPTOR + +BO_ 513 GAS_SENSOR: 6 INTERCEPTOR + SG_ INTERCEPTOR_GAS : 7|16@0+ (1,0) [0|1] "" EON + SG_ INTERCEPTOR_GAS2 : 23|16@0+ (1,0) [0|1] "" EON + SG_ STATE : 39|4@0+ (1,0) [0|15] "" EON + SG_ COUNTER_PEDAL : 35|4@0+ (1,0) [0|15] "" EON + SG_ CHECKSUM_PEDAL : 47|8@0+ (1,0) [0|255] "" EON + +VAL_ 513 STATE 5 "FAULT_TIMEOUT" 4 "FAULT_STARTUP" 3 "FAULT_SCE" 2 "FAULT_SEND" 1 "FAULT_BAD_CHECKSUM" 0 "NO_FAULT" ; + + +CM_ "Imported file _honda_common.dbc starts here"; +BO_ 304 GAS_PEDAL_2: 8 PCM + SG_ ENGINE_TORQUE_ESTIMATE : 7|16@0- (1,0) [-1000|1000] "Nm" EON + SG_ ENGINE_TORQUE_REQUEST : 23|16@0- (1,0) [-1000|1000] "Nm" EON + SG_ CAR_GAS : 39|8@0+ (1,0) [0|255] "" EON + SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" EON + SG_ CHECKSUM : 59|4@0+ (1,0) [0|15] "" EON + +BO_ 316 GAS_PEDAL: 8 PCM + SG_ CAR_GAS : 39|8@0+ (1,0) [0|255] "" EON + SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" EON + SG_ CHECKSUM : 59|4@0+ (1,0) [0|15] "" EON + +BO_ 344 ENGINE_DATA: 8 PCM + SG_ XMISSION_SPEED : 7|16@0+ (0.01,0) [0|250] "kph" EON + SG_ ENGINE_RPM : 23|16@0+ (1,0) [0|15000] "rpm" EON + SG_ XMISSION_SPEED2 : 39|16@0+ (0.01,0) [0|250] "kph" EON + SG_ ODOMETER : 55|8@0+ (10,0) [0|2550] "m" XXX + SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" EON + SG_ CHECKSUM : 59|4@0+ (1,0) [0|15] "" EON + +BO_ 380 POWERTRAIN_DATA: 8 PCM + SG_ PEDAL_GAS : 7|8@0+ (1,0) [0|255] "" EON + SG_ ENGINE_RPM : 23|16@0+ (1,0) [0|15000] "rpm" EON + SG_ GAS_PRESSED : 39|1@0+ (1,0) [0|1] "" EON + SG_ ACC_STATUS : 38|1@0+ (1,0) [0|1] "" EON + SG_ BOH_17C : 37|5@0+ (1,0) [0|1] "" EON + SG_ BRAKE_SWITCH : 32|1@0+ (1,0) [0|1] "" EON + SG_ BOH2_17C : 47|10@0+ (1,0) [0|1] "" EON + SG_ BRAKE_PRESSED : 53|1@0+ (1,0) [0|1] "" EON + SG_ BOH3_17C : 52|5@0+ (1,0) [0|1] "" EON + SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" EON + SG_ CHECKSUM : 59|4@0+ (1,0) [0|15] "" EON + +BO_ 420 VSA_STATUS: 8 VSA + SG_ USER_BRAKE : 7|16@0+ (0.015625,-1.609375) [0|1000] "" EON + SG_ COMPUTER_BRAKING : 23|1@0+ (1,0) [0|1] "" EON + SG_ ESP_DISABLED : 28|1@0+ (1,0) [0|1] "" EON + SG_ BRAKE_HOLD_RELATED : 52|1@0+ (1,0) [0|1] "" XXX + SG_ BRAKE_HOLD_ACTIVE : 46|1@0+ (1,0) [0|1] "" EON + SG_ BRAKE_HOLD_ENABLED : 45|1@0+ (1,0) [0|1] "" EON + SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" EON + SG_ CHECKSUM : 59|4@0+ (1,0) [0|15] "" EON + +BO_ 427 STEER_MOTOR_TORQUE: 3 EPS + SG_ CONFIG_VALID : 7|1@0+ (1,0) [0|1] "" EON + SG_ MOTOR_TORQUE : 1|10@0+ (1,0) [0|256] "" EON + SG_ OUTPUT_DISABLED : 22|1@0+ (1,0) [0|1] "" EON + SG_ COUNTER : 21|2@0+ (1,0) [0|3] "" EON + SG_ CHECKSUM : 19|4@0+ (1,0) [0|15] "" EON + +BO_ 464 WHEEL_SPEEDS: 8 VSA + SG_ WHEEL_SPEED_FL : 7|15@0+ (0.01,0) [0|250] "kph" EON + SG_ WHEEL_SPEED_FR : 8|15@0+ (0.01,0) [0|250] "kph" EON + SG_ WHEEL_SPEED_RL : 25|15@0+ (0.01,0) [0|250] "kph" EON + SG_ WHEEL_SPEED_RR : 42|15@0+ (0.01,0) [0|250] "kph" EON + SG_ CHECKSUM : 59|4@0+ (1,0) [0|3] "" EON + +BO_ 490 VEHICLE_DYNAMICS: 8 VSA + SG_ LAT_ACCEL : 7|16@0- (0.0015,0) [-20|20] "m/s2" EON + SG_ LONG_ACCEL : 23|16@0- (0.0015,0) [-20|20] "m/s2" EON + SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" EON + SG_ CHECKSUM : 59|4@0+ (1,0) [0|3] "" EON + +BO_ 773 SEATBELT_STATUS: 7 BDY + SG_ SEATBELT_DRIVER_LAMP : 7|1@0+ (1,0) [0|1] "" EON + SG_ SEATBELT_PASS_UNLATCHED : 10|1@0+ (1,0) [0|1] "" EON + SG_ SEATBELT_PASS_LATCHED : 11|1@0+ (1,0) [0|1] "" EON + SG_ SEATBELT_DRIVER_UNLATCHED : 12|1@0+ (1,0) [0|1] "" EON + SG_ SEATBELT_DRIVER_LATCHED : 13|1@0+ (1,0) [0|1] "" EON + SG_ PASS_AIRBAG_OFF : 14|1@0+ (1,0) [0|1] "" EON + SG_ PASS_AIRBAG_ON : 15|1@0+ (1,0) [0|1] "" EON + SG_ COUNTER : 53|2@0+ (1,0) [0|3] "" EON + SG_ CHECKSUM : 51|4@0+ (1,0) [0|3] "" EON + +BO_ 777 CAR_SPEED: 8 PCM + SG_ ROUGH_CAR_SPEED : 23|8@0+ (1,0) [0|255] "mph" XXX + SG_ CAR_SPEED : 7|16@0+ (0.01,0) [0|65535] "kph" XXX + SG_ ROUGH_CAR_SPEED_3 : 39|16@0+ (0.01,0) [0|65535] "kph" XXX + SG_ ROUGH_CAR_SPEED_2 : 31|8@0+ (1,0) [0|255] "mph" XXX + SG_ LOCK_STATUS : 55|2@0+ (1,0) [0|255] "" XXX + SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" XXX + SG_ CHECKSUM : 59|4@0+ (1,0) [0|15] "" XXX + SG_ IMPERIAL_UNIT : 63|1@0+ (1,0) [0|1] "" XXX + +BO_ 780 ACC_HUD: 8 ADAS + SG_ PCM_SPEED : 7|16@0+ (0.01,0) [0|250] "kph" BDY + SG_ PCM_GAS : 23|8@0+ (1,0) [0|127] "" BDY + SG_ CRUISE_SPEED : 31|8@0+ (1,0) [0|255] "kph" BDY + SG_ DTC_MODE : 39|1@0+ (1,0) [0|1] "" BDY + SG_ BOH : 38|1@0+ (1,0) [0|1] "" BDY + SG_ ACC_PROBLEM : 37|1@0+ (1,0) [0|1] "" BDY + SG_ FCM_OFF : 35|1@0+ (1,0) [0|1] "" BDY + SG_ FCM_OFF_2 : 36|1@0+ (1,0) [0|1] "" BDY + SG_ FCM_PROBLEM : 34|1@0+ (1,0) [0|1] "" BDY + SG_ RADAR_OBSTRUCTED : 33|1@0+ (1,0) [0|1] "" BDY + SG_ ENABLE_MINI_CAR : 32|1@0+ (1,0) [0|1] "" BDY + SG_ HUD_DISTANCE : 47|2@0+ (1,0) [0|3] "" BDY + SG_ HUD_LEAD : 45|2@0+ (1,0) [0|3] "" BDY + SG_ BOH_3 : 43|1@0+ (1,0) [0|3] "" BDY + SG_ BOH_4 : 42|1@0+ (1,0) [0|3] "" BDY + SG_ BOH_5 : 41|1@0+ (1,0) [0|3] "" BDY + SG_ CRUISE_CONTROL_LABEL : 40|1@0+ (1,0) [0|3] "" BDY + SG_ SET_ME_X01_2 : 55|1@0+ (1,0) [0|1] "" BDY + SG_ IMPERIAL_UNIT : 54|1@0+ (1,0) [0|1] "" BDY + SG_ ACC_ON : 52|1@0+ (1,0) [0|1] "" BDY + SG_ CHIME : 51|3@0+ (1,0) [0|1] "" BDY + SG_ SET_ME_X01 : 48|1@0+ (1,0) [0|1] "" BDY + SG_ ICONS : 63|2@0+ (1,0) [0|1] "" BDY + SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" BDY + SG_ CHECKSUM : 59|4@0+ (1,0) [0|3] "" BDY + +BO_ 804 CRUISE: 8 PCM + SG_ HUD_SPEED_KPH : 7|8@0+ (1,0) [0|255] "kph" EON + SG_ HUD_SPEED_MPH : 15|8@0+ (1,0) [0|255] "mph" EON + SG_ TRIP_FUEL_CONSUMED : 23|16@0+ (1,0) [0|255] "" EON + SG_ CRUISE_SPEED_PCM : 39|8@0+ (1,0) [0|255] "" EON + SG_ BOH2 : 47|8@0- (1,0) [0|255] "" EON + SG_ BOH3 : 55|8@0+ (1,0) [0|255] "" EON + SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" EON + SG_ CHECKSUM : 59|4@0+ (1,0) [0|15] "" EON + +BO_ 884 STALK_STATUS: 8 XXX + SG_ DASHBOARD_ALERT : 39|8@0+ (1,0) [0|255] "" EON + SG_ AUTO_HEADLIGHTS : 46|1@0+ (1,0) [0|1] "" EON + SG_ HIGH_BEAM_HOLD : 47|1@0+ (1,0) [0|1] "" EON + SG_ HIGH_BEAM_FLASH : 45|1@0+ (1,0) [0|1] "" EON + SG_ HEADLIGHTS_ON : 54|1@0+ (1,0) [0|1] "" EON + SG_ WIPER_SWITCH : 53|2@0+ (1,0) [0|3] "" XXX + SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" EON + SG_ CHECKSUM : 59|4@0+ (1,0) [0|15] "" EON + +BO_ 891 STALK_STATUS_2: 8 XXX + SG_ WIPERS : 17|2@0+ (1,0) [0|3] "" EON + SG_ LOW_BEAMS : 35|1@0+ (1,0) [0|1] "" XXX + SG_ HIGH_BEAMS : 34|1@0+ (1,0) [0|1] "" XXX + SG_ PARK_LIGHTS : 36|1@0+ (1,0) [0|1] "" XXX + SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" EON + SG_ CHECKSUM : 59|4@0+ (1,0) [0|15] "" EON + +BO_ 1029 DOORS_STATUS: 8 BDY + SG_ DOOR_OPEN_FL : 37|1@0+ (1,0) [0|1] "" EON + SG_ DOOR_OPEN_FR : 38|1@0+ (1,0) [0|1] "" EON + SG_ DOOR_OPEN_RL : 39|1@0+ (1,0) [0|1] "" EON + SG_ DOOR_OPEN_RR : 40|1@0+ (1,0) [0|1] "" EON + SG_ TRUNK_OPEN : 41|1@0+ (1,0) [0|1] "" EON + SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" EON + SG_ CHECKSUM : 59|4@0+ (1,0) [0|15] "" EON + +CM_ SG_ 304 "Seems to be platform-agnostic"; +CM_ SG_ 316 "Should exist on Nidec"; +CM_ SG_ 420 BRAKE_HOLD_RELATED "On when Brake Hold engaged"; +CM_ SG_ 490 LONG_ACCEL "wheel speed derivative, noisy and zero snapping"; +CM_ SG_ 773 PASS_AIRBAG_ON "Might just be indicator light"; +CM_ SG_ 773 PASS_AIRBAG_OFF "Might just be indicator light"; +CM_ SG_ 780 CRUISE_SPEED "255 = no speed"; +CM_ SG_ 780 PCM_SPEED "Used by Nidec"; +CM_ SG_ 780 PCM_GAS "Used by Nidec"; +CM_ SG_ 804 CRUISE_SPEED_PCM "255 = no speed"; + +VAL_ 780 CRUISE_SPEED 255 "no_speed" 252 "stopped"; +VAL_ 780 HUD_LEAD 3 "acc_off" 2 "solid_car" 1 "dashed_car" 0 "no_car"; +VAL_ 884 DASHBOARD_ALERT 0 "none" 51 "acc_problem" 55 "cmbs_problem" 75 "key_not_detected" 79 "fasten_seatbelt" 111 "lkas_problem" 131 "brake_system_problem" 132 "brake_hold_problem" 139 "tbd" 161 "door_open"; +VAL_ 891 WIPERS 4 "High" 2 "Low" 0 "Off"; + + +CM_ "Imported file _bosch_2018.dbc starts here"; +BO_ 148 KINEMATICS: 8 XXX + SG_ LAT_ACCEL : 7|10@0+ (-0.035,17.92) [-20|20] "m/s2" EON + SG_ LONG_ACCEL : 25|10@0+ (-0.035,17.92) [-20|20] "m/s2" EON + SG_ CHECKSUM : 59|4@0+ (1,0) [0|3] "" EON + SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" EON + +BO_ 228 STEERING_CONTROL: 5 EON + SG_ STEER_TORQUE_REQUEST : 23|1@0+ (1,0) [0|1] "" EPS + SG_ SET_ME_X00 : 22|7@0+ (1,0) [0|127] "" EPS + SG_ SET_ME_X00_2 : 31|8@0+ (1,0) [0|0] "" EPS + SG_ STEER_TORQUE : 7|16@0- (1,0) [-4096|4096] "" EPS + SG_ STEER_DOWN_TO_ZERO : 38|1@0+ (1,0) [0|1] "" EPS + SG_ COUNTER : 37|2@0+ (1,0) [0|3] "" EPS + SG_ CHECKSUM : 35|4@0+ (1,0) [0|15] "" EPS + +BO_ 229 BOSCH_SUPPLEMENTAL_1: 8 XXX + SG_ SET_ME_X04 : 0|8@1+ (1,0) [0|255] "" XXX + SG_ SET_ME_X00 : 8|8@1+ (1,0) [0|255] "" XXX + SG_ SET_ME_X80 : 16|8@1+ (1,0) [0|255] "" XXX + SG_ SET_ME_X10 : 24|8@1+ (1,0) [0|255] "" XXX + SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" XXX + SG_ CHECKSUM : 59|4@0+ (1,0) [0|15] "" XXX + +BO_ 232 BRAKE_HOLD: 7 XXX + SG_ XMISSION_SPEED : 7|14@0- (1,0) [1|0] "" XXX + SG_ COMPUTER_BRAKE : 39|16@0+ (1,0) [0|0] "" XXX + SG_ COMPUTER_BRAKE_REQUEST : 29|1@0+ (1,0) [0|0] "" XXX + SG_ COUNTER : 53|2@0+ (1,0) [0|3] "" XXX + SG_ CHECKSUM : 51|4@0+ (1,0) [0|15] "" XXX + +BO_ 399 STEER_STATUS: 7 EPS + SG_ STEER_TORQUE_SENSOR : 7|16@0- (-1,0) [-31000|31000] "tbd" EON + SG_ STEER_ANGLE_RATE : 23|16@0- (-0.1,0) [-31000|31000] "deg/s" EON + SG_ STEER_STATUS : 39|4@0+ (1,0) [0|15] "" EON + SG_ STEER_CONTROL_ACTIVE : 35|1@0+ (1,0) [0|1] "" EON + SG_ STEER_CONFIG_INDEX : 43|4@0+ (1,0) [0|15] "" EON + SG_ COUNTER : 53|2@0+ (1,0) [0|3] "" EON + SG_ CHECKSUM : 51|4@0+ (1,0) [0|15] "" EON + +BO_ 450 EPB_STATUS: 8 EPB + SG_ EPB_ACTIVE : 3|1@0+ (1,0) [0|1] "" EON + SG_ EPB_STATE : 29|2@0+ (1,0) [0|3] "" EON + SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" XXX + SG_ CHECKSUM : 59|4@0+ (1,0) [0|15] "" XXX + +BO_ 545 XXX_16: 6 SCM + SG_ ECON_ON : 23|1@0+ (1,0) [0|1] "" XXX + SG_ DRIVE_MODE : 37|2@0+ (1,0) [0|3] "" XXX + SG_ COUNTER : 45|2@0+ (1,0) [0|3] "" BDY + SG_ CHECKSUM : 43|4@0+ (1,0) [0|15] "" BDY + +BO_ 576 LEFT_LANE_LINE_1: 8 CAM + SG_ LINE_DISTANCE_VISIBLE : 39|9@0+ (1,0) [0|1] "" XXX + SG_ LINE_PROBABILITY : 46|6@0+ (0.015625,0) [0|1] "" XXX + SG_ LINE_OFFSET : 23|12@0+ (0.004,-8.192) [0|1] "Meters" XXX + SG_ LINE_ANGLE : 7|12@0+ (0.0005,-1.024) [0|1] "" XXX + SG_ FRAME_INDEX : 8|4@1+ (1,0) [0|15] "" XXX + SG_ COUNTER : 61|2@0+ (1,0) [0|1] "" XXX + SG_ CHECKSUM : 59|4@0+ (1,0) [0|1] "" XXX + +BO_ 577 LEFT_LANE_LINE_2: 8 CAM + SG_ LINE_FAR_EDGE_POSITION : 55|8@0+ (1,-128) [0|1] "" XXX + SG_ LINE_SOLID : 13|1@0+ (1,0) [0|1] "" XXX + SG_ LINE_DASHED : 14|1@0+ (1,0) [0|1] "" XXX + SG_ LINE_CURVATURE : 23|12@0+ (0.00001,-0.02048) [0|1] "" XXX + SG_ LINE_PARAMETER : 39|12@0+ (1,0) [0|1] "" XXX + SG_ FRAME_INDEX : 7|4@0+ (1,0) [0|15] "" XXX + SG_ COUNTER : 61|2@0+ (1,0) [0|1] "" XXX + SG_ CHECKSUM : 59|4@0+ (1,0) [0|1] "" XXX + +BO_ 579 RIGHT_LANE_LINE_1: 8 CAM + SG_ LINE_DISTANCE_VISIBLE : 39|9@0+ (1,0) [0|1] "" XXX + SG_ LINE_PROBABILITY : 46|6@0+ (0.015625,0) [0|1] "" XXX + SG_ LINE_OFFSET : 23|12@0+ (0.004,-8.192) [0|1] "Meters" XXX + SG_ LINE_ANGLE : 7|12@0+ (0.0005,-1.024) [0|1] "" XXX + SG_ FRAME_INDEX : 8|4@1+ (1,0) [0|15] "" XXX + SG_ COUNTER : 61|2@0+ (1,0) [0|1] "" XXX + SG_ CHECKSUM : 59|4@0+ (1,0) [0|1] "" XXX + +BO_ 580 RIGHT_LANE_LINE_2: 8 CAM + SG_ LINE_FAR_EDGE_POSITION : 55|8@0+ (1,-128) [0|1] "" XXX + SG_ LINE_SOLID : 13|1@0+ (1,0) [0|1] "" XXX + SG_ LINE_DASHED : 14|1@0+ (1,0) [0|1] "" XXX + SG_ LINE_CURVATURE : 23|12@0+ (0.00001,-0.02048) [0|1] "" XXX + SG_ LINE_PARAMETER : 39|12@0+ (1,0) [0|1] "" XXX + SG_ FRAME_INDEX : 7|4@0+ (1,0) [0|15] "" XXX + SG_ COUNTER : 61|2@0+ (1,0) [0|1] "" XXX + SG_ CHECKSUM : 59|4@0+ (1,0) [0|1] "" XXX + +BO_ 582 ADJACENT_LEFT_LANE_LINE_1: 8 CAM + SG_ LINE_DISTANCE_VISIBLE : 39|9@0+ (1,0) [0|1] "" XXX + SG_ LINE_PROBABILITY : 46|6@0+ (0.015625,0) [0|1] "" XXX + SG_ LINE_OFFSET : 23|12@0+ (0.004,-8.192) [0|1] "Meters" XXX + SG_ LINE_ANGLE : 7|12@0+ (0.0005,-1.024) [0|1] "" XXX + SG_ FRAME_INDEX : 8|4@1+ (1,0) [0|15] "" XXX + SG_ COUNTER : 61|2@0+ (1,0) [0|1] "" XXX + SG_ CHECKSUM : 59|4@0+ (1,0) [0|1] "" XXX + +BO_ 583 ADJACENT_LEFT_LANE_LINE_2: 8 CAM + SG_ LINE_FAR_EDGE_POSITION : 55|8@0+ (1,-128) [0|1] "" XXX + SG_ LINE_SOLID : 13|1@0+ (1,0) [0|1] "" XXX + SG_ LINE_DASHED : 14|1@0+ (1,0) [0|1] "" XXX + SG_ LINE_CURVATURE : 23|12@0+ (0.00001,-0.02048) [0|1] "" XXX + SG_ LINE_PARAMETER : 39|12@0+ (1,0) [0|1] "" XXX + SG_ FRAME_INDEX : 7|4@0+ (1,0) [0|15] "" XXX + SG_ COUNTER : 61|2@0+ (1,0) [0|1] "" XXX + SG_ CHECKSUM : 59|4@0+ (1,0) [0|1] "" XXX + +BO_ 585 ADJACENT_RIGHT_LANE_LINE_1: 8 CAM + SG_ LINE_DISTANCE_VISIBLE : 39|9@0+ (1,0) [0|1] "" XXX + SG_ LINE_PROBABILITY : 46|6@0+ (0.015625,0) [0|1] "" XXX + SG_ LINE_OFFSET : 23|12@0+ (0.004,-8.192) [0|1] "Meters" XXX + SG_ LINE_ANGLE : 7|12@0+ (0.0005,-1.024) [0|1] "" XXX + SG_ FRAME_INDEX : 8|4@1+ (1,0) [0|15] "" XXX + SG_ COUNTER : 61|2@0+ (1,0) [0|1] "" XXX + SG_ CHECKSUM : 59|4@0+ (1,0) [0|1] "" XXX + +BO_ 586 ADJACENT_RIGHT_LANE_LINE_2: 8 CAM + SG_ LINE_FAR_EDGE_POSITION : 55|8@0+ (1,-128) [0|1] "" XXX + SG_ LINE_SOLID : 13|1@0+ (1,0) [0|1] "" XXX + SG_ LINE_DASHED : 14|1@0+ (1,0) [0|1] "" XXX + SG_ LINE_CURVATURE : 23|12@0+ (0.00001,-0.02048) [0|1] "" XXX + SG_ LINE_PARAMETER : 39|12@0+ (1,0) [0|1] "" XXX + SG_ FRAME_INDEX : 7|4@0+ (1,0) [0|15] "" XXX + SG_ COUNTER : 61|2@0+ (1,0) [0|1] "" XXX + SG_ CHECKSUM : 59|4@0+ (1,0) [0|1] "" XXX + +BO_ 597 ROUGH_WHEEL_SPEED: 8 VSA + SG_ WHEEL_SPEED_FL : 7|8@0+ (1,0) [0|255] "mph" EON + SG_ WHEEL_SPEED_FR : 15|8@0+ (1,0) [0|255] "mph" EON + SG_ WHEEL_SPEED_RL : 23|8@0+ (1,0) [0|255] "mph" EON + SG_ WHEEL_SPEED_RR : 31|8@0+ (1,0) [0|255] "mph" EON + SG_ SET_TO_X55 : 39|8@0+ (1,0) [0|255] "" XXX + SG_ SET_TO_X55_2 : 47|8@0+ (1,0) [0|255] "" EON + SG_ LONG_COUNTER : 55|8@0+ (1,0) [0|255] "" XXX + SG_ CHECKSUM : 59|4@0+ (1,0) [0|15] "" XXX + SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" XXX + +BO_ 662 SCM_BUTTONS: 4 SCM + SG_ CRUISE_BUTTONS : 7|3@0+ (1,0) [0|7] "" EON + SG_ CRUISE_SETTING : 3|2@0+ (1,0) [0|3] "" EON + SG_ COUNTER : 29|2@0+ (1,0) [0|3] "" EON + SG_ CHECKSUM : 27|4@0+ (1,0) [0|15] "" EON + +BO_ 806 SCM_FEEDBACK: 8 SCM + SG_ DRIVERS_DOOR_OPEN : 17|1@0+ (1,0) [0|1] "" XXX + SG_ MAIN_ON : 28|1@0+ (1,0) [0|1] "" EON + SG_ RIGHT_BLINKER : 27|1@0+ (1,0) [0|1] "" EON + SG_ LEFT_BLINKER : 26|1@0+ (1,0) [0|1] "" EON + SG_ CMBS_STATES : 22|2@0+ (1,0) [0|3] "" EON + SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" XXX + SG_ CHECKSUM : 59|4@0+ (1,0) [0|15] "" XXX + +BO_ 862 CAMERA_MESSAGES: 8 CAM + SG_ ZEROS_BOH : 7|50@0+ (1,0) [0|127] "" BDY + SG_ AUTO_HIGHBEAMS_ACTIVE : 53|1@0+ (1,0) [0|1] "" XXX + SG_ HIGHBEAMS_ON : 52|1@0+ (1,0) [0|1] "" XXX + SG_ ZEROS_BOH_2 : 51|4@0+ (1,0) [0|15] "" XXX + SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" XXX + SG_ CHECKSUM : 59|4@0+ (1,0) [0|15] "" XXX + +BO_ 927 RADAR_HUD: 8 RADAR + SG_ ZEROS_BOH : 7|10@0+ (1,0) [0|127] "" BDY + SG_ CMBS_OFF : 12|1@0+ (1,0) [0|1] "" BDY + SG_ RESUME_INSTRUCTION : 21|1@0+ (1,0) [0|1] "" XXX + SG_ SET_TO_1 : 13|1@0+ (1,0) [0|1] "" BDY + SG_ ZEROS_BOH2 : 11|4@0+ (1,0) [0|1] "" XXX + SG_ APPLY_BRAKES_FOR_CANC : 23|1@0+ (1,0) [0|1] "" XXX + SG_ ACC_ALERTS : 20|5@0+ (1,0) [0|1] "" BDY + SG_ SET_TO_0 : 22|1@0+ (1,0) [0|1] "" XXX + SG_ HUD_LEAD : 40|1@0+ (1,0) [0|1] "" XXX + SG_ SET_TO_64 : 31|8@0+ (1,0) [0|255] "" XXX + SG_ LEAD_DISTANCE : 39|8@0+ (1,0) [0|255] "" XXX + SG_ ZEROS_BOH3 : 47|7@0+ (1,0) [0|127] "" XXX + SG_ ZEROS_BOH4 : 55|8@0+ (1,0) [0|255] "" XXX + SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" XXX + SG_ CHECKSUM : 59|4@0+ (1,0) [0|15] "" XXX + +BO_ 13274 LKAS_HUD_A: 5 ADAS + SG_ CAM_TEMP_HIGH : 7|1@0+ (1,0) [0|255] "" BDY + SG_ SET_ME_X41 : 6|7@0+ (1,0) [0|127] "" BDY + SG_ BOH : 6|7@0+ (1,0) [0|127] "" BDY + SG_ DASHED_LANES : 14|1@0+ (1,0) [0|1] "" BDY + SG_ DTC : 13|1@0+ (1,0) [0|1] "" BDY + SG_ LKAS_PROBLEM : 12|1@0+ (1,0) [0|1] "" BDY + SG_ LKAS_OFF : 11|1@0+ (1,0) [0|1] "" BDY + SG_ SOLID_LANES : 10|1@0+ (1,0) [0|1] "" BDY + SG_ LDW_RIGHT : 9|1@0+ (1,0) [0|1] "" BDY + SG_ STEERING_REQUIRED : 8|1@0+ (1,0) [0|1] "" BDY + SG_ BOH : 23|2@0+ (1,0) [0|4] "" BDY + SG_ LDW_PROBLEM : 21|1@0+ (1,0) [0|1] "" BDY + SG_ BEEP : 17|2@0+ (1,0) [0|1] "" BDY + SG_ SET_ME_X01 : 20|1@0+ (1,0) [0|1] "" BDY + SG_ LDW_ON : 28|1@0+ (1,0) [0|1] "" BDY + SG_ LDW_OFF : 27|1@0+ (1,0) [0|1] "" BDY + SG_ CLEAN_WINDSHIELD : 26|1@0+ (1,0) [0|1] "" BDY + SG_ COUNTER : 37|2@0+ (1,0) [0|3] "" BDY + SG_ CHECKSUM : 35|4@0+ (1,0) [0|15] "" BDY + +BO_ 13275 LKAS_HUD_B: 8 ADAS + SG_ CAM_TEMP_HIGH : 7|1@0+ (1,0) [0|255] "" BDY + SG_ SET_ME_X41 : 6|7@0+ (1,0) [0|127] "" BDY + SG_ BOH : 6|7@0+ (1,0) [0|127] "" BDY + SG_ DASHED_LANES : 14|1@0+ (1,0) [0|1] "" BDY + SG_ DTC : 13|1@0+ (1,0) [0|1] "" BDY + SG_ LKAS_PROBLEM : 12|1@0+ (1,0) [0|1] "" BDY + SG_ LKAS_OFF : 11|1@0+ (1,0) [0|1] "" BDY + SG_ SOLID_LANES : 10|1@0+ (1,0) [0|1] "" BDY + SG_ LDW_RIGHT : 9|1@0+ (1,0) [0|1] "" BDY + SG_ STEERING_REQUIRED : 8|1@0+ (1,0) [0|1] "" BDY + SG_ BOH : 23|2@0+ (1,0) [0|4] "" BDY + SG_ LDW_PROBLEM : 21|1@0+ (1,0) [0|1] "" BDY + SG_ BEEP : 17|2@0+ (1,0) [0|1] "" BDY + SG_ SET_ME_X01 : 20|1@0+ (1,0) [0|1] "" BDY + SG_ LDW_ON : 28|1@0+ (1,0) [0|1] "" BDY + SG_ LDW_OFF : 27|1@0+ (1,0) [0|1] "" BDY + SG_ CLEAN_WINDSHIELD : 26|1@0+ (1,0) [0|1] "" BDY + SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" BDY + SG_ CHECKSUM : 59|4@0+ (1,0) [0|15] "" BDY + +CM_ SG_ 450 EPB_STATE "3: On, 2: Disengaging, 1: Engaging, 0: Off"; +CM_ SG_ 576 LINE_DISTANCE_VISIBLE "Length of line visible, undecoded"; +CM_ SG_ 577 LINE_FAR_EDGE_POSITION "Appears to be a measure of line thickness, indicates location of the portion of the line furthest from the car, undecoded"; +CM_ SG_ 577 LINE_PARAMETER "Unclear if this is low quality line curvature rate or if this is something else, but it is correlated with line curvature, undecoded"; +CM_ SG_ 577 LINE_DASHED "1 = line is dashed"; +CM_ SG_ 577 LINE_SOLID "1 = line is solid"; + +VAL_ 399 STEER_STATUS 6 "tmp_fault" 5 "fault_1" 4 "no_torque_alert_2" 3 "low_speed_lockout" 2 "no_torque_alert_1" 0 "normal"; + + +CM_ "Imported file _steering_sensors_a.dbc starts here"; +BO_ 330 STEERING_SENSORS: 8 EPS + SG_ STEER_ANGLE : 7|16@0- (-0.1,0) [-500|500] "deg" EON + SG_ STEER_ANGLE_RATE : 23|16@0- (-1,0) [-3000|3000] "deg/s" EON + SG_ STEER_SENSOR_STATUS_1 : 34|1@0+ (1,0) [0|1] "" EON + SG_ STEER_SENSOR_STATUS_2 : 33|1@0+ (1,0) [0|1] "" EON + SG_ STEER_SENSOR_STATUS_3 : 32|1@0+ (1,0) [0|1] "" EON + SG_ STEER_WHEEL_ANGLE : 47|16@0- (-0.1,0) [-500|500] "deg" EON + SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" EON + SG_ CHECKSUM : 59|4@0+ (1,0) [0|15] "" EON + +CM_ "honda_civic_ex_2022_can.dbc starts here"; + +BO_ 401 GEARBOX: 8 PCM + SG_ GEAR_SHIFTER : 5|6@0+ (1,0) [0|63] "" EON + SG_ BOH : 45|6@0+ (1,0) [0|63] "" XXX + SG_ GEAR2 : 31|8@0+ (1,0) [0|1] "" XXX + SG_ GEAR : 39|8@0+ (1,0) [0|255] "" XXX + SG_ ZEROS_BOH : 47|2@0+ (1,0) [0|3] "" XXX + SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" EON + SG_ CHECKSUM : 59|4@0+ (1,0) [0|15] "" EON + +BO_ 432 STANDSTILL: 7 VSA + SG_ WHEELS_MOVING : 12|1@0+ (1,0) [0|1] "" EON + SG_ BRAKE_ERROR_1 : 11|1@0+ (1,0) [0|1] "" EON + SG_ BRAKE_ERROR_2 : 9|1@0+ (1,0) [0|1] "" EON + SG_ COUNTER : 53|2@0+ (1,0) [0|3] "" EON + SG_ CHECKSUM : 51|4@0+ (1,0) [0|15] "" EON + +BO_ 456 ACC_CONTROL: 8 XXX + SG_ ACCEL_COMMAND : 7|12@0- (0.01,0) [0|0] "m/s^2" XXX + SG_ CONTROL_ON : 10|1@0+ (1,0) [0|1] "" XXX + SG_ CONTROL_OFF : 8|1@0+ (1,0) [0|1] "" XXX + SG_ CHECKSUM : 59|4@0+ (1,0) [0|15] "" XXX + SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" XXX + +BO_ 829 LKAS_HUD: 8 ADAS + SG_ CAM_TEMP_HIGH : 7|1@0+ (1,0) [0|255] "" BDY + SG_ SET_ME_X41 : 6|7@0+ (1,0) [0|127] "" BDY + SG_ BOH : 6|7@0+ (1,0) [0|127] "" BDY + SG_ DASHED_LANES : 14|1@0+ (1,0) [0|1] "" BDY + SG_ DTC : 13|1@0+ (1,0) [0|1] "" BDY + SG_ LKAS_PROBLEM : 12|1@0+ (1,0) [0|1] "" BDY + SG_ LKAS_OFF : 11|1@0+ (1,0) [0|1] "" BDY + SG_ SOLID_LANES : 10|1@0+ (1,0) [0|1] "" BDY + SG_ LDW_RIGHT : 9|1@0+ (1,0) [0|1] "" BDY + SG_ STEERING_REQUIRED : 8|1@0+ (1,0) [0|1] "" BDY + SG_ BOH : 23|2@0+ (1,0) [0|4] "" BDY + SG_ LDW_PROBLEM : 21|1@0+ (1,0) [0|1] "" BDY + SG_ BEEP : 17|2@0+ (1,0) [0|1] "" BDY + SG_ LDW_ON : 28|1@0+ (1,0) [0|1] "" BDY + SG_ LDW_OFF : 27|1@0+ (1,0) [0|1] "" BDY + SG_ CLEAN_WINDSHIELD : 26|1@0+ (1,0) [0|1] "" BDY + SG_ SET_ME_X48 : 31|8@0+ (1,0) [0|255] "" BDY + SG_ LANE_LINES : 36|2@0+ (1,0) [0|3] "" BDY + SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" BDY + SG_ CHECKSUM : 59|4@0+ (1,0) [0|15] "" BDY + +BO_ 882 CRUISE_PARAMS: 8 PCM + SG_ CRUISE_SPEED_OFFSET : 31|8@0- (0.1,0) [-128|127] "kph" EON + SG_ CHECKSUM : 59|4@0+ (1,0) [0|3] "" EON + SG_ COUNTER : 61|2@0+ (1,0) [0|15] "" EON + +BO_ 254913108 LKAS_HUD_2: 8 ADAS + SG_ COUNTER_2 : 7|2@0+ (1,0) [0|3] "" XXX + SG_ SET_ME_X01 : 5|1@0+ (1,0) [0|1] "" XXX + SG_ LKAS_BOH_1 : 15|6@0+ (1,0) [0|63] "" XXX + SG_ LEFT_LANE : 23|2@0+ (1,0) [0|3] "" XXX + SG_ RIGHT_LANE : 21|2@0+ (1,0) [0|3] "" XXX + SG_ LKAS_BOH_2 : 30|5@0+ (1,0) [0|31] "" XXX + SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" XXX + SG_ CHECKSUM : 59|4@0+ (1,0) [0|15] "" XXX + +CM_ BO 456 "possible ACC message, sent from the camera"; +CM_ BO 456 "not sure which bit enables cruise"; +CM_ SG_ 456 ACCEL_COMMAND "seems to be m/s^2? need to verify"; + +VAL_ 401 GEAR_SHIFTER 32 "L" 16 "S" 8 "D" 4 "N" 2 "R" 1 "P"; +VAL_ 401 GEAR 7 "L" 10 "S" 4 "D" 3 "N" 2 "R" 1 "P"; diff --git a/opendbc/honda_civic_hatchback_ex_2017_can_generated.dbc b/opendbc/honda_civic_hatchback_ex_2017_can_generated.dbc index bd972f7f8..349fd2f9e 100644 --- a/opendbc/honda_civic_hatchback_ex_2017_can_generated.dbc +++ b/opendbc/honda_civic_hatchback_ex_2017_can_generated.dbc @@ -76,6 +76,43 @@ BO_ 773 SEATBELT_STATUS: 7 BDY SG_ COUNTER : 53|2@0+ (1,0) [0|3] "" EON SG_ CHECKSUM : 51|4@0+ (1,0) [0|3] "" EON +BO_ 777 CAR_SPEED: 8 PCM + SG_ ROUGH_CAR_SPEED : 23|8@0+ (1,0) [0|255] "mph" XXX + SG_ CAR_SPEED : 7|16@0+ (0.01,0) [0|65535] "kph" XXX + SG_ ROUGH_CAR_SPEED_3 : 39|16@0+ (0.01,0) [0|65535] "kph" XXX + SG_ ROUGH_CAR_SPEED_2 : 31|8@0+ (1,0) [0|255] "mph" XXX + SG_ LOCK_STATUS : 55|2@0+ (1,0) [0|255] "" XXX + SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" XXX + SG_ CHECKSUM : 59|4@0+ (1,0) [0|15] "" XXX + SG_ IMPERIAL_UNIT : 63|1@0+ (1,0) [0|1] "" XXX + +BO_ 780 ACC_HUD: 8 ADAS + SG_ PCM_SPEED : 7|16@0+ (0.01,0) [0|250] "kph" BDY + SG_ PCM_GAS : 23|8@0+ (1,0) [0|127] "" BDY + SG_ CRUISE_SPEED : 31|8@0+ (1,0) [0|255] "kph" BDY + SG_ DTC_MODE : 39|1@0+ (1,0) [0|1] "" BDY + SG_ BOH : 38|1@0+ (1,0) [0|1] "" BDY + SG_ ACC_PROBLEM : 37|1@0+ (1,0) [0|1] "" BDY + SG_ FCM_OFF : 35|1@0+ (1,0) [0|1] "" BDY + SG_ FCM_OFF_2 : 36|1@0+ (1,0) [0|1] "" BDY + SG_ FCM_PROBLEM : 34|1@0+ (1,0) [0|1] "" BDY + SG_ RADAR_OBSTRUCTED : 33|1@0+ (1,0) [0|1] "" BDY + SG_ ENABLE_MINI_CAR : 32|1@0+ (1,0) [0|1] "" BDY + SG_ HUD_DISTANCE : 47|2@0+ (1,0) [0|3] "" BDY + SG_ HUD_LEAD : 45|2@0+ (1,0) [0|3] "" BDY + SG_ BOH_3 : 43|1@0+ (1,0) [0|3] "" BDY + SG_ BOH_4 : 42|1@0+ (1,0) [0|3] "" BDY + SG_ BOH_5 : 41|1@0+ (1,0) [0|3] "" BDY + SG_ CRUISE_CONTROL_LABEL : 40|1@0+ (1,0) [0|3] "" BDY + SG_ SET_ME_X01_2 : 55|1@0+ (1,0) [0|1] "" BDY + SG_ IMPERIAL_UNIT : 54|1@0+ (1,0) [0|1] "" BDY + SG_ ACC_ON : 52|1@0+ (1,0) [0|1] "" BDY + SG_ CHIME : 51|3@0+ (1,0) [0|1] "" BDY + SG_ SET_ME_X01 : 48|1@0+ (1,0) [0|1] "" BDY + SG_ ICONS : 63|2@0+ (1,0) [0|1] "" BDY + SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" BDY + SG_ CHECKSUM : 59|4@0+ (1,0) [0|3] "" BDY + BO_ 804 CRUISE: 8 PCM SG_ HUD_SPEED_KPH : 7|8@0+ (1,0) [0|255] "kph" EON SG_ HUD_SPEED_MPH : 15|8@0+ (1,0) [0|255] "mph" EON @@ -86,27 +123,6 @@ BO_ 804 CRUISE: 8 PCM SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" EON SG_ CHECKSUM : 59|4@0+ (1,0) [0|15] "" EON -BO_ 829 LKAS_HUD: 5 ADAS - SG_ CAM_TEMP_HIGH : 7|1@0+ (1,0) [0|255] "" BDY - SG_ SET_ME_X41 : 6|7@0+ (1,0) [0|127] "" BDY - SG_ BOH : 6|7@0+ (1,0) [0|127] "" BDY - SG_ DASHED_LANES : 14|1@0+ (1,0) [0|1] "" BDY - SG_ DTC : 13|1@0+ (1,0) [0|1] "" BDY - SG_ LKAS_PROBLEM : 12|1@0+ (1,0) [0|1] "" BDY - SG_ LKAS_OFF : 11|1@0+ (1,0) [0|1] "" BDY - SG_ SOLID_LANES : 10|1@0+ (1,0) [0|1] "" BDY - SG_ LDW_RIGHT : 9|1@0+ (1,0) [0|1] "" BDY - SG_ STEERING_REQUIRED : 8|1@0+ (1,0) [0|1] "" BDY - SG_ BOH : 23|2@0+ (1,0) [0|4] "" BDY - SG_ LDW_PROBLEM : 21|1@0+ (1,0) [0|1] "" BDY - SG_ BEEP : 17|2@0+ (1,0) [0|1] "" BDY - SG_ LDW_ON : 28|1@0+ (1,0) [0|1] "" BDY - SG_ LDW_OFF : 27|1@0+ (1,0) [0|1] "" BDY - SG_ CLEAN_WINDSHIELD : 26|1@0+ (1,0) [0|1] "" BDY - SG_ SET_ME_X48 : 31|8@0+ (1,0) [0|255] "" BDY - SG_ COUNTER : 37|2@0+ (1,0) [0|3] "" BDY - SG_ CHECKSUM : 35|4@0+ (1,0) [0|15] "" BDY - BO_ 884 STALK_STATUS: 8 XXX SG_ DASHBOARD_ALERT : 39|8@0+ (1,0) [0|255] "" EON SG_ AUTO_HEADLIGHTS : 46|1@0+ (1,0) [0|1] "" EON @@ -140,10 +156,13 @@ CM_ SG_ 420 BRAKE_HOLD_RELATED "On when Brake Hold engaged"; CM_ SG_ 490 LONG_ACCEL "wheel speed derivative, noisy and zero snapping"; CM_ SG_ 773 PASS_AIRBAG_ON "Might just be indicator light"; CM_ SG_ 773 PASS_AIRBAG_OFF "Might just be indicator light"; +CM_ SG_ 780 CRUISE_SPEED "255 = no speed"; +CM_ SG_ 780 PCM_SPEED "Used by Nidec"; +CM_ SG_ 780 PCM_GAS "Used by Nidec"; CM_ SG_ 804 CRUISE_SPEED_PCM "255 = no speed"; -CM_ SG_ 829 BEEP "beeps are pleasant, chimes are for warnngs etc..."; -VAL_ 829 BEEP 3 "single_beep" 2 "triple_beep" 1 "repeated_beep" 0 "no_beep"; +VAL_ 780 CRUISE_SPEED 255 "no_speed" 252 "stopped"; +VAL_ 780 HUD_LEAD 3 "acc_off" 2 "solid_car" 1 "dashed_car" 0 "no_car"; VAL_ 884 DASHBOARD_ALERT 0 "none" 51 "acc_problem" 55 "cmbs_problem" 75 "key_not_detected" 79 "fasten_seatbelt" 111 "lkas_problem" 131 "brake_system_problem" 132 "brake_hold_problem" 139 "tbd" 161 "door_open"; VAL_ 891 WIPERS 4 "High" 2 "Low" 0 "Off"; @@ -194,32 +213,6 @@ BO_ 450 EPB_STATUS: 8 EPB SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" XXX SG_ CHECKSUM : 59|4@0+ (1,0) [0|15] "" XXX -BO_ 479 ACC_CONTROL: 8 EON - SG_ SET_TO_0 : 20|5@0+ (1,0) [0|1] "" XXX - SG_ CONTROL_ON : 23|3@0+ (1,0) [0|5] "" XXX - SG_ GAS_COMMAND : 7|16@0- (1,0) [0|0] "" XXX - SG_ ACCEL_COMMAND : 31|11@0- (0.01,0) [0|0] "m/s2" XXX - SG_ BRAKE_LIGHTS : 62|1@0+ (1,0) [0|1] "" XXX - SG_ BRAKE_REQUEST : 34|1@0+ (1,0) [0|1] "" XXX - SG_ STANDSTILL : 35|1@0+ (1,0) [0|1] "" XXX - SG_ STANDSTILL_RELEASE : 36|1@0+ (1,0) [0|1] "" XXX - SG_ AEB_STATUS : 33|1@0+ (1,0) [0|1] "" XXX - SG_ AEB_BRAKING : 47|1@0+ (1,0) [0|1] "" XXX - SG_ AEB_PREPARE : 43|1@0+ (1,0) [0|1] "" XXX - SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" XXX - SG_ CHECKSUM : 59|4@0+ (1,0) [0|15] "" XXX - -BO_ 495 ACC_CONTROL_ON: 8 XXX - SG_ SET_TO_75 : 31|8@0+ (1,0) [0|255] "" XXX - SG_ SET_TO_30 : 39|8@0+ (1,0) [0|255] "" XXX - SG_ ZEROS_BOH : 23|8@0+ (1,0) [0|255] "" XXX - SG_ ZEROS_BOH2 : 47|16@0+ (1,0) [0|255] "" XXX - SG_ SET_TO_FF : 15|8@0+ (1,0) [0|255] "" XXX - SG_ SET_TO_3 : 6|7@0+ (1,0) [0|4095] "" XXX - SG_ CONTROL_ON : 7|1@0+ (1,0) [0|1] "" XXX - SG_ CHECKSUM : 59|4@0+ (1,0) [0|15] "" XXX - SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" XXX - BO_ 545 XXX_16: 6 SCM SG_ ECON_ON : 23|1@0+ (1,0) [0|1] "" XXX SG_ DRIVE_MODE : 37|2@0+ (1,0) [0|3] "" XXX @@ -319,40 +312,6 @@ BO_ 662 SCM_BUTTONS: 4 SCM SG_ COUNTER : 29|2@0+ (1,0) [0|3] "" EON SG_ CHECKSUM : 27|4@0+ (1,0) [0|15] "" EON -BO_ 777 CAR_SPEED: 8 PCM - SG_ ROUGH_CAR_SPEED : 23|8@0+ (1,0) [0|255] "mph" XXX - SG_ CAR_SPEED : 7|16@0+ (0.01,0) [0|65535] "kph" XXX - SG_ ROUGH_CAR_SPEED_3 : 39|16@0+ (0.01,0) [0|65535] "kph" XXX - SG_ ROUGH_CAR_SPEED_2 : 31|8@0+ (1,0) [0|255] "mph" XXX - SG_ LOCK_STATUS : 55|2@0+ (1,0) [0|255] "" XXX - SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" XXX - SG_ CHECKSUM : 59|4@0+ (1,0) [0|15] "" XXX - SG_ IMPERIAL_UNIT : 63|1@0+ (1,0) [0|1] "" XXX - -BO_ 780 ACC_HUD: 8 ADAS - SG_ CRUISE_SPEED : 31|8@0+ (1,0) [0|255] "kph" BDY - SG_ DTC_MODE : 39|1@0+ (1,0) [0|1] "" BDY - SG_ BOH : 38|1@0+ (1,0) [0|1] "" BDY - SG_ FCM_PROBLEM : 34|1@0+ (1,0) [0|1] "" BDY - SG_ RADAR_OBSTRUCTED : 33|1@0+ (1,0) [0|1] "" BDY - SG_ ENABLE_MINI_CAR : 32|1@0+ (1,0) [0|1] "" BDY - SG_ BOH_3 : 43|1@0+ (1,0) [0|3] "" BDY - SG_ BOH_4 : 42|1@0+ (1,0) [0|3] "" BDY - SG_ BOH_5 : 41|1@0+ (1,0) [0|3] "" BDY - SG_ CRUISE_CONTROL_LABEL : 40|1@0+ (1,0) [0|3] "" BDY - SG_ ZEROS_BOH : 7|24@0+ (0.002759506,0) [0|100] "m/s" BDY - SG_ FCM_OFF : 35|1@0+ (1,0) [0|1] "" BDY - SG_ SET_TO_1 : 36|1@0+ (1,0) [0|1] "" XXX - SG_ HUD_DISTANCE : 47|2@0+ (1,0) [0|3] "" BDY - SG_ HUD_LEAD : 45|2@0+ (1,0) [0|3] "" BDY - SG_ ACC_PROBLEM : 37|1@0+ (1,0) [0|1] "" BDY - SG_ ACC_ON : 52|1@0+ (1,0) [0|1] "" XXX - SG_ BOH_6 : 51|4@0+ (1,0) [0|15] "" XXX - SG_ SET_TO_X1 : 55|1@0+ (1,0) [0|1] "" XXX - SG_ IMPERIAL_UNIT : 54|1@0+ (1,0) [0|1] "" XXX - SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" XXX - SG_ CHECKSUM : 59|4@0+ (1,0) [0|15] "" XXX - BO_ 806 SCM_FEEDBACK: 8 SCM SG_ DRIVERS_DOOR_OPEN : 17|1@0+ (1,0) [0|1] "" XXX SG_ MAIN_ON : 28|1@0+ (1,0) [0|1] "" EON @@ -370,6 +329,23 @@ BO_ 862 CAMERA_MESSAGES: 8 CAM SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" XXX SG_ CHECKSUM : 59|4@0+ (1,0) [0|15] "" XXX +BO_ 927 RADAR_HUD: 8 RADAR + SG_ ZEROS_BOH : 7|10@0+ (1,0) [0|127] "" BDY + SG_ CMBS_OFF : 12|1@0+ (1,0) [0|1] "" BDY + SG_ RESUME_INSTRUCTION : 21|1@0+ (1,0) [0|1] "" XXX + SG_ SET_TO_1 : 13|1@0+ (1,0) [0|1] "" BDY + SG_ ZEROS_BOH2 : 11|4@0+ (1,0) [0|1] "" XXX + SG_ APPLY_BRAKES_FOR_CANC : 23|1@0+ (1,0) [0|1] "" XXX + SG_ ACC_ALERTS : 20|5@0+ (1,0) [0|1] "" BDY + SG_ SET_TO_0 : 22|1@0+ (1,0) [0|1] "" XXX + SG_ HUD_LEAD : 40|1@0+ (1,0) [0|1] "" XXX + SG_ SET_TO_64 : 31|8@0+ (1,0) [0|255] "" XXX + SG_ LEAD_DISTANCE : 39|8@0+ (1,0) [0|255] "" XXX + SG_ ZEROS_BOH3 : 47|7@0+ (1,0) [0|127] "" XXX + SG_ ZEROS_BOH4 : 55|8@0+ (1,0) [0|255] "" XXX + SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" XXX + SG_ CHECKSUM : 59|4@0+ (1,0) [0|15] "" XXX + BO_ 13274 LKAS_HUD_A: 5 ADAS SG_ CAM_TEMP_HIGH : 7|1@0+ (1,0) [0|255] "" BDY SG_ SET_ME_X41 : 6|7@0+ (1,0) [0|127] "" BDY @@ -413,10 +389,6 @@ BO_ 13275 LKAS_HUD_B: 8 ADAS SG_ CHECKSUM : 59|4@0+ (1,0) [0|15] "" BDY CM_ SG_ 450 EPB_STATE "3: On, 2: Disengaging, 1: Engaging, 0: Off"; -CM_ SG_ 479 CONTROL_ON "Set to 5 when car is being controlled"; -CM_ SG_ 479 AEB_STATUS "set for the duration of AEB event"; -CM_ SG_ 479 AEB_BRAKING "set when braking is commanded during AEB event"; -CM_ SG_ 479 AEB_PREPARE "set 1s before AEB"; CM_ SG_ 576 LINE_DISTANCE_VISIBLE "Length of line visible, undecoded"; CM_ SG_ 577 LINE_FAR_EDGE_POSITION "Appears to be a measure of line thickness, indicates location of the portion of the line furthest from the car, undecoded"; CM_ SG_ 577 LINE_PARAMETER "Unclear if this is low quality line curvature rate or if this is something else, but it is correlated with line curvature, undecoded"; @@ -425,8 +397,65 @@ CM_ SG_ 577 LINE_SOLID "1 = line is solid"; VAL_ 399 STEER_STATUS 6 "tmp_fault" 5 "fault_1" 4 "no_torque_alert_2" 3 "low_speed_lockout" 2 "no_torque_alert_1" 0 "normal"; -CM_ "honda_civic_hatchback_ex_2017_can.dbc starts here"; +CM_ "Imported file _bosch_adas_2018.dbc starts here"; +BO_ 479 ACC_CONTROL: 8 EON + SG_ SET_TO_0 : 20|5@0+ (1,0) [0|1] "" XXX + SG_ CONTROL_ON : 23|3@0+ (1,0) [0|5] "" XXX + SG_ GAS_COMMAND : 7|16@0- (1,0) [0|0] "" XXX + SG_ ACCEL_COMMAND : 31|11@0- (0.01,0) [0|0] "m/s2" XXX + SG_ BRAKE_LIGHTS : 62|1@0+ (1,0) [0|1] "" XXX + SG_ BRAKE_REQUEST : 34|1@0+ (1,0) [0|1] "" XXX + SG_ STANDSTILL : 35|1@0+ (1,0) [0|1] "" XXX + SG_ STANDSTILL_RELEASE : 36|1@0+ (1,0) [0|1] "" XXX + SG_ AEB_STATUS : 33|1@0+ (1,0) [0|1] "" XXX + SG_ AEB_BRAKING : 47|1@0+ (1,0) [0|1] "" XXX + SG_ AEB_PREPARE : 43|1@0+ (1,0) [0|1] "" XXX + SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" XXX + SG_ CHECKSUM : 59|4@0+ (1,0) [0|15] "" XXX + +BO_ 495 ACC_CONTROL_ON: 8 XXX + SG_ SET_TO_75 : 31|8@0+ (1,0) [0|255] "" XXX + SG_ SET_TO_30 : 39|8@0+ (1,0) [0|255] "" XXX + SG_ ZEROS_BOH : 23|8@0+ (1,0) [0|255] "" XXX + SG_ ZEROS_BOH2 : 47|16@0+ (1,0) [0|255] "" XXX + SG_ SET_TO_FF : 15|8@0+ (1,0) [0|255] "" XXX + SG_ SET_TO_3 : 6|7@0+ (1,0) [0|4095] "" XXX + SG_ CONTROL_ON : 7|1@0+ (1,0) [0|1] "" XXX + SG_ CHECKSUM : 59|4@0+ (1,0) [0|15] "" XXX + SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" XXX + +BO_ 829 LKAS_HUD: 5 ADAS + SG_ CAM_TEMP_HIGH : 7|1@0+ (1,0) [0|255] "" BDY + SG_ SET_ME_X41 : 6|7@0+ (1,0) [0|127] "" BDY + SG_ BOH : 6|7@0+ (1,0) [0|127] "" BDY + SG_ DASHED_LANES : 14|1@0+ (1,0) [0|1] "" BDY + SG_ DTC : 13|1@0+ (1,0) [0|1] "" BDY + SG_ LKAS_PROBLEM : 12|1@0+ (1,0) [0|1] "" BDY + SG_ LKAS_OFF : 11|1@0+ (1,0) [0|1] "" BDY + SG_ SOLID_LANES : 10|1@0+ (1,0) [0|1] "" BDY + SG_ LDW_RIGHT : 9|1@0+ (1,0) [0|1] "" BDY + SG_ STEERING_REQUIRED : 8|1@0+ (1,0) [0|1] "" BDY + SG_ BOH : 23|2@0+ (1,0) [0|4] "" BDY + SG_ LDW_PROBLEM : 21|1@0+ (1,0) [0|1] "" BDY + SG_ BEEP : 17|2@0+ (1,0) [0|1] "" BDY + SG_ LDW_ON : 28|1@0+ (1,0) [0|1] "" BDY + SG_ LDW_OFF : 27|1@0+ (1,0) [0|1] "" BDY + SG_ CLEAN_WINDSHIELD : 26|1@0+ (1,0) [0|1] "" BDY + SG_ SET_ME_X48 : 31|8@0+ (1,0) [0|255] "" BDY + SG_ COUNTER : 37|2@0+ (1,0) [0|3] "" BDY + SG_ CHECKSUM : 35|4@0+ (1,0) [0|15] "" BDY + +CM_ SG_ 479 CONTROL_ON "Set to 5 when car is being controlled"; +CM_ SG_ 479 AEB_STATUS "set for the duration of AEB event"; +CM_ SG_ 479 AEB_BRAKING "set when braking is commanded during AEB event"; +CM_ SG_ 479 AEB_PREPARE "set 1s before AEB"; +CM_ SG_ 829 BEEP "beeps are pleasant, chimes are for warnngs etc..."; + +VAL_ 829 BEEP 3 "single_beep" 2 "triple_beep" 1 "repeated_beep" 0 "no_beep"; + + +CM_ "Imported file _steering_sensors_a.dbc starts here"; BO_ 330 STEERING_SENSORS: 8 EPS SG_ STEER_ANGLE : 7|16@0- (-0.1,0) [-500|500] "deg" EON SG_ STEER_ANGLE_RATE : 23|16@0- (-1,0) [-3000|3000] "deg/s" EON @@ -437,6 +466,8 @@ BO_ 330 STEERING_SENSORS: 8 EPS SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" EON SG_ CHECKSUM : 59|4@0+ (1,0) [0|15] "" EON +CM_ "honda_civic_hatchback_ex_2017_can.dbc starts here"; + BO_ 401 GEARBOX: 8 PCM SG_ GEAR_SHIFTER : 5|6@0+ (1,0) [0|63] "" EON SG_ BOH : 45|6@0+ (1,0) [0|63] "" XXX @@ -463,19 +494,6 @@ BO_ 892 CRUISE_PARAMS: 8 PCM SG_ CHECKSUM : 59|4@0+ (1,0) [0|3] "" EON SG_ COUNTER : 61|2@0+ (1,0) [0|15] "" EON -BO_ 927 RADAR_HUD: 8 RADAR - SG_ ZEROS_BOH : 7|10@0+ (1,0) [0|127] "" BDY - SG_ CMBS_OFF : 12|1@0+ (1,0) [0|1] "" BDY - SG_ ZEROS_BOH3 : 31|32@0+ (1,0) [0|4294967295] "" XXX - SG_ RESUME_INSTRUCTION : 21|1@0+ (1,0) [0|1] "" XXX - SG_ SET_TO_1 : 13|1@0+ (1,0) [0|1] "" BDY - SG_ ZEROS_BOH2 : 11|4@0+ (1,0) [0|1] "" XXX - SG_ APPLY_BRAKES_FOR_CANC : 23|1@0+ (1,0) [0|1] "" XXX - SG_ ACC_ALERTS : 20|5@0+ (1,0) [0|1] "" BDY - SG_ SET_TO_0 : 22|1@0+ (1,0) [0|1] "" XXX - SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" XXX - SG_ CHECKSUM : 59|4@0+ (1,0) [0|15] "" XXX - VAL_ 401 GEAR_SHIFTER 32 "L" 16 "S" 8 "D" 4 "N" 2 "R" 1 "P" ; VAL_ 401 GEAR 7 "L" 10 "S" 4 "D" 3 "N" 2 "R" 1 "P" ; VAL_ 545 ECON_ON_2 0 "off" 3 "on" ; diff --git a/opendbc/honda_civic_touring_2016_can_generated.dbc b/opendbc/honda_civic_touring_2016_can_generated.dbc index 2cbb570a9..9be63fe36 100644 --- a/opendbc/honda_civic_touring_2016_can_generated.dbc +++ b/opendbc/honda_civic_touring_2016_can_generated.dbc @@ -94,6 +94,43 @@ BO_ 773 SEATBELT_STATUS: 7 BDY SG_ COUNTER : 53|2@0+ (1,0) [0|3] "" EON SG_ CHECKSUM : 51|4@0+ (1,0) [0|3] "" EON +BO_ 777 CAR_SPEED: 8 PCM + SG_ ROUGH_CAR_SPEED : 23|8@0+ (1,0) [0|255] "mph" XXX + SG_ CAR_SPEED : 7|16@0+ (0.01,0) [0|65535] "kph" XXX + SG_ ROUGH_CAR_SPEED_3 : 39|16@0+ (0.01,0) [0|65535] "kph" XXX + SG_ ROUGH_CAR_SPEED_2 : 31|8@0+ (1,0) [0|255] "mph" XXX + SG_ LOCK_STATUS : 55|2@0+ (1,0) [0|255] "" XXX + SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" XXX + SG_ CHECKSUM : 59|4@0+ (1,0) [0|15] "" XXX + SG_ IMPERIAL_UNIT : 63|1@0+ (1,0) [0|1] "" XXX + +BO_ 780 ACC_HUD: 8 ADAS + SG_ PCM_SPEED : 7|16@0+ (0.01,0) [0|250] "kph" BDY + SG_ PCM_GAS : 23|8@0+ (1,0) [0|127] "" BDY + SG_ CRUISE_SPEED : 31|8@0+ (1,0) [0|255] "kph" BDY + SG_ DTC_MODE : 39|1@0+ (1,0) [0|1] "" BDY + SG_ BOH : 38|1@0+ (1,0) [0|1] "" BDY + SG_ ACC_PROBLEM : 37|1@0+ (1,0) [0|1] "" BDY + SG_ FCM_OFF : 35|1@0+ (1,0) [0|1] "" BDY + SG_ FCM_OFF_2 : 36|1@0+ (1,0) [0|1] "" BDY + SG_ FCM_PROBLEM : 34|1@0+ (1,0) [0|1] "" BDY + SG_ RADAR_OBSTRUCTED : 33|1@0+ (1,0) [0|1] "" BDY + SG_ ENABLE_MINI_CAR : 32|1@0+ (1,0) [0|1] "" BDY + SG_ HUD_DISTANCE : 47|2@0+ (1,0) [0|3] "" BDY + SG_ HUD_LEAD : 45|2@0+ (1,0) [0|3] "" BDY + SG_ BOH_3 : 43|1@0+ (1,0) [0|3] "" BDY + SG_ BOH_4 : 42|1@0+ (1,0) [0|3] "" BDY + SG_ BOH_5 : 41|1@0+ (1,0) [0|3] "" BDY + SG_ CRUISE_CONTROL_LABEL : 40|1@0+ (1,0) [0|3] "" BDY + SG_ SET_ME_X01_2 : 55|1@0+ (1,0) [0|1] "" BDY + SG_ IMPERIAL_UNIT : 54|1@0+ (1,0) [0|1] "" BDY + SG_ ACC_ON : 52|1@0+ (1,0) [0|1] "" BDY + SG_ CHIME : 51|3@0+ (1,0) [0|1] "" BDY + SG_ SET_ME_X01 : 48|1@0+ (1,0) [0|1] "" BDY + SG_ ICONS : 63|2@0+ (1,0) [0|1] "" BDY + SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" BDY + SG_ CHECKSUM : 59|4@0+ (1,0) [0|3] "" BDY + BO_ 804 CRUISE: 8 PCM SG_ HUD_SPEED_KPH : 7|8@0+ (1,0) [0|255] "kph" EON SG_ HUD_SPEED_MPH : 15|8@0+ (1,0) [0|255] "mph" EON @@ -104,27 +141,6 @@ BO_ 804 CRUISE: 8 PCM SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" EON SG_ CHECKSUM : 59|4@0+ (1,0) [0|15] "" EON -BO_ 829 LKAS_HUD: 5 ADAS - SG_ CAM_TEMP_HIGH : 7|1@0+ (1,0) [0|255] "" BDY - SG_ SET_ME_X41 : 6|7@0+ (1,0) [0|127] "" BDY - SG_ BOH : 6|7@0+ (1,0) [0|127] "" BDY - SG_ DASHED_LANES : 14|1@0+ (1,0) [0|1] "" BDY - SG_ DTC : 13|1@0+ (1,0) [0|1] "" BDY - SG_ LKAS_PROBLEM : 12|1@0+ (1,0) [0|1] "" BDY - SG_ LKAS_OFF : 11|1@0+ (1,0) [0|1] "" BDY - SG_ SOLID_LANES : 10|1@0+ (1,0) [0|1] "" BDY - SG_ LDW_RIGHT : 9|1@0+ (1,0) [0|1] "" BDY - SG_ STEERING_REQUIRED : 8|1@0+ (1,0) [0|1] "" BDY - SG_ BOH : 23|2@0+ (1,0) [0|4] "" BDY - SG_ LDW_PROBLEM : 21|1@0+ (1,0) [0|1] "" BDY - SG_ BEEP : 17|2@0+ (1,0) [0|1] "" BDY - SG_ LDW_ON : 28|1@0+ (1,0) [0|1] "" BDY - SG_ LDW_OFF : 27|1@0+ (1,0) [0|1] "" BDY - SG_ CLEAN_WINDSHIELD : 26|1@0+ (1,0) [0|1] "" BDY - SG_ SET_ME_X48 : 31|8@0+ (1,0) [0|255] "" BDY - SG_ COUNTER : 37|2@0+ (1,0) [0|3] "" BDY - SG_ CHECKSUM : 35|4@0+ (1,0) [0|15] "" BDY - BO_ 884 STALK_STATUS: 8 XXX SG_ DASHBOARD_ALERT : 39|8@0+ (1,0) [0|255] "" EON SG_ AUTO_HEADLIGHTS : 46|1@0+ (1,0) [0|1] "" EON @@ -158,15 +174,18 @@ CM_ SG_ 420 BRAKE_HOLD_RELATED "On when Brake Hold engaged"; CM_ SG_ 490 LONG_ACCEL "wheel speed derivative, noisy and zero snapping"; CM_ SG_ 773 PASS_AIRBAG_ON "Might just be indicator light"; CM_ SG_ 773 PASS_AIRBAG_OFF "Might just be indicator light"; +CM_ SG_ 780 CRUISE_SPEED "255 = no speed"; +CM_ SG_ 780 PCM_SPEED "Used by Nidec"; +CM_ SG_ 780 PCM_GAS "Used by Nidec"; CM_ SG_ 804 CRUISE_SPEED_PCM "255 = no speed"; -CM_ SG_ 829 BEEP "beeps are pleasant, chimes are for warnngs etc..."; -VAL_ 829 BEEP 3 "single_beep" 2 "triple_beep" 1 "repeated_beep" 0 "no_beep"; +VAL_ 780 CRUISE_SPEED 255 "no_speed" 252 "stopped"; +VAL_ 780 HUD_LEAD 3 "acc_off" 2 "solid_car" 1 "dashed_car" 0 "no_car"; VAL_ 884 DASHBOARD_ALERT 0 "none" 51 "acc_problem" 55 "cmbs_problem" 75 "key_not_detected" 79 "fasten_seatbelt" 111 "lkas_problem" 131 "brake_system_problem" 132 "brake_hold_problem" 139 "tbd" 161 "door_open"; VAL_ 891 WIPERS 4 "High" 2 "Low" 0 "Off"; -CM_ "Imported file _nidec_2017.dbc starts here"; +CM_ "Imported file _nidec_common.dbc starts here"; BO_ 145 KINEMATICS: 8 XXX SG_ LAT_ACCEL : 7|10@0+ (0.02,-512) [-20|20] "m/s2" EON SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" EON @@ -194,11 +213,11 @@ BO_ 487 BRAKE_PRESSURE: 4 VSA BO_ 506 BRAKE_COMMAND: 8 ADAS SG_ COMPUTER_BRAKE : 7|10@0+ (1,0) [0|1] "" EBCM - SG_ SET_ME_X00 : 13|5@0+ (1,0) [0|1] "" EBCM SG_ BRAKE_PUMP_REQUEST : 8|1@0+ (1,0) [0|1] "" EBCM - SG_ SET_ME_X00_2 : 23|3@0+ (1,0) [0|1] "" EBCM + SG_ BRAKE_PUMP_REQUEST_ALT : 11|1@0+ (1,0) [0|1] "" EBCM + SG_ SET_ME_X00 : 23|3@0+ (1,0) [0|1] "" EBCM SG_ CRUISE_OVERRIDE : 20|1@0+ (1,0) [0|1] "" EBCM - SG_ SET_ME_X00_3 : 19|1@0+ (1,0) [0|1] "" EBCM + SG_ SET_ME_X00_2 : 19|1@0+ (1,0) [0|1] "" EBCM SG_ CRUISE_FAULT_CMD : 18|1@0+ (1,0) [0|1] "" EBCM SG_ CRUISE_CANCEL_CMD : 17|1@0+ (1,0) [0|1] "" EBCM SG_ COMPUTER_BRAKE_REQUEST : 16|1@0+ (1,0) [0|1] "" EBCM @@ -208,10 +227,10 @@ BO_ 506 BRAKE_COMMAND: 8 ADAS SG_ BRAKE_LIGHTS : 39|1@0+ (1,0) [0|1] "" EBCM SG_ CRUISE_STATES : 38|7@0+ (1,0) [0|1] "" EBCM SG_ CHIME : 47|3@0+ (1,0) [0|7] "" EBCM - SG_ SET_ME_X00_4 : 44|1@0+ (1,0) [0|1] "" EBCM + SG_ SET_ME_X00_3 : 44|1@0+ (1,0) [0|1] "" EBCM SG_ FCW : 43|2@0+ (1,0) [0|3] "" EBCM SG_ AEB_STATUS : 41|2@0+ (1,0) [0|3] "" XXX - SG_ SET_ME_X00_5 : 55|8@0+ (1,0) [0|0] "" EBCM + SG_ COMPUTER_BRAKE_ALT : 55|10@0+ (1,0) [0|0] "" EBCM SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" EBCM SG_ CHECKSUM : 59|4@0+ (1,0) [0|15] "" EBCM @@ -225,38 +244,26 @@ BO_ 597 ROUGH_WHEEL_SPEED: 8 VSA SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" EON SG_ CHECKSUM : 59|4@0+ (1,0) [0|15] "" EON -BO_ 777 LOCK_STATUS: 8 XXX - SG_ DOORS_UNLOCKED : 54|1@0+ (1,0) [0|1] "" EON - SG_ DOORS_LOCKED : 55|1@0+ (1,0) [0|1] "" EON - SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" EON - SG_ CHECKSUM : 59|4@0+ (1,0) [0|3] "" EON - -BO_ 780 ACC_HUD: 8 ADAS - SG_ PCM_SPEED : 7|16@0+ (0.01,0) [0|250] "kph" BDY - SG_ PCM_GAS : 23|8@0+ (1,0) [0|127] "" BDY - SG_ CRUISE_SPEED : 31|8@0+ (1,0) [0|255] "" BDY - SG_ DTC_MODE : 39|1@0+ (1,0) [0|1] "" BDY - SG_ BOH : 38|1@0+ (1,0) [0|1] "" BDY - SG_ ACC_PROBLEM : 37|1@0+ (1,0) [0|1] "" BDY - SG_ FCM_OFF : 36|1@0+ (1,0) [0|1] "" BDY - SG_ FCM_OFF_2 : 35|1@0+ (1,0) [0|1] "" BDY - SG_ FCM_PROBLEM : 34|1@0+ (1,0) [0|1] "" BDY - SG_ RADAR_OBSTRUCTED : 33|1@0+ (1,0) [0|1] "" BDY - SG_ ENABLE_MINI_CAR : 32|1@0+ (1,0) [0|1] "" BDY - SG_ HUD_DISTANCE : 47|2@0+ (1,0) [0|3] "" BDY - SG_ HUD_LEAD : 45|2@0+ (1,0) [0|3] "" BDY - SG_ BOH_3 : 43|1@0+ (1,0) [0|3] "" BDY - SG_ BOH_4 : 42|1@0+ (1,0) [0|3] "" BDY - SG_ BOH_5 : 41|1@0+ (1,0) [0|3] "" BDY - SG_ CRUISE_CONTROL_LABEL : 40|1@0+ (1,0) [0|3] "" BDY - SG_ SET_ME_X01_2 : 55|1@0+ (1,0) [0|1] "" BDY - SG_ IMPERIAL_UNIT : 54|1@0+ (1,0) [0|1] "" BDY - SG_ HUD_DISTANCE_3 : 52|1@0+ (1,0) [0|1] "" BDY - SG_ CHIME : 51|3@0+ (1,0) [0|1] "" BDY - SG_ SET_ME_X01 : 48|1@0+ (1,0) [0|1] "" BDY - SG_ ICONS : 63|2@0+ (1,0) [0|1] "" BDY - SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" BDY - SG_ CHECKSUM : 59|4@0+ (1,0) [0|3] "" BDY +BO_ 829 LKAS_HUD: 5 ADAS + SG_ CAM_TEMP_HIGH : 7|1@0+ (1,0) [0|255] "" BDY + SG_ SET_ME_X41 : 6|7@0+ (1,0) [0|127] "" BDY + SG_ BOH : 6|7@0+ (1,0) [0|127] "" BDY + SG_ DASHED_LANES : 14|1@0+ (1,0) [0|1] "" BDY + SG_ DTC : 13|1@0+ (1,0) [0|1] "" BDY + SG_ LKAS_PROBLEM : 12|1@0+ (1,0) [0|1] "" BDY + SG_ LKAS_OFF : 11|1@0+ (1,0) [0|1] "" BDY + SG_ SOLID_LANES : 10|1@0+ (1,0) [0|1] "" BDY + SG_ LDW_RIGHT : 9|1@0+ (1,0) [0|1] "" BDY + SG_ STEERING_REQUIRED : 8|1@0+ (1,0) [0|1] "" BDY + SG_ BOH : 23|2@0+ (1,0) [0|4] "" BDY + SG_ LDW_PROBLEM : 21|1@0+ (1,0) [0|1] "" BDY + SG_ BEEP : 17|2@0+ (1,0) [0|1] "" BDY + SG_ LDW_ON : 28|1@0+ (1,0) [0|1] "" BDY + SG_ LDW_OFF : 27|1@0+ (1,0) [0|1] "" BDY + SG_ CLEAN_WINDSHIELD : 26|1@0+ (1,0) [0|1] "" BDY + SG_ SET_ME_X48 : 31|8@0+ (1,0) [0|255] "" BDY + SG_ COUNTER : 37|2@0+ (1,0) [0|3] "" BDY + SG_ CHECKSUM : 35|4@0+ (1,0) [0|15] "" BDY BO_ 892 CRUISE_PARAMS: 8 PCM SG_ CRUISE_SPEED_OFFSET : 31|8@0- (0.1,0) [-128|127] "kph" EON @@ -264,13 +271,26 @@ BO_ 892 CRUISE_PARAMS: 8 PCM SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" EON CM_ SG_ 506 AEB_REQ_1 "set for duration of suspected AEB event"; -CM_ SG_ 780 CRUISE_SPEED "255 = no speed"; +CM_ SG_ 506 COMPUTER_BRAKE_ALT "Used by dual-can Nidec"; +CM_ SG_ 506 BRAKE_PUMP_REQUEST_ALT "Used by dual-can Nidec"; +CM_ SG_ 829 BEEP "beeps are pleasant, chimes are for warnngs etc..."; VAL_ 506 FCW 3 "fcw" 2 "fcw" 1 "fcw" 0 "no_fcw"; VAL_ 506 CHIME 4 "double_chime" 3 "single_chime" 2 "continuous_chime" 1 "repeating_chime" 0 "no_chime"; VAL_ 506 AEB_STATUS 3 "aeb_prepare" 2 "aeb_ready" 1 "aeb_braking" 0 "no_aeb"; -VAL_ 780 CRUISE_SPEED 255 "no_speed" 252 "stopped"; -VAL_ 780 HUD_LEAD 3 "acc_off" 2 "solid_car" 1 "dashed_car" 0 "no_car"; +VAL_ 829 BEEP 3 "single_beep" 2 "triple_beep" 1 "repeated_beep" 0 "no_beep"; + + +CM_ "Imported file _steering_sensors_a.dbc starts here"; +BO_ 330 STEERING_SENSORS: 8 EPS + SG_ STEER_ANGLE : 7|16@0- (-0.1,0) [-500|500] "deg" EON + SG_ STEER_ANGLE_RATE : 23|16@0- (-1,0) [-3000|3000] "deg/s" EON + SG_ STEER_SENSOR_STATUS_1 : 34|1@0+ (1,0) [0|1] "" EON + SG_ STEER_SENSOR_STATUS_2 : 33|1@0+ (1,0) [0|1] "" EON + SG_ STEER_SENSOR_STATUS_3 : 32|1@0+ (1,0) [0|1] "" EON + SG_ STEER_WHEEL_ANGLE : 47|16@0- (-0.1,0) [-500|500] "deg" EON + SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" EON + SG_ CHECKSUM : 59|4@0+ (1,0) [0|15] "" EON CM_ "honda_civic_touring_2016_can.dbc starts here"; @@ -282,16 +302,6 @@ BO_ 228 STEERING_CONTROL: 5 ADAS SG_ COUNTER : 37|2@0+ (1,0) [0|3] "" EPS SG_ CHECKSUM : 35|4@0+ (1,0) [0|15] "" EPS -BO_ 330 STEERING_SENSORS: 8 EPS - SG_ STEER_ANGLE : 7|16@0- (-0.1,0) [-500|500] "deg" EON - SG_ STEER_ANGLE_RATE : 23|16@0- (-1,0) [-3000|3000] "deg/s" EON - SG_ STEER_SENSOR_STATUS_1 : 34|1@0+ (1,0) [0|1] "" EON - SG_ STEER_SENSOR_STATUS_2 : 33|1@0+ (1,0) [0|1] "" EON - SG_ STEER_SENSOR_STATUS_3 : 32|1@0+ (1,0) [0|1] "" EON - SG_ STEER_WHEEL_ANGLE : 47|16@0- (-0.1,0) [-500|500] "deg" EON - SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" EON - SG_ CHECKSUM : 59|4@0+ (1,0) [0|3] "" EON - BO_ 399 STEER_STATUS: 7 EPS SG_ STEER_TORQUE_SENSOR : 7|16@0- (-1,0) [-31000|31000] "tbd" EON SG_ STEER_ANGLE_RATE : 23|16@0- (-0.1,0) [-31000|31000] "deg/s" EON diff --git a/opendbc/honda_crv_ex_2017_can_generated.dbc b/opendbc/honda_crv_ex_2017_can_generated.dbc index 95b0f46ba..345d94819 100644 --- a/opendbc/honda_crv_ex_2017_can_generated.dbc +++ b/opendbc/honda_crv_ex_2017_can_generated.dbc @@ -76,6 +76,43 @@ BO_ 773 SEATBELT_STATUS: 7 BDY SG_ COUNTER : 53|2@0+ (1,0) [0|3] "" EON SG_ CHECKSUM : 51|4@0+ (1,0) [0|3] "" EON +BO_ 777 CAR_SPEED: 8 PCM + SG_ ROUGH_CAR_SPEED : 23|8@0+ (1,0) [0|255] "mph" XXX + SG_ CAR_SPEED : 7|16@0+ (0.01,0) [0|65535] "kph" XXX + SG_ ROUGH_CAR_SPEED_3 : 39|16@0+ (0.01,0) [0|65535] "kph" XXX + SG_ ROUGH_CAR_SPEED_2 : 31|8@0+ (1,0) [0|255] "mph" XXX + SG_ LOCK_STATUS : 55|2@0+ (1,0) [0|255] "" XXX + SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" XXX + SG_ CHECKSUM : 59|4@0+ (1,0) [0|15] "" XXX + SG_ IMPERIAL_UNIT : 63|1@0+ (1,0) [0|1] "" XXX + +BO_ 780 ACC_HUD: 8 ADAS + SG_ PCM_SPEED : 7|16@0+ (0.01,0) [0|250] "kph" BDY + SG_ PCM_GAS : 23|8@0+ (1,0) [0|127] "" BDY + SG_ CRUISE_SPEED : 31|8@0+ (1,0) [0|255] "kph" BDY + SG_ DTC_MODE : 39|1@0+ (1,0) [0|1] "" BDY + SG_ BOH : 38|1@0+ (1,0) [0|1] "" BDY + SG_ ACC_PROBLEM : 37|1@0+ (1,0) [0|1] "" BDY + SG_ FCM_OFF : 35|1@0+ (1,0) [0|1] "" BDY + SG_ FCM_OFF_2 : 36|1@0+ (1,0) [0|1] "" BDY + SG_ FCM_PROBLEM : 34|1@0+ (1,0) [0|1] "" BDY + SG_ RADAR_OBSTRUCTED : 33|1@0+ (1,0) [0|1] "" BDY + SG_ ENABLE_MINI_CAR : 32|1@0+ (1,0) [0|1] "" BDY + SG_ HUD_DISTANCE : 47|2@0+ (1,0) [0|3] "" BDY + SG_ HUD_LEAD : 45|2@0+ (1,0) [0|3] "" BDY + SG_ BOH_3 : 43|1@0+ (1,0) [0|3] "" BDY + SG_ BOH_4 : 42|1@0+ (1,0) [0|3] "" BDY + SG_ BOH_5 : 41|1@0+ (1,0) [0|3] "" BDY + SG_ CRUISE_CONTROL_LABEL : 40|1@0+ (1,0) [0|3] "" BDY + SG_ SET_ME_X01_2 : 55|1@0+ (1,0) [0|1] "" BDY + SG_ IMPERIAL_UNIT : 54|1@0+ (1,0) [0|1] "" BDY + SG_ ACC_ON : 52|1@0+ (1,0) [0|1] "" BDY + SG_ CHIME : 51|3@0+ (1,0) [0|1] "" BDY + SG_ SET_ME_X01 : 48|1@0+ (1,0) [0|1] "" BDY + SG_ ICONS : 63|2@0+ (1,0) [0|1] "" BDY + SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" BDY + SG_ CHECKSUM : 59|4@0+ (1,0) [0|3] "" BDY + BO_ 804 CRUISE: 8 PCM SG_ HUD_SPEED_KPH : 7|8@0+ (1,0) [0|255] "kph" EON SG_ HUD_SPEED_MPH : 15|8@0+ (1,0) [0|255] "mph" EON @@ -86,27 +123,6 @@ BO_ 804 CRUISE: 8 PCM SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" EON SG_ CHECKSUM : 59|4@0+ (1,0) [0|15] "" EON -BO_ 829 LKAS_HUD: 5 ADAS - SG_ CAM_TEMP_HIGH : 7|1@0+ (1,0) [0|255] "" BDY - SG_ SET_ME_X41 : 6|7@0+ (1,0) [0|127] "" BDY - SG_ BOH : 6|7@0+ (1,0) [0|127] "" BDY - SG_ DASHED_LANES : 14|1@0+ (1,0) [0|1] "" BDY - SG_ DTC : 13|1@0+ (1,0) [0|1] "" BDY - SG_ LKAS_PROBLEM : 12|1@0+ (1,0) [0|1] "" BDY - SG_ LKAS_OFF : 11|1@0+ (1,0) [0|1] "" BDY - SG_ SOLID_LANES : 10|1@0+ (1,0) [0|1] "" BDY - SG_ LDW_RIGHT : 9|1@0+ (1,0) [0|1] "" BDY - SG_ STEERING_REQUIRED : 8|1@0+ (1,0) [0|1] "" BDY - SG_ BOH : 23|2@0+ (1,0) [0|4] "" BDY - SG_ LDW_PROBLEM : 21|1@0+ (1,0) [0|1] "" BDY - SG_ BEEP : 17|2@0+ (1,0) [0|1] "" BDY - SG_ LDW_ON : 28|1@0+ (1,0) [0|1] "" BDY - SG_ LDW_OFF : 27|1@0+ (1,0) [0|1] "" BDY - SG_ CLEAN_WINDSHIELD : 26|1@0+ (1,0) [0|1] "" BDY - SG_ SET_ME_X48 : 31|8@0+ (1,0) [0|255] "" BDY - SG_ COUNTER : 37|2@0+ (1,0) [0|3] "" BDY - SG_ CHECKSUM : 35|4@0+ (1,0) [0|15] "" BDY - BO_ 884 STALK_STATUS: 8 XXX SG_ DASHBOARD_ALERT : 39|8@0+ (1,0) [0|255] "" EON SG_ AUTO_HEADLIGHTS : 46|1@0+ (1,0) [0|1] "" EON @@ -140,10 +156,13 @@ CM_ SG_ 420 BRAKE_HOLD_RELATED "On when Brake Hold engaged"; CM_ SG_ 490 LONG_ACCEL "wheel speed derivative, noisy and zero snapping"; CM_ SG_ 773 PASS_AIRBAG_ON "Might just be indicator light"; CM_ SG_ 773 PASS_AIRBAG_OFF "Might just be indicator light"; +CM_ SG_ 780 CRUISE_SPEED "255 = no speed"; +CM_ SG_ 780 PCM_SPEED "Used by Nidec"; +CM_ SG_ 780 PCM_GAS "Used by Nidec"; CM_ SG_ 804 CRUISE_SPEED_PCM "255 = no speed"; -CM_ SG_ 829 BEEP "beeps are pleasant, chimes are for warnngs etc..."; -VAL_ 829 BEEP 3 "single_beep" 2 "triple_beep" 1 "repeated_beep" 0 "no_beep"; +VAL_ 780 CRUISE_SPEED 255 "no_speed" 252 "stopped"; +VAL_ 780 HUD_LEAD 3 "acc_off" 2 "solid_car" 1 "dashed_car" 0 "no_car"; VAL_ 884 DASHBOARD_ALERT 0 "none" 51 "acc_problem" 55 "cmbs_problem" 75 "key_not_detected" 79 "fasten_seatbelt" 111 "lkas_problem" 131 "brake_system_problem" 132 "brake_hold_problem" 139 "tbd" 161 "door_open"; VAL_ 891 WIPERS 4 "High" 2 "Low" 0 "Off"; @@ -194,32 +213,6 @@ BO_ 450 EPB_STATUS: 8 EPB SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" XXX SG_ CHECKSUM : 59|4@0+ (1,0) [0|15] "" XXX -BO_ 479 ACC_CONTROL: 8 EON - SG_ SET_TO_0 : 20|5@0+ (1,0) [0|1] "" XXX - SG_ CONTROL_ON : 23|3@0+ (1,0) [0|5] "" XXX - SG_ GAS_COMMAND : 7|16@0- (1,0) [0|0] "" XXX - SG_ ACCEL_COMMAND : 31|11@0- (0.01,0) [0|0] "m/s2" XXX - SG_ BRAKE_LIGHTS : 62|1@0+ (1,0) [0|1] "" XXX - SG_ BRAKE_REQUEST : 34|1@0+ (1,0) [0|1] "" XXX - SG_ STANDSTILL : 35|1@0+ (1,0) [0|1] "" XXX - SG_ STANDSTILL_RELEASE : 36|1@0+ (1,0) [0|1] "" XXX - SG_ AEB_STATUS : 33|1@0+ (1,0) [0|1] "" XXX - SG_ AEB_BRAKING : 47|1@0+ (1,0) [0|1] "" XXX - SG_ AEB_PREPARE : 43|1@0+ (1,0) [0|1] "" XXX - SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" XXX - SG_ CHECKSUM : 59|4@0+ (1,0) [0|15] "" XXX - -BO_ 495 ACC_CONTROL_ON: 8 XXX - SG_ SET_TO_75 : 31|8@0+ (1,0) [0|255] "" XXX - SG_ SET_TO_30 : 39|8@0+ (1,0) [0|255] "" XXX - SG_ ZEROS_BOH : 23|8@0+ (1,0) [0|255] "" XXX - SG_ ZEROS_BOH2 : 47|16@0+ (1,0) [0|255] "" XXX - SG_ SET_TO_FF : 15|8@0+ (1,0) [0|255] "" XXX - SG_ SET_TO_3 : 6|7@0+ (1,0) [0|4095] "" XXX - SG_ CONTROL_ON : 7|1@0+ (1,0) [0|1] "" XXX - SG_ CHECKSUM : 59|4@0+ (1,0) [0|15] "" XXX - SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" XXX - BO_ 545 XXX_16: 6 SCM SG_ ECON_ON : 23|1@0+ (1,0) [0|1] "" XXX SG_ DRIVE_MODE : 37|2@0+ (1,0) [0|3] "" XXX @@ -319,40 +312,6 @@ BO_ 662 SCM_BUTTONS: 4 SCM SG_ COUNTER : 29|2@0+ (1,0) [0|3] "" EON SG_ CHECKSUM : 27|4@0+ (1,0) [0|15] "" EON -BO_ 777 CAR_SPEED: 8 PCM - SG_ ROUGH_CAR_SPEED : 23|8@0+ (1,0) [0|255] "mph" XXX - SG_ CAR_SPEED : 7|16@0+ (0.01,0) [0|65535] "kph" XXX - SG_ ROUGH_CAR_SPEED_3 : 39|16@0+ (0.01,0) [0|65535] "kph" XXX - SG_ ROUGH_CAR_SPEED_2 : 31|8@0+ (1,0) [0|255] "mph" XXX - SG_ LOCK_STATUS : 55|2@0+ (1,0) [0|255] "" XXX - SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" XXX - SG_ CHECKSUM : 59|4@0+ (1,0) [0|15] "" XXX - SG_ IMPERIAL_UNIT : 63|1@0+ (1,0) [0|1] "" XXX - -BO_ 780 ACC_HUD: 8 ADAS - SG_ CRUISE_SPEED : 31|8@0+ (1,0) [0|255] "kph" BDY - SG_ DTC_MODE : 39|1@0+ (1,0) [0|1] "" BDY - SG_ BOH : 38|1@0+ (1,0) [0|1] "" BDY - SG_ FCM_PROBLEM : 34|1@0+ (1,0) [0|1] "" BDY - SG_ RADAR_OBSTRUCTED : 33|1@0+ (1,0) [0|1] "" BDY - SG_ ENABLE_MINI_CAR : 32|1@0+ (1,0) [0|1] "" BDY - SG_ BOH_3 : 43|1@0+ (1,0) [0|3] "" BDY - SG_ BOH_4 : 42|1@0+ (1,0) [0|3] "" BDY - SG_ BOH_5 : 41|1@0+ (1,0) [0|3] "" BDY - SG_ CRUISE_CONTROL_LABEL : 40|1@0+ (1,0) [0|3] "" BDY - SG_ ZEROS_BOH : 7|24@0+ (0.002759506,0) [0|100] "m/s" BDY - SG_ FCM_OFF : 35|1@0+ (1,0) [0|1] "" BDY - SG_ SET_TO_1 : 36|1@0+ (1,0) [0|1] "" XXX - SG_ HUD_DISTANCE : 47|2@0+ (1,0) [0|3] "" BDY - SG_ HUD_LEAD : 45|2@0+ (1,0) [0|3] "" BDY - SG_ ACC_PROBLEM : 37|1@0+ (1,0) [0|1] "" BDY - SG_ ACC_ON : 52|1@0+ (1,0) [0|1] "" XXX - SG_ BOH_6 : 51|4@0+ (1,0) [0|15] "" XXX - SG_ SET_TO_X1 : 55|1@0+ (1,0) [0|1] "" XXX - SG_ IMPERIAL_UNIT : 54|1@0+ (1,0) [0|1] "" XXX - SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" XXX - SG_ CHECKSUM : 59|4@0+ (1,0) [0|15] "" XXX - BO_ 806 SCM_FEEDBACK: 8 SCM SG_ DRIVERS_DOOR_OPEN : 17|1@0+ (1,0) [0|1] "" XXX SG_ MAIN_ON : 28|1@0+ (1,0) [0|1] "" EON @@ -370,6 +329,23 @@ BO_ 862 CAMERA_MESSAGES: 8 CAM SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" XXX SG_ CHECKSUM : 59|4@0+ (1,0) [0|15] "" XXX +BO_ 927 RADAR_HUD: 8 RADAR + SG_ ZEROS_BOH : 7|10@0+ (1,0) [0|127] "" BDY + SG_ CMBS_OFF : 12|1@0+ (1,0) [0|1] "" BDY + SG_ RESUME_INSTRUCTION : 21|1@0+ (1,0) [0|1] "" XXX + SG_ SET_TO_1 : 13|1@0+ (1,0) [0|1] "" BDY + SG_ ZEROS_BOH2 : 11|4@0+ (1,0) [0|1] "" XXX + SG_ APPLY_BRAKES_FOR_CANC : 23|1@0+ (1,0) [0|1] "" XXX + SG_ ACC_ALERTS : 20|5@0+ (1,0) [0|1] "" BDY + SG_ SET_TO_0 : 22|1@0+ (1,0) [0|1] "" XXX + SG_ HUD_LEAD : 40|1@0+ (1,0) [0|1] "" XXX + SG_ SET_TO_64 : 31|8@0+ (1,0) [0|255] "" XXX + SG_ LEAD_DISTANCE : 39|8@0+ (1,0) [0|255] "" XXX + SG_ ZEROS_BOH3 : 47|7@0+ (1,0) [0|127] "" XXX + SG_ ZEROS_BOH4 : 55|8@0+ (1,0) [0|255] "" XXX + SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" XXX + SG_ CHECKSUM : 59|4@0+ (1,0) [0|15] "" XXX + BO_ 13274 LKAS_HUD_A: 5 ADAS SG_ CAM_TEMP_HIGH : 7|1@0+ (1,0) [0|255] "" BDY SG_ SET_ME_X41 : 6|7@0+ (1,0) [0|127] "" BDY @@ -413,10 +389,6 @@ BO_ 13275 LKAS_HUD_B: 8 ADAS SG_ CHECKSUM : 59|4@0+ (1,0) [0|15] "" BDY CM_ SG_ 450 EPB_STATE "3: On, 2: Disengaging, 1: Engaging, 0: Off"; -CM_ SG_ 479 CONTROL_ON "Set to 5 when car is being controlled"; -CM_ SG_ 479 AEB_STATUS "set for the duration of AEB event"; -CM_ SG_ 479 AEB_BRAKING "set when braking is commanded during AEB event"; -CM_ SG_ 479 AEB_PREPARE "set 1s before AEB"; CM_ SG_ 576 LINE_DISTANCE_VISIBLE "Length of line visible, undecoded"; CM_ SG_ 577 LINE_FAR_EDGE_POSITION "Appears to be a measure of line thickness, indicates location of the portion of the line furthest from the car, undecoded"; CM_ SG_ 577 LINE_PARAMETER "Unclear if this is low quality line curvature rate or if this is something else, but it is correlated with line curvature, undecoded"; @@ -425,8 +397,65 @@ CM_ SG_ 577 LINE_SOLID "1 = line is solid"; VAL_ 399 STEER_STATUS 6 "tmp_fault" 5 "fault_1" 4 "no_torque_alert_2" 3 "low_speed_lockout" 2 "no_torque_alert_1" 0 "normal"; -CM_ "honda_crv_ex_2017_can.dbc starts here"; +CM_ "Imported file _bosch_adas_2018.dbc starts here"; +BO_ 479 ACC_CONTROL: 8 EON + SG_ SET_TO_0 : 20|5@0+ (1,0) [0|1] "" XXX + SG_ CONTROL_ON : 23|3@0+ (1,0) [0|5] "" XXX + SG_ GAS_COMMAND : 7|16@0- (1,0) [0|0] "" XXX + SG_ ACCEL_COMMAND : 31|11@0- (0.01,0) [0|0] "m/s2" XXX + SG_ BRAKE_LIGHTS : 62|1@0+ (1,0) [0|1] "" XXX + SG_ BRAKE_REQUEST : 34|1@0+ (1,0) [0|1] "" XXX + SG_ STANDSTILL : 35|1@0+ (1,0) [0|1] "" XXX + SG_ STANDSTILL_RELEASE : 36|1@0+ (1,0) [0|1] "" XXX + SG_ AEB_STATUS : 33|1@0+ (1,0) [0|1] "" XXX + SG_ AEB_BRAKING : 47|1@0+ (1,0) [0|1] "" XXX + SG_ AEB_PREPARE : 43|1@0+ (1,0) [0|1] "" XXX + SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" XXX + SG_ CHECKSUM : 59|4@0+ (1,0) [0|15] "" XXX + +BO_ 495 ACC_CONTROL_ON: 8 XXX + SG_ SET_TO_75 : 31|8@0+ (1,0) [0|255] "" XXX + SG_ SET_TO_30 : 39|8@0+ (1,0) [0|255] "" XXX + SG_ ZEROS_BOH : 23|8@0+ (1,0) [0|255] "" XXX + SG_ ZEROS_BOH2 : 47|16@0+ (1,0) [0|255] "" XXX + SG_ SET_TO_FF : 15|8@0+ (1,0) [0|255] "" XXX + SG_ SET_TO_3 : 6|7@0+ (1,0) [0|4095] "" XXX + SG_ CONTROL_ON : 7|1@0+ (1,0) [0|1] "" XXX + SG_ CHECKSUM : 59|4@0+ (1,0) [0|15] "" XXX + SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" XXX + +BO_ 829 LKAS_HUD: 5 ADAS + SG_ CAM_TEMP_HIGH : 7|1@0+ (1,0) [0|255] "" BDY + SG_ SET_ME_X41 : 6|7@0+ (1,0) [0|127] "" BDY + SG_ BOH : 6|7@0+ (1,0) [0|127] "" BDY + SG_ DASHED_LANES : 14|1@0+ (1,0) [0|1] "" BDY + SG_ DTC : 13|1@0+ (1,0) [0|1] "" BDY + SG_ LKAS_PROBLEM : 12|1@0+ (1,0) [0|1] "" BDY + SG_ LKAS_OFF : 11|1@0+ (1,0) [0|1] "" BDY + SG_ SOLID_LANES : 10|1@0+ (1,0) [0|1] "" BDY + SG_ LDW_RIGHT : 9|1@0+ (1,0) [0|1] "" BDY + SG_ STEERING_REQUIRED : 8|1@0+ (1,0) [0|1] "" BDY + SG_ BOH : 23|2@0+ (1,0) [0|4] "" BDY + SG_ LDW_PROBLEM : 21|1@0+ (1,0) [0|1] "" BDY + SG_ BEEP : 17|2@0+ (1,0) [0|1] "" BDY + SG_ LDW_ON : 28|1@0+ (1,0) [0|1] "" BDY + SG_ LDW_OFF : 27|1@0+ (1,0) [0|1] "" BDY + SG_ CLEAN_WINDSHIELD : 26|1@0+ (1,0) [0|1] "" BDY + SG_ SET_ME_X48 : 31|8@0+ (1,0) [0|255] "" BDY + SG_ COUNTER : 37|2@0+ (1,0) [0|3] "" BDY + SG_ CHECKSUM : 35|4@0+ (1,0) [0|15] "" BDY + +CM_ SG_ 479 CONTROL_ON "Set to 5 when car is being controlled"; +CM_ SG_ 479 AEB_STATUS "set for the duration of AEB event"; +CM_ SG_ 479 AEB_BRAKING "set when braking is commanded during AEB event"; +CM_ SG_ 479 AEB_PREPARE "set 1s before AEB"; +CM_ SG_ 829 BEEP "beeps are pleasant, chimes are for warnngs etc..."; + +VAL_ 829 BEEP 3 "single_beep" 2 "triple_beep" 1 "repeated_beep" 0 "no_beep"; + + +CM_ "Imported file _steering_sensors_a.dbc starts here"; BO_ 330 STEERING_SENSORS: 8 EPS SG_ STEER_ANGLE : 7|16@0- (-0.1,0) [-500|500] "deg" EON SG_ STEER_ANGLE_RATE : 23|16@0- (-1,0) [-3000|3000] "deg/s" EON @@ -437,6 +466,8 @@ BO_ 330 STEERING_SENSORS: 8 EPS SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" EON SG_ CHECKSUM : 59|4@0+ (1,0) [0|15] "" EON +CM_ "honda_crv_ex_2017_can.dbc starts here"; + BO_ 401 GEARBOX: 8 PCM SG_ GEAR_SHIFTER : 5|6@0+ (1,0) [0|63] "" EON SG_ BOH : 45|6@0+ (1,0) [0|63] "" XXX @@ -458,19 +489,6 @@ BO_ 446 BRAKE_MODULE: 3 VSA SG_ COUNTER : 21|2@0+ (1,0) [0|3] "" XXX SG_ CHECKSUM : 19|4@0+ (1,0) [0|15] "" XXX -BO_ 927 RADAR_HUD: 8 RADAR - SG_ ZEROS_BOH : 7|10@0+ (1,0) [0|127] "" BDY - SG_ CMBS_OFF : 12|1@0+ (1,0) [0|1] "" BDY - SG_ ZEROS_BOH3 : 31|32@0+ (1,0) [0|4294967295] "" XXX - SG_ RESUME_INSTRUCTION : 21|1@0+ (1,0) [0|1] "" XXX - SG_ SET_TO_1 : 13|1@0+ (1,0) [0|1] "" BDY - SG_ ZEROS_BOH2 : 11|4@0+ (1,0) [0|1] "" XXX - SG_ APPLY_BRAKES_FOR_CANC : 23|1@0+ (1,0) [0|1] "" XXX - SG_ ACC_ALERTS : 20|5@0+ (1,0) [0|1] "" BDY - SG_ SET_TO_0 : 22|1@0+ (1,0) [0|1] "" XXX - SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" XXX - SG_ CHECKSUM : 59|4@0+ (1,0) [0|15] "" XXX - BO_ 1302 ODOMETER: 8 XXX SG_ ODOMETER : 7|24@0+ (1,0) [0|16777215] "km" EON SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" EON diff --git a/opendbc/honda_crv_executive_2016_can_generated.dbc b/opendbc/honda_crv_executive_2016_can_generated.dbc index c25f4fcfd..a00563215 100644 --- a/opendbc/honda_crv_executive_2016_can_generated.dbc +++ b/opendbc/honda_crv_executive_2016_can_generated.dbc @@ -94,6 +94,43 @@ BO_ 773 SEATBELT_STATUS: 7 BDY SG_ COUNTER : 53|2@0+ (1,0) [0|3] "" EON SG_ CHECKSUM : 51|4@0+ (1,0) [0|3] "" EON +BO_ 777 CAR_SPEED: 8 PCM + SG_ ROUGH_CAR_SPEED : 23|8@0+ (1,0) [0|255] "mph" XXX + SG_ CAR_SPEED : 7|16@0+ (0.01,0) [0|65535] "kph" XXX + SG_ ROUGH_CAR_SPEED_3 : 39|16@0+ (0.01,0) [0|65535] "kph" XXX + SG_ ROUGH_CAR_SPEED_2 : 31|8@0+ (1,0) [0|255] "mph" XXX + SG_ LOCK_STATUS : 55|2@0+ (1,0) [0|255] "" XXX + SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" XXX + SG_ CHECKSUM : 59|4@0+ (1,0) [0|15] "" XXX + SG_ IMPERIAL_UNIT : 63|1@0+ (1,0) [0|1] "" XXX + +BO_ 780 ACC_HUD: 8 ADAS + SG_ PCM_SPEED : 7|16@0+ (0.01,0) [0|250] "kph" BDY + SG_ PCM_GAS : 23|8@0+ (1,0) [0|127] "" BDY + SG_ CRUISE_SPEED : 31|8@0+ (1,0) [0|255] "kph" BDY + SG_ DTC_MODE : 39|1@0+ (1,0) [0|1] "" BDY + SG_ BOH : 38|1@0+ (1,0) [0|1] "" BDY + SG_ ACC_PROBLEM : 37|1@0+ (1,0) [0|1] "" BDY + SG_ FCM_OFF : 35|1@0+ (1,0) [0|1] "" BDY + SG_ FCM_OFF_2 : 36|1@0+ (1,0) [0|1] "" BDY + SG_ FCM_PROBLEM : 34|1@0+ (1,0) [0|1] "" BDY + SG_ RADAR_OBSTRUCTED : 33|1@0+ (1,0) [0|1] "" BDY + SG_ ENABLE_MINI_CAR : 32|1@0+ (1,0) [0|1] "" BDY + SG_ HUD_DISTANCE : 47|2@0+ (1,0) [0|3] "" BDY + SG_ HUD_LEAD : 45|2@0+ (1,0) [0|3] "" BDY + SG_ BOH_3 : 43|1@0+ (1,0) [0|3] "" BDY + SG_ BOH_4 : 42|1@0+ (1,0) [0|3] "" BDY + SG_ BOH_5 : 41|1@0+ (1,0) [0|3] "" BDY + SG_ CRUISE_CONTROL_LABEL : 40|1@0+ (1,0) [0|3] "" BDY + SG_ SET_ME_X01_2 : 55|1@0+ (1,0) [0|1] "" BDY + SG_ IMPERIAL_UNIT : 54|1@0+ (1,0) [0|1] "" BDY + SG_ ACC_ON : 52|1@0+ (1,0) [0|1] "" BDY + SG_ CHIME : 51|3@0+ (1,0) [0|1] "" BDY + SG_ SET_ME_X01 : 48|1@0+ (1,0) [0|1] "" BDY + SG_ ICONS : 63|2@0+ (1,0) [0|1] "" BDY + SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" BDY + SG_ CHECKSUM : 59|4@0+ (1,0) [0|3] "" BDY + BO_ 804 CRUISE: 8 PCM SG_ HUD_SPEED_KPH : 7|8@0+ (1,0) [0|255] "kph" EON SG_ HUD_SPEED_MPH : 15|8@0+ (1,0) [0|255] "mph" EON @@ -104,27 +141,6 @@ BO_ 804 CRUISE: 8 PCM SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" EON SG_ CHECKSUM : 59|4@0+ (1,0) [0|15] "" EON -BO_ 829 LKAS_HUD: 5 ADAS - SG_ CAM_TEMP_HIGH : 7|1@0+ (1,0) [0|255] "" BDY - SG_ SET_ME_X41 : 6|7@0+ (1,0) [0|127] "" BDY - SG_ BOH : 6|7@0+ (1,0) [0|127] "" BDY - SG_ DASHED_LANES : 14|1@0+ (1,0) [0|1] "" BDY - SG_ DTC : 13|1@0+ (1,0) [0|1] "" BDY - SG_ LKAS_PROBLEM : 12|1@0+ (1,0) [0|1] "" BDY - SG_ LKAS_OFF : 11|1@0+ (1,0) [0|1] "" BDY - SG_ SOLID_LANES : 10|1@0+ (1,0) [0|1] "" BDY - SG_ LDW_RIGHT : 9|1@0+ (1,0) [0|1] "" BDY - SG_ STEERING_REQUIRED : 8|1@0+ (1,0) [0|1] "" BDY - SG_ BOH : 23|2@0+ (1,0) [0|4] "" BDY - SG_ LDW_PROBLEM : 21|1@0+ (1,0) [0|1] "" BDY - SG_ BEEP : 17|2@0+ (1,0) [0|1] "" BDY - SG_ LDW_ON : 28|1@0+ (1,0) [0|1] "" BDY - SG_ LDW_OFF : 27|1@0+ (1,0) [0|1] "" BDY - SG_ CLEAN_WINDSHIELD : 26|1@0+ (1,0) [0|1] "" BDY - SG_ SET_ME_X48 : 31|8@0+ (1,0) [0|255] "" BDY - SG_ COUNTER : 37|2@0+ (1,0) [0|3] "" BDY - SG_ CHECKSUM : 35|4@0+ (1,0) [0|15] "" BDY - BO_ 884 STALK_STATUS: 8 XXX SG_ DASHBOARD_ALERT : 39|8@0+ (1,0) [0|255] "" EON SG_ AUTO_HEADLIGHTS : 46|1@0+ (1,0) [0|1] "" EON @@ -158,15 +174,18 @@ CM_ SG_ 420 BRAKE_HOLD_RELATED "On when Brake Hold engaged"; CM_ SG_ 490 LONG_ACCEL "wheel speed derivative, noisy and zero snapping"; CM_ SG_ 773 PASS_AIRBAG_ON "Might just be indicator light"; CM_ SG_ 773 PASS_AIRBAG_OFF "Might just be indicator light"; +CM_ SG_ 780 CRUISE_SPEED "255 = no speed"; +CM_ SG_ 780 PCM_SPEED "Used by Nidec"; +CM_ SG_ 780 PCM_GAS "Used by Nidec"; CM_ SG_ 804 CRUISE_SPEED_PCM "255 = no speed"; -CM_ SG_ 829 BEEP "beeps are pleasant, chimes are for warnngs etc..."; -VAL_ 829 BEEP 3 "single_beep" 2 "triple_beep" 1 "repeated_beep" 0 "no_beep"; +VAL_ 780 CRUISE_SPEED 255 "no_speed" 252 "stopped"; +VAL_ 780 HUD_LEAD 3 "acc_off" 2 "solid_car" 1 "dashed_car" 0 "no_car"; VAL_ 884 DASHBOARD_ALERT 0 "none" 51 "acc_problem" 55 "cmbs_problem" 75 "key_not_detected" 79 "fasten_seatbelt" 111 "lkas_problem" 131 "brake_system_problem" 132 "brake_hold_problem" 139 "tbd" 161 "door_open"; VAL_ 891 WIPERS 4 "High" 2 "Low" 0 "Off"; -CM_ "Imported file _nidec_2017.dbc starts here"; +CM_ "Imported file _nidec_common.dbc starts here"; BO_ 145 KINEMATICS: 8 XXX SG_ LAT_ACCEL : 7|10@0+ (0.02,-512) [-20|20] "m/s2" EON SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" EON @@ -194,11 +213,11 @@ BO_ 487 BRAKE_PRESSURE: 4 VSA BO_ 506 BRAKE_COMMAND: 8 ADAS SG_ COMPUTER_BRAKE : 7|10@0+ (1,0) [0|1] "" EBCM - SG_ SET_ME_X00 : 13|5@0+ (1,0) [0|1] "" EBCM SG_ BRAKE_PUMP_REQUEST : 8|1@0+ (1,0) [0|1] "" EBCM - SG_ SET_ME_X00_2 : 23|3@0+ (1,0) [0|1] "" EBCM + SG_ BRAKE_PUMP_REQUEST_ALT : 11|1@0+ (1,0) [0|1] "" EBCM + SG_ SET_ME_X00 : 23|3@0+ (1,0) [0|1] "" EBCM SG_ CRUISE_OVERRIDE : 20|1@0+ (1,0) [0|1] "" EBCM - SG_ SET_ME_X00_3 : 19|1@0+ (1,0) [0|1] "" EBCM + SG_ SET_ME_X00_2 : 19|1@0+ (1,0) [0|1] "" EBCM SG_ CRUISE_FAULT_CMD : 18|1@0+ (1,0) [0|1] "" EBCM SG_ CRUISE_CANCEL_CMD : 17|1@0+ (1,0) [0|1] "" EBCM SG_ COMPUTER_BRAKE_REQUEST : 16|1@0+ (1,0) [0|1] "" EBCM @@ -208,10 +227,10 @@ BO_ 506 BRAKE_COMMAND: 8 ADAS SG_ BRAKE_LIGHTS : 39|1@0+ (1,0) [0|1] "" EBCM SG_ CRUISE_STATES : 38|7@0+ (1,0) [0|1] "" EBCM SG_ CHIME : 47|3@0+ (1,0) [0|7] "" EBCM - SG_ SET_ME_X00_4 : 44|1@0+ (1,0) [0|1] "" EBCM + SG_ SET_ME_X00_3 : 44|1@0+ (1,0) [0|1] "" EBCM SG_ FCW : 43|2@0+ (1,0) [0|3] "" EBCM SG_ AEB_STATUS : 41|2@0+ (1,0) [0|3] "" XXX - SG_ SET_ME_X00_5 : 55|8@0+ (1,0) [0|0] "" EBCM + SG_ COMPUTER_BRAKE_ALT : 55|10@0+ (1,0) [0|0] "" EBCM SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" EBCM SG_ CHECKSUM : 59|4@0+ (1,0) [0|15] "" EBCM @@ -225,38 +244,26 @@ BO_ 597 ROUGH_WHEEL_SPEED: 8 VSA SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" EON SG_ CHECKSUM : 59|4@0+ (1,0) [0|15] "" EON -BO_ 777 LOCK_STATUS: 8 XXX - SG_ DOORS_UNLOCKED : 54|1@0+ (1,0) [0|1] "" EON - SG_ DOORS_LOCKED : 55|1@0+ (1,0) [0|1] "" EON - SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" EON - SG_ CHECKSUM : 59|4@0+ (1,0) [0|3] "" EON - -BO_ 780 ACC_HUD: 8 ADAS - SG_ PCM_SPEED : 7|16@0+ (0.01,0) [0|250] "kph" BDY - SG_ PCM_GAS : 23|8@0+ (1,0) [0|127] "" BDY - SG_ CRUISE_SPEED : 31|8@0+ (1,0) [0|255] "" BDY - SG_ DTC_MODE : 39|1@0+ (1,0) [0|1] "" BDY - SG_ BOH : 38|1@0+ (1,0) [0|1] "" BDY - SG_ ACC_PROBLEM : 37|1@0+ (1,0) [0|1] "" BDY - SG_ FCM_OFF : 36|1@0+ (1,0) [0|1] "" BDY - SG_ FCM_OFF_2 : 35|1@0+ (1,0) [0|1] "" BDY - SG_ FCM_PROBLEM : 34|1@0+ (1,0) [0|1] "" BDY - SG_ RADAR_OBSTRUCTED : 33|1@0+ (1,0) [0|1] "" BDY - SG_ ENABLE_MINI_CAR : 32|1@0+ (1,0) [0|1] "" BDY - SG_ HUD_DISTANCE : 47|2@0+ (1,0) [0|3] "" BDY - SG_ HUD_LEAD : 45|2@0+ (1,0) [0|3] "" BDY - SG_ BOH_3 : 43|1@0+ (1,0) [0|3] "" BDY - SG_ BOH_4 : 42|1@0+ (1,0) [0|3] "" BDY - SG_ BOH_5 : 41|1@0+ (1,0) [0|3] "" BDY - SG_ CRUISE_CONTROL_LABEL : 40|1@0+ (1,0) [0|3] "" BDY - SG_ SET_ME_X01_2 : 55|1@0+ (1,0) [0|1] "" BDY - SG_ IMPERIAL_UNIT : 54|1@0+ (1,0) [0|1] "" BDY - SG_ HUD_DISTANCE_3 : 52|1@0+ (1,0) [0|1] "" BDY - SG_ CHIME : 51|3@0+ (1,0) [0|1] "" BDY - SG_ SET_ME_X01 : 48|1@0+ (1,0) [0|1] "" BDY - SG_ ICONS : 63|2@0+ (1,0) [0|1] "" BDY - SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" BDY - SG_ CHECKSUM : 59|4@0+ (1,0) [0|3] "" BDY +BO_ 829 LKAS_HUD: 5 ADAS + SG_ CAM_TEMP_HIGH : 7|1@0+ (1,0) [0|255] "" BDY + SG_ SET_ME_X41 : 6|7@0+ (1,0) [0|127] "" BDY + SG_ BOH : 6|7@0+ (1,0) [0|127] "" BDY + SG_ DASHED_LANES : 14|1@0+ (1,0) [0|1] "" BDY + SG_ DTC : 13|1@0+ (1,0) [0|1] "" BDY + SG_ LKAS_PROBLEM : 12|1@0+ (1,0) [0|1] "" BDY + SG_ LKAS_OFF : 11|1@0+ (1,0) [0|1] "" BDY + SG_ SOLID_LANES : 10|1@0+ (1,0) [0|1] "" BDY + SG_ LDW_RIGHT : 9|1@0+ (1,0) [0|1] "" BDY + SG_ STEERING_REQUIRED : 8|1@0+ (1,0) [0|1] "" BDY + SG_ BOH : 23|2@0+ (1,0) [0|4] "" BDY + SG_ LDW_PROBLEM : 21|1@0+ (1,0) [0|1] "" BDY + SG_ BEEP : 17|2@0+ (1,0) [0|1] "" BDY + SG_ LDW_ON : 28|1@0+ (1,0) [0|1] "" BDY + SG_ LDW_OFF : 27|1@0+ (1,0) [0|1] "" BDY + SG_ CLEAN_WINDSHIELD : 26|1@0+ (1,0) [0|1] "" BDY + SG_ SET_ME_X48 : 31|8@0+ (1,0) [0|255] "" BDY + SG_ COUNTER : 37|2@0+ (1,0) [0|3] "" BDY + SG_ CHECKSUM : 35|4@0+ (1,0) [0|15] "" BDY BO_ 892 CRUISE_PARAMS: 8 PCM SG_ CRUISE_SPEED_OFFSET : 31|8@0- (0.1,0) [-128|127] "kph" EON @@ -264,13 +271,14 @@ BO_ 892 CRUISE_PARAMS: 8 PCM SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" EON CM_ SG_ 506 AEB_REQ_1 "set for duration of suspected AEB event"; -CM_ SG_ 780 CRUISE_SPEED "255 = no speed"; +CM_ SG_ 506 COMPUTER_BRAKE_ALT "Used by dual-can Nidec"; +CM_ SG_ 506 BRAKE_PUMP_REQUEST_ALT "Used by dual-can Nidec"; +CM_ SG_ 829 BEEP "beeps are pleasant, chimes are for warnngs etc..."; VAL_ 506 FCW 3 "fcw" 2 "fcw" 1 "fcw" 0 "no_fcw"; VAL_ 506 CHIME 4 "double_chime" 3 "single_chime" 2 "continuous_chime" 1 "repeating_chime" 0 "no_chime"; VAL_ 506 AEB_STATUS 3 "aeb_prepare" 2 "aeb_ready" 1 "aeb_braking" 0 "no_aeb"; -VAL_ 780 CRUISE_SPEED 255 "no_speed" 252 "stopped"; -VAL_ 780 HUD_LEAD 3 "acc_off" 2 "solid_car" 1 "dashed_car" 0 "no_car"; +VAL_ 829 BEEP 3 "single_beep" 2 "triple_beep" 1 "repeated_beep" 0 "no_beep"; CM_ "honda_crv_executive_2016_can.dbc starts here"; diff --git a/opendbc/honda_crv_touring_2016_can_generated.dbc b/opendbc/honda_crv_touring_2016_can_generated.dbc index f6bf793dd..d56746bc2 100644 --- a/opendbc/honda_crv_touring_2016_can_generated.dbc +++ b/opendbc/honda_crv_touring_2016_can_generated.dbc @@ -94,6 +94,43 @@ BO_ 773 SEATBELT_STATUS: 7 BDY SG_ COUNTER : 53|2@0+ (1,0) [0|3] "" EON SG_ CHECKSUM : 51|4@0+ (1,0) [0|3] "" EON +BO_ 777 CAR_SPEED: 8 PCM + SG_ ROUGH_CAR_SPEED : 23|8@0+ (1,0) [0|255] "mph" XXX + SG_ CAR_SPEED : 7|16@0+ (0.01,0) [0|65535] "kph" XXX + SG_ ROUGH_CAR_SPEED_3 : 39|16@0+ (0.01,0) [0|65535] "kph" XXX + SG_ ROUGH_CAR_SPEED_2 : 31|8@0+ (1,0) [0|255] "mph" XXX + SG_ LOCK_STATUS : 55|2@0+ (1,0) [0|255] "" XXX + SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" XXX + SG_ CHECKSUM : 59|4@0+ (1,0) [0|15] "" XXX + SG_ IMPERIAL_UNIT : 63|1@0+ (1,0) [0|1] "" XXX + +BO_ 780 ACC_HUD: 8 ADAS + SG_ PCM_SPEED : 7|16@0+ (0.01,0) [0|250] "kph" BDY + SG_ PCM_GAS : 23|8@0+ (1,0) [0|127] "" BDY + SG_ CRUISE_SPEED : 31|8@0+ (1,0) [0|255] "kph" BDY + SG_ DTC_MODE : 39|1@0+ (1,0) [0|1] "" BDY + SG_ BOH : 38|1@0+ (1,0) [0|1] "" BDY + SG_ ACC_PROBLEM : 37|1@0+ (1,0) [0|1] "" BDY + SG_ FCM_OFF : 35|1@0+ (1,0) [0|1] "" BDY + SG_ FCM_OFF_2 : 36|1@0+ (1,0) [0|1] "" BDY + SG_ FCM_PROBLEM : 34|1@0+ (1,0) [0|1] "" BDY + SG_ RADAR_OBSTRUCTED : 33|1@0+ (1,0) [0|1] "" BDY + SG_ ENABLE_MINI_CAR : 32|1@0+ (1,0) [0|1] "" BDY + SG_ HUD_DISTANCE : 47|2@0+ (1,0) [0|3] "" BDY + SG_ HUD_LEAD : 45|2@0+ (1,0) [0|3] "" BDY + SG_ BOH_3 : 43|1@0+ (1,0) [0|3] "" BDY + SG_ BOH_4 : 42|1@0+ (1,0) [0|3] "" BDY + SG_ BOH_5 : 41|1@0+ (1,0) [0|3] "" BDY + SG_ CRUISE_CONTROL_LABEL : 40|1@0+ (1,0) [0|3] "" BDY + SG_ SET_ME_X01_2 : 55|1@0+ (1,0) [0|1] "" BDY + SG_ IMPERIAL_UNIT : 54|1@0+ (1,0) [0|1] "" BDY + SG_ ACC_ON : 52|1@0+ (1,0) [0|1] "" BDY + SG_ CHIME : 51|3@0+ (1,0) [0|1] "" BDY + SG_ SET_ME_X01 : 48|1@0+ (1,0) [0|1] "" BDY + SG_ ICONS : 63|2@0+ (1,0) [0|1] "" BDY + SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" BDY + SG_ CHECKSUM : 59|4@0+ (1,0) [0|3] "" BDY + BO_ 804 CRUISE: 8 PCM SG_ HUD_SPEED_KPH : 7|8@0+ (1,0) [0|255] "kph" EON SG_ HUD_SPEED_MPH : 15|8@0+ (1,0) [0|255] "mph" EON @@ -104,27 +141,6 @@ BO_ 804 CRUISE: 8 PCM SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" EON SG_ CHECKSUM : 59|4@0+ (1,0) [0|15] "" EON -BO_ 829 LKAS_HUD: 5 ADAS - SG_ CAM_TEMP_HIGH : 7|1@0+ (1,0) [0|255] "" BDY - SG_ SET_ME_X41 : 6|7@0+ (1,0) [0|127] "" BDY - SG_ BOH : 6|7@0+ (1,0) [0|127] "" BDY - SG_ DASHED_LANES : 14|1@0+ (1,0) [0|1] "" BDY - SG_ DTC : 13|1@0+ (1,0) [0|1] "" BDY - SG_ LKAS_PROBLEM : 12|1@0+ (1,0) [0|1] "" BDY - SG_ LKAS_OFF : 11|1@0+ (1,0) [0|1] "" BDY - SG_ SOLID_LANES : 10|1@0+ (1,0) [0|1] "" BDY - SG_ LDW_RIGHT : 9|1@0+ (1,0) [0|1] "" BDY - SG_ STEERING_REQUIRED : 8|1@0+ (1,0) [0|1] "" BDY - SG_ BOH : 23|2@0+ (1,0) [0|4] "" BDY - SG_ LDW_PROBLEM : 21|1@0+ (1,0) [0|1] "" BDY - SG_ BEEP : 17|2@0+ (1,0) [0|1] "" BDY - SG_ LDW_ON : 28|1@0+ (1,0) [0|1] "" BDY - SG_ LDW_OFF : 27|1@0+ (1,0) [0|1] "" BDY - SG_ CLEAN_WINDSHIELD : 26|1@0+ (1,0) [0|1] "" BDY - SG_ SET_ME_X48 : 31|8@0+ (1,0) [0|255] "" BDY - SG_ COUNTER : 37|2@0+ (1,0) [0|3] "" BDY - SG_ CHECKSUM : 35|4@0+ (1,0) [0|15] "" BDY - BO_ 884 STALK_STATUS: 8 XXX SG_ DASHBOARD_ALERT : 39|8@0+ (1,0) [0|255] "" EON SG_ AUTO_HEADLIGHTS : 46|1@0+ (1,0) [0|1] "" EON @@ -158,15 +174,18 @@ CM_ SG_ 420 BRAKE_HOLD_RELATED "On when Brake Hold engaged"; CM_ SG_ 490 LONG_ACCEL "wheel speed derivative, noisy and zero snapping"; CM_ SG_ 773 PASS_AIRBAG_ON "Might just be indicator light"; CM_ SG_ 773 PASS_AIRBAG_OFF "Might just be indicator light"; +CM_ SG_ 780 CRUISE_SPEED "255 = no speed"; +CM_ SG_ 780 PCM_SPEED "Used by Nidec"; +CM_ SG_ 780 PCM_GAS "Used by Nidec"; CM_ SG_ 804 CRUISE_SPEED_PCM "255 = no speed"; -CM_ SG_ 829 BEEP "beeps are pleasant, chimes are for warnngs etc..."; -VAL_ 829 BEEP 3 "single_beep" 2 "triple_beep" 1 "repeated_beep" 0 "no_beep"; +VAL_ 780 CRUISE_SPEED 255 "no_speed" 252 "stopped"; +VAL_ 780 HUD_LEAD 3 "acc_off" 2 "solid_car" 1 "dashed_car" 0 "no_car"; VAL_ 884 DASHBOARD_ALERT 0 "none" 51 "acc_problem" 55 "cmbs_problem" 75 "key_not_detected" 79 "fasten_seatbelt" 111 "lkas_problem" 131 "brake_system_problem" 132 "brake_hold_problem" 139 "tbd" 161 "door_open"; VAL_ 891 WIPERS 4 "High" 2 "Low" 0 "Off"; -CM_ "Imported file _nidec_2017.dbc starts here"; +CM_ "Imported file _nidec_common.dbc starts here"; BO_ 145 KINEMATICS: 8 XXX SG_ LAT_ACCEL : 7|10@0+ (0.02,-512) [-20|20] "m/s2" EON SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" EON @@ -194,11 +213,11 @@ BO_ 487 BRAKE_PRESSURE: 4 VSA BO_ 506 BRAKE_COMMAND: 8 ADAS SG_ COMPUTER_BRAKE : 7|10@0+ (1,0) [0|1] "" EBCM - SG_ SET_ME_X00 : 13|5@0+ (1,0) [0|1] "" EBCM SG_ BRAKE_PUMP_REQUEST : 8|1@0+ (1,0) [0|1] "" EBCM - SG_ SET_ME_X00_2 : 23|3@0+ (1,0) [0|1] "" EBCM + SG_ BRAKE_PUMP_REQUEST_ALT : 11|1@0+ (1,0) [0|1] "" EBCM + SG_ SET_ME_X00 : 23|3@0+ (1,0) [0|1] "" EBCM SG_ CRUISE_OVERRIDE : 20|1@0+ (1,0) [0|1] "" EBCM - SG_ SET_ME_X00_3 : 19|1@0+ (1,0) [0|1] "" EBCM + SG_ SET_ME_X00_2 : 19|1@0+ (1,0) [0|1] "" EBCM SG_ CRUISE_FAULT_CMD : 18|1@0+ (1,0) [0|1] "" EBCM SG_ CRUISE_CANCEL_CMD : 17|1@0+ (1,0) [0|1] "" EBCM SG_ COMPUTER_BRAKE_REQUEST : 16|1@0+ (1,0) [0|1] "" EBCM @@ -208,10 +227,10 @@ BO_ 506 BRAKE_COMMAND: 8 ADAS SG_ BRAKE_LIGHTS : 39|1@0+ (1,0) [0|1] "" EBCM SG_ CRUISE_STATES : 38|7@0+ (1,0) [0|1] "" EBCM SG_ CHIME : 47|3@0+ (1,0) [0|7] "" EBCM - SG_ SET_ME_X00_4 : 44|1@0+ (1,0) [0|1] "" EBCM + SG_ SET_ME_X00_3 : 44|1@0+ (1,0) [0|1] "" EBCM SG_ FCW : 43|2@0+ (1,0) [0|3] "" EBCM SG_ AEB_STATUS : 41|2@0+ (1,0) [0|3] "" XXX - SG_ SET_ME_X00_5 : 55|8@0+ (1,0) [0|0] "" EBCM + SG_ COMPUTER_BRAKE_ALT : 55|10@0+ (1,0) [0|0] "" EBCM SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" EBCM SG_ CHECKSUM : 59|4@0+ (1,0) [0|15] "" EBCM @@ -225,38 +244,26 @@ BO_ 597 ROUGH_WHEEL_SPEED: 8 VSA SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" EON SG_ CHECKSUM : 59|4@0+ (1,0) [0|15] "" EON -BO_ 777 LOCK_STATUS: 8 XXX - SG_ DOORS_UNLOCKED : 54|1@0+ (1,0) [0|1] "" EON - SG_ DOORS_LOCKED : 55|1@0+ (1,0) [0|1] "" EON - SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" EON - SG_ CHECKSUM : 59|4@0+ (1,0) [0|3] "" EON - -BO_ 780 ACC_HUD: 8 ADAS - SG_ PCM_SPEED : 7|16@0+ (0.01,0) [0|250] "kph" BDY - SG_ PCM_GAS : 23|8@0+ (1,0) [0|127] "" BDY - SG_ CRUISE_SPEED : 31|8@0+ (1,0) [0|255] "" BDY - SG_ DTC_MODE : 39|1@0+ (1,0) [0|1] "" BDY - SG_ BOH : 38|1@0+ (1,0) [0|1] "" BDY - SG_ ACC_PROBLEM : 37|1@0+ (1,0) [0|1] "" BDY - SG_ FCM_OFF : 36|1@0+ (1,0) [0|1] "" BDY - SG_ FCM_OFF_2 : 35|1@0+ (1,0) [0|1] "" BDY - SG_ FCM_PROBLEM : 34|1@0+ (1,0) [0|1] "" BDY - SG_ RADAR_OBSTRUCTED : 33|1@0+ (1,0) [0|1] "" BDY - SG_ ENABLE_MINI_CAR : 32|1@0+ (1,0) [0|1] "" BDY - SG_ HUD_DISTANCE : 47|2@0+ (1,0) [0|3] "" BDY - SG_ HUD_LEAD : 45|2@0+ (1,0) [0|3] "" BDY - SG_ BOH_3 : 43|1@0+ (1,0) [0|3] "" BDY - SG_ BOH_4 : 42|1@0+ (1,0) [0|3] "" BDY - SG_ BOH_5 : 41|1@0+ (1,0) [0|3] "" BDY - SG_ CRUISE_CONTROL_LABEL : 40|1@0+ (1,0) [0|3] "" BDY - SG_ SET_ME_X01_2 : 55|1@0+ (1,0) [0|1] "" BDY - SG_ IMPERIAL_UNIT : 54|1@0+ (1,0) [0|1] "" BDY - SG_ HUD_DISTANCE_3 : 52|1@0+ (1,0) [0|1] "" BDY - SG_ CHIME : 51|3@0+ (1,0) [0|1] "" BDY - SG_ SET_ME_X01 : 48|1@0+ (1,0) [0|1] "" BDY - SG_ ICONS : 63|2@0+ (1,0) [0|1] "" BDY - SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" BDY - SG_ CHECKSUM : 59|4@0+ (1,0) [0|3] "" BDY +BO_ 829 LKAS_HUD: 5 ADAS + SG_ CAM_TEMP_HIGH : 7|1@0+ (1,0) [0|255] "" BDY + SG_ SET_ME_X41 : 6|7@0+ (1,0) [0|127] "" BDY + SG_ BOH : 6|7@0+ (1,0) [0|127] "" BDY + SG_ DASHED_LANES : 14|1@0+ (1,0) [0|1] "" BDY + SG_ DTC : 13|1@0+ (1,0) [0|1] "" BDY + SG_ LKAS_PROBLEM : 12|1@0+ (1,0) [0|1] "" BDY + SG_ LKAS_OFF : 11|1@0+ (1,0) [0|1] "" BDY + SG_ SOLID_LANES : 10|1@0+ (1,0) [0|1] "" BDY + SG_ LDW_RIGHT : 9|1@0+ (1,0) [0|1] "" BDY + SG_ STEERING_REQUIRED : 8|1@0+ (1,0) [0|1] "" BDY + SG_ BOH : 23|2@0+ (1,0) [0|4] "" BDY + SG_ LDW_PROBLEM : 21|1@0+ (1,0) [0|1] "" BDY + SG_ BEEP : 17|2@0+ (1,0) [0|1] "" BDY + SG_ LDW_ON : 28|1@0+ (1,0) [0|1] "" BDY + SG_ LDW_OFF : 27|1@0+ (1,0) [0|1] "" BDY + SG_ CLEAN_WINDSHIELD : 26|1@0+ (1,0) [0|1] "" BDY + SG_ SET_ME_X48 : 31|8@0+ (1,0) [0|255] "" BDY + SG_ COUNTER : 37|2@0+ (1,0) [0|3] "" BDY + SG_ CHECKSUM : 35|4@0+ (1,0) [0|15] "" BDY BO_ 892 CRUISE_PARAMS: 8 PCM SG_ CRUISE_SPEED_OFFSET : 31|8@0- (0.1,0) [-128|127] "kph" EON @@ -264,22 +271,25 @@ BO_ 892 CRUISE_PARAMS: 8 PCM SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" EON CM_ SG_ 506 AEB_REQ_1 "set for duration of suspected AEB event"; -CM_ SG_ 780 CRUISE_SPEED "255 = no speed"; +CM_ SG_ 506 COMPUTER_BRAKE_ALT "Used by dual-can Nidec"; +CM_ SG_ 506 BRAKE_PUMP_REQUEST_ALT "Used by dual-can Nidec"; +CM_ SG_ 829 BEEP "beeps are pleasant, chimes are for warnngs etc..."; VAL_ 506 FCW 3 "fcw" 2 "fcw" 1 "fcw" 0 "no_fcw"; VAL_ 506 CHIME 4 "double_chime" 3 "single_chime" 2 "continuous_chime" 1 "repeating_chime" 0 "no_chime"; VAL_ 506 AEB_STATUS 3 "aeb_prepare" 2 "aeb_ready" 1 "aeb_braking" 0 "no_aeb"; -VAL_ 780 CRUISE_SPEED 255 "no_speed" 252 "stopped"; -VAL_ 780 HUD_LEAD 3 "acc_off" 2 "solid_car" 1 "dashed_car" 0 "no_car"; +VAL_ 829 BEEP 3 "single_beep" 2 "triple_beep" 1 "repeated_beep" 0 "no_beep"; -CM_ "honda_crv_touring_2016_can.dbc starts here"; +CM_ "Imported file _steering_sensors_b.dbc starts here"; BO_ 342 STEERING_SENSORS: 6 EPS SG_ STEER_ANGLE : 7|16@0- (-0.1,0) [-500|500] "deg" EON SG_ STEER_ANGLE_RATE : 23|16@0- (-1,0) [-3000|3000] "deg/s" EON SG_ COUNTER : 45|2@0+ (1,0) [0|3] "" EON SG_ CHECKSUM : 43|4@0+ (1,0) [0|15] "" EON +CM_ "honda_crv_touring_2016_can.dbc starts here"; + BO_ 399 STEER_STATUS: 6 EPS SG_ STEER_TORQUE_SENSOR : 7|12@0- (-1,0) [-2047.5|2047.5] "tbd" EON SG_ STEER_ANGLE_RATE : 23|16@0- (-0.1,0) [-31000|31000] "deg/s" EON diff --git a/opendbc/honda_fit_ex_2018_can_generated.dbc b/opendbc/honda_fit_ex_2018_can_generated.dbc index b3bda7d97..b1900455c 100644 --- a/opendbc/honda_fit_ex_2018_can_generated.dbc +++ b/opendbc/honda_fit_ex_2018_can_generated.dbc @@ -94,6 +94,43 @@ BO_ 773 SEATBELT_STATUS: 7 BDY SG_ COUNTER : 53|2@0+ (1,0) [0|3] "" EON SG_ CHECKSUM : 51|4@0+ (1,0) [0|3] "" EON +BO_ 777 CAR_SPEED: 8 PCM + SG_ ROUGH_CAR_SPEED : 23|8@0+ (1,0) [0|255] "mph" XXX + SG_ CAR_SPEED : 7|16@0+ (0.01,0) [0|65535] "kph" XXX + SG_ ROUGH_CAR_SPEED_3 : 39|16@0+ (0.01,0) [0|65535] "kph" XXX + SG_ ROUGH_CAR_SPEED_2 : 31|8@0+ (1,0) [0|255] "mph" XXX + SG_ LOCK_STATUS : 55|2@0+ (1,0) [0|255] "" XXX + SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" XXX + SG_ CHECKSUM : 59|4@0+ (1,0) [0|15] "" XXX + SG_ IMPERIAL_UNIT : 63|1@0+ (1,0) [0|1] "" XXX + +BO_ 780 ACC_HUD: 8 ADAS + SG_ PCM_SPEED : 7|16@0+ (0.01,0) [0|250] "kph" BDY + SG_ PCM_GAS : 23|8@0+ (1,0) [0|127] "" BDY + SG_ CRUISE_SPEED : 31|8@0+ (1,0) [0|255] "kph" BDY + SG_ DTC_MODE : 39|1@0+ (1,0) [0|1] "" BDY + SG_ BOH : 38|1@0+ (1,0) [0|1] "" BDY + SG_ ACC_PROBLEM : 37|1@0+ (1,0) [0|1] "" BDY + SG_ FCM_OFF : 35|1@0+ (1,0) [0|1] "" BDY + SG_ FCM_OFF_2 : 36|1@0+ (1,0) [0|1] "" BDY + SG_ FCM_PROBLEM : 34|1@0+ (1,0) [0|1] "" BDY + SG_ RADAR_OBSTRUCTED : 33|1@0+ (1,0) [0|1] "" BDY + SG_ ENABLE_MINI_CAR : 32|1@0+ (1,0) [0|1] "" BDY + SG_ HUD_DISTANCE : 47|2@0+ (1,0) [0|3] "" BDY + SG_ HUD_LEAD : 45|2@0+ (1,0) [0|3] "" BDY + SG_ BOH_3 : 43|1@0+ (1,0) [0|3] "" BDY + SG_ BOH_4 : 42|1@0+ (1,0) [0|3] "" BDY + SG_ BOH_5 : 41|1@0+ (1,0) [0|3] "" BDY + SG_ CRUISE_CONTROL_LABEL : 40|1@0+ (1,0) [0|3] "" BDY + SG_ SET_ME_X01_2 : 55|1@0+ (1,0) [0|1] "" BDY + SG_ IMPERIAL_UNIT : 54|1@0+ (1,0) [0|1] "" BDY + SG_ ACC_ON : 52|1@0+ (1,0) [0|1] "" BDY + SG_ CHIME : 51|3@0+ (1,0) [0|1] "" BDY + SG_ SET_ME_X01 : 48|1@0+ (1,0) [0|1] "" BDY + SG_ ICONS : 63|2@0+ (1,0) [0|1] "" BDY + SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" BDY + SG_ CHECKSUM : 59|4@0+ (1,0) [0|3] "" BDY + BO_ 804 CRUISE: 8 PCM SG_ HUD_SPEED_KPH : 7|8@0+ (1,0) [0|255] "kph" EON SG_ HUD_SPEED_MPH : 15|8@0+ (1,0) [0|255] "mph" EON @@ -104,27 +141,6 @@ BO_ 804 CRUISE: 8 PCM SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" EON SG_ CHECKSUM : 59|4@0+ (1,0) [0|15] "" EON -BO_ 829 LKAS_HUD: 5 ADAS - SG_ CAM_TEMP_HIGH : 7|1@0+ (1,0) [0|255] "" BDY - SG_ SET_ME_X41 : 6|7@0+ (1,0) [0|127] "" BDY - SG_ BOH : 6|7@0+ (1,0) [0|127] "" BDY - SG_ DASHED_LANES : 14|1@0+ (1,0) [0|1] "" BDY - SG_ DTC : 13|1@0+ (1,0) [0|1] "" BDY - SG_ LKAS_PROBLEM : 12|1@0+ (1,0) [0|1] "" BDY - SG_ LKAS_OFF : 11|1@0+ (1,0) [0|1] "" BDY - SG_ SOLID_LANES : 10|1@0+ (1,0) [0|1] "" BDY - SG_ LDW_RIGHT : 9|1@0+ (1,0) [0|1] "" BDY - SG_ STEERING_REQUIRED : 8|1@0+ (1,0) [0|1] "" BDY - SG_ BOH : 23|2@0+ (1,0) [0|4] "" BDY - SG_ LDW_PROBLEM : 21|1@0+ (1,0) [0|1] "" BDY - SG_ BEEP : 17|2@0+ (1,0) [0|1] "" BDY - SG_ LDW_ON : 28|1@0+ (1,0) [0|1] "" BDY - SG_ LDW_OFF : 27|1@0+ (1,0) [0|1] "" BDY - SG_ CLEAN_WINDSHIELD : 26|1@0+ (1,0) [0|1] "" BDY - SG_ SET_ME_X48 : 31|8@0+ (1,0) [0|255] "" BDY - SG_ COUNTER : 37|2@0+ (1,0) [0|3] "" BDY - SG_ CHECKSUM : 35|4@0+ (1,0) [0|15] "" BDY - BO_ 884 STALK_STATUS: 8 XXX SG_ DASHBOARD_ALERT : 39|8@0+ (1,0) [0|255] "" EON SG_ AUTO_HEADLIGHTS : 46|1@0+ (1,0) [0|1] "" EON @@ -158,15 +174,18 @@ CM_ SG_ 420 BRAKE_HOLD_RELATED "On when Brake Hold engaged"; CM_ SG_ 490 LONG_ACCEL "wheel speed derivative, noisy and zero snapping"; CM_ SG_ 773 PASS_AIRBAG_ON "Might just be indicator light"; CM_ SG_ 773 PASS_AIRBAG_OFF "Might just be indicator light"; +CM_ SG_ 780 CRUISE_SPEED "255 = no speed"; +CM_ SG_ 780 PCM_SPEED "Used by Nidec"; +CM_ SG_ 780 PCM_GAS "Used by Nidec"; CM_ SG_ 804 CRUISE_SPEED_PCM "255 = no speed"; -CM_ SG_ 829 BEEP "beeps are pleasant, chimes are for warnngs etc..."; -VAL_ 829 BEEP 3 "single_beep" 2 "triple_beep" 1 "repeated_beep" 0 "no_beep"; +VAL_ 780 CRUISE_SPEED 255 "no_speed" 252 "stopped"; +VAL_ 780 HUD_LEAD 3 "acc_off" 2 "solid_car" 1 "dashed_car" 0 "no_car"; VAL_ 884 DASHBOARD_ALERT 0 "none" 51 "acc_problem" 55 "cmbs_problem" 75 "key_not_detected" 79 "fasten_seatbelt" 111 "lkas_problem" 131 "brake_system_problem" 132 "brake_hold_problem" 139 "tbd" 161 "door_open"; VAL_ 891 WIPERS 4 "High" 2 "Low" 0 "Off"; -CM_ "Imported file _nidec_2017.dbc starts here"; +CM_ "Imported file _nidec_common.dbc starts here"; BO_ 145 KINEMATICS: 8 XXX SG_ LAT_ACCEL : 7|10@0+ (0.02,-512) [-20|20] "m/s2" EON SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" EON @@ -194,11 +213,11 @@ BO_ 487 BRAKE_PRESSURE: 4 VSA BO_ 506 BRAKE_COMMAND: 8 ADAS SG_ COMPUTER_BRAKE : 7|10@0+ (1,0) [0|1] "" EBCM - SG_ SET_ME_X00 : 13|5@0+ (1,0) [0|1] "" EBCM SG_ BRAKE_PUMP_REQUEST : 8|1@0+ (1,0) [0|1] "" EBCM - SG_ SET_ME_X00_2 : 23|3@0+ (1,0) [0|1] "" EBCM + SG_ BRAKE_PUMP_REQUEST_ALT : 11|1@0+ (1,0) [0|1] "" EBCM + SG_ SET_ME_X00 : 23|3@0+ (1,0) [0|1] "" EBCM SG_ CRUISE_OVERRIDE : 20|1@0+ (1,0) [0|1] "" EBCM - SG_ SET_ME_X00_3 : 19|1@0+ (1,0) [0|1] "" EBCM + SG_ SET_ME_X00_2 : 19|1@0+ (1,0) [0|1] "" EBCM SG_ CRUISE_FAULT_CMD : 18|1@0+ (1,0) [0|1] "" EBCM SG_ CRUISE_CANCEL_CMD : 17|1@0+ (1,0) [0|1] "" EBCM SG_ COMPUTER_BRAKE_REQUEST : 16|1@0+ (1,0) [0|1] "" EBCM @@ -208,10 +227,10 @@ BO_ 506 BRAKE_COMMAND: 8 ADAS SG_ BRAKE_LIGHTS : 39|1@0+ (1,0) [0|1] "" EBCM SG_ CRUISE_STATES : 38|7@0+ (1,0) [0|1] "" EBCM SG_ CHIME : 47|3@0+ (1,0) [0|7] "" EBCM - SG_ SET_ME_X00_4 : 44|1@0+ (1,0) [0|1] "" EBCM + SG_ SET_ME_X00_3 : 44|1@0+ (1,0) [0|1] "" EBCM SG_ FCW : 43|2@0+ (1,0) [0|3] "" EBCM SG_ AEB_STATUS : 41|2@0+ (1,0) [0|3] "" XXX - SG_ SET_ME_X00_5 : 55|8@0+ (1,0) [0|0] "" EBCM + SG_ COMPUTER_BRAKE_ALT : 55|10@0+ (1,0) [0|0] "" EBCM SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" EBCM SG_ CHECKSUM : 59|4@0+ (1,0) [0|15] "" EBCM @@ -225,38 +244,26 @@ BO_ 597 ROUGH_WHEEL_SPEED: 8 VSA SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" EON SG_ CHECKSUM : 59|4@0+ (1,0) [0|15] "" EON -BO_ 777 LOCK_STATUS: 8 XXX - SG_ DOORS_UNLOCKED : 54|1@0+ (1,0) [0|1] "" EON - SG_ DOORS_LOCKED : 55|1@0+ (1,0) [0|1] "" EON - SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" EON - SG_ CHECKSUM : 59|4@0+ (1,0) [0|3] "" EON - -BO_ 780 ACC_HUD: 8 ADAS - SG_ PCM_SPEED : 7|16@0+ (0.01,0) [0|250] "kph" BDY - SG_ PCM_GAS : 23|8@0+ (1,0) [0|127] "" BDY - SG_ CRUISE_SPEED : 31|8@0+ (1,0) [0|255] "" BDY - SG_ DTC_MODE : 39|1@0+ (1,0) [0|1] "" BDY - SG_ BOH : 38|1@0+ (1,0) [0|1] "" BDY - SG_ ACC_PROBLEM : 37|1@0+ (1,0) [0|1] "" BDY - SG_ FCM_OFF : 36|1@0+ (1,0) [0|1] "" BDY - SG_ FCM_OFF_2 : 35|1@0+ (1,0) [0|1] "" BDY - SG_ FCM_PROBLEM : 34|1@0+ (1,0) [0|1] "" BDY - SG_ RADAR_OBSTRUCTED : 33|1@0+ (1,0) [0|1] "" BDY - SG_ ENABLE_MINI_CAR : 32|1@0+ (1,0) [0|1] "" BDY - SG_ HUD_DISTANCE : 47|2@0+ (1,0) [0|3] "" BDY - SG_ HUD_LEAD : 45|2@0+ (1,0) [0|3] "" BDY - SG_ BOH_3 : 43|1@0+ (1,0) [0|3] "" BDY - SG_ BOH_4 : 42|1@0+ (1,0) [0|3] "" BDY - SG_ BOH_5 : 41|1@0+ (1,0) [0|3] "" BDY - SG_ CRUISE_CONTROL_LABEL : 40|1@0+ (1,0) [0|3] "" BDY - SG_ SET_ME_X01_2 : 55|1@0+ (1,0) [0|1] "" BDY - SG_ IMPERIAL_UNIT : 54|1@0+ (1,0) [0|1] "" BDY - SG_ HUD_DISTANCE_3 : 52|1@0+ (1,0) [0|1] "" BDY - SG_ CHIME : 51|3@0+ (1,0) [0|1] "" BDY - SG_ SET_ME_X01 : 48|1@0+ (1,0) [0|1] "" BDY - SG_ ICONS : 63|2@0+ (1,0) [0|1] "" BDY - SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" BDY - SG_ CHECKSUM : 59|4@0+ (1,0) [0|3] "" BDY +BO_ 829 LKAS_HUD: 5 ADAS + SG_ CAM_TEMP_HIGH : 7|1@0+ (1,0) [0|255] "" BDY + SG_ SET_ME_X41 : 6|7@0+ (1,0) [0|127] "" BDY + SG_ BOH : 6|7@0+ (1,0) [0|127] "" BDY + SG_ DASHED_LANES : 14|1@0+ (1,0) [0|1] "" BDY + SG_ DTC : 13|1@0+ (1,0) [0|1] "" BDY + SG_ LKAS_PROBLEM : 12|1@0+ (1,0) [0|1] "" BDY + SG_ LKAS_OFF : 11|1@0+ (1,0) [0|1] "" BDY + SG_ SOLID_LANES : 10|1@0+ (1,0) [0|1] "" BDY + SG_ LDW_RIGHT : 9|1@0+ (1,0) [0|1] "" BDY + SG_ STEERING_REQUIRED : 8|1@0+ (1,0) [0|1] "" BDY + SG_ BOH : 23|2@0+ (1,0) [0|4] "" BDY + SG_ LDW_PROBLEM : 21|1@0+ (1,0) [0|1] "" BDY + SG_ BEEP : 17|2@0+ (1,0) [0|1] "" BDY + SG_ LDW_ON : 28|1@0+ (1,0) [0|1] "" BDY + SG_ LDW_OFF : 27|1@0+ (1,0) [0|1] "" BDY + SG_ CLEAN_WINDSHIELD : 26|1@0+ (1,0) [0|1] "" BDY + SG_ SET_ME_X48 : 31|8@0+ (1,0) [0|255] "" BDY + SG_ COUNTER : 37|2@0+ (1,0) [0|3] "" BDY + SG_ CHECKSUM : 35|4@0+ (1,0) [0|15] "" BDY BO_ 892 CRUISE_PARAMS: 8 PCM SG_ CRUISE_SPEED_OFFSET : 31|8@0- (0.1,0) [-128|127] "kph" EON @@ -264,13 +271,22 @@ BO_ 892 CRUISE_PARAMS: 8 PCM SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" EON CM_ SG_ 506 AEB_REQ_1 "set for duration of suspected AEB event"; -CM_ SG_ 780 CRUISE_SPEED "255 = no speed"; +CM_ SG_ 506 COMPUTER_BRAKE_ALT "Used by dual-can Nidec"; +CM_ SG_ 506 BRAKE_PUMP_REQUEST_ALT "Used by dual-can Nidec"; +CM_ SG_ 829 BEEP "beeps are pleasant, chimes are for warnngs etc..."; VAL_ 506 FCW 3 "fcw" 2 "fcw" 1 "fcw" 0 "no_fcw"; VAL_ 506 CHIME 4 "double_chime" 3 "single_chime" 2 "continuous_chime" 1 "repeating_chime" 0 "no_chime"; VAL_ 506 AEB_STATUS 3 "aeb_prepare" 2 "aeb_ready" 1 "aeb_braking" 0 "no_aeb"; -VAL_ 780 CRUISE_SPEED 255 "no_speed" 252 "stopped"; -VAL_ 780 HUD_LEAD 3 "acc_off" 2 "solid_car" 1 "dashed_car" 0 "no_car"; +VAL_ 829 BEEP 3 "single_beep" 2 "triple_beep" 1 "repeated_beep" 0 "no_beep"; + + +CM_ "Imported file _steering_sensors_b.dbc starts here"; +BO_ 342 STEERING_SENSORS: 6 EPS + SG_ STEER_ANGLE : 7|16@0- (-0.1,0) [-500|500] "deg" EON + SG_ STEER_ANGLE_RATE : 23|16@0- (-1,0) [-3000|3000] "deg/s" EON + SG_ COUNTER : 45|2@0+ (1,0) [0|3] "" EON + SG_ CHECKSUM : 43|4@0+ (1,0) [0|15] "" EON CM_ "honda_fit_ex_2018_can.dbc starts here"; @@ -282,12 +298,6 @@ BO_ 228 STEERING_CONTROL: 5 ADAS SG_ COUNTER : 37|2@0+ (1,0) [0|3] "" EPS SG_ CHECKSUM : 35|4@0+ (1,0) [0|15] "" EPS -BO_ 342 STEERING_SENSORS: 6 EPS - SG_ STEER_ANGLE : 7|16@0- (-0.1,0) [-500|500] "deg" EON - SG_ STEER_ANGLE_RATE : 23|16@0- (-1,0) [-3000|3000] "deg/s" EON - SG_ COUNTER : 45|2@0+ (1,0) [0|3] "" EON - SG_ CHECKSUM : 43|4@0+ (1,0) [0|3] "" EON - BO_ 399 STEER_STATUS: 7 EPS SG_ STEER_TORQUE_SENSOR : 7|16@0- (-1,0) [-31000|31000] "tbd" EON SG_ STEER_ANGLE_RATE : 23|16@0- (-0.1,0) [-31000|31000] "deg/s" EON diff --git a/opendbc/honda_insight_ex_2019_can_generated.dbc b/opendbc/honda_insight_ex_2019_can_generated.dbc index 12ccc0873..095774203 100644 --- a/opendbc/honda_insight_ex_2019_can_generated.dbc +++ b/opendbc/honda_insight_ex_2019_can_generated.dbc @@ -76,6 +76,43 @@ BO_ 773 SEATBELT_STATUS: 7 BDY SG_ COUNTER : 53|2@0+ (1,0) [0|3] "" EON SG_ CHECKSUM : 51|4@0+ (1,0) [0|3] "" EON +BO_ 777 CAR_SPEED: 8 PCM + SG_ ROUGH_CAR_SPEED : 23|8@0+ (1,0) [0|255] "mph" XXX + SG_ CAR_SPEED : 7|16@0+ (0.01,0) [0|65535] "kph" XXX + SG_ ROUGH_CAR_SPEED_3 : 39|16@0+ (0.01,0) [0|65535] "kph" XXX + SG_ ROUGH_CAR_SPEED_2 : 31|8@0+ (1,0) [0|255] "mph" XXX + SG_ LOCK_STATUS : 55|2@0+ (1,0) [0|255] "" XXX + SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" XXX + SG_ CHECKSUM : 59|4@0+ (1,0) [0|15] "" XXX + SG_ IMPERIAL_UNIT : 63|1@0+ (1,0) [0|1] "" XXX + +BO_ 780 ACC_HUD: 8 ADAS + SG_ PCM_SPEED : 7|16@0+ (0.01,0) [0|250] "kph" BDY + SG_ PCM_GAS : 23|8@0+ (1,0) [0|127] "" BDY + SG_ CRUISE_SPEED : 31|8@0+ (1,0) [0|255] "kph" BDY + SG_ DTC_MODE : 39|1@0+ (1,0) [0|1] "" BDY + SG_ BOH : 38|1@0+ (1,0) [0|1] "" BDY + SG_ ACC_PROBLEM : 37|1@0+ (1,0) [0|1] "" BDY + SG_ FCM_OFF : 35|1@0+ (1,0) [0|1] "" BDY + SG_ FCM_OFF_2 : 36|1@0+ (1,0) [0|1] "" BDY + SG_ FCM_PROBLEM : 34|1@0+ (1,0) [0|1] "" BDY + SG_ RADAR_OBSTRUCTED : 33|1@0+ (1,0) [0|1] "" BDY + SG_ ENABLE_MINI_CAR : 32|1@0+ (1,0) [0|1] "" BDY + SG_ HUD_DISTANCE : 47|2@0+ (1,0) [0|3] "" BDY + SG_ HUD_LEAD : 45|2@0+ (1,0) [0|3] "" BDY + SG_ BOH_3 : 43|1@0+ (1,0) [0|3] "" BDY + SG_ BOH_4 : 42|1@0+ (1,0) [0|3] "" BDY + SG_ BOH_5 : 41|1@0+ (1,0) [0|3] "" BDY + SG_ CRUISE_CONTROL_LABEL : 40|1@0+ (1,0) [0|3] "" BDY + SG_ SET_ME_X01_2 : 55|1@0+ (1,0) [0|1] "" BDY + SG_ IMPERIAL_UNIT : 54|1@0+ (1,0) [0|1] "" BDY + SG_ ACC_ON : 52|1@0+ (1,0) [0|1] "" BDY + SG_ CHIME : 51|3@0+ (1,0) [0|1] "" BDY + SG_ SET_ME_X01 : 48|1@0+ (1,0) [0|1] "" BDY + SG_ ICONS : 63|2@0+ (1,0) [0|1] "" BDY + SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" BDY + SG_ CHECKSUM : 59|4@0+ (1,0) [0|3] "" BDY + BO_ 804 CRUISE: 8 PCM SG_ HUD_SPEED_KPH : 7|8@0+ (1,0) [0|255] "kph" EON SG_ HUD_SPEED_MPH : 15|8@0+ (1,0) [0|255] "mph" EON @@ -86,27 +123,6 @@ BO_ 804 CRUISE: 8 PCM SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" EON SG_ CHECKSUM : 59|4@0+ (1,0) [0|15] "" EON -BO_ 829 LKAS_HUD: 5 ADAS - SG_ CAM_TEMP_HIGH : 7|1@0+ (1,0) [0|255] "" BDY - SG_ SET_ME_X41 : 6|7@0+ (1,0) [0|127] "" BDY - SG_ BOH : 6|7@0+ (1,0) [0|127] "" BDY - SG_ DASHED_LANES : 14|1@0+ (1,0) [0|1] "" BDY - SG_ DTC : 13|1@0+ (1,0) [0|1] "" BDY - SG_ LKAS_PROBLEM : 12|1@0+ (1,0) [0|1] "" BDY - SG_ LKAS_OFF : 11|1@0+ (1,0) [0|1] "" BDY - SG_ SOLID_LANES : 10|1@0+ (1,0) [0|1] "" BDY - SG_ LDW_RIGHT : 9|1@0+ (1,0) [0|1] "" BDY - SG_ STEERING_REQUIRED : 8|1@0+ (1,0) [0|1] "" BDY - SG_ BOH : 23|2@0+ (1,0) [0|4] "" BDY - SG_ LDW_PROBLEM : 21|1@0+ (1,0) [0|1] "" BDY - SG_ BEEP : 17|2@0+ (1,0) [0|1] "" BDY - SG_ LDW_ON : 28|1@0+ (1,0) [0|1] "" BDY - SG_ LDW_OFF : 27|1@0+ (1,0) [0|1] "" BDY - SG_ CLEAN_WINDSHIELD : 26|1@0+ (1,0) [0|1] "" BDY - SG_ SET_ME_X48 : 31|8@0+ (1,0) [0|255] "" BDY - SG_ COUNTER : 37|2@0+ (1,0) [0|3] "" BDY - SG_ CHECKSUM : 35|4@0+ (1,0) [0|15] "" BDY - BO_ 884 STALK_STATUS: 8 XXX SG_ DASHBOARD_ALERT : 39|8@0+ (1,0) [0|255] "" EON SG_ AUTO_HEADLIGHTS : 46|1@0+ (1,0) [0|1] "" EON @@ -140,10 +156,13 @@ CM_ SG_ 420 BRAKE_HOLD_RELATED "On when Brake Hold engaged"; CM_ SG_ 490 LONG_ACCEL "wheel speed derivative, noisy and zero snapping"; CM_ SG_ 773 PASS_AIRBAG_ON "Might just be indicator light"; CM_ SG_ 773 PASS_AIRBAG_OFF "Might just be indicator light"; +CM_ SG_ 780 CRUISE_SPEED "255 = no speed"; +CM_ SG_ 780 PCM_SPEED "Used by Nidec"; +CM_ SG_ 780 PCM_GAS "Used by Nidec"; CM_ SG_ 804 CRUISE_SPEED_PCM "255 = no speed"; -CM_ SG_ 829 BEEP "beeps are pleasant, chimes are for warnngs etc..."; -VAL_ 829 BEEP 3 "single_beep" 2 "triple_beep" 1 "repeated_beep" 0 "no_beep"; +VAL_ 780 CRUISE_SPEED 255 "no_speed" 252 "stopped"; +VAL_ 780 HUD_LEAD 3 "acc_off" 2 "solid_car" 1 "dashed_car" 0 "no_car"; VAL_ 884 DASHBOARD_ALERT 0 "none" 51 "acc_problem" 55 "cmbs_problem" 75 "key_not_detected" 79 "fasten_seatbelt" 111 "lkas_problem" 131 "brake_system_problem" 132 "brake_hold_problem" 139 "tbd" 161 "door_open"; VAL_ 891 WIPERS 4 "High" 2 "Low" 0 "Off"; @@ -194,32 +213,6 @@ BO_ 450 EPB_STATUS: 8 EPB SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" XXX SG_ CHECKSUM : 59|4@0+ (1,0) [0|15] "" XXX -BO_ 479 ACC_CONTROL: 8 EON - SG_ SET_TO_0 : 20|5@0+ (1,0) [0|1] "" XXX - SG_ CONTROL_ON : 23|3@0+ (1,0) [0|5] "" XXX - SG_ GAS_COMMAND : 7|16@0- (1,0) [0|0] "" XXX - SG_ ACCEL_COMMAND : 31|11@0- (0.01,0) [0|0] "m/s2" XXX - SG_ BRAKE_LIGHTS : 62|1@0+ (1,0) [0|1] "" XXX - SG_ BRAKE_REQUEST : 34|1@0+ (1,0) [0|1] "" XXX - SG_ STANDSTILL : 35|1@0+ (1,0) [0|1] "" XXX - SG_ STANDSTILL_RELEASE : 36|1@0+ (1,0) [0|1] "" XXX - SG_ AEB_STATUS : 33|1@0+ (1,0) [0|1] "" XXX - SG_ AEB_BRAKING : 47|1@0+ (1,0) [0|1] "" XXX - SG_ AEB_PREPARE : 43|1@0+ (1,0) [0|1] "" XXX - SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" XXX - SG_ CHECKSUM : 59|4@0+ (1,0) [0|15] "" XXX - -BO_ 495 ACC_CONTROL_ON: 8 XXX - SG_ SET_TO_75 : 31|8@0+ (1,0) [0|255] "" XXX - SG_ SET_TO_30 : 39|8@0+ (1,0) [0|255] "" XXX - SG_ ZEROS_BOH : 23|8@0+ (1,0) [0|255] "" XXX - SG_ ZEROS_BOH2 : 47|16@0+ (1,0) [0|255] "" XXX - SG_ SET_TO_FF : 15|8@0+ (1,0) [0|255] "" XXX - SG_ SET_TO_3 : 6|7@0+ (1,0) [0|4095] "" XXX - SG_ CONTROL_ON : 7|1@0+ (1,0) [0|1] "" XXX - SG_ CHECKSUM : 59|4@0+ (1,0) [0|15] "" XXX - SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" XXX - BO_ 545 XXX_16: 6 SCM SG_ ECON_ON : 23|1@0+ (1,0) [0|1] "" XXX SG_ DRIVE_MODE : 37|2@0+ (1,0) [0|3] "" XXX @@ -319,40 +312,6 @@ BO_ 662 SCM_BUTTONS: 4 SCM SG_ COUNTER : 29|2@0+ (1,0) [0|3] "" EON SG_ CHECKSUM : 27|4@0+ (1,0) [0|15] "" EON -BO_ 777 CAR_SPEED: 8 PCM - SG_ ROUGH_CAR_SPEED : 23|8@0+ (1,0) [0|255] "mph" XXX - SG_ CAR_SPEED : 7|16@0+ (0.01,0) [0|65535] "kph" XXX - SG_ ROUGH_CAR_SPEED_3 : 39|16@0+ (0.01,0) [0|65535] "kph" XXX - SG_ ROUGH_CAR_SPEED_2 : 31|8@0+ (1,0) [0|255] "mph" XXX - SG_ LOCK_STATUS : 55|2@0+ (1,0) [0|255] "" XXX - SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" XXX - SG_ CHECKSUM : 59|4@0+ (1,0) [0|15] "" XXX - SG_ IMPERIAL_UNIT : 63|1@0+ (1,0) [0|1] "" XXX - -BO_ 780 ACC_HUD: 8 ADAS - SG_ CRUISE_SPEED : 31|8@0+ (1,0) [0|255] "kph" BDY - SG_ DTC_MODE : 39|1@0+ (1,0) [0|1] "" BDY - SG_ BOH : 38|1@0+ (1,0) [0|1] "" BDY - SG_ FCM_PROBLEM : 34|1@0+ (1,0) [0|1] "" BDY - SG_ RADAR_OBSTRUCTED : 33|1@0+ (1,0) [0|1] "" BDY - SG_ ENABLE_MINI_CAR : 32|1@0+ (1,0) [0|1] "" BDY - SG_ BOH_3 : 43|1@0+ (1,0) [0|3] "" BDY - SG_ BOH_4 : 42|1@0+ (1,0) [0|3] "" BDY - SG_ BOH_5 : 41|1@0+ (1,0) [0|3] "" BDY - SG_ CRUISE_CONTROL_LABEL : 40|1@0+ (1,0) [0|3] "" BDY - SG_ ZEROS_BOH : 7|24@0+ (0.002759506,0) [0|100] "m/s" BDY - SG_ FCM_OFF : 35|1@0+ (1,0) [0|1] "" BDY - SG_ SET_TO_1 : 36|1@0+ (1,0) [0|1] "" XXX - SG_ HUD_DISTANCE : 47|2@0+ (1,0) [0|3] "" BDY - SG_ HUD_LEAD : 45|2@0+ (1,0) [0|3] "" BDY - SG_ ACC_PROBLEM : 37|1@0+ (1,0) [0|1] "" BDY - SG_ ACC_ON : 52|1@0+ (1,0) [0|1] "" XXX - SG_ BOH_6 : 51|4@0+ (1,0) [0|15] "" XXX - SG_ SET_TO_X1 : 55|1@0+ (1,0) [0|1] "" XXX - SG_ IMPERIAL_UNIT : 54|1@0+ (1,0) [0|1] "" XXX - SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" XXX - SG_ CHECKSUM : 59|4@0+ (1,0) [0|15] "" XXX - BO_ 806 SCM_FEEDBACK: 8 SCM SG_ DRIVERS_DOOR_OPEN : 17|1@0+ (1,0) [0|1] "" XXX SG_ MAIN_ON : 28|1@0+ (1,0) [0|1] "" EON @@ -370,6 +329,23 @@ BO_ 862 CAMERA_MESSAGES: 8 CAM SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" XXX SG_ CHECKSUM : 59|4@0+ (1,0) [0|15] "" XXX +BO_ 927 RADAR_HUD: 8 RADAR + SG_ ZEROS_BOH : 7|10@0+ (1,0) [0|127] "" BDY + SG_ CMBS_OFF : 12|1@0+ (1,0) [0|1] "" BDY + SG_ RESUME_INSTRUCTION : 21|1@0+ (1,0) [0|1] "" XXX + SG_ SET_TO_1 : 13|1@0+ (1,0) [0|1] "" BDY + SG_ ZEROS_BOH2 : 11|4@0+ (1,0) [0|1] "" XXX + SG_ APPLY_BRAKES_FOR_CANC : 23|1@0+ (1,0) [0|1] "" XXX + SG_ ACC_ALERTS : 20|5@0+ (1,0) [0|1] "" BDY + SG_ SET_TO_0 : 22|1@0+ (1,0) [0|1] "" XXX + SG_ HUD_LEAD : 40|1@0+ (1,0) [0|1] "" XXX + SG_ SET_TO_64 : 31|8@0+ (1,0) [0|255] "" XXX + SG_ LEAD_DISTANCE : 39|8@0+ (1,0) [0|255] "" XXX + SG_ ZEROS_BOH3 : 47|7@0+ (1,0) [0|127] "" XXX + SG_ ZEROS_BOH4 : 55|8@0+ (1,0) [0|255] "" XXX + SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" XXX + SG_ CHECKSUM : 59|4@0+ (1,0) [0|15] "" XXX + BO_ 13274 LKAS_HUD_A: 5 ADAS SG_ CAM_TEMP_HIGH : 7|1@0+ (1,0) [0|255] "" BDY SG_ SET_ME_X41 : 6|7@0+ (1,0) [0|127] "" BDY @@ -413,10 +389,6 @@ BO_ 13275 LKAS_HUD_B: 8 ADAS SG_ CHECKSUM : 59|4@0+ (1,0) [0|15] "" BDY CM_ SG_ 450 EPB_STATE "3: On, 2: Disengaging, 1: Engaging, 0: Off"; -CM_ SG_ 479 CONTROL_ON "Set to 5 when car is being controlled"; -CM_ SG_ 479 AEB_STATUS "set for the duration of AEB event"; -CM_ SG_ 479 AEB_BRAKING "set when braking is commanded during AEB event"; -CM_ SG_ 479 AEB_PREPARE "set 1s before AEB"; CM_ SG_ 576 LINE_DISTANCE_VISIBLE "Length of line visible, undecoded"; CM_ SG_ 577 LINE_FAR_EDGE_POSITION "Appears to be a measure of line thickness, indicates location of the portion of the line furthest from the car, undecoded"; CM_ SG_ 577 LINE_PARAMETER "Unclear if this is low quality line curvature rate or if this is something else, but it is correlated with line curvature, undecoded"; @@ -425,8 +397,65 @@ CM_ SG_ 577 LINE_SOLID "1 = line is solid"; VAL_ 399 STEER_STATUS 6 "tmp_fault" 5 "fault_1" 4 "no_torque_alert_2" 3 "low_speed_lockout" 2 "no_torque_alert_1" 0 "normal"; -CM_ "honda_insight_ex_2019_can.dbc starts here"; +CM_ "Imported file _bosch_adas_2018.dbc starts here"; +BO_ 479 ACC_CONTROL: 8 EON + SG_ SET_TO_0 : 20|5@0+ (1,0) [0|1] "" XXX + SG_ CONTROL_ON : 23|3@0+ (1,0) [0|5] "" XXX + SG_ GAS_COMMAND : 7|16@0- (1,0) [0|0] "" XXX + SG_ ACCEL_COMMAND : 31|11@0- (0.01,0) [0|0] "m/s2" XXX + SG_ BRAKE_LIGHTS : 62|1@0+ (1,0) [0|1] "" XXX + SG_ BRAKE_REQUEST : 34|1@0+ (1,0) [0|1] "" XXX + SG_ STANDSTILL : 35|1@0+ (1,0) [0|1] "" XXX + SG_ STANDSTILL_RELEASE : 36|1@0+ (1,0) [0|1] "" XXX + SG_ AEB_STATUS : 33|1@0+ (1,0) [0|1] "" XXX + SG_ AEB_BRAKING : 47|1@0+ (1,0) [0|1] "" XXX + SG_ AEB_PREPARE : 43|1@0+ (1,0) [0|1] "" XXX + SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" XXX + SG_ CHECKSUM : 59|4@0+ (1,0) [0|15] "" XXX + +BO_ 495 ACC_CONTROL_ON: 8 XXX + SG_ SET_TO_75 : 31|8@0+ (1,0) [0|255] "" XXX + SG_ SET_TO_30 : 39|8@0+ (1,0) [0|255] "" XXX + SG_ ZEROS_BOH : 23|8@0+ (1,0) [0|255] "" XXX + SG_ ZEROS_BOH2 : 47|16@0+ (1,0) [0|255] "" XXX + SG_ SET_TO_FF : 15|8@0+ (1,0) [0|255] "" XXX + SG_ SET_TO_3 : 6|7@0+ (1,0) [0|4095] "" XXX + SG_ CONTROL_ON : 7|1@0+ (1,0) [0|1] "" XXX + SG_ CHECKSUM : 59|4@0+ (1,0) [0|15] "" XXX + SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" XXX + +BO_ 829 LKAS_HUD: 5 ADAS + SG_ CAM_TEMP_HIGH : 7|1@0+ (1,0) [0|255] "" BDY + SG_ SET_ME_X41 : 6|7@0+ (1,0) [0|127] "" BDY + SG_ BOH : 6|7@0+ (1,0) [0|127] "" BDY + SG_ DASHED_LANES : 14|1@0+ (1,0) [0|1] "" BDY + SG_ DTC : 13|1@0+ (1,0) [0|1] "" BDY + SG_ LKAS_PROBLEM : 12|1@0+ (1,0) [0|1] "" BDY + SG_ LKAS_OFF : 11|1@0+ (1,0) [0|1] "" BDY + SG_ SOLID_LANES : 10|1@0+ (1,0) [0|1] "" BDY + SG_ LDW_RIGHT : 9|1@0+ (1,0) [0|1] "" BDY + SG_ STEERING_REQUIRED : 8|1@0+ (1,0) [0|1] "" BDY + SG_ BOH : 23|2@0+ (1,0) [0|4] "" BDY + SG_ LDW_PROBLEM : 21|1@0+ (1,0) [0|1] "" BDY + SG_ BEEP : 17|2@0+ (1,0) [0|1] "" BDY + SG_ LDW_ON : 28|1@0+ (1,0) [0|1] "" BDY + SG_ LDW_OFF : 27|1@0+ (1,0) [0|1] "" BDY + SG_ CLEAN_WINDSHIELD : 26|1@0+ (1,0) [0|1] "" BDY + SG_ SET_ME_X48 : 31|8@0+ (1,0) [0|255] "" BDY + SG_ COUNTER : 37|2@0+ (1,0) [0|3] "" BDY + SG_ CHECKSUM : 35|4@0+ (1,0) [0|15] "" BDY + +CM_ SG_ 479 CONTROL_ON "Set to 5 when car is being controlled"; +CM_ SG_ 479 AEB_STATUS "set for the duration of AEB event"; +CM_ SG_ 479 AEB_BRAKING "set when braking is commanded during AEB event"; +CM_ SG_ 479 AEB_PREPARE "set 1s before AEB"; +CM_ SG_ 829 BEEP "beeps are pleasant, chimes are for warnngs etc..."; + +VAL_ 829 BEEP 3 "single_beep" 2 "triple_beep" 1 "repeated_beep" 0 "no_beep"; + + +CM_ "Imported file _steering_sensors_a.dbc starts here"; BO_ 330 STEERING_SENSORS: 8 EPS SG_ STEER_ANGLE : 7|16@0- (-0.1,0) [-500|500] "deg" EON SG_ STEER_ANGLE_RATE : 23|16@0- (-1,0) [-3000|3000] "deg/s" EON @@ -437,6 +466,8 @@ BO_ 330 STEERING_SENSORS: 8 EPS SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" EON SG_ CHECKSUM : 59|4@0+ (1,0) [0|15] "" EON +CM_ "honda_insight_ex_2019_can.dbc starts here"; + BO_ 419 GEARBOX: 8 PCM SG_ GEAR : 7|8@0+ (1,0) [0|255] "" EON SG_ GEAR_SHIFTER : 29|6@0+ (1,0) [0|63] "" EON @@ -449,22 +480,5 @@ BO_ 432 STANDSTILL: 7 VSA SG_ COUNTER : 53|2@0+ (1,0) [0|3] "" EON SG_ CHECKSUM : 51|4@0+ (1,0) [0|15] "" EON -BO_ 927 RADAR_HUD: 8 RADAR - SG_ ZEROS_BOH : 7|10@0+ (1,0) [0|127] "" BDY - SG_ CMBS_OFF : 12|1@0+ (1,0) [0|1] "" BDY - SG_ RESUME_INSTRUCTION : 21|1@0+ (1,0) [0|1] "" XXX - SG_ SET_TO_1 : 13|1@0+ (1,0) [0|1] "" BDY - SG_ ZEROS_BOH2 : 11|4@0+ (1,0) [0|1] "" XXX - SG_ APPLY_BRAKES_FOR_CANC : 23|1@0+ (1,0) [0|1] "" XXX - SG_ ACC_ALERTS : 20|5@0+ (1,0) [0|1] "" BDY - SG_ SET_TO_0 : 22|1@0+ (1,0) [0|1] "" XXX - SG_ HUD_LEAD : 40|1@0+ (1,0) [0|1] "" XXX - SG_ SET_TO_64 : 31|8@0+ (1,0) [0|255] "" XXX - SG_ LEAD_DISTANCE : 39|8@0+ (1,0) [0|255] "" XXX - SG_ ZEROS_BOH3 : 47|7@0+ (1,0) [0|127] "" XXX - SG_ ZEROS_BOH4 : 55|8@0+ (1,0) [0|255] "" XXX - SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" XXX - SG_ CHECKSUM : 59|4@0+ (1,0) [0|15] "" XXX - VAL_ 419 GEAR 10 "R" 1 "D" 0 "P"; VAL_ 419 GEAR_SHIFTER 32 "D" 16 "N" 8 "R" 4 "P"; diff --git a/opendbc/honda_odyssey_exl_2018_generated.dbc b/opendbc/honda_odyssey_exl_2018_generated.dbc index 78343bcd9..1afefeee1 100644 --- a/opendbc/honda_odyssey_exl_2018_generated.dbc +++ b/opendbc/honda_odyssey_exl_2018_generated.dbc @@ -94,6 +94,43 @@ BO_ 773 SEATBELT_STATUS: 7 BDY SG_ COUNTER : 53|2@0+ (1,0) [0|3] "" EON SG_ CHECKSUM : 51|4@0+ (1,0) [0|3] "" EON +BO_ 777 CAR_SPEED: 8 PCM + SG_ ROUGH_CAR_SPEED : 23|8@0+ (1,0) [0|255] "mph" XXX + SG_ CAR_SPEED : 7|16@0+ (0.01,0) [0|65535] "kph" XXX + SG_ ROUGH_CAR_SPEED_3 : 39|16@0+ (0.01,0) [0|65535] "kph" XXX + SG_ ROUGH_CAR_SPEED_2 : 31|8@0+ (1,0) [0|255] "mph" XXX + SG_ LOCK_STATUS : 55|2@0+ (1,0) [0|255] "" XXX + SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" XXX + SG_ CHECKSUM : 59|4@0+ (1,0) [0|15] "" XXX + SG_ IMPERIAL_UNIT : 63|1@0+ (1,0) [0|1] "" XXX + +BO_ 780 ACC_HUD: 8 ADAS + SG_ PCM_SPEED : 7|16@0+ (0.01,0) [0|250] "kph" BDY + SG_ PCM_GAS : 23|8@0+ (1,0) [0|127] "" BDY + SG_ CRUISE_SPEED : 31|8@0+ (1,0) [0|255] "kph" BDY + SG_ DTC_MODE : 39|1@0+ (1,0) [0|1] "" BDY + SG_ BOH : 38|1@0+ (1,0) [0|1] "" BDY + SG_ ACC_PROBLEM : 37|1@0+ (1,0) [0|1] "" BDY + SG_ FCM_OFF : 35|1@0+ (1,0) [0|1] "" BDY + SG_ FCM_OFF_2 : 36|1@0+ (1,0) [0|1] "" BDY + SG_ FCM_PROBLEM : 34|1@0+ (1,0) [0|1] "" BDY + SG_ RADAR_OBSTRUCTED : 33|1@0+ (1,0) [0|1] "" BDY + SG_ ENABLE_MINI_CAR : 32|1@0+ (1,0) [0|1] "" BDY + SG_ HUD_DISTANCE : 47|2@0+ (1,0) [0|3] "" BDY + SG_ HUD_LEAD : 45|2@0+ (1,0) [0|3] "" BDY + SG_ BOH_3 : 43|1@0+ (1,0) [0|3] "" BDY + SG_ BOH_4 : 42|1@0+ (1,0) [0|3] "" BDY + SG_ BOH_5 : 41|1@0+ (1,0) [0|3] "" BDY + SG_ CRUISE_CONTROL_LABEL : 40|1@0+ (1,0) [0|3] "" BDY + SG_ SET_ME_X01_2 : 55|1@0+ (1,0) [0|1] "" BDY + SG_ IMPERIAL_UNIT : 54|1@0+ (1,0) [0|1] "" BDY + SG_ ACC_ON : 52|1@0+ (1,0) [0|1] "" BDY + SG_ CHIME : 51|3@0+ (1,0) [0|1] "" BDY + SG_ SET_ME_X01 : 48|1@0+ (1,0) [0|1] "" BDY + SG_ ICONS : 63|2@0+ (1,0) [0|1] "" BDY + SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" BDY + SG_ CHECKSUM : 59|4@0+ (1,0) [0|3] "" BDY + BO_ 804 CRUISE: 8 PCM SG_ HUD_SPEED_KPH : 7|8@0+ (1,0) [0|255] "kph" EON SG_ HUD_SPEED_MPH : 15|8@0+ (1,0) [0|255] "mph" EON @@ -104,27 +141,6 @@ BO_ 804 CRUISE: 8 PCM SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" EON SG_ CHECKSUM : 59|4@0+ (1,0) [0|15] "" EON -BO_ 829 LKAS_HUD: 5 ADAS - SG_ CAM_TEMP_HIGH : 7|1@0+ (1,0) [0|255] "" BDY - SG_ SET_ME_X41 : 6|7@0+ (1,0) [0|127] "" BDY - SG_ BOH : 6|7@0+ (1,0) [0|127] "" BDY - SG_ DASHED_LANES : 14|1@0+ (1,0) [0|1] "" BDY - SG_ DTC : 13|1@0+ (1,0) [0|1] "" BDY - SG_ LKAS_PROBLEM : 12|1@0+ (1,0) [0|1] "" BDY - SG_ LKAS_OFF : 11|1@0+ (1,0) [0|1] "" BDY - SG_ SOLID_LANES : 10|1@0+ (1,0) [0|1] "" BDY - SG_ LDW_RIGHT : 9|1@0+ (1,0) [0|1] "" BDY - SG_ STEERING_REQUIRED : 8|1@0+ (1,0) [0|1] "" BDY - SG_ BOH : 23|2@0+ (1,0) [0|4] "" BDY - SG_ LDW_PROBLEM : 21|1@0+ (1,0) [0|1] "" BDY - SG_ BEEP : 17|2@0+ (1,0) [0|1] "" BDY - SG_ LDW_ON : 28|1@0+ (1,0) [0|1] "" BDY - SG_ LDW_OFF : 27|1@0+ (1,0) [0|1] "" BDY - SG_ CLEAN_WINDSHIELD : 26|1@0+ (1,0) [0|1] "" BDY - SG_ SET_ME_X48 : 31|8@0+ (1,0) [0|255] "" BDY - SG_ COUNTER : 37|2@0+ (1,0) [0|3] "" BDY - SG_ CHECKSUM : 35|4@0+ (1,0) [0|15] "" BDY - BO_ 884 STALK_STATUS: 8 XXX SG_ DASHBOARD_ALERT : 39|8@0+ (1,0) [0|255] "" EON SG_ AUTO_HEADLIGHTS : 46|1@0+ (1,0) [0|1] "" EON @@ -158,15 +174,18 @@ CM_ SG_ 420 BRAKE_HOLD_RELATED "On when Brake Hold engaged"; CM_ SG_ 490 LONG_ACCEL "wheel speed derivative, noisy and zero snapping"; CM_ SG_ 773 PASS_AIRBAG_ON "Might just be indicator light"; CM_ SG_ 773 PASS_AIRBAG_OFF "Might just be indicator light"; +CM_ SG_ 780 CRUISE_SPEED "255 = no speed"; +CM_ SG_ 780 PCM_SPEED "Used by Nidec"; +CM_ SG_ 780 PCM_GAS "Used by Nidec"; CM_ SG_ 804 CRUISE_SPEED_PCM "255 = no speed"; -CM_ SG_ 829 BEEP "beeps are pleasant, chimes are for warnngs etc..."; -VAL_ 829 BEEP 3 "single_beep" 2 "triple_beep" 1 "repeated_beep" 0 "no_beep"; +VAL_ 780 CRUISE_SPEED 255 "no_speed" 252 "stopped"; +VAL_ 780 HUD_LEAD 3 "acc_off" 2 "solid_car" 1 "dashed_car" 0 "no_car"; VAL_ 884 DASHBOARD_ALERT 0 "none" 51 "acc_problem" 55 "cmbs_problem" 75 "key_not_detected" 79 "fasten_seatbelt" 111 "lkas_problem" 131 "brake_system_problem" 132 "brake_hold_problem" 139 "tbd" 161 "door_open"; VAL_ 891 WIPERS 4 "High" 2 "Low" 0 "Off"; -CM_ "Imported file _nidec_2017.dbc starts here"; +CM_ "Imported file _nidec_common.dbc starts here"; BO_ 145 KINEMATICS: 8 XXX SG_ LAT_ACCEL : 7|10@0+ (0.02,-512) [-20|20] "m/s2" EON SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" EON @@ -194,11 +213,11 @@ BO_ 487 BRAKE_PRESSURE: 4 VSA BO_ 506 BRAKE_COMMAND: 8 ADAS SG_ COMPUTER_BRAKE : 7|10@0+ (1,0) [0|1] "" EBCM - SG_ SET_ME_X00 : 13|5@0+ (1,0) [0|1] "" EBCM SG_ BRAKE_PUMP_REQUEST : 8|1@0+ (1,0) [0|1] "" EBCM - SG_ SET_ME_X00_2 : 23|3@0+ (1,0) [0|1] "" EBCM + SG_ BRAKE_PUMP_REQUEST_ALT : 11|1@0+ (1,0) [0|1] "" EBCM + SG_ SET_ME_X00 : 23|3@0+ (1,0) [0|1] "" EBCM SG_ CRUISE_OVERRIDE : 20|1@0+ (1,0) [0|1] "" EBCM - SG_ SET_ME_X00_3 : 19|1@0+ (1,0) [0|1] "" EBCM + SG_ SET_ME_X00_2 : 19|1@0+ (1,0) [0|1] "" EBCM SG_ CRUISE_FAULT_CMD : 18|1@0+ (1,0) [0|1] "" EBCM SG_ CRUISE_CANCEL_CMD : 17|1@0+ (1,0) [0|1] "" EBCM SG_ COMPUTER_BRAKE_REQUEST : 16|1@0+ (1,0) [0|1] "" EBCM @@ -208,10 +227,10 @@ BO_ 506 BRAKE_COMMAND: 8 ADAS SG_ BRAKE_LIGHTS : 39|1@0+ (1,0) [0|1] "" EBCM SG_ CRUISE_STATES : 38|7@0+ (1,0) [0|1] "" EBCM SG_ CHIME : 47|3@0+ (1,0) [0|7] "" EBCM - SG_ SET_ME_X00_4 : 44|1@0+ (1,0) [0|1] "" EBCM + SG_ SET_ME_X00_3 : 44|1@0+ (1,0) [0|1] "" EBCM SG_ FCW : 43|2@0+ (1,0) [0|3] "" EBCM SG_ AEB_STATUS : 41|2@0+ (1,0) [0|3] "" XXX - SG_ SET_ME_X00_5 : 55|8@0+ (1,0) [0|0] "" EBCM + SG_ COMPUTER_BRAKE_ALT : 55|10@0+ (1,0) [0|0] "" EBCM SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" EBCM SG_ CHECKSUM : 59|4@0+ (1,0) [0|15] "" EBCM @@ -225,38 +244,26 @@ BO_ 597 ROUGH_WHEEL_SPEED: 8 VSA SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" EON SG_ CHECKSUM : 59|4@0+ (1,0) [0|15] "" EON -BO_ 777 LOCK_STATUS: 8 XXX - SG_ DOORS_UNLOCKED : 54|1@0+ (1,0) [0|1] "" EON - SG_ DOORS_LOCKED : 55|1@0+ (1,0) [0|1] "" EON - SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" EON - SG_ CHECKSUM : 59|4@0+ (1,0) [0|3] "" EON - -BO_ 780 ACC_HUD: 8 ADAS - SG_ PCM_SPEED : 7|16@0+ (0.01,0) [0|250] "kph" BDY - SG_ PCM_GAS : 23|8@0+ (1,0) [0|127] "" BDY - SG_ CRUISE_SPEED : 31|8@0+ (1,0) [0|255] "" BDY - SG_ DTC_MODE : 39|1@0+ (1,0) [0|1] "" BDY - SG_ BOH : 38|1@0+ (1,0) [0|1] "" BDY - SG_ ACC_PROBLEM : 37|1@0+ (1,0) [0|1] "" BDY - SG_ FCM_OFF : 36|1@0+ (1,0) [0|1] "" BDY - SG_ FCM_OFF_2 : 35|1@0+ (1,0) [0|1] "" BDY - SG_ FCM_PROBLEM : 34|1@0+ (1,0) [0|1] "" BDY - SG_ RADAR_OBSTRUCTED : 33|1@0+ (1,0) [0|1] "" BDY - SG_ ENABLE_MINI_CAR : 32|1@0+ (1,0) [0|1] "" BDY - SG_ HUD_DISTANCE : 47|2@0+ (1,0) [0|3] "" BDY - SG_ HUD_LEAD : 45|2@0+ (1,0) [0|3] "" BDY - SG_ BOH_3 : 43|1@0+ (1,0) [0|3] "" BDY - SG_ BOH_4 : 42|1@0+ (1,0) [0|3] "" BDY - SG_ BOH_5 : 41|1@0+ (1,0) [0|3] "" BDY - SG_ CRUISE_CONTROL_LABEL : 40|1@0+ (1,0) [0|3] "" BDY - SG_ SET_ME_X01_2 : 55|1@0+ (1,0) [0|1] "" BDY - SG_ IMPERIAL_UNIT : 54|1@0+ (1,0) [0|1] "" BDY - SG_ HUD_DISTANCE_3 : 52|1@0+ (1,0) [0|1] "" BDY - SG_ CHIME : 51|3@0+ (1,0) [0|1] "" BDY - SG_ SET_ME_X01 : 48|1@0+ (1,0) [0|1] "" BDY - SG_ ICONS : 63|2@0+ (1,0) [0|1] "" BDY - SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" BDY - SG_ CHECKSUM : 59|4@0+ (1,0) [0|3] "" BDY +BO_ 829 LKAS_HUD: 5 ADAS + SG_ CAM_TEMP_HIGH : 7|1@0+ (1,0) [0|255] "" BDY + SG_ SET_ME_X41 : 6|7@0+ (1,0) [0|127] "" BDY + SG_ BOH : 6|7@0+ (1,0) [0|127] "" BDY + SG_ DASHED_LANES : 14|1@0+ (1,0) [0|1] "" BDY + SG_ DTC : 13|1@0+ (1,0) [0|1] "" BDY + SG_ LKAS_PROBLEM : 12|1@0+ (1,0) [0|1] "" BDY + SG_ LKAS_OFF : 11|1@0+ (1,0) [0|1] "" BDY + SG_ SOLID_LANES : 10|1@0+ (1,0) [0|1] "" BDY + SG_ LDW_RIGHT : 9|1@0+ (1,0) [0|1] "" BDY + SG_ STEERING_REQUIRED : 8|1@0+ (1,0) [0|1] "" BDY + SG_ BOH : 23|2@0+ (1,0) [0|4] "" BDY + SG_ LDW_PROBLEM : 21|1@0+ (1,0) [0|1] "" BDY + SG_ BEEP : 17|2@0+ (1,0) [0|1] "" BDY + SG_ LDW_ON : 28|1@0+ (1,0) [0|1] "" BDY + SG_ LDW_OFF : 27|1@0+ (1,0) [0|1] "" BDY + SG_ CLEAN_WINDSHIELD : 26|1@0+ (1,0) [0|1] "" BDY + SG_ SET_ME_X48 : 31|8@0+ (1,0) [0|255] "" BDY + SG_ COUNTER : 37|2@0+ (1,0) [0|3] "" BDY + SG_ CHECKSUM : 35|4@0+ (1,0) [0|15] "" BDY BO_ 892 CRUISE_PARAMS: 8 PCM SG_ CRUISE_SPEED_OFFSET : 31|8@0- (0.1,0) [-128|127] "kph" EON @@ -264,13 +271,22 @@ BO_ 892 CRUISE_PARAMS: 8 PCM SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" EON CM_ SG_ 506 AEB_REQ_1 "set for duration of suspected AEB event"; -CM_ SG_ 780 CRUISE_SPEED "255 = no speed"; +CM_ SG_ 506 COMPUTER_BRAKE_ALT "Used by dual-can Nidec"; +CM_ SG_ 506 BRAKE_PUMP_REQUEST_ALT "Used by dual-can Nidec"; +CM_ SG_ 829 BEEP "beeps are pleasant, chimes are for warnngs etc..."; VAL_ 506 FCW 3 "fcw" 2 "fcw" 1 "fcw" 0 "no_fcw"; VAL_ 506 CHIME 4 "double_chime" 3 "single_chime" 2 "continuous_chime" 1 "repeating_chime" 0 "no_chime"; VAL_ 506 AEB_STATUS 3 "aeb_prepare" 2 "aeb_ready" 1 "aeb_braking" 0 "no_aeb"; -VAL_ 780 CRUISE_SPEED 255 "no_speed" 252 "stopped"; -VAL_ 780 HUD_LEAD 3 "acc_off" 2 "solid_car" 1 "dashed_car" 0 "no_car"; +VAL_ 829 BEEP 3 "single_beep" 2 "triple_beep" 1 "repeated_beep" 0 "no_beep"; + + +CM_ "Imported file _steering_sensors_b.dbc starts here"; +BO_ 342 STEERING_SENSORS: 6 EPS + SG_ STEER_ANGLE : 7|16@0- (-0.1,0) [-500|500] "deg" EON + SG_ STEER_ANGLE_RATE : 23|16@0- (-1,0) [-3000|3000] "deg/s" EON + SG_ COUNTER : 45|2@0+ (1,0) [0|3] "" EON + SG_ CHECKSUM : 43|4@0+ (1,0) [0|15] "" EON CM_ "honda_odyssey_exl_2018.dbc starts here"; @@ -281,12 +297,6 @@ BO_ 228 STEERING_CONTROL: 5 ADAS SG_ COUNTER : 37|2@0+ (1,0) [0|3] "" EPS SG_ CHECKSUM : 35|4@0+ (1,0) [0|3] "" EPS -BO_ 342 STEERING_SENSORS: 6 EPS - SG_ STEER_ANGLE : 7|16@0- (-0.1,0) [-500|500] "deg" EON - SG_ STEER_ANGLE_RATE : 23|16@0- (-1,0) [-3000|3000] "deg/s" EON - SG_ COUNTER : 45|2@0+ (1,0) [0|3] "" EON - SG_ CHECKSUM : 43|4@0+ (1,0) [0|3] "" EON - BO_ 399 STEER_STATUS: 7 EPS SG_ STEER_TORQUE_SENSOR : 7|16@0- (-1,0) [-31000|31000] "tbd" EON SG_ STEER_ANGLE_RATE : 23|16@0- (-0.1,0) [-31000|31000] "deg/s" EON diff --git a/opendbc/honda_odyssey_extreme_edition_2018_china_can_generated.dbc b/opendbc/honda_odyssey_extreme_edition_2018_china_can_generated.dbc index 6de07a93d..5ee66d2ca 100644 --- a/opendbc/honda_odyssey_extreme_edition_2018_china_can_generated.dbc +++ b/opendbc/honda_odyssey_extreme_edition_2018_china_can_generated.dbc @@ -94,6 +94,43 @@ BO_ 773 SEATBELT_STATUS: 7 BDY SG_ COUNTER : 53|2@0+ (1,0) [0|3] "" EON SG_ CHECKSUM : 51|4@0+ (1,0) [0|3] "" EON +BO_ 777 CAR_SPEED: 8 PCM + SG_ ROUGH_CAR_SPEED : 23|8@0+ (1,0) [0|255] "mph" XXX + SG_ CAR_SPEED : 7|16@0+ (0.01,0) [0|65535] "kph" XXX + SG_ ROUGH_CAR_SPEED_3 : 39|16@0+ (0.01,0) [0|65535] "kph" XXX + SG_ ROUGH_CAR_SPEED_2 : 31|8@0+ (1,0) [0|255] "mph" XXX + SG_ LOCK_STATUS : 55|2@0+ (1,0) [0|255] "" XXX + SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" XXX + SG_ CHECKSUM : 59|4@0+ (1,0) [0|15] "" XXX + SG_ IMPERIAL_UNIT : 63|1@0+ (1,0) [0|1] "" XXX + +BO_ 780 ACC_HUD: 8 ADAS + SG_ PCM_SPEED : 7|16@0+ (0.01,0) [0|250] "kph" BDY + SG_ PCM_GAS : 23|8@0+ (1,0) [0|127] "" BDY + SG_ CRUISE_SPEED : 31|8@0+ (1,0) [0|255] "kph" BDY + SG_ DTC_MODE : 39|1@0+ (1,0) [0|1] "" BDY + SG_ BOH : 38|1@0+ (1,0) [0|1] "" BDY + SG_ ACC_PROBLEM : 37|1@0+ (1,0) [0|1] "" BDY + SG_ FCM_OFF : 35|1@0+ (1,0) [0|1] "" BDY + SG_ FCM_OFF_2 : 36|1@0+ (1,0) [0|1] "" BDY + SG_ FCM_PROBLEM : 34|1@0+ (1,0) [0|1] "" BDY + SG_ RADAR_OBSTRUCTED : 33|1@0+ (1,0) [0|1] "" BDY + SG_ ENABLE_MINI_CAR : 32|1@0+ (1,0) [0|1] "" BDY + SG_ HUD_DISTANCE : 47|2@0+ (1,0) [0|3] "" BDY + SG_ HUD_LEAD : 45|2@0+ (1,0) [0|3] "" BDY + SG_ BOH_3 : 43|1@0+ (1,0) [0|3] "" BDY + SG_ BOH_4 : 42|1@0+ (1,0) [0|3] "" BDY + SG_ BOH_5 : 41|1@0+ (1,0) [0|3] "" BDY + SG_ CRUISE_CONTROL_LABEL : 40|1@0+ (1,0) [0|3] "" BDY + SG_ SET_ME_X01_2 : 55|1@0+ (1,0) [0|1] "" BDY + SG_ IMPERIAL_UNIT : 54|1@0+ (1,0) [0|1] "" BDY + SG_ ACC_ON : 52|1@0+ (1,0) [0|1] "" BDY + SG_ CHIME : 51|3@0+ (1,0) [0|1] "" BDY + SG_ SET_ME_X01 : 48|1@0+ (1,0) [0|1] "" BDY + SG_ ICONS : 63|2@0+ (1,0) [0|1] "" BDY + SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" BDY + SG_ CHECKSUM : 59|4@0+ (1,0) [0|3] "" BDY + BO_ 804 CRUISE: 8 PCM SG_ HUD_SPEED_KPH : 7|8@0+ (1,0) [0|255] "kph" EON SG_ HUD_SPEED_MPH : 15|8@0+ (1,0) [0|255] "mph" EON @@ -104,27 +141,6 @@ BO_ 804 CRUISE: 8 PCM SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" EON SG_ CHECKSUM : 59|4@0+ (1,0) [0|15] "" EON -BO_ 829 LKAS_HUD: 5 ADAS - SG_ CAM_TEMP_HIGH : 7|1@0+ (1,0) [0|255] "" BDY - SG_ SET_ME_X41 : 6|7@0+ (1,0) [0|127] "" BDY - SG_ BOH : 6|7@0+ (1,0) [0|127] "" BDY - SG_ DASHED_LANES : 14|1@0+ (1,0) [0|1] "" BDY - SG_ DTC : 13|1@0+ (1,0) [0|1] "" BDY - SG_ LKAS_PROBLEM : 12|1@0+ (1,0) [0|1] "" BDY - SG_ LKAS_OFF : 11|1@0+ (1,0) [0|1] "" BDY - SG_ SOLID_LANES : 10|1@0+ (1,0) [0|1] "" BDY - SG_ LDW_RIGHT : 9|1@0+ (1,0) [0|1] "" BDY - SG_ STEERING_REQUIRED : 8|1@0+ (1,0) [0|1] "" BDY - SG_ BOH : 23|2@0+ (1,0) [0|4] "" BDY - SG_ LDW_PROBLEM : 21|1@0+ (1,0) [0|1] "" BDY - SG_ BEEP : 17|2@0+ (1,0) [0|1] "" BDY - SG_ LDW_ON : 28|1@0+ (1,0) [0|1] "" BDY - SG_ LDW_OFF : 27|1@0+ (1,0) [0|1] "" BDY - SG_ CLEAN_WINDSHIELD : 26|1@0+ (1,0) [0|1] "" BDY - SG_ SET_ME_X48 : 31|8@0+ (1,0) [0|255] "" BDY - SG_ COUNTER : 37|2@0+ (1,0) [0|3] "" BDY - SG_ CHECKSUM : 35|4@0+ (1,0) [0|15] "" BDY - BO_ 884 STALK_STATUS: 8 XXX SG_ DASHBOARD_ALERT : 39|8@0+ (1,0) [0|255] "" EON SG_ AUTO_HEADLIGHTS : 46|1@0+ (1,0) [0|1] "" EON @@ -158,15 +174,18 @@ CM_ SG_ 420 BRAKE_HOLD_RELATED "On when Brake Hold engaged"; CM_ SG_ 490 LONG_ACCEL "wheel speed derivative, noisy and zero snapping"; CM_ SG_ 773 PASS_AIRBAG_ON "Might just be indicator light"; CM_ SG_ 773 PASS_AIRBAG_OFF "Might just be indicator light"; +CM_ SG_ 780 CRUISE_SPEED "255 = no speed"; +CM_ SG_ 780 PCM_SPEED "Used by Nidec"; +CM_ SG_ 780 PCM_GAS "Used by Nidec"; CM_ SG_ 804 CRUISE_SPEED_PCM "255 = no speed"; -CM_ SG_ 829 BEEP "beeps are pleasant, chimes are for warnngs etc..."; -VAL_ 829 BEEP 3 "single_beep" 2 "triple_beep" 1 "repeated_beep" 0 "no_beep"; +VAL_ 780 CRUISE_SPEED 255 "no_speed" 252 "stopped"; +VAL_ 780 HUD_LEAD 3 "acc_off" 2 "solid_car" 1 "dashed_car" 0 "no_car"; VAL_ 884 DASHBOARD_ALERT 0 "none" 51 "acc_problem" 55 "cmbs_problem" 75 "key_not_detected" 79 "fasten_seatbelt" 111 "lkas_problem" 131 "brake_system_problem" 132 "brake_hold_problem" 139 "tbd" 161 "door_open"; VAL_ 891 WIPERS 4 "High" 2 "Low" 0 "Off"; -CM_ "Imported file _nidec_2017.dbc starts here"; +CM_ "Imported file _nidec_common.dbc starts here"; BO_ 145 KINEMATICS: 8 XXX SG_ LAT_ACCEL : 7|10@0+ (0.02,-512) [-20|20] "m/s2" EON SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" EON @@ -194,11 +213,11 @@ BO_ 487 BRAKE_PRESSURE: 4 VSA BO_ 506 BRAKE_COMMAND: 8 ADAS SG_ COMPUTER_BRAKE : 7|10@0+ (1,0) [0|1] "" EBCM - SG_ SET_ME_X00 : 13|5@0+ (1,0) [0|1] "" EBCM SG_ BRAKE_PUMP_REQUEST : 8|1@0+ (1,0) [0|1] "" EBCM - SG_ SET_ME_X00_2 : 23|3@0+ (1,0) [0|1] "" EBCM + SG_ BRAKE_PUMP_REQUEST_ALT : 11|1@0+ (1,0) [0|1] "" EBCM + SG_ SET_ME_X00 : 23|3@0+ (1,0) [0|1] "" EBCM SG_ CRUISE_OVERRIDE : 20|1@0+ (1,0) [0|1] "" EBCM - SG_ SET_ME_X00_3 : 19|1@0+ (1,0) [0|1] "" EBCM + SG_ SET_ME_X00_2 : 19|1@0+ (1,0) [0|1] "" EBCM SG_ CRUISE_FAULT_CMD : 18|1@0+ (1,0) [0|1] "" EBCM SG_ CRUISE_CANCEL_CMD : 17|1@0+ (1,0) [0|1] "" EBCM SG_ COMPUTER_BRAKE_REQUEST : 16|1@0+ (1,0) [0|1] "" EBCM @@ -208,10 +227,10 @@ BO_ 506 BRAKE_COMMAND: 8 ADAS SG_ BRAKE_LIGHTS : 39|1@0+ (1,0) [0|1] "" EBCM SG_ CRUISE_STATES : 38|7@0+ (1,0) [0|1] "" EBCM SG_ CHIME : 47|3@0+ (1,0) [0|7] "" EBCM - SG_ SET_ME_X00_4 : 44|1@0+ (1,0) [0|1] "" EBCM + SG_ SET_ME_X00_3 : 44|1@0+ (1,0) [0|1] "" EBCM SG_ FCW : 43|2@0+ (1,0) [0|3] "" EBCM SG_ AEB_STATUS : 41|2@0+ (1,0) [0|3] "" XXX - SG_ SET_ME_X00_5 : 55|8@0+ (1,0) [0|0] "" EBCM + SG_ COMPUTER_BRAKE_ALT : 55|10@0+ (1,0) [0|0] "" EBCM SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" EBCM SG_ CHECKSUM : 59|4@0+ (1,0) [0|15] "" EBCM @@ -225,38 +244,26 @@ BO_ 597 ROUGH_WHEEL_SPEED: 8 VSA SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" EON SG_ CHECKSUM : 59|4@0+ (1,0) [0|15] "" EON -BO_ 777 LOCK_STATUS: 8 XXX - SG_ DOORS_UNLOCKED : 54|1@0+ (1,0) [0|1] "" EON - SG_ DOORS_LOCKED : 55|1@0+ (1,0) [0|1] "" EON - SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" EON - SG_ CHECKSUM : 59|4@0+ (1,0) [0|3] "" EON - -BO_ 780 ACC_HUD: 8 ADAS - SG_ PCM_SPEED : 7|16@0+ (0.01,0) [0|250] "kph" BDY - SG_ PCM_GAS : 23|8@0+ (1,0) [0|127] "" BDY - SG_ CRUISE_SPEED : 31|8@0+ (1,0) [0|255] "" BDY - SG_ DTC_MODE : 39|1@0+ (1,0) [0|1] "" BDY - SG_ BOH : 38|1@0+ (1,0) [0|1] "" BDY - SG_ ACC_PROBLEM : 37|1@0+ (1,0) [0|1] "" BDY - SG_ FCM_OFF : 36|1@0+ (1,0) [0|1] "" BDY - SG_ FCM_OFF_2 : 35|1@0+ (1,0) [0|1] "" BDY - SG_ FCM_PROBLEM : 34|1@0+ (1,0) [0|1] "" BDY - SG_ RADAR_OBSTRUCTED : 33|1@0+ (1,0) [0|1] "" BDY - SG_ ENABLE_MINI_CAR : 32|1@0+ (1,0) [0|1] "" BDY - SG_ HUD_DISTANCE : 47|2@0+ (1,0) [0|3] "" BDY - SG_ HUD_LEAD : 45|2@0+ (1,0) [0|3] "" BDY - SG_ BOH_3 : 43|1@0+ (1,0) [0|3] "" BDY - SG_ BOH_4 : 42|1@0+ (1,0) [0|3] "" BDY - SG_ BOH_5 : 41|1@0+ (1,0) [0|3] "" BDY - SG_ CRUISE_CONTROL_LABEL : 40|1@0+ (1,0) [0|3] "" BDY - SG_ SET_ME_X01_2 : 55|1@0+ (1,0) [0|1] "" BDY - SG_ IMPERIAL_UNIT : 54|1@0+ (1,0) [0|1] "" BDY - SG_ HUD_DISTANCE_3 : 52|1@0+ (1,0) [0|1] "" BDY - SG_ CHIME : 51|3@0+ (1,0) [0|1] "" BDY - SG_ SET_ME_X01 : 48|1@0+ (1,0) [0|1] "" BDY - SG_ ICONS : 63|2@0+ (1,0) [0|1] "" BDY - SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" BDY - SG_ CHECKSUM : 59|4@0+ (1,0) [0|3] "" BDY +BO_ 829 LKAS_HUD: 5 ADAS + SG_ CAM_TEMP_HIGH : 7|1@0+ (1,0) [0|255] "" BDY + SG_ SET_ME_X41 : 6|7@0+ (1,0) [0|127] "" BDY + SG_ BOH : 6|7@0+ (1,0) [0|127] "" BDY + SG_ DASHED_LANES : 14|1@0+ (1,0) [0|1] "" BDY + SG_ DTC : 13|1@0+ (1,0) [0|1] "" BDY + SG_ LKAS_PROBLEM : 12|1@0+ (1,0) [0|1] "" BDY + SG_ LKAS_OFF : 11|1@0+ (1,0) [0|1] "" BDY + SG_ SOLID_LANES : 10|1@0+ (1,0) [0|1] "" BDY + SG_ LDW_RIGHT : 9|1@0+ (1,0) [0|1] "" BDY + SG_ STEERING_REQUIRED : 8|1@0+ (1,0) [0|1] "" BDY + SG_ BOH : 23|2@0+ (1,0) [0|4] "" BDY + SG_ LDW_PROBLEM : 21|1@0+ (1,0) [0|1] "" BDY + SG_ BEEP : 17|2@0+ (1,0) [0|1] "" BDY + SG_ LDW_ON : 28|1@0+ (1,0) [0|1] "" BDY + SG_ LDW_OFF : 27|1@0+ (1,0) [0|1] "" BDY + SG_ CLEAN_WINDSHIELD : 26|1@0+ (1,0) [0|1] "" BDY + SG_ SET_ME_X48 : 31|8@0+ (1,0) [0|255] "" BDY + SG_ COUNTER : 37|2@0+ (1,0) [0|3] "" BDY + SG_ CHECKSUM : 35|4@0+ (1,0) [0|15] "" BDY BO_ 892 CRUISE_PARAMS: 8 PCM SG_ CRUISE_SPEED_OFFSET : 31|8@0- (0.1,0) [-128|127] "kph" EON @@ -264,13 +271,14 @@ BO_ 892 CRUISE_PARAMS: 8 PCM SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" EON CM_ SG_ 506 AEB_REQ_1 "set for duration of suspected AEB event"; -CM_ SG_ 780 CRUISE_SPEED "255 = no speed"; +CM_ SG_ 506 COMPUTER_BRAKE_ALT "Used by dual-can Nidec"; +CM_ SG_ 506 BRAKE_PUMP_REQUEST_ALT "Used by dual-can Nidec"; +CM_ SG_ 829 BEEP "beeps are pleasant, chimes are for warnngs etc..."; VAL_ 506 FCW 3 "fcw" 2 "fcw" 1 "fcw" 0 "no_fcw"; VAL_ 506 CHIME 4 "double_chime" 3 "single_chime" 2 "continuous_chime" 1 "repeating_chime" 0 "no_chime"; VAL_ 506 AEB_STATUS 3 "aeb_prepare" 2 "aeb_ready" 1 "aeb_braking" 0 "no_aeb"; -VAL_ 780 CRUISE_SPEED 255 "no_speed" 252 "stopped"; -VAL_ 780 HUD_LEAD 3 "acc_off" 2 "solid_car" 1 "dashed_car" 0 "no_car"; +VAL_ 829 BEEP 3 "single_beep" 2 "triple_beep" 1 "repeated_beep" 0 "no_beep"; CM_ "honda_odyssey_extreme_edition_2018_china_can.dbc starts here"; diff --git a/opendbc/kia_ev6.dbc b/opendbc/kia_ev6.dbc new file mode 100644 index 000000000..1def4dc12 --- /dev/null +++ b/opendbc/kia_ev6.dbc @@ -0,0 +1,265 @@ +VERSION "" + + +NS_ : + NS_DESC_ + CM_ + BA_DEF_ + BA_ + VAL_ + CAT_DEF_ + CAT_ + FILTER + BA_DEF_DEF_ + EV_DATA_ + ENVVAR_DATA_ + SGTYPE_ + SGTYPE_VAL_ + BA_DEF_SGTYPE_ + BA_SGTYPE_ + SIG_TYPE_REF_ + VAL_TABLE_ + SIG_GROUP_ + SIG_VALTYPE_ + SIGTYPE_VALTYPE_ + BO_TX_BU_ + BA_DEF_REL_ + BA_REL_ + BA_DEF_DEF_REL_ + BU_SG_REL_ + BU_EV_REL_ + BU_BO_REL_ + SG_MUL_VAL_ + +BS_: + +BU_: XXX CAMERA + + +BO_ 53 ACCELERATOR: 32 XXX + SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX + SG_ COUNTER : 16|8@1+ (1,0) [0|255] "" XXX + SG_ GEAR : 192|3@1+ (1,0) [0|7] "" XXX + SG_ ACCELERATOR_PEDAL : 40|8@1+ (1,0) [0|255] "" XXX + +BO_ 80 LKAS: 16 XXX + SG_ STEER_REQ : 52|1@1+ (1,0) [0|1] "" XXX + SG_ TORQUE_REQUEST : 41|11@1+ (1,-1024) [0|4095] "" XXX + SG_ LKA_ICON : 38|2@1+ (1,0) [0|255] "" XXX + SG_ NEW_SIGNAL_1 : 27|2@1+ (1,0) [0|255] "" XXX + SG_ LFA_BUTTON : 56|1@1+ (1,0) [0|255] "" XXX + SG_ COUNTER : 16|8@1+ (1,0) [0|255] "" XXX + SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX + SG_ STEER_MODE : 65|3@1+ (1,0) [0|1] "" XXX + SG_ LKA_WARNING : 32|1@1+ (1,0) [0|1] "" XXX + SG_ LKA_ASSIST : 62|1@1+ (1,0) [0|1] "" XXX + SG_ LKA_MODE : 24|3@1+ (1,0) [0|7] "" XXX + SG_ NEW_SIGNAL_2 : 70|2@0+ (1,0) [0|3] "" XXX + SG_ SET_ME_1 : 80|1@0+ (1,0) [0|1] "" XXX + SG_ NEW_SIGNAL_3 : 111|8@0+ (1,0) [0|255] "" XXX + +BO_ 96 ESP_STATUS: 32 XXX + SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX + SG_ COUNTER : 16|8@1+ (1,0) [0|255] "" XXX + SG_ ESP_DISABLED : 42|3@1+ (1,0) [0|63] "" XXX + +BO_ 101 BRAKE: 32 XXX + SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX + SG_ COUNTER : 16|8@1+ (1,0) [0|255] "" XXX + SG_ BRAKE_PRESSED : 57|1@1+ (1,0) [0|3] "" XXX + +BO_ 160 WHEEL_SPEEDS: 24 XXX + SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX + SG_ COUNTER : 16|8@1+ (1,0) [0|255] "" XXX + SG_ WHEEL_SPEED_1 : 64|16@1+ (0.03125,0) [0|65535] "m/s" XXX + SG_ WHEEL_SPEED_2 : 80|16@1+ (0.03125,0) [0|65535] "m/s" XXX + SG_ WHEEL_SPEED_3 : 96|16@1+ (0.03125,0) [0|65535] "m/s" XXX + SG_ WHEEL_SPEED_4 : 112|16@1+ (0.03125,0) [0|65535] "m/s" XXX + +BO_ 234 MDPS: 24 XXX + SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX + SG_ COUNTER : 16|8@1+ (1,0) [0|255] "" XXX + SG_ LKA_ACTIVE : 48|1@0+ (1,0) [0|16777215] "" XXX + SG_ STEERING_OUT_TORQUE : 64|12@1+ (0.1,-204.8) [0|65535] "" XXX + SG_ STEERING_COL_TORQUE : 80|13@1+ (1,-4095) [0|4095] "" XXX + SG_ STEERING_ANGLE : 96|16@1- (-0.1,0) [0|255] "deg" XXX + SG_ STEERING_ANGLE_2 : 128|16@1- (-0.1,0) [0|65535] "deg" XXX + +BO_ 293 STEERING_SENSORS: 16 XXX + SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX + SG_ COUNTER : 16|8@1+ (1,0) [0|255] "" XXX + SG_ STEERING_RATE : 40|8@1- (-0.1,0) [0|255] "deg/s" XXX + SG_ STEERING_ANGLE : 24|16@1- (-0.1,0) [0|255] "deg" XXX + +BO_ 298 LFA: 16 XXX + SG_ STEER_REQ : 52|1@1+ (1,0) [0|1] "" XXX + SG_ TORQUE_REQUEST : 41|11@1+ (1,-1024) [0|4095] "" XXX + SG_ LKA_ICON : 38|2@1+ (1,0) [0|255] "" XXX + SG_ NEW_SIGNAL_1 : 27|2@1+ (1,0) [0|255] "" XXX + SG_ LFA_BUTTON : 56|1@1+ (1,0) [0|255] "" XXX + SG_ COUNTER : 16|8@1+ (1,0) [0|255] "" XXX + SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX + SG_ STEER_MODE : 65|3@1+ (1,0) [0|1] "" XXX + SG_ LKA_WARNING : 32|1@1+ (1,0) [0|1] "" XXX + SG_ LKA_ASSIST : 62|1@1+ (1,0) [0|1] "" XXX + SG_ LKA_MODE : 24|3@1+ (1,0) [0|7] "" XXX + SG_ NEW_SIGNAL_2 : 70|2@0+ (1,0) [0|3] "" XXX + SG_ SET_ME_1 : 80|1@0+ (1,0) [0|1] "" XXX + SG_ NEW_SIGNAL_3 : 111|8@0+ (1,0) [0|255] "" XXX + +BO_ 373 SCC1: 24 XXX + SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX + SG_ COUNTER : 16|8@1+ (1,0) [0|255] "" XXX + SG_ ACCEL_REQ : 48|13@1- (1,0) [0|1023] "" XXX + SG_ CRUISE_ACTIVE : 68|1@0+ (1,0) [0|1] "" XXX + +BO_ 352 CAM_0x160: 16 CAMERA + SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX + SG_ COUNTER : 16|8@1+ (1,0) [0|255] "" XXX + +BO_ 384 CAM_0x180: 32 CAMERA + SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX + SG_ COUNTER : 16|8@1+ (1,0) [0|255] "" XXX + +BO_ 385 CAM_0x181: 32 CAMERA + SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX + SG_ COUNTER : 16|8@1+ (1,0) [0|255] "" XXX + +BO_ 386 CAM_0x182: 32 CAMERA + SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX + SG_ COUNTER : 16|8@1+ (1,0) [0|255] "" XXX + +BO_ 387 CAM_0x183: 32 CAMERA + SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX + SG_ COUNTER : 16|8@1+ (1,0) [0|255] "" XXX + +BO_ 388 CAM_0x184: 32 CAMERA + SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX + SG_ COUNTER : 16|8@1+ (1,0) [0|255] "" XXX + +BO_ 389 CAM_0x185: 8 CAMERA + SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX + SG_ COUNTER : 16|8@1+ (1,0) [0|255] "" XXX + +BO_ 416 CRUISE_INFO: 32 XXX + SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX + SG_ COUNTER : 16|8@1+ (1,0) [0|255] "" XXX + SG_ CRUISE_STANDSTILL : 76|1@1+ (1,0) [0|1] "" XXX + SG_ SET_SPEED : 102|7@0+ (1,0) [0|127] "km/h or mph" XXX + SG_ CRUISE_ACTIVE : 184|1@1+ (1,0) [0|1] "" XXX + +BO_ 438 CAM_0x1b6: 32 CAMERA + SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX + SG_ COUNTER : 16|8@1+ (1,0) [0|255] "" XXX + +BO_ 439 CAM_0x1b7: 32 CAMERA + SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX + SG_ COUNTER : 16|8@1+ (1,0) [0|255] "" XXX + +BO_ 440 CAM_0x1b8: 32 CAMERA + SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX + SG_ COUNTER : 16|8@1+ (1,0) [0|255] "" XXX + +BO_ 441 CAM_0x1b9: 32 CAMERA + SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX + SG_ COUNTER : 16|8@1+ (1,0) [0|255] "" XXX + +BO_ 463 CRUISE_BUTTONS: 8 XXX + SG_ _CHECKSUM : 0|8@1+ (1,0) [0|65535] "" XXX + SG_ LKAS_BTN : 23|1@1+ (1,0) [0|1] "" XXX + SG_ SET_ME_1 : 29|1@1+ (1,0) [0|1] "" XXX + SG_ ADAPTIVE_CRUISE_MAIN_BTN : 19|1@1+ (1,0) [0|1] "" XXX + SG_ NORMAL_CRUISE_MAIN_BTN : 21|1@1+ (1,0) [0|1] "" XXX + SG_ COUNTER : 12|4@1+ (1,0) [0|255] "" XXX + SG_ CRUISE_BUTTONS : 16|3@1+ (1,0) [0|3] "" XXX + +BO_ 480 NEW_MSG_1: 16 XXX + SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX + SG_ COUNTER : 16|8@1+ (1,0) [0|255] "" XXX + SG_ LFA_GREY : 47|1@0+ (1,0) [0|1] "" XXX + +BO_ 507 CAM_0x1fb: 32 CAMERA + SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX + SG_ COUNTER : 16|8@1+ (1,0) [0|255] "" XXX + +BO_ 674 CAM_0x2a2: 32 CAMERA + SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX + SG_ COUNTER : 16|8@1+ (1,0) [0|255] "" XXX + +BO_ 675 CAM_0x2a3: 32 CAMERA + SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX + SG_ COUNTER : 16|8@1+ (1,0) [0|255] "" XXX + +BO_ 676 CAM_0x2a4: 24 XXX + SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX + SG_ COUNTER : 16|8@1+ (1,0) [0|255] "" XXX + SG_ BYTE3 : 24|8@1+ (1,0) [0|255] "" XXX + SG_ BYTE4 : 32|8@1+ (1,0) [0|255] "" XXX + SG_ BYTE5 : 40|8@1+ (1,0) [0|255] "" XXX + SG_ BYTE6 : 48|8@1+ (1,0) [0|255] "" XXX + SG_ BYTE7 : 56|8@1+ (1,0) [0|255] "" XXX + SG_ BYTE8 : 64|8@1+ (1,0) [0|255] "" XXX + SG_ BYTE9 : 72|8@1+ (1,0) [0|255] "" XXX + SG_ BYTE10 : 80|8@1+ (1,0) [0|255] "" XXX + SG_ BYTE11 : 88|8@1+ (1,0) [0|255] "" XXX + SG_ BYTE12 : 96|8@1+ (1,0) [0|255] "" XXX + SG_ BYTE13 : 104|8@1+ (1,0) [0|255] "" XXX + SG_ BYTE14 : 112|8@1+ (1,0) [0|255] "" XXX + SG_ BYTE15 : 120|8@1+ (1,0) [0|255] "" XXX + SG_ BYTE16 : 128|8@1+ (1,0) [0|255] "" XXX + SG_ BYTE17 : 136|8@1+ (1,0) [0|255] "" XXX + SG_ BYTE18 : 144|8@1+ (1,0) [0|255] "" XXX + SG_ BYTE19 : 152|8@1+ (1,0) [0|255] "" XXX + SG_ BYTE20 : 160|8@1+ (1,0) [0|255] "" XXX + SG_ BYTE21 : 168|8@1+ (1,0) [0|255] "" XXX + SG_ BYTE22 : 176|8@1+ (1,0) [0|255] "" XXX + SG_ BYTE23 : 184|8@1+ (1,0) [0|255] "" XXX + +BO_ 699 CAM_0x2bb: 32 CAMERA + SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX + SG_ COUNTER : 16|8@1+ (1,0) [0|255] "" XXX + +BO_ 700 CAM_0x2bc: 32 CAMERA + SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX + SG_ COUNTER : 16|8@1+ (1,0) [0|255] "" XXX + +BO_ 701 CAM_0x2bd: 32 CAMERA + SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX + SG_ COUNTER : 16|8@1+ (1,0) [0|255] "" XXX + +BO_ 702 CAM_0x2be: 32 CAMERA + SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX + SG_ COUNTER : 16|8@1+ (1,0) [0|255] "" XXX + +BO_ 961 BLINKER_STALKS: 8 XXX + SG_ COUNTER_ALT : 15|4@0+ (1,0) [0|15] "" XXX + SG_ CHECKSUM_MAYBE : 7|8@0+ (1,0) [0|255] "" XXX + SG_ HIGHBEAM_FORWARD : 18|1@0+ (1,0) [0|1] "" XXX + SG_ HIGHBEAM_BACKWARD : 26|1@0+ (1,0) [0|1] "" XXX + SG_ RIGHT_BLINKER : 32|1@0+ (1,0) [0|1] "" XXX + SG_ LEFT_BLINKER : 30|1@0+ (1,0) [0|1] "" XXX + SG_ LIGHT_KNOB_POSITION : 21|2@0+ (1,0) [0|3] "" XXX + +BO_ 1041 DOORS_SEATBELTS: 8 XXX + SG_ CHECKSUM_MAYBE : 7|8@0+ (1,0) [0|65535] "" XXX + SG_ COUNTER_ALT : 15|4@0+ (1,0) [0|15] "" XXX + SG_ DRIVER_SEATBELT_LATCHED : 42|1@0+ (1,0) [0|1] "" XXX + SG_ DRIVER_DOOR_OPEN : 24|1@1+ (1,0) [0|1] "" XXX + +BO_ 1043 BLINKERS: 8 XXX + SG_ COUNTER_ALT : 15|4@0+ (1,0) [0|15] "" XXX + SG_ LEFT_LAMP : 20|1@0+ (1,0) [0|1] "" XXX + SG_ RIGHT_LAMP : 22|1@0+ (1,0) [0|1] "" XXX + +BO_ 1240 CLUSTER_INFO: 8 XXX + SG_ DISTANCE_UNIT : 0|1@1+ (1,0) [0|1] "" XXX + + + +CM_ SG_ 961 COUNTER_ALT "only increments on change"; +CM_ SG_ 1041 COUNTER_ALT "only increments on change"; +CM_ SG_ 1043 COUNTER_ALT "only increments on change"; +VAL_ 53 GEAR 0 "P" 5 "D" 6 "N" 7 "R" ; +VAL_ 80 LKA_ICON 0 "hidden" 1 "grey" 2 "green" 3 "flashing green" ; +VAL_ 80 LKA_MODE 1 "warning only" 2 "assist" 6 "off" ; +VAL_ 463 CRUISE_BUTTONS 0 "none" 1 "res_accel" 2 "set_decel" 3 "gap_distance" 4 "pause_resume" ; diff --git a/opendbc/toyota_new_mc_pt_generated.dbc b/opendbc/toyota_new_mc_pt_generated.dbc index 1d385fdeb..adb419896 100644 --- a/opendbc/toyota_new_mc_pt_generated.dbc +++ b/opendbc/toyota_new_mc_pt_generated.dbc @@ -127,6 +127,7 @@ BO_ 466 PCM_CRUISE: 8 XXX SG_ CHECKSUM : 63|8@0+ (1,0) [0|255] "" XXX BO_ 467 PCM_CRUISE_2: 8 XXX + SG_ PCM_FOLLOW_DISTANCE : 12|2@0+ (1,0) [0|3] "" XXX SG_ MAIN_ON : 15|1@0+ (1,0) [0|1] "" XXX SG_ LOW_SPEED_LOCKOUT : 14|2@0+ (1,0) [0|3] "" XXX SG_ SET_SPEED : 23|8@0+ (1,0) [0|255] "km/h" XXX @@ -476,6 +477,7 @@ CM_ SG_ 1592 LOCKED_VIA_KEYFOB "1 for as long as car is locked with key fob or d VAL_ 295 GEAR 0 "P" 1 "R" 2 "N" 3 "D" 4 "B"; VAL_ 466 CRUISE_STATE 11 "timer_3sec" 10 "adaptive click down" 9 "adaptive click up" 8 "adaptive engaged" 7 "standstill" 6 "non-adaptive click up" 5 "non-adaptive click down" 4 "non-adaptive hold down" 3 "non-adaptive hold up" 2 "non-adaptive being engaged" 1 "non-adaptive engaged" 0 "off"; VAL_ 467 LOW_SPEED_LOCKOUT 2 "low speed locked" 1 "ok"; +VAL_ 467 PCM_FOLLOW_DISTANCE 1 "far" 2 "medium" 3 "close"; VAL_ 614 STATE 3 "enabled" 1 "disabled"; VAL_ 614 DIRECTION_CMD 3 "right" 2 "center" 1 "left"; VAL_ 643 STATE 0 "normal" 1 "adaptive_cruise_control" 3 "emergency_braking"; diff --git a/opendbc/toyota_nodsu_pt_generated.dbc b/opendbc/toyota_nodsu_pt_generated.dbc index a568c5cb4..ffba161b7 100644 --- a/opendbc/toyota_nodsu_pt_generated.dbc +++ b/opendbc/toyota_nodsu_pt_generated.dbc @@ -127,6 +127,7 @@ BO_ 466 PCM_CRUISE: 8 XXX SG_ CHECKSUM : 63|8@0+ (1,0) [0|255] "" XXX BO_ 467 PCM_CRUISE_2: 8 XXX + SG_ PCM_FOLLOW_DISTANCE : 12|2@0+ (1,0) [0|3] "" XXX SG_ MAIN_ON : 15|1@0+ (1,0) [0|1] "" XXX SG_ LOW_SPEED_LOCKOUT : 14|2@0+ (1,0) [0|3] "" XXX SG_ SET_SPEED : 23|8@0+ (1,0) [0|255] "km/h" XXX @@ -476,6 +477,7 @@ CM_ SG_ 1592 LOCKED_VIA_KEYFOB "1 for as long as car is locked with key fob or d VAL_ 295 GEAR 0 "P" 1 "R" 2 "N" 3 "D" 4 "B"; VAL_ 466 CRUISE_STATE 11 "timer_3sec" 10 "adaptive click down" 9 "adaptive click up" 8 "adaptive engaged" 7 "standstill" 6 "non-adaptive click up" 5 "non-adaptive click down" 4 "non-adaptive hold down" 3 "non-adaptive hold up" 2 "non-adaptive being engaged" 1 "non-adaptive engaged" 0 "off"; VAL_ 467 LOW_SPEED_LOCKOUT 2 "low speed locked" 1 "ok"; +VAL_ 467 PCM_FOLLOW_DISTANCE 1 "far" 2 "medium" 3 "close"; VAL_ 614 STATE 3 "enabled" 1 "disabled"; VAL_ 614 DIRECTION_CMD 3 "right" 2 "center" 1 "left"; VAL_ 643 STATE 0 "normal" 1 "adaptive_cruise_control" 3 "emergency_braking"; @@ -541,6 +543,7 @@ BO_ 550 BRAKE_MODULE: 8 XXX BO_ 610 EPS_STATUS: 8 EPS SG_ IPAS_STATE : 3|4@0+ (1,0) [0|15] "" XXX SG_ LKA_STATE : 31|7@0+ (1,0) [0|127] "" XXX + SG_ LTA_STATE : 15|5@0+ (1,0) [0|31] "" XXX SG_ TYPE : 24|1@0+ (1,0) [0|1] "" XXX SG_ CHECKSUM : 63|8@0+ (1,0) [0|255] "" XXX @@ -570,4 +573,5 @@ CM_ SG_ 1014 ADJACENT_ENABLED "when BSM is enabled in settings, this is on along CM_ SG_ 1014 APPROACHING_ENABLED "when BSM is enabled in settings, this is on along with ADJACENT_ENABLED. this controls bsm alert visibility"; VAL_ 610 IPAS_STATE 5 "override" 3 "enabled" 1 "disabled"; -VAL_ 610 LKA_STATE 25 "temporary_fault" 9 "temporary_fault2" 5 "active" 1 "standby"; +VAL_ 610 LKA_STATE 25 "temporary_fault" 17 "permanent_fault" 9 "temporary_fault2" 5 "active" 1 "standby"; +VAL_ 610 LTA_STATE 25 "temporary_fault" 9 "temporary_fault2" 5 "active" 1 "standby"; diff --git a/opendbc/toyota_tnga_k_pt_generated.dbc b/opendbc/toyota_tnga_k_pt_generated.dbc index 79c7cc62a..955a116ea 100644 --- a/opendbc/toyota_tnga_k_pt_generated.dbc +++ b/opendbc/toyota_tnga_k_pt_generated.dbc @@ -127,6 +127,7 @@ BO_ 466 PCM_CRUISE: 8 XXX SG_ CHECKSUM : 63|8@0+ (1,0) [0|255] "" XXX BO_ 467 PCM_CRUISE_2: 8 XXX + SG_ PCM_FOLLOW_DISTANCE : 12|2@0+ (1,0) [0|3] "" XXX SG_ MAIN_ON : 15|1@0+ (1,0) [0|1] "" XXX SG_ LOW_SPEED_LOCKOUT : 14|2@0+ (1,0) [0|3] "" XXX SG_ SET_SPEED : 23|8@0+ (1,0) [0|255] "km/h" XXX @@ -476,6 +477,7 @@ CM_ SG_ 1592 LOCKED_VIA_KEYFOB "1 for as long as car is locked with key fob or d VAL_ 295 GEAR 0 "P" 1 "R" 2 "N" 3 "D" 4 "B"; VAL_ 466 CRUISE_STATE 11 "timer_3sec" 10 "adaptive click down" 9 "adaptive click up" 8 "adaptive engaged" 7 "standstill" 6 "non-adaptive click up" 5 "non-adaptive click down" 4 "non-adaptive hold down" 3 "non-adaptive hold up" 2 "non-adaptive being engaged" 1 "non-adaptive engaged" 0 "off"; VAL_ 467 LOW_SPEED_LOCKOUT 2 "low speed locked" 1 "ok"; +VAL_ 467 PCM_FOLLOW_DISTANCE 1 "far" 2 "medium" 3 "close"; VAL_ 614 STATE 3 "enabled" 1 "disabled"; VAL_ 614 DIRECTION_CMD 3 "right" 2 "center" 1 "left"; VAL_ 643 STATE 0 "normal" 1 "adaptive_cruise_control" 3 "emergency_braking"; diff --git a/panda/board/main_declarations.h b/panda/board/main_declarations.h index 27890cdf2..7fddb24dc 100644 --- a/panda/board/main_declarations.h +++ b/panda/board/main_declarations.h @@ -20,8 +20,6 @@ bool green_led_enabled = false; uint32_t heartbeat_counter = 0; bool heartbeat_lost = false; bool heartbeat_disabled = false; // set over USB -bool heartbeat_engaged = false; // openpilot enabled, passed in heartbeat USB command -uint32_t heartbeat_engaged_mismatches = 0; // count of mismatches between heartbeat_engaged and controls_allowed // Enter deep sleep mode bool deepsleep_requested = false; diff --git a/panda/board/safety.h b/panda/board/safety.h index ce32cc795..dfbf30769 100644 --- a/panda/board/safety.h +++ b/panda/board/safety.h @@ -60,7 +60,15 @@ const safety_hooks *current_hooks = &nooutput_hooks; const addr_checks *current_rx_checks = &default_rx_checks; int safety_rx_hook(CANPacket_t *to_push) { - return current_hooks->rx(to_push); + bool controls_allowed_prev = controls_allowed; + int ret = current_hooks->rx(to_push); + + // reset mismatches on rising edge of controls_allowed to avoid rare race condition + if (controls_allowed && !controls_allowed_prev) { + heartbeat_engaged_mismatches = 0; + } + + return ret; } int safety_tx_hook(CANPacket_t *to_send) { @@ -378,7 +386,7 @@ bool max_limit_check(int val, const int MAX_VAL, const int MIN_VAL) { // check that commanded value isn't too far from measured bool dist_to_meas_check(int val, int val_last, struct sample_t *val_meas, - const int MAX_RATE_UP, const int MAX_RATE_DOWN, const int MAX_ERROR) { + const int MAX_RATE_UP, const int MAX_RATE_DOWN, const int MAX_ERROR) { // *** val rate limit check *** int highest_allowed_rl = MAX(val_last, 0) + MAX_RATE_UP; @@ -394,12 +402,14 @@ bool dist_to_meas_check(int val, int val_last, struct sample_t *val_meas, // check that commanded value isn't fighting against driver bool driver_limit_check(int val, int val_last, struct sample_t *val_driver, - const int MAX_VAL, const int MAX_RATE_UP, const int MAX_RATE_DOWN, - const int MAX_ALLOWANCE, const int DRIVER_FACTOR) { + const int MAX_VAL, const int MAX_RATE_UP, const int MAX_RATE_DOWN, + const int MAX_ALLOWANCE, const int DRIVER_FACTOR) { + // torque delta/rate limits int highest_allowed_rl = MAX(val_last, 0) + MAX_RATE_UP; int lowest_allowed_rl = MIN(val_last, 0) - MAX_RATE_UP; + // driver int driver_max_limit = MAX_VAL + (MAX_ALLOWANCE + val_driver->max) * DRIVER_FACTOR; int driver_min_limit = -MAX_VAL + (-MAX_ALLOWANCE + val_driver->min) * DRIVER_FACTOR; diff --git a/panda/board/safety/safety_chrysler.h b/panda/board/safety/safety_chrysler.h index e7b63cfef..ba642e05e 100644 --- a/panda/board/safety/safety_chrysler.h +++ b/panda/board/safety/safety_chrysler.h @@ -1,30 +1,80 @@ const int CHRYSLER_MAX_STEER = 261; -const int CHRYSLER_MAX_RT_DELTA = 112; // max delta torque allowed for real time checks +const int CHRYSLER_MAX_RT_DELTA = 112; // max delta torque allowed for real time checks const uint32_t CHRYSLER_RT_INTERVAL = 250000; // 250ms between real time checks const int CHRYSLER_MAX_RATE_UP = 3; const int CHRYSLER_MAX_RATE_DOWN = 3; -const int CHRYSLER_MAX_TORQUE_ERROR = 80; // max torque cmd in excess of torque motor -const int CHRYSLER_STANDSTILL_THRSLD = 10; // about 1m/s -const CanMsg CHRYSLER_TX_MSGS[] = {{571, 0, 3}, {658, 0, 6}, {678, 0, 8}}; +const int CHRYSLER_MAX_TORQUE_ERROR = 80; // max torque cmd in excess of torque motor +const int CHRYSLER_STANDSTILL_THRSLD = 10; // about 1m/s +const int CHRYSLER_RAM_STANDSTILL_THRSLD = 3; // about 1m/s changed from wheel rpm to km/h + +const int CHRYSLER_RAM_MAX_RATE_UP = 6; +const int CHRYSLER_RAM_MAX_RATE_DOWN = 6; + +// CAN messages for Chrysler/Jeep platforms +#define EPS_2 544 // EPS driver input torque +#define ESP_1 320 // Brake pedal and vehicle speed +#define ESP_8 284 // Brake pedal and vehicle speed +#define ECM_5 559 // Throttle position sensor +#define DAS_3 500 // ACC engagement states from DASM +#define DAS_6 678 // LKAS HUD and auto headlight control from DASM +#define LKAS_COMMAND 658 // LKAS controls from DASM +#define CRUISE_BUTTONS 571 // Cruise control buttons + +// CAN messages for the 5h gen RAM DT platform +#define EPS_2_RAM 49 // EPS driver input torque +#define ESP_1_RAM 131 // Brake pedal and vehicle speed +#define ESP_8_RAM 121 // Brake pedal and vehicle speed +#define ECM_5_RAM 157 // Throttle position sensor +#define DAS_3_RAM 153 // ACC engagement states from DASM +#define DAS_6_RAM 250 // LKAS HUD and auto headlight control from DASM +#define LKAS_COMMAND_RAM 166 // LKAS controls from DASM +#define CRUISE_BUTTONS_RAM 177 // Cruise control buttons + +const CanMsg CHRYSLER_TX_MSGS[] = { + {CRUISE_BUTTONS, 0, 3}, + {LKAS_COMMAND, 0, 6}, + {DAS_6, 0, 8}, +}; + +const CanMsg CHRYSLER_RAM_TX_MSGS[] = { + {CRUISE_BUTTONS_RAM, 2, 3}, + {LKAS_COMMAND_RAM, 0, 8}, + {DAS_6_RAM, 0, 8}, +}; AddrCheckStruct chrysler_addr_checks[] = { - {.msg = {{544, 0, 8, .check_checksum = true, .max_counter = 15U, .expected_timestep = 10000U}, { 0 }, { 0 }}}, + {.msg = {{EPS_2, 0, 8, .check_checksum = true, .max_counter = 15U, .expected_timestep = 10000U}, { 0 }, { 0 }}}, // EPS module + {.msg = {{ESP_1, 0, 8, .check_checksum = true, .max_counter = 15U, .expected_timestep = 20000U}, { 0 }, { 0 }}}, // brake pressed + //{.msg = {{ESP_8, 0, 8, .check_checksum = true, .max_counter = 15U, .expected_timestep = 20000U}}}, // vehicle Speed {.msg = {{514, 0, 8, .check_checksum = false, .max_counter = 0U, .expected_timestep = 10000U}, { 0 }, { 0 }}}, - {.msg = {{500, 0, 8, .check_checksum = true, .max_counter = 15U, .expected_timestep = 20000U}, { 0 }, { 0 }}}, - {.msg = {{308, 0, 8, .check_checksum = false, .max_counter = 15U, .expected_timestep = 20000U}, { 0 }, { 0 }}}, - {.msg = {{320, 0, 8, .check_checksum = true, .max_counter = 15U, .expected_timestep = 20000U}, { 0 }, { 0 }}}, + {.msg = {{ECM_5, 0, 8, .check_checksum = true, .max_counter = 15U, .expected_timestep = 20000U}, { 0 }, { 0 }}}, // gas pedal + {.msg = {{DAS_3, 0, 8, .check_checksum = true, .max_counter = 15U, .expected_timestep = 20000U}, { 0 }, { 0 }}}, // cruise state }; #define CHRYSLER_ADDR_CHECK_LEN (sizeof(chrysler_addr_checks) / sizeof(chrysler_addr_checks[0])) + +AddrCheckStruct chrysler_ram_addr_checks[] = { + {.msg = {{EPS_2_RAM, 0, 8, .check_checksum = true, .max_counter = 15U, .expected_timestep = 10000U}, { 0 }, { 0 }}}, // EPS module + {.msg = {{ESP_1_RAM, 0, 8, .check_checksum = true, .max_counter = 15U, .expected_timestep = 20000U}, { 0 }, { 0 }}}, // brake pressed + {.msg = {{ESP_8_RAM, 0, 8, .check_checksum = true, .max_counter = 15U, .expected_timestep = 20000U}, { 0 }, { 0 }}}, // vehicle Speed + {.msg = {{ECM_5_RAM, 0, 8, .check_checksum = true, .max_counter = 15U, .expected_timestep = 20000U}, { 0 }, { 0 }}}, // gas pedal + {.msg = {{DAS_3_RAM, 2, 8, .check_checksum = true, .max_counter = 15U, .expected_timestep = 20000U}, { 0 }, { 0 }}}, // cruise state +}; +#define CHRYSLER_RAM_ADDR_CHECK_LEN (sizeof(chrysler_ram_addr_checks) / sizeof(chrysler_ram_addr_checks[0])) + addr_checks chrysler_rx_checks = {chrysler_addr_checks, CHRYSLER_ADDR_CHECK_LEN}; +const uint32_t CHRYSLER_PARAM_RAM_DT = 1U; // set for Ram DT platform + +bool chrysler_ram = false; + static uint32_t chrysler_get_checksum(CANPacket_t *to_push) { int checksum_byte = GET_LEN(to_push) - 1U; return (uint8_t)(GET_BYTE(to_push, checksum_byte)); } static uint32_t chrysler_compute_checksum(CANPacket_t *to_push) { - /* This function does not want the checksum byte in the input data. - jeep chrysler canbus checksum from http://illmatics.com/Remote%20Car%20Hacking.pdf */ + // TODO: clean this up + // http://illmatics.com/Remote%20Car%20Hacking.pdf uint8_t checksum = 0xFFU; int len = GET_LEN(to_push); for (int j = 0; j < (len - 1); j++) { @@ -56,7 +106,6 @@ static uint32_t chrysler_compute_checksum(CANPacket_t *to_push) { } static uint8_t chrysler_get_counter(CANPacket_t *to_push) { - // Well defined counter only for 8 bytes messages return (uint8_t)(GET_BYTE(to_push, 6) >> 4); } @@ -66,20 +115,23 @@ static int chrysler_rx_hook(CANPacket_t *to_push) { chrysler_get_checksum, chrysler_compute_checksum, chrysler_get_counter); - if (valid && (GET_BUS(to_push) == 0U)) { - int addr = GET_ADDR(to_push); + const int bus = GET_BUS(to_push); + const int addr = GET_ADDR(to_push); - // Measured eps torque - if (addr == 544) { + if (valid) { + + // Measured EPS torque + const int eps_2 = chrysler_ram ? EPS_2_RAM : EPS_2; + if ((bus == 0) && (addr == eps_2)) { int torque_meas_new = ((GET_BYTE(to_push, 4) & 0x7U) << 8) + GET_BYTE(to_push, 5) - 1024U; - - // update array of samples update_sample(&torque_meas, torque_meas_new); } // enter controls on rising edge of ACC, exit controls on ACC off - if (addr == 500) { - int cruise_engaged = ((GET_BYTE(to_push, 2) & 0x38U) >> 3) == 7U; + const int das_3 = chrysler_ram ? DAS_3_RAM : DAS_3; + const int das_3_bus = chrysler_ram ? 2 : 0; + if ((bus == das_3_bus) && (addr == das_3)) { + int cruise_engaged = GET_BIT(to_push, 21U) == 1U; if (cruise_engaged && !cruise_engaged_prev) { controls_allowed = 1; } @@ -89,8 +141,13 @@ static int chrysler_rx_hook(CANPacket_t *to_push) { cruise_engaged_prev = cruise_engaged; } + // TODO: use the same message for both // update speed - if (addr == 514) { + if (chrysler_ram && (bus == 0) && (addr == ESP_8_RAM)) { + vehicle_speed = (((GET_BYTE(to_push, 4) & 0x3U) << 8) + GET_BYTE(to_push, 5))*0.0078125; + vehicle_moving = (int)vehicle_speed > CHRYSLER_RAM_STANDSTILL_THRSLD; + } + if (!chrysler_ram && (bus == 0) && (addr == 514)) { int speed_l = (GET_BYTE(to_push, 0) << 4) + (GET_BYTE(to_push, 1) >> 4); int speed_r = (GET_BYTE(to_push, 2) << 4) + (GET_BYTE(to_push, 3) >> 4); vehicle_speed = (speed_l + speed_r) / 2; @@ -98,16 +155,19 @@ static int chrysler_rx_hook(CANPacket_t *to_push) { } // exit controls on rising edge of gas press - if (addr == 308) { - gas_pressed = ((GET_BYTE(to_push, 5) & 0x7FU) != 0U); + const int ecm_5 = chrysler_ram ? ECM_5_RAM : ECM_5; + if ((bus == 0) && (addr == ecm_5)) { + gas_pressed = GET_BYTE(to_push, 0U) != 0U; } // exit controls on rising edge of brake press - if (addr == 320) { - brake_pressed = (GET_BYTE(to_push, 0) & 0x7U) == 5U; + const int esp_1 = chrysler_ram ? ESP_1_RAM : ESP_1; + if ((bus == 0) && (addr == esp_1)) { + brake_pressed = ((GET_BYTE(to_push, 0U) & 0xFU) >> 2U) == 1U; } - generic_rx_checks((addr == 0x292)); + const int lkas_command = chrysler_ram ? LKAS_COMMAND_RAM : LKAS_COMMAND; + generic_rx_checks((bus == 0) && (addr == lkas_command)); } return valid; } @@ -118,24 +178,31 @@ static int chrysler_tx_hook(CANPacket_t *to_send, bool longitudinal_allowed) { int tx = 1; int addr = GET_ADDR(to_send); - if (!msg_allowed(to_send, CHRYSLER_TX_MSGS, sizeof(CHRYSLER_TX_MSGS) / sizeof(CHRYSLER_TX_MSGS[0]))) { - tx = 0; + if (chrysler_ram) { + tx = msg_allowed(to_send, CHRYSLER_RAM_TX_MSGS, sizeof(CHRYSLER_RAM_TX_MSGS) / sizeof(CHRYSLER_RAM_TX_MSGS[0])); + } else { + tx = msg_allowed(to_send, CHRYSLER_TX_MSGS, sizeof(CHRYSLER_TX_MSGS) / sizeof(CHRYSLER_TX_MSGS[0])); } - // LKA STEER - if (addr == 0x292) { - int desired_torque = ((GET_BYTE(to_send, 0) & 0x7U) << 8) + GET_BYTE(to_send, 1) - 1024U; + // STEERING + const int lkas_addr = chrysler_ram ? LKAS_COMMAND_RAM : LKAS_COMMAND; + if (tx && (addr == lkas_addr)) { + int start_byte = chrysler_ram ? 1 : 0; + int desired_torque = ((GET_BYTE(to_send, start_byte) & 0x7U) << 8) | GET_BYTE(to_send, start_byte + 1); + desired_torque -= 1024; + uint32_t ts = microsecond_timer_get(); bool violation = 0; if (controls_allowed) { - // *** global torque limit check *** violation |= max_limit_check(desired_torque, CHRYSLER_MAX_STEER, -CHRYSLER_MAX_STEER); // *** torque rate limit check *** + const int max_rate_up = chrysler_ram ? CHRYSLER_RAM_MAX_RATE_UP : CHRYSLER_MAX_RATE_UP; + const int max_rate_down = chrysler_ram ? CHRYSLER_RAM_MAX_RATE_DOWN : CHRYSLER_MAX_RATE_DOWN; violation |= dist_to_meas_check(desired_torque, desired_torque_last, - &torque_meas, CHRYSLER_MAX_RATE_UP, CHRYSLER_MAX_RATE_DOWN, CHRYSLER_MAX_TORQUE_ERROR); + &torque_meas, max_rate_up, max_rate_down, CHRYSLER_MAX_TORQUE_ERROR); // used next time desired_torque_last = desired_torque; @@ -169,8 +236,11 @@ static int chrysler_tx_hook(CANPacket_t *to_send, bool longitudinal_allowed) { } // FORCE CANCEL: only the cancel button press is allowed - if (addr == 571) { - if ((GET_BYTE(to_send, 0) != 1U) || ((GET_BYTE(to_send, 1) & 1U) == 1U)) { + if ((addr == CRUISE_BUTTONS) || (addr == CRUISE_BUTTONS_RAM)) { + const bool is_cancel = GET_BYTE(to_send, 0) == 1U; + const bool is_resume = GET_BYTE(to_send, 0) == 0x10U; + const bool allowed = is_cancel || (is_resume && controls_allowed); + if (!allowed) { tx = 0; } } @@ -179,17 +249,18 @@ static int chrysler_tx_hook(CANPacket_t *to_send, bool longitudinal_allowed) { } static int chrysler_fwd_hook(int bus_num, CANPacket_t *to_fwd) { - int bus_fwd = -1; int addr = GET_ADDR(to_fwd); - // forward CAN 0 -> 2 so stock LKAS camera sees messages + // forward to camera if (bus_num == 0) { bus_fwd = 2; } - // forward all messages from camera except LKAS_COMMAND and LKAS_HUD - if ((bus_num == 2) && (addr != 658) && (addr != 678)) { + // forward all messages from camera except LKAS messages + const bool is_lkas = (!chrysler_ram && ((addr == LKAS_COMMAND) || (addr == DAS_6))) || + (chrysler_ram && ((addr == LKAS_COMMAND_RAM) || (addr == DAS_6_RAM))); + if ((bus_num == 2) && !is_lkas){ bus_fwd = 0; } @@ -197,7 +268,14 @@ static int chrysler_fwd_hook(int bus_num, CANPacket_t *to_fwd) { } static const addr_checks* chrysler_init(uint16_t param) { - UNUSED(param); + chrysler_ram = GET_FLAG(param, CHRYSLER_PARAM_RAM_DT); + + if (chrysler_ram) { + chrysler_rx_checks = (addr_checks){chrysler_ram_addr_checks, CHRYSLER_RAM_ADDR_CHECK_LEN}; + } else { + chrysler_rx_checks = (addr_checks){chrysler_addr_checks, CHRYSLER_ADDR_CHECK_LEN}; + } + return &chrysler_rx_checks; } diff --git a/panda/board/safety/safety_honda.h b/panda/board/safety/safety_honda.h index 260741115..1d20cfe80 100644 --- a/panda/board/safety/safety_honda.h +++ b/panda/board/safety/safety_honda.h @@ -8,6 +8,7 @@ // brake > 0mph const CanMsg HONDA_N_TX_MSGS[] = {{0xE4, 0, 5}, {0x194, 0, 4}, {0x1FA, 0, 8}, {0x200, 0, 6}, {0x30C, 0, 8}, {0x33D, 0, 5}}; const CanMsg HONDA_BOSCH_TX_MSGS[] = {{0xE4, 0, 5}, {0xE5, 0, 8}, {0x296, 1, 4}, {0x33D, 0, 5}, {0x33DA, 0, 5}, {0x33DB, 0, 8}}; // Bosch +const CanMsg HONDA_RADARLESS_TX_MSGS[] = {{0xE4, 0, 5}, {0x296, 2, 4}, {0x33D, 0, 8}}; // Bosch radarless const CanMsg HONDA_BOSCH_LONG_TX_MSGS[] = {{0xE4, 1, 5}, {0x1DF, 1, 8}, {0x1EF, 1, 8}, {0x1FA, 1, 8}, {0x30C, 1, 8}, {0x33D, 1, 5}, {0x33DA, 1, 5}, {0x33DB, 1, 8}, {0x39F, 1, 8}, {0x18DAB0F1, 1, 8}}; // Bosch w/ gas and brakes // panda interceptor threshold needs to be equivalent to openpilot threshold to avoid controls mismatches @@ -19,15 +20,15 @@ const int HONDA_BOSCH_NO_GAS_VALUE = -30000; // value sent when not requesting g const int HONDA_BOSCH_GAS_MAX = 2000; const int HONDA_BOSCH_ACCEL_MIN = -350; // max braking == -3.5m/s2 -// Nidec has the powertrain bus on bus 0 -AddrCheckStruct honda_nidec_addr_checks[] = { - {.msg = {{0x1A6, 0, 8, .check_checksum = true, .max_counter = 3U, .expected_timestep = 40000U}, +// Nidec and bosch radarless has the powertrain bus on bus 0 +AddrCheckStruct honda_common_addr_checks[] = { + {.msg = {{0x1A6, 0, 8, .check_checksum = true, .max_counter = 3U, .expected_timestep = 40000U}, // SCM_BUTTONS {0x296, 0, 4, .check_checksum = true, .max_counter = 3U, .expected_timestep = 40000U}, { 0 }}}, - {.msg = {{0x158, 0, 8, .check_checksum = true, .max_counter = 3U, .expected_timestep = 10000U}, { 0 }, { 0 }}}, - {.msg = {{0x17C, 0, 8, .check_checksum = true, .max_counter = 3U, .expected_timestep = 10000U}, { 0 }, { 0 }}}, - {.msg = {{0x326, 0, 8, .check_checksum = true, .max_counter = 3U, .expected_timestep = 100000U}, { 0 }, { 0 }}}, + {.msg = {{0x158, 0, 8, .check_checksum = true, .max_counter = 3U, .expected_timestep = 10000U}, { 0 }, { 0 }}}, // ENGINE_DATA + {.msg = {{0x17C, 0, 8, .check_checksum = true, .max_counter = 3U, .expected_timestep = 10000U}, { 0 }, { 0 }}}, // POWERTRAIN_DATA + {.msg = {{0x326, 0, 8, .check_checksum = true, .max_counter = 3U, .expected_timestep = 100000U}, { 0 }, { 0 }}}, // SCM_FEEDBACK }; -#define HONDA_NIDEC_ADDR_CHECKS_LEN (sizeof(honda_nidec_addr_checks) / sizeof(honda_nidec_addr_checks[0])) +#define HONDA_COMMON_ADDR_CHECKS_LEN (sizeof(honda_common_addr_checks) / sizeof(honda_common_addr_checks[0])) // For Nidecs with main on signal on an alternate msg AddrCheckStruct honda_nidec_alt_addr_checks[] = { @@ -51,6 +52,7 @@ AddrCheckStruct honda_bosch_addr_checks[] = { const uint16_t HONDA_PARAM_ALT_BRAKE = 1; const uint16_t HONDA_PARAM_BOSCH_LONG = 2; const uint16_t HONDA_PARAM_NIDEC_ALT = 4; +const uint16_t HONDA_PARAM_RADARLESS = 8; enum { HONDA_BTN_NONE = 0, @@ -65,10 +67,15 @@ bool honda_brake_switch_prev = false; bool honda_alt_brake_msg = false; bool honda_fwd_brake = false; bool honda_bosch_long = false; +bool honda_bosch_radarless = false; enum {HONDA_NIDEC, HONDA_BOSCH} honda_hw = HONDA_NIDEC; -addr_checks honda_rx_checks = {honda_nidec_addr_checks, HONDA_NIDEC_ADDR_CHECKS_LEN}; +addr_checks honda_rx_checks = {honda_common_addr_checks, HONDA_COMMON_ADDR_CHECKS_LEN}; +int honda_get_pt_bus(void) { + return ((honda_hw == HONDA_BOSCH) && !honda_bosch_radarless) ? 1 : 0; +} + static uint32_t honda_get_checksum(CANPacket_t *to_push) { int checksum_byte = GET_LEN(to_push) - 1U; return (uint8_t)(GET_BYTE(to_push, checksum_byte)) & 0xFU; @@ -101,10 +108,11 @@ static int honda_rx_hook(CANPacket_t *to_push) { bool valid = addr_safety_check(to_push, &honda_rx_checks, honda_get_checksum, honda_compute_checksum, honda_get_counter); - const bool pcm_cruise = ((honda_hw == HONDA_BOSCH) && !honda_bosch_long) || \ - ((honda_hw == HONDA_NIDEC) && !gas_interceptor_detected); - if (valid) { + const bool pcm_cruise = ((honda_hw == HONDA_BOSCH) && !honda_bosch_long) || \ + ((honda_hw == HONDA_NIDEC) && !gas_interceptor_detected); + int pt_bus = honda_get_pt_bus(); + int addr = GET_ADDR(to_push); int len = GET_LEN(to_push); int bus = GET_BUS(to_push); @@ -142,7 +150,7 @@ static int honda_rx_hook(CANPacket_t *to_push) { // state machine to enter and exit controls for button enabling // 0x1A6 for the ILX, 0x296 for the Civic Touring - if (((addr == 0x1A6) || (addr == 0x296))) { + if (((addr == 0x1A6) || (addr == 0x296)) && (bus == pt_bus)) { int button = (GET_BYTE(to_push, 0) & 0xE0U) >> 5; // exit controls once main or cancel are pressed @@ -210,9 +218,8 @@ static int honda_rx_hook(CANPacket_t *to_push) { } } - bool stock_ecu_detected = false; int bus_rdr_car = (honda_hw == HONDA_BOSCH) ? 0 : 2; // radar bus, car side - int pt_bus = (honda_hw == HONDA_BOSCH) ? 1 : 0; + bool stock_ecu_detected = false; if (safety_mode_cnt > RELAY_TRNS_TIMEOUT) { // If steering controls messages are received on the destination bus, it's an indication @@ -224,7 +231,7 @@ static int honda_rx_hook(CANPacket_t *to_push) { } // If Honda Bosch longitudinal mode is selected we need to ensure the radar is turned off // Verify this by ensuring ACC_CONTROL (0x1DF) is not received on the PT bus - if (honda_bosch_long && (bus == pt_bus) && (addr == 0x1DF)) { + if (honda_bosch_long && !honda_bosch_radarless && (bus == pt_bus) && (addr == 0x1DF)) { stock_ecu_detected = true; } } @@ -246,7 +253,9 @@ static int honda_tx_hook(CANPacket_t *to_send, bool longitudinal_allowed) { int addr = GET_ADDR(to_send); int bus = GET_BUS(to_send); - if ((honda_hw == HONDA_BOSCH) && !honda_bosch_long) { + if ((honda_hw == HONDA_BOSCH) && honda_bosch_radarless) { + tx = msg_allowed(to_send, HONDA_RADARLESS_TX_MSGS, sizeof(HONDA_RADARLESS_TX_MSGS)/sizeof(HONDA_RADARLESS_TX_MSGS[0])); + } else if ((honda_hw == HONDA_BOSCH) && !honda_bosch_long) { tx = msg_allowed(to_send, HONDA_BOSCH_TX_MSGS, sizeof(HONDA_BOSCH_TX_MSGS)/sizeof(HONDA_BOSCH_TX_MSGS[0])); } else if ((honda_hw == HONDA_BOSCH) && honda_bosch_long) { tx = msg_allowed(to_send, HONDA_BOSCH_LONG_TX_MSGS, sizeof(HONDA_BOSCH_LONG_TX_MSGS)/sizeof(HONDA_BOSCH_LONG_TX_MSGS[0])); @@ -255,14 +264,15 @@ static int honda_tx_hook(CANPacket_t *to_send, bool longitudinal_allowed) { } // disallow actuator commands if gas or brake (with vehicle moving) are pressed - // and the the latching controls_allowed flag is True + // and the latching controls_allowed flag is True int pedal_pressed = brake_pressed_prev && vehicle_moving; bool alt_exp_allow_gas = alternative_experience & ALT_EXP_DISABLE_DISENGAGE_ON_GAS; if (!alt_exp_allow_gas) { pedal_pressed = pedal_pressed || gas_pressed_prev; } bool current_controls_allowed = controls_allowed && !(pedal_pressed); - int bus_pt = (honda_hw == HONDA_BOSCH) ? 1 : 0; + int bus_pt = honda_get_pt_bus(); + int bus_buttons = (honda_bosch_radarless) ? 2 : bus_pt; // the camera controls ACC on radarless Bosch cars // ACC_HUD: safety check (nidec w/o pedal) if ((addr == 0x30C) && (bus == bus_pt)) { @@ -345,7 +355,7 @@ static int honda_tx_hook(CANPacket_t *to_send, bool longitudinal_allowed) { // FORCE CANCEL: safety check only relevant when spamming the cancel button in Bosch HW // ensuring that only the cancel button press is sent (VAL 2) when controls are off. // This avoids unintended engagements while still allowing resume spam - if ((addr == 0x296) && !current_controls_allowed && (bus == bus_pt)) { + if ((addr == 0x296) && !current_controls_allowed && (bus == bus_buttons)) { if (((GET_BYTE(to_send, 0) >> 5) & 0x7U) != 2U) { tx = 0; } @@ -367,26 +377,32 @@ static const addr_checks* honda_nidec_init(uint16_t param) { honda_hw = HONDA_NIDEC; honda_alt_brake_msg = false; honda_bosch_long = false; + honda_bosch_radarless = false; if (GET_FLAG(param, HONDA_PARAM_NIDEC_ALT)) { honda_rx_checks = (addr_checks){honda_nidec_alt_addr_checks, HONDA_NIDEC_ALT_ADDR_CHECKS_LEN}; } else { - honda_rx_checks = (addr_checks){honda_nidec_addr_checks, HONDA_NIDEC_ADDR_CHECKS_LEN}; + honda_rx_checks = (addr_checks){honda_common_addr_checks, HONDA_COMMON_ADDR_CHECKS_LEN}; } return &honda_rx_checks; } static const addr_checks* honda_bosch_init(uint16_t param) { honda_hw = HONDA_BOSCH; + honda_bosch_radarless = GET_FLAG(param, HONDA_PARAM_RADARLESS); // Checking for alternate brake override from safety parameter - honda_alt_brake_msg = GET_FLAG(param, HONDA_PARAM_ALT_BRAKE); + honda_alt_brake_msg = GET_FLAG(param, HONDA_PARAM_ALT_BRAKE) && !honda_bosch_radarless; // radar disabled so allow gas/brakes #ifdef ALLOW_DEBUG - honda_bosch_long = GET_FLAG(param, HONDA_PARAM_BOSCH_LONG); + honda_bosch_long = GET_FLAG(param, HONDA_PARAM_BOSCH_LONG) && !honda_bosch_radarless; #endif - honda_rx_checks = (addr_checks){honda_bosch_addr_checks, HONDA_BOSCH_ADDR_CHECKS_LEN}; + if (honda_bosch_radarless) { + honda_rx_checks = (addr_checks){honda_common_addr_checks, HONDA_COMMON_ADDR_CHECKS_LEN}; + } else { + honda_rx_checks = (addr_checks){honda_bosch_addr_checks, HONDA_BOSCH_ADDR_CHECKS_LEN}; + } return &honda_rx_checks; } diff --git a/panda/board/safety/safety_hyundai.h b/panda/board/safety/safety_hyundai.h index 1f365c874..8b072463e 100644 --- a/panda/board/safety/safety_hyundai.h +++ b/panda/board/safety/safety_hyundai.h @@ -71,7 +71,7 @@ enum { // some newer HKG models can re-enable after spamming cancel button, // so keep track of user button presses to deny engagement if no interaction -const uint8_t HYUNDAI_PREV_BUTTON_SAMPLES = 4; // roughly 80 ms +const uint8_t HYUNDAI_PREV_BUTTON_SAMPLES = 8; // roughly 160 ms uint8_t hyundai_last_button_interaction; // button messages since the user pressed an enable button bool hyundai_legacy = false; @@ -296,6 +296,7 @@ static int hyundai_tx_hook(CANPacket_t *to_send, bool longitudinal_allowed) { // LKA STEER: safety check if (addr == 832) { int desired_torque = ((GET_BYTES_04(to_send) >> 16) & 0x7ffU) - 1024U; + bool steer_req = GET_BIT(to_send, 27U) != 0U; uint32_t ts = microsecond_timer_get(); bool violation = 0; @@ -323,8 +324,8 @@ static int hyundai_tx_hook(CANPacket_t *to_send, bool longitudinal_allowed) { } } - // no torque if controls is not allowed - if (!controls_allowed && (desired_torque != 0)) { + // no torque if controls is not allowed or mismatch with CF_Lkas_ActToi bit + if ((!controls_allowed || !steer_req) && (desired_torque != 0)) { violation = 1; } diff --git a/panda/board/safety/safety_hyundai_hda2.h b/panda/board/safety/safety_hyundai_hda2.h index b28b470e9..3989075df 100644 --- a/panda/board/safety/safety_hyundai_hda2.h +++ b/panda/board/safety/safety_hyundai_hda2.h @@ -1,15 +1,16 @@ -const int HYUNDAI_HDA2_MAX_STEER = 150; +const int HYUNDAI_HDA2_MAX_STEER = 270; const int HYUNDAI_HDA2_MAX_RT_DELTA = 112; // max delta torque allowed for real time checks const uint32_t HYUNDAI_HDA2_RT_INTERVAL = 250000; // 250ms between real time checks const int HYUNDAI_HDA2_MAX_RATE_UP = 3; const int HYUNDAI_HDA2_MAX_RATE_DOWN = 7; -const int HYUNDAI_HDA2_DRIVER_TORQUE_ALLOWANCE = 50; +const int HYUNDAI_HDA2_DRIVER_TORQUE_ALLOWANCE = 250; const int HYUNDAI_HDA2_DRIVER_TORQUE_FACTOR = 2; const uint32_t HYUNDAI_HDA2_STANDSTILL_THRSLD = 30; // ~1kph const CanMsg HYUNDAI_HDA2_TX_MSGS[] = { {0x50, 0, 16}, {0x1CF, 1, 8}, + {0x2A4, 0, 24}, }; AddrCheckStruct hyundai_hda2_addr_checks[] = { @@ -18,15 +19,23 @@ AddrCheckStruct hyundai_hda2_addr_checks[] = { {.msg = {{0xa0, 1, 24, .check_checksum = true, .max_counter = 0xffU, .expected_timestep = 10000U}, { 0 }, { 0 }}}, {.msg = {{0xea, 1, 24, .check_checksum = true, .max_counter = 0xffU, .expected_timestep = 10000U}, { 0 }, { 0 }}}, {.msg = {{0x175, 1, 24, .check_checksum = true, .max_counter = 0xffU, .expected_timestep = 10000U}, { 0 }, { 0 }}}, + {.msg = {{0x1cf, 1, 8, .check_checksum = false, .max_counter = 0xfU, .expected_timestep = 20000U}, { 0 }, { 0 }}}, }; #define HYUNDAI_HDA2_ADDR_CHECK_LEN (sizeof(hyundai_hda2_addr_checks) / sizeof(hyundai_hda2_addr_checks[0])) addr_checks hyundai_hda2_rx_checks = {hyundai_hda2_addr_checks, HYUNDAI_HDA2_ADDR_CHECK_LEN}; + uint16_t hyundai_hda2_crc_lut[256]; static uint8_t hyundai_hda2_get_counter(CANPacket_t *to_push) { - return GET_BYTE(to_push, 2); + uint8_t ret = 0; + if (GET_LEN(to_push) == 8U) { + ret = GET_BYTE(to_push, 1) >> 4; + } else { + ret = GET_BYTE(to_push, 2); + } + return ret; } static uint32_t hyundai_hda2_get_checksum(CANPacket_t *to_push) { @@ -73,16 +82,29 @@ static int hyundai_hda2_rx_hook(CANPacket_t *to_push) { if (valid && (bus == 1)) { + // driver torque if (addr == 0xea) { int torque_driver_new = ((GET_BYTE(to_push, 11) & 0x1fU) << 8U) | GET_BYTE(to_push, 10); torque_driver_new -= 4095; update_sample(&torque_driver, torque_driver_new); } + // cruise buttons + if (addr == 0x1cf) { + int cruise_button = GET_BYTE(to_push, 2) & 0x7U; + int main_button = GET_BIT(to_push, 19U); + + if ((cruise_button == HYUNDAI_BTN_RESUME) || (cruise_button == HYUNDAI_BTN_SET) || (cruise_button == HYUNDAI_BTN_CANCEL) || (main_button != 0)) { + hyundai_last_button_interaction = 0U; + } else { + hyundai_last_button_interaction = MIN(hyundai_last_button_interaction + 1U, HYUNDAI_PREV_BUTTON_SAMPLES); + } + } + + // cruise state if (addr == 0x175) { bool cruise_engaged = GET_BIT(to_push, 68U); - - if (cruise_engaged && !cruise_engaged_prev) { + if (cruise_engaged && !cruise_engaged_prev && (hyundai_last_button_interaction < HYUNDAI_PREV_BUTTON_SAMPLES)) { controls_allowed = 1; } @@ -92,14 +114,17 @@ static int hyundai_hda2_rx_hook(CANPacket_t *to_push) { cruise_engaged_prev = cruise_engaged; } + // gas press if (addr == 0x35) { gas_pressed = GET_BYTE(to_push, 5) != 0U; } + // brake press if (addr == 0x65) { brake_pressed = GET_BIT(to_push, 57U) != 0U; } + // vehicle moving if (addr == 0xa0) { uint32_t speed = 0; for (int i = 8; i < 15; i+=2) { @@ -189,7 +214,7 @@ static int hyundai_hda2_fwd_hook(int bus_num, CANPacket_t *to_fwd) { if (bus_num == 0) { bus_fwd = 2; } - if ((bus_num == 2) && (addr != 0x50)) { + if ((bus_num == 2) && (addr != 0x50) && (addr != 0x2a4)) { bus_fwd = 0; } @@ -199,6 +224,7 @@ static int hyundai_hda2_fwd_hook(int bus_num, CANPacket_t *to_fwd) { static const addr_checks* hyundai_hda2_init(uint16_t param) { UNUSED(param); gen_crc_lookup_table_16(0x1021, hyundai_hda2_crc_lut); + hyundai_last_button_interaction = HYUNDAI_PREV_BUTTON_SAMPLES; return &hyundai_hda2_rx_checks; } diff --git a/panda/board/safety/safety_mazda.h b/panda/board/safety/safety_mazda.h index ef04a15c7..bfc3a6dbe 100644 --- a/panda/board/safety/safety_mazda.h +++ b/panda/board/safety/safety_mazda.h @@ -12,12 +12,11 @@ #define MAZDA_AUX 1 #define MAZDA_CAM 2 -#define MAZDA_MAX_STEER 2048U +#define MAZDA_MAX_STEER 800U -// max delta torque allowed for real time checks -#define MAZDA_MAX_RT_DELTA 940 -// 250ms between real time checks -#define MAZDA_RT_INTERVAL 250000 +// the real time limit is 960/sec, a 20% buffer +#define MAZDA_MAX_RT_DELTA 300 // max delta torque allowed for real time checks +#define MAZDA_RT_INTERVAL 250000 // 250ms between real time checks #define MAZDA_MAX_RATE_UP 10 #define MAZDA_MAX_RATE_DOWN 25 #define MAZDA_DRIVER_TORQUE_ALLOWANCE 15 @@ -96,7 +95,7 @@ static int mazda_tx_hook(CANPacket_t *to_send, bool longitudinal_allowed) { // steer cmd checks if (addr == MAZDA_LKAS) { - int desired_torque = (((GET_BYTE(to_send, 0) & 0x0FU) << 8) | GET_BYTE(to_send, 1)) - MAZDA_MAX_STEER; + int desired_torque = (((GET_BYTE(to_send, 0) & 0x0FU) << 8) | GET_BYTE(to_send, 1)) - 2048U; bool violation = 0; uint32_t ts = microsecond_timer_get(); diff --git a/panda/board/safety_declarations.h b/panda/board/safety_declarations.h index 9c91b9d33..fc244fdc3 100644 --- a/panda/board/safety_declarations.h +++ b/panda/board/safety_declarations.h @@ -123,6 +123,10 @@ struct sample_t torque_meas; // last 6 motor torques produced by the eps struct sample_t torque_driver; // last 6 driver torques measured uint32_t ts_last = 0; +// state for controls_allowed timeout logic +bool heartbeat_engaged = false; // openpilot enabled, passed in heartbeat USB command +uint32_t heartbeat_engaged_mismatches = 0; // count of mismatches between heartbeat_engaged and controls_allowed + // for safety modes with angle steering control uint32_t ts_angle_last = 0; int desired_angle_last = 0; diff --git a/panda/python/__init__.py b/panda/python/__init__.py index c20da46a3..3b3ecd800 100644 --- a/panda/python/__init__.py +++ b/panda/python/__init__.py @@ -189,6 +189,7 @@ class Panda: FLAG_HONDA_ALT_BRAKE = 1 FLAG_HONDA_BOSCH_LONG = 2 FLAG_HONDA_NIDEC_ALT = 4 + FLAG_HONDA_RADARLESS = 8 FLAG_HYUNDAI_EV_GAS = 1 FLAG_HYUNDAI_HYBRID_GAS = 2 @@ -197,6 +198,8 @@ class Panda: FLAG_TESLA_POWERTRAIN = 1 FLAG_TESLA_LONG_CONTROL = 2 + FLAG_CHRYSLER_RAM_DT = 1 + def __init__(self, serial: Optional[str] = None, claim: bool = True): self._serial = serial self._handle = None diff --git a/rednose/.gitignore b/rednose/.gitignore new file mode 100644 index 000000000..d771ae601 --- /dev/null +++ b/rednose/.gitignore @@ -0,0 +1,5 @@ +# Cython intermediates +*_pyx.cpp +*_pyx.h +*_pyx_api.h +*.os diff --git a/rednose/SConscript b/rednose/SConscript index ca1b28423..1810e641a 100644 --- a/rednose/SConscript +++ b/rednose/SConscript @@ -11,18 +11,18 @@ ekf_sym_cc = env.Object("#rednose/helpers/ekf_sym.cc") common_ekf = "#rednose/helpers/common_ekf.cc" found = {} -for target, (command, combined_lib, extra_generated) in rednose_config['to_build'].items(): +for target, (command, *args) in rednose_config['to_build'].items(): if File(command).exists(): - found[target] = (command, combined_lib, extra_generated) + found[target] = (command, *args) lib_target = [common_ekf] -for target, (command, combined_lib, extra_generated) in found.items(): +for target, (command, combined_lib, extra_generated, deps) in found.items(): target_files = File([f'{generated_folder}/{target}.cpp', f'{generated_folder}/{target}.h']) extra_generated = [File(f'{generated_folder}/{x}') for x in extra_generated] command_file = File(command) env.Command(target_files + extra_generated, - [templates, command_file, sympy_helpers, ekf_sym], + [templates, command_file, sympy_helpers, ekf_sym] + deps, command_file.get_abspath() + " " + target + " " + Dir(generated_folder).get_abspath()) if combined_lib: diff --git a/rednose/helpers/ekf_sym_pyx.pyx b/rednose/helpers/ekf_sym_pyx.pyx index a5c0d5681..e7461285a 100644 --- a/rednose/helpers/ekf_sym_pyx.pyx +++ b/rednose/helpers/ekf_sym_pyx.pyx @@ -78,7 +78,7 @@ cdef np.ndarray[np.float64_t, ndim=1, mode="c"] vector_to_numpy(VectorXd arr): cdef double[:] mem_view = arr.data() return np.copy(np.asarray(mem_view, dtype=np.double, order="C")) -cdef class EKF_sym: +cdef class EKF_sym_pyx: cdef EKFSym* ekf def __cinit__(self, str gen_dir, str name, np.ndarray[np.float64_t, ndim=2] Q, np.ndarray[np.float64_t, ndim=1] x_initial, np.ndarray[np.float64_t, ndim=2] P_initial, int dim_main, diff --git a/release/check-dirty.sh b/release/check-dirty.sh new file mode 100755 index 000000000..9c6389f38 --- /dev/null +++ b/release/check-dirty.sh @@ -0,0 +1,11 @@ +#!/usr/bin/bash +set -e + +DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null && pwd)" +cd $DIR + +if [ ! -z "$(git status --porcelain)" ]; then + echo "Dirty working tree after build:" + git status --porcelain + exit 1 +fi diff --git a/release/files_common b/release/files_common index e5384e614..954726d96 100644 --- a/release/files_common +++ b/release/files_common @@ -35,7 +35,6 @@ common/filter_simple.py common/stat_live.py common/spinner.py common/text_window.py -common/SConscript common/kalman/.gitignore common/kalman/* @@ -58,20 +57,23 @@ common/api/__init__.py release/* +tools/__init__.py tools/lib/* tools/joystick/* - -selfdrive/version.py +tools/replay/*.cc +tools/replay/*.h selfdrive/__init__.py selfdrive/sentry.py -selfdrive/swaglog.py -selfdrive/logmessaged.py selfdrive/tombstoned.py selfdrive/updated.py selfdrive/rtshield.py selfdrive/statsd.py +system/logmessaged.py +system/swaglog.py +system/version.py + selfdrive/athena/__init__.py selfdrive/athena/athenad.py selfdrive/athena/manage_athenad.py @@ -101,9 +103,13 @@ selfdrive/car/interfaces.py selfdrive/car/vin.py selfdrive/car/disable_ecu.py selfdrive/car/fw_versions.py +selfdrive/car/ecu_addrs.py selfdrive/car/isotp_parallel_query.py selfdrive/car/tests/__init__.py selfdrive/car/tests/test_car_interfaces.py +selfdrive/car/torque_data/params.yaml +selfdrive/car/torque_data/substitute.yaml +selfdrive/car/torque_data/override.yaml selfdrive/car/body/*.py selfdrive/car/chrysler/*.py @@ -119,11 +125,19 @@ selfdrive/car/tesla/*.py selfdrive/car/toyota/*.py selfdrive/car/volkswagen/*.py -selfdrive/clocksd/.gitignore -selfdrive/clocksd/SConscript -selfdrive/clocksd/clocksd.cc +system/clocksd/.gitignore +system/clocksd/SConscript +system/clocksd/clocksd.cc -selfdrive/debug/*.py +selfdrive/debug/can_printer.py +selfdrive/debug/check_freq.py +selfdrive/debug/dump.py +selfdrive/debug/filter_log_message.py +selfdrive/debug/get_fingerprint.py +selfdrive/debug/uiview.py + +selfdrive/debug/hyundai_enable_radar_points.py +selfdrive/debug/vw_mqb_config.py common/SConscript common/version.h @@ -146,15 +160,11 @@ common/modeldata.h common/mat.h common/timing.h -common/visionimg.cc -common/visionimg.h - common/gpio.cc common/gpio.h common/i2c.cc common/i2c.h - selfdrive/controls/__init__.py selfdrive/controls/controlsd.py selfdrive/controls/plannerd.py @@ -168,7 +178,6 @@ selfdrive/controls/lib/events.py selfdrive/controls/lib/lane_planner.py selfdrive/controls/lib/latcontrol_angle.py selfdrive/controls/lib/latcontrol_indi.py -selfdrive/controls/lib/latcontrol_lqr.py selfdrive/controls/lib/latcontrol_torque.py selfdrive/controls/lib/latcontrol_pid.py selfdrive/controls/lib/latcontrol.py @@ -186,21 +195,26 @@ selfdrive/controls/lib/longitudinal_mpc_lib/.gitignore selfdrive/controls/lib/lateral_mpc_lib/* selfdrive/controls/lib/longitudinal_mpc_lib/* -selfdrive/hardware/__init__.py -selfdrive/hardware/base.h -selfdrive/hardware/base.py -selfdrive/hardware/hw.h -selfdrive/hardware/tici/__init__.py -selfdrive/hardware/tici/hardware.h -selfdrive/hardware/tici/hardware.py -selfdrive/hardware/tici/pins.py -selfdrive/hardware/tici/agnos.py -selfdrive/hardware/tici/agnos.json -selfdrive/hardware/tici/amplifier.py -selfdrive/hardware/tici/updater -selfdrive/hardware/tici/iwlist.py -selfdrive/hardware/pc/__init__.py -selfdrive/hardware/pc/hardware.py +selfdrive/hardware + +system/__init__.py + +system/hardware/__init__.py +system/hardware/base.h +system/hardware/base.py +system/hardware/hw.h +system/hardware/tici/__init__.py +system/hardware/tici/hardware.h +system/hardware/tici/hardware.py +system/hardware/tici/pins.py +system/hardware/tici/agnos.py +system/hardware/tici/casync.py +system/hardware/tici/agnos.json +system/hardware/tici/amplifier.py +system/hardware/tici/updater +system/hardware/tici/iwlist.py +system/hardware/pc/__init__.py +system/hardware/pc/hardware.py selfdrive/locationd/__init__.py selfdrive/locationd/.gitignore @@ -213,26 +227,33 @@ selfdrive/locationd/generated/ubx.h selfdrive/locationd/generated/gps.cpp selfdrive/locationd/generated/gps.h +selfdrive/locationd/laikad.py +selfdrive/locationd/laikad_helpers.py selfdrive/locationd/locationd.h selfdrive/locationd/locationd.cc selfdrive/locationd/paramsd.py +selfdrive/locationd/models/__init__.py selfdrive/locationd/models/.gitignore -selfdrive/locationd/models/live_kf.py selfdrive/locationd/models/car_kf.py -selfdrive/locationd/models/constants.py +selfdrive/locationd/models/gnss_kf.py +selfdrive/locationd/models/live_kf.py selfdrive/locationd/models/live_kf.h selfdrive/locationd/models/live_kf.cc +selfdrive/locationd/models/constants.py +selfdrive/locationd/models/gnss_helpers.py selfdrive/locationd/calibrationd.py -selfdrive/logcatd/SConscript -selfdrive/logcatd/logcatd_systemd.cc +system/logcatd/.gitignore +system/logcatd/SConscript +system/logcatd/logcatd_systemd.cc -selfdrive/proclogd/SConscript -selfdrive/proclogd/main.cc -selfdrive/proclogd/proclog.cc -selfdrive/proclogd/proclog.h +system/proclogd/SConscript +system/proclogd/main.cc +system/proclogd/proclog.cc +system/proclogd/proclog.h +selfdrive/loggerd/.gitignore selfdrive/loggerd/SConscript selfdrive/loggerd/encoder/encoder.cc selfdrive/loggerd/encoder/encoder.h @@ -282,6 +303,7 @@ selfdrive/ui/soundd/*.cc selfdrive/ui/soundd/*.h selfdrive/ui/soundd/soundd selfdrive/ui/soundd/.gitignore +selfdrive/ui/translations/* selfdrive/ui/qt/*.cc selfdrive/ui/qt/*.h @@ -290,28 +312,27 @@ selfdrive/ui/qt/offroad/*.h selfdrive/ui/qt/offroad/*.qml selfdrive/ui/qt/widgets/*.cc selfdrive/ui/qt/widgets/*.h +selfdrive/ui/qt/maps/*.cc +selfdrive/ui/qt/maps/*.h -selfdrive/ui/replay/*.cc -selfdrive/ui/replay/*.h +system/camerad/SConscript +system/camerad/main.cc -selfdrive/camerad/SConscript -selfdrive/camerad/main.cc +system/camerad/snapshot/* +system/camerad/include/* +system/camerad/cameras/camera_common.h +system/camerad/cameras/camera_common.cc +system/camerad/cameras/sensor2_i2c.h -selfdrive/camerad/snapshot/* -selfdrive/camerad/include/* -selfdrive/camerad/cameras/camera_common.h -selfdrive/camerad/cameras/camera_common.cc -selfdrive/camerad/cameras/sensor2_i2c.h +system/camerad/transforms/rgb_to_yuv.cc +system/camerad/transforms/rgb_to_yuv.h +system/camerad/transforms/rgb_to_yuv.cl +system/camerad/transforms/rgb_to_yuv_test.cc -selfdrive/camerad/transforms/rgb_to_yuv.cc -selfdrive/camerad/transforms/rgb_to_yuv.h -selfdrive/camerad/transforms/rgb_to_yuv.cl -selfdrive/camerad/transforms/rgb_to_yuv_test.cc - -selfdrive/camerad/imgproc/conv.cl -selfdrive/camerad/imgproc/pool.cl -selfdrive/camerad/imgproc/utils.cc -selfdrive/camerad/imgproc/utils.h +system/camerad/imgproc/conv.cl +system/camerad/imgproc/pool.cl +system/camerad/imgproc/utils.cc +system/camerad/imgproc/utils.h selfdrive/manager/__init__.py selfdrive/manager/build.py @@ -322,6 +343,7 @@ selfdrive/manager/process.py selfdrive/manager/test/__init__.py selfdrive/manager/test/test_manager.py +selfdrive/modeld/__init__.py selfdrive/modeld/SConscript selfdrive/modeld/modeld.cc selfdrive/modeld/dmonitoringmodeld.cc @@ -363,6 +385,8 @@ selfdrive/modeld/runners/run.h selfdrive/monitoring/dmonitoringd.py selfdrive/monitoring/driver_monitor.py +selfdrive/navd/*.py + selfdrive/assets/.gitignore selfdrive/assets/assets.qrc selfdrive/assets/*.png @@ -406,7 +430,9 @@ scripts/stop_updater.sh pyextra/.gitignore pyextra/acados_template/** +rednose/.gitignore rednose/** +laika/** cereal/.gitignore cereal/__init__.py @@ -465,14 +491,14 @@ opendbc/can/parser_pyx.pyx opendbc/comma_body.dbc -opendbc/chrysler_pacifica_2017_hybrid.dbc +opendbc/chrysler_ram_dt_generated.dbc +opendbc/chrysler_pacifica_2017_hybrid_generated.dbc opendbc/chrysler_pacifica_2017_hybrid_private_fusion.dbc opendbc/gm_global_a_powertrain_generated.dbc opendbc/gm_global_a_object.dbc opendbc/gm_global_a_chassis.dbc -opendbc/ford_fusion_2018_adas.dbc opendbc/ford_lincoln_base_pt.dbc opendbc/honda_accord_2018_can_generated.dbc @@ -490,7 +516,9 @@ opendbc/honda_odyssey_exl_2018_generated.dbc opendbc/honda_odyssey_extreme_edition_2018_china_can_generated.dbc opendbc/honda_insight_ex_2019_can_generated.dbc opendbc/acura_ilx_2016_nidec.dbc +opendbc/honda_civic_ex_2022_can_generated.dbc +opendbc/kia_ev6.dbc opendbc/hyundai_kia_generic.dbc opendbc/hyundai_kia_mando_front_radar.dbc diff --git a/release/files_pc b/release/files_pc index e401badb8..01ecae432 100644 --- a/release/files_pc +++ b/release/files_pc @@ -2,8 +2,6 @@ selfdrive/modeld/runners/onnx* third_party/mapbox-gl-native-qt/x86_64/*.so -third_party/qt-plugins/x86_64/geoservices/*.so - third_party/libyuv/x64/** third_party/snpe/x86_64/** third_party/snpe/x86_64-linux-clang/** diff --git a/release/files_tici b/release/files_tici index 75abc13ab..f8c0a2795 100644 --- a/release/files_tici +++ b/release/files_tici @@ -2,22 +2,14 @@ third_party/snpe/larch64** third_party/snpe/aarch64-ubuntu-gcc7.5/* third_party/mapbox-gl-native-qt/include/* -selfdrive/timezoned.py +system/timezoned.py selfdrive/assets/navigation/* selfdrive/assets/training_wide/* -selfdrive/camerad/cameras/camera_qcom2.cc -selfdrive/camerad/cameras/camera_qcom2.h -selfdrive/camerad/cameras/real_debayer.cl +system/camerad/cameras/camera_qcom2.cc +system/camerad/cameras/camera_qcom2.h +system/camerad/cameras/real_debayer.cl selfdrive/ui/qt/spinner_larch64 selfdrive/ui/qt/text_larch64 -selfdrive/ui/qt/maps/*.cc -selfdrive/ui/qt/maps/*.h - -selfdrive/ui/navd/*.cc -selfdrive/ui/navd/*.h -selfdrive/ui/navd/navd -selfdrive/ui/navd/.gitignore - diff --git a/selfdrive/assets/fonts/opensans_bold.ttf b/selfdrive/assets/fonts/opensans_bold.ttf deleted file mode 100644 index 7b5294560..000000000 Binary files a/selfdrive/assets/fonts/opensans_bold.ttf and /dev/null differ diff --git a/selfdrive/assets/fonts/opensans_regular.ttf b/selfdrive/assets/fonts/opensans_regular.ttf deleted file mode 100644 index 2e31d0242..000000000 Binary files a/selfdrive/assets/fonts/opensans_regular.ttf and /dev/null differ diff --git a/selfdrive/assets/fonts/opensans_semibold.ttf b/selfdrive/assets/fonts/opensans_semibold.ttf deleted file mode 100644 index 99db86aa0..000000000 Binary files a/selfdrive/assets/fonts/opensans_semibold.ttf and /dev/null differ diff --git a/selfdrive/assets/navigation/direction_turn_left_inactive.png b/selfdrive/assets/navigation/direction_turn_left_inactive.png new file mode 100644 index 000000000..2946984ac Binary files /dev/null and b/selfdrive/assets/navigation/direction_turn_left_inactive.png differ diff --git a/selfdrive/assets/navigation/direction_turn_right_inactive.png b/selfdrive/assets/navigation/direction_turn_right_inactive.png new file mode 100644 index 000000000..7d327766a Binary files /dev/null and b/selfdrive/assets/navigation/direction_turn_right_inactive.png differ diff --git a/selfdrive/assets/navigation/direction_turn_straight_inactive.png b/selfdrive/assets/navigation/direction_turn_straight_inactive.png new file mode 100644 index 000000000..4c567966e Binary files /dev/null and b/selfdrive/assets/navigation/direction_turn_straight_inactive.png differ diff --git a/selfdrive/athena/athenad.py b/selfdrive/athena/athenad.py index f91752479..6ccd6c3de 100755 --- a/selfdrive/athena/athenad.py +++ b/selfdrive/athena/athenad.py @@ -32,12 +32,12 @@ from common.basedir import PERSIST from common.file_helpers import CallbackReader from common.params import Params from common.realtime import sec_since_boot, set_core_affinity -from selfdrive.hardware import HARDWARE, PC, TICI +from system.hardware import HARDWARE, PC, AGNOS from selfdrive.loggerd.config import ROOT from selfdrive.loggerd.xattr_cache import getxattr, setxattr from selfdrive.statsd import STATS_DIR -from selfdrive.swaglog import SWAGLOG_DIR, cloudlog -from selfdrive.version import get_commit, get_origin, get_short_branch, get_version +from system.swaglog import SWAGLOG_DIR, cloudlog +from system.version import get_commit, get_origin, get_short_branch, get_version ATHENA_HOST = os.getenv('ATHENA_HOST', 'wss://athena.comma.ai') HANDLER_THREADS = int(os.getenv('HANDLER_THREADS', "4")) @@ -364,6 +364,11 @@ def uploadFilesToUrls(files_data): failed.append(fn) continue + # Skip item if already in queue + url = file['url'].split('?')[0] + if any(url == item['url'].split('?')[0] for item in listUploadQueue()): + continue + item = UploadItem( path=path, url=file['url'], @@ -413,8 +418,8 @@ def primeActivated(activated): @dispatcher.add_method def setBandwithLimit(upload_speed_kbps, download_speed_kbps): - if not TICI: - return {"success": 0, "error": "only supported on comma three"} + if not AGNOS: + return {"success": 0, "error": "only supported on AGNOS"} try: HARDWARE.set_bandwidth_limit(upload_speed_kbps, download_speed_kbps) @@ -493,7 +498,7 @@ def getNetworks(): @dispatcher.add_method def takeSnapshot(): - from selfdrive.camerad.snapshot.snapshot import jpeg_write, snapshot + from system.camerad.snapshot.snapshot import jpeg_write, snapshot ret = snapshot() if ret is not None: def b64jpeg(x): @@ -725,7 +730,6 @@ def main(): enable_multithread=True, timeout=30.0) cloudlog.event("athenad.main.connected_ws", ws_uri=ws_uri) - params.delete("PrimeRedirected") conn_retries = 0 cur_upload_items.clear() @@ -735,22 +739,13 @@ def main(): break except (ConnectionError, TimeoutError, WebSocketException): conn_retries += 1 - params.delete("PrimeRedirected") params.delete("LastAthenaPingTime") except socket.timeout: - try: - r = requests.get("http://api.commadotai.com/v1/me", allow_redirects=False, - headers={"User-Agent": f"openpilot-{get_version()}"}, timeout=15.0) - if r.status_code == 302 and r.headers['Location'].startswith("http://u.web2go.com"): - params.put_bool("PrimeRedirected", True) - except Exception: - cloudlog.exception("athenad.socket_timeout.exception") params.delete("LastAthenaPingTime") except Exception: cloudlog.exception("athenad.main.exception") conn_retries += 1 - params.delete("PrimeRedirected") params.delete("LastAthenaPingTime") time.sleep(backoff(conn_retries)) diff --git a/selfdrive/athena/manage_athenad.py b/selfdrive/athena/manage_athenad.py index 58ad58310..6bbb03a79 100755 --- a/selfdrive/athena/manage_athenad.py +++ b/selfdrive/athena/manage_athenad.py @@ -5,8 +5,8 @@ from multiprocessing import Process from common.params import Params from selfdrive.manager.process import launcher -from selfdrive.swaglog import cloudlog -from selfdrive.version import get_version, is_dirty +from system.swaglog import cloudlog +from system.version import get_version, is_dirty ATHENA_MGR_PID_PARAM = "AthenadPid" diff --git a/selfdrive/athena/registration.py b/selfdrive/athena/registration.py index 274893436..32bc92059 100755 --- a/selfdrive/athena/registration.py +++ b/selfdrive/athena/registration.py @@ -11,8 +11,8 @@ from common.params import Params from common.spinner import Spinner from common.basedir import PERSIST from selfdrive.controls.lib.alertmanager import set_offroad_alert -from selfdrive.hardware import HARDWARE, PC -from selfdrive.swaglog import cloudlog +from system.hardware import HARDWARE, PC +from system.swaglog import cloudlog UNREGISTERED_DONGLE_ID = "UnregisteredDevice" diff --git a/selfdrive/boardd/boardd.cc b/selfdrive/boardd/boardd.cc index 2622f7eba..f47b5936b 100644 --- a/selfdrive/boardd/boardd.cc +++ b/selfdrive/boardd/boardd.cc @@ -27,7 +27,7 @@ #include "common/swaglog.h" #include "common/timing.h" #include "common/util.h" -#include "selfdrive/hardware/hw.h" +#include "system/hardware/hw.h" #include "selfdrive/boardd/pigeon.h" @@ -405,17 +405,12 @@ void send_peripheral_state(PubMaster *pm, Panda *panda) { auto ps = evt.initPeripheralState(); ps.setPandaType(panda->hw_type); - if (Hardware::TICI()) { - double read_time = millis_since_boot(); - ps.setVoltage(std::atoi(util::read_file("/sys/class/hwmon/hwmon1/in1_input").c_str())); - ps.setCurrent(std::atoi(util::read_file("/sys/class/hwmon/hwmon1/curr1_input").c_str())); - read_time = millis_since_boot() - read_time; - if (read_time > 50) { - LOGW("reading hwmon took %lfms", read_time); - } - } else { - ps.setVoltage(pandaState.voltage_pkt); - ps.setCurrent(pandaState.current_pkt); + double read_time = millis_since_boot(); + ps.setVoltage(Hardware::get_voltage()); + ps.setCurrent(Hardware::get_current()); + read_time = millis_since_boot() - read_time; + if (read_time > 50) { + LOGW("reading hwmon took %lfms", read_time); } uint16_t fan_speed_rpm = panda->get_fan_speed(); @@ -534,9 +529,7 @@ void peripheral_control_thread(Panda *panda) { int cur_integ_lines = event.getDriverCameraState().getIntegLines(); float cur_gain = event.getDriverCameraState().getGain(); - if (Hardware::TICI()) { - cur_integ_lines = integ_lines_filter.update(cur_integ_lines * cur_gain); - } + cur_integ_lines = integ_lines_filter.update(cur_integ_lines * cur_gain); last_front_frame_t = event.getLogMonoTime(); if (cur_integ_lines <= CUTOFF_IL) { diff --git a/selfdrive/boardd/main.cc b/selfdrive/boardd/main.cc index b151832b7..cb17a584b 100644 --- a/selfdrive/boardd/main.cc +++ b/selfdrive/boardd/main.cc @@ -3,7 +3,7 @@ #include "selfdrive/boardd/boardd.h" #include "common/swaglog.h" #include "common/util.h" -#include "selfdrive/hardware/hw.h" +#include "system/hardware/hw.h" int main(int argc, char *argv[]) { LOGW("starting boardd"); @@ -12,7 +12,7 @@ int main(int argc, char *argv[]) { int err; err = util::set_realtime_priority(54); assert(err == 0); - err = util::set_core_affinity({Hardware::TICI() ? 4 : 3}); + err = util::set_core_affinity({4}); assert(err == 0); } diff --git a/selfdrive/boardd/pandad.py b/selfdrive/boardd/pandad.py index 306d764e5..54a28a678 100755 --- a/selfdrive/boardd/pandad.py +++ b/selfdrive/boardd/pandad.py @@ -10,8 +10,8 @@ from functools import cmp_to_key from panda import DEFAULT_FW_FN, DEFAULT_H7_FW_FN, MCU_TYPE_H7, Panda, PandaDFU from common.basedir import BASEDIR from common.params import Params -from selfdrive.hardware import HARDWARE -from selfdrive.swaglog import cloudlog +from system.hardware import HARDWARE +from system.swaglog import cloudlog def get_expected_signature(panda: Panda) -> bytes: diff --git a/selfdrive/car/__init__.py b/selfdrive/car/__init__.py index 7bb91c40f..c77e40daa 100644 --- a/selfdrive/car/__init__.py +++ b/selfdrive/car/__init__.py @@ -1,11 +1,41 @@ # functions common among cars +import capnp + from cereal import car from common.numpy_fast import clip -from typing import Dict +from typing import Dict, List # kg of standard extra cargo to count for drive, gas, etc... STD_CARGO_KG = 136. +ButtonType = car.CarState.ButtonEvent.Type +EventName = car.CarEvent.EventName + + +def create_button_event(cur_but: int, prev_but: int, buttons_dict: Dict[int, capnp.lib.capnp._EnumModule], + unpressed: int = 0) -> capnp.lib.capnp._DynamicStructBuilder: + if cur_but != unpressed: + be = car.CarState.ButtonEvent(pressed=True) + but = cur_but + else: + be = car.CarState.ButtonEvent(pressed=False) + but = prev_but + be.type = buttons_dict.get(but, ButtonType.unknown) + return be + + +def create_button_enable_events(buttonEvents: capnp.lib.capnp._DynamicListBuilder, pcm_cruise: bool = False) -> List[int]: + events = [] + for b in buttonEvents: + # do enable on both accel and decel buttons + if not pcm_cruise: + if b.type in (ButtonType.accelCruise, ButtonType.decelCruise) and not b.pressed: + events.append(EventName.buttonEnable) + # do disable on button down + if b.type == ButtonType.cancel and b.pressed: + events.append(EventName.buttonCancel) + return events + def gen_empty_fingerprint(): return {i: {} for i in range(0, 4)} diff --git a/selfdrive/car/body/carcontroller.py b/selfdrive/car/body/carcontroller.py index 5714445f6..c13acebac 100644 --- a/selfdrive/car/body/carcontroller.py +++ b/selfdrive/car/body/carcontroller.py @@ -1,8 +1,8 @@ import numpy as np from common.realtime import DT_CTRL -from selfdrive.car.body import bodycan from opendbc.can.packer import CANPacker +from selfdrive.car.body import bodycan from selfdrive.car.body.values import SPEED_FROM_RPM from selfdrive.controls.lib.pid import PIDController @@ -14,7 +14,7 @@ MAX_POS_INTEGRATOR = 0.2 # meters MAX_TURN_INTEGRATOR = 0.1 # meters -class CarController(): +class CarController: def __init__(self, dbc_name, CP, VM): self.frame = 0 self.packer = CANPacker(dbc_name) diff --git a/selfdrive/car/body/interface.py b/selfdrive/car/body/interface.py index 2b4420039..e85735b1a 100644 --- a/selfdrive/car/body/interface.py +++ b/selfdrive/car/body/interface.py @@ -17,8 +17,8 @@ class CarInterface(CarInterfaceBase): ret.safetyConfigs = [get_safety_config(car.CarParams.SafetyModel.body)] ret.minSteerSpeed = -math.inf + ret.maxLateralAccel = math.inf # TODO: set to a reasonable value ret.steerRatio = 0.5 - ret.steerRateCost = 0.5 ret.steerLimitTimer = 1.0 ret.steerActuatorDelay = 0. diff --git a/selfdrive/car/body/values.py b/selfdrive/car/body/values.py index a009b40bc..0caf93b61 100644 --- a/selfdrive/car/body/values.py +++ b/selfdrive/car/body/values.py @@ -18,7 +18,7 @@ class CAR: BODY = "COMMA BODY" CAR_INFO: Dict[str, CarInfo] = { - CAR.BODY: CarInfo("comma body", package="All", good_torque=True, harness=Harness.none), + CAR.BODY: CarInfo("comma body", package="All", harness=Harness.none), } FW_VERSIONS = { diff --git a/selfdrive/car/car_helpers.py b/selfdrive/car/car_helpers.py index 46991bb4d..1a9a5f50f 100644 --- a/selfdrive/car/car_helpers.py +++ b/selfdrive/car/car_helpers.py @@ -1,17 +1,18 @@ import os -from typing import Any, Dict, List +from typing import Dict, List +from cereal import car from common.params import Params from common.basedir import BASEDIR -from selfdrive.version import is_comma_remote, is_tested_branch +from system.version import is_comma_remote, is_tested_branch +from selfdrive.car.interfaces import get_interface_attr from selfdrive.car.fingerprints import eliminate_incompatible_cars, all_legacy_fingerprint_cars from selfdrive.car.vin import get_vin, VIN_UNKNOWN -from selfdrive.car.fw_versions import get_fw_versions, match_fw_to_car -from selfdrive.swaglog import cloudlog +from selfdrive.car.fw_versions import get_fw_versions_ordered, match_fw_to_car, get_present_ecus +from system.swaglog import cloudlog import cereal.messaging as messaging from selfdrive.car import gen_empty_fingerprint -from cereal import car EventName = car.CarEvent.EventName @@ -59,19 +60,6 @@ def load_interfaces(brand_names): return ret -def get_interface_attr(attr: str) -> Dict[str, Any]: - # returns given attribute from each interface - brand_names = {} - for car_folder in sorted([x[0] for x in os.walk(BASEDIR + '/selfdrive/car')]): - try: - brand_name = car_folder.split('/')[-1] - attr_data = getattr(__import__(f'selfdrive.car.{brand_name}.values', fromlist=[attr]), attr, None) - brand_names[brand_name] = attr_data - except (ImportError, OSError): - pass - return brand_names - - def _get_interface_names() -> Dict[str, List[str]]: # returns a dict of brand name and its respective models brand_names = {} @@ -91,6 +79,7 @@ interfaces = load_interfaces(interface_names) def fingerprint(logcan, sendcan): fixed_fingerprint = os.environ.get('FINGERPRINT', "") skip_fw_query = os.environ.get('SKIP_FW_QUERY', False) + ecu_rx_addrs = set() if not fixed_fingerprint and not skip_fw_query: # Vin query only reliably works thorugh OBDII @@ -104,28 +93,35 @@ def fingerprint(logcan, sendcan): if cached_params is not None and len(cached_params.carFw) > 0 and cached_params.carVin is not VIN_UNKNOWN: cloudlog.warning("Using cached CarParams") - vin = cached_params.carVin + vin, vin_rx_addr = cached_params.carVin, 0 car_fw = list(cached_params.carFw) else: cloudlog.warning("Getting VIN & FW versions") - _, vin = get_vin(logcan, sendcan, bus) - car_fw = get_fw_versions(logcan, sendcan) + _, vin_rx_addr, vin = get_vin(logcan, sendcan, bus) + ecu_rx_addrs = get_present_ecus(logcan, sendcan) + car_fw = get_fw_versions_ordered(logcan, sendcan, ecu_rx_addrs) exact_fw_match, fw_candidates = match_fw_to_car(car_fw) else: - vin = VIN_UNKNOWN + vin, vin_rx_addr = VIN_UNKNOWN, 0 exact_fw_match, fw_candidates, car_fw = True, set(), [] + if len(vin) != 17: + cloudlog.event("Malformed VIN", vin=vin, error=True) + vin = VIN_UNKNOWN cloudlog.warning("VIN %s", vin) Params().put("CarVin", vin) finger = gen_empty_fingerprint() candidate_cars = {i: all_legacy_fingerprint_cars() for i in [0, 1]} # attempt fingerprint on both bus 0 and 1 frame = 0 - frame_fingerprint = 10 # 0.1s + frame_fingerprint = 100 # 1s car_fingerprint = None done = False + # drain CAN socket so we always get the latest messages + messaging.drain_sock_raw(logcan) + while not done: a = get_one_can(logcan) @@ -169,8 +165,8 @@ def fingerprint(logcan, sendcan): car_fingerprint = fixed_fingerprint source = car.CarParams.FingerprintSource.fixed - cloudlog.event("fingerprinted", car_fingerprint=car_fingerprint, - source=source, fuzzy=not exact_match, fw_count=len(car_fw)) + cloudlog.event("fingerprinted", car_fingerprint=car_fingerprint, source=source, fuzzy=not exact_match, + fw_count=len(car_fw), ecu_responses=list(ecu_rx_addrs), vin_rx_addr=vin_rx_addr, error=True) return car_fingerprint, finger, vin, car_fw, source, exact_match diff --git a/selfdrive/car/chrysler/carcontroller.py b/selfdrive/car/chrysler/carcontroller.py index 3d6295d77..6d158e7cd 100644 --- a/selfdrive/car/chrysler/carcontroller.py +++ b/selfdrive/car/chrysler/carcontroller.py @@ -1,8 +1,8 @@ -from cereal import car from opendbc.can.packer import CANPacker +from common.realtime import DT_CTRL from selfdrive.car import apply_toyota_steer_torque_limits -from selfdrive.car.chrysler.chryslercan import create_lkas_hud, create_lkas_command, create_wheel_buttons -from selfdrive.car.chrysler.values import CAR, CarControllerParams +from selfdrive.car.chrysler.chryslercan import create_lkas_hud, create_lkas_command, create_cruise_buttons +from selfdrive.car.chrysler.values import CAR, RAM_CARS, CarControllerParams class CarController: @@ -10,61 +10,75 @@ class CarController: self.CP = CP self.apply_steer_last = 0 self.frame = 0 - self.prev_lkas_frame = -1 - self.hud_count = 0 - self.car_fingerprint = CP.carFingerprint - self.gone_fast_yet = False self.steer_rate_limited = False + self.hud_count = 0 + self.last_lkas_falling_edge = 0 + self.lkas_control_bit_prev = False + self.last_button_frame = 0 + self.packer = CANPacker(dbc_name) + self.params = CarControllerParams(CP) def update(self, CC, CS): - # this seems needed to avoid steering faults and to force the sync with the EPS counter - if self.prev_lkas_frame == CS.lkas_counter: - return car.CarControl.Actuators.new_message(), [] - - actuators = CC.actuators - - # steer torque - new_steer = int(round(actuators.steer * CarControllerParams.STEER_MAX)) - apply_steer = apply_toyota_steer_torque_limits(new_steer, self.apply_steer_last, - CS.out.steeringTorqueEps, CarControllerParams) - self.steer_rate_limited = new_steer != apply_steer - - moving_fast = CS.out.vEgo > self.CP.minSteerSpeed # for status message - if CS.out.vEgo > (self.CP.minSteerSpeed - 0.5): # for command high bit - self.gone_fast_yet = True - elif self.car_fingerprint in (CAR.PACIFICA_2019_HYBRID, CAR.PACIFICA_2020, CAR.JEEP_CHEROKEE_2019): - if CS.out.vEgo < (self.CP.minSteerSpeed - 3.0): - self.gone_fast_yet = False # < 14.5m/s stock turns off this bit, but fine down to 13.5 - lkas_active = moving_fast and CC.enabled - - if not lkas_active: - apply_steer = 0 - - self.apply_steer_last = apply_steer - can_sends = [] + # TODO: can we make this more sane? why is it different for all the cars? + lkas_control_bit = self.lkas_control_bit_prev + if CS.out.vEgo > self.CP.minSteerSpeed: + lkas_control_bit = True + elif self.CP.carFingerprint in (CAR.PACIFICA_2019_HYBRID, CAR.PACIFICA_2020, CAR.JEEP_CHEROKEE_2019): + if CS.out.vEgo < (self.CP.minSteerSpeed - 3.0): + lkas_control_bit = False + elif self.CP.carFingerprint in RAM_CARS: + if CS.out.vEgo < (self.CP.minSteerSpeed - 0.5): + lkas_control_bit = False + + # EPS faults if LKAS re-enables too quickly + lkas_control_bit = lkas_control_bit and (self.frame - self.last_lkas_falling_edge > 200) + lkas_active = CC.latActive and self.lkas_control_bit_prev + # *** control msgs *** - if CC.cruiseControl.cancel: - can_sends.append(create_wheel_buttons(self.packer, CS.button_counter + 1, cancel=True)) + # cruise buttons + if (self.frame - self.last_button_frame)*DT_CTRL > 0.05: + das_bus = 2 if self.CP.carFingerprint in RAM_CARS else 0 - # LKAS_HEARTBIT is forwarded by Panda so no need to send it here. - # frame is 100Hz (0.01s period) - if self.frame % 25 == 0: # 0.25s period + # ACC cancellation + if CC.cruiseControl.cancel: + self.last_button_frame = self.frame + can_sends.append(create_cruise_buttons(self.packer, CS.button_counter + 1, das_bus, cancel=True)) + + # ACC resume from standstill + elif CC.cruiseControl.resume: + self.last_button_frame = self.frame + can_sends.append(create_cruise_buttons(self.packer, CS.button_counter + 1, das_bus, resume=True)) + + # HUD alerts + if self.frame % 25 == 0: if CS.lkas_car_model != -1: - can_sends.append(create_lkas_hud(self.packer, CS.out.gearShifter, lkas_active, - CC.hudControl.visualAlert, self.hud_count, CS.lkas_car_model)) + can_sends.append(create_lkas_hud(self.packer, self.CP, lkas_active, CC.hudControl.visualAlert, self.hud_count, CS.lkas_car_model, CS.auto_high_beam)) self.hud_count += 1 - can_sends.append(create_lkas_command(self.packer, int(apply_steer), self.gone_fast_yet, CS.lkas_counter)) + # steering + if self.frame % 2 == 0: + # steer torque + new_steer = int(round(CC.actuators.steer * self.params.STEER_MAX)) + apply_steer = apply_toyota_steer_torque_limits(new_steer, self.apply_steer_last, CS.out.steeringTorqueEps, self.params) + if not lkas_active: + apply_steer = 0 + self.steer_rate_limited = new_steer != apply_steer + self.apply_steer_last = apply_steer + + idx = self.frame // 2 + can_sends.append(create_lkas_command(self.packer, self.CP, int(apply_steer), lkas_control_bit, idx)) self.frame += 1 - self.prev_lkas_frame = CS.lkas_counter + if not lkas_control_bit and self.lkas_control_bit_prev: + self.last_lkas_falling_edge = self.frame + self.lkas_control_bit_prev = lkas_control_bit - new_actuators = actuators.copy() - new_actuators.steer = apply_steer / CarControllerParams.STEER_MAX + new_actuators = CC.actuators.copy() + new_actuators.steer = self.apply_steer_last / self.params.STEER_MAX return new_actuators, can_sends diff --git a/selfdrive/car/chrysler/carstate.py b/selfdrive/car/chrysler/carstate.py index 8ddf1c868..47b28f9e0 100644 --- a/selfdrive/car/chrysler/carstate.py +++ b/selfdrive/car/chrysler/carstate.py @@ -3,150 +3,202 @@ from common.conversions import Conversions as CV from opendbc.can.parser import CANParser from opendbc.can.can_define import CANDefine from selfdrive.car.interfaces import CarStateBase -from selfdrive.car.chrysler.values import DBC, STEER_THRESHOLD +from selfdrive.car.chrysler.values import DBC, STEER_THRESHOLD, RAM_CARS class CarState(CarStateBase): def __init__(self, CP): super().__init__(CP) + self.CP = CP can_define = CANDefine(DBC[CP.carFingerprint]["pt"]) - self.shifter_values = can_define.dv["GEAR"]["PRNDL"] + + self.auto_high_beam = 0 + self.button_counter = 0 + self.lkas_car_model = -1 + + if CP.carFingerprint in RAM_CARS: + self.shifter_values = can_define.dv["Transmission_Status"]["Gear_State"] + else: + self.shifter_values = can_define.dv["GEAR"]["PRNDL"] def update(self, cp, cp_cam): ret = car.CarState.new_message() - self.frame = int(cp.vl["EPS_STATUS"]["COUNTER"]) + # lock info + ret.doorOpen = any([cp.vl["BCM_1"]["DOOR_OPEN_FL"], + cp.vl["BCM_1"]["DOOR_OPEN_FR"], + cp.vl["BCM_1"]["DOOR_OPEN_RL"], + cp.vl["BCM_1"]["DOOR_OPEN_RR"]]) + ret.seatbeltUnlatched = cp.vl["ORC_1"]["SEATBELT_DRIVER_UNLATCHED"] == 1 - ret.doorOpen = any([cp.vl["DOORS"]["DOOR_OPEN_FL"], - cp.vl["DOORS"]["DOOR_OPEN_FR"], - cp.vl["DOORS"]["DOOR_OPEN_RL"], - cp.vl["DOORS"]["DOOR_OPEN_RR"]]) - ret.seatbeltUnlatched = cp.vl["SEATBELT_STATUS"]["SEATBELT_DRIVER_UNLATCHED"] == 1 - - ret.brakePressed = cp.vl["BRAKE_2"]["BRAKE_PRESSED_2"] == 5 # human-only + # brake pedal ret.brake = 0 - ret.gas = cp.vl["ACCEL_GAS_134"]["ACCEL_134"] + ret.brakePressed = cp.vl["ESP_1"]['Brake_Pedal_State'] == 1 # Physical brake pedal switch + + # gas pedal + ret.gas = cp.vl["ECM_5"]["Accelerator_Position"] ret.gasPressed = ret.gas > 1e-5 - ret.espDisabled = (cp.vl["TRACTION_BUTTON"]["TRACTION_OFF"] == 1) - - ret.wheelSpeeds = self.get_wheel_speeds( - cp.vl["WHEEL_SPEEDS"]["WHEEL_SPEED_FL"], - cp.vl["WHEEL_SPEEDS"]["WHEEL_SPEED_FR"], - cp.vl["WHEEL_SPEEDS"]["WHEEL_SPEED_RL"], - cp.vl["WHEEL_SPEEDS"]["WHEEL_SPEED_RR"], - unit=1, - ) - ret.vEgoRaw = (cp.vl["SPEED_1"]["SPEED_LEFT"] + cp.vl["SPEED_1"]["SPEED_RIGHT"]) / 2. + # car speed + if self.CP.carFingerprint in RAM_CARS: + ret.vEgoRaw = cp.vl["ESP_8"]["Vehicle_Speed"] * CV.KPH_TO_MS + ret.gearShifter = self.parse_gear_shifter(self.shifter_values.get(cp.vl["Transmission_Status"]["Gear_State"], None)) + else: + ret.vEgoRaw = (cp.vl["SPEED_1"]["SPEED_LEFT"] + cp.vl["SPEED_1"]["SPEED_RIGHT"]) / 2. + ret.gearShifter = self.parse_gear_shifter(self.shifter_values.get(cp.vl["GEAR"]["PRNDL"], None)) ret.vEgo, ret.aEgo = self.update_speed_kf(ret.vEgoRaw) ret.standstill = not ret.vEgoRaw > 0.001 + ret.wheelSpeeds = self.get_wheel_speeds( + cp.vl["ESP_6"]["WHEEL_SPEED_FL"], + cp.vl["ESP_6"]["WHEEL_SPEED_FR"], + cp.vl["ESP_6"]["WHEEL_SPEED_RL"], + cp.vl["ESP_6"]["WHEEL_SPEED_RR"], + unit=1, + ) + # button presses ret.leftBlinker = cp.vl["STEERING_LEVERS"]["TURN_SIGNALS"] == 1 ret.rightBlinker = cp.vl["STEERING_LEVERS"]["TURN_SIGNALS"] == 2 + ret.genericToggle = cp.vl["STEERING_LEVERS"]["HIGH_BEAM_PRESSED"] == 1 + + # steering wheel ret.steeringAngleDeg = cp.vl["STEERING"]["STEER_ANGLE"] ret.steeringRateDeg = cp.vl["STEERING"]["STEERING_RATE"] - ret.gearShifter = self.parse_gear_shifter(self.shifter_values.get(cp.vl["GEAR"]["PRNDL"], None)) - - ret.cruiseState.enabled = cp.vl["ACC_2"]["ACC_STATUS_2"] == 7 # ACC is green. - ret.cruiseState.available = ret.cruiseState.enabled # FIXME: for now same as enabled - ret.cruiseState.speed = cp.vl["DASHBOARD"]["ACC_SPEED_CONFIG_KPH"] * CV.KPH_TO_MS - # CRUISE_STATE is a three bit msg, 0 is off, 1 and 2 are Non-ACC mode, 3 and 4 are ACC mode, find if there are other states too - ret.cruiseState.nonAdaptive = cp.vl["DASHBOARD"]["CRUISE_STATE"] in (1, 2) - ret.accFaulted = cp.vl["ACC_2"]["ACC_FAULTED"] != 0 - - ret.steeringTorque = cp.vl["EPS_STATUS"]["TORQUE_DRIVER"] - ret.steeringTorqueEps = cp.vl["EPS_STATUS"]["TORQUE_MOTOR"] + ret.steeringTorque = cp.vl["EPS_2"]["COLUMN_TORQUE"] + ret.steeringTorqueEps = cp.vl["EPS_2"]["EPS_TORQUE_MOTOR"] ret.steeringPressed = abs(ret.steeringTorque) > STEER_THRESHOLD - steer_state = cp.vl["EPS_STATUS"]["LKAS_STATE"] - ret.steerFaultPermanent = steer_state == 4 or (steer_state == 0 and ret.vEgo > self.CP.minSteerSpeed) - ret.genericToggle = bool(cp.vl["STEERING_LEVERS"]["HIGH_BEAM_FLASH"]) + # cruise state + cp_cruise = cp_cam if self.CP.carFingerprint in RAM_CARS else cp + ret.cruiseState.available = cp_cruise.vl["DAS_3"]["ACC_AVAILABLE"] == 1 + ret.cruiseState.enabled = cp_cruise.vl["DAS_3"]["ACC_ACTIVE"] == 1 + ret.cruiseState.speed = cp_cruise.vl["DAS_4"]["ACC_SET_SPEED_KPH"] * CV.KPH_TO_MS + ret.cruiseState.nonAdaptive = cp_cruise.vl["DAS_4"]["ACC_STATE"] in (1, 2) # 1 NormalCCOn and 2 NormalCCSet + ret.cruiseState.standstill = cp_cruise.vl["DAS_3"]["ACC_STANDSTILL"] == 1 + ret.accFaulted = cp_cruise.vl["DAS_3"]["ACC_FAULTED"] != 0 + + if self.CP.carFingerprint in RAM_CARS: + self.auto_high_beam = cp_cam.vl["DAS_6"]['AUTO_HIGH_BEAM_ON'] # Auto High Beam isn't Located in this message on chrysler or jeep currently located in 729 message + ret.steerFaultTemporary = cp.vl["EPS_3"]["DASM_FAULT"] == 1 + else: + ret.steerFaultPermanent = cp.vl["EPS_2"]["LKAS_STATE"] == 4 + + # blindspot sensors if self.CP.enableBsm: - ret.leftBlindspot = cp.vl["BLIND_SPOT_WARNINGS"]["BLIND_SPOT_LEFT"] == 1 - ret.rightBlindspot = cp.vl["BLIND_SPOT_WARNINGS"]["BLIND_SPOT_RIGHT"] == 1 + ret.leftBlindspot = cp.vl["BSM_1"]["LEFT_STATUS"] == 1 + ret.rightBlindspot = cp.vl["BSM_1"]["RIGHT_STATUS"] == 1 - self.lkas_counter = cp_cam.vl["LKAS_COMMAND"]["COUNTER"] - self.lkas_car_model = cp_cam.vl["LKAS_HUD"]["CAR_MODEL"] - self.lkas_status_ok = cp_cam.vl["LKAS_HEARTBIT"]["LKAS_STATUS_OK"] - self.button_counter = cp.vl["WHEEL_BUTTONS"]["COUNTER"] + self.lkas_car_model = cp_cam.vl["DAS_6"]["CAR_MODEL"] + self.button_counter = cp.vl["CRUISE_BUTTONS"]["COUNTER"] return ret + @staticmethod + def get_cruise_signals(): + signals = [ + ("ACC_AVAILABLE", "DAS_3"), + ("ACC_ACTIVE", "DAS_3"), + ("ACC_FAULTED", "DAS_3"), + ("ACC_STANDSTILL", "DAS_3"), + ("COUNTER", "DAS_3"), + ("ACC_SET_SPEED_KPH", "DAS_4"), + ("ACC_STATE", "DAS_4"), + ] + checks = [ + ("DAS_3", 50), + ("DAS_4", 50), + ] + return signals, checks + @staticmethod def get_can_parser(CP): signals = [ # sig_name, sig_address - ("PRNDL", "GEAR"), - ("DOOR_OPEN_FL", "DOORS"), - ("DOOR_OPEN_FR", "DOORS"), - ("DOOR_OPEN_RL", "DOORS"), - ("DOOR_OPEN_RR", "DOORS"), - ("BRAKE_PRESSED_2", "BRAKE_2"), - ("ACCEL_134", "ACCEL_GAS_134"), - ("SPEED_LEFT", "SPEED_1"), - ("SPEED_RIGHT", "SPEED_1"), - ("WHEEL_SPEED_FL", "WHEEL_SPEEDS"), - ("WHEEL_SPEED_RR", "WHEEL_SPEEDS"), - ("WHEEL_SPEED_RL", "WHEEL_SPEEDS"), - ("WHEEL_SPEED_FR", "WHEEL_SPEEDS"), + ("DOOR_OPEN_FL", "BCM_1"), + ("DOOR_OPEN_FR", "BCM_1"), + ("DOOR_OPEN_RL", "BCM_1"), + ("DOOR_OPEN_RR", "BCM_1"), + ("Brake_Pedal_State", "ESP_1"), + ("Accelerator_Position", "ECM_5"), + ("WHEEL_SPEED_FL", "ESP_6"), + ("WHEEL_SPEED_RR", "ESP_6"), + ("WHEEL_SPEED_RL", "ESP_6"), + ("WHEEL_SPEED_FR", "ESP_6"), ("STEER_ANGLE", "STEERING"), ("STEERING_RATE", "STEERING"), ("TURN_SIGNALS", "STEERING_LEVERS"), - ("ACC_STATUS_2", "ACC_2"), - ("ACC_FAULTED", "ACC_2"), - ("HIGH_BEAM_FLASH", "STEERING_LEVERS"), - ("ACC_SPEED_CONFIG_KPH", "DASHBOARD"), - ("CRUISE_STATE", "DASHBOARD"), - ("TORQUE_DRIVER", "EPS_STATUS"), - ("TORQUE_MOTOR", "EPS_STATUS"), - ("LKAS_STATE", "EPS_STATUS"), - ("COUNTER", "EPS_STATUS",), - ("TRACTION_OFF", "TRACTION_BUTTON"), - ("SEATBELT_DRIVER_UNLATCHED", "SEATBELT_STATUS"), - ("COUNTER", "WHEEL_BUTTONS"), + ("HIGH_BEAM_PRESSED", "STEERING_LEVERS"), + ("SEATBELT_DRIVER_UNLATCHED", "ORC_1"), + ("COUNTER", "EPS_2",), + ("COLUMN_TORQUE", "EPS_2"), + ("EPS_TORQUE_MOTOR", "EPS_2"), + ("LKAS_STATE", "EPS_2"), + ("COUNTER", "CRUISE_BUTTONS"), ] checks = [ # sig_address, frequency - ("BRAKE_2", 50), - ("EPS_STATUS", 100), - ("SPEED_1", 100), - ("WHEEL_SPEEDS", 50), + ("ESP_1", 50), + ("EPS_2", 100), + ("ESP_6", 50), ("STEERING", 100), - ("ACC_2", 50), - ("GEAR", 50), - ("ACCEL_GAS_134", 50), - ("WHEEL_BUTTONS", 50), - ("DASHBOARD", 15), + ("ECM_5", 50), + ("CRUISE_BUTTONS", 50), ("STEERING_LEVERS", 10), - ("SEATBELT_STATUS", 2), - ("DOORS", 1), - ("TRACTION_BUTTON", 1), + ("ORC_1", 2), + ("BCM_1", 1), ] if CP.enableBsm: signals += [ - ("BLIND_SPOT_RIGHT", "BLIND_SPOT_WARNINGS"), - ("BLIND_SPOT_LEFT", "BLIND_SPOT_WARNINGS"), + ("RIGHT_STATUS", "BSM_1"), + ("LEFT_STATUS", "BSM_1"), ] - checks.append(("BLIND_SPOT_WARNINGS", 2)) + checks.append(("BSM_1", 2)) + + if CP.carFingerprint in RAM_CARS: + signals += [ + ("DASM_FAULT", "EPS_3"), + ("Vehicle_Speed", "ESP_8"), + ("Gear_State", "Transmission_Status"), + ] + checks += [ + ("ESP_8", 50), + ("EPS_3", 50), + ("Transmission_Status", 50), + ] + else: + signals += [ + ("PRNDL", "GEAR"), + ("SPEED_LEFT", "SPEED_1"), + ("SPEED_RIGHT", "SPEED_1"), + ] + checks += [ + ("GEAR", 50), + ("SPEED_1", 100), + ] + signals += CarState.get_cruise_signals()[0] + checks += CarState.get_cruise_signals()[1] return CANParser(DBC[CP.carFingerprint]["pt"], signals, checks, 0) @staticmethod def get_cam_can_parser(CP): signals = [ - # sig_name, sig_address - ("COUNTER", "LKAS_COMMAND"), - ("CAR_MODEL", "LKAS_HUD"), - ("LKAS_STATUS_OK", "LKAS_HEARTBIT") + # sig_name, sig_address, default + ("CAR_MODEL", "DAS_6"), ] checks = [ - ("LKAS_COMMAND", 100), - ("LKAS_HEARTBIT", 10), - ("LKAS_HUD", 4), + ("DAS_6", 4), ] + if CP.carFingerprint in RAM_CARS: + signals += [ + ("AUTO_HIGH_BEAM_ON", "DAS_6"), + ] + signals += CarState.get_cruise_signals()[0] + checks += CarState.get_cruise_signals()[1] + return CANParser(DBC[CP.carFingerprint]["pt"], signals, checks, 2) diff --git a/selfdrive/car/chrysler/chryslercan.py b/selfdrive/car/chrysler/chryslercan.py index 896a6b15d..1e26a6d27 100644 --- a/selfdrive/car/chrysler/chryslercan.py +++ b/selfdrive/car/chrysler/chryslercan.py @@ -1,57 +1,70 @@ from cereal import car -from selfdrive.car import make_can_msg - +from selfdrive.car.chrysler.values import RAM_CARS GearShifter = car.CarState.GearShifter VisualAlert = car.CarControl.HUDControl.VisualAlert -def create_lkas_hud(packer, gear, lkas_active, hud_alert, hud_count, lkas_car_model): - # LKAS_HUD 0x2a6 (678) Controls what lane-keeping icon is displayed. +def create_lkas_hud(packer, CP, lkas_active, hud_alert, hud_count, car_model, auto_high_beam): + # LKAS_HUD - Controls what lane-keeping icon is displayed - if hud_alert in (VisualAlert.steerRequired, VisualAlert.ldw): - msg = b'\x00\x00\x00\x03\x00\x00\x00\x00' - return make_can_msg(0x2a6, msg, 0) + # == Color == + # 0 hidden? + # 1 white + # 2 green + # 3 ldw - color = 1 # default values are for park or neutral in 2017 are 0 0, but trying 1 1 for 2019 - lines = 1 - alerts = 0 + # == Lines == + # 03 white Lines + # 04 grey lines + # 09 left lane close + # 0A right lane close + # 0B left Lane very close + # 0C right Lane very close + # 0D left cross cross + # 0E right lane cross + + # == Alerts == + # 7 Normal + # 6 lane departure place hands on wheel + + color = 2 if lkas_active else 1 + lines = 3 if lkas_active else 0 + alerts = 7 if lkas_active else 0 if hud_count < (1 * 4): # first 3 seconds, 4Hz alerts = 1 - # CAR.PACIFICA_2018_HYBRID and CAR.PACIFICA_2019_HYBRID - # had color = 1 and lines = 1 but trying 2017 hybrid style for now. - if gear in (GearShifter.drive, GearShifter.reverse, GearShifter.low): - if lkas_active: - color = 2 # control active, display green. - lines = 6 - else: - color = 1 # control off, display white. - lines = 1 + + if hud_alert in (VisualAlert.ldw, VisualAlert.steerRequired): + color = 4 + lines = 0 + alerts = 6 values = { - "LKAS_ICON_COLOR": color, # byte 0, last 2 bits - "CAR_MODEL": lkas_car_model, # byte 1 - "LKAS_LANE_LINES": lines, # byte 2, last 4 bits - "LKAS_ALERTS": alerts, # byte 3, last 4 bits - } - - return packer.make_can_msg("LKAS_HUD", 0, values) # 0x2a6 - - -def create_lkas_command(packer, apply_steer, moving_fast, frame): - # LKAS_COMMAND 0x292 (658) Lane-keeping signal to turn the wheel. - values = { - "LKAS_STEERING_TORQUE": apply_steer, - "LKAS_HIGH_TORQUE": int(moving_fast), - "COUNTER": frame % 0x10, + "LKAS_ICON_COLOR": color, + "CAR_MODEL": car_model, + "LKAS_LANE_LINES": lines, + "LKAS_ALERTS": alerts, } - return packer.make_can_msg("LKAS_COMMAND", 0, values) + + if CP.carFingerprint in RAM_CARS: + values['AUTO_HIGH_BEAM_ON'] = auto_high_beam + + return packer.make_can_msg("DAS_6", 0, values) -def create_wheel_buttons(packer, frame, cancel=False): - # WHEEL_BUTTONS (571) Message sent to cancel ACC. +def create_lkas_command(packer, CP, apply_steer, lkas_control_bit, frame): + # LKAS_COMMAND Lane-keeping signal to turn the wheel + enabled_val = 2 if CP.carFingerprint in RAM_CARS else 1 values = { - "ACC_CANCEL": cancel, - "COUNTER": frame % 0x10 + "STEERING_TORQUE": apply_steer, + "LKAS_CONTROL_BIT": enabled_val if lkas_control_bit else 0, } - return packer.make_can_msg("WHEEL_BUTTONS", 0, values) + return packer.make_can_msg("LKAS_COMMAND", 0, values, frame % 0x10) + + +def create_cruise_buttons(packer, frame, bus, cancel=False, resume=False): + values = { + "ACC_Cancel": cancel, + "ACC_Resume": resume, + } + return packer.make_can_msg("CRUISE_BUTTONS", bus, values, frame % 0x10) diff --git a/selfdrive/car/chrysler/interface.py b/selfdrive/car/chrysler/interface.py index 55672dd4a..acc08954a 100755 --- a/selfdrive/car/chrysler/interface.py +++ b/selfdrive/car/chrysler/interface.py @@ -1,7 +1,8 @@ #!/usr/bin/env python3 from cereal import car -from selfdrive.car.chrysler.values import CAR +from panda import Panda from selfdrive.car import STD_CARGO_KG, scale_rot_inertia, scale_tire_stiffness, gen_empty_fingerprint, get_safety_config +from selfdrive.car.chrysler.values import CAR, DBC, RAM_CARS from selfdrive.car.interfaces import CarInterfaceBase @@ -10,31 +11,56 @@ class CarInterface(CarInterfaceBase): def get_params(candidate, fingerprint=gen_empty_fingerprint(), car_fw=None, disable_radar=False): ret = CarInterfaceBase.get_std_params(candidate, fingerprint) ret.carName = "chrysler" - ret.safetyConfigs = [get_safety_config(car.CarParams.SafetyModel.chrysler)] - # Speed conversion: 20, 45 mph - ret.wheelbase = 3.089 # in meters for Pacifica Hybrid 2017 - ret.steerRatio = 16.2 # Pacifica Hybrid 2017 - ret.mass = 2242. + STD_CARGO_KG # kg curb weight Pacifica Hybrid 2017 - ret.lateralTuning.pid.kpBP, ret.lateralTuning.pid.kiBP = [[9., 20.], [9., 20.]] - ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.15, 0.30], [0.03, 0.05]] - ret.lateralTuning.pid.kf = 0.00006 # full torque for 10 deg at 80mph means 0.00007818594 + ret.radarOffCan = DBC[candidate]['radar'] is None + + param = Panda.FLAG_CHRYSLER_RAM_DT if candidate in RAM_CARS else None + ret.safetyConfigs = [get_safety_config(car.CarParams.SafetyModel.chrysler, param)] + ret.steerActuatorDelay = 0.1 - ret.steerRateCost = 0.7 ret.steerLimitTimer = 0.4 - if candidate in (CAR.JEEP_CHEROKEE, CAR.JEEP_CHEROKEE_2019): - ret.wheelbase = 2.91 # in meters - ret.steerRatio = 12.7 - ret.steerActuatorDelay = 0.2 # in seconds - - ret.centerToFront = ret.wheelbase * 0.44 - ret.minSteerSpeed = 3.8 # m/s if candidate in (CAR.PACIFICA_2019_HYBRID, CAR.PACIFICA_2020, CAR.JEEP_CHEROKEE_2019): - # TODO allow 2019 cars to steer down to 13 m/s if already engaged. + # TODO: allow 2019 cars to steer down to 13 m/s if already engaged. ret.minSteerSpeed = 17.5 # m/s 17 on the way up, 13 on the way down once engaged. + # Chrysler + if candidate in (CAR.PACIFICA_2017_HYBRID, CAR.PACIFICA_2018, CAR.PACIFICA_2018_HYBRID, CAR.PACIFICA_2019_HYBRID, CAR.PACIFICA_2020): + ret.mass = 2242. + STD_CARGO_KG + ret.wheelbase = 3.089 + ret.steerRatio = 16.2 # Pacifica Hybrid 2017 + ret.lateralTuning.pid.kpBP, ret.lateralTuning.pid.kiBP = [[9., 20.], [9., 20.]] + ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.15, 0.30], [0.03, 0.05]] + ret.lateralTuning.pid.kf = 0.00006 + + # Jeep + elif candidate in (CAR.JEEP_CHEROKEE, CAR.JEEP_CHEROKEE_2019): + ret.mass = 1778 + STD_CARGO_KG + ret.wheelbase = 2.71 + ret.steerRatio = 16.7 + ret.steerActuatorDelay = 0.2 + ret.lateralTuning.pid.kpBP, ret.lateralTuning.pid.kiBP = [[9., 20.], [9., 20.]] + ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.15, 0.30], [0.03, 0.05]] + ret.lateralTuning.pid.kf = 0.00006 + + # Ram + elif candidate == CAR.RAM_1500: + ret.steerActuatorDelay = 0.2 + + ret.wheelbase = 3.88 + ret.steerRatio = 16.3 + ret.mass = 2493. + STD_CARGO_KG + ret.maxLateralAccel = 2.4 + ret.minSteerSpeed = 14.5 + CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning) + + + else: + raise ValueError(f"Unsupported car: {candidate}") + + ret.centerToFront = ret.wheelbase * 0.44 + # starting with reasonable value for civic and scaling by mass and wheelbase ret.rotationalInertia = scale_rot_inertia(ret.mass, ret.wheelbase) @@ -46,7 +72,6 @@ class CarInterface(CarInterfaceBase): return ret - # returns a car.CarState def _update(self, c): ret = self.CS.update(self.cp, self.cp_cam) @@ -55,14 +80,17 @@ class CarInterface(CarInterfaceBase): # events events = self.create_common_events(ret, extra_gears=[car.CarState.GearShifter.low]) - if ret.vEgo < self.CP.minSteerSpeed: + # Low speed steer alert hysteresis logic + if self.CP.minSteerSpeed > 0. and ret.vEgo < (self.CP.minSteerSpeed + 0.5): + self.low_speed_alert = True + elif ret.vEgo > (self.CP.minSteerSpeed + 1.): + self.low_speed_alert = False + if self.low_speed_alert: events.add(car.CarEvent.EventName.belowSteerSpeed) ret.events = events.to_msg() return ret - # pass in a car.CarControl - # to be called @ 100hz def apply(self, c): return self.CC.update(c, self.CS) diff --git a/selfdrive/car/chrysler/radar_interface.py b/selfdrive/car/chrysler/radar_interface.py index 8882dc2d9..348e3c363 100755 --- a/selfdrive/car/chrysler/radar_interface.py +++ b/selfdrive/car/chrysler/radar_interface.py @@ -10,6 +10,10 @@ LAST_MSG = max(RADAR_MSGS_C + RADAR_MSGS_D) NUMBER_MSGS = len(RADAR_MSGS_C) + len(RADAR_MSGS_D) def _create_radar_can_parser(car_fingerprint): + dbc = DBC[car_fingerprint]['radar'] + if dbc is None: + return None + msg_n = len(RADAR_MSGS_C) # list of [(signal name, message name or number), (...)] # [('RADAR_STATE', 1024), @@ -46,6 +50,9 @@ class RadarInterface(RadarInterfaceBase): self.trigger_msg = LAST_MSG def update(self, can_strings): + if self.rcp is None: + return super().update(None) + vls = self.rcp.update_strings(can_strings) self.updated_messages.update(vls) @@ -81,4 +88,4 @@ class RadarInterface(RadarInterfaceBase): ret.points = [x for x in self.pts.values() if x.dRel != 0] self.updated_messages.clear() - return ret + return ret \ No newline at end of file diff --git a/selfdrive/car/chrysler/values.py b/selfdrive/car/chrysler/values.py index b2757aafc..80baba9bd 100644 --- a/selfdrive/car/chrysler/values.py +++ b/selfdrive/car/chrysler/values.py @@ -1,44 +1,60 @@ +import capnp from dataclasses import dataclass from enum import Enum -from typing import Dict, List, Optional, Union +from typing import Dict, List, Optional, Tuple, Union +from cereal import car from selfdrive.car import dbc_dict from selfdrive.car.docs_definitions import CarInfo, Harness -from cereal import car Ecu = car.CarParams.Ecu -class CarControllerParams: - STEER_MAX = 261 # 262 faults - STEER_DELTA_UP = 3 # 3 is stock. 100 is fine. 200 is too much it seems - STEER_DELTA_DOWN = 3 # no faults on the way down it seems - STEER_ERROR_MAX = 80 - - class CAR: + # Chrysler PACIFICA_2017_HYBRID = "CHRYSLER PACIFICA HYBRID 2017" PACIFICA_2018_HYBRID = "CHRYSLER PACIFICA HYBRID 2018" PACIFICA_2019_HYBRID = "CHRYSLER PACIFICA HYBRID 2019" PACIFICA_2018 = "CHRYSLER PACIFICA 2018" # includes 2017 Pacifica PACIFICA_2020 = "CHRYSLER PACIFICA 2020" - JEEP_CHEROKEE = "JEEP GRAND CHEROKEE V6 2018" # includes 2017 Trailhawk - JEEP_CHEROKEE_2019 = "JEEP GRAND CHEROKEE 2019" # includes 2020 Trailhawk + # Jeep + JEEP_CHEROKEE = "JEEP GRAND CHEROKEE V6 2018" # includes 2017 Trailhawk + JEEP_CHEROKEE_2019 = "JEEP GRAND CHEROKEE 2019" # includes 2020 Trailhawk + + # Ram + RAM_1500 = "RAM 1500 5TH GEN" + + +class CarControllerParams: + def __init__(self, CP): + self.STEER_MAX = 261 # higher than this faults the EPS on Chrysler/Jeep. Ram DT allows more + self.STEER_ERROR_MAX = 80 + + if CP.carFingerprint in RAM_CARS: + self.STEER_DELTA_UP = 6 + self.STEER_DELTA_DOWN = 6 + else: + self.STEER_DELTA_UP = 3 + self.STEER_DELTA_DOWN = 3 + +STEER_THRESHOLD = 120 + +RAM_CARS = {CAR.RAM_1500, } @dataclass class ChryslerCarInfo(CarInfo): package: str = "Adaptive Cruise" harness: Enum = Harness.fca - CAR_INFO: Dict[str, Optional[Union[ChryslerCarInfo, List[ChryslerCarInfo]]]] = { CAR.PACIFICA_2017_HYBRID: ChryslerCarInfo("Chrysler Pacifica Hybrid 2017-18"), CAR.PACIFICA_2018_HYBRID: None, # same platforms - CAR.PACIFICA_2019_HYBRID: ChryslerCarInfo("Chrysler Pacifica Hybrid 2019-21"), + CAR.PACIFICA_2019_HYBRID: ChryslerCarInfo("Chrysler Pacifica Hybrid 2019-22"), CAR.PACIFICA_2018: ChryslerCarInfo("Chrysler Pacifica 2017-18"), - CAR.PACIFICA_2020: ChryslerCarInfo("Chrysler Pacifica 2020"), + CAR.PACIFICA_2020: ChryslerCarInfo("Chrysler Pacifica 2019-20"), CAR.JEEP_CHEROKEE: ChryslerCarInfo("Jeep Grand Cherokee 2016-18", video_link="https://www.youtube.com/watch?v=eLR9o2JkuRk"), - CAR.JEEP_CHEROKEE_2019: ChryslerCarInfo("Jeep Grand Cherokee 2019-20", video_link="https://www.youtube.com/watch?v=jBe4lWnRSu4"), + CAR.JEEP_CHEROKEE_2019: ChryslerCarInfo("Jeep Grand Cherokee 2019-21", video_link="https://www.youtube.com/watch?v=jBe4lWnRSu4"), + CAR.RAM_1500: ChryslerCarInfo("Ram 1500 2019-22"), } # Unique CAN messages: @@ -85,7 +101,7 @@ FINGERPRINTS = { }, # Based on "8190c7275a24557b|2020-02-24--09-57-23" { - 168: 8, 257: 5, 258: 8, 264: 8, 268: 8, 270: 8, 274: 2, 280: 8, 284: 8, 288: 7, 290: 6, 291: 8, 292: 8, 294: 8, 300: 8, 308: 8, 320: 8, 324: 8, 331: 8, 332: 8, 344: 8, 368: 8, 376: 3, 384: 8, 388: 4, 448: 6, 456: 4, 464: 8, 469: 8, 480: 8, 500: 8, 501: 8, 512: 8, 514: 8, 515: 7, 516: 7, 517: 7, 518: 7, 520: 8, 524: 8, 526: 6, 528: 8, 532: 8, 542: 8, 544: 8, 557: 8, 559: 8, 560: 8, 564: 8, 571: 3, 579: 8, 584: 8, 608: 8, 624: 8, 625: 8, 632: 8, 639: 8, 640: 1, 650: 8, 653: 8, 654: 8, 655: 8, 656: 4, 658: 6, 660: 8, 669: 3, 671: 8, 672: 8, 678: 8, 680: 8, 683: 8, 701: 8, 703: 8, 704: 8, 705: 8, 706: 8, 709: 8, 710: 8, 711: 8, 719: 8, 720: 6, 729: 5, 736: 8, 737: 8, 738: 8, 746: 5, 752: 2, 754: 8, 760: 8, 764: 8, 766: 8, 770: 8, 773: 8, 779: 8, 782: 8, 784: 8, 792: 8, 793: 8, 794: 8, 795: 8, 796: 8, 797: 8, 798: 8, 799: 8, 800: 8, 801: 8, 802: 8, 803: 8, 804: 8, 805: 8, 807: 8, 808: 8, 816: 8, 817: 8, 820: 8, 825: 2, 826: 8, 832: 8, 838: 2, 847: 1, 848: 8, 853: 8, 856: 4, 860: 6, 863: 8, 878: 8, 882: 8, 886: 8, 897: 8, 906: 8, 908: 8, 924: 8, 926: 3, 929: 8, 937: 8, 938: 8, 939: 8, 940: 8, 941: 8, 942: 8, 943: 8, 947: 8, 948: 8, 958: 8, 959: 8, 962: 8, 969: 4, 973: 8, 974: 5, 979: 8, 980: 8, 981: 8, 982: 8, 983: 8, 984: 8, 992: 8, 993: 7, 995: 8, 996: 8, 1000: 8, 1001: 8, 1002: 8, 1003: 8, 1008: 8, 1009: 8, 1010: 8, 1011: 8, 1012: 8, 1013: 8, 1014: 8, 1015: 8, 1024: 8, 1025: 8, 1026: 8, 1031: 8, 1033: 8, 1050: 8, 1059: 8, 1082: 8, 1083: 8, 1098: 8, 1100: 8, 1216: 8, 1218: 8, 1220: 8, 1225: 8, 1235: 8, 1242: 8, 1246: 8, 1250: 8, 1251: 8, 1252: 8, 1258: 8, 1259: 8, 1260: 8, 1262: 8, 1284: 8, 1568: 8, 1570: 8, 1856: 8, 1858: 8, 1860: 8, 1863: 8, 1865: 8, 1875: 8, 1882: 8, 1886: 8, 1890: 8, 1891: 8, 1892: 8, 1898: 8, 1899: 8, 1900: 8, 1902: 8, 2015: 8, 2016: 8, 2017: 8, 2018: 8, 2019: 8, 2020: 8, 2023: 8, 2024: 8, 2026: 8, 2027: 8, 2028: 8, 2031: 8 + 168: 8, 257: 5, 258: 8, 264: 8, 268: 8, 270: 8, 274: 2, 280: 8, 284: 8, 288: 7, 290: 6, 291: 8, 292: 8, 294: 8, 300: 8, 308: 8, 320: 8, 324: 8, 331: 8, 332: 8, 344: 8, 368: 8, 376: 3, 384: 8, 388: 4, 448: 6, 456: 4, 464: 8, 469: 8, 480: 8, 500: 8, 501: 8, 512: 8, 514: 8, 515: 7, 516: 7, 517: 7, 518: 7, 520: 8, 524: 8, 526: 6, 528: 8, 532: 8, 542: 8, 544: 8, 557: 8, 559: 8, 560: 8, 564: 8, 571: 3, 579: 8, 584: 8, 608: 8, 624: 8, 625: 8, 632: 8, 639: 8, 640: 1, 650: 8, 653: 8, 654: 8, 655: 8, 656: 4, 658: 6, 660: 8, 669: 3, 671: 8, 672: 8, 678: 8, 680: 8, 683: 8, 701: 8, 703: 8, 704: 8, 705: 8, 706: 8, 709: 8, 710: 8, 711: 8, 719: 8, 720: 6, 729: 5, 736: 8, 737: 8, 738: 8, 746: 5, 752: 2, 754: 8, 760: 8, 764: 8, 766: 8, 770: 8, 773: 8, 779: 8, 782: 8, 784: 8, 792: 8, 793: 8, 794: 8, 795: 8, 796: 8, 797: 8, 798: 8, 799: 8, 800: 8, 801: 8, 802: 8, 803: 8, 804: 8, 805: 8, 807: 8, 808: 8, 816: 8, 817: 8, 820: 8, 825: 2, 826: 8, 832: 8, 838: 2, 847: 1, 848: 8, 853: 8, 856: 4, 860: 6, 863: 8, 878: 8, 882: 8, 886: 8, 897: 8, 906: 8, 908: 8, 924: 8, 926: 3, 929: 8, 937: 8, 938: 8, 939: 8, 940: 8, 941: 8, 942: 8, 943: 8, 947: 8, 948: 8, 958: 8, 959: 8, 962: 8, 969: 4, 973: 8, 974: 5, 979: 8, 980: 8, 981: 8, 982: 8, 983: 8, 984: 8, 992: 8, 993: 7, 995: 8, 996: 8, 1000: 8, 1001: 8, 1002: 8, 1003: 8, 1008: 8, 1009: 8, 1010: 8, 1011: 8, 1012: 8, 1013: 8, 1014: 8, 1015: 8, 1024: 8, 1025: 8, 1026: 8, 1031: 8, 1033: 8, 1050: 8, 1059: 8, 1082: 8, 1083: 8, 1098: 8, 1100: 8, 1216: 8, 1218: 8, 1220: 8, 1225: 8, 1235: 8, 1242: 8, 1246: 8, 1250: 8, 1251: 8, 1252: 8, 1258: 8, 1259: 8, 1260: 8, 1262: 8, 1284: 8, 1536: 8, 1568: 8, 1570: 8, 1856: 8, 1858: 8, 1860: 8, 1863: 8, 1865: 8, 1875: 8, 1882: 8, 1886: 8, 1890: 8, 1891: 8, 1892: 8, 1898: 8, 1899: 8, 1900: 8, 1902: 8, 2015: 8, 2016: 8, 2017: 8, 2018: 8, 2019: 8, 2020: 8, 2023: 8, 2024: 8, 2026: 8, 2027: 8, 2028: 8, 2031: 8 }], CAR.JEEP_CHEROKEE: [{ 55: 8, 168: 8, 181: 8, 256: 4, 257: 5, 258: 8, 264: 8, 268: 8, 272: 6, 273: 6, 274: 2, 280: 8, 284: 8, 288: 7, 290: 6, 292: 8, 300: 8, 308: 8, 320: 8, 324: 8, 331: 8, 332: 8, 344: 8, 352: 8, 362: 8, 368: 8, 376: 3, 384: 8, 388: 4, 416: 7, 448: 6, 456: 4, 464: 8, 500: 8, 501: 8, 512: 8, 514: 8, 520: 8, 532: 8, 544: 8, 557: 8, 559: 8, 560: 4, 564: 4, 571: 3, 579: 8, 584: 8, 608: 8, 618: 8, 624: 8, 625: 8, 632: 8, 639: 8, 656: 4, 658: 6, 660: 8, 671: 8, 672: 8, 676: 8, 678: 8, 680: 8, 683: 8, 684: 8, 703: 8, 705: 8, 706: 8, 709: 8, 710: 8, 719: 8, 720: 6, 729: 5, 736: 8, 737: 8, 738: 8, 746: 5, 752: 2, 754: 8, 760: 8, 761: 8, 764: 8, 766: 8, 773: 8, 776: 8, 779: 8, 782: 8, 783: 8, 784: 8, 785: 8, 788: 3, 792: 8, 799: 8, 800: 8, 804: 8, 806: 2, 808: 8, 810: 8, 816: 8, 817: 8, 820: 8, 825: 2, 826: 8, 831: 6, 832: 8, 838: 2, 840: 8, 844: 5, 847: 1, 848: 8, 853: 8, 856: 4, 860: 6, 863: 8, 874: 2, 882: 8, 897: 8, 906: 8, 924: 8, 937: 8, 938: 8, 939: 8, 940: 8, 941: 8, 942: 8, 943: 8, 947: 8, 948: 8, 956: 8, 968: 8, 969: 4, 970: 8, 973: 8, 974: 5, 975: 8, 976: 8, 977: 4, 979: 8, 980: 8, 981: 8, 982: 8, 983: 8, 984: 8, 992: 8, 993: 7, 995: 8, 996: 8, 1000: 8, 1001: 8, 1002: 8, 1003: 8, 1008: 8, 1009: 8, 1010: 8, 1011: 8, 1012: 8, 1013: 8, 1014: 8, 1015: 8, 1024: 8, 1025: 8, 1026: 8, 1031: 8, 1033: 8, 1050: 8, 1059: 8, 1062: 8, 1098: 8, 1100: 8, 1543: 8, 1562: 8, 2015: 8, 2016: 8, 2017: 8, 2024: 8, 2025: 8 @@ -94,17 +110,36 @@ FINGERPRINTS = { # Jeep Grand Cherokee 2019, including most 2020 models 55: 8, 168: 8, 179: 8, 181: 8, 256: 4, 257: 5, 258: 8, 264: 8, 268: 8, 272: 6, 273: 6, 274: 2, 280: 8, 284: 8, 288: 7, 290: 6, 292: 8, 300: 8, 308: 8, 320: 8, 324: 8, 331: 8, 332: 8, 341: 8, 344: 8, 352: 8, 362: 8, 368: 8, 376: 3, 384: 8, 388: 4, 416: 7, 448: 6, 456: 4, 464: 8, 500: 8, 501: 8, 512: 8, 514: 8, 520: 8, 530: 8, 532: 8, 544: 8, 557: 8, 559: 8, 560: 8, 564: 8, 571: 3, 579: 8, 584: 8, 608: 8, 618: 8, 624: 8, 625: 8, 632: 8, 639: 8, 640: 1, 656: 4, 658: 6, 660: 8, 671: 8, 672: 8, 676: 8, 678: 8, 680: 8, 683: 8, 684: 8, 703: 8, 705: 8, 706: 8, 709: 8, 710: 8, 719: 8, 720: 6, 729: 5, 736: 8, 737: 8, 738: 8, 746: 5, 752: 2, 754: 8, 760: 8, 761: 8, 764: 8, 766: 8, 773: 8, 776: 8, 779: 8, 782: 8, 783: 8, 784: 8, 785: 8, 792: 8, 799: 8, 800: 8, 804: 8, 806: 2, 808: 8, 810: 8, 816: 8, 817: 8, 820: 8, 825: 2, 826: 8, 831: 6, 832: 8, 838: 2, 840: 8, 844: 5, 847: 1, 848: 8, 853: 8, 856: 4, 860: 6, 863: 8, 882: 8, 897: 8, 906: 8, 924: 8, 937: 8, 938: 8, 939: 8, 940: 8, 941: 8, 942: 8, 943: 8, 947: 8, 948: 8, 960: 4, 968: 8, 969: 4, 970: 8, 973: 8, 974: 5, 976: 8, 977: 4, 979: 8, 980: 8, 981: 8, 982: 8, 983: 8, 984: 8, 992: 8, 993: 7, 995: 8, 996: 8, 1000: 8, 1001: 8, 1002: 8, 1003: 8, 1008: 8, 1009: 8, 1010: 8, 1011: 8, 1012: 8, 1013: 8, 1014: 8, 1015: 8, 1024: 8, 1025: 8, 1026: 8, 1031: 8, 1033: 8, 1050: 8, 1059: 8, 1062: 8, 1098: 8, 1100: 8, 1216: 8, 1218: 8, 1220: 8, 1223: 8, 1225: 8, 1227: 8, 1235: 8, 1242: 8, 1250: 8, 1251: 8, 1252: 8, 1254: 8, 1264: 8, 1284: 8, 1536: 8, 1537: 8, 1543: 8, 1545: 8, 1562: 8, 1568: 8, 1570: 8, 1572: 8, 1593: 8, 1856: 8, 1858: 8, 1860: 8, 1863: 8, 1865: 8, 1867: 8, 1875: 8, 1882: 8, 1890: 8, 1891: 8, 1892: 8, 1894: 8, 1896: 8, 1904: 8, 2015: 8, 2016: 8, 2017: 8, 2024: 8, 2025: 8 }], + CAR.RAM_1500: [ + {35: 8, 37: 8, 39: 8, 41: 8, 43: 8, 47: 8, 49: 8, 53: 8, 55: 8, 113: 8, 119: 2, 121: 8, 123: 7, 125: 6, 127: 8, 129: 8, 131: 8, 133: 8, 135: 8, 137: 8, 139: 8, 141: 8, 145: 8, 147: 8, 149: 7, 153: 8, 155: 8, 157: 8, 163: 8, 164: 8, 166: 8, 167: 8, 169: 8, 171: 8, 173: 5, 177: 3, 179: 8, 181: 8, 213: 3, 221: 8, 232: 8, 250: 8, 278: 8, 289: 5, 293: 3, 295: 8, 296: 8, 297: 4, 298: 8, 299: 8, 305: 8, 307: 8, 311: 8, 315: 8, 317: 8, 319: 8, 323: 8, 333: 8, 334: 8, 341: 8, 343: 8, 345: 8, 347: 8, 409: 6, 421: 8, 448: 6, 456: 4, 464: 8, 489: 8, 491: 8, 502: 8, 503: 8, 505: 8, 507: 5, 516: 7, 517: 7, 524: 8, 526: 6, 557: 8, 560: 8, 584: 8, 601: 8, 605: 8, 607: 8, 609: 8, 611: 8, 613: 8, 623: 8, 631: 8, 633: 8, 634: 8, 635: 8, 637: 8, 641: 8, 643: 8, 645: 2, 649: 8, 650: 8, 651: 8, 656: 4, 657: 8, 659: 5, 663: 8, 664: 8, 673: 8, 676: 8, 679: 8, 685: 8, 687: 8, 689: 5, 706: 8, 709: 8, 710: 8, 711: 8, 720: 6, 752: 2, 754: 8, 773: 8, 788: 3, 792: 8, 808: 8, 818: 8, 819: 8, 822: 8, 823: 8, 825: 2, 838: 2, 840: 8, 848: 8, 856: 4, 860: 6, 862: 8, 875: 2, 897: 8, 906: 8, 910: 8, 926: 3, 929: 8, 930: 8, 931: 8, 932: 8, 937: 8, 938: 8, 939: 8, 940: 8, 941: 8, 942: 8, 943: 8, 947: 8, 948: 8, 956: 8, 961: 8, 962: 8, 969: 4, 971: 8, 972: 8, 973: 8, 979: 8, 980: 8, 981: 8, 982: 8, 983: 8, 984: 8, 992: 8, 993: 7, 995: 8, 996: 8, 1000: 8, 1001: 8, 1002: 8, 1003: 8, 1008: 8, 1009: 8, 1010: 8, 1011: 8, 1012: 8, 1013: 8, 1014: 8, 1015: 8, 1024: 8, 1025: 8, 1026: 8, 1031: 8, 1033: 8, 1050: 8, 1059: 8, 1062: 8, 1098: 8, 1100: 8, 2015: 8, 2016: 8, 2017: 8, 2024: 8, 2025: 8}, + {35: 8, 37: 8, 39: 8, 43: 8, 47: 8, 49: 8, 53: 8, 55: 8, 113: 8, 119: 2, 121: 8, 123: 7, 125: 6, 127: 8, 129: 8, 131: 8, 133: 8, 135: 8, 137: 8, 139: 8, 141: 8, 145: 8, 147: 8, 149: 7, 153: 8, 155: 8, 157: 8, 163: 8, 164: 8, 166: 8, 167: 8, 169: 8, 171: 8, 173: 5, 177: 3, 179: 8, 181: 8, 213: 3, 221: 8, 232: 8, 250: 8, 276: 8, 277: 8, 278: 8, 289: 5, 293: 3, 295: 8, 296: 8, 297: 4, 299: 8, 301: 8, 302: 8, 305: 8, 307: 8, 311: 8, 317: 8, 319: 8, 323: 8, 327: 8, 333: 8, 334: 8, 341: 8, 343: 8, 345: 8, 347: 8, 421: 8, 448: 6, 456: 4, 457: 8, 464: 8, 489: 8, 491: 8, 502: 8, 503: 8, 507: 5, 516: 7, 517: 7, 524: 8, 526: 6, 557: 8, 560: 8, 584: 8, 601: 8, 605: 8, 607: 8, 609: 8, 613: 8, 623: 8, 631: 8, 633: 8, 634: 8, 635: 8, 637: 8, 641: 8, 643: 8, 645: 2, 649: 8, 650: 8, 651: 8, 656: 4, 657: 8, 663: 8, 673: 8, 676: 8, 679: 8, 685: 8, 687: 8, 689: 5, 706: 8, 709: 8, 710: 8, 711: 8, 720: 6, 738: 8, 752: 2, 754: 8, 773: 8, 792: 8, 808: 8, 812: 8, 813: 8, 814: 8, 818: 8, 819: 8, 821: 8, 822: 8, 823: 8, 825: 2, 838: 2, 840: 8, 847: 1, 848: 8, 856: 4, 860: 6, 862: 8, 874: 2, 876: 8, 897: 8, 906: 8, 910: 8, 926: 3, 929: 8, 930: 8, 931: 8, 932: 8, 937: 8, 938: 8, 939: 8, 940: 8, 941: 8, 942: 8, 943: 8, 947: 8, 948: 8, 961: 8, 962: 8, 969: 4, 971: 8, 972: 8, 973: 8, 975: 8, 976: 8, 979: 8, 980: 8, 981: 8, 982: 8, 983: 8, 984: 8, 992: 8, 993: 7, 995: 8, 996: 8, 1000: 8, 1001: 8, 1002: 8, 1003: 8, 1008: 8, 1009: 8, 1010: 8, 1011: 8, 1012: 8, 1013: 8, 1014: 8, 1015: 8, 1024: 8, 1025: 8, 1026: 8, 1030: 8, 1031: 8, 1033: 8, 1050: 8, 1059: 8, 1098: 8, 1100: 8}, + {35: 8, 37: 8, 39: 8, 43: 8, 47: 8, 49: 8, 53: 8, 55: 8, 113: 8, 119: 2, 121: 8, 123: 7, 125: 6, 127: 8, 129: 8, 131: 8, 133: 8, 135: 8, 137: 8, 139: 8, 141: 8, 145: 8, 147: 8, 149: 7, 153: 8, 155: 8, 157: 8, 163: 8, 164: 8, 166: 8, 167: 8, 169: 8, 171: 8, 173: 5, 177: 3, 179: 8, 181: 8, 213: 3, 221: 8, 232: 8, 250: 8, 289: 5, 293: 3, 295: 8, 296: 8, 297: 4, 299: 8, 301: 8, 302: 8, 305: 8, 307: 8, 311: 8, 317: 8, 319: 8, 323: 8, 334: 8, 337: 8, 343: 8, 347: 8, 409: 6, 421: 8, 448: 6, 456: 4, 464: 8, 489: 8, 491: 8, 502: 8, 503: 8, 507: 5, 516: 7, 517: 7, 524: 8, 526: 6, 557: 8, 560: 8, 584: 8, 601: 8, 605: 8, 607: 8, 609: 8, 613: 8, 623: 8, 631: 8, 633: 8, 634: 8, 635: 8, 637: 8, 641: 8, 643: 8, 645: 2, 649: 8, 650: 8, 651: 8, 656: 4, 657: 8, 659: 5, 663: 8, 664: 8, 673: 8, 676: 8, 679: 8, 685: 8, 687: 8, 689: 5, 706: 8, 709: 8, 710: 8, 711: 8, 720: 6, 752: 2, 754: 8, 773: 8, 788: 3, 792: 8, 808: 8, 812: 8, 813: 8, 814: 8, 818: 8, 819: 8, 821: 8, 822: 8, 825: 2, 838: 2, 840: 8, 847: 1, 848: 8, 856: 4, 860: 6, 862: 8, 876: 8, 897: 8, 906: 8, 910: 8, 926: 3, 929: 8, 930: 8, 931: 8, 932: 8, 937: 8, 938: 8, 939: 8, 940: 8, 941: 8, 942: 8, 943: 8, 947: 8, 948: 8, 956: 8, 961: 8, 962: 8, 969: 4, 971: 8, 972: 8, 973: 8, 979: 8, 980: 8, 981: 8, 982: 8, 983: 8, 984: 8, 992: 8, 993: 7, 995: 8, 996: 8, 1000: 8, 1001: 8, 1002: 8, 1003: 8, 1008: 8, 1009: 8, 1010: 8, 1011: 8, 1012: 8, 1013: 8, 1014: 8, 1015: 8, 1024: 8, 1025: 8, 1026: 8, 1030: 8, 1031: 8, 1033: 8, 1050: 8, 1059: 8, 1062: 8, 1098: 8, 1100: 8}, + ], } +FW_VERSIONS: Dict[str, Dict[Tuple[capnp.lib.capnp._EnumModule, int, Optional[int]], List[str]]] = { + CAR.RAM_1500: { + (Ecu.combinationMeter, 0x742, None): [], + (Ecu.srs, 0x744, None): [], + (Ecu.esp, 0x747, None): [], + (Ecu.fwdCamera, 0x753, None): [], + (Ecu.fwdCamera, 0x764, None): [], + (Ecu.eps, 0x761, None): [], + (Ecu.fwdRadar, 0x757, None): [], + (Ecu.eps, 0x75A, None): [], + (Ecu.engine, 0x7e0, None): [], + (Ecu.transmission, 0x7e1, None): [], + (Ecu.gateway, 0x18DACBF1, None): [], + } +} DBC = { - CAR.PACIFICA_2017_HYBRID: dbc_dict('chrysler_pacifica_2017_hybrid', 'chrysler_pacifica_2017_hybrid_private_fusion'), - CAR.PACIFICA_2018: dbc_dict('chrysler_pacifica_2017_hybrid', 'chrysler_pacifica_2017_hybrid_private_fusion'), - CAR.PACIFICA_2020: dbc_dict('chrysler_pacifica_2017_hybrid', 'chrysler_pacifica_2017_hybrid_private_fusion'), - CAR.PACIFICA_2018_HYBRID: dbc_dict('chrysler_pacifica_2017_hybrid', 'chrysler_pacifica_2017_hybrid_private_fusion'), - CAR.PACIFICA_2019_HYBRID: dbc_dict('chrysler_pacifica_2017_hybrid', 'chrysler_pacifica_2017_hybrid_private_fusion'), - CAR.JEEP_CHEROKEE: dbc_dict('chrysler_pacifica_2017_hybrid', 'chrysler_pacifica_2017_hybrid_private_fusion'), - CAR.JEEP_CHEROKEE_2019: dbc_dict('chrysler_pacifica_2017_hybrid', 'chrysler_pacifica_2017_hybrid_private_fusion'), + CAR.PACIFICA_2017_HYBRID: dbc_dict('chrysler_pacifica_2017_hybrid_generated', 'chrysler_pacifica_2017_hybrid_private_fusion'), + CAR.PACIFICA_2018: dbc_dict('chrysler_pacifica_2017_hybrid_generated', 'chrysler_pacifica_2017_hybrid_private_fusion'), + CAR.PACIFICA_2020: dbc_dict('chrysler_pacifica_2017_hybrid_generated', 'chrysler_pacifica_2017_hybrid_private_fusion'), + CAR.PACIFICA_2018_HYBRID: dbc_dict('chrysler_pacifica_2017_hybrid_generated', 'chrysler_pacifica_2017_hybrid_private_fusion'), + CAR.PACIFICA_2019_HYBRID: dbc_dict('chrysler_pacifica_2017_hybrid_generated', 'chrysler_pacifica_2017_hybrid_private_fusion'), + CAR.JEEP_CHEROKEE: dbc_dict('chrysler_pacifica_2017_hybrid_generated', 'chrysler_pacifica_2017_hybrid_private_fusion'), + CAR.JEEP_CHEROKEE_2019: dbc_dict('chrysler_pacifica_2017_hybrid_generated', 'chrysler_pacifica_2017_hybrid_private_fusion'), + CAR.RAM_1500: dbc_dict('chrysler_ram_dt_generated', None), } - -STEER_THRESHOLD = 120 diff --git a/selfdrive/car/disable_ecu.py b/selfdrive/car/disable_ecu.py old mode 100644 new mode 100755 index 3a06cc06f..cd3e93fa8 --- a/selfdrive/car/disable_ecu.py +++ b/selfdrive/car/disable_ecu.py @@ -1,11 +1,12 @@ from selfdrive.car.isotp_parallel_query import IsoTpParallelQuery -from selfdrive.swaglog import cloudlog +from system.swaglog import cloudlog EXT_DIAG_REQUEST = b'\x10\x03' EXT_DIAG_RESPONSE = b'\x50\x03' COM_CONT_RESPONSE = b'' + def disable_ecu(logcan, sendcan, bus=0, addr=0x7d0, com_cont_req=b'\x28\x83\x01', timeout=0.1, retry=10, debug=False): """Silence an ECU by disabling sending and receiving messages using UDS 0x28. The ECU will stay silent as long as openpilot keeps sending Tester Present. @@ -26,9 +27,22 @@ def disable_ecu(logcan, sendcan, bus=0, addr=0x7d0, com_cont_req=b'\x28\x83\x01' cloudlog.warning("ecu disabled") return True + except Exception: cloudlog.exception("ecu disable exception") print(f"ecu disable retry ({i+1}) ...") cloudlog.warning("ecu disable failed") return False + + +if __name__ == "__main__": + import time + import cereal.messaging as messaging + sendcan = messaging.pub_sock('sendcan') + logcan = messaging.sub_sock('can') + time.sleep(1) + + # honda bosch radar disable + disabled = disable_ecu(logcan, sendcan, bus=1, addr=0x18DAB0F1, com_cont_req=b'\x28\x83\x03', timeout=0.5, debug=False) + print(f"disabled: {disabled}") diff --git a/selfdrive/car/docs_definitions.py b/selfdrive/car/docs_definitions.py index 3046f5c9e..1efa23037 100644 --- a/selfdrive/car/docs_definitions.py +++ b/selfdrive/car/docs_definitions.py @@ -1,9 +1,15 @@ +import math + from cereal import car from collections import namedtuple from dataclasses import dataclass from enum import Enum from typing import Dict, List, Optional, Union, no_type_check +TACO_TORQUE_THRESHOLD = 2.5 # m/s^2 +GREAT_TORQUE_THRESHOLD = 1.4 # m/s^2 +GOOD_TORQUE_THRESHOLD = 1.0 # m/s^2 + class Tier(Enum): GOLD = "The best openpilot experience. Great highway driving and beyond." @@ -49,7 +55,6 @@ class CarInfo: footnotes: Optional[List[Enum]] = None min_steer_speed: Optional[float] = None min_enable_speed: Optional[float] = None - good_torque: bool = False harness: Optional[Enum] = None def init(self, CP: car.CarParams, non_tested_cars: List[str], all_footnotes: Dict[Enum, int]): @@ -73,21 +78,28 @@ class CarInfo: Column.MODEL: self.model, Column.PACKAGE: self.package, # StarColumns - Column.LONGITUDINAL: CP.openpilotLongitudinalControl and not CP.radarOffCan, - Column.FSR_LONGITUDINAL: min_enable_speed <= 0., - Column.FSR_STEERING: min_steer_speed <= 0., - Column.STEERING_TORQUE: self.good_torque, - Column.MAINTAINED: CP.carFingerprint not in non_tested_cars and self.harness is not Harness.none, + Column.LONGITUDINAL: Star.FULL if CP.openpilotLongitudinalControl and not CP.radarOffCan else Star.EMPTY, + Column.FSR_LONGITUDINAL: Star.FULL if min_enable_speed <= 0. else Star.EMPTY, + Column.FSR_STEERING: Star.FULL if min_steer_speed <= 0. else Star.EMPTY, + # Column.STEERING_TORQUE set below + Column.MAINTAINED: Star.FULL if CP.carFingerprint not in non_tested_cars and self.harness is not Harness.none else Star.EMPTY, } + # Set steering torque star from max lateral acceleration + if not math.isnan(CP.maxLateralAccel): + if CP.maxLateralAccel >= GREAT_TORQUE_THRESHOLD: + self.row[Column.STEERING_TORQUE] = Star.FULL + elif CP.maxLateralAccel >= GOOD_TORQUE_THRESHOLD: + self.row[Column.STEERING_TORQUE] = Star.HALF + else: + self.row[Column.STEERING_TORQUE] = Star.EMPTY + if CP.notCar: for col in StarColumns: - self.row[col] = True + self.row[col] = Star.FULL self.all_footnotes = all_footnotes for column in StarColumns: - self.row[column] = Star.FULL if self.row[column] else Star.EMPTY - # Demote if footnote specifies a star footnote = get_footnote(self.footnotes, column) if footnote is not None and footnote.value.star is not None: @@ -111,7 +123,8 @@ class CarInfo: class Harness(Enum): nidec = "Honda Nidec" - bosch = "Honda Bosch" + bosch_a = "Honda Bosch A" + bosch_b = "Honda Bosch B" toyota = "Toyota" subaru = "Subaru" fca = "FCA" @@ -132,9 +145,42 @@ class Harness(Enum): hyundai_m = "Hyundai M" hyundai_n = "Hyundai N" hyundai_o = "Hyundai O" + hyundai_p = "Hyundai P" custom = "Developer" obd_ii = "OBD-II" nissan_a = "Nissan A" nissan_b = "Nissan B" mazda = "Mazda" none = "None" + + +STAR_DESCRIPTIONS = { + "Gas & Brakes": { # icon and row name + "openpilot Adaptive Cruise Control (ACC)": [ # star column + [Star.FULL.value, "openpilot is able to control the gas and brakes."], + [Star.HALF.value, "openpilot is able to control the gas and brakes with some restrictions."], + [Star.EMPTY.value, "The gas and brakes are controlled by the car's stock Adaptive Cruise Control (ACC) system."], + ], + Column.FSR_LONGITUDINAL.value: [ + [Star.FULL.value, "Adaptive Cruise Control (ACC) operates down to 0 mph."], + [Star.EMPTY.value, "Adaptive Cruise Control (ACC) available only above certain speeds. See your car's manual for the minimum speed."], + ], + }, + "Steering": { + Column.FSR_STEERING.value: [ + [Star.FULL.value, "openpilot can control the steering wheel down to 0 mph."], + [Star.EMPTY.value, "No steering control below certain speeds."], + ], + Column.STEERING_TORQUE.value: [ + [Star.FULL.value, "Car has enough steering torque to take tighter turns."], + [Star.HALF.value, "Car has enough steering torque for comfortable highway driving."], + [Star.EMPTY.value, "Limited ability to make turns."], + ], + }, + "Support": { + Column.MAINTAINED.value: [ + [Star.FULL.value, "Mainline software support, harness hardware sold by comma, lots of users, primary development target."], + [Star.EMPTY.value, "Low user count, community maintained, harness hardware not sold by comma."], + ], + }, +} diff --git a/selfdrive/car/ecu_addrs.py b/selfdrive/car/ecu_addrs.py new file mode 100755 index 000000000..267701509 --- /dev/null +++ b/selfdrive/car/ecu_addrs.py @@ -0,0 +1,90 @@ +#!/usr/bin/env python3 +import capnp +import time +import traceback +from typing import Optional, Set, Tuple + +import cereal.messaging as messaging +from panda.python.uds import SERVICE_TYPE +from selfdrive.car import make_can_msg +from selfdrive.boardd.boardd import can_list_to_can_capnp +from system.swaglog import cloudlog + + +def make_tester_present_msg(addr, bus, subaddr=None): + dat = [0x02, SERVICE_TYPE.TESTER_PRESENT, 0x0] + if subaddr is not None: + dat.insert(0, subaddr) + + dat.extend([0x0] * (8 - len(dat))) + return make_can_msg(addr, bytes(dat), bus) + + +def is_tester_present_response(msg: capnp.lib.capnp._DynamicStructReader, subaddr: Optional[int] = None) -> bool: + # ISO-TP messages are always padded to 8 bytes + # tester present response is always a single frame + dat_offset = 1 if subaddr is not None else 0 + if len(msg.dat) == 8 and 1 <= msg.dat[dat_offset] <= 7: + # success response + if msg.dat[dat_offset + 1] == (SERVICE_TYPE.TESTER_PRESENT + 0x40): + return True + # error response + if msg.dat[dat_offset + 1] == 0x7F and msg.dat[dat_offset + 2] == SERVICE_TYPE.TESTER_PRESENT: + return True + return False + + +def get_all_ecu_addrs(logcan: messaging.SubSocket, sendcan: messaging.PubSocket, bus: int, timeout: float = 1, debug: bool = True) -> Set[Tuple[int, Optional[int], int]]: + addr_list = [0x700 + i for i in range(256)] + [0x18da00f1 + (i << 8) for i in range(256)] + queries: Set[Tuple[int, Optional[int], int]] = {(addr, None, bus) for addr in addr_list} + responses = queries + return get_ecu_addrs(logcan, sendcan, queries, responses, timeout=timeout, debug=debug) + + +def get_ecu_addrs(logcan: messaging.SubSocket, sendcan: messaging.PubSocket, queries: Set[Tuple[int, Optional[int], int]], + responses: Set[Tuple[int, Optional[int], int]], timeout: float = 1, debug: bool = False) -> Set[Tuple[int, Optional[int], int]]: + ecu_responses: Set[Tuple[int, Optional[int], int]] = set() # set((addr, subaddr, bus),) + try: + msgs = [make_tester_present_msg(addr, bus, subaddr) for addr, subaddr, bus in queries] + + messaging.drain_sock_raw(logcan) + sendcan.send(can_list_to_can_capnp(msgs, msgtype='sendcan')) + start_time = time.monotonic() + while time.monotonic() - start_time < timeout: + can_packets = messaging.drain_sock(logcan, wait_for_one=True) + for packet in can_packets: + for msg in packet.can: + subaddr = None if (msg.address, None, msg.src) in responses else msg.dat[0] + if (msg.address, subaddr, msg.src) in responses and is_tester_present_response(msg, subaddr): + if debug: + print(f"CAN-RX: {hex(msg.address)} - 0x{bytes.hex(msg.dat)}") + if (msg.address, subaddr, msg.src) in ecu_responses: + print(f"Duplicate ECU address: {hex(msg.address)}") + ecu_responses.add((msg.address, subaddr, msg.src)) + except Exception: + cloudlog.warning(f"ECU addr scan exception: {traceback.format_exc()}") + return ecu_responses + + +if __name__ == "__main__": + import argparse + + parser = argparse.ArgumentParser(description='Get addresses of all ECUs') + parser.add_argument('--debug', action='store_true') + args = parser.parse_args() + + logcan = messaging.sub_sock('can') + sendcan = messaging.pub_sock('sendcan') + + time.sleep(1.0) + + print("Getting ECU addresses ...") + ecu_addrs = get_all_ecu_addrs(logcan, sendcan, 1, debug=args.debug) + + print() + print("Found ECUs on addresses:") + for addr, subaddr, bus in ecu_addrs: + msg = f" 0x{hex(addr)}" + if subaddr is not None: + msg += f" (sub-address: 0x{hex(subaddr)})" + print(msg) diff --git a/selfdrive/car/fingerprints.py b/selfdrive/car/fingerprints.py index 9b280f3b4..1a9bb8c4e 100644 --- a/selfdrive/car/fingerprints.py +++ b/selfdrive/car/fingerprints.py @@ -1,44 +1,12 @@ -import os -from common.basedir import BASEDIR +from selfdrive.car.interfaces import get_interface_attr -def get_attr_from_cars(attr, result=dict, combine_brands=True): - # read all the folders in selfdrive/car and return a dict where: - # - keys are all the car models - # - values are attr values from all car folders - result = result() - - for car_folder in [x[0] for x in os.walk(BASEDIR + '/selfdrive/car')]: - try: - car_name = car_folder.split('/')[-1] - values = __import__(f'selfdrive.car.{car_name}.values', fromlist=[attr]) - if hasattr(values, attr): - attr_values = getattr(values, attr) - else: - continue - - if isinstance(attr_values, dict): - for f, v in attr_values.items(): - if combine_brands: - result[f] = v - else: - if car_name not in result: - result[car_name] = {} - result[car_name][f] = v - elif isinstance(attr_values, list): - result += attr_values - - except (ImportError, OSError): - pass - - return result - - -FW_VERSIONS = get_attr_from_cars('FW_VERSIONS') -_FINGERPRINTS = get_attr_from_cars('FINGERPRINTS') +FW_VERSIONS = get_interface_attr('FW_VERSIONS', combine_brands=True, ignore_none=True) +_FINGERPRINTS = get_interface_attr('FINGERPRINTS', combine_brands=True, ignore_none=True) _DEBUG_ADDRESS = {1880: 8} # reserved for debug purposes + def is_valid_for_fingerprint(msg, car_fingerprint): adr = msg.address # ignore addresses that are more than 11 bits diff --git a/selfdrive/car/ford/carcontroller.py b/selfdrive/car/ford/carcontroller.py index c2f87a480..d7666c1d6 100644 --- a/selfdrive/car/ford/carcontroller.py +++ b/selfdrive/car/ford/carcontroller.py @@ -1,8 +1,8 @@ from cereal import car from common.numpy_fast import clip, interp +from opendbc.can.packer import CANPacker from selfdrive.car.ford import fordcan from selfdrive.car.ford.values import CarControllerParams -from opendbc.can.packer import CANPacker VisualAlert = car.CarControl.HUDControl.VisualAlert @@ -17,11 +17,12 @@ def apply_ford_steer_angle_limits(apply_steer, apply_steer_last, vEgo): return apply_steer -class CarController(): +class CarController: def __init__(self, dbc_name, CP, VM): self.CP = CP self.VM = VM self.packer = CANPacker(dbc_name) + self.frame = 0 self.apply_steer_last = 0 self.steer_rate_limited = False @@ -29,7 +30,7 @@ class CarController(): self.lkas_enabled_last = False self.steer_alert_last = False - def update(self, CC, CS, frame): + def update(self, CC, CS): can_sends = [] actuators = CC.actuators @@ -48,7 +49,7 @@ class CarController(): self.steer_rate_limited = new_steer != apply_steer # send steering commands at 20Hz - if (frame % CarControllerParams.LKAS_STEER_STEP) == 0: + if (self.frame % CarControllerParams.LKAS_STEER_STEP) == 0: lca_rq = 1 if CC.latActive else 0 # use LatCtlPath_An_Actl to actuate steering for now until curvature control is implemented @@ -69,15 +70,14 @@ class CarController(): can_sends.append(fordcan.create_tja_command(self.packer, lca_rq, ramp_type, precision, path_offset, path_angle, curvature_rate, curvature)) - send_ui = (self.main_on_last != main_on) or (self.lkas_enabled_last != CC.latActive) or (self.steer_alert_last != steer_alert) # send lkas ui command at 1Hz or if ui state changes - if (frame % CarControllerParams.LKAS_UI_STEP) == 0 or send_ui: + if (self.frame % CarControllerParams.LKAS_UI_STEP) == 0 or send_ui: can_sends.append(fordcan.create_lkas_ui_command(self.packer, main_on, CC.latActive, steer_alert, CS.lkas_status_stock_values)) # send acc ui command at 20Hz or if ui state changes - if (frame % CarControllerParams.ACC_UI_STEP) == 0 or send_ui: + if (self.frame % CarControllerParams.ACC_UI_STEP) == 0 or send_ui: can_sends.append(fordcan.create_acc_ui_command(self.packer, main_on, CC.latActive, CS.acc_tja_status_stock_values)) self.main_on_last = main_on @@ -87,4 +87,5 @@ class CarController(): new_actuators = actuators.copy() new_actuators.steeringAngleDeg = apply_steer + self.frame += 1 return new_actuators, can_sends diff --git a/selfdrive/car/ford/interface.py b/selfdrive/car/ford/interface.py index ec7c72366..1af04bee4 100644 --- a/selfdrive/car/ford/interface.py +++ b/selfdrive/car/ford/interface.py @@ -59,7 +59,6 @@ class CarInterface(CarInterfaceBase): # LCA can steer down to zero ret.minSteerSpeed = 0. - ret.steerRateCost = 1.0 ret.centerToFront = ret.wheelbase * 0.44 ret.rotationalInertia = scale_rot_inertia(ret.mass, ret.wheelbase) @@ -79,6 +78,4 @@ class CarInterface(CarInterfaceBase): return ret def apply(self, c): - ret = self.CC.update(c, self.CS, self.frame) - self.frame += 1 - return ret + return self.CC.update(c, self.CS) diff --git a/selfdrive/car/ford/radar_interface.py b/selfdrive/car/ford/radar_interface.py index 1dfc27e67..866602cf0 100644 --- a/selfdrive/car/ford/radar_interface.py +++ b/selfdrive/car/ford/radar_interface.py @@ -9,6 +9,9 @@ RADAR_MSGS = list(range(0x500, 0x540)) def _create_radar_can_parser(car_fingerprint): + if DBC[car_fingerprint]['radar'] is None: + return None + msg_n = len(RADAR_MSGS) signals = list(zip(['X_Rel'] * msg_n + ['Angle'] * msg_n + ['V_Rel'] * msg_n, RADAR_MSGS * 3)) @@ -27,6 +30,9 @@ class RadarInterface(RadarInterfaceBase): self.updated_messages = set() def update(self, can_strings): + if self.rcp is None: + return super().update(None) + vls = self.rcp.update_strings(can_strings) self.updated_messages.update(vls) diff --git a/selfdrive/car/ford/values.py b/selfdrive/car/ford/values.py index 96ec87d75..aae011ad3 100644 --- a/selfdrive/car/ford/values.py +++ b/selfdrive/car/ford/values.py @@ -79,6 +79,6 @@ FW_VERSIONS = { DBC = { - CAR.ESCAPE_MK4: dbc_dict('ford_lincoln_base_pt', 'ford_fusion_2018_adas'), - CAR.FOCUS_MK4: dbc_dict('ford_lincoln_base_pt', 'ford_fusion_2018_adas'), + CAR.ESCAPE_MK4: dbc_dict('ford_lincoln_base_pt', None), + CAR.FOCUS_MK4: dbc_dict('ford_lincoln_base_pt', None), } diff --git a/selfdrive/car/fw_versions.py b/selfdrive/car/fw_versions.py index 120174c45..051abc6e0 100755 --- a/selfdrive/car/fw_versions.py +++ b/selfdrive/car/fw_versions.py @@ -1,20 +1,22 @@ #!/usr/bin/env python3 import struct import traceback -from typing import Any, List from collections import defaultdict -from dataclasses import dataclass - +from dataclasses import dataclass, field +from typing import Any, List, Optional, Set, Tuple from tqdm import tqdm import panda.python.uds as uds from cereal import car -from selfdrive.car.fingerprints import FW_VERSIONS, get_attr_from_cars +from selfdrive.car.ecu_addrs import get_ecu_addrs +from selfdrive.car.interfaces import get_interface_attr +from selfdrive.car.fingerprints import FW_VERSIONS from selfdrive.car.isotp_parallel_query import IsoTpParallelQuery from selfdrive.car.toyota.values import CAR as TOYOTA -from selfdrive.swaglog import cloudlog +from system.swaglog import cloudlog Ecu = car.CarParams.Ecu +ESSENTIAL_ECUS = [Ecu.engine, Ecu.eps, Ecu.esp, Ecu.fwdRadar, Ecu.fwdCamera, Ecu.vsa] def p16(val): @@ -90,15 +92,24 @@ SUBARU_VERSION_REQUEST = bytes([uds.SERVICE_TYPE.READ_DATA_BY_IDENTIFIER]) + \ SUBARU_VERSION_RESPONSE = bytes([uds.SERVICE_TYPE.READ_DATA_BY_IDENTIFIER + 0x40]) + \ p16(uds.DATA_IDENTIFIER_TYPE.APPLICATION_DATA_IDENTIFICATION) +CHRYSLER_VERSION_REQUEST = bytes([uds.SERVICE_TYPE.READ_DATA_BY_IDENTIFIER]) + \ + p16(0xf132) +CHRYSLER_VERSION_RESPONSE = bytes([uds.SERVICE_TYPE.READ_DATA_BY_IDENTIFIER + 0x40]) + \ + p16(0xf132) + +CHRYSLER_RX_OFFSET = -0x280 + @dataclass class Request: brand: str request: List[bytes] response: List[bytes] + whitelist_ecus: List[int] = field(default_factory=list) rx_offset: int = DEFAULT_RX_OFFSET bus: int = 1 + REQUESTS: List[Request] = [ # Subaru Request( @@ -144,12 +155,14 @@ REQUESTS: List[Request] = [ "volkswagen", [VOLKSWAGEN_VERSION_REQUEST_MULTI], [VOLKSWAGEN_VERSION_RESPONSE], + whitelist_ecus=[Ecu.srs, Ecu.eps, Ecu.fwdRadar], rx_offset=VOLKSWAGEN_RX_OFFSET, ), Request( "volkswagen", [VOLKSWAGEN_VERSION_REQUEST_MULTI], [VOLKSWAGEN_VERSION_RESPONSE], + whitelist_ecus=[Ecu.engine, Ecu.transmission], ), # Mazda Request( @@ -182,6 +195,18 @@ REQUESTS: List[Request] = [ [TESTER_PRESENT_RESPONSE, UDS_VERSION_RESPONSE], bus=0, ), + # Chrysler / FCA / Stellantis + Request( + "chrysler", + [CHRYSLER_VERSION_REQUEST], + [CHRYSLER_VERSION_RESPONSE], + rx_offset=CHRYSLER_RX_OFFSET, + ), + Request( + "chrysler", + [CHRYSLER_VERSION_REQUEST], + [CHRYSLER_VERSION_RESPONSE], + ), ] @@ -190,13 +215,23 @@ def chunks(l, n=128): yield l[i:i + n] -def build_fw_dict(fw_versions): - fw_versions_dict = {} +def build_fw_dict(fw_versions, filter_brand=None): + fw_versions_dict = defaultdict(set) for fw in fw_versions: - addr = fw.address - sub_addr = fw.subAddress if fw.subAddress != 0 else None - fw_versions_dict[(addr, sub_addr)] = fw.fwVersion - return fw_versions_dict + if filter_brand is None or fw.brand == filter_brand: + addr = fw.address + sub_addr = fw.subAddress if fw.subAddress != 0 else None + fw_versions_dict[(addr, sub_addr)].add(fw.fwVersion) + return dict(fw_versions_dict) + + +def get_brand_addrs(): + versions = get_interface_attr('FW_VERSIONS', ignore_none=True) + brand_addrs = defaultdict(set) + for brand, cars in versions.items(): + for fw in cars.values(): + brand_addrs[brand] |= {(addr, sub_addr) for _, addr, sub_addr in fw.keys()} + return brand_addrs def match_fw_to_car_fuzzy(fw_versions_dict, log=True, exclude=None): @@ -210,7 +245,7 @@ def match_fw_to_car_fuzzy(fw_versions_dict, log=True, exclude=None): # time and only one is in our database. exclude_types = [Ecu.fwdCamera, Ecu.fwdRadar, Ecu.eps, Ecu.debug] - # Build lookup table from (addr, subaddr, fw) to list of candidate cars + # Build lookup table from (addr, sub_addr, fw) to list of candidate cars all_fw_versions = defaultdict(list) for candidate, fw_by_addr in FW_VERSIONS.items(): if candidate == exclude: @@ -224,17 +259,18 @@ def match_fw_to_car_fuzzy(fw_versions_dict, log=True, exclude=None): match_count = 0 candidate = None - for addr, version in fw_versions_dict.items(): - # All cars that have this FW response on the specified address - candidates = all_fw_versions[(addr[0], addr[1], version)] + for addr, versions in fw_versions_dict.items(): + for version in versions: + # All cars that have this FW response on the specified address + candidates = all_fw_versions[(addr[0], addr[1], version)] - if len(candidates) == 1: - match_count += 1 - if candidate is None: - candidate = candidates[0] - # We uniquely matched two different cars. No fuzzy match possible - elif candidate != candidates[0]: - return set() + if len(candidates) == 1: + match_count += 1 + if candidate is None: + candidate = candidates[0] + # We uniquely matched two different cars. No fuzzy match possible + elif candidate != candidates[0]: + return set() if match_count >= 2: if log: @@ -256,24 +292,23 @@ def match_fw_to_car_exact(fw_versions_dict): for ecu, expected_versions in fws.items(): ecu_type = ecu[0] addr = ecu[1:] - found_version = fw_versions_dict.get(addr, None) - ESSENTIAL_ECUS = [Ecu.engine, Ecu.eps, Ecu.esp, Ecu.fwdRadar, Ecu.fwdCamera, Ecu.vsa] - if ecu_type == Ecu.esp and candidate in (TOYOTA.RAV4, TOYOTA.COROLLA, TOYOTA.HIGHLANDER, TOYOTA.SIENNA, TOYOTA.LEXUS_IS) and found_version is None: + found_versions = fw_versions_dict.get(addr, set()) + if ecu_type == Ecu.esp and candidate in (TOYOTA.RAV4, TOYOTA.COROLLA, TOYOTA.HIGHLANDER, TOYOTA.SIENNA, TOYOTA.LEXUS_IS) and not len(found_versions): continue # On some Toyota models, the engine can show on two different addresses - if ecu_type == Ecu.engine and candidate in (TOYOTA.CAMRY, TOYOTA.COROLLA_TSS2, TOYOTA.CHR, TOYOTA.LEXUS_IS) and found_version is None: + if ecu_type == Ecu.engine and candidate in (TOYOTA.CAMRY, TOYOTA.COROLLA_TSS2, TOYOTA.CHR, TOYOTA.LEXUS_IS) and not len(found_versions): continue # Ignore non essential ecus - if ecu_type not in ESSENTIAL_ECUS and found_version is None: + if ecu_type not in ESSENTIAL_ECUS and not len(found_versions): continue # Virtual debug ecu doesn't need to match the database if ecu_type == Ecu.debug: continue - if found_version not in expected_versions: + if not any([found_version in expected_versions for found_version in found_versions]): invalid.append(candidate) break @@ -281,38 +316,113 @@ def match_fw_to_car_exact(fw_versions_dict): def match_fw_to_car(fw_versions, allow_fuzzy=True): - fw_versions_dict = build_fw_dict(fw_versions) - matches = match_fw_to_car_exact(fw_versions_dict) + # Try exact matching first + exact_matches = [(True, match_fw_to_car_exact)] + if allow_fuzzy: + exact_matches.append((False, match_fw_to_car_fuzzy)) - exact_match = True - if allow_fuzzy and len(matches) == 0: - matches = match_fw_to_car_fuzzy(fw_versions_dict) + for exact_match, match_func in exact_matches: + # TODO: For each brand, attempt to fingerprint using only FW returned from its queries + matches = set() + fw_versions_dict = build_fw_dict(fw_versions, filter_brand=None) + matches |= match_func(fw_versions_dict) - # Fuzzy match found + if len(matches): + return exact_match, matches + + return True, set() + + +def get_present_ecus(logcan, sendcan): + queries = list() + parallel_queries = list() + responses = set() + versions = get_interface_attr('FW_VERSIONS', ignore_none=True) + + for r in REQUESTS: + if r.brand not in versions: + continue + + for brand_versions in versions[r.brand].values(): + for ecu_type, addr, sub_addr in brand_versions: + # Only query ecus in whitelist if whitelist is not empty + if len(r.whitelist_ecus) == 0 or ecu_type in r.whitelist_ecus: + a = (addr, sub_addr, r.bus) + # Build set of queries + if sub_addr is None: + if a not in parallel_queries: + parallel_queries.append(a) + else: # subaddresses must be queried one by one + if [a] not in queries: + queries.append([a]) + + # Build set of expected responses to filter + response_addr = uds.get_rx_addr_for_tx_addr(addr, r.rx_offset) + responses.add((response_addr, sub_addr, r.bus)) + + queries.insert(0, parallel_queries) + + ecu_responses: Set[Tuple[int, Optional[int], int]] = set() + for query in queries: + ecu_responses.update(get_ecu_addrs(logcan, sendcan, set(query), responses, timeout=0.1)) + return ecu_responses + + +def get_brand_ecu_matches(ecu_rx_addrs): + """Returns dictionary of brands and matches with ECUs in their FW versions""" + + brand_addrs = get_brand_addrs() + brand_matches = {r.brand: set() for r in REQUESTS} + + brand_rx_offsets = set((r.brand, r.rx_offset) for r in REQUESTS) + for addr, sub_addr, _ in ecu_rx_addrs: + # Since we can't know what request an ecu responded to, add matches for all possible rx offsets + for brand, rx_offset in brand_rx_offsets: + a = (uds.get_rx_addr_for_tx_addr(addr, -rx_offset), sub_addr) + if a in brand_addrs[brand]: + brand_matches[brand].add(a) + + return brand_matches + + +def get_fw_versions_ordered(logcan, sendcan, ecu_rx_addrs, timeout=0.1, debug=False, progress=False): + """Queries for FW versions ordering brands by likelihood, breaks when exact match is found""" + + all_car_fw = [] + brand_matches = get_brand_ecu_matches(ecu_rx_addrs) + + for brand in sorted(brand_matches, key=lambda b: len(brand_matches[b]), reverse=True): + car_fw = get_fw_versions(logcan, sendcan, query_brand=brand, timeout=timeout, debug=debug, progress=progress) + all_car_fw.extend(car_fw) + + # TODO: Until erroneous FW versions are removed, try to fingerprint on all possible combinations so far + _, matches = match_fw_to_car(all_car_fw, allow_fuzzy=False) if len(matches) == 1: - exact_match = False + break - return exact_match, matches + return all_car_fw -def get_fw_versions(logcan, sendcan, extra=None, timeout=0.1, debug=False, progress=False): - ecu_types = {} +def get_fw_versions(logcan, sendcan, query_brand=None, extra=None, timeout=0.1, debug=False, progress=False): + versions = get_interface_attr('FW_VERSIONS', ignore_none=True) + if query_brand is not None: + versions = {query_brand: versions[query_brand]} - # Extract ECU addresses to query from fingerprints - # ECUs using a subadress need be queried one by one, the rest can be done in parallel - addrs = [] - parallel_addrs = [] - - versions = get_attr_from_cars('FW_VERSIONS', combine_brands=False) if extra is not None: versions.update(extra) + # Extract ECU addresses to query from fingerprints + # ECUs using a subaddress need be queried one by one, the rest can be done in parallel + addrs = [] + parallel_addrs = [] + ecu_types = {} + for brand, brand_versions in versions.items(): for c in brand_versions.values(): for ecu_type, addr, sub_addr in c.keys(): a = (brand, addr, sub_addr) if a not in ecu_types: - ecu_types[(addr, sub_addr)] = ecu_type + ecu_types[a] = ecu_type if sub_addr is None: if a not in parallel_addrs: @@ -324,27 +434,31 @@ def get_fw_versions(logcan, sendcan, extra=None, timeout=0.1, debug=False, progr addrs.insert(0, parallel_addrs) fw_versions = {} - for i, addr in enumerate(tqdm(addrs, disable=not progress)): + requests = [r for r in REQUESTS if query_brand is None or r.brand == query_brand] + for addr in tqdm(addrs, disable=not progress): for addr_chunk in chunks(addr): - for r in REQUESTS: + for r in requests: try: - addrs = [(a, s) for (b, a, s) in addr_chunk if b in (r.brand, 'any')] + addrs = [(a, s) for (b, a, s) in addr_chunk if b in (r.brand, 'any') and + (len(r.whitelist_ecus) == 0 or ecu_types[(b, a, s)] in r.whitelist_ecus)] if addrs: query = IsoTpParallelQuery(sendcan, logcan, r.bus, addrs, r.request, r.response, r.rx_offset, debug=debug) - t = 2 * timeout if i == 0 else timeout - fw_versions.update(query.get_data(t)) + fw_versions.update({(r.brand, addr): (version, r) for (addr, _), version in query.get_data(timeout).items()}) except Exception: cloudlog.warning(f"FW query exception: {traceback.format_exc()}") # Build capnp list to put into CarParams car_fw = [] - for addr, version in fw_versions.items(): + for (brand, addr), (version, request) in fw_versions.items(): f = car.CarParams.CarFw.new_message() - f.ecu = ecu_types[addr] + f.ecu = ecu_types[(brand, addr[0], addr[1])] f.fwVersion = version f.address = addr[0] + f.responseAddress = uds.get_rx_addr_for_tx_addr(addr[0], request.rx_offset) + f.request = request.request + f.brand = brand if addr[1] is not None: f.subAddress = addr[1] @@ -363,6 +477,7 @@ if __name__ == "__main__": parser = argparse.ArgumentParser(description='Get firmware version of ECUs') parser.add_argument('--scan', action='store_true') parser.add_argument('--debug', action='store_true') + parser.add_argument('--brand', help='Only query addresses/with requests for this brand') args = parser.parse_args() logcan = messaging.sub_sock('can') @@ -382,21 +497,22 @@ if __name__ == "__main__": t = time.time() print("Getting vin...") - addr, vin = get_vin(logcan, sendcan, 1, retry=10, debug=args.debug) - print(f"VIN: {vin}") + addr, vin_rx_addr, vin = get_vin(logcan, sendcan, 1, retry=10, debug=args.debug) + print(f'TX: {hex(addr)}, RX: {hex(vin_rx_addr)}, VIN: {vin}') print(f"Getting VIN took {time.time() - t:.3f} s") print() t = time.time() - fw_vers = get_fw_versions(logcan, sendcan, extra=extra, debug=args.debug, progress=True) + fw_vers = get_fw_versions(logcan, sendcan, query_brand=args.brand, extra=extra, debug=args.debug, progress=True) _, candidates = match_fw_to_car(fw_vers) print() print("Found FW versions") print("{") + padding = max([len(fw.brand) for fw in fw_vers] or [0]) for version in fw_vers: subaddr = None if version.subAddress == 0 else hex(version.subAddress) - print(f" (Ecu.{version.ecu}, {hex(version.address)}, {subaddr}): [{version.fwVersion}]") + print(f" Brand: {version.brand:{padding}} - (Ecu.{version.ecu}, {hex(version.address)}, {subaddr}): [{version.fwVersion}]") print("}") print() diff --git a/selfdrive/car/gm/carcontroller.py b/selfdrive/car/gm/carcontroller.py index ae2a188e3..f763a5853 100644 --- a/selfdrive/car/gm/carcontroller.py +++ b/selfdrive/car/gm/carcontroller.py @@ -1,17 +1,19 @@ from cereal import car from common.conversions import Conversions as CV -from common.realtime import DT_CTRL from common.numpy_fast import interp +from common.realtime import DT_CTRL from opendbc.can.packer import CANPacker from selfdrive.car import apply_std_steer_torque_limits from selfdrive.car.gm import gmcan from selfdrive.car.gm.values import DBC, CanBus, CarControllerParams VisualAlert = car.CarControl.HUDControl.VisualAlert +NetworkLocation = car.CarParams.NetworkLocation class CarController: def __init__(self, dbc_name, CP, VM): + self.CP = CP self.start_time = 0. self.apply_steer_last = 0 self.apply_gas = 0 @@ -24,9 +26,9 @@ class CarController: self.params = CarControllerParams() - self.packer_pt = CANPacker(DBC[CP.carFingerprint]['pt']) - self.packer_obj = CANPacker(DBC[CP.carFingerprint]['radar']) - self.packer_ch = CANPacker(DBC[CP.carFingerprint]['chassis']) + self.packer_pt = CANPacker(DBC[self.CP.carFingerprint]['pt']) + self.packer_obj = CANPacker(DBC[self.CP.carFingerprint]['radar']) + self.packer_ch = CANPacker(DBC[self.CP.carFingerprint]['chassis']) def update(self, CC, CS): actuators = CC.actuators @@ -60,48 +62,48 @@ class CarController: can_sends.append(gmcan.create_steering_control(self.packer_pt, CanBus.POWERTRAIN, apply_steer, idx, lkas_enabled)) - # Gas/regen and brakes - all at 25Hz - if (self.frame % 4) == 0: - if not CC.longActive: - # Stock ECU sends max regen when not enabled - self.apply_gas = self.params.MAX_ACC_REGEN - self.apply_brake = 0 - else: - self.apply_gas = int(round(interp(actuators.accel, self.params.GAS_LOOKUP_BP, self.params.GAS_LOOKUP_V))) - self.apply_brake = int(round(interp(actuators.accel, self.params.BRAKE_LOOKUP_BP, self.params.BRAKE_LOOKUP_V))) + if self.CP.openpilotLongitudinalControl: + # Gas/regen, brakes, and UI commands - all at 25Hz + if self.frame % 4 == 0: + if not CC.longActive: + # Stock ECU sends max regen when not enabled + self.apply_gas = self.params.MAX_ACC_REGEN + self.apply_brake = 0 + else: + self.apply_gas = int(round(interp(actuators.accel, self.params.GAS_LOOKUP_BP, self.params.GAS_LOOKUP_V))) + self.apply_brake = int(round(interp(actuators.accel, self.params.BRAKE_LOOKUP_BP, self.params.BRAKE_LOOKUP_V))) - idx = (self.frame // 4) % 4 + idx = (self.frame // 4) % 4 - at_full_stop = CC.longActive and CS.out.standstill - near_stop = CC.longActive and (CS.out.vEgo < self.params.NEAR_STOP_BRAKE_PHASE) - # GasRegenCmdActive needs to be 1 to avoid cruise faults. It describes the ACC state, not actuation - can_sends.append(gmcan.create_gas_regen_command(self.packer_pt, CanBus.POWERTRAIN, self.apply_gas, idx, CC.enabled, at_full_stop)) - can_sends.append(gmcan.create_friction_brake_command(self.packer_ch, CanBus.CHASSIS, self.apply_brake, idx, near_stop, at_full_stop)) + at_full_stop = CC.longActive and CS.out.standstill + near_stop = CC.longActive and (CS.out.vEgo < self.params.NEAR_STOP_BRAKE_PHASE) + # GasRegenCmdActive needs to be 1 to avoid cruise faults. It describes the ACC state, not actuation + can_sends.append(gmcan.create_gas_regen_command(self.packer_pt, CanBus.POWERTRAIN, self.apply_gas, idx, CC.enabled, at_full_stop)) + can_sends.append(gmcan.create_friction_brake_command(self.packer_ch, CanBus.CHASSIS, self.apply_brake, idx, near_stop, at_full_stop)) - # Send dashboard UI commands (ACC status), 25hz - if (self.frame % 4) == 0: - send_fcw = hud_alert == VisualAlert.fcw - can_sends.append(gmcan.create_acc_dashboard_command(self.packer_pt, CanBus.POWERTRAIN, CC.enabled, - hud_v_cruise * CV.MS_TO_KPH, hud_control.leadVisible, send_fcw)) + # Send dashboard UI commands (ACC status) + send_fcw = hud_alert == VisualAlert.fcw + can_sends.append(gmcan.create_acc_dashboard_command(self.packer_pt, CanBus.POWERTRAIN, CC.enabled, + hud_v_cruise * CV.MS_TO_KPH, hud_control.leadVisible, send_fcw)) - # Radar needs to know current speed and yaw rate (50hz), - # and that ADAS is alive (10hz) - time_and_headlights_step = 10 - tt = self.frame * DT_CTRL + # Radar needs to know current speed and yaw rate (50hz), + # and that ADAS is alive (10hz) + if not self.CP.radarOffCan: + tt = self.frame * DT_CTRL + time_and_headlights_step = 10 + if self.frame % time_and_headlights_step == 0: + idx = (self.frame // time_and_headlights_step) % 4 + can_sends.append(gmcan.create_adas_time_status(CanBus.OBSTACLE, int((tt - self.start_time) * 60), idx)) + can_sends.append(gmcan.create_adas_headlights_status(self.packer_obj, CanBus.OBSTACLE)) - if self.frame % time_and_headlights_step == 0: - idx = (self.frame // time_and_headlights_step) % 4 - can_sends.append(gmcan.create_adas_time_status(CanBus.OBSTACLE, int((tt - self.start_time) * 60), idx)) - can_sends.append(gmcan.create_adas_headlights_status(self.packer_obj, CanBus.OBSTACLE)) + speed_and_accelerometer_step = 2 + if self.frame % speed_and_accelerometer_step == 0: + idx = (self.frame // speed_and_accelerometer_step) % 4 + can_sends.append(gmcan.create_adas_steering_status(CanBus.OBSTACLE, idx)) + can_sends.append(gmcan.create_adas_accelerometer_speed_status(CanBus.OBSTACLE, CS.out.vEgo, idx)) - speed_and_accelerometer_step = 2 - if self.frame % speed_and_accelerometer_step == 0: - idx = (self.frame // speed_and_accelerometer_step) % 4 - can_sends.append(gmcan.create_adas_steering_status(CanBus.OBSTACLE, idx)) - can_sends.append(gmcan.create_adas_accelerometer_speed_status(CanBus.OBSTACLE, CS.out.vEgo, idx)) - - if self.frame % self.params.ADAS_KEEPALIVE_STEP == 0: - can_sends += gmcan.create_adas_keepalive(CanBus.POWERTRAIN) + if self.CP.networkLocation == NetworkLocation.gateway and self.frame % self.params.ADAS_KEEPALIVE_STEP == 0: + can_sends += gmcan.create_adas_keepalive(CanBus.POWERTRAIN) # Show green icon when LKA torque is applied, and # alarming orange icon when approaching torque limit. @@ -110,7 +112,7 @@ class CarController: lka_active = CS.lkas_status == 1 lka_critical = lka_active and abs(actuators.steer) > 0.9 lka_icon_status = (lka_active, lka_critical) - if self.frame % self.params.CAMERA_KEEPALIVE_STEP == 0 or lka_icon_status != self.lka_icon_status_last: + if self.CP.networkLocation != NetworkLocation.fwdCamera and (self.frame % self.params.CAMERA_KEEPALIVE_STEP == 0 or lka_icon_status != self.lka_icon_status_last): steer_alert = hud_alert in (VisualAlert.steerRequired, VisualAlert.ldw) can_sends.append(gmcan.create_lka_icon_command(CanBus.SW_GMLAN, lka_active, lka_critical, steer_alert)) self.lka_icon_status_last = lka_icon_status diff --git a/selfdrive/car/gm/carstate.py b/selfdrive/car/gm/carstate.py index e6a1c08dd..c28abc603 100644 --- a/selfdrive/car/gm/carstate.py +++ b/selfdrive/car/gm/carstate.py @@ -3,7 +3,9 @@ from common.numpy_fast import mean from opendbc.can.can_define import CANDefine from opendbc.can.parser import CANParser from selfdrive.car.interfaces import CarStateBase -from selfdrive.car.gm.values import DBC, CAR, AccState, CanBus, STEER_THRESHOLD +from selfdrive.car.gm.values import DBC, AccState, CanBus, STEER_THRESHOLD + +TransmissionType = car.CarParams.TransmissionType class CarState(CarStateBase): @@ -35,7 +37,7 @@ class CarState(CarStateBase): ret.brakePressed = pt_cp.vl["EBCMBrakePedalPosition"]["BrakePedalPosition"] >= 10 # Regen braking is braking - if self.car_fingerprint == CAR.VOLT: + if self.CP.transmissionType == TransmissionType.direct: ret.brakePressed = ret.brakePressed or pt_cp.vl["EBCMRegenPaddle"]["RegenPaddle"] != 0 ret.gas = pt_cp.vl["AcceleratorPedal2"]["AcceleratorPedal2"] / 254. @@ -64,7 +66,7 @@ class CarState(CarStateBase): ret.leftBlinker = pt_cp.vl["BCMTurnSignals"]["TurnSignals"] == 1 ret.rightBlinker = pt_cp.vl["BCMTurnSignals"]["TurnSignals"] == 2 - ret.parkingBrake = pt_cp.vl["EPBStatus"]["EPBClosed"] == 1 + ret.parkingBrake = pt_cp.vl["VehicleIgnitionAlt"]["ParkBrake"] == 1 ret.cruiseState.available = pt_cp.vl["ECMEngineStatus"]["CruiseMainOn"] != 0 ret.espDisabled = pt_cp.vl["ESPStatus"]["TractionControlOn"] != 1 ret.accFaulted = pt_cp.vl["AcceleratorPedal2"]["CruiseState"] == AccState.FAULTED @@ -100,7 +102,7 @@ class CarState(CarStateBase): ("LKATorqueDelivered", "PSCMStatus"), ("LKATorqueDeliveredStatus", "PSCMStatus"), ("TractionControlOn", "ESPStatus"), - ("EPBClosed", "EPBStatus"), + ("ParkBrake", "VehicleIgnitionAlt"), ("CruiseMainOn", "ECMEngineStatus"), ] @@ -110,7 +112,7 @@ class CarState(CarStateBase): ("PSCMStatus", 10), ("ESPStatus", 10), ("BCMDoorBeltStatus", 10), - ("EPBStatus", 20), + ("VehicleIgnitionAlt", 10), ("EBCMWheelSpdFront", 20), ("EBCMWheelSpdRear", 20), ("AcceleratorPedal2", 33), @@ -120,7 +122,7 @@ class CarState(CarStateBase): ("EBCMBrakePedalPosition", 100), ] - if CP.carFingerprint == CAR.VOLT: + if CP.transmissionType == TransmissionType.direct: signals.append(("RegenPaddle", "EBCMRegenPaddle")) checks.append(("EBCMRegenPaddle", 50)) @@ -133,7 +135,9 @@ class CarState(CarStateBase): ] checks = [ - ("ASCMLKASteeringCmd", 50), + ("ASCMLKASteeringCmd", 10), # 10 Hz is the stock inactive rate (every 100ms). + # While active 50 Hz (every 20 ms) is normal + # EPS will tolerate around 200ms when active before faulting ] return CANParser(DBC[CP.carFingerprint]["pt"], signals, checks, CanBus.LOOPBACK) diff --git a/selfdrive/car/gm/interface.py b/selfdrive/car/gm/interface.py index d00708c33..e0dd10d4a 100755 --- a/selfdrive/car/gm/interface.py +++ b/selfdrive/car/gm/interface.py @@ -3,12 +3,17 @@ from cereal import car from math import fabs from common.conversions import Conversions as CV -from selfdrive.car import STD_CARGO_KG, scale_rot_inertia, scale_tire_stiffness, gen_empty_fingerprint, get_safety_config +from selfdrive.car import STD_CARGO_KG, create_button_enable_events, create_button_event, scale_rot_inertia, scale_tire_stiffness, gen_empty_fingerprint, get_safety_config from selfdrive.car.gm.values import CAR, CruiseButtons, CarControllerParams from selfdrive.car.interfaces import CarInterfaceBase ButtonType = car.CarState.ButtonEvent.Type EventName = car.CarEvent.EventName +TransmissionType = car.CarParams.TransmissionType +NetworkLocation = car.CarParams.NetworkLocation +BUTTONS_DICT = {CruiseButtons.RES_ACCEL: ButtonType.accelCruise, CruiseButtons.DECEL_SET: ButtonType.decelCruise, + CruiseButtons.MAIN: ButtonType.altButton3, CruiseButtons.CANCEL: ButtonType.cancel} + class CarInterface(CarInterfaceBase): @staticmethod @@ -42,7 +47,11 @@ class CarInterface(CarInterfaceBase): ret = CarInterfaceBase.get_std_params(candidate, fingerprint) ret.carName = "gm" ret.safetyConfigs = [get_safety_config(car.CarParams.SafetyModel.gm)] - ret.pcmCruise = False # stock cruise control is kept off + ret.pcmCruise = False # For ASCM, stock non-adaptive cruise control is kept off + ret.radarOffCan = False # For ASCM, radar exists + ret.transmissionType = TransmissionType.automatic + # NetworkLocation.gateway: OBD-II harness (typically ASCM), NetworkLocation.fwdCamera: non-ASCM + ret.networkLocation = NetworkLocation.gateway # These cars have been put into dashcam only due to both a lack of users and test coverage. # These cars likely still work fine. Once a user confirms each car works and a test route is @@ -55,38 +64,43 @@ class CarInterface(CarInterfaceBase): ret.openpilotLongitudinalControl = True tire_stiffness_factor = 0.444 # not optimized yet - # Start with a baseline lateral tuning for all GM vehicles. Override tuning as needed in each model section below. + # Start with a baseline tuning for all GM vehicles. Override tuning as needed in each model section below. ret.minSteerSpeed = 7 * CV.MPH_TO_MS ret.lateralTuning.pid.kiBP, ret.lateralTuning.pid.kpBP = [[0.], [0.]] ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.2], [0.00]] ret.lateralTuning.pid.kf = 0.00004 # full torque for 20 deg at 80mph means 0.00007818594 - ret.steerRateCost = 1.0 ret.steerActuatorDelay = 0.1 # Default delay, not measured yet + ret.longitudinalTuning.kpBP = [5., 35.] + ret.longitudinalTuning.kpV = [2.4, 1.5] + ret.longitudinalTuning.kiBP = [0.] + ret.longitudinalTuning.kiV = [0.36] + + ret.steerLimitTimer = 0.4 + ret.radarTimeStep = 0.0667 # GM radar runs at 15Hz instead of standard 20Hz + + # supports stop and go, but initial engage must (conservatively) be above 18mph + ret.minEnableSpeed = 18 * CV.MPH_TO_MS + if candidate == CAR.VOLT: - # supports stop and go, but initial engage must be above 18mph (which include conservatism) - ret.minEnableSpeed = 18 * CV.MPH_TO_MS + ret.transmissionType = TransmissionType.direct ret.mass = 1607. + STD_CARGO_KG ret.wheelbase = 2.69 ret.steerRatio = 17.7 # Stock 15.7, LiveParameters - tire_stiffness_factor = 0.469 # Stock Michelin Energy Saver A/S, LiveParameters - ret.steerRatioRear = 0. - ret.centerToFront = ret.wheelbase * 0.45 # Volt Gen 1, TODO corner weigh + tire_stiffness_factor = 0.469 # Stock Michelin Energy Saver A/S, LiveParameters + ret.centerToFront = ret.wheelbase * 0.45 # Volt Gen 1, TODO corner weigh ret.lateralTuning.pid.kpBP = [0., 40.] ret.lateralTuning.pid.kpV = [0., 0.17] ret.lateralTuning.pid.kiBP = [0.] ret.lateralTuning.pid.kiV = [0.] - ret.lateralTuning.pid.kf = 1. # get_steer_feedforward_volt() + ret.lateralTuning.pid.kf = 1. # get_steer_feedforward_volt() ret.steerActuatorDelay = 0.2 elif candidate == CAR.MALIBU: - # supports stop and go, but initial engage must be above 18mph (which include conservatism) - ret.minEnableSpeed = 18 * CV.MPH_TO_MS ret.mass = 1496. + STD_CARGO_KG ret.wheelbase = 2.83 ret.steerRatio = 15.8 - ret.steerRatioRear = 0. ret.centerToFront = ret.wheelbase * 0.4 # wild guess elif candidate == CAR.HOLDEN_ASTRA: @@ -94,33 +108,26 @@ class CarInterface(CarInterfaceBase): ret.wheelbase = 2.662 # Remaining parameters copied from Volt for now ret.centerToFront = ret.wheelbase * 0.4 - ret.minEnableSpeed = 18 * CV.MPH_TO_MS ret.steerRatio = 15.7 - ret.steerRatioRear = 0. elif candidate == CAR.ACADIA: ret.minEnableSpeed = -1. # engage speed is decided by pcm ret.mass = 4353. * CV.LB_TO_KG + STD_CARGO_KG ret.wheelbase = 2.86 ret.steerRatio = 14.4 # end to end is 13.46 - ret.steerRatioRear = 0. ret.centerToFront = ret.wheelbase * 0.4 - ret.lateralTuning.pid.kf = 1. # get_steer_feedforward_acadia() + ret.lateralTuning.pid.kf = 1. # get_steer_feedforward_acadia() elif candidate == CAR.BUICK_REGAL: - ret.minEnableSpeed = 18 * CV.MPH_TO_MS ret.mass = 3779. * CV.LB_TO_KG + STD_CARGO_KG # (3849+3708)/2 ret.wheelbase = 2.83 # 111.4 inches in meters ret.steerRatio = 14.4 # guess for tourx - ret.steerRatioRear = 0. ret.centerToFront = ret.wheelbase * 0.4 # guess for tourx elif candidate == CAR.CADILLAC_ATS: - ret.minEnableSpeed = 18 * CV.MPH_TO_MS ret.mass = 1601. + STD_CARGO_KG ret.wheelbase = 2.78 ret.steerRatio = 15.3 - ret.steerRatioRear = 0. ret.centerToFront = ret.wheelbase * 0.49 elif candidate == CAR.ESCALADE_ESV: @@ -143,14 +150,6 @@ class CarInterface(CarInterfaceBase): ret.tireStiffnessFront, ret.tireStiffnessRear = scale_tire_stiffness(ret.mass, ret.wheelbase, ret.centerToFront, tire_stiffness_factor=tire_stiffness_factor) - ret.longitudinalTuning.kpBP = [5., 35.] - ret.longitudinalTuning.kpV = [2.4, 1.5] - ret.longitudinalTuning.kiBP = [0.] - ret.longitudinalTuning.kiV = [0.36] - - ret.steerLimitTimer = 0.4 - ret.radarTimeStep = 0.0667 # GM radar runs at 15Hz instead of standard 20Hz - return ret # returns a car.CarState @@ -159,31 +158,16 @@ class CarInterface(CarInterfaceBase): ret.steeringRateLimited = self.CC.steer_rate_limited if self.CC is not None else False - buttonEvents = [] - if self.CS.cruise_buttons != self.CS.prev_cruise_buttons and self.CS.prev_cruise_buttons != CruiseButtons.INIT: - be = car.CarState.ButtonEvent.new_message() - be.type = ButtonType.unknown - if self.CS.cruise_buttons != CruiseButtons.UNPRESS: - be.pressed = True - but = self.CS.cruise_buttons - else: - be.pressed = False - but = self.CS.prev_cruise_buttons - if but == CruiseButtons.RES_ACCEL: - if not (ret.cruiseState.enabled and ret.standstill): - be.type = ButtonType.accelCruise # Suppress resume button if we're resuming from stop so we don't adjust speed. - elif but == CruiseButtons.DECEL_SET: - be.type = ButtonType.decelCruise - elif but == CruiseButtons.CANCEL: - be.type = ButtonType.cancel - elif but == CruiseButtons.MAIN: - be.type = ButtonType.altButton3 - buttonEvents.append(be) + be = create_button_event(self.CS.cruise_buttons, self.CS.prev_cruise_buttons, BUTTONS_DICT, CruiseButtons.UNPRESS) - ret.buttonEvents = buttonEvents + # Suppress resume button if we're resuming from stop so we don't adjust speed. + if be.type == ButtonType.accelCruise and (ret.cruiseState.enabled and ret.standstill): + be.type = ButtonType.unknown - events = self.create_common_events(ret, pcm_enable=False) + ret.buttonEvents = [be] + + events = self.create_common_events(ret, pcm_enable=self.CP.pcmCruise) if ret.vEgo < self.CP.minEnableSpeed: events.add(EventName.belowEngageSpeed) @@ -193,18 +177,11 @@ class CarInterface(CarInterfaceBase): events.add(car.CarEvent.EventName.belowSteerSpeed) # handle button presses - for b in ret.buttonEvents: - # do enable on both accel and decel buttons - if b.type in (ButtonType.accelCruise, ButtonType.decelCruise) and not b.pressed: - events.add(EventName.buttonEnable) - # do disable on button down - if b.type == ButtonType.cancel and b.pressed: - events.add(EventName.buttonCancel) + events.events.extend(create_button_enable_events(ret.buttonEvents, pcm_cruise=self.CP.pcmCruise)) ret.events = events.to_msg() return ret def apply(self, c): - ret = self.CC.update(c, self.CS) - return ret + return self.CC.update(c, self.CS) diff --git a/selfdrive/car/gm/values.py b/selfdrive/car/gm/values.py index 8ef33e829..328ca7c28 100644 --- a/selfdrive/car/gm/values.py +++ b/selfdrive/car/gm/values.py @@ -1,4 +1,5 @@ -from dataclasses import dataclass +from collections import defaultdict +from dataclasses import dataclass, field from enum import Enum from typing import Dict, List, Union @@ -9,9 +10,9 @@ Ecu = car.CarParams.Ecu class CarControllerParams: - STEER_MAX = 300 # Safety limit, not LKA max. Trucks use 600. - STEER_STEP = 2 # control frames per command - STEER_DELTA_UP = 7 + STEER_MAX = 300 # GM limit is 3Nm. Used by carcontroller to generate LKA output + STEER_STEP = 2 # Control frames per command (50hz) + STEER_DELTA_UP = 7 # Delta rates require review due to observed EPS weakness STEER_DELTA_DOWN = 17 MIN_STEER_SPEED = 3. # m/s STEER_DRIVER_ALLOWANCE = 50 @@ -24,19 +25,20 @@ class CarControllerParams: CAMERA_KEEPALIVE_STEP = 100 # Volt gasbrake lookups - MAX_GAS = 3072 # Safety limit, not ACC max. Stock ACC >4096 from standstill. - ZERO_GAS = 2048 # Coasting - MAX_BRAKE = 350 # ~ -3.5 m/s^2 with regen + # TODO: These values should be confirmed on non-Volt vehicles + MAX_GAS = 3072 # Safety limit, not ACC max. Stock ACC >4096 from standstill. + ZERO_GAS = 2048 # Coasting + MAX_BRAKE = 350 # ~ -3.5 m/s^2 with regen + MAX_ACC_REGEN = 1404 # Max ACC regen is slightly less than max paddle regen # Allow small margin below -3.5 m/s^2 from ISO 15622:2018 since we # perform the closed loop control, and might need some # to apply some more braking if we're on a downhill slope. # Our controller should still keep the 2 second average above # -3.5 m/s^2 as per planner limits - ACCEL_MAX = 2. # m/s^2 - ACCEL_MIN = -4. # m/s^2 + ACCEL_MAX = 2. # m/s^2 + ACCEL_MIN = -4. # m/s^2 - MAX_ACC_REGEN = 1404 # Max ACC regen is slightly less than max paddle regen GAS_LOOKUP_BP = [-1., 0., ACCEL_MAX] GAS_LOOKUP_V = [MAX_ACC_REGEN, ZERO_GAS, MAX_GAS] BRAKE_LOOKUP_BP = [ACCEL_MIN, -1.] @@ -67,16 +69,17 @@ class Footnote(Enum): class GMCarInfo(CarInfo): package: str = "Adaptive Cruise" harness: Enum = Harness.none + footnotes: List[Enum] = field(default_factory=lambda: [Footnote.OBD_II]) CAR_INFO: Dict[str, Union[GMCarInfo, List[GMCarInfo]]] = { CAR.HOLDEN_ASTRA: GMCarInfo("Holden Astra 2017", harness=Harness.custom), - CAR.VOLT: GMCarInfo("Chevrolet Volt 2017-18", footnotes=[Footnote.OBD_II], min_enable_speed=0, harness=Harness.custom), + CAR.VOLT: GMCarInfo("Chevrolet Volt 2017-18", min_enable_speed=0, harness=Harness.custom), CAR.CADILLAC_ATS: GMCarInfo("Cadillac ATS Premium Performance 2018"), CAR.MALIBU: GMCarInfo("Chevrolet Malibu Premier 2017", harness=Harness.custom), - CAR.ACADIA: GMCarInfo("GMC Acadia 2018", video_link="https://www.youtube.com/watch?v=0ZN6DdsBUZo", footnotes=[Footnote.OBD_II]), + CAR.ACADIA: GMCarInfo("GMC Acadia 2018", video_link="https://www.youtube.com/watch?v=0ZN6DdsBUZo"), CAR.BUICK_REGAL: GMCarInfo("Buick Regal Essence 2018"), - CAR.ESCALADE_ESV: GMCarInfo("Cadillac Escalade ESV 2016", "ACC + LKAS", footnotes=[Footnote.OBD_II]), + CAR.ESCALADE_ESV: GMCarInfo("Cadillac Escalade ESV 2016", "ACC + LKAS"), } @@ -100,10 +103,12 @@ class CanBus: CHASSIS = 2 SW_GMLAN = 3 LOOPBACK = 128 + DROPPED = 192 FINGERPRINTS = { + CAR.HOLDEN_ASTRA: [ # Astra BK MY17, ASCM unplugged - CAR.HOLDEN_ASTRA: [{ + { 190: 8, 193: 8, 197: 8, 199: 4, 201: 8, 209: 7, 211: 8, 241: 6, 249: 8, 288: 5, 298: 8, 304: 1, 309: 8, 311: 8, 313: 8, 320: 3, 328: 1, 352: 5, 381: 6, 384: 4, 386: 8, 388: 8, 393: 8, 398: 8, 401: 8, 413: 8, 417: 8, 419: 8, 422: 1, 426: 7, 431: 8, 442: 8, 451: 8, 452: 8, 453: 8, 455: 7, 456: 8, 458: 5, 479: 8, 481: 7, 485: 8, 489: 8, 497: 8, 499: 3, 500: 8, 501: 8, 508: 8, 528: 5, 532: 6, 554: 3, 560: 8, 562: 8, 563: 5, 564: 5, 565: 5, 567: 5, 647: 5, 707: 8, 715: 8, 723: 8, 753: 5, 761: 7, 806: 1, 810: 8, 840: 5, 842: 5, 844: 8, 866: 4, 961: 8, 969: 8, 977: 8, 979: 8, 985: 5, 1001: 8, 1009: 8, 1011: 6, 1017: 8, 1019: 3, 1020: 8, 1105: 6, 1217: 8, 1221: 5, 1225: 8, 1233: 8, 1249: 8, 1257: 6, 1259: 8, 1261: 7, 1263: 4, 1265: 8, 1267: 8, 1280: 4, 1300: 8, 1328: 4, 1417: 8, 1906: 7, 1907: 7, 1908: 7, 1912: 7, 1919: 7, }], CAR.VOLT: [ @@ -145,12 +150,4 @@ FINGERPRINTS = { }], } -DBC = { - CAR.HOLDEN_ASTRA: dbc_dict('gm_global_a_powertrain_generated', 'gm_global_a_object', chassis_dbc='gm_global_a_chassis'), - CAR.VOLT: dbc_dict('gm_global_a_powertrain_generated', 'gm_global_a_object', chassis_dbc='gm_global_a_chassis'), - CAR.MALIBU: dbc_dict('gm_global_a_powertrain_generated', 'gm_global_a_object', chassis_dbc='gm_global_a_chassis'), - CAR.ACADIA: dbc_dict('gm_global_a_powertrain_generated', 'gm_global_a_object', chassis_dbc='gm_global_a_chassis'), - CAR.CADILLAC_ATS: dbc_dict('gm_global_a_powertrain_generated', 'gm_global_a_object', chassis_dbc='gm_global_a_chassis'), - CAR.BUICK_REGAL: dbc_dict('gm_global_a_powertrain_generated', 'gm_global_a_object', chassis_dbc='gm_global_a_chassis'), - CAR.ESCALADE_ESV: dbc_dict('gm_global_a_powertrain_generated', 'gm_global_a_object', chassis_dbc='gm_global_a_chassis'), -} +DBC: Dict[str, Dict[str, str]] = defaultdict(lambda: dbc_dict('gm_global_a_powertrain_generated', 'gm_global_a_object', chassis_dbc='gm_global_a_chassis')) diff --git a/selfdrive/car/honda/carcontroller.py b/selfdrive/car/honda/carcontroller.py index cf01fa2aa..d47caaa9a 100644 --- a/selfdrive/car/honda/carcontroller.py +++ b/selfdrive/car/honda/carcontroller.py @@ -7,7 +7,7 @@ from common.realtime import DT_CTRL from opendbc.can.packer import CANPacker from selfdrive.car import create_gas_interceptor_command from selfdrive.car.honda import hondacan -from selfdrive.car.honda.values import CruiseButtons, VISUAL_HUD, HONDA_BOSCH, HONDA_NIDEC_ALT_PCM_ACCEL, CarControllerParams +from selfdrive.car.honda.values import CruiseButtons, VISUAL_HUD, HONDA_BOSCH, HONDA_BOSCH_RADARLESS, HONDA_NIDEC_ALT_PCM_ACCEL, CarControllerParams from selfdrive.controls.lib.drive_helpers import rate_limit VisualAlert = car.CarControl.HUDControl.VisualAlert @@ -95,8 +95,8 @@ def process_hud_alert(hud_alert): HUDData = namedtuple("HUDData", - ["pcm_accel", "v_cruise", "car", - "lanes", "fcw", "acc_alert", "steer_required"]) + ["pcm_accel", "v_cruise", "lead_visible", + "lanes_visible", "fcw", "acc_alert", "steer_required"]) class CarController: @@ -138,19 +138,6 @@ class CarController: self.brake_last = rate_limit(pre_limit_brake, self.brake_last, -2., DT_CTRL) # vehicle hud display, wait for one update from 10Hz 0x304 msg - if hud_control.lanesVisible: - hud_lanes = 1 - else: - hud_lanes = 0 - - if CC.enabled: - if hud_control.leadVisible: - hud_car = 2 - else: - hud_car = 1 - else: - hud_car = 0 - fcw_display, steer_required, acc_alert = process_hud_alert(hud_control.visualAlert) # **** process the car messages **** @@ -172,8 +159,6 @@ class CarController: can_sends.append(hondacan.create_steering_control(self.packer, apply_steer, CC.latActive, self.CP.carFingerprint, idx, CS.CP.openpilotLongitudinalControl)) - stopping = actuators.longControlState == LongCtrlState.stopping - # wind brake from air resistance decel at high speed wind_brake = interp(CS.out.vEgo, [0.0, 2.3, 35.0], [0.001, 0.002, 0.15]) # all of this is only relevant for HONDA NIDEC @@ -204,13 +189,13 @@ class CarController: pcm_accel = int(clip((accel / 1.44) / max_accel, 0.0, 1.0) * 0xc6) if not self.CP.openpilotLongitudinalControl: - if self.frame % 2 == 0: + if self.frame % 2 == 0 and self.CP.carFingerprint not in HONDA_BOSCH_RADARLESS: # radarless cars don't have supplemental message idx = self.frame // 2 can_sends.append(hondacan.create_bosch_supplemental_1(self.packer, self.CP.carFingerprint, idx)) # If using stock ACC, spam cancel command to kill gas when OP disengages. if pcm_cancel_cmd: can_sends.append(hondacan.spam_buttons_command(self.packer, CruiseButtons.CANCEL, idx, self.CP.carFingerprint)) - elif CS.out.cruiseState.standstill: + elif CC.cruiseControl.resume: can_sends.append(hondacan.spam_buttons_command(self.packer, CruiseButtons.RES_ACCEL, idx, self.CP.carFingerprint)) else: @@ -222,6 +207,8 @@ class CarController: if self.CP.carFingerprint in HONDA_BOSCH: self.accel = clip(accel, self.params.BOSCH_ACCEL_MIN, self.params.BOSCH_ACCEL_MAX) self.gas = interp(accel, self.params.BOSCH_GAS_LOOKUP_BP, self.params.BOSCH_GAS_LOOKUP_V) + + stopping = actuators.longControlState == LongCtrlState.stopping can_sends.extend(hondacan.create_acc_commands(self.packer, CC.enabled, CC.longActive, self.accel, self.gas, idx, stopping, self.CP.carFingerprint)) else: @@ -252,9 +239,9 @@ class CarController: # Send dashboard UI commands. if self.frame % 10 == 0: idx = (self.frame // 10) % 4 - hud = HUDData(int(pcm_accel), int(round(hud_v_cruise)), hud_car, - hud_lanes, fcw_display, acc_alert, steer_required) - can_sends.extend(hondacan.create_ui_commands(self.packer, self.CP, pcm_speed, hud, CS.is_metric, idx, CS.stock_hud)) + hud = HUDData(int(pcm_accel), int(round(hud_v_cruise)), hud_control.leadVisible, + hud_control.lanesVisible, fcw_display, acc_alert, steer_required) + can_sends.extend(hondacan.create_ui_commands(self.packer, self.CP, CC.enabled, pcm_speed, hud, CS.is_metric, idx, CS.stock_hud)) if self.CP.openpilotLongitudinalControl and self.CP.carFingerprint not in HONDA_BOSCH: self.speed = pcm_speed diff --git a/selfdrive/car/honda/carstate.py b/selfdrive/car/honda/carstate.py index 5314fe375..5358ce249 100644 --- a/selfdrive/car/honda/carstate.py +++ b/selfdrive/car/honda/carstate.py @@ -5,8 +5,9 @@ from common.conversions import Conversions as CV from common.numpy_fast import interp from opendbc.can.can_define import CANDefine from opendbc.can.parser import CANParser +from selfdrive.car.honda.hondacan import get_pt_bus +from selfdrive.car.honda.values import CAR, DBC, STEER_THRESHOLD, HONDA_BOSCH, HONDA_NIDEC_ALT_SCM_MESSAGES, HONDA_BOSCH_ALT_BRAKE_SIGNAL, HONDA_BOSCH_RADARLESS from selfdrive.car.interfaces import CarStateBase -from selfdrive.car.honda.values import CAR, DBC, STEER_THRESHOLD, HONDA_BOSCH, HONDA_NIDEC_ALT_SCM_MESSAGES, HONDA_BOSCH_ALT_BRAKE_SIGNAL TransmissionType = car.CarParams.TransmissionType @@ -22,9 +23,9 @@ def get_can_signals(CP, gearbox_msg, main_on_sig_msg): ("STEER_ANGLE_RATE", "STEERING_SENSORS"), ("MOTOR_TORQUE", "STEER_MOTOR_TORQUE"), ("STEER_TORQUE_SENSOR", "STEER_STATUS"), + ("IMPERIAL_UNIT", "CAR_SPEED"), ("LEFT_BLINKER", "SCM_FEEDBACK"), ("RIGHT_BLINKER", "SCM_FEEDBACK"), - ("GEAR", gearbox_msg), ("SEATBELT_DRIVER_LAMP", "SEATBELT_STATUS"), ("SEATBELT_DRIVER_LATCHED", "SEATBELT_STATUS"), ("BRAKE_PRESSED", "POWERTRAIN_DATA"), @@ -35,6 +36,7 @@ def get_can_signals(CP, gearbox_msg, main_on_sig_msg): ("BRAKE_HOLD_ACTIVE", "VSA_STATUS"), ("STEER_STATUS", "STEER_STATUS"), ("GEAR_SHIFTER", gearbox_msg), + ("GEAR", gearbox_msg), ("PEDAL_GAS", "POWERTRAIN_DATA"), ("CRUISE_SETTING", "SCM_BUTTONS"), ("ACC_STATUS", "POWERTRAIN_DATA"), @@ -48,9 +50,10 @@ def get_can_signals(CP, gearbox_msg, main_on_sig_msg): ("SEATBELT_STATUS", 10), ("CRUISE", 10), ("POWERTRAIN_DATA", 100), + ("CAR_SPEED", 10), ("VSA_STATUS", 50), ("STEER_STATUS", 100), - ("STEER_MOTOR_TORQUE", 0), # TODO: not on every car + ("STEER_MOTOR_TORQUE", 0), # TODO: not on every car ] if CP.carFingerprint == CAR.ODYSSEY_CHN: @@ -73,17 +76,13 @@ def get_can_signals(CP, gearbox_msg, main_on_sig_msg): signals.append(("BRAKE_PRESSED", "BRAKE_MODULE")) checks.append(("BRAKE_MODULE", 50)) - if CP.carFingerprint in HONDA_BOSCH: - signals += [ - ("EPB_STATE", "EPB_STATUS"), - ("IMPERIAL_UNIT", "CAR_SPEED"), - ] - checks += [ - ("EPB_STATUS", 50), - ("CAR_SPEED", 10), - ] + if CP.carFingerprint in (HONDA_BOSCH | {CAR.CIVIC, CAR.ODYSSEY, CAR.ODYSSEY_CHN}): + signals.append(("EPB_STATE", "EPB_STATUS")) + checks.append(("EPB_STATUS", 50)) - if not CP.openpilotLongitudinalControl: + if CP.carFingerprint in HONDA_BOSCH: + # these messages are on camera bus on radarless cars + if not CP.openpilotLongitudinalControl and CP.carFingerprint not in HONDA_BOSCH_RADARLESS: signals += [ ("CRUISE_CONTROL_LABEL", "ACC_HUD"), ("CRUISE_SPEED", "ACC_HUD"), @@ -103,34 +102,16 @@ def get_can_signals(CP, gearbox_msg, main_on_sig_msg): else: checks.append(("CRUISE_PARAMS", 50)) - if CP.carFingerprint in (CAR.ACCORD, CAR.ACCORDH, CAR.CIVIC_BOSCH, CAR.CIVIC_BOSCH_DIESEL, CAR.CRV_HYBRID, CAR.INSIGHT, CAR.ACURA_RDX_3G, CAR.HONDA_E): + if CP.carFingerprint in (CAR.ACCORD, CAR.ACCORDH, CAR.CIVIC_BOSCH, CAR.CIVIC_BOSCH_DIESEL, CAR.CRV_HYBRID, CAR.INSIGHT, CAR.ACURA_RDX_3G, CAR.HONDA_E, CAR.CIVIC_2022): signals.append(("DRIVERS_DOOR_OPEN", "SCM_FEEDBACK")) - elif CP.carFingerprint == CAR.ODYSSEY_CHN: + elif CP.carFingerprint in (CAR.ODYSSEY_CHN, CAR.FREED, CAR.HRV): signals.append(("DRIVERS_DOOR_OPEN", "SCM_BUTTONS")) - elif CP.carFingerprint in (CAR.FREED, CAR.HRV): - signals += [("DRIVERS_DOOR_OPEN", "SCM_BUTTONS"), - ("WHEELS_MOVING", "STANDSTILL")] else: signals += [("DOOR_OPEN_FL", "DOORS_STATUS"), ("DOOR_OPEN_FR", "DOORS_STATUS"), ("DOOR_OPEN_RL", "DOORS_STATUS"), - ("DOOR_OPEN_RR", "DOORS_STATUS"), - ("WHEELS_MOVING", "STANDSTILL")] - checks += [ - ("DOORS_STATUS", 3), - ("STANDSTILL", 50), - ] - - if CP.carFingerprint == CAR.CIVIC: - signals += [("IMPERIAL_UNIT", "HUD_SETTING"), - ("EPB_STATE", "EPB_STATUS")] - checks += [ - ("HUD_SETTING", 50), - ("EPB_STATUS", 50), - ] - elif CP.carFingerprint in (CAR.ODYSSEY, CAR.ODYSSEY_CHN): - signals.append(("EPB_STATE", "EPB_STATUS")) - checks.append(("EPB_STATUS", 50)) + ("DOOR_OPEN_RR", "DOORS_STATUS")] + checks.append(("DOORS_STATUS", 3)) # add gas interceptor reading if we are using it if CP.enableGasInterceptor: @@ -179,12 +160,18 @@ class CarState(CarStateBase): # update prevs, update must run once per loop self.prev_cruise_buttons = self.cruise_buttons self.prev_cruise_setting = self.cruise_setting + self.cruise_setting = cp.vl["SCM_BUTTONS"]["CRUISE_SETTING"] + self.cruise_buttons = cp.vl["SCM_BUTTONS"]["CRUISE_BUTTONS"] + + # used for car hud message + self.is_metric = not cp.vl["CAR_SPEED"]["IMPERIAL_UNIT"] # ******************* parse out can ******************* # STANDSTILL->WHEELS_MOVING bit can be noisy around zero, so use XMISSION_SPEED # panda checks if the signal is non-zero ret.standstill = cp.vl["ENGINE_DATA"]["XMISSION_SPEED"] < 1e-5 - if self.CP.carFingerprint in (CAR.ACCORD, CAR.ACCORDH, CAR.CIVIC_BOSCH, CAR.CIVIC_BOSCH_DIESEL, CAR.CRV_HYBRID, CAR.INSIGHT, CAR.ACURA_RDX_3G, CAR.HONDA_E): + # TODO: find a common signal across all cars + if self.CP.carFingerprint in (CAR.ACCORD, CAR.ACCORDH, CAR.CIVIC_BOSCH, CAR.CIVIC_BOSCH_DIESEL, CAR.CRV_HYBRID, CAR.INSIGHT, CAR.ACURA_RDX_3G, CAR.HONDA_E, CAR.CIVIC_2022): ret.doorOpen = bool(cp.vl["SCM_FEEDBACK"]["DRIVERS_DOOR_OPEN"]) elif self.CP.carFingerprint in (CAR.ODYSSEY_CHN, CAR.FREED, CAR.HRV): ret.doorOpen = bool(cp.vl["SCM_BUTTONS"]["DRIVERS_DOOR_OPEN"]) @@ -219,16 +206,12 @@ class CarState(CarStateBase): ret.steeringAngleDeg = cp.vl["STEERING_SENSORS"]["STEER_ANGLE"] ret.steeringRateDeg = cp.vl["STEERING_SENSORS"]["STEER_ANGLE_RATE"] - self.cruise_setting = cp.vl["SCM_BUTTONS"]["CRUISE_SETTING"] - self.cruise_buttons = cp.vl["SCM_BUTTONS"]["CRUISE_BUTTONS"] - ret.leftBlinker, ret.rightBlinker = self.update_blinker_from_stalk( 250, cp.vl["SCM_FEEDBACK"]["LEFT_BLINKER"], cp.vl["SCM_FEEDBACK"]["RIGHT_BLINKER"]) ret.brakeHoldActive = cp.vl["VSA_STATUS"]["BRAKE_HOLD_ACTIVE"] == 1 # TODO: set for all cars - if self.CP.carFingerprint in (CAR.CIVIC, CAR.ODYSSEY, CAR.ODYSSEY_CHN, CAR.CRV_5G, CAR.ACCORD, CAR.ACCORDH, CAR.CIVIC_BOSCH, - CAR.CIVIC_BOSCH_DIESEL, CAR.CRV_HYBRID, CAR.INSIGHT, CAR.ACURA_RDX_3G, CAR.HONDA_E): + if self.CP.carFingerprint in (HONDA_BOSCH | {CAR.CIVIC, CAR.ODYSSEY, CAR.ODYSSEY_CHN}): ret.parkingBrake = cp.vl["EPB_STATUS"]["EPB_STATE"] != 0 gear = int(cp.vl[self.gearbox_msg]["GEAR_SHIFTER"]) @@ -248,11 +231,15 @@ class CarState(CarStateBase): if self.CP.carFingerprint in HONDA_BOSCH: if not self.CP.openpilotLongitudinalControl: - ret.cruiseState.nonAdaptive = cp.vl["ACC_HUD"]["CRUISE_CONTROL_LABEL"] != 0 - ret.cruiseState.standstill = cp.vl["ACC_HUD"]["CRUISE_SPEED"] == 252. + # ACC_HUD is on camera bus on radarless cars + acc_hud = cp_cam.vl["ACC_HUD"] if self.CP.carFingerprint in HONDA_BOSCH_RADARLESS else cp.vl["ACC_HUD"] + ret.cruiseState.nonAdaptive = acc_hud["CRUISE_CONTROL_LABEL"] != 0 + ret.cruiseState.standstill = acc_hud["CRUISE_SPEED"] == 252. + # on certain cars, CRUISE_SPEED changes to imperial with car's unit setting + conversion_factor = CV.MPH_TO_MS if self.CP.carFingerprint in HONDA_BOSCH_RADARLESS and not self.is_metric else CV.KPH_TO_MS # On set, cruise set speed pulses between 254~255 and the set speed prev is set to avoid this. - ret.cruiseState.speed = self.v_cruise_pcm_prev if cp.vl["ACC_HUD"]["CRUISE_SPEED"] > 160.0 else cp.vl["ACC_HUD"]["CRUISE_SPEED"] * CV.KPH_TO_MS + ret.cruiseState.speed = self.v_cruise_pcm_prev if acc_hud["CRUISE_SPEED"] > 160.0 else acc_hud["CRUISE_SPEED"] * conversion_factor self.v_cruise_pcm_prev = ret.cruiseState.speed else: ret.cruiseState.speed = cp.vl["CRUISE"]["CRUISE_SPEED_PCM"] * CV.KPH_TO_MS @@ -281,23 +268,15 @@ class CarState(CarStateBase): if ret.brake > 0.1: ret.brakePressed = True - # TODO: discover the CAN msg that has the imperial unit bit for all other cars - if self.CP.carFingerprint in (CAR.CIVIC, ): - self.is_metric = not cp.vl["HUD_SETTING"]["IMPERIAL_UNIT"] - elif self.CP.carFingerprint in HONDA_BOSCH: - self.is_metric = not cp.vl["CAR_SPEED"]["IMPERIAL_UNIT"] - else: - self.is_metric = False - if self.CP.carFingerprint in HONDA_BOSCH: - ret.stockAeb = (not self.CP.openpilotLongitudinalControl) and bool(cp.vl["ACC_CONTROL"]["AEB_STATUS"] and cp.vl["ACC_CONTROL"]["ACCEL_COMMAND"] < -1e-5) + # TODO: find the radarless AEB_STATUS bit and make sure ACCEL_COMMAND is correct to enable AEB alerts + if self.CP.carFingerprint not in HONDA_BOSCH_RADARLESS: + ret.stockAeb = (not self.CP.openpilotLongitudinalControl) and bool(cp.vl["ACC_CONTROL"]["AEB_STATUS"] and cp.vl["ACC_CONTROL"]["ACCEL_COMMAND"] < -1e-5) else: ret.stockAeb = bool(cp_cam.vl["BRAKE_COMMAND"]["AEB_REQ_1"] and cp_cam.vl["BRAKE_COMMAND"]["COMPUTER_BRAKE"] > 1e-5) - if self.CP.carFingerprint in HONDA_BOSCH: - self.stock_hud = False - ret.stockFcw = False - else: + self.stock_hud = False + if self.CP.carFingerprint not in HONDA_BOSCH: ret.stockFcw = cp_cam.vl["BRAKE_COMMAND"]["FCW"] != 0 self.stock_hud = cp_cam.vl["ACC_HUD"] self.stock_brake = cp_cam.vl["BRAKE_COMMAND"] @@ -312,8 +291,7 @@ class CarState(CarStateBase): def get_can_parser(self, CP): signals, checks = get_can_signals(CP, self.gearbox_msg, self.main_on_sig_msg) - bus_pt = 1 if CP.carFingerprint in HONDA_BOSCH else 0 - return CANParser(DBC[CP.carFingerprint]["pt"], signals, checks, bus_pt) + return CANParser(DBC[CP.carFingerprint]["pt"], signals, checks, get_pt_bus(CP.carFingerprint)) @staticmethod def get_cam_can_parser(CP): @@ -322,7 +300,14 @@ class CarState(CarStateBase): ("STEERING_CONTROL", 100), ] - if CP.carFingerprint not in HONDA_BOSCH: + if CP.carFingerprint in HONDA_BOSCH_RADARLESS and not CP.openpilotLongitudinalControl: + signals += [ + ("CRUISE_SPEED", "ACC_HUD"), + ("CRUISE_CONTROL_LABEL", "ACC_HUD"), + ] + checks.append(("ACC_HUD", 10)) + + elif CP.carFingerprint not in HONDA_BOSCH: signals += [("COMPUTER_BRAKE", "BRAKE_COMMAND"), ("AEB_REQ_1", "BRAKE_COMMAND"), ("FCW", "BRAKE_COMMAND"), diff --git a/selfdrive/car/honda/hondacan.py b/selfdrive/car/honda/hondacan.py index dcdc0e5f9..7246b9868 100644 --- a/selfdrive/car/honda/hondacan.py +++ b/selfdrive/car/honda/hondacan.py @@ -1,5 +1,5 @@ from common.conversions import Conversions as CV -from selfdrive.car.honda.values import HondaFlags, HONDA_BOSCH, CAR, CarControllerParams +from selfdrive.car.honda.values import HondaFlags, HONDA_BOSCH, HONDA_BOSCH_RADARLESS, CAR, CarControllerParams # CAN bus layout with relay # 0 = ACC-CAN - radar side @@ -7,8 +7,9 @@ from selfdrive.car.honda.values import HondaFlags, HONDA_BOSCH, CAR, CarControll # 2 = ACC-CAN - camera side # 3 = F-CAN A - OBDII port + def get_pt_bus(car_fingerprint): - return 1 if car_fingerprint in HONDA_BOSCH else 0 + return 1 if car_fingerprint in (HONDA_BOSCH - HONDA_BOSCH_RADARLESS) else 0 def get_lkas_cmd_bus(car_fingerprint, radar_disabled=False): @@ -18,6 +19,7 @@ def get_lkas_cmd_bus(car_fingerprint, radar_disabled=False): # normally steering commands are sent to radar, which forwards them to powertrain bus return 0 + def create_brake_command(packer, apply_brake, pump_on, pcm_override, pcm_cancel_cmd, fcw, idx, car_fingerprint, stock_brake): # TODO: do we loose pressure if we keep pump off for long? brakelights = apply_brake > 0 @@ -78,6 +80,7 @@ def create_acc_commands(packer, enabled, active, accel, gas, idx, stopping, car_ return commands + def create_steering_control(packer, apply_steer, lkas_active, car_fingerprint, idx, radar_disabled): values = { "STEER_TORQUE": apply_steer if lkas_active else 0, @@ -98,50 +101,47 @@ def create_bosch_supplemental_1(packer, car_fingerprint, idx): return packer.make_can_msg("BOSCH_SUPPLEMENTAL_1", bus, values, idx) -def create_ui_commands(packer, CP, pcm_speed, hud, is_metric, idx, stock_hud): +def create_ui_commands(packer, CP, enabled, pcm_speed, hud, is_metric, idx, stock_hud): commands = [] bus_pt = get_pt_bus(CP.carFingerprint) radar_disabled = CP.carFingerprint in HONDA_BOSCH and CP.openpilotLongitudinalControl bus_lkas = get_lkas_cmd_bus(CP.carFingerprint, radar_disabled) if CP.openpilotLongitudinalControl: + acc_hud_values = { + 'CRUISE_SPEED': hud.v_cruise, + 'ENABLE_MINI_CAR': 1, + 'HUD_DISTANCE': 0, # max distance setting on display + 'IMPERIAL_UNIT': int(not is_metric), + 'HUD_LEAD': 2 if enabled and hud.lead_visible else 1 if enabled else 0, + 'SET_ME_X01_2': 1, + } + if CP.carFingerprint in HONDA_BOSCH: - acc_hud_values = { - 'CRUISE_SPEED': hud.v_cruise, - 'ENABLE_MINI_CAR': 1, - 'SET_TO_1': 1, - 'HUD_LEAD': hud.car, - 'HUD_DISTANCE': 3, - 'ACC_ON': hud.car != 0, - 'SET_TO_X1': 1, - 'IMPERIAL_UNIT': int(not is_metric), - 'FCM_OFF': 1, - } + acc_hud_values['ACC_ON'] = int(enabled) + acc_hud_values['FCM_OFF'] = 1 + acc_hud_values['FCM_OFF_2'] = 1 else: - acc_hud_values = { - 'PCM_SPEED': pcm_speed * CV.MS_TO_KPH, - 'PCM_GAS': hud.pcm_accel, - 'CRUISE_SPEED': hud.v_cruise, - 'ENABLE_MINI_CAR': 1, - 'HUD_LEAD': hud.car, - 'HUD_DISTANCE': 3, # max distance setting on display - 'IMPERIAL_UNIT': int(not is_metric), - 'SET_ME_X01_2': 1, - 'SET_ME_X01': 1, - "FCM_OFF": stock_hud["FCM_OFF"], - "FCM_OFF_2": stock_hud["FCM_OFF_2"], - "FCM_PROBLEM": stock_hud["FCM_PROBLEM"], - "ICONS": stock_hud["ICONS"], - } + acc_hud_values['PCM_SPEED'] = pcm_speed * CV.MS_TO_KPH + acc_hud_values['PCM_GAS'] = hud.pcm_accel + acc_hud_values['SET_ME_X01'] = 1 + acc_hud_values['FCM_OFF'] = stock_hud['FCM_OFF'] + acc_hud_values['FCM_OFF_2'] = stock_hud['FCM_OFF_2'] + acc_hud_values['FCM_PROBLEM'] = stock_hud['FCM_PROBLEM'] + acc_hud_values['ICONS'] = stock_hud['ICONS'] commands.append(packer.make_can_msg("ACC_HUD", bus_pt, acc_hud_values, idx)) lkas_hud_values = { 'SET_ME_X41': 0x41, 'STEERING_REQUIRED': hud.steer_required, - 'SOLID_LANES': hud.lanes, + 'SOLID_LANES': hud.lanes_visible, 'BEEP': 0, } + if CP.carFingerprint in HONDA_BOSCH_RADARLESS: + lkas_hud_values['LANE_LINES'] = 3 + lkas_hud_values['DASHED_LANES'] = hud.lanes_visible + if not (CP.flags & HondaFlags.BOSCH_EXT_HUD): lkas_hud_values['SET_ME_X48'] = 0x48 @@ -169,5 +169,6 @@ def spam_buttons_command(packer, button_val, idx, car_fingerprint): 'CRUISE_BUTTONS': button_val, 'CRUISE_SETTING': 0, } - bus = get_pt_bus(car_fingerprint) + # send buttons to camera on radarless cars + bus = 2 if car_fingerprint in HONDA_BOSCH_RADARLESS else get_pt_bus(car_fingerprint) return packer.make_can_msg("SCM_BUTTONS", bus, values, idx) diff --git a/selfdrive/car/honda/interface.py b/selfdrive/car/honda/interface.py index 265175d94..a78189b69 100755 --- a/selfdrive/car/honda/interface.py +++ b/selfdrive/car/honda/interface.py @@ -3,8 +3,8 @@ from cereal import car from panda import Panda from common.conversions import Conversions as CV from common.numpy_fast import interp -from selfdrive.car.honda.values import CarControllerParams, CruiseButtons, HondaFlags, CAR, HONDA_BOSCH, HONDA_NIDEC_ALT_SCM_MESSAGES, HONDA_BOSCH_ALT_BRAKE_SIGNAL -from selfdrive.car import STD_CARGO_KG, CivicParams, scale_rot_inertia, scale_tire_stiffness, gen_empty_fingerprint, get_safety_config +from selfdrive.car.honda.values import CarControllerParams, CruiseButtons, HondaFlags, CAR, HONDA_BOSCH, HONDA_NIDEC_ALT_SCM_MESSAGES, HONDA_BOSCH_ALT_BRAKE_SIGNAL, HONDA_BOSCH_RADARLESS +from selfdrive.car import STD_CARGO_KG, CivicParams, create_button_enable_events, create_button_event, scale_rot_inertia, scale_tire_stiffness, gen_empty_fingerprint, get_safety_config from selfdrive.car.interfaces import CarInterfaceBase from selfdrive.car.disable_ecu import disable_ecu @@ -12,6 +12,8 @@ from selfdrive.car.disable_ecu import disable_ecu ButtonType = car.CarState.ButtonEvent.Type EventName = car.CarEvent.EventName TransmissionType = car.CarParams.TransmissionType +BUTTONS_DICT = {CruiseButtons.RES_ACCEL: ButtonType.accelCruise, CruiseButtons.DECEL_SET: ButtonType.decelCruise, + CruiseButtons.MAIN: ButtonType.altButton3, CruiseButtons.CANCEL: ButtonType.cancel} class CarInterface(CarInterfaceBase): @@ -35,9 +37,10 @@ class CarInterface(CarInterfaceBase): ret.safetyConfigs = [get_safety_config(car.CarParams.SafetyModel.hondaBosch)] ret.radarOffCan = True - # Disable the radar and let openpilot control longitudinal - # WARNING: THIS DISABLES AEB! - ret.openpilotLongitudinalControl = disable_radar + if candidate not in HONDA_BOSCH_RADARLESS: + # Disable the radar and let openpilot control longitudinal + # WARNING: THIS DISABLES AEB! + ret.openpilotLongitudinalControl = disable_radar ret.pcmCruise = not ret.openpilotLongitudinalControl else: @@ -102,7 +105,7 @@ class CarInterface(CarInterfaceBase): ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[1.1], [0.33]] tire_stiffness_factor = 1. - elif candidate in (CAR.CIVIC_BOSCH, CAR.CIVIC_BOSCH_DIESEL): + elif candidate in (CAR.CIVIC_BOSCH, CAR.CIVIC_BOSCH_DIESEL, CAR.CIVIC_2022): stop_and_go = True ret.mass = CivicParams.MASS ret.wheelbase = CivicParams.WHEELBASE @@ -214,8 +217,8 @@ class CarInterface(CarInterfaceBase): ret.wheelbase = 2.68 ret.centerToFront = ret.wheelbase * 0.38 ret.steerRatio = 15.0 # as spec - ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 1000], [0, 1000]] # TODO: determine if there is a dead zone at the top end tire_stiffness_factor = 0.444 + ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 1000], [0, 1000]] # TODO: determine if there is a dead zone at the top end ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.8], [0.24]] elif candidate == CAR.ACURA_RDX_3G: @@ -302,6 +305,9 @@ class CarInterface(CarInterfaceBase): if ret.openpilotLongitudinalControl and candidate in HONDA_BOSCH: ret.safetyConfigs[0].safetyParam |= Panda.FLAG_HONDA_BOSCH_LONG + if candidate in HONDA_BOSCH_RADARLESS: + ret.safetyConfigs[0].safetyParam |= Panda.FLAG_HONDA_RADARLESS + # min speed to enable ACC. if car can do stop and go, then set enabling speed # to a negative value, so it won't matter. Otherwise, add 0.5 mph margin to not # conflict with PCM acc @@ -317,14 +323,13 @@ class CarInterface(CarInterfaceBase): tire_stiffness_factor=tire_stiffness_factor) ret.steerActuatorDelay = 0.1 - ret.steerRateCost = 0.5 ret.steerLimitTimer = 0.8 return ret @staticmethod def init(CP, logcan, sendcan): - if CP.carFingerprint in HONDA_BOSCH and CP.openpilotLongitudinalControl: + if CP.carFingerprint in (HONDA_BOSCH - HONDA_BOSCH_RADARLESS) and CP.openpilotLongitudinalControl: disable_ecu(logcan, sendcan, bus=1, addr=0x18DAB0F1, com_cont_req=b'\x28\x83\x03') # returns a car.CarState @@ -334,37 +339,11 @@ class CarInterface(CarInterfaceBase): buttonEvents = [] if self.CS.cruise_buttons != self.CS.prev_cruise_buttons: - be = car.CarState.ButtonEvent.new_message() - be.type = ButtonType.unknown - if self.CS.cruise_buttons != 0: - be.pressed = True - but = self.CS.cruise_buttons - else: - be.pressed = False - but = self.CS.prev_cruise_buttons - if but == CruiseButtons.RES_ACCEL: - be.type = ButtonType.accelCruise - elif but == CruiseButtons.DECEL_SET: - be.type = ButtonType.decelCruise - elif but == CruiseButtons.CANCEL: - be.type = ButtonType.cancel - elif but == CruiseButtons.MAIN: - be.type = ButtonType.altButton3 - buttonEvents.append(be) + buttonEvents.append(create_button_event(self.CS.cruise_buttons, self.CS.prev_cruise_buttons, BUTTONS_DICT)) if self.CS.cruise_setting != self.CS.prev_cruise_setting: - be = car.CarState.ButtonEvent.new_message() - be.type = ButtonType.unknown - if self.CS.cruise_setting != 0: - be.pressed = True - but = self.CS.cruise_setting - else: - be.pressed = False - but = self.CS.prev_cruise_setting - if but == 1: - be.type = ButtonType.altButton1 - # TODO: more buttons? - buttonEvents.append(be) + buttonEvents.append(create_button_event(self.CS.cruise_setting, self.CS.prev_cruise_setting, {1: ButtonType.altButton1})) + ret.buttonEvents = buttonEvents # events @@ -391,16 +370,7 @@ class CarInterface(CarInterfaceBase): events.add(EventName.manualRestart) # handle button presses - for b in ret.buttonEvents: - - # do enable on both accel and decel buttons - if not self.CP.pcmCruise: - if b.type in (ButtonType.accelCruise, ButtonType.decelCruise) and not b.pressed: - events.add(EventName.buttonEnable) - - # do disable on button down - if b.type == ButtonType.cancel and b.pressed: - events.add(EventName.buttonCancel) + events.events.extend(create_button_enable_events(ret.buttonEvents, self.CP.pcmCruise)) ret.events = events.to_msg() diff --git a/selfdrive/car/honda/values.py b/selfdrive/car/honda/values.py index 152f71e98..b8417ee19 100644 --- a/selfdrive/car/honda/values.py +++ b/selfdrive/car/honda/values.py @@ -75,6 +75,7 @@ class CAR: CIVIC = "HONDA CIVIC 2016" CIVIC_BOSCH = "HONDA CIVIC (BOSCH) 2019" CIVIC_BOSCH_DIESEL = "HONDA CIVIC SEDAN 1.6 DIESEL 2019" + CIVIC_2022 = "HONDA CIVIC 2022" ACURA_ILX = "ACURA ILX 2016" CRV = "HONDA CR-V 2016" CRV_5G = "HONDA CR-V 2017" @@ -108,33 +109,37 @@ class HondaCarInfo(CarInfo): CAR_INFO: Dict[str, Optional[Union[HondaCarInfo, List[HondaCarInfo]]]] = { CAR.ACCORD: [ - HondaCarInfo("Honda Accord 2018-21", "All", video_link="https://www.youtube.com/watch?v=mrUwlj3Mi58", min_steer_speed=3. * CV.MPH_TO_MS, harness=Harness.bosch), - HondaCarInfo("Honda Inspire 2018", "All", min_steer_speed=3. * CV.MPH_TO_MS, harness=Harness.bosch), + HondaCarInfo("Honda Accord 2018-22", "All", video_link="https://www.youtube.com/watch?v=mrUwlj3Mi58", min_steer_speed=3. * CV.MPH_TO_MS, harness=Harness.bosch_a), + HondaCarInfo("Honda Inspire 2018", "All", min_steer_speed=3. * CV.MPH_TO_MS, harness=Harness.bosch_a), ], - CAR.ACCORDH: HondaCarInfo("Honda Accord Hybrid 2018-21", "All", min_steer_speed=3. * CV.MPH_TO_MS, harness=Harness.bosch), + CAR.ACCORDH: HondaCarInfo("Honda Accord Hybrid 2018-22", "All", min_steer_speed=3. * CV.MPH_TO_MS, harness=Harness.bosch_a), CAR.CIVIC: HondaCarInfo("Honda Civic 2016-18", harness=Harness.nidec), CAR.CIVIC_BOSCH: [ - HondaCarInfo("Honda Civic 2019-20", "All", video_link="https://www.youtube.com/watch?v=4Iz1Mz5LGF8", footnotes=[Footnote.CIVIC_DIESEL], min_steer_speed=2. * CV.MPH_TO_MS, harness=Harness.bosch), - HondaCarInfo("Honda Civic Hatchback 2017-21", harness=Harness.bosch), + HondaCarInfo("Honda Civic 2019-21", "All", video_link="https://www.youtube.com/watch?v=4Iz1Mz5LGF8", footnotes=[Footnote.CIVIC_DIESEL], min_steer_speed=2. * CV.MPH_TO_MS, harness=Harness.bosch_a), + HondaCarInfo("Honda Civic Hatchback 2017-21", harness=Harness.bosch_a), ], CAR.CIVIC_BOSCH_DIESEL: None, # same platform + CAR.CIVIC_2022: [ + HondaCarInfo("Honda Civic 2022", "All", min_steer_speed=0., harness=Harness.bosch_b), + HondaCarInfo("Honda Civic Hatchback 2022", "All", min_steer_speed=0., harness=Harness.bosch_b), + ], CAR.ACURA_ILX: HondaCarInfo("Acura ILX 2016-19", "AcuraWatch Plus", min_steer_speed=25. * CV.MPH_TO_MS, harness=Harness.nidec), CAR.CRV: HondaCarInfo("Honda CR-V 2015-16", "Touring", harness=Harness.nidec), - CAR.CRV_5G: HondaCarInfo("Honda CR-V 2017-21", harness=Harness.bosch), + CAR.CRV_5G: HondaCarInfo("Honda CR-V 2017-22", harness=Harness.bosch_a), CAR.CRV_EU: None, # HondaCarInfo("Honda CR-V EU", "Touring"), # Euro version of CRV Touring - CAR.CRV_HYBRID: HondaCarInfo("Honda CR-V Hybrid 2017-19", harness=Harness.bosch), - CAR.FIT: HondaCarInfo("Honda Fit 2018-19", harness=Harness.nidec), + CAR.CRV_HYBRID: HondaCarInfo("Honda CR-V Hybrid 2017-19", harness=Harness.bosch_a), + CAR.FIT: HondaCarInfo("Honda Fit 2018-20", harness=Harness.nidec), CAR.FREED: HondaCarInfo("Honda Freed 2020", harness=Harness.nidec), - CAR.HRV: HondaCarInfo("Honda HR-V 2019-20", harness=Harness.nidec), - CAR.ODYSSEY: HondaCarInfo("Honda Odyssey 2018-20", min_steer_speed=0., harness=Harness.nidec), + CAR.HRV: HondaCarInfo("Honda HR-V 2019-22", harness=Harness.nidec), + CAR.ODYSSEY: HondaCarInfo("Honda Odyssey 2018-22", min_steer_speed=0., harness=Harness.nidec), CAR.ODYSSEY_CHN: None, # Chinese version of Odyssey CAR.ACURA_RDX: HondaCarInfo("Acura RDX 2016-18", "AcuraWatch Plus", harness=Harness.nidec), - CAR.ACURA_RDX_3G: HondaCarInfo("Acura RDX 2019-21", "All", min_steer_speed=3. * CV.MPH_TO_MS, harness=Harness.bosch), - CAR.PILOT: HondaCarInfo("Honda Pilot 2016-21", harness=Harness.nidec), + CAR.ACURA_RDX_3G: HondaCarInfo("Acura RDX 2019-22", "All", min_steer_speed=3. * CV.MPH_TO_MS, harness=Harness.bosch_a), + CAR.PILOT: HondaCarInfo("Honda Pilot 2016-22", harness=Harness.nidec), CAR.PASSPORT: HondaCarInfo("Honda Passport 2019-21", "All", harness=Harness.nidec), CAR.RIDGELINE: HondaCarInfo("Honda Ridgeline 2017-22", harness=Harness.nidec), - CAR.INSIGHT: HondaCarInfo("Honda Insight 2019-21", "All", min_steer_speed=3. * CV.MPH_TO_MS, harness=Harness.bosch), - CAR.HONDA_E: HondaCarInfo("Honda e 2020", "All", min_steer_speed=3. * CV.MPH_TO_MS, harness=Harness.bosch), + CAR.INSIGHT: HondaCarInfo("Honda Insight 2019-22", "All", min_steer_speed=3. * CV.MPH_TO_MS, harness=Harness.bosch_a), + CAR.HONDA_E: HondaCarInfo("Honda e 2020", "All", min_steer_speed=3. * CV.MPH_TO_MS, harness=Harness.bosch_a), } @@ -1392,6 +1397,40 @@ FW_VERSIONS = { b'57114-TYF-E030\x00\x00' ], }, + CAR.CIVIC_2022: { + (Ecu.eps, 0x18DA30F1, None): [ + b'39990-T39-A130\x00\x00', + b'39990-T43-J020\x00\x00', + ], + (Ecu.gateway, 0x18DAEFF1, None): [ + b'38897-T20-A020\x00\x00', + b'38897-T20-A510\x00\x00', + b'38897-T21-A010\x00\x00', + ], + (Ecu.srs, 0x18DA53F1, None): [ + b'77959-T20-A970\x00\x00', + b'77959-T47-A940\x00\x00', + ], + (Ecu.combinationMeter, 0x18DA60F1, None): [ + b'78108-T21-A220\x00\x00', + b'78108-T21-A620\x00\x00', + b'78108-T23-A110\x00\x00', + ], + (Ecu.vsa, 0x18DA28F1, None): [ + b'57114-T20-AB40\x00\x00', + b'57114-T43-JB30\x00\x00', + ], + (Ecu.transmission, 0x18da1ef1, None): [ + b'28101-65D-A020\x00\x00', + b'28101-65D-A120\x00\x00', + b'28101-65H-A020\x00\x00', + ], + (Ecu.programmedFuelInjection, 0x18da10f1, None): [ + b'37805-64L-A540\x00\x00', + b'37805-64S-A540\x00\x00', + b'37805-64S-A720\x00\x00', + ], + }, } DBC = { @@ -1417,6 +1456,7 @@ DBC = { CAR.RIDGELINE: dbc_dict('acura_ilx_2016_can_generated', 'acura_ilx_2016_nidec'), CAR.INSIGHT: dbc_dict('honda_insight_ex_2019_can_generated', None), CAR.HONDA_E: dbc_dict('acura_rdx_2020_can_generated', None), + CAR.CIVIC_2022: dbc_dict('honda_civic_ex_2022_can_generated', None), } STEER_THRESHOLD = { @@ -1429,5 +1469,6 @@ HONDA_NIDEC_ALT_PCM_ACCEL = {CAR.ODYSSEY} HONDA_NIDEC_ALT_SCM_MESSAGES = {CAR.ACURA_ILX, CAR.ACURA_RDX, CAR.CRV, CAR.CRV_EU, CAR.FIT, CAR.FREED, CAR.HRV, CAR.ODYSSEY_CHN, CAR.PILOT, CAR.PASSPORT, CAR.RIDGELINE} HONDA_BOSCH = {CAR.ACCORD, CAR.ACCORDH, CAR.CIVIC_BOSCH, CAR.CIVIC_BOSCH_DIESEL, CAR.CRV_5G, - CAR.CRV_HYBRID, CAR.INSIGHT, CAR.ACURA_RDX_3G, CAR.HONDA_E} + CAR.CRV_HYBRID, CAR.INSIGHT, CAR.ACURA_RDX_3G, CAR.HONDA_E, CAR.CIVIC_2022} HONDA_BOSCH_ALT_BRAKE_SIGNAL = {CAR.ACCORD, CAR.CRV_5G, CAR.ACURA_RDX_3G} +HONDA_BOSCH_RADARLESS = {CAR.CIVIC_2022} diff --git a/selfdrive/car/hyundai/carcontroller.py b/selfdrive/car/hyundai/carcontroller.py index 9abdf8d03..d0d9c4083 100644 --- a/selfdrive/car/hyundai/carcontroller.py +++ b/selfdrive/car/hyundai/carcontroller.py @@ -1,11 +1,11 @@ from cereal import car -from common.realtime import DT_CTRL -from common.numpy_fast import clip, interp from common.conversions import Conversions as CV -from selfdrive.car import apply_std_steer_torque_limits -from selfdrive.car.hyundai.hyundaican import create_lkas11, create_clu11, create_lfahda_mfc, create_acc_commands, create_acc_opt, create_frt_radar_opt -from selfdrive.car.hyundai.values import Buttons, CarControllerParams, CAR +from common.numpy_fast import clip, interp +from common.realtime import DT_CTRL from opendbc.can.packer import CANPacker +from selfdrive.car import apply_std_steer_torque_limits +from selfdrive.car.hyundai import hda2can, hyundaican +from selfdrive.car.hyundai.values import Buttons, CarControllerParams, HDA2_CAR, CAR VisualAlert = car.CarControl.HUDControl.VisualAlert LongCtrlState = car.CarControl.Actuators.LongControlState @@ -44,16 +44,20 @@ class CarController: self.apply_steer_last = 0 self.car_fingerprint = CP.carFingerprint self.steer_rate_limited = False - self.last_resume_frame = 0 + self.last_button_frame = 0 self.accel = 0 def update(self, CC, CS): actuators = CC.actuators hud_control = CC.hudControl - pcm_cancel_cmd = CC.cruiseControl.cancel # Steering Torque - new_steer = int(round(actuators.steer * self.params.STEER_MAX)) + + # These cars have significantly more torque than most HKG. Limit to 70% of max. + steer = actuators.steer + if self.CP.carFingerprint in (CAR.KONA, CAR.KONA_EV, CAR.KONA_HEV): + steer = clip(steer, -0.7, 0.7) + new_steer = int(round(steer * self.params.STEER_MAX)) apply_steer = apply_std_steer_torque_limits(new_steer, self.apply_steer_last, CS.out.steeringTorque, self.params) self.steer_rate_limited = new_steer != apply_steer @@ -67,58 +71,77 @@ class CarController: can_sends = [] - # tester present - w/ no response (keeps radar disabled) - if self.CP.openpilotLongitudinalControl: - if self.frame % 100 == 0: - can_sends.append([0x7D0, 0, b"\x02\x3E\x80\x00\x00\x00\x00\x00", 0]) + if self.CP.carFingerprint in HDA2_CAR: + # steering control + can_sends.append(hda2can.create_lkas(self.packer, CC.enabled, self.frame, CC.latActive, apply_steer)) - can_sends.append(create_lkas11(self.packer, self.frame, self.car_fingerprint, apply_steer, CC.latActive, - CS.lkas11, sys_warning, sys_state, CC.enabled, - hud_control.leftLaneVisible, hud_control.rightLaneVisible, - left_lane_warning, right_lane_warning)) + if self.frame % 5 == 0: + can_sends.append(hda2can.create_cam_0x2a4(self.packer, self.frame, CS.cam_0x2a4)) - if not self.CP.openpilotLongitudinalControl: - if pcm_cancel_cmd: - can_sends.append(create_clu11(self.packer, self.frame, CS.clu11, Buttons.CANCEL)) - elif CS.out.cruiseState.standstill: - # send resume at a max freq of 10Hz - if (self.frame - self.last_resume_frame) * DT_CTRL > 0.1: - # send 25 messages at a time to increases the likelihood of resume being accepted - can_sends.extend([create_clu11(self.packer, self.frame, CS.clu11, Buttons.RES_ACCEL)] * 25) - self.last_resume_frame = self.frame + # cruise cancel + if (self.frame - self.last_button_frame) * DT_CTRL > 0.25: + if CC.cruiseControl.cancel: + for _ in range(20): + can_sends.append(hda2can.create_buttons(self.packer, CS.buttons_counter+1, Buttons.CANCEL)) + self.last_button_frame = self.frame - if self.frame % 2 == 0 and self.CP.openpilotLongitudinalControl: - accel = actuators.accel - jerk = 0 + # cruise standstill resume + elif CC.cruiseControl.resume: + can_sends.append(hda2can.create_buttons(self.packer, CS.buttons_counter+1, Buttons.RES_ACCEL)) + self.last_button_frame = self.frame + else: - if CC.longActive: - jerk = clip(2.0 * (accel - CS.out.aEgo), -12.7, 12.7) - if accel < 0: - accel = interp(accel - CS.out.aEgo, [-1.0, -0.5], [2 * accel, accel]) + # tester present - w/ no response (keeps radar disabled) + if self.CP.openpilotLongitudinalControl: + if self.frame % 100 == 0: + can_sends.append([0x7D0, 0, b"\x02\x3E\x80\x00\x00\x00\x00\x00", 0]) - accel = clip(accel, CarControllerParams.ACCEL_MIN, CarControllerParams.ACCEL_MAX) + can_sends.append(hyundaican.create_lkas11(self.packer, self.frame, self.car_fingerprint, apply_steer, CC.latActive, + CS.lkas11, sys_warning, sys_state, CC.enabled, + hud_control.leftLaneVisible, hud_control.rightLaneVisible, + left_lane_warning, right_lane_warning)) - lead_visible = False - stopping = actuators.longControlState == LongCtrlState.stopping - set_speed_in_units = hud_control.setSpeed * (CV.MS_TO_MPH if CS.clu11["CF_Clu_SPEED_UNIT"] == 1 else CV.MS_TO_KPH) - can_sends.extend(create_acc_commands(self.packer, CC.enabled, accel, jerk, int(self.frame / 2), lead_visible, - set_speed_in_units, stopping, CS.out.gasPressed)) - self.accel = accel + if not self.CP.openpilotLongitudinalControl: + if CC.cruiseControl.cancel: + can_sends.append(hyundaican.create_clu11(self.packer, self.frame, CS.clu11, Buttons.CANCEL)) + elif CC.cruiseControl.resume: + # send resume at a max freq of 10Hz + if (self.frame - self.last_button_frame) * DT_CTRL > 0.1: + # send 25 messages at a time to increases the likelihood of resume being accepted + can_sends.extend([hyundaican.create_clu11(self.packer, self.frame, CS.clu11, Buttons.RES_ACCEL)] * 25) + self.last_button_frame = self.frame - # 20 Hz LFA MFA message - if self.frame % 5 == 0 and self.car_fingerprint in (CAR.SONATA, CAR.PALISADE, CAR.IONIQ, CAR.KIA_NIRO_EV, CAR.KIA_NIRO_HEV_2021, - CAR.IONIQ_EV_2020, CAR.IONIQ_PHEV, CAR.KIA_CEED, CAR.KIA_SELTOS, CAR.KONA_EV, - CAR.ELANTRA_2021, CAR.ELANTRA_HEV_2021, CAR.SONATA_HYBRID, CAR.KONA_HEV, CAR.SANTA_FE_2022, - CAR.KIA_K5_2021, CAR.IONIQ_HEV_2022, CAR.SANTA_FE_HEV_2022, CAR.GENESIS_G70_2020, CAR.SANTA_FE_PHEV_2022): - can_sends.append(create_lfahda_mfc(self.packer, CC.enabled)) + if self.frame % 2 == 0 and self.CP.openpilotLongitudinalControl: + accel = actuators.accel + jerk = 0 - # 5 Hz ACC options - if self.frame % 20 == 0 and self.CP.openpilotLongitudinalControl: - can_sends.extend(create_acc_opt(self.packer)) + if CC.longActive: + jerk = clip(2.0 * (accel - CS.out.aEgo), -12.7, 12.7) + if accel < 0: + accel = interp(accel - CS.out.aEgo, [-1.0, -0.5], [2 * accel, accel]) - # 2 Hz front radar options - if self.frame % 50 == 0 and self.CP.openpilotLongitudinalControl: - can_sends.append(create_frt_radar_opt(self.packer)) + accel = clip(accel, CarControllerParams.ACCEL_MIN, CarControllerParams.ACCEL_MAX) + + stopping = actuators.longControlState == LongCtrlState.stopping + set_speed_in_units = hud_control.setSpeed * (CV.MS_TO_MPH if CS.clu11["CF_Clu_SPEED_UNIT"] == 1 else CV.MS_TO_KPH) + can_sends.extend(hyundaican.create_acc_commands(self.packer, CC.enabled, accel, jerk, int(self.frame / 2), + hud_control.leadVisible, set_speed_in_units, stopping, CS.out.gasPressed)) + self.accel = accel + + # 20 Hz LFA MFA message + if self.frame % 5 == 0 and self.car_fingerprint in (CAR.SONATA, CAR.PALISADE, CAR.IONIQ, CAR.KIA_NIRO_EV, CAR.KIA_NIRO_HEV_2021, + CAR.IONIQ_EV_2020, CAR.IONIQ_PHEV, CAR.KIA_CEED, CAR.KIA_SELTOS, CAR.KONA_EV, + CAR.ELANTRA_2021, CAR.ELANTRA_HEV_2021, CAR.SONATA_HYBRID, CAR.KONA_HEV, CAR.SANTA_FE_2022, + CAR.KIA_K5_2021, CAR.IONIQ_HEV_2022, CAR.SANTA_FE_HEV_2022, CAR.GENESIS_G70_2020, CAR.SANTA_FE_PHEV_2022): + can_sends.append(hyundaican.create_lfahda_mfc(self.packer, CC.enabled)) + + # 5 Hz ACC options + if self.frame % 20 == 0 and self.CP.openpilotLongitudinalControl: + can_sends.extend(hyundaican.create_acc_opt(self.packer)) + + # 2 Hz front radar options + if self.frame % 50 == 0 and self.CP.openpilotLongitudinalControl: + can_sends.append(hyundaican.create_frt_radar_opt(self.packer)) new_actuators = actuators.copy() new_actuators.steer = apply_steer / self.params.STEER_MAX diff --git a/selfdrive/car/hyundai/carstate.py b/selfdrive/car/hyundai/carstate.py index 7752bf82c..8afd851f0 100644 --- a/selfdrive/car/hyundai/carstate.py +++ b/selfdrive/car/hyundai/carstate.py @@ -5,10 +5,10 @@ from cereal import car from common.conversions import Conversions as CV from opendbc.can.parser import CANParser from opendbc.can.can_define import CANDefine -from selfdrive.car.hyundai.values import DBC, STEER_THRESHOLD, FEATURES, EV_CAR, HYBRID_CAR, Buttons +from selfdrive.car.hyundai.values import DBC, FEATURES, HDA2_CAR, EV_CAR, HYBRID_CAR, Buttons, CarControllerParams from selfdrive.car.interfaces import CarStateBase -PREV_BUTTON_SAMPLES = 4 +PREV_BUTTON_SAMPLES = 8 class CarState(CarStateBase): @@ -19,14 +19,25 @@ class CarState(CarStateBase): self.cruise_buttons = deque([Buttons.NONE] * PREV_BUTTON_SAMPLES, maxlen=PREV_BUTTON_SAMPLES) self.main_buttons = deque([Buttons.NONE] * PREV_BUTTON_SAMPLES, maxlen=PREV_BUTTON_SAMPLES) - if self.CP.carFingerprint in FEATURES["use_cluster_gears"]: + if CP.carFingerprint in HDA2_CAR: + self.shifter_values = can_define.dv["ACCELERATOR"]["GEAR"] + elif self.CP.carFingerprint in FEATURES["use_cluster_gears"]: self.shifter_values = can_define.dv["CLU15"]["CF_Clu_Gear"] elif self.CP.carFingerprint in FEATURES["use_tcu_gears"]: self.shifter_values = can_define.dv["TCU12"]["CUR_GR"] else: # preferred and elect gear methods use same definition self.shifter_values = can_define.dv["LVR12"]["CF_Lvr_Gear"] + self.brake_error = False + self.park_brake = False + self.buttons_counter = 0 + + self.params = CarControllerParams(CP) + def update(self, cp, cp_cam): + if self.CP.carFingerprint in HDA2_CAR: + return self.update_hda2(cp, cp_cam) + ret = car.CarState.new_message() ret.doorOpen = any([cp.vl["CGW1"]["CF_Gway_DrvDrSw"], cp.vl["CGW1"]["CF_Gway_AstDrSw"], @@ -52,7 +63,7 @@ class CarState(CarStateBase): 50, cp.vl["CGW1"]["CF_Gway_TurnSigLh"], cp.vl["CGW1"]["CF_Gway_TurnSigRh"]) ret.steeringTorque = cp.vl["MDPS12"]["CR_Mdps_StrColTq"] ret.steeringTorqueEps = cp.vl["MDPS12"]["CR_Mdps_OutTq"] - ret.steeringPressed = abs(ret.steeringTorque) > STEER_THRESHOLD + ret.steeringPressed = abs(ret.steeringTorque) > self.params.STEER_THRESHOLD ret.steerFaultTemporary = cp.vl["MDPS12"]["CF_Mdps_ToiUnavail"] != 0 or cp.vl["MDPS12"]["CF_Mdps_ToiFlt"] != 0 # cruise state @@ -120,10 +131,61 @@ class CarState(CarStateBase): return ret + def update_hda2(self, cp, cp_cam): + ret = car.CarState.new_message() + + ret.gas = cp.vl["ACCELERATOR"]["ACCELERATOR_PEDAL"] / 255. + ret.gasPressed = ret.gas > 1e-3 + ret.brakePressed = cp.vl["BRAKE"]["BRAKE_PRESSED"] == 1 + + ret.doorOpen = cp.vl["DOORS_SEATBELTS"]["DRIVER_DOOR_OPEN"] == 1 + ret.seatbeltUnlatched = cp.vl["DOORS_SEATBELTS"]["DRIVER_SEATBELT_LATCHED"] == 0 + + gear = cp.vl["ACCELERATOR"]["GEAR"] + ret.gearShifter = self.parse_gear_shifter(self.shifter_values.get(gear)) + + # TODO: figure out positions + ret.wheelSpeeds = self.get_wheel_speeds( + cp.vl["WHEEL_SPEEDS"]["WHEEL_SPEED_1"], + cp.vl["WHEEL_SPEEDS"]["WHEEL_SPEED_2"], + cp.vl["WHEEL_SPEEDS"]["WHEEL_SPEED_3"], + cp.vl["WHEEL_SPEEDS"]["WHEEL_SPEED_4"], + ) + ret.vEgoRaw = (ret.wheelSpeeds.fl + ret.wheelSpeeds.fr + ret.wheelSpeeds.rl + ret.wheelSpeeds.rr) / 4. + ret.vEgo, ret.aEgo = self.update_speed_kf(ret.vEgoRaw) + ret.standstill = ret.vEgoRaw < 0.1 + + ret.steeringRateDeg = cp.vl["STEERING_SENSORS"]["STEERING_RATE"] + ret.steeringAngleDeg = cp.vl["STEERING_SENSORS"]["STEERING_ANGLE"] * -1 + ret.steeringTorque = cp.vl["MDPS"]["STEERING_COL_TORQUE"] + ret.steeringTorqueEps = cp.vl["MDPS"]["STEERING_OUT_TORQUE"] + ret.steeringPressed = abs(ret.steeringTorque) > self.params.STEER_THRESHOLD + + ret.leftBlinker, ret.rightBlinker = self.update_blinker_from_lamp(50, cp.vl["BLINKERS"]["LEFT_LAMP"], + cp.vl["BLINKERS"]["RIGHT_LAMP"]) + + ret.cruiseState.available = True + ret.cruiseState.enabled = cp.vl["SCC1"]["CRUISE_ACTIVE"] == 1 + ret.cruiseState.standstill = cp.vl["CRUISE_INFO"]["CRUISE_STANDSTILL"] == 1 + + speed_factor = CV.MPH_TO_MS if cp.vl["CLUSTER_INFO"]["DISTANCE_UNIT"] == 1 else CV.KPH_TO_MS + ret.cruiseState.speed = cp.vl["CRUISE_INFO"]["SET_SPEED"] * speed_factor + + self.cruise_buttons.extend(cp.vl_all["CRUISE_BUTTONS"]["CRUISE_BUTTONS"]) + self.main_buttons.extend(cp.vl_all["CRUISE_BUTTONS"]["ADAPTIVE_CRUISE_MAIN_BTN"]) + self.buttons_counter = cp.vl["CRUISE_BUTTONS"]["COUNTER"] + + self.cam_0x2a4 = copy.copy(cp_cam.vl["CAM_0x2a4"]) + + return ret + @staticmethod def get_can_parser(CP): + if CP.carFingerprint in HDA2_CAR: + return CarState.get_can_parser_hda2(CP) + signals = [ - # sig_name, sig_address + # signal_name, signal_address ("WHL_SPD_FL", "WHL_SPD11"), ("WHL_SPD_FR", "WHL_SPD11"), ("WHL_SPD_RL", "WHL_SPD11"), @@ -135,9 +197,9 @@ class CarState(CarStateBase): ("CF_Gway_DrvSeatBeltSw", "CGW1"), ("CF_Gway_DrvDrSw", "CGW1"), # Driver Door - ("CF_Gway_AstDrSw", "CGW1"), # Passenger door - ("CF_Gway_RLDrSw", "CGW2"), # Rear reft door - ("CF_Gway_RRDrSw", "CGW2"), # Rear right door + ("CF_Gway_AstDrSw", "CGW1"), # Passenger Door + ("CF_Gway_RLDrSw", "CGW2"), # Rear left Door + ("CF_Gway_RRDrSw", "CGW2"), # Rear right Door ("CF_Gway_TurnSigLh", "CGW1"), ("CF_Gway_TurnSigRh", "CGW1"), ("CF_Gway_ParkBrakeSw", "CGW1"), @@ -175,7 +237,6 @@ class CarState(CarStateBase): ("SAS_Angle", "SAS11"), ("SAS_Speed", "SAS11"), ] - checks = [ # address, frequency ("MDPS12", 50), @@ -198,7 +259,6 @@ class CarState(CarStateBase): ("ACC_ObjDist", "SCC11"), ("ACCMode", "SCC12"), ] - checks += [ ("SCC11", 50), ("SCC12", 50), @@ -256,8 +316,13 @@ class CarState(CarStateBase): @staticmethod def get_cam_can_parser(CP): + if CP.carFingerprint in HDA2_CAR: + signals = [(f"BYTE{i}", "CAM_0x2a4") for i in range(3, 24)] + checks = [("CAM_0x2a4", 20)] + return CANParser(DBC[CP.carFingerprint]["pt"], signals, checks, 6) + signals = [ - # sig_name, sig_address + # signal_name, signal_address ("CF_Lkas_LdwsActivemode", "LKAS11"), ("CF_Lkas_LdwsSysState", "LKAS11"), ("CF_Lkas_SysWarning", "LKAS11"), @@ -274,9 +339,57 @@ class CarState(CarStateBase): ("CF_Lkas_FcwOpt_USM", "LKAS11"), ("CF_Lkas_LdwsOpt_USM", "LKAS11"), ] - checks = [ ("LKAS11", 100) ] return CANParser(DBC[CP.carFingerprint]["pt"], signals, checks, 2) + + @staticmethod + def get_can_parser_hda2(CP): + signals = [ + ("WHEEL_SPEED_1", "WHEEL_SPEEDS"), + ("WHEEL_SPEED_2", "WHEEL_SPEEDS"), + ("WHEEL_SPEED_3", "WHEEL_SPEEDS"), + ("WHEEL_SPEED_4", "WHEEL_SPEEDS"), + + ("ACCELERATOR_PEDAL", "ACCELERATOR"), + ("GEAR", "ACCELERATOR"), + ("BRAKE_PRESSED", "BRAKE"), + + ("STEERING_RATE", "STEERING_SENSORS"), + ("STEERING_ANGLE", "STEERING_SENSORS"), + ("STEERING_COL_TORQUE", "MDPS"), + ("STEERING_OUT_TORQUE", "MDPS"), + + ("CRUISE_ACTIVE", "SCC1"), + ("SET_SPEED", "CRUISE_INFO"), + ("CRUISE_STANDSTILL", "CRUISE_INFO"), + ("COUNTER", "CRUISE_BUTTONS"), + ("CRUISE_BUTTONS", "CRUISE_BUTTONS"), + ("ADAPTIVE_CRUISE_MAIN_BTN", "CRUISE_BUTTONS"), + + ("DISTANCE_UNIT", "CLUSTER_INFO"), + + ("LEFT_LAMP", "BLINKERS"), + ("RIGHT_LAMP", "BLINKERS"), + + ("DRIVER_DOOR_OPEN", "DOORS_SEATBELTS"), + ("DRIVER_SEATBELT_LATCHED", "DOORS_SEATBELTS"), + ] + + checks = [ + ("WHEEL_SPEEDS", 100), + ("ACCELERATOR", 100), + ("BRAKE", 100), + ("STEERING_SENSORS", 100), + ("MDPS", 100), + ("SCC1", 50), + ("CRUISE_INFO", 50), + ("CRUISE_BUTTONS", 50), + ("CLUSTER_INFO", 4), + ("BLINKERS", 4), + ("DOORS_SEATBELTS", 4), + ] + + return CANParser(DBC[CP.carFingerprint]["pt"], signals, checks, 5) diff --git a/selfdrive/car/hyundai/hda2can.py b/selfdrive/car/hyundai/hda2can.py new file mode 100644 index 000000000..9a9e477cf --- /dev/null +++ b/selfdrive/car/hyundai/hda2can.py @@ -0,0 +1,26 @@ +def create_lkas(packer, enabled, frame, lat_active, apply_steer): + values = { + "LKA_MODE": 2, + "LKA_ICON": 2 if enabled else 1, + "TORQUE_REQUEST": apply_steer, + "LKA_ASSIST": 0, + "STEER_REQ": 1 if lat_active else 0, + "STEER_MODE": 0, + "SET_ME_1": 0, + "NEW_SIGNAL_1": 0, + "NEW_SIGNAL_2": 0, + } + return packer.make_can_msg("LKAS", 4, values, frame % 255) + +def create_cam_0x2a4(packer, frame, camera_values): + camera_values.update({ + "BYTE7": 0, + }) + return packer.make_can_msg("CAM_0x2a4", 4, camera_values, frame % 255) + +def create_buttons(packer, cnt, btn): + values = { + "SET_ME_1": 1, + "CRUISE_BUTTONS": btn, + } + return packer.make_can_msg("CRUISE_BUTTONS", 5, values, cnt % 0xf) diff --git a/selfdrive/car/hyundai/hyundaican.py b/selfdrive/car/hyundai/hyundaican.py index d03eb135f..53499053e 100644 --- a/selfdrive/car/hyundai/hyundaican.py +++ b/selfdrive/car/hyundai/hyundaican.py @@ -86,8 +86,8 @@ def create_acc_commands(packer, enabled, accel, jerk, idx, lead_visible, set_spe "TauGapSet": 4, "VSetDis": set_speed if enabled else 0, "AliveCounterACC": idx % 0x10, - "ObjValid": 1 if lead_visible else 0, - "ACC_ObjStatus": 1 if lead_visible else 0, + "ObjValid": 0, # TODO: these two bits may allow for better longitudinal control + "ACC_ObjStatus": 0, "ACC_ObjLatPos": 0, "ACC_ObjRelSpd": 0, "ACC_ObjDist": 0, diff --git a/selfdrive/car/hyundai/interface.py b/selfdrive/car/hyundai/interface.py index cd71ff980..a32ee2c0a 100644 --- a/selfdrive/car/hyundai/interface.py +++ b/selfdrive/car/hyundai/interface.py @@ -4,13 +4,15 @@ from panda import Panda from common.conversions import Conversions as CV from selfdrive.car.hyundai.values import CAR, DBC, EV_CAR, HYBRID_CAR, LEGACY_SAFETY_MODE_CAR, Buttons, CarControllerParams from selfdrive.car.hyundai.radar_interface import RADAR_START_ADDR -from selfdrive.car import STD_CARGO_KG, scale_rot_inertia, scale_tire_stiffness, gen_empty_fingerprint, get_safety_config +from selfdrive.car import STD_CARGO_KG, create_button_enable_events, create_button_event, scale_rot_inertia, scale_tire_stiffness, gen_empty_fingerprint, get_safety_config from selfdrive.car.interfaces import CarInterfaceBase from selfdrive.car.disable_ecu import disable_ecu ButtonType = car.CarState.ButtonEvent.Type EventName = car.CarEvent.EventName ENABLE_BUTTONS = (Buttons.RES_ACCEL, Buttons.SET_DECEL, Buttons.CANCEL) +BUTTONS_DICT = {Buttons.RES_ACCEL: ButtonType.accelCruise, Buttons.SET_DECEL: ButtonType.decelCruise, + Buttons.GAP_DIST: ButtonType.gapAdjustCruise, Buttons.CANCEL: ButtonType.cancel} class CarInterface(CarInterfaceBase): @@ -37,7 +39,6 @@ class CarInterface(CarInterfaceBase): ret.dashcamOnly = candidate in {CAR.KIA_OPTIMA_H, CAR.ELANTRA_GT_I30} ret.steerActuatorDelay = 0.1 # Default delay - ret.steerRateCost = 0.5 ret.steerLimitTimer = 0.4 tire_stiffness_factor = 1. @@ -48,8 +49,7 @@ class CarInterface(CarInterfaceBase): ret.longitudinalTuning.kiV = [0.0] ret.stopAccel = 0.0 - ret.longitudinalActuatorDelayUpperBound = 1.0 # s - + ret.longitudinalActuatorDelayUpperBound = 1.0 # s if candidate in (CAR.SANTA_FE, CAR.SANTA_FE_2022, CAR.SANTA_FE_HEV_2022, CAR.SANTA_FE_PHEV_2022): ret.lateralTuning.pid.kf = 0.00005 ret.mass = 3982. * CV.LB_TO_KG + STD_CARGO_KG @@ -60,13 +60,11 @@ class CarInterface(CarInterfaceBase): ret.lateralTuning.pid.kiBP, ret.lateralTuning.pid.kpBP = [[9., 22.], [9., 22.]] ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.2, 0.35], [0.05, 0.09]] elif candidate in (CAR.SONATA, CAR.SONATA_HYBRID): - ret.lateralTuning.pid.kf = 0.00005 ret.mass = 1513. + STD_CARGO_KG ret.wheelbase = 2.84 ret.steerRatio = 13.27 * 1.15 # 15% higher at the center seems reasonable tire_stiffness_factor = 0.65 - ret.lateralTuning.pid.kiBP, ret.lateralTuning.pid.kpBP = [[0.], [0.]] - ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.25], [0.05]] + CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning) elif candidate == CAR.SONATA_LF: ret.lateralTuning.pid.kf = 0.00005 ret.mass = 4497. * CV.LB_TO_KG @@ -92,21 +90,17 @@ class CarInterface(CarInterfaceBase): ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.25], [0.05]] ret.minSteerSpeed = 32 * CV.MPH_TO_MS elif candidate == CAR.ELANTRA_2021: - ret.lateralTuning.pid.kf = 0.00005 ret.mass = (2800. * CV.LB_TO_KG) + STD_CARGO_KG ret.wheelbase = 2.72 ret.steerRatio = 12.9 tire_stiffness_factor = 0.65 - ret.lateralTuning.pid.kiBP, ret.lateralTuning.pid.kpBP = [[0.], [0.]] - ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.25], [0.05]] + CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning) elif candidate == CAR.ELANTRA_HEV_2021: - ret.lateralTuning.pid.kf = 0.00005 ret.mass = (3017. * CV.LB_TO_KG) + STD_CARGO_KG ret.wheelbase = 2.72 ret.steerRatio = 12.9 tire_stiffness_factor = 0.65 - ret.lateralTuning.pid.kiBP, ret.lateralTuning.pid.kpBP = [[0.], [0.]] - ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.25], [0.05]] + CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning) elif candidate == CAR.HYUNDAI_GENESIS: ret.lateralTuning.pid.kf = 0.00005 ret.mass = 2060. + STD_CARGO_KG @@ -162,9 +156,9 @@ class CarInterface(CarInterfaceBase): tire_stiffness_factor = 0.5 ret.lateralTuning.pid.kiBP, ret.lateralTuning.pid.kpBP = [[0.], [0.]] ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.25], [0.05]] - elif candidate == CAR.TUCSON_DIESEL_2019: + elif candidate == CAR.TUCSON: ret.lateralTuning.pid.kf = 0.00005 - ret.mass = 3633. * CV.LB_TO_KG + ret.mass = 3520. * CV.LB_TO_KG ret.wheelbase = 2.67 ret.steerRatio = 14.00 * 1.15 tire_stiffness_factor = 0.385 @@ -204,13 +198,11 @@ class CarInterface(CarInterfaceBase): ret.lateralTuning.indi.actuatorEffectivenessBP = [0.] ret.lateralTuning.indi.actuatorEffectivenessV = [1.8] elif candidate in (CAR.KIA_OPTIMA, CAR.KIA_OPTIMA_H): - ret.lateralTuning.pid.kf = 0.00005 ret.mass = 3558. * CV.LB_TO_KG ret.wheelbase = 2.80 ret.steerRatio = 13.75 tire_stiffness_factor = 0.5 - ret.lateralTuning.pid.kiBP, ret.lateralTuning.pid.kpBP = [[0.], [0.]] - ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.25], [0.05]] + CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning) elif candidate == CAR.KIA_STINGER: ret.lateralTuning.pid.kf = 0.00005 ret.mass = 1825. + STD_CARGO_KG @@ -243,6 +235,14 @@ class CarInterface(CarInterfaceBase): tire_stiffness_factor = 0.5 ret.lateralTuning.pid.kiBP, ret.lateralTuning.pid.kpBP = [[0.], [0.]] ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.25], [0.05]] + elif candidate == CAR.KIA_EV6: + ret.mass = 2055 + STD_CARGO_KG + ret.wheelbase = 2.9 + ret.steerRatio = 16. + ret.safetyConfigs = [get_safety_config(car.CarParams.SafetyModel.noOutput), + get_safety_config(car.CarParams.SafetyModel.hyundaiHDA2)] + tire_stiffness_factor = 0.65 + CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning) # Genesis elif candidate == CAR.GENESIS_G70: @@ -326,37 +326,14 @@ class CarInterface(CarInterfaceBase): if self.CS.brake_error: events.add(EventName.brakeUnavailable) - if self.CS.CP.openpilotLongitudinalControl: - buttonEvents = [] + if self.CS.CP.openpilotLongitudinalControl and self.CS.cruise_buttons[-1] != self.CS.prev_cruise_buttons: + buttonEvents = [create_button_event(self.CS.cruise_buttons[-1], self.CS.prev_cruise_buttons, BUTTONS_DICT)] + # Handle CF_Clu_CruiseSwState changing buttons mid-press + if self.CS.cruise_buttons[-1] != 0 and self.CS.prev_cruise_buttons != 0: + buttonEvents.append(create_button_event(0, self.CS.prev_cruise_buttons, BUTTONS_DICT)) - if self.CS.cruise_buttons[-1] != self.CS.prev_cruise_buttons: - be = car.CarState.ButtonEvent.new_message() - be.type = ButtonType.unknown - if self.CS.cruise_buttons[-1] != 0: - be.pressed = True - but = self.CS.cruise_buttons[-1] - else: - be.pressed = False - but = self.CS.prev_cruise_buttons - if but == Buttons.RES_ACCEL: - be.type = ButtonType.accelCruise - elif but == Buttons.SET_DECEL: - be.type = ButtonType.decelCruise - elif but == Buttons.GAP_DIST: - be.type = ButtonType.gapAdjustCruise - elif but == Buttons.CANCEL: - be.type = ButtonType.cancel - buttonEvents.append(be) - - ret.buttonEvents = buttonEvents - - for b in ret.buttonEvents: - # do enable on both accel and decel buttons - if b.type in (ButtonType.accelCruise, ButtonType.decelCruise) and not b.pressed: - events.add(EventName.buttonEnable) - # do disable on button down - if b.type == ButtonType.cancel and b.pressed: - events.add(EventName.buttonCancel) + ret.buttonEvents = buttonEvents + events.events.extend(create_button_enable_events(ret.buttonEvents)) # low speed steer alert hysteresis logic (only for cars with steer cut off above 10 m/s) if ret.vEgo < (self.CP.minSteerSpeed + 2.) and self.CP.minSteerSpeed > 10.: @@ -371,5 +348,4 @@ class CarInterface(CarInterfaceBase): return ret def apply(self, c): - ret = self.CC.update(c, self.CS) - return ret + return self.CC.update(c, self.CS) diff --git a/selfdrive/car/hyundai/values.py b/selfdrive/car/hyundai/values.py index 6cf3c9b05..ffa29c60d 100644 --- a/selfdrive/car/hyundai/values.py +++ b/selfdrive/car/hyundai/values.py @@ -7,25 +7,35 @@ from selfdrive.car import dbc_dict from selfdrive.car.docs_definitions import CarInfo, Harness Ecu = car.CarParams.Ecu -# Steer torque limits + class CarControllerParams: ACCEL_MIN = -3.5 # m/s ACCEL_MAX = 2.0 # m/s def __init__(self, CP): - # To determine the limit for your car, find the maximum value that the stock LKAS will request. - # If the max stock LKAS request is <384, add your car to this list. - if CP.carFingerprint in (CAR.GENESIS_G80, CAR.GENESIS_G90, CAR.ELANTRA, CAR.HYUNDAI_GENESIS, CAR.ELANTRA_GT_I30, CAR.IONIQ, - CAR.IONIQ_EV_LTD, CAR.SANTA_FE_PHEV_2022, CAR.SONATA_LF, CAR.KIA_FORTE, CAR.KIA_NIRO_HEV, - CAR.KIA_OPTIMA_H, CAR.KIA_SORENTO, CAR.KIA_STINGER): - self.STEER_MAX = 255 - else: - self.STEER_MAX = 384 self.STEER_DELTA_UP = 3 self.STEER_DELTA_DOWN = 7 self.STEER_DRIVER_ALLOWANCE = 50 self.STEER_DRIVER_MULTIPLIER = 2 self.STEER_DRIVER_FACTOR = 1 + self.STEER_THRESHOLD = 150 + + if CP.carFingerprint in HDA2_CAR: + self.STEER_MAX = 270 + self.STEER_DRIVER_ALLOWANCE = 250 + self.STEER_DRIVER_MULTIPLIER = 2 + self.STEER_THRESHOLD = 250 + + # To determine the limit for your car, find the maximum value that the stock LKAS will request. + # If the max stock LKAS request is <384, add your car to this list. + elif CP.carFingerprint in (CAR.GENESIS_G80, CAR.GENESIS_G90, CAR.ELANTRA, CAR.HYUNDAI_GENESIS, CAR.ELANTRA_GT_I30, CAR.IONIQ, + CAR.IONIQ_EV_LTD, CAR.SANTA_FE_PHEV_2022, CAR.SONATA_LF, CAR.KIA_FORTE, CAR.KIA_NIRO_HEV, + CAR.KIA_OPTIMA_H, CAR.KIA_SORENTO, CAR.KIA_STINGER): + self.STEER_MAX = 255 + + # Default for most HKG + else: + self.STEER_MAX = 384 class CAR: @@ -50,7 +60,7 @@ class CAR: SANTA_FE_PHEV_2022 = "HYUNDAI SANTA FE PlUG-IN HYBRID 2022" SONATA = "HYUNDAI SONATA 2020" SONATA_LF = "HYUNDAI SONATA 2019" - TUCSON_DIESEL_2019 = "HYUNDAI TUCSON DIESEL 2019" + TUCSON = "HYUNDAI TUCSON 2019" PALISADE = "HYUNDAI PALISADE 2020" VELOSTER = "HYUNDAI VELOSTER 2019" SONATA_HYBRID = "HYUNDAI SONATA HYBRID 2021" @@ -67,6 +77,7 @@ class CAR: KIA_SORENTO = "KIA SORENTO GT LINE 2018" KIA_STINGER = "KIA STINGER GT2 2018" KIA_CEED = "KIA CEED INTRO ED 2019" + KIA_EV6 = "KIA EV6 2022" # Genesis GENESIS_G70 = "GENESIS G70 2018" @@ -77,8 +88,9 @@ class CAR: @dataclass class HyundaiCarInfo(CarInfo): + # TODO: we can probably remove LKAS. LKAS is standard on many + # HKG and for others, it's likely packaged together with SCC package: str = "SCC + LKAS" - good_torque: bool = True CAR_INFO: Dict[str, Optional[Union[HyundaiCarInfo, List[HyundaiCarInfo]]]] = { @@ -95,14 +107,17 @@ CAR_INFO: Dict[str, Optional[Union[HyundaiCarInfo, List[HyundaiCarInfo]]]] = { CAR.IONIQ_PHEV: HyundaiCarInfo("Hyundai Ioniq Plug-in Hybrid 2020-21", harness=Harness.hyundai_h), CAR.KONA: HyundaiCarInfo("Hyundai Kona 2020", harness=Harness.hyundai_b), CAR.KONA_EV: HyundaiCarInfo("Hyundai Kona Electric 2018-21", harness=Harness.hyundai_g), - CAR.KONA_HEV: HyundaiCarInfo("Hyundai Kona Hybrid 2020", harness=Harness.hyundai_i), + CAR.KONA_HEV: HyundaiCarInfo("Hyundai Kona Hybrid 2020", video_link="https://youtu.be/0dwpAHiZgFo", harness=Harness.hyundai_i), CAR.SANTA_FE: HyundaiCarInfo("Hyundai Santa Fe 2019-20", "All", harness=Harness.hyundai_d), CAR.SANTA_FE_2022: HyundaiCarInfo("Hyundai Santa Fe 2021-22", "All", harness=Harness.hyundai_l), CAR.SANTA_FE_HEV_2022: HyundaiCarInfo("Hyundai Santa Fe Hybrid 2022", "All", harness=Harness.hyundai_l), CAR.SANTA_FE_PHEV_2022: HyundaiCarInfo("Hyundai Santa Fe Plug-in Hybrid 2022", "All", harness=Harness.hyundai_l), CAR.SONATA: HyundaiCarInfo("Hyundai Sonata 2020-22", "All", video_link="https://www.youtube.com/watch?v=ix63r9kE3Fw", harness=Harness.hyundai_a), CAR.SONATA_LF: HyundaiCarInfo("Hyundai Sonata 2018-19", harness=Harness.hyundai_e), - CAR.TUCSON_DIESEL_2019: HyundaiCarInfo("Hyundai Tucson Diesel 2019", harness=Harness.hyundai_l), + CAR.TUCSON: [ + HyundaiCarInfo("Hyundai Tucson 2021", min_enable_speed=19 * CV.MPH_TO_MS, harness=Harness.hyundai_l), + HyundaiCarInfo("Hyundai Tucson Diesel 2019", harness=Harness.hyundai_l), + ], CAR.PALISADE: [ HyundaiCarInfo("Hyundai Palisade 2020-21", "All", video_link="https://youtu.be/TAnDqjF4fDY?t=456", harness=Harness.hyundai_h), HyundaiCarInfo("Kia Telluride 2020", harness=Harness.hyundai_h), @@ -115,7 +130,7 @@ CAR_INFO: Dict[str, Optional[Union[HyundaiCarInfo, List[HyundaiCarInfo]]]] = { HyundaiCarInfo("Kia Forte 2018", harness=Harness.hyundai_b), HyundaiCarInfo("Kia Forte 2019-21", harness=Harness.hyundai_g), ], - CAR.KIA_K5_2021: HyundaiCarInfo("Kia K5 2021-22", "SCC + LFA", harness=Harness.hyundai_a), + CAR.KIA_K5_2021: HyundaiCarInfo("Kia K5 2021-22", "SCC", harness=Harness.hyundai_a), CAR.KIA_NIRO_EV: [ HyundaiCarInfo("Kia Niro Electric 2019-20", "All", video_link="https://www.youtube.com/watch?v=lT7zcG6ZpGo", harness=Harness.hyundai_f), HyundaiCarInfo("Kia Niro Electric 2021", "All", video_link="https://www.youtube.com/watch?v=lT7zcG6ZpGo", harness=Harness.hyundai_c), @@ -136,14 +151,15 @@ CAR_INFO: Dict[str, Optional[Union[HyundaiCarInfo, List[HyundaiCarInfo]]]] = { HyundaiCarInfo("Kia Sorento 2018", video_link="https://www.youtube.com/watch?v=Fkh3s6WHJz8", harness=Harness.hyundai_c), HyundaiCarInfo("Kia Sorento 2019", video_link="https://www.youtube.com/watch?v=Fkh3s6WHJz8", harness=Harness.hyundai_e), ], - CAR.KIA_STINGER: HyundaiCarInfo("Kia Stinger 2018", video_link="https://www.youtube.com/watch?v=MJ94qoofYw0", harness=Harness.hyundai_c), + CAR.KIA_STINGER: HyundaiCarInfo("Kia Stinger 2018-20", video_link="https://www.youtube.com/watch?v=MJ94qoofYw0", harness=Harness.hyundai_c), CAR.KIA_CEED: HyundaiCarInfo("Kia Ceed 2019", harness=Harness.hyundai_e), + CAR.KIA_EV6: HyundaiCarInfo("Kia EV6 2022", "All", harness=Harness.hyundai_p), # Genesis - CAR.GENESIS_G70: HyundaiCarInfo("Genesis G70 2018", "All", harness=Harness.hyundai_f), + CAR.GENESIS_G70: HyundaiCarInfo("Genesis G70 2018-19", "All", harness=Harness.hyundai_f), CAR.GENESIS_G70_2020: HyundaiCarInfo("Genesis G70 2020", "All", harness=Harness.hyundai_f), - CAR.GENESIS_G80: HyundaiCarInfo("Genesis G80 2018", "All", harness=Harness.hyundai_h), - CAR.GENESIS_G90: HyundaiCarInfo("Genesis G90 2018", "All", harness=Harness.hyundai_c), + CAR.GENESIS_G80: HyundaiCarInfo("Genesis G80 2017-19", "All", harness=Harness.hyundai_h), + CAR.GENESIS_G90: HyundaiCarInfo("Genesis G90 2017-18", "All", harness=Harness.hyundai_c), } class Buttons: @@ -151,7 +167,7 @@ class Buttons: RES_ACCEL = 1 SET_DECEL = 2 GAP_DIST = 3 - CANCEL = 4 + CANCEL = 4 # on newer models, this is a pause/resume button FINGERPRINTS = { CAR.ELANTRA: [{ @@ -373,7 +389,7 @@ FW_VERSIONS = { b'\xf1\x00DN ESC \x06 104\x19\x08\x01 58910-L0100', b'\xf1\x00DN ESC \x07 104\x19\x08\x01 58910-L0100', b'\xf1\x00DN ESC \x08 103\x19\x06\x01 58910-L1300', - b'\xf1\x8758910-L0100\xf1\x00DN ESC \a 106 \a\x01 58910-L0100', + b'\xf1\x8758910-L0100\xf1\x00DN ESC \x07 106 \x07\x01 58910-L0100', b'\xf1\x8758910-L0100\xf1\x00DN ESC \x06 104\x19\x08\x01 58910-L0100', b'\xf1\x8758910-L0100\xf1\x00DN ESC \x06 106 \x07\x01 58910-L0100', b'\xf1\x8758910-L0100\xf1\x00DN ESC \x07 104\x19\x08\x01 58910-L0100', @@ -394,6 +410,7 @@ FW_VERSIONS = { b'HM6M1_0a0_F00', b'HM6M1_0a0_G20', b'HM6M2_0a0_BD0', + b'\xf1\x8739110-2S278\xf1\x82DNDVD5GMCCXXXL5B', ], (Ecu.eps, 0x7d4, None): [ b'\xf1\x00DN8 MDPS C 1,00 1,01 56310L0010\x00 4DNAC101', # modified firmware @@ -475,6 +492,7 @@ FW_VERSIONS = { b'\xf1\x87SAMFBA7978674GJ2gw\x87xgw\x97ywwwwvUGeUUeU\x87O\xfb\xff\x98w\x8f\xfffF\xf1\x81U913\x00\x00\x00\x00\x00\x00\xf1\x00bcsh8p54 U913\x00\x00\x00\x00\x00\x00SDN8T16NB2\n\xdd^\xbc', b'\xf1\x87SAMFBA9283024GJ2wwwwEUuWwwgwwwwwwwww\x87/\xfb\xff\x98w\x8f\xff<\xd3\xf1\x81U913\x00\x00\x00\x00\x00\x00\xf1\x00bcsh8p54 U913\x00\x00\x00\x00\x00\x00SDN8T16NB2\n\xdd^\xbc', b'\xf1\x87SAMFBA9708354GJ2wwwwVf\x86h\x88wx\x87xww\x87\x88\x88\x88\x88w/\xfa\xff\x97w\x8f\xff\x86\xa0\xf1\x81U913\x00\x00\x00\x00\x00\x00\xf1\x00bcsh8p54 U913\x00\x00\x00\x00\x00\x00SDN8T16NB2\n\xdd^\xbc', + b'\xf1\x87SANDB45316691GC6\x99\x99\x99\x99\x88\x88\xa8\x8avfwfwwww\x87wxwT\x9f\xfd\xff\x88wo\xff\x1c\xfa\xf1\x89HT6WAD10A1\xf1\x82SDN8G25NB3\x00\x00\x00\x00\x00\x00', ], }, CAR.SONATA_LF: { @@ -492,6 +510,7 @@ FW_VERSIONS = { ], (Ecu.fwdCamera, 0x7c4, None): [ b'\xf1\x00LFF LKAS AT USA LHD 1.00 1.01 95740-C1000 E51', + b'\xf1\x00LFF LKAS AT USA LHD 1.01 1.02 95740-C1000 E52', ], (Ecu.transmission, 0x7e1, None): [ b'\xf1\x006T6H0_C2\x00\x006T6B4051\x00\x00TLF0G24NL1\xb0\x9f\xee\xf5', @@ -500,20 +519,25 @@ FW_VERSIONS = { b'\xf1\x87\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xf1\x816T6B4051\x00\x00\xf1\x006T6H0_C2\x00\x006T6B4051\x00\x00TLF0G24SL2n\x8d\xbe\xd8', b'\xf1\x87LAHSGN012918KF10\x98\x88x\x87\x88\x88x\x87\x88\x88\x98\x88\x87w\x88w\x88\x88\x98\x886o\xf6\xff\x98w\x7f\xff3\x00\xf1\x816W3B1051\x00\x00\xf1\x006W351_C2\x00\x006W3B1051\x00\x00TLF0T20NL2\x00\x00\x00\x00', b'\xf1\x87LAHSGN012918KF10\x98\x88x\x87\x88\x88x\x87\x88\x88\x98\x88\x87w\x88w\x88\x88\x98\x886o\xf6\xff\x98w\x7f\xff3\x00\xf1\x816W3B1051\x00\x00\xf1\x006W351_C2\x00\x006W3B1051\x00\x00TLF0T20NL2H\r\xbdm', + b'\xf1\x87LAJSG49645724HF0\x87x\x87\x88\x87www\x88\x99\xa8\x89\x88\x99\xa8\x89\x88\x99\xa8\x89S_\xfb\xff\x87f\x7f\xff^2\xf1\x816W3B1051\x00\x00\xf1\x006W351_C2\x00\x006W3B1051\x00\x00TLF0T20NL2H\r\xbdm', ], }, - CAR.TUCSON_DIESEL_2019: { + CAR.TUCSON: { (Ecu.fwdRadar, 0x7d0, None): [ b'\xf1\x00TL__ FCA F-CUP 1.00 1.01 99110-D3500 ', + b'\xf1\x00TL__ FCA F-CUP 1.00 1.02 99110-D3510 ', ], (Ecu.engine, 0x7e0, None): [ b'\xf1\x8971TLC2NAIDDIR002\xf1\x8271TLC2NAIDDIR002', + b'\xf1\x81606G3051\x00\x00\x00\x00\x00\x00\x00\x00', ], (Ecu.fwdCamera, 0x7c4, None): [ b'\xf1\x00TL MFC AT KOR LHD 1.00 1.02 95895-D3800 180719', + b'\xf1\x00TL MFC AT USA LHD 1.00 1.06 95895-D3800 190107', ], (Ecu.transmission, 0x7e1, None): [ b'\xf1\x87LBJXAN202299KF22\x87x\x87\x88ww\x87xx\x88\x97\x88\x87\x88\x98x\x88\x99\x98\x89\x87o\xf6\xff\x87w\x7f\xff\x12\x9a\xf1\x81U083\x00\x00\x00\x00\x00\x00\xf1\x00bcsh8p54 U083\x00\x00\x00\x00\x00\x00TTL2V20KL1\x8fRn\x8a', + b'\xf1\x87KMLDCU585233TJ20wx\x87\x88x\x88\x98\x89vfwfwwww\x87f\x9f\xff\x98\xff\x7f\xf9\xf7s\xf1\x816T6G4051\x00\x00\xf1\x006T6J0_C2\x00\x006T6G4051\x00\x00TTL4G24NH2\x00\x00\x00\x00', ], }, CAR.SANTA_FE: { @@ -839,7 +863,7 @@ FW_VERSIONS = { }, CAR.KIA_CEED: { (Ecu.fwdRadar, 0x7D0, None): [b'\xf1\000CD__ SCC F-CUP 1.00 1.02 99110-J7000 ', ], - (Ecu.esp, 0x7D4, None): [b'\xf1\000CD MDPS C 1.00 1.06 56310-XX000 4CDEC106', ], + (Ecu.eps, 0x7D4, None): [b'\xf1\000CD MDPS C 1.00 1.06 56310-XX000 4CDEC106', ], (Ecu.fwdCamera, 0x7C4, None): [b'\xf1\000CD LKAS AT EUR LHD 1.00 1.01 99211-J7000 B40', ], (Ecu.engine, 0x7E0, None): [b'\001TCD-JECU4F202H0K', ], (Ecu.transmission, 0x7E1, None): [ @@ -942,6 +966,7 @@ FW_VERSIONS = { b'\xf1\x8799110Q4000\xf1\x00DEev SCC F-CUP 1.00 1.00 99110-Q4000 ', b'\xf1\x8799110Q4100\xf1\x00DEev SCC F-CUP 1.00 1.00 99110-Q4100 ', b'\xf1\x8799110Q4500\xf1\x00DEev SCC F-CUP 1.00 1.00 99110-Q4500 ', + b'\xf1\x8799110Q4600\xf1\x00DEev SCC F-CUP 1.00 1.00 99110-Q4600 ', b'\xf1\x8799110Q4600\xf1\x00DEev SCC FNCUP 1.00 1.00 99110-Q4600 ', b'\xf1\x8799110Q4600\xf1\x00DEev SCC FHCUP 1.00 1.00 99110-Q4600 ', ], @@ -1122,9 +1147,6 @@ FW_VERSIONS = { b'\xf1\000DNhe SCC F-CUP 1.00 1.02 99110-L5000 ', b'\xf1\x8799110L5000\xf1\000DNhe SCC F-CUP 1.00 1.02 99110-L5000 ', ], - (Ecu.esp, 0x7b0, None): [ - b'\xf1\x87\x00\x00\x00\x00\x00\x00\x00\x00\xf1\x81\x00\x00\x00\x00\x00\x00\x00\x00', - ], (Ecu.eps, 0x7d4, None): [ b'\xf1\x8756310-L5500\xf1\x00DN8 MDPS C 1.00 1.02 56310-L5500 4DNHC102', b'\xf1\x8756310-L5450\xf1\x00DN8 MDPS C 1.00 1.02 56310-L5450 4DNHC102', @@ -1166,6 +1188,21 @@ FW_VERSIONS = { b'\xf1\x81640F0051\x00\x00\x00\x00\x00\x00\x00\x00' ], }, + CAR.KIA_EV6: { + (Ecu.esp, 0x7d1, None): [ + b'\xf1\x8758520CV100\xf1\x00CV IEB \x02 101!\x10\x18 58520-CV100', + ], + (Ecu.eps, 0x7d4, None): [ + b'\xf1\x00CV1 MDPS R 1.00 1.04 57700-CV000 1B30', + ], + (Ecu.fwdRadar, 0x7d0, None): [ + b'\xf1\x00CV1_ RDR ----- 1.00 1.01 99110-CV000 ', + b'\xf1\x8799110CV000\xf1\x00CV1_ RDR ----- 1.00 1.01 99110-CV000 ', + ], + (Ecu.fwdCamera, 0x7c4, None): [ + b'\xf1\x00CV1 MFC AT USA LHD 1.00 1.05 99210-CV000 211027', + ], + }, } CHECKSUM = { @@ -1176,13 +1213,15 @@ CHECKSUM = { FEATURES = { # which message has the gear "use_cluster_gears": {CAR.ELANTRA, CAR.ELANTRA_GT_I30, CAR.KONA}, - "use_tcu_gears": {CAR.KIA_OPTIMA, CAR.SONATA_LF, CAR.VELOSTER, CAR.TUCSON_DIESEL_2019}, + "use_tcu_gears": {CAR.KIA_OPTIMA, CAR.SONATA_LF, CAR.VELOSTER, CAR.TUCSON}, "use_elect_gears": {CAR.KIA_NIRO_EV, CAR.KIA_NIRO_HEV, CAR.KIA_NIRO_HEV_2021, CAR.KIA_OPTIMA_H, CAR.IONIQ_EV_LTD, CAR.KONA_EV, CAR.IONIQ, CAR.IONIQ_EV_2020, CAR.IONIQ_PHEV, CAR.ELANTRA_HEV_2021, CAR.SONATA_HYBRID, CAR.KONA_HEV, CAR.IONIQ_HEV_2022, CAR.SANTA_FE_HEV_2022, CAR.SANTA_FE_PHEV_2022, CAR.IONIQ_PHEV_2019}, # these cars use the FCA11 message for the AEB and FCW signals, all others use SCC12 - "use_fca": {CAR.SONATA, CAR.SONATA_HYBRID, CAR.ELANTRA, CAR.ELANTRA_2021, CAR.ELANTRA_HEV_2021, CAR.ELANTRA_GT_I30, CAR.KIA_STINGER, CAR.IONIQ_EV_2020, CAR.IONIQ_PHEV, CAR.KONA_EV, CAR.KIA_FORTE, CAR.KIA_NIRO_EV, CAR.PALISADE, CAR.GENESIS_G70, CAR.GENESIS_G70_2020, CAR.KONA, CAR.SANTA_FE, CAR.KIA_SELTOS, CAR.KONA_HEV, CAR.SANTA_FE_2022, CAR.KIA_K5_2021, CAR.IONIQ_HEV_2022, CAR.SANTA_FE_HEV_2022, CAR.SANTA_FE_PHEV_2022, CAR.TUCSON_DIESEL_2019}, + "use_fca": {CAR.SONATA, CAR.SONATA_HYBRID, CAR.ELANTRA, CAR.ELANTRA_2021, CAR.ELANTRA_HEV_2021, CAR.ELANTRA_GT_I30, CAR.KIA_STINGER, CAR.IONIQ_EV_2020, CAR.IONIQ_PHEV, CAR.KONA_EV, CAR.KIA_FORTE, CAR.KIA_NIRO_EV, CAR.PALISADE, CAR.GENESIS_G70, CAR.GENESIS_G70_2020, CAR.KONA, CAR.SANTA_FE, CAR.KIA_SELTOS, CAR.KONA_HEV, CAR.SANTA_FE_2022, CAR.KIA_K5_2021, CAR.IONIQ_HEV_2022, CAR.SANTA_FE_HEV_2022, CAR.SANTA_FE_PHEV_2022, CAR.TUCSON}, } +HDA2_CAR = {CAR.KIA_EV6, } + HYBRID_CAR = {CAR.IONIQ_PHEV, CAR.ELANTRA_HEV_2021, CAR.KIA_NIRO_HEV, CAR.KIA_NIRO_HEV_2021, CAR.SONATA_HYBRID, CAR.KONA_HEV, CAR.IONIQ, CAR.IONIQ_HEV_2022, CAR.SANTA_FE_HEV_2022, CAR.SANTA_FE_PHEV_2022, CAR.IONIQ_PHEV_2019} # these cars use a different gas signal EV_CAR = {CAR.IONIQ_EV_2020, CAR.IONIQ_EV_LTD, CAR.KONA_EV, CAR.KIA_NIRO_EV} @@ -1226,11 +1265,10 @@ DBC = { CAR.SANTA_FE_PHEV_2022: dbc_dict('hyundai_kia_generic', None), CAR.SONATA: dbc_dict('hyundai_kia_generic', 'hyundai_kia_mando_front_radar'), CAR.SONATA_LF: dbc_dict('hyundai_kia_generic', None), # Has 0x5XX messages, but different format - CAR.TUCSON_DIESEL_2019: dbc_dict('hyundai_kia_generic', None), + CAR.TUCSON: dbc_dict('hyundai_kia_generic', None), CAR.PALISADE: dbc_dict('hyundai_kia_generic', 'hyundai_kia_mando_front_radar'), CAR.VELOSTER: dbc_dict('hyundai_kia_generic', None), CAR.KIA_CEED: dbc_dict('hyundai_kia_generic', None), + CAR.KIA_EV6: dbc_dict('kia_ev6', None), CAR.SONATA_HYBRID: dbc_dict('hyundai_kia_generic', 'hyundai_kia_mando_front_radar'), } - -STEER_THRESHOLD = 150 diff --git a/selfdrive/car/interfaces.py b/selfdrive/car/interfaces.py index 1ee237fe3..4c7ea97df 100644 --- a/selfdrive/car/interfaces.py +++ b/selfdrive/car/interfaces.py @@ -1,13 +1,15 @@ +import yaml import os import time from abc import abstractmethod, ABC -from typing import Dict, Tuple, List +from typing import Any, Dict, Tuple, List from cereal import car +from common.basedir import BASEDIR +from common.conversions import Conversions as CV from common.kalman.simple_kalman import KF1D from common.realtime import DT_CTRL from selfdrive.car import gen_empty_fingerprint -from common.conversions import Conversions as CV from selfdrive.controls.lib.drive_helpers import V_CRUISE_MAX from selfdrive.controls.lib.events import Events from selfdrive.controls.lib.vehicle_model import VehicleModel @@ -19,10 +21,37 @@ MAX_CTRL_SPEED = (V_CRUISE_MAX + 4) * CV.KPH_TO_MS ACCEL_MAX = 2.0 ACCEL_MIN = -3.5 +TORQUE_PARAMS_PATH = os.path.join(BASEDIR, 'selfdrive/car/torque_data/params.yaml') +TORQUE_OVERRIDE_PATH = os.path.join(BASEDIR, 'selfdrive/car/torque_data/override.yaml') +TORQUE_SUBSTITUTE_PATH = os.path.join(BASEDIR, 'selfdrive/car/torque_data/substitute.yaml') + + +def get_torque_params(candidate): + with open(TORQUE_SUBSTITUTE_PATH) as f: + sub = yaml.load(f, Loader=yaml.CSafeLoader) + if candidate in sub: + candidate = sub[candidate] + + with open(TORQUE_PARAMS_PATH) as f: + params = yaml.load(f, Loader=yaml.CSafeLoader) + with open(TORQUE_OVERRIDE_PATH) as f: + override = yaml.load(f, Loader=yaml.CSafeLoader) + + # Ensure no overlap + if sum([candidate in x for x in [sub, params, override]]) > 1: + raise RuntimeError(f'{candidate} is defined twice in torque config') + + if candidate in override: + out = override[candidate] + elif candidate in params: + out = params[candidate] + else: + raise NotImplementedError(f"Did not find torque params for {candidate}") + return {key: out[i] for i, key in enumerate(params['legend'])} + # generic car and radar interfaces - class CarInterfaceBase(ABC): def __init__(self, CP, CarController, CarState): self.CP = CP @@ -81,6 +110,7 @@ class CarInterfaceBase(ABC): ret.steerControlType = car.CarParams.SteerControlType.torque ret.minSteerSpeed = 0. ret.wheelSpeedFactor = 1.0 + ret.maxLateralAccel = get_torque_params(candidate)['MAX_LAT_ACCEL_MEASURED'] ret.pcmCruise = True # openpilot's state is tied to the PCM's cruise state on most cars ret.minEnableSpeed = -1. # enable is done by stock ACC, so ignore this @@ -104,6 +134,18 @@ class CarInterfaceBase(ABC): ret.steerLimitTimer = 1.0 return ret + @staticmethod + def configure_torque_tune(candidate, tune, steering_angle_deadzone_deg=0.0, use_steering_angle=True): + params = get_torque_params(candidate) + + tune.init('torque') + tune.torque.useSteeringAngle = use_steering_angle + tune.torque.kp = 1.0 / params['LAT_ACCEL_FACTOR'] + tune.torque.kf = 1.0 / params['LAT_ACCEL_FACTOR'] + tune.torque.ki = 0.1 / params['LAT_ACCEL_FACTOR'] + tune.torque.friction = params['FRICTION'] + tune.torque.steeringAngleDeadzoneDeg = steering_angle_deadzone_deg + @abstractmethod def _update(self, c: car.CarControl) -> car.CarState: pass @@ -293,3 +335,31 @@ class CarStateBase(ABC): @staticmethod def get_loopback_can_parser(CP): return None + + +# interface-specific helpers + +def get_interface_attr(attr: str, combine_brands: bool = False, ignore_none: bool = False) -> Dict[str, Any]: + # read all the folders in selfdrive/car and return a dict where: + # - keys are all the car models or brand names + # - values are attr values from all car folders + result = {} + for car_folder in sorted([x[0] for x in os.walk(BASEDIR + '/selfdrive/car')]): + try: + brand_name = car_folder.split('/')[-1] + brand_values = __import__(f'selfdrive.car.{brand_name}.values', fromlist=[attr]) + if hasattr(brand_values, attr) or not ignore_none: + attr_data = getattr(brand_values, attr, None) + else: + continue + + if combine_brands: + if isinstance(attr_data, dict): + for f, v in attr_data.items(): + result[f] = v + else: + result[brand_name] = attr_data + except (ImportError, OSError): + pass + + return result diff --git a/selfdrive/car/isotp_parallel_query.py b/selfdrive/car/isotp_parallel_query.py index 1209a8f1a..bb96572c3 100644 --- a/selfdrive/car/isotp_parallel_query.py +++ b/selfdrive/car/isotp_parallel_query.py @@ -4,13 +4,13 @@ from functools import partial from typing import Optional import cereal.messaging as messaging -from selfdrive.swaglog import cloudlog +from system.swaglog import cloudlog from selfdrive.boardd.boardd import can_list_to_can_capnp from panda.python.uds import CanClient, IsoTpMessage, FUNCTIONAL_ADDRS, get_rx_addr_for_tx_addr class IsoTpParallelQuery: - def __init__(self, sendcan, logcan, bus, addrs, request, response, response_offset=0x8, functional_addr=False, debug=False): + def __init__(self, sendcan, logcan, bus, addrs, request, response, response_offset=0x8, functional_addr=False, debug=False, response_pending_timeout=10): self.sendcan = sendcan self.logcan = logcan self.bus = bus @@ -18,6 +18,7 @@ class IsoTpParallelQuery: self.response = response self.debug = debug self.functional_addr = functional_addr + self.response_pending_timeout = response_pending_timeout self.real_addrs = [] for a in addrs: @@ -71,10 +72,7 @@ class IsoTpParallelQuery: messaging.drain_sock(self.logcan) self.msg_buffer = defaultdict(list) - def get_data(self, timeout, total_timeout=None): - if total_timeout is None: - total_timeout = 10 * timeout - + def get_data(self, timeout, total_timeout=60.): self._drain_rx() # Create message objects @@ -100,7 +98,7 @@ class IsoTpParallelQuery: results = {} start_time = time.monotonic() - last_response_time = start_time + response_timeouts = {tx_addr: start_time + timeout for tx_addr in self.msg_addrs} while True: self.rx() @@ -123,21 +121,27 @@ class IsoTpParallelQuery: response_valid = dat[:len(expected_response)] == expected_response if response_valid: - last_response_time = time.monotonic() + response_timeouts[tx_addr] = time.monotonic() + timeout if counter + 1 < len(self.request): msg.send(self.request[counter + 1]) request_counter[tx_addr] += 1 else: - results[tx_addr] = dat[len(expected_response):] + results[(tx_addr, msg._can_client.rx_addr)] = dat[len(expected_response):] request_done[tx_addr] = True else: - request_done[tx_addr] = True - cloudlog.warning(f"iso-tp query bad response: 0x{dat.hex()}") + error_code = dat[2] if len(dat) > 2 else -1 + if error_code == 0x78: + response_timeouts[tx_addr] = time.monotonic() + self.response_pending_timeout + if self.debug: + cloudlog.warning(f"iso-tp query response pending: {tx_addr}") + else: + request_done[tx_addr] = True + cloudlog.warning(f"iso-tp query bad response: {tx_addr} - 0x{dat.hex()}") cur_time = time.monotonic() - if cur_time - last_response_time > timeout: + if cur_time - max(response_timeouts.values()) > 0: for tx_addr in msgs: - if (request_counter[tx_addr] > 0) and (not request_done[tx_addr]): + if request_counter[tx_addr] > 0 and not request_done[tx_addr]: cloudlog.warning(f"iso-tp query timeout after receiving response: {tx_addr}") break diff --git a/selfdrive/car/mazda/carcontroller.py b/selfdrive/car/mazda/carcontroller.py index 68eebeaf7..a83cef508 100644 --- a/selfdrive/car/mazda/carcontroller.py +++ b/selfdrive/car/mazda/carcontroller.py @@ -1,66 +1,67 @@ from cereal import car from opendbc.can.packer import CANPacker +from selfdrive.car import apply_std_steer_torque_limits from selfdrive.car.mazda import mazdacan from selfdrive.car.mazda.values import CarControllerParams, Buttons -from selfdrive.car import apply_std_steer_torque_limits VisualAlert = car.CarControl.HUDControl.VisualAlert -class CarController(): + +class CarController: def __init__(self, dbc_name, CP, VM): self.CP = CP self.apply_steer_last = 0 self.packer = CANPacker(dbc_name) self.steer_rate_limited = False self.brake_counter = 0 + self.frame = 0 - def update(self, c, CS, frame): + def update(self, CC, CS): can_sends = [] apply_steer = 0 self.steer_rate_limited = False - if c.latActive: + if CC.latActive: # calculate steer and also set limits due to driver torque - new_steer = int(round(c.actuators.steer * CarControllerParams.STEER_MAX)) + new_steer = int(round(CC.actuators.steer * CarControllerParams.STEER_MAX)) apply_steer = apply_std_steer_torque_limits(new_steer, self.apply_steer_last, CS.out.steeringTorque, CarControllerParams) self.steer_rate_limited = new_steer != apply_steer - if CS.out.standstill and frame % 5 == 0: - # Mazda Stop and Go requires a RES button (or gas) press if the car stops more than 3 seconds - # Send Resume button at 20hz if we're engaged at standstill to support full stop and go! - # TODO: improve the resume trigger logic by looking at actual radar data - can_sends.append(mazdacan.create_button_cmd(self.packer, self.CP.carFingerprint, CS.crz_btns_counter, Buttons.RESUME)) - - if c.cruiseControl.cancel: + if CC.cruiseControl.cancel: # If brake is pressed, let us wait >70ms before trying to disable crz to avoid # a race condition with the stock system, where the second cancel from openpilot # will disable the crz 'main on'. crz ctrl msg runs at 50hz. 70ms allows us to # read 3 messages and most likely sync state before we attempt cancel. self.brake_counter = self.brake_counter + 1 - if frame % 10 == 0 and not (CS.out.brakePressed and self.brake_counter < 7): + if self.frame % 10 == 0 and not (CS.out.brakePressed and self.brake_counter < 7): # Cancel Stock ACC if it's enabled while OP is disengaged # Send at a rate of 10hz until we sync with stock ACC state can_sends.append(mazdacan.create_button_cmd(self.packer, self.CP.carFingerprint, CS.crz_btns_counter, Buttons.CANCEL)) else: self.brake_counter = 0 + if CC.cruiseControl.resume and self.frame % 5 == 0: + # Mazda Stop and Go requires a RES button (or gas) press if the car stops more than 3 seconds + # Send Resume button when planner wants car to move + can_sends.append(mazdacan.create_button_cmd(self.packer, self.CP.carFingerprint, CS.crz_btns_counter, Buttons.RESUME)) self.apply_steer_last = apply_steer # send HUD alerts - if frame % 50 == 0: - ldw = c.hudControl.visualAlert == VisualAlert.ldw - steer_required = c.hudControl.visualAlert == VisualAlert.steerRequired + if self.frame % 50 == 0: + ldw = CC.hudControl.visualAlert == VisualAlert.ldw + steer_required = CC.hudControl.visualAlert == VisualAlert.steerRequired # TODO: find a way to silence audible warnings so we can add more hud alerts steer_required = steer_required and CS.lkas_allowed_speed can_sends.append(mazdacan.create_alert_command(self.packer, CS.cam_laneinfo, ldw, steer_required)) # send steering command can_sends.append(mazdacan.create_steering_control(self.packer, self.CP.carFingerprint, - frame, apply_steer, CS.cam_lkas)) + self.frame, apply_steer, CS.cam_lkas)) - new_actuators = c.actuators.copy() + new_actuators = CC.actuators.copy() new_actuators.steer = apply_steer / CarControllerParams.STEER_MAX + self.frame += 1 return new_actuators, can_sends diff --git a/selfdrive/car/mazda/carstate.py b/selfdrive/car/mazda/carstate.py index 6e1ad3e48..944d79809 100644 --- a/selfdrive/car/mazda/carstate.py +++ b/selfdrive/car/mazda/carstate.py @@ -79,6 +79,7 @@ class CarState(CarStateBase): # it should be used for carState.cruiseState.nonAdaptive instead ret.cruiseState.available = cp.vl["CRZ_CTRL"]["CRZ_AVAILABLE"] == 1 ret.cruiseState.enabled = cp.vl["CRZ_CTRL"]["CRZ_ACTIVE"] == 1 + ret.cruiseState.standstill = cp.vl["PEDALS"]["STANDSTILL"] == 1 ret.cruiseState.speed = cp.vl["CRZ_EVENTS"]["CRZ_SPEED"] * CV.KPH_TO_MS if ret.cruiseState.enabled: diff --git a/selfdrive/car/mazda/interface.py b/selfdrive/car/mazda/interface.py index 0ef573c6b..35e3c1bb0 100755 --- a/selfdrive/car/mazda/interface.py +++ b/selfdrive/car/mazda/interface.py @@ -25,7 +25,6 @@ class CarInterface(CarInterfaceBase): ret.dashcamOnly = candidate not in (CAR.CX5_2022, CAR.CX9_2021) ret.steerActuatorDelay = 0.1 - ret.steerRateCost = 1.0 ret.steerLimitTimer = 0.8 tire_stiffness_factor = 0.70 # not optimized yet @@ -91,6 +90,4 @@ class CarInterface(CarInterfaceBase): return ret def apply(self, c): - ret = self.CC.update(c, self.CS, self.frame) - self.frame += 1 - return ret + return self.CC.update(c, self.CS) diff --git a/selfdrive/car/mazda/values.py b/selfdrive/car/mazda/values.py index 5c80303db..e1d690799 100644 --- a/selfdrive/car/mazda/values.py +++ b/selfdrive/car/mazda/values.py @@ -40,8 +40,8 @@ CAR_INFO: Dict[str, Union[MazdaCarInfo, List[MazdaCarInfo]]] = { CAR.CX9: MazdaCarInfo("Mazda CX-9 2016-17"), CAR.MAZDA3: MazdaCarInfo("Mazda 3 2017"), CAR.MAZDA6: MazdaCarInfo("Mazda 6 2017"), - CAR.CX9_2021: MazdaCarInfo("Mazda CX-9 2021", good_torque=True), - CAR.CX5_2022: MazdaCarInfo("Mazda CX-5 2022", good_torque=True), + CAR.CX9_2021: MazdaCarInfo("Mazda CX-9 2021-22"), + CAR.CX5_2022: MazdaCarInfo("Mazda CX-5 2022"), } @@ -65,6 +65,8 @@ FW_VERSIONS = { ], (Ecu.engine, 0x7e0, None): [ b'PX2G-188K2-H\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', + b'PX2H-188K2-H\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', + b'SH54-188K2-D\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', ], (Ecu.fwdRadar, 0x764, None): [ b'K131-67XK2-F\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', @@ -77,6 +79,7 @@ FW_VERSIONS = { ], (Ecu.transmission, 0x7e1, None): [ b'PYB2-21PS1-H\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', + b'SH51-21PS1-C\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', ], }, CAR.CX5: { @@ -264,6 +267,7 @@ FW_VERSIONS = { (Ecu.engine, 0x7e0, None): [ b'PXM4-188K2-C\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', b'PXM4-188K2-D\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', + b'PXM6-188K2-E\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', ], (Ecu.fwdRadar, 0x764, None): [ b'K131-67XK2-E\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', @@ -276,9 +280,11 @@ FW_VERSIONS = { b'GSH7-67XK2-M\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', b'GSH7-67XK2-N\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', b'GSH7-67XK2-P\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', + b'GSH7-67XK2-S\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', ], (Ecu.transmission, 0x7e1, None): [ b'PXM4-21PS1-B\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', + b'PXM6-21PS1-B\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', ], } } diff --git a/selfdrive/car/mock/interface.py b/selfdrive/car/mock/interface.py index 65e886751..715850c22 100755 --- a/selfdrive/car/mock/interface.py +++ b/selfdrive/car/mock/interface.py @@ -2,7 +2,7 @@ import math from cereal import car from common.conversions import Conversions as CV -from selfdrive.swaglog import cloudlog +from system.swaglog import cloudlog import cereal.messaging as messaging from selfdrive.car import gen_empty_fingerprint, get_safety_config from selfdrive.car.interfaces import CarInterfaceBase diff --git a/selfdrive/car/nissan/carcontroller.py b/selfdrive/car/nissan/carcontroller.py index 4ceb142d4..7d9dad669 100644 --- a/selfdrive/car/nissan/carcontroller.py +++ b/selfdrive/car/nissan/carcontroller.py @@ -1,25 +1,27 @@ from cereal import car from common.numpy_fast import clip, interp -from selfdrive.car.nissan import nissancan from opendbc.can.packer import CANPacker +from selfdrive.car.nissan import nissancan from selfdrive.car.nissan.values import CAR, CarControllerParams - VisualAlert = car.CarControl.HUDControl.VisualAlert -class CarController(): +class CarController: def __init__(self, dbc_name, CP, VM): self.CP = CP self.car_fingerprint = CP.carFingerprint + self.frame = 0 self.lkas_max_torque = 0 self.last_angle = 0 self.packer = CANPacker(dbc_name) - def update(self, c, CS, frame, actuators, cruise_cancel, hud_alert, - left_line, right_line, left_lane_depart, right_lane_depart): + def update(self, CC, CS): + actuators = CC.actuators + hud_control = CC.hudControl + pcm_cancel_cmd = CC.cruiseControl.cancel can_sends = [] @@ -28,9 +30,9 @@ class CarController(): lkas_hud_info_msg = CS.lkas_hud_info_msg apply_angle = actuators.steeringAngleDeg - steer_hud_alert = 1 if hud_alert in (VisualAlert.steerRequired, VisualAlert.ldw) else 0 + steer_hud_alert = 1 if hud_control.visualAlert in (VisualAlert.steerRequired, VisualAlert.ldw) else 0 - if c.latActive: + if CC.latActive: # # windup slower if self.last_angle * apply_angle > 0. and abs(apply_angle) > abs(self.last_angle): angle_rate_lim = interp(CS.out.vEgo, CarControllerParams.ANGLE_DELTA_BP, CarControllerParams.ANGLE_DELTA_V) @@ -57,25 +59,25 @@ class CarController(): self.last_angle = apply_angle - if self.CP.carFingerprint in (CAR.ROGUE, CAR.XTRAIL, CAR.ALTIMA) and cruise_cancel: - can_sends.append(nissancan.create_acc_cancel_cmd(self.packer, self.car_fingerprint, CS.cruise_throttle_msg, frame)) + if self.CP.carFingerprint in (CAR.ROGUE, CAR.XTRAIL, CAR.ALTIMA) and pcm_cancel_cmd: + can_sends.append(nissancan.create_acc_cancel_cmd(self.packer, self.car_fingerprint, CS.cruise_throttle_msg, self.frame)) # TODO: Find better way to cancel! # For some reason spamming the cancel button is unreliable on the Leaf # We now cancel by making propilot think the seatbelt is unlatched, # this generates a beep and a warning message every time you disengage - if self.CP.carFingerprint in (CAR.LEAF, CAR.LEAF_IC) and frame % 2 == 0: - can_sends.append(nissancan.create_cancel_msg(self.packer, CS.cancel_msg, cruise_cancel)) + if self.CP.carFingerprint in (CAR.LEAF, CAR.LEAF_IC) and self.frame % 2 == 0: + can_sends.append(nissancan.create_cancel_msg(self.packer, CS.cancel_msg, pcm_cancel_cmd)) can_sends.append(nissancan.create_steering_control( - self.packer, apply_angle, frame, c.enabled, self.lkas_max_torque)) + self.packer, apply_angle, self.frame, CC.enabled, self.lkas_max_torque)) if lkas_hud_msg and lkas_hud_info_msg: - if frame % 2 == 0: + if self.frame % 2 == 0: can_sends.append(nissancan.create_lkas_hud_msg( - self.packer, lkas_hud_msg, c.enabled, left_line, right_line, left_lane_depart, right_lane_depart)) + self.packer, lkas_hud_msg, CC.enabled, hud_control.leftLaneVisible, hud_control.rightLaneVisible, hud_control.leftLaneDepart, hud_control.rightLaneDepart)) - if frame % 50 == 0: + if self.frame % 50 == 0: can_sends.append(nissancan.create_lkas_hud_info_msg( self.packer, lkas_hud_info_msg, steer_hud_alert )) @@ -83,4 +85,5 @@ class CarController(): new_actuators = actuators.copy() new_actuators.steeringAngleDeg = apply_angle + self.frame += 1 return new_actuators, can_sends diff --git a/selfdrive/car/nissan/interface.py b/selfdrive/car/nissan/interface.py index 3bf34e826..9c04d975f 100644 --- a/selfdrive/car/nissan/interface.py +++ b/selfdrive/car/nissan/interface.py @@ -1,8 +1,9 @@ #!/usr/bin/env python3 from cereal import car -from selfdrive.car.nissan.values import CAR from selfdrive.car import STD_CARGO_KG, scale_rot_inertia, scale_tire_stiffness, gen_empty_fingerprint, get_safety_config from selfdrive.car.interfaces import CarInterfaceBase +from selfdrive.car.nissan.values import CAR + class CarInterface(CarInterfaceBase): @@ -14,7 +15,6 @@ class CarInterface(CarInterfaceBase): ret.safetyConfigs = [get_safety_config(car.CarParams.SafetyModel.nissan)] ret.steerLimitTimer = 1.0 - ret.steerRateCost = 0.5 ret.steerActuatorDelay = 0.1 @@ -68,10 +68,4 @@ class CarInterface(CarInterfaceBase): return ret def apply(self, c): - hud_control = c.hudControl - ret = self.CC.update(c, self.CS, self.frame, c.actuators, - c.cruiseControl.cancel, hud_control.visualAlert, - hud_control.leftLaneVisible, hud_control.rightLaneVisible, - hud_control.leftLaneDepart, hud_control.rightLaneDepart) - self.frame += 1 - return ret + return self.CC.update(c, self.CS) diff --git a/selfdrive/car/subaru/carcontroller.py b/selfdrive/car/subaru/carcontroller.py index d37b82eed..dca86c30a 100644 --- a/selfdrive/car/subaru/carcontroller.py +++ b/selfdrive/car/subaru/carcontroller.py @@ -1,10 +1,10 @@ +from opendbc.can.packer import CANPacker from selfdrive.car import apply_std_steer_torque_limits from selfdrive.car.subaru import subarucan from selfdrive.car.subaru.values import DBC, PREGLOBAL_CARS, CarControllerParams -from opendbc.can.packer import CANPacker -class CarController(): +class CarController: def __init__(self, dbc_name, CP, VM): self.CP = CP self.apply_steer_last = 0 @@ -12,16 +12,20 @@ class CarController(): self.es_lkas_cnt = -1 self.cruise_button_prev = 0 self.steer_rate_limited = False + self.frame = 0 self.p = CarControllerParams(CP) self.packer = CANPacker(DBC[CP.carFingerprint]['pt']) - def update(self, c, CS, frame, actuators, pcm_cancel_cmd, visual_alert, left_line, right_line, left_lane_depart, right_lane_depart): + def update(self, CC, CS): + actuators = CC.actuators + hud_control = CC.hudControl + pcm_cancel_cmd = CC.cruiseControl.cancel can_sends = [] # *** steering *** - if (frame % self.p.STEER_STEP) == 0: + if (self.frame % self.p.STEER_STEP) == 0: apply_steer = int(round(actuators.steer * self.p.STEER_MAX)) @@ -31,13 +35,13 @@ class CarController(): apply_steer = apply_std_steer_torque_limits(new_steer, self.apply_steer_last, CS.out.steeringTorque, self.p) self.steer_rate_limited = new_steer != apply_steer - if not c.latActive: + if not CC.latActive: apply_steer = 0 if self.CP.carFingerprint in PREGLOBAL_CARS: - can_sends.append(subarucan.create_preglobal_steering_control(self.packer, apply_steer, frame, self.p.STEER_STEP)) + can_sends.append(subarucan.create_preglobal_steering_control(self.packer, apply_steer, self.frame, self.p.STEER_STEP)) else: - can_sends.append(subarucan.create_steering_control(self.packer, apply_steer, frame, self.p.STEER_STEP)) + can_sends.append(subarucan.create_steering_control(self.packer, apply_steer, self.frame, self.p.STEER_STEP)) self.apply_steer_last = apply_steer @@ -70,10 +74,11 @@ class CarController(): self.es_distance_cnt = CS.es_distance_msg["Counter"] if self.es_lkas_cnt != CS.es_lkas_msg["Counter"]: - can_sends.append(subarucan.create_es_lkas(self.packer, CS.es_lkas_msg, c.enabled, visual_alert, left_line, right_line, left_lane_depart, right_lane_depart)) + can_sends.append(subarucan.create_es_lkas(self.packer, CS.es_lkas_msg, CC.enabled, hud_control.visualAlert, hud_control.leftLaneVisible, hud_control.rightLaneVisible, hud_control.leftLaneDepart, hud_control.rightLaneDepart)) self.es_lkas_cnt = CS.es_lkas_msg["Counter"] new_actuators = actuators.copy() new_actuators.steer = self.apply_steer_last / self.p.STEER_MAX + self.frame += 1 return new_actuators, can_sends diff --git a/selfdrive/car/subaru/interface.py b/selfdrive/car/subaru/interface.py index d0d8e91ce..952885a75 100644 --- a/selfdrive/car/subaru/interface.py +++ b/selfdrive/car/subaru/interface.py @@ -1,8 +1,9 @@ #!/usr/bin/env python3 from cereal import car -from selfdrive.car.subaru.values import CAR, PREGLOBAL_CARS from selfdrive.car import STD_CARGO_KG, scale_rot_inertia, scale_tire_stiffness, gen_empty_fingerprint, get_safety_config from selfdrive.car.interfaces import CarInterfaceBase +from selfdrive.car.subaru.values import CAR, PREGLOBAL_CARS + class CarInterface(CarInterfaceBase): @@ -22,7 +23,6 @@ class CarInterface(CarInterfaceBase): ret.dashcamOnly = candidate in PREGLOBAL_CARS - ret.steerRateCost = 0.7 ret.steerLimitTimer = 0.4 if candidate == CAR.ASCENT: @@ -118,9 +118,4 @@ class CarInterface(CarInterfaceBase): return ret def apply(self, c): - hud_control = c.hudControl - ret = self.CC.update(c, self.CS, self.frame, c.actuators, - c.cruiseControl.cancel, hud_control.visualAlert, - hud_control.leftLaneVisible, hud_control.rightLaneVisible, hud_control.leftLaneDepart, hud_control.rightLaneDepart) - self.frame += 1 - return ret + return self.CC.update(c, self.CS) diff --git a/selfdrive/car/subaru/values.py b/selfdrive/car/subaru/values.py index 6e81fcb1b..8fac93428 100644 --- a/selfdrive/car/subaru/values.py +++ b/selfdrive/car/subaru/values.py @@ -41,16 +41,18 @@ class SubaruCarInfo(CarInfo): CAR_INFO: Dict[str, Union[SubaruCarInfo, List[SubaruCarInfo]]] = { - CAR.ASCENT: SubaruCarInfo("Subaru Ascent 2019-20"), + CAR.ASCENT: SubaruCarInfo("Subaru Ascent 2019-21", "All"), CAR.IMPREZA: [ - SubaruCarInfo("Subaru Impreza 2017-19", good_torque=True), - SubaruCarInfo("Subaru Crosstrek 2018-19", video_link="https://youtu.be/Agww7oE1k-s?t=26", good_torque=True), + SubaruCarInfo("Subaru Impreza 2017-19"), + SubaruCarInfo("Subaru Crosstrek 2018-19", video_link="https://youtu.be/Agww7oE1k-s?t=26"), + SubaruCarInfo("Subaru XV 2018-19", video_link="https://youtu.be/Agww7oE1k-s?t=26"), ], CAR.IMPREZA_2020: [ - SubaruCarInfo("Subaru Impreza 2020-21"), + SubaruCarInfo("Subaru Impreza 2020-22"), SubaruCarInfo("Subaru Crosstrek 2020-21"), + SubaruCarInfo("Subaru XV 2020-21"), ], - CAR.FORESTER: SubaruCarInfo("Subaru Forester 2019-21", good_torque=True), + CAR.FORESTER: SubaruCarInfo("Subaru Forester 2019-22", "All"), CAR.FORESTER_PREGLOBAL: SubaruCarInfo("Subaru Forester 2017-18"), CAR.LEGACY_PREGLOBAL: SubaruCarInfo("Subaru Legacy 2015-18"), CAR.OUTBACK_PREGLOBAL: SubaruCarInfo("Subaru Outback 2015-17"), diff --git a/selfdrive/car/tesla/interface.py b/selfdrive/car/tesla/interface.py index 71594cecb..5e78c72b6 100755 --- a/selfdrive/car/tesla/interface.py +++ b/selfdrive/car/tesla/interface.py @@ -42,7 +42,6 @@ class CarInterface(CarInterfaceBase): ret.steerLimitTimer = 1.0 ret.steerActuatorDelay = 0.25 - ret.steerRateCost = 0.5 if candidate in (CAR.AP2_MODELS, CAR.AP1_MODELS): ret.mass = 2100. + STD_CARGO_KG @@ -65,5 +64,4 @@ class CarInterface(CarInterfaceBase): return ret def apply(self, c): - ret = self.CC.update(c, self.CS) - return ret + return self.CC.update(c, self.CS) diff --git a/selfdrive/car/tesla/values.py b/selfdrive/car/tesla/values.py index 2ed4c963e..296169587 100644 --- a/selfdrive/car/tesla/values.py +++ b/selfdrive/car/tesla/values.py @@ -22,7 +22,7 @@ CAR_INFO: Dict[str, Union[CarInfo, List[CarInfo]]] = { FINGERPRINTS = { CAR.AP2_MODELS: [ { - 1: 8, 3: 8, 14: 8, 21: 4, 69: 8, 109: 4, 257: 3, 264: 8, 277: 6, 280: 6, 293: 4, 296: 4, 309: 5, 325: 8, 328: 5, 336: 8, 341: 8, 360: 7, 373: 8, 389: 8, 415: 8, 513: 5, 516: 8, 518: 8, 520: 4, 522: 8, 524: 8, 526: 8, 532: 3, 536: 8, 537: 3, 538: 8, 542: 8, 551: 5, 552: 2, 556: 8, 558: 8, 568: 8, 569: 8, 574: 8, 576: 3, 577: 8, 582: 5, 583: 8, 584: 4, 585: 8, 590: 8, 601: 8, 606: 8, 608: 1, 622: 8, 627: 6, 638: 8, 641: 8, 643: 8, 692: 8, 693: 8, 695: 8, 696: 8, 697: 8, 699: 8, 700: 8, 701: 8, 702: 8, 703: 8, 704: 8, 708: 8, 709: 8, 710: 8, 711: 8, 712: 8, 728: 8, 744: 8, 760: 8, 772: 8, 775: 8, 776: 8, 777: 8, 778: 8, 782: 8, 788: 8, 791: 8, 792: 8, 796: 2, 797: 8, 798: 6, 799: 8, 804: 8, 805: 8, 807: 8, 808: 1, 811: 8, 812: 8, 813: 8, 814: 5, 815: 8, 820: 8, 823: 8, 824: 8, 829: 8, 830: 5, 836: 8, 840: 8, 845: 8, 846: 5, 848: 8, 852: 8, 853: 8, 856: 4, 857: 6, 861: 8, 862: 5, 872: 8, 876: 8, 877: 8, 879: 8, 880: 8, 882: 8, 884: 8, 888: 8, 893: 8, 894: 8, 901: 6, 904: 3, 905: 8, 906: 8, 908: 2, 909: 8, 910: 8, 912: 8, 920: 8, 921: 8, 925: 4, 926: 6, 936: 8, 941: 8, 949: 8, 952: 8, 953: 6, 968: 8, 969: 6, 970: 8, 971: 8, 977: 8, 984: 8, 987: 8, 990: 8, 1000: 8, 1001: 8, 1006: 8, 1007: 8, 1008: 8, 1010: 6, 1014: 1, 1015: 8, 1016: 8, 1017: 8, 1018: 8, 1020: 8, 1026: 8, 1028: 8, 1029: 8, 1030: 8, 1032: 1, 1033: 1, 1034: 8, 1048: 1, 1049: 8, 1061: 8, 1064: 8, 1065: 8, 1070: 8, 1080: 8, 1081: 8, 1097: 8, 1113: 8, 1129: 8, 1145: 8, 1160: 4, 1177: 8, 1281: 8, 1328: 8, 1329: 8, 1332: 8, 1335: 8, 1337: 8, 1353: 8, 1368: 8, 1412: 8, 1436: 8, 1476: 8, 1481: 8, 1497: 8, 1513: 8, 1519: 8, 1601: 8, 1605: 8, 1617: 8, 1621: 8, 1625: 8, 1800: 4, 1804: 8, 1812: 8, 1815: 8, 1816: 8, 1824: 8, 1828: 8, 1831: 8, 1832: 8, 1840: 8, 1848: 8, 1864: 8, 1880: 8, 1892: 8, 1896: 8, 1912: 8, 1960: 8, 1992: 8, 2008: 3, 2015: 8, 2043: 5, 2045: 4 + 1: 8, 3: 8, 14: 8, 21: 4, 69: 8, 109: 4, 257: 3, 264: 8, 277: 6, 280: 6, 293: 4, 296: 4, 309: 5, 325: 8, 328: 5, 336: 8, 341: 8, 360: 7, 373: 8, 389: 8, 415: 8, 513: 5, 516: 8, 518: 8, 520: 4, 522: 8, 524: 8, 526: 8, 532: 3, 536: 8, 537: 3, 538: 8, 542: 8, 551: 5, 552: 2, 556: 8, 558: 8, 568: 8, 569: 8, 574: 8, 576: 3, 577: 8, 582: 5, 583: 8, 584: 4, 585: 8, 590: 8, 601: 8, 606: 8, 608: 1, 622: 8, 627: 6, 638: 8, 641: 8, 643: 8, 692: 8, 693: 8, 695: 8, 696: 8, 697: 8, 699: 8, 700: 8, 701: 8, 702: 8, 703: 8, 704: 8, 708: 8, 709: 8, 710: 8, 711: 8, 712: 8, 728: 8, 744: 8, 760: 8, 772: 8, 775: 8, 776: 8, 777: 8, 778: 8, 782: 8, 788: 8, 791: 8, 792: 8, 796: 2, 797: 8, 798: 6, 799: 8, 804: 8, 805: 8, 807: 8, 808: 1, 811: 8, 812: 8, 813: 8, 814: 5, 815: 8, 820: 8, 823: 8, 824: 8, 829: 8, 830: 5, 836: 8, 840: 8, 845: 8, 846: 5, 848: 8, 852: 8, 853: 8, 856: 4, 857: 6, 861: 8, 862: 5, 872: 8, 876: 8, 877: 8, 879: 8, 880: 8, 882: 8, 884: 8, 888: 8, 893: 8, 894: 8, 901: 6, 904: 3, 905: 8, 906: 8, 908: 2, 909: 8, 910: 8, 912: 8, 920: 8, 921: 8, 925: 4, 926: 6, 936: 8, 941: 8, 949: 8, 952: 8, 953: 6, 968: 8, 969: 6, 970: 8, 971: 8, 977: 8, 984: 8, 987: 8, 990: 8, 1000: 8, 1001: 8, 1006: 8, 1007: 8, 1008: 8, 1010: 6, 1014: 1, 1015: 8, 1016: 8, 1017: 8, 1018: 8, 1020: 8, 1026: 8, 1028: 8, 1029: 8, 1030: 8, 1032: 1, 1033: 1, 1034: 8, 1048: 1, 1049: 8, 1061: 8, 1064: 8, 1065: 8, 1070: 8, 1080: 8, 1081: 8, 1097: 8, 1113: 8, 1129: 8, 1145: 8, 1160: 4, 1177: 8, 1281: 8, 1328: 8, 1329: 8, 1332: 8, 1335: 8, 1337: 8, 1353: 8, 1368: 8, 1412: 8, 1436: 8, 1476: 8, 1481: 8, 1497: 8, 1513: 8, 1519: 8, 1601: 8, 1605: 8, 1617: 8, 1621: 8, 1625: 8, 1665: 8, 1800: 4, 1804: 8, 1812: 8, 1815: 8, 1816: 8, 1824: 8, 1828: 8, 1831: 8, 1832: 8, 1840: 8, 1848: 8, 1864: 8, 1880: 8, 1892: 8, 1896: 8, 1912: 8, 1960: 8, 1992: 8, 2008: 3, 2015: 8, 2043: 5, 2045: 4 }, ], CAR.AP1_MODELS: [ diff --git a/selfdrive/car/tests/test_car_interfaces.py b/selfdrive/car/tests/test_car_interfaces.py index 18bf9c1f0..412874c81 100755 --- a/selfdrive/car/tests/test_car_interfaces.py +++ b/selfdrive/car/tests/test_car_interfaces.py @@ -1,4 +1,5 @@ #!/usr/bin/env python3 +import math import unittest import importlib from parameterized import parameterized @@ -32,16 +33,16 @@ class TestCarInterfaces(unittest.TestCase): assert car_interface self.assertGreater(car_params.mass, 1) - self.assertGreater(car_params.steerRateCost, 1e-3) + self.assertGreater(car_params.maxLateralAccel, 0) if car_params.steerControlType != car.CarParams.SteerControlType.angle: tuning = car_params.lateralTuning.which() if tuning == 'pid': self.assertTrue(len(car_params.lateralTuning.pid.kpV)) - elif tuning == 'lqr': - self.assertTrue(len(car_params.lateralTuning.lqr.a)) elif tuning == 'torque': - self.assertTrue(car_params.lateralTuning.torque.kf > 0) + kf = car_params.lateralTuning.torque.kf + self.assertTrue(not math.isnan(kf) and kf > 0) + self.assertTrue(not math.isnan(car_params.lateralTuning.torque.friction)) elif tuning == 'indi': self.assertTrue(len(car_params.lateralTuning.indi.outerLoopGainV)) diff --git a/selfdrive/car/torque_data/override.yaml b/selfdrive/car/torque_data/override.yaml new file mode 100644 index 000000000..8e6f62c4e --- /dev/null +++ b/selfdrive/car/torque_data/override.yaml @@ -0,0 +1,30 @@ +legend: [LAT_ACCEL_FACTOR, MAX_LAT_ACCEL_MEASURED, FRICTION] +### angle control +# Nissan appears to have torque +NISSAN X-TRAIL 2017: [.nan, 1.5, .nan] +NISSAN ALTIMA 2020: [.nan, 1.5, .nan] +NISSAN LEAF 2018 Instrument Cluster: [.nan, 1.5, .nan] +NISSAN LEAF 2018: [.nan, 1.5, .nan] +NISSAN ROGUE 2019: [.nan, 1.5, .nan] + +# Tesla has high torque +TESLA AP1 MODEL S: [.nan, 2.5, .nan] +TESLA AP2 MODEL S: [.nan, 2.5, .nan] + +# Guess +FORD ESCAPE 4TH GEN: [.nan, 1.5, .nan] +FORD FOCUS 4TH GEN: [.nan, 1.5, .nan] +### + +# No steering wheel +COMMA BODY: [.nan, 1000, .nan] + +# Totally new cars +KIA EV6 2022: [3.5, 2.5, 0.0] +RAM 1500 5TH GEN: [2.0, 2.0, 0.0] + +# Dashcam or fallback configured as ideal car +mock: [10.0, 10, 0.0] + +# Manually checked +HONDA CIVIC 2022: [2.5, 1.2, 0.15] diff --git a/selfdrive/car/torque_data/params.yaml b/selfdrive/car/torque_data/params.yaml new file mode 100644 index 000000000..160f60548 --- /dev/null +++ b/selfdrive/car/torque_data/params.yaml @@ -0,0 +1,96 @@ +ACURA ILX 2016: [1.524988973896102, 0.519011053086259, 0.34236219253028] +ACURA RDX 2018: [0.9987728568686902, 0.5323765166196301, 0.303218805715844] +ACURA RDX 2020: [1.4314459806646749, 0.33874701282109954, 0.18048847083897598] +AUDI A3 3RD GEN: [1.5122414863077502, 1.7443517531719404, 0.15194151892450905] +AUDI Q3 2ND GEN: [1.4439223359448605, 1.2254955789112076, 0.1413798895978097] +CHEVROLET VOLT PREMIER 2017: [1.5961527626411784, 1.8422651988094612, 0.1572393918005158] +CHRYSLER PACIFICA 2018: [1.593387270257916, 1.3366521181047952, 0.13776367250652022] +CHRYSLER PACIFICA 2020: [1.4323553627965695, 1.509076559398423, 0.14328246159386085] +CHRYSLER PACIFICA HYBRID 2017: [1.3032470208409048, 1.06831764583744, 0.13287170990024627] +CHRYSLER PACIFICA HYBRID 2018: [1.6068280248761635, 1.2943025830995154, 0.1358557824293823] +CHRYSLER PACIFICA HYBRID 2019: [1.4624643614072217, 1.1958788168371808, 0.15748488008472716] +GENESIS G70 2018: [3.8520195946707947, 2.354697063349854, 0.06830285485626221] +GMC ACADIA DENALI 2018: [1.3181430320331884, 1.1853735340610179, 0.3450592280031644] +HONDA ACCORD 2018: [1.7135052593468778, 0.3461280068322071, 0.21579936052863807] +HONDA ACCORD HYBRID 2018: [1.6651615004829625, 0.30322180951193245, 0.2083000440586149] +HONDA CIVIC (BOSCH) 2019: [1.691708637466905, 0.40132900729454185, 0.25460295304024094] +HONDA CIVIC 2016: [1.6528895627785531, 0.4018518740819229, 0.25458812851328544] +HONDA CR-V 2016: [0.7667141440182675, 0.5927571534745969, 0.40909087636157127] +HONDA CR-V 2017: [2.01323205142022, 0.2700612209345081, 0.2238412881331528] +HONDA CR-V HYBRID 2019: [2.072034634644233, 0.7152085160516978, 0.20237105008376083] +HONDA FIT 2018: [1.5719981427109775, 0.5712761407108976, 0.110773383324281] +HONDA HRV 2019: [2.0661212805710205, 0.7521343418694775, 0.17760375789242094] +HONDA INSIGHT 2019: [1.5201671214069354, 0.5660229120683284, 0.25808042580281876] +HONDA ODYSSEY 2018: [1.8774809275211801, 0.8394431662987996, 0.2096978613792822] +HONDA PASSPORT 2021: [1.5305538930036766, 0.7956063674638759, 0.19599407381531284] +HONDA PILOT 2017: [1.7262026201812795, 0.9470005614967523, 0.21351430733218763] +HONDA RIDGELINE 2017: [1.4146525028237624, 0.7356572861629564, 0.23307177552211328] +HYUNDAI GENESIS 2015-2016: [1.8466226943929824, 1.5552063647830634, 0.0984484465421171] +HYUNDAI IONIQ ELECTRIC LIMITED 2019: [1.7662975472852054, 1.613755614526594, 0.17087579756306276] +HYUNDAI IONIQ PHEV 2020: [3.2928700076638537, 2.1193482926455656, 0.12463700961468778] +HYUNDAI IONIQ PLUG-IN HYBRID 2019: [2.970807902012267, 1.6312321830002083, 0.1088964990357482] +HYUNDAI KONA ELECTRIC 2019: [4.398306735170212, 3.2961956260770484, 0.08651833437845884] +HYUNDAI PALISADE 2020: [2.544642494803999, 1.8721703683337008, 0.1301424599248651] +HYUNDAI SANTA FE 2019: [3.0787027729757632, 2.6173437483495565, 0.1207019341823945] +HYUNDAI SANTA FE HYBRID 2022: [3.501877602644835, 2.729064118456137, 0.10384068104538963] +HYUNDAI SANTA FE PlUG-IN HYBRID 2022: [1.6953050513611045, 1.5837614296206861, 0.12672855941458458] +HYUNDAI SONATA 2019: [2.2200457811703953, 1.2967330275895228, 0.14039920986586393] +HYUNDAI SONATA 2020: [3.284505627881726, 2.1259108157250735, 0.08452048323586728] +HYUNDAI SONATA HYBRID 2021: [2.8990264092395734, 2.061410192222139, 0.0899805488717382] +JEEP GRAND CHEROKEE 2019: [1.7321233388827006, 1.289689569171081, 0.15046331002097185] +JEEP GRAND CHEROKEE V6 2018: [1.8776598027756923, 1.4057367824262523, 0.11725947414922003] +KIA K5 2021: [2.405339728085138, 1.460032270828705, 0.11650989850813716] +KIA NIRO EV 2020: [2.9215954981365337, 2.1500583840260044, 0.09236802474810267] +KIA SORENTO GT LINE 2018: [2.464854685101844, 1.5335274218367956, 0.12056170567599558] +KIA STINGER GT2 2018: [2.7499043387418967, 1.849652021986449, 0.12048334239559202] +LEXUS ES 2019: [2.0203086922726112, 2.134803912579666, 0.12757526789308554] +LEXUS ES HYBRID 2019: [2.392442298703042, 1.863360677810788, 0.17690002108856212] +LEXUS NX 2018: [2.302625600642627, 2.1382378491466625, 0.14986840878892838] +LEXUS NX 2020: [2.4331999786982936, 2.1045680431705414, 0.14099899317761067] +LEXUS NX HYBRID 2018: [2.4025593501080955, 1.8080446063815507, 0.15349361249519017] +LEXUS RX 2016: [1.5876816543130423, 1.0427699298523752, 0.21334066732397142] +LEXUS RX 2020: [1.5228812994274734, 1.431102486563665, 0.2093316728710659] +LEXUS RX HYBRID 2017: [1.6984261557042386, 1.3211501880159107, 0.1820354534928893] +LEXUS RX HYBRID 2020: [1.5522309889823778, 1.255230465866663, 0.2220954003055114] +MAZDA CX-9 2021: [1.7601682915983443, 1.0889677335154337, 0.17713792194297195] +SKODA SUPERB 3RD GEN: [1.166437404652981, 1.1686163012668165, 0.12194533036948708] +SUBARU FORESTER 2019: [3.6617001649776793, 2.342197172531713, 0.11075960785398745] +SUBARU IMPREZA LIMITED 2019: [1.0670704910352047, 0.8234374840709592, 0.20986563268614938] +SUBARU IMPREZA SPORT 2020: [2.6068223389108303, 2.134872342760203, 0.15261513193561627] +TOYOTA AVALON 2016: [2.5185770183845646, 1.7153346784214922, 0.10603968787111022] +TOYOTA AVALON 2019: [1.7036141952825095, 1.239619084240008, 0.08459830394899492] +TOYOTA AVALON 2022: [2.3154403649717357, 2.7777922854327124, 0.11453999639164605] +TOYOTA C-HR 2018: [1.5591084333664578, 1.271271459066948, 0.20259087058453193] +TOYOTA C-HR 2021: [1.7678810166088303, 1.3742176337919942, 0.2319674583741509] +TOYOTA CAMRY 2018: [2.1172995371905015, 1.7156177222420887, 0.13519250664782062] +TOYOTA CAMRY 2021: [2.6922769557433055, 2.3476510120007434, 0.1450430192989234] +TOYOTA CAMRY HYBRID 2018: [2.0974120828287774, 1.7996193116697359, 0.13823613467632756] +TOYOTA CAMRY HYBRID 2021: [2.6426668350384457, 2.3901492458927986, 0.16103875108816076] +TOYOTA COROLLA 2017: [3.117154369115421, 1.8438132575043773, 0.12289685869250652] +TOYOTA COROLLA HYBRID TSS2 2019: [2.3287672277252005, 1.8118712531729109, 0.2215868445753317] +TOYOTA COROLLA TSS2 2019: [2.4204464833010175, 1.9258612322678952, 0.20670411068012526] +TOYOTA HIGHLANDER 2017: [1.8696367437248915, 1.626293990451463, 0.17485372210240796] +TOYOTA HIGHLANDER 2020: [2.022340166827233, 1.6183134804881791, 0.14592306380054457] +TOYOTA HIGHLANDER HYBRID 2018: [1.9421825202382728, 1.6433903296845025, 0.16928956792275918] +TOYOTA HIGHLANDER HYBRID 2020: [2.103373061114133, 2.104015182965606, 0.14447040132184993] +TOYOTA MIRAI 2021: [2.506899832157829, 1.7417213930750164, 0.20182618449440565] +TOYOTA PRIUS 2017: [2.0183401513314294, 1.5023147650693636, 0.20856908464957724] +TOYOTA PRIUS TSS2 2021: [2.327639738920072, 1.9104337425537743, 0.2030762265549664] +TOYOTA RAV4 2017: [2.085695074355425, 2.2142832316984733, 0.13339165270103975] +TOYOTA RAV4 2019: [2.5038362866776835, 2.0993589721530252, 0.1552425356342368] +TOYOTA RAV4 2019 8965: [2.5084506298290377, 2.4216520504763475, 0.11992835265067918] +TOYOTA RAV4 2019 x02: [2.7209621987605024, 2.2148637653781593, 0.10862567142268198] +TOYOTA RAV4 HYBRID 2017: [1.9796257271652042, 1.7503987331707576, 0.14628860048885406] +TOYOTA RAV4 HYBRID 2019: [2.2271858492309153, 2.074844961405639, 0.14382216826893632] +TOYOTA RAV4 HYBRID 2019 8965: [2.1077397198131336, 1.8162215166877735, 0.13891369391200137] +TOYOTA RAV4 HYBRID 2019 x02: [2.803624333289342, 2.272367966572498, 0.11364569214387774] +TOYOTA RAV4 HYBRID 2022: [2.241883248393209, 1.9304407208090029, 0.1565442715453653] +TOYOTA RAV4 HYBRID 2022 x02: [3.044930631831037, 2.3979189796380918, 0.14023209146703736] +TOYOTA SIENNA 2018: [1.8660896232147548, 1.3208264576110418, 0.18799149615227198] +VOLKSWAGEN ARTEON 1ST GEN: [1.45136518053819, 1.3639364049316804, 0.23806361745695032] +VOLKSWAGEN ATLAS 1ST GEN: [1.4677006726964945, 1.6733266634075656, 0.12959584092073367] +VOLKSWAGEN GOLF 7TH GEN: [1.3750394140491293, 1.5814743077200641, 0.2018321939386586] +VOLKSWAGEN JETTA 7TH GEN: [1.2271623034089392, 1.216955117387, 0.19437384688370712] +VOLKSWAGEN PASSAT 8TH GEN: [1.3432120736752917, 1.7087275587362314, 0.19444383787326647] +VOLKSWAGEN TIGUAN 2ND GEN: [0.9711965500094828, 1.0001565939459098, 0.1465626137072916] +legend: [LAT_ACCEL_FACTOR, MAX_LAT_ACCEL_MEASURED, FRICTION] diff --git a/selfdrive/car/torque_data/substitute.yaml b/selfdrive/car/torque_data/substitute.yaml new file mode 100644 index 000000000..d368b2c67 --- /dev/null +++ b/selfdrive/car/torque_data/substitute.yaml @@ -0,0 +1,75 @@ +MAZDA 3: MAZDA CX-9 2021 +MAZDA 6: MAZDA CX-9 2021 +MAZDA CX-5: MAZDA CX-9 2021 +MAZDA CX-5 2022: MAZDA CX-9 2021 +MAZDA CX-9: MAZDA CX-9 2021 + +TOYOTA ALPHARD HYBRID 2021 : TOYOTA SIENNA 2018 +TOYOTA ALPHARD 2020: TOYOTA SIENNA 2018 +TOYOTA PRIUS v 2017 : TOYOTA PRIUS 2017 +TOYOTA RAV4 2022: TOYOTA RAV4 HYBRID 2022 +TOYOTA C-HR HYBRID 2018: TOYOTA C-HR 2018 +LEXUS IS 2018: LEXUS NX 2018 +LEXUS CT HYBRID 2018 : LEXUS NX 2018 +LEXUS ES HYBRID 2018: TOYOTA CAMRY HYBRID 2018 +LEXUS NX HYBRID 2020: LEXUS NX 2020 +LEXUS RC 2020: LEXUS NX 2020 +TOYOTA AVALON HYBRID 2019: TOYOTA AVALON 2019 +TOYOTA AVALON HYBRID 2022: TOYOTA AVALON 2022 + +KIA OPTIMA SX 2019 & 2016: HYUNDAI SONATA 2020 +KIA OPTIMA HYBRID 2017 & SPORTS 2019: HYUNDAI SONATA 2020 +KIA FORTE E 2018 & GT 2021: HYUNDAI SONATA 2020 +KIA CEED INTRO ED 2019: HYUNDAI SONATA 2020 +KIA SELTOS 2021: HYUNDAI SONATA 2020 +KIA NIRO HYBRID 2019: KIA NIRO EV 2020 +KIA NIRO HYBRID 2021: KIA NIRO EV 2020 +HYUNDAI VELOSTER 2019: HYUNDAI SONATA 2019 +HYUNDAI I30 N LINE 2019 & GT 2018 DCT: HYUNDAI SONATA 2019 +HYUNDAI KONA 2020: HYUNDAI KONA ELECTRIC 2019 +HYUNDAI KONA HYBRID 2020: HYUNDAI KONA ELECTRIC 2019 +HYUNDAI IONIQ HYBRID 2017-2019: HYUNDAI IONIQ PLUG-IN HYBRID 2019 +HYUNDAI IONIQ HYBRID 2020-2022: HYUNDAI IONIQ PLUG-IN HYBRID 2019 +HYUNDAI IONIQ ELECTRIC 2020: HYUNDAI IONIQ PLUG-IN HYBRID 2019 +HYUNDAI ELANTRA 2017: HYUNDAI SONATA 2019 +HYUNDAI ELANTRA HYBRID 2021: HYUNDAI SONATA 2020 +HYUNDAI ELANTRA 2021: HYUNDAI SONATA 2020 +HYUNDAI TUCSON 2019: HYUNDAI SANTA FE 2019 +HYUNDAI SANTA FE 2022: HYUNDAI SANTA FE HYBRID 2022 +GENESIS G90 2017: GENESIS G70 2018 +GENESIS G80 2017: GENESIS G70 2018 +GENESIS G70 2020: HYUNDAI SONATA 2020 + +HONDA FREED 2020: HONDA ODYSSEY 2018 +HONDA CR-V EU 2016: HONDA CR-V 2016 +HONDA CIVIC SEDAN 1.6 DIESEL 2019: HONDA CIVIC (BOSCH) 2019 +HONDA E 2020: HONDA CIVIC (BOSCH) 2019 +HONDA ODYSSEY CHN 2019: HONDA ODYSSEY 2018 + +BUICK REGAL ESSENCE 2018: CHEVROLET VOLT PREMIER 2017 +CADILLAC ESCALADE ESV 2016: CHEVROLET VOLT PREMIER 2017 +CADILLAC ATS Premium Performance 2018: CHEVROLET VOLT PREMIER 2017 +CHEVROLET MALIBU PREMIER 2017: CHEVROLET VOLT PREMIER 2017 +HOLDEN ASTRA RS-V BK 2017: CHEVROLET VOLT PREMIER 2017 + +SKODA OCTAVIA 3RD GEN: SKODA SUPERB 3RD GEN +SKODA SCALA 1ST GEN: SKODA SUPERB 3RD GEN +SKODA KODIAQ 1ST GEN: SKODA SUPERB 3RD GEN +SKODA KAROQ 1ST GEN: SKODA SUPERB 3RD GEN +SKODA KAMIQ 1ST GEN: SKODA SUPERB 3RD GEN +VOLKSWAGEN T-ROC 1ST GEN: VOLKSWAGEN TIGUAN 2ND GEN +VOLKSWAGEN T-CROSS 1ST GEN: VOLKSWAGEN TIGUAN 2ND GEN +VOLKSWAGEN TOURAN 2ND GEN: VOLKSWAGEN TIGUAN 2ND GEN +VOLKSWAGEN TRANSPORTER T6.1: VOLKSWAGEN TIGUAN 2ND GEN +AUDI Q2 1ST GEN: VOLKSWAGEN TIGUAN 2ND GEN +VOLKSWAGEN TAOS 1ST GEN: VOLKSWAGEN TIGUAN 2ND GEN +VOLKSWAGEN POLO 6TH GEN: VOLKSWAGEN GOLF 7TH GEN +SEAT LEON 3RD GEN: VOLKSWAGEN GOLF 7TH GEN +SEAT ATECA 1ST GEN: VOLKSWAGEN GOLF 7TH GEN + +# Old subarus don't have much data guessing it's like low torque impreza +SUBARU OUTBACK 2018 - 2019: SUBARU IMPREZA LIMITED 2019 +SUBARU OUTBACK 2015 - 2017: SUBARU IMPREZA LIMITED 2019 +SUBARU FORESTER 2017 - 2018: SUBARU IMPREZA LIMITED 2019 +SUBARU LEGACY 2015 - 2018: SUBARU IMPREZA LIMITED 2019 +SUBARU ASCENT LIMITED 2019: SUBARU FORESTER 2019 diff --git a/selfdrive/car/toyota/interface.py b/selfdrive/car/toyota/interface.py index fd3bd15cf..337c03956 100644 --- a/selfdrive/car/toyota/interface.py +++ b/selfdrive/car/toyota/interface.py @@ -31,6 +31,8 @@ class CarInterface(CarInterfaceBase): ret.stoppingControl = False # Toyota starts braking more when it thinks you want to stop stop_and_go = False + steering_angle_deadzone_deg = 0.0 + CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning, steering_angle_deadzone_deg) if candidate == CAR.PRIUS: stop_and_go = True @@ -38,8 +40,11 @@ class CarInterface(CarInterfaceBase): ret.steerRatio = 15.74 # unknown end-to-end spec tire_stiffness_factor = 0.6371 # hand-tune ret.mass = 3045. * CV.LB_TO_KG + STD_CARGO_KG - set_lat_tune(ret.lateralTuning, LatTunes.INDI_PRIUS) - ret.steerActuatorDelay = 0.3 + # Only give steer angle deadzone to for bad angle sensor prius + for fw in car_fw: + if fw.ecu == "eps" and not fw.fwVersion == b'8965B47060\x00\x00\x00\x00\x00\x00': + steering_angle_deadzone_deg = 1.0 + CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning, steering_angle_deadzone_deg) elif candidate == CAR.PRIUS_V: stop_and_go = True @@ -47,7 +52,7 @@ class CarInterface(CarInterfaceBase): ret.steerRatio = 17.4 tire_stiffness_factor = 0.5533 ret.mass = 3340. * CV.LB_TO_KG + STD_CARGO_KG - set_lat_tune(ret.lateralTuning, LatTunes.LQR_RAV4) + CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning, steering_angle_deadzone_deg) elif candidate in (CAR.RAV4, CAR.RAV4H): stop_and_go = True if (candidate in CAR.RAV4H) else False @@ -55,14 +60,12 @@ class CarInterface(CarInterfaceBase): ret.steerRatio = 16.88 # 14.5 is spec end-to-end tire_stiffness_factor = 0.5533 ret.mass = 3650. * CV.LB_TO_KG + STD_CARGO_KG # mean between normal and hybrid - set_lat_tune(ret.lateralTuning, LatTunes.LQR_RAV4) elif candidate == CAR.COROLLA: ret.wheelbase = 2.70 ret.steerRatio = 18.27 tire_stiffness_factor = 0.444 # not optimized yet ret.mass = 2860. * CV.LB_TO_KG + STD_CARGO_KG # mean between normal and hybrid - set_lat_tune(ret.lateralTuning, LatTunes.PID_A) elif candidate in (CAR.LEXUS_RX, CAR.LEXUS_RXH, CAR.LEXUS_RX_TSS2, CAR.LEXUS_RXH_TSS2): stop_and_go = True @@ -87,7 +90,8 @@ class CarInterface(CarInterfaceBase): ret.steerRatio = 13.7 tire_stiffness_factor = 0.7933 ret.mass = 3400. * CV.LB_TO_KG + STD_CARGO_KG # mean between normal and hybrid - set_lat_tune(ret.lateralTuning, LatTunes.PID_C) + if candidate not in (CAR.CAMRY_TSS2, CAR.CAMRYH_TSS2): + set_lat_tune(ret.lateralTuning, LatTunes.PID_C) elif candidate in (CAR.HIGHLANDER_TSS2, CAR.HIGHLANDERH_TSS2): stop_and_go = True @@ -136,7 +140,6 @@ class CarInterface(CarInterfaceBase): ret.steerRatio = 13.9 tire_stiffness_factor = 0.444 # not optimized yet ret.mass = 3060. * CV.LB_TO_KG + STD_CARGO_KG - set_lat_tune(ret.lateralTuning, LatTunes.PID_D) elif candidate in (CAR.LEXUS_ES_TSS2, CAR.LEXUS_ESH_TSS2, CAR.LEXUS_ESH): stop_and_go = True @@ -169,7 +172,7 @@ class CarInterface(CarInterfaceBase): ret.mass = 3108 * CV.LB_TO_KG + STD_CARGO_KG # mean between min and max set_lat_tune(ret.lateralTuning, LatTunes.PID_M) - elif candidate in (CAR.LEXUS_NXH, CAR.LEXUS_NX, CAR.LEXUS_NX_TSS2): + elif candidate in (CAR.LEXUS_NX, CAR.LEXUS_NXH, CAR.LEXUS_NX_TSS2, CAR.LEXUS_NXH_TSS2): stop_and_go = True ret.wheelbase = 2.66 ret.steerRatio = 14.7 @@ -201,7 +204,6 @@ class CarInterface(CarInterfaceBase): ret.mass = 4305. * CV.LB_TO_KG + STD_CARGO_KG set_lat_tune(ret.lateralTuning, LatTunes.PID_J) - ret.steerRateCost = 1. ret.centerToFront = ret.wheelbase * 0.44 # TODO: get actual value, for now starting with reasonable value for @@ -272,5 +274,4 @@ class CarInterface(CarInterfaceBase): # pass in a car.CarControl # to be called @ 100hz def apply(self, c): - ret = self.CC.update(c, self.CS) - return ret + return self.CC.update(c, self.CS) diff --git a/selfdrive/car/toyota/tunes.py b/selfdrive/car/toyota/tunes.py index c200e39c0..a8b8758d8 100644 --- a/selfdrive/car/toyota/tunes.py +++ b/selfdrive/car/toyota/tunes.py @@ -1,7 +1,6 @@ #!/usr/bin/env python3 from enum import Enum - class LongTunes(Enum): PEDAL = 0 TSS2 = 1 @@ -24,7 +23,6 @@ class LatTunes(Enum): PID_L = 13 PID_M = 14 PID_N = 15 - TORQUE = 16 ###### LONG ###### @@ -50,35 +48,8 @@ def set_long_tune(tune, name): ###### LAT ###### -def set_lat_tune(tune, name, MAX_LAT_ACCEL=2.5, FRICTION=.1): - if name == LatTunes.TORQUE: - tune.init('torque') - tune.torque.useSteeringAngle = True - tune.torque.kp = 1.0 / MAX_LAT_ACCEL - tune.torque.kf = 1.0 / MAX_LAT_ACCEL - tune.torque.ki = 0.1 / MAX_LAT_ACCEL - tune.torque.friction = FRICTION - elif name == LatTunes.LQR_RAV4: - tune.init('lqr') - tune.lqr.scale = 1500.0 - tune.lqr.ki = 0.05 - tune.lqr.a = [0., 1., -0.22619643, 1.21822268] - tune.lqr.b = [-1.92006585e-04, 3.95603032e-05] - tune.lqr.c = [1., 0.] - tune.lqr.k = [-110.73572306, 451.22718255] - tune.lqr.l = [0.3233671, 0.3185757] - tune.lqr.dcGain = 0.002237852961363602 - elif name == LatTunes.INDI_PRIUS: - tune.init('indi') - tune.indi.innerLoopGainBP = [0.] - tune.indi.innerLoopGainV = [4.0] - tune.indi.outerLoopGainBP = [0.] - tune.indi.outerLoopGainV = [3.0] - tune.indi.timeConstantBP = [0.] - tune.indi.timeConstantV = [1.0] - tune.indi.actuatorEffectivenessBP = [0.] - tune.indi.actuatorEffectivenessV = [1.0] - elif 'PID' in str(name): +def set_lat_tune(tune, name, MAX_LAT_ACCEL=2.5, FRICTION=0.01, steering_angle_deadzone_deg=0.0, use_steering_angle=True): + if 'PID' in str(name): tune.init('pid') tune.pid.kiBP = [0.0] tune.pid.kpBP = [0.0] diff --git a/selfdrive/car/toyota/values.py b/selfdrive/car/toyota/values.py index b1f8c2b9d..63b7240e2 100644 --- a/selfdrive/car/toyota/values.py +++ b/selfdrive/car/toyota/values.py @@ -77,6 +77,7 @@ class CAR: LEXUS_NX = "LEXUS NX 2018" LEXUS_NXH = "LEXUS NX HYBRID 2018" LEXUS_NX_TSS2 = "LEXUS NX 2020" + LEXUS_NXH_TSS2 = "LEXUS NX HYBRID 2020" LEXUS_RC = "LEXUS RC 2020" LEXUS_RX = "LEXUS RX 2016" LEXUS_RXH = "LEXUS RX HYBRID 2017" @@ -92,15 +93,11 @@ class Footnote(Enum): CAMRY = CarFootnote( "28mph for Camry 4CYL L, 4CYL LE and 4CYL SE which don't have Full-Speed Range Dynamic Radar Cruise Control.", Column.FSR_LONGITUDINAL) - ANGLE_SENSOR = CarFootnote( - "An inaccurate steering wheel angle sensor makes precise control difficult.", - Column.STEERING_TORQUE, star=Star.HALF) @dataclass class ToyotaCarInfo(CarInfo): package: str = "All" - good_torque: bool = True harness: Enum = Harness.toyota @@ -122,21 +119,22 @@ CAR_INFO: Dict[str, Union[ToyotaCarInfo, List[ToyotaCarInfo]]] = { CAR.COROLLA: ToyotaCarInfo("Toyota Corolla 2017-19", footnotes=[Footnote.DSU]), CAR.COROLLA_TSS2: [ ToyotaCarInfo("Toyota Corolla 2020-22", video_link="https://www.youtube.com/watch?v=_66pXk0CBYA"), + ToyotaCarInfo("Toyota Corolla Cross 2020-21 (Non-US only)", min_enable_speed=7.5), ToyotaCarInfo("Toyota Corolla Hatchback 2019-22", video_link="https://www.youtube.com/watch?v=_66pXk0CBYA"), ], CAR.COROLLAH_TSS2: [ ToyotaCarInfo("Toyota Corolla Hybrid 2020-22"), - ToyotaCarInfo("Lexus UX Hybrid 2019-21"), + ToyotaCarInfo("Lexus UX Hybrid 2019-22"), ], CAR.HIGHLANDER: ToyotaCarInfo("Toyota Highlander 2017-19", video_link="https://www.youtube.com/watch?v=0wS0wXSLzoo", footnotes=[Footnote.DSU]), CAR.HIGHLANDER_TSS2: ToyotaCarInfo("Toyota Highlander 2020-22"), CAR.HIGHLANDERH: ToyotaCarInfo("Toyota Highlander Hybrid 2017-19", footnotes=[Footnote.DSU]), CAR.HIGHLANDERH_TSS2: ToyotaCarInfo("Toyota Highlander Hybrid 2020-22"), CAR.PRIUS: [ - ToyotaCarInfo("Toyota Prius 2016-20", "TSS-P", video_link="https://www.youtube.com/watch?v=8zopPJI8XQ0", footnotes=[Footnote.DSU, Footnote.ANGLE_SENSOR]), - ToyotaCarInfo("Toyota Prius Prime 2017-20", video_link="https://www.youtube.com/watch?v=8zopPJI8XQ0", footnotes=[Footnote.DSU, Footnote.ANGLE_SENSOR]), + ToyotaCarInfo("Toyota Prius 2016-20", "TSS-P", video_link="https://www.youtube.com/watch?v=8zopPJI8XQ0", footnotes=[Footnote.DSU]), + ToyotaCarInfo("Toyota Prius Prime 2017-20", video_link="https://www.youtube.com/watch?v=8zopPJI8XQ0", footnotes=[Footnote.DSU]), ], - CAR.PRIUS_V: ToyotaCarInfo("Toyota Prius v 2017", "TSS-P", min_enable_speed=MIN_ACC_SPEED, footnotes=[Footnote.DSU, Footnote.ANGLE_SENSOR]), + CAR.PRIUS_V: ToyotaCarInfo("Toyota Prius v 2017", "TSS-P", min_enable_speed=MIN_ACC_SPEED, footnotes=[Footnote.DSU]), CAR.PRIUS_TSS2: [ ToyotaCarInfo("Toyota Prius 2021-22", video_link="https://www.youtube.com/watch?v=J58TvCpUd4U"), ToyotaCarInfo("Toyota Prius Prime 2021-22", video_link="https://www.youtube.com/watch?v=J58TvCpUd4U"), @@ -146,20 +144,21 @@ CAR_INFO: Dict[str, Union[ToyotaCarInfo, List[ToyotaCarInfo]]] = { CAR.RAV4_TSS2: ToyotaCarInfo("Toyota RAV4 2019-21", video_link="https://www.youtube.com/watch?v=wJxjDd42gGA"), CAR.RAV4_TSS2_2022: ToyotaCarInfo("Toyota RAV4 2022"), CAR.RAV4H_TSS2: ToyotaCarInfo("Toyota RAV4 Hybrid 2019-21"), - CAR.RAV4H_TSS2_2022: ToyotaCarInfo("Toyota RAV4 Hybrid 2022"), + CAR.RAV4H_TSS2_2022: ToyotaCarInfo("Toyota RAV4 Hybrid 2022", video_link="https://youtu.be/U0nH9cnrFB0"), CAR.MIRAI: ToyotaCarInfo("Toyota Mirai 2021"), CAR.SIENNA: ToyotaCarInfo("Toyota Sienna 2018-20", video_link="https://www.youtube.com/watch?v=q1UPOo4Sh68", footnotes=[Footnote.DSU]), # Lexus CAR.LEXUS_CTH: ToyotaCarInfo("Lexus CT Hybrid 2017-18", "LSS", footnotes=[Footnote.DSU]), CAR.LEXUS_ESH: ToyotaCarInfo("Lexus ES Hybrid 2017-18", "LSS", footnotes=[Footnote.DSU]), - CAR.LEXUS_ES_TSS2: ToyotaCarInfo("Lexus ES 2019-21"), - CAR.LEXUS_ESH_TSS2: ToyotaCarInfo("Lexus ES Hybrid 2019-22"), + CAR.LEXUS_ES_TSS2: ToyotaCarInfo("Lexus ES 2019-22"), + CAR.LEXUS_ESH_TSS2: ToyotaCarInfo("Lexus ES Hybrid 2019-22", video_link="https://youtu.be/BZ29osRVJeg?t=12"), CAR.LEXUS_IS: ToyotaCarInfo("Lexus IS 2017-19"), CAR.LEXUS_NX: ToyotaCarInfo("Lexus NX 2018-19", footnotes=[Footnote.DSU]), CAR.LEXUS_NXH: ToyotaCarInfo("Lexus NX Hybrid 2018-19", footnotes=[Footnote.DSU]), - CAR.LEXUS_NX_TSS2: ToyotaCarInfo("Lexus NX 2020"), - CAR.LEXUS_RC: ToyotaCarInfo("Lexus RC 2020"), + CAR.LEXUS_NX_TSS2: ToyotaCarInfo("Lexus NX 2020-21"), + CAR.LEXUS_NXH_TSS2: ToyotaCarInfo("Lexus NX Hybrid 2020-21"), + CAR.LEXUS_RC: ToyotaCarInfo("Lexus RC 2017-2020"), CAR.LEXUS_RX: ToyotaCarInfo("Lexus RX 2016-18", footnotes=[Footnote.DSU]), CAR.LEXUS_RXH: ToyotaCarInfo("Lexus RX Hybrid 2016-19", footnotes=[Footnote.DSU]), CAR.LEXUS_RX_TSS2: ToyotaCarInfo("Lexus RX 2020-22"), @@ -559,6 +558,7 @@ FW_VERSIONS = { b'\x033F401100\x00\x00\x00\x00\x00\x00\x00\x00A0202000\x00\x00\x00\x00\x00\x00\x00\x00895231203102\x00\x00\x00\x00', b'\x033F401200\x00\x00\x00\x00\x00\x00\x00\x00A0202000\x00\x00\x00\x00\x00\x00\x00\x00895231203202\x00\x00\x00\x00', b'\x033F424000\x00\x00\x00\x00\x00\x00\x00\x00A0202000\x00\x00\x00\x00\x00\x00\x00\x00895231203202\x00\x00\x00\x00', + b'\x033F424000\x00\x00\x00\x00\x00\x00\x00\x00A0202000\x00\x00\x00\x00\x00\x00\x00\x00895231203302\x00\x00\x00\x00', ], (Ecu.fwdRadar, 0x750, 0xf): [ b'8821F0W01000 ', @@ -675,6 +675,7 @@ FW_VERSIONS = { b'\x01896630ZP1000\x00\x00\x00\x00', b'\x01896630ZP2000\x00\x00\x00\x00', b'\x01896630ZQ5000\x00\x00\x00\x00', + b'\x01896630ZU9000\x00\x00\x00\x00', b'\x018966312L8000\x00\x00\x00\x00', b'\x018966312M0000\x00\x00\x00\x00', b'\x018966312M9000\x00\x00\x00\x00', @@ -815,6 +816,7 @@ FW_VERSIONS = { b'F152676303\x00\x00\x00\x00\x00\x00', b'F152676304\x00\x00\x00\x00\x00\x00', b'F152612D00\x00\x00\x00\x00\x00\x00', + b'F152612842\x00\x00\x00\x00\x00\x00', ], (Ecu.fwdRadar, 0x750, 0xf): [ b'\x018821F3301100\x00\x00\x00\x00', @@ -912,6 +914,7 @@ FW_VERSIONS = { b'\x01F15260E051\x00\x00\x00\x00\x00\x00', b'\x01F15260E061\x00\x00\x00\x00\x00\x00', b'\x01F15260E110\x00\x00\x00\x00\x00\x00', + b'\x01F15260E170\x00\x00\x00\x00\x00\x00', ], (Ecu.engine, 0x700, None): [ b'\x01896630E62100\x00\x00\x00\x00', @@ -931,10 +934,12 @@ FW_VERSIONS = { (Ecu.fwdRadar, 0x750, 0xf): [ b'\x018821F3301400\x00\x00\x00\x00', b'\x018821F6201200\x00\x00\x00\x00', + b'\x018821F6201300\x00\x00\x00\x00', ], (Ecu.fwdCamera, 0x750, 0x6d): [ b'\x028646F0E02100\x00\x00\x00\x008646G2601200\x00\x00\x00\x00', b'\x028646F4803000\x00\x00\x00\x008646G5301200\x00\x00\x00\x00', + b'\x028646F4803000\x00\x00\x00\x008646G3304000\x00\x00\x00\x00', ], }, CAR.HIGHLANDERH_TSS2: { @@ -949,11 +954,13 @@ FW_VERSIONS = { b'\x01F15264873500\x00\x00\x00\x00', b'\x01F152648C6300\x00\x00\x00\x00', b'\x01F152648J4000\x00\x00\x00\x00', + b'\x01F152648J6000\x00\x00\x00\x00', ], (Ecu.engine, 0x700, None): [ + b'\x01896630EE4000\x00\x00\x00\x00', + b'\x01896630EE6000\x00\x00\x00\x00', b'\x01896630E67000\x00\x00\x00\x00', b'\x01896630EA1000\x00\x00\x00\x00', - b'\x01896630EE4000\x00\x00\x00\x00', b'\x01896630EA1000\x00\x00\x00\x00897CF4801001\x00\x00\x00\x00', b'\x02896630E66000\x00\x00\x00\x00897CF4801001\x00\x00\x00\x00', b'\x02896630EB3000\x00\x00\x00\x00897CF4801001\x00\x00\x00\x00', @@ -963,10 +970,12 @@ FW_VERSIONS = { (Ecu.fwdRadar, 0x750, 0xf): [ b'\x018821F3301400\x00\x00\x00\x00', b'\x018821F6201200\x00\x00\x00\x00', + b'\x018821F6201300\x00\x00\x00\x00', ], (Ecu.fwdCamera, 0x750, 0x6d): [ b'\x028646F0E02100\x00\x00\x00\x008646G2601200\x00\x00\x00\x00', b'\x028646F4803000\x00\x00\x00\x008646G5301200\x00\x00\x00\x00', + b'\x028646F4803000\x00\x00\x00\x008646G3304000\x00\x00\x00\x00', ], }, CAR.LEXUS_IS: { @@ -1246,6 +1255,7 @@ FW_VERSIONS = { b'\x01F15260R220\x00\x00\x00\x00\x00\x00', b'\x01F15260R290\x00\x00\x00\x00\x00\x00', b'\x01F15260R300\x00\x00\x00\x00\x00\x00', + b'\x01F15260R302\x00\x00\x00\x00\x00\x00', b'\x01F152642551\x00\x00\x00\x00\x00\x00', b'\x01F152642561\x00\x00\x00\x00\x00\x00', b'\x01F152642700\x00\x00\x00\x00\x00\x00', @@ -1254,6 +1264,7 @@ FW_VERSIONS = { b'\x01F152642711\x00\x00\x00\x00\x00\x00', b'\x01F152642750\x00\x00\x00\x00\x00\x00', b'\x01F152642751\x00\x00\x00\x00\x00\x00', + b'\x01F15260R292\x00\x00\x00\x00\x00\x00', ], (Ecu.eps, 0x7a1, None): [ b'8965B42170\x00\x00\x00\x00\x00\x00', @@ -1288,8 +1299,10 @@ FW_VERSIONS = { b'\x028965B0R01500\x00\x00\x00\x008965B0R02500\x00\x00\x00\x00', ], (Ecu.engine, 0x700, None): [ + b'\x01896634AA0000\x00\x00\x00\x00', b'\x01896634AA1000\x00\x00\x00\x00', b'\x01896634A88000\x00\x00\x00\x00', + b'\x01896634A89000\x00\x00\x00\x00', ], (Ecu.fwdRadar, 0x750, 0xf): [ b'\x018821F0R01100\x00\x00\x00\x00', @@ -1307,6 +1320,7 @@ FW_VERSIONS = { b'\x018966342X6000\x00\x00\x00\x00', b'\x01896634A25000\x00\x00\x00\x00', b'\x018966342W5000\x00\x00\x00\x00', + b'\x018966342W7000\x00\x00\x00\x00', b'\x028966342W4001\x00\x00\x00\x00897CF1203001\x00\x00\x00\x00', b'\x02896634A13000\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', b'\x02896634A13001\x00\x00\x00\x00897CF4801001\x00\x00\x00\x00', @@ -1368,10 +1382,12 @@ FW_VERSIONS = { b'8965B42172\x00\x00\x00\x00\x00\x00', ], (Ecu.engine, 0x700, None): [ - b'\x01896634A62000\x00\x00\x00\x00', + b'\x01896634A02001\x00\x00\x00\x00', + b'\x01896634A03000\x00\x00\x00\x00', b'\x01896634A08000\x00\x00\x00\x00', b'\x01896634A61000\x00\x00\x00\x00', - b'\x01896634A02001\x00\x00\x00\x00', + b'\x01896634A62000\x00\x00\x00\x00', + b'\x01896634A63000\x00\x00\x00\x00', ], (Ecu.fwdRadar, 0x750, 0xf): [ b'\x018821F0R01100\x00\x00\x00\x00', @@ -1577,6 +1593,23 @@ FW_VERSIONS = { b'\x028646F7803100\x00\x00\x00\x008646G2601400\x00\x00\x00\x00', ], }, + CAR.LEXUS_NXH_TSS2: { + (Ecu.engine, 0x7e0, None): [ + b'\x0237887000\x00\x00\x00\x00\x00\x00\x00\x00A4701000\x00\x00\x00\x00\x00\x00\x00\x00', + ], + (Ecu.esp, 0x7b0, None): [ + b'F152678210\x00\x00\x00\x00\x00\x00', + ], + (Ecu.eps, 0x7a1, None): [ + b'8965B78120\x00\x00\x00\x00\x00\x00', + ], + (Ecu.fwdRadar, 0x750, 0xf): [ + b'\x018821F3301400\x00\x00\x00\x00', + ], + (Ecu.fwdCamera, 0x750, 0x6d): [ + b'\x028646F78030A0\x00\x00\x00\x008646G2601200\x00\x00\x00\x00', + ], + }, CAR.LEXUS_NXH: { (Ecu.engine, 0x7e0, None): [ b'\x0237841000\x00\x00\x00\x00\x00\x00\x00\x00A4701000\x00\x00\x00\x00\x00\x00\x00\x00', @@ -1734,13 +1767,14 @@ FW_VERSIONS = { b'\x01896630EB0000\x00\x00\x00\x00', b'\x01896630EC9000\x00\x00\x00\x00', b'\x01896630ED0000\x00\x00\x00\x00', + b'\x01896630ED0100\x00\x00\x00\x00', b'\x01896630ED6000\x00\x00\x00\x00', b'\x018966348W5100\x00\x00\x00\x00', b'\x018966348W9000\x00\x00\x00\x00', b'\x01896634D12000\x00\x00\x00\x00', b'\x01896634D12100\x00\x00\x00\x00', b'\x01896634D43000\x00\x00\x00\x00', - b'\x01896630ED0100\x00\x00\x00\x00', + b'\x01896634D44000\x00\x00\x00\x00', ], (Ecu.esp, 0x7b0, None): [ b'\x01F15260E031\x00\x00\x00\x00\x00\x00', @@ -1767,11 +1801,13 @@ FW_VERSIONS = { CAR.LEXUS_RXH_TSS2: { (Ecu.engine, 0x7e0, None): [ b'\x02348X8000\x00\x00\x00\x00\x00\x00\x00\x00A4802000\x00\x00\x00\x00\x00\x00\x00\x00', + b'\x02348Y3000\x00\x00\x00\x00\x00\x00\x00\x00A4802000\x00\x00\x00\x00\x00\x00\x00\x00', b'\x0234D14000\x00\x00\x00\x00\x00\x00\x00\x00A4802000\x00\x00\x00\x00\x00\x00\x00\x00', b'\x0234D16000\x00\x00\x00\x00\x00\x00\x00\x00A4802000\x00\x00\x00\x00\x00\x00\x00\x00', ], (Ecu.esp, 0x7b0, None): [ b'F152648831\x00\x00\x00\x00\x00\x00', + b'F152648891\x00\x00\x00\x00\x00\x00', b'F152648D00\x00\x00\x00\x00\x00\x00', b'F152648D60\x00\x00\x00\x00\x00\x00', ], @@ -1817,7 +1853,10 @@ FW_VERSIONS = { }, CAR.MIRAI: { (Ecu.esp, 0x7D1, None): [b'\x01898A36203000\x00\x00\x00\x00',], - (Ecu.esp, 0x7B0, None): [b'\x01F15266203200\x00\x00\x00\x00',], # a second ESP ECU + (Ecu.esp, 0x7B0, None): [ # a second ESP ECU + b'\x01F15266203200\x00\x00\x00\x00', + b'\x01F15266203500\x00\x00\x00\x00', + ], (Ecu.eps, 0x7A1, None): [b'\x028965B6204100\x00\x00\x00\x008965B6203100\x00\x00\x00\x00',], (Ecu.fwdRadar, 0x750, 0xf): [b'\x018821F6201200\x00\x00\x00\x00',], (Ecu.fwdCamera, 0x750, 0x6d): [b'\x028646F6201400\x00\x00\x00\x008646G5301200\x00\x00\x00\x00',], @@ -1902,6 +1941,7 @@ DBC = { CAR.LEXUS_NXH: dbc_dict('toyota_tnga_k_pt_generated', 'toyota_adas'), CAR.LEXUS_NX: dbc_dict('toyota_tnga_k_pt_generated', 'toyota_adas'), CAR.LEXUS_NX_TSS2: dbc_dict('toyota_nodsu_pt_generated', 'toyota_tss2_adas'), + CAR.LEXUS_NXH_TSS2: dbc_dict('toyota_nodsu_pt_generated', 'toyota_tss2_adas'), CAR.PRIUS_TSS2: dbc_dict('toyota_nodsu_pt_generated', 'toyota_tss2_adas'), CAR.MIRAI: dbc_dict('toyota_nodsu_pt_generated', 'toyota_tss2_adas'), CAR.ALPHARD_TSS2: dbc_dict('toyota_nodsu_pt_generated', 'toyota_tss2_adas'), @@ -1914,7 +1954,7 @@ EPS_SCALE = defaultdict(lambda: 73, {CAR.PRIUS: 66, CAR.COROLLA: 88, CAR.LEXUS_I # Toyota/Lexus Safety Sense 2.0 and 2.5 TSS2_CAR = {CAR.RAV4_TSS2, CAR.RAV4_TSS2_2022, CAR.COROLLA_TSS2, CAR.COROLLAH_TSS2, CAR.LEXUS_ES_TSS2, CAR.LEXUS_ESH_TSS2, CAR.RAV4H_TSS2, CAR.RAV4H_TSS2_2022, CAR.LEXUS_RX_TSS2, CAR.LEXUS_RXH_TSS2, CAR.HIGHLANDER_TSS2, CAR.HIGHLANDERH_TSS2, CAR.PRIUS_TSS2, CAR.CAMRY_TSS2, CAR.CAMRYH_TSS2, - CAR.MIRAI, CAR.LEXUS_NX_TSS2, CAR.ALPHARD_TSS2, CAR.AVALON_TSS2, CAR.AVALONH_TSS2, CAR.ALPHARDH_TSS2} + CAR.MIRAI, CAR.LEXUS_NX_TSS2, CAR.LEXUS_NXH_TSS2, CAR.ALPHARD_TSS2, CAR.AVALON_TSS2, CAR.AVALONH_TSS2, CAR.ALPHARDH_TSS2} NO_DSU_CAR = TSS2_CAR | {CAR.CHR, CAR.CHRH, CAR.CAMRY, CAR.CAMRYH} @@ -1923,7 +1963,7 @@ RADAR_ACC_CAR = {CAR.RAV4H_TSS2_2022, CAR.RAV4_TSS2_2022} EV_HYBRID_CAR = {CAR.AVALONH_2019, CAR.AVALONH_TSS2, CAR.CAMRYH, CAR.CAMRYH_TSS2, CAR.CHRH, CAR.COROLLAH_TSS2, CAR.HIGHLANDERH, CAR.HIGHLANDERH_TSS2, CAR.PRIUS, CAR.PRIUS_V, CAR.RAV4H, CAR.RAV4H_TSS2, CAR.RAV4H_TSS2_2022, CAR.LEXUS_CTH, CAR.MIRAI, CAR.LEXUS_ESH, CAR.LEXUS_ESH_TSS2, CAR.LEXUS_NXH, CAR.LEXUS_RXH, - CAR.LEXUS_RXH_TSS2, CAR.PRIUS_TSS2, CAR.ALPHARDH_TSS2} + CAR.LEXUS_RXH_TSS2, CAR.LEXUS_NXH_TSS2, CAR.PRIUS_TSS2, CAR.ALPHARDH_TSS2} # no resume button press required NO_STOP_TIMER_CAR = TSS2_CAR | {CAR.PRIUS_V, CAR.RAV4H, CAR.HIGHLANDERH, CAR.HIGHLANDER, CAR.SIENNA, CAR.LEXUS_ESH} diff --git a/selfdrive/car/vin.py b/selfdrive/car/vin.py index 648f41651..007c10e77 100755 --- a/selfdrive/car/vin.py +++ b/selfdrive/car/vin.py @@ -1,27 +1,39 @@ #!/usr/bin/env python3 +import struct import traceback import cereal.messaging as messaging +import panda.python.uds as uds from panda.python.uds import FUNCTIONAL_ADDRS from selfdrive.car.isotp_parallel_query import IsoTpParallelQuery -from selfdrive.swaglog import cloudlog +from system.swaglog import cloudlog + +OBD_VIN_REQUEST = b'\x09\x02' +OBD_VIN_RESPONSE = b'\x49\x02\x01' + +UDS_VIN_REQUEST = bytes([uds.SERVICE_TYPE.READ_DATA_BY_IDENTIFIER]) + struct.pack("!H", uds.DATA_IDENTIFIER_TYPE.VIN) +UDS_VIN_RESPONSE = bytes([uds.SERVICE_TYPE.READ_DATA_BY_IDENTIFIER + 0x40]) + struct.pack("!H", uds.DATA_IDENTIFIER_TYPE.VIN) -VIN_REQUEST = b'\x09\x02' -VIN_RESPONSE = b'\x49\x02\x01' VIN_UNKNOWN = "0" * 17 def get_vin(logcan, sendcan, bus, timeout=0.1, retry=5, debug=False): for i in range(retry): - try: - query = IsoTpParallelQuery(sendcan, logcan, bus, FUNCTIONAL_ADDRS, [VIN_REQUEST], [VIN_RESPONSE], functional_addr=True, debug=debug) - for addr, vin in query.get_data(timeout).items(): - return addr[0], vin.decode() - print(f"vin query retry ({i+1}) ...") - except Exception: - cloudlog.warning(f"VIN query exception: {traceback.format_exc()}") + for request, response in ((UDS_VIN_REQUEST, UDS_VIN_RESPONSE), (OBD_VIN_REQUEST, OBD_VIN_RESPONSE)): + try: + query = IsoTpParallelQuery(sendcan, logcan, bus, FUNCTIONAL_ADDRS, [request, ], [response, ], functional_addr=True, debug=debug) + for (addr, rx_addr), vin in query.get_data(timeout).items(): - return 0, VIN_UNKNOWN + # Honda Bosch response starts with a length, trim to correct length + if vin.startswith(b'\x11'): + vin = vin[1:18] + + return addr[0], rx_addr, vin.decode() + print(f"vin query retry ({i+1}) ...") + except Exception: + cloudlog.warning(f"VIN query exception: {traceback.format_exc()}") + + return 0, 0, VIN_UNKNOWN if __name__ == "__main__": @@ -29,5 +41,5 @@ if __name__ == "__main__": sendcan = messaging.pub_sock('sendcan') logcan = messaging.sub_sock('can') time.sleep(1) - addr, vin = get_vin(logcan, sendcan, 1, debug=False) - print(hex(addr), vin) + addr, vin_rx_addr, vin = get_vin(logcan, sendcan, 1, debug=False) + print(f'TX: {hex(addr)}, RX: {hex(vin_rx_addr)}, VIN: {vin}') diff --git a/selfdrive/car/volkswagen/carcontroller.py b/selfdrive/car/volkswagen/carcontroller.py index aeb56c5d6..1643fbe9b 100644 --- a/selfdrive/car/volkswagen/carcontroller.py +++ b/selfdrive/car/volkswagen/carcontroller.py @@ -1,15 +1,17 @@ from cereal import car +from opendbc.can.packer import CANPacker from selfdrive.car import apply_std_steer_torque_limits from selfdrive.car.volkswagen import volkswagencan from selfdrive.car.volkswagen.values import DBC_FILES, CANBUS, MQB_LDW_MESSAGES, BUTTON_STATES, CarControllerParams as P -from opendbc.can.packer import CANPacker VisualAlert = car.CarControl.HUDControl.VisualAlert -class CarController(): + +class CarController: def __init__(self, dbc_name, CP, VM): - self.apply_steer_last = 0 self.CP = CP + self.apply_steer_last = 0 + self.frame = 0 self.packer_pt = CANPacker(DBC_FILES.mqb) @@ -22,14 +24,15 @@ class CarController(): self.steer_rate_limited = False - def update(self, c, CS, frame, ext_bus, actuators, visual_alert, left_lane_visible, right_lane_visible, left_lane_depart, right_lane_depart): - """ Controls thread """ + def update(self, CC, CS, ext_bus): + actuators = CC.actuators + hud_control = CC.hudControl can_sends = [] # **** Steering Controls ************************************************ # - if frame % P.HCA_STEP == 0: + if self.frame % P.HCA_STEP == 0: # Logic to avoid HCA state 4 "refused": # * Don't steer unless HCA is in state 3 "ready" or 5 "active" # * Don't steer at standstill @@ -40,7 +43,7 @@ class CarController(): # torque value. Do that anytime we happen to have 0 torque, or failing that, # when exceeding ~1/3 the 360 second timer. - if c.latActive: + if CC.latActive: new_steer = int(round(actuators.steer * P.STEER_MAX)) apply_steer = apply_std_steer_torque_limits(new_steer, self.apply_steer_last, CS.out.steeringTorque, P) self.steer_rate_limited = new_steer != apply_steer @@ -66,36 +69,35 @@ class CarController(): apply_steer = 0 self.apply_steer_last = apply_steer - idx = (frame / P.HCA_STEP) % 16 + idx = (self.frame / P.HCA_STEP) % 16 can_sends.append(volkswagencan.create_mqb_steering_control(self.packer_pt, CANBUS.pt, apply_steer, idx, hcaEnabled)) # **** HUD Controls ***************************************************** # - if frame % P.LDW_STEP == 0: - if visual_alert in (VisualAlert.steerRequired, VisualAlert.ldw): + if self.frame % P.LDW_STEP == 0: + if hud_control.visualAlert in (VisualAlert.steerRequired, VisualAlert.ldw): hud_alert = MQB_LDW_MESSAGES["laneAssistTakeOverSilent"] else: hud_alert = MQB_LDW_MESSAGES["none"] - can_sends.append(volkswagencan.create_mqb_hud_control(self.packer_pt, CANBUS.pt, c.enabled, - CS.out.steeringPressed, hud_alert, left_lane_visible, - right_lane_visible, CS.ldw_stock_values, - left_lane_depart, right_lane_depart)) + can_sends.append(volkswagencan.create_mqb_hud_control(self.packer_pt, CANBUS.pt, CC.enabled, + CS.out.steeringPressed, hud_alert, hud_control.leftLaneVisible, + hud_control.rightLaneVisible, CS.ldw_stock_values, + hud_control.leftLaneDepart, hud_control.rightLaneDepart)) # **** ACC Button Controls ********************************************** # # FIXME: this entire section is in desperate need of refactoring if self.CP.pcmCruise: - if frame > self.graMsgStartFramePrev + P.GRA_VBP_STEP: - if c.cruiseControl.cancel: + if self.frame > self.graMsgStartFramePrev + P.GRA_VBP_STEP: + if CC.cruiseControl.cancel: # Cancel ACC if it's engaged with OP disengaged. self.graButtonStatesToSend = BUTTON_STATES.copy() self.graButtonStatesToSend["cancel"] = True - elif c.enabled and CS.esp_hold_confirmation: - # Blip the Resume button if we're engaged at standstill. - # FIXME: This is a naive implementation, improve with visiond or radar input. + elif CC.cruiseControl.resume: + # Send Resume button when planner wants car to move self.graButtonStatesToSend = BUTTON_STATES.copy() self.graButtonStatesToSend["resumeCruise"] = True @@ -103,7 +105,7 @@ class CarController(): self.graMsgBusCounterPrev = CS.graMsgBusCounter if self.graButtonStatesToSend is not None: if self.graMsgSentCount == 0: - self.graMsgStartFramePrev = frame + self.graMsgStartFramePrev = self.frame idx = (CS.graMsgBusCounter + 1) % 16 can_sends.append(volkswagencan.create_mqb_acc_buttons_control(self.packer_pt, ext_bus, self.graButtonStatesToSend, CS, idx)) self.graMsgSentCount += 1 @@ -114,4 +116,5 @@ class CarController(): new_actuators = actuators.copy() new_actuators.steer = self.apply_steer_last / P.STEER_MAX + self.frame += 1 return new_actuators, can_sends diff --git a/selfdrive/car/volkswagen/carstate.py b/selfdrive/car/volkswagen/carstate.py index 4193030d8..9c4a111e4 100644 --- a/selfdrive/car/volkswagen/carstate.py +++ b/selfdrive/car/volkswagen/carstate.py @@ -50,7 +50,6 @@ class CarState(CarStateBase): ret.brake = pt_cp.vl["ESP_05"]["ESP_Bremsdruck"] / 250.0 # FIXME: this is pressure in Bar, not sure what OP expects ret.brakePressed = bool(pt_cp.vl["ESP_05"]["ESP_Fahrer_bremst"]) ret.parkingBrake = bool(pt_cp.vl["Kombi_01"]["KBI_Handbremse"]) # FIXME: need to include an EPB check as well - self.esp_hold_confirmation = pt_cp.vl["ESP_21"]["ESP_Haltebestaetigung"] # Update gear and/or clutch position data. if trans_type == TransmissionType.automatic: @@ -105,6 +104,7 @@ class CarState(CarStateBase): # ACC okay but disabled (1), or a radar visibility or other fault/disruption (6 or 7) ret.cruiseState.available = False ret.cruiseState.enabled = False + ret.cruiseState.standstill = bool(pt_cp.vl["ESP_21"]["ESP_Haltebestaetigung"]) ret.accFaulted = pt_cp.vl["TSK_06"]["TSK_Status"] in (6, 7) # Update ACC setpoint. When the setpoint is zero or there's an error, the diff --git a/selfdrive/car/volkswagen/interface.py b/selfdrive/car/volkswagen/interface.py index 3a0d7c8ce..c2b077b6d 100644 --- a/selfdrive/car/volkswagen/interface.py +++ b/selfdrive/car/volkswagen/interface.py @@ -45,7 +45,6 @@ class CarInterface(CarInterfaceBase): # Global lateral tuning defaults, can be overridden per-vehicle ret.steerActuatorDelay = 0.1 - ret.steerRateCost = 1.0 ret.steerLimitTimer = 0.4 ret.steerRatio = 15.6 # Let the params learner figure this out tire_stiffness_factor = 1.0 # Let the params learner figure this out @@ -194,12 +193,4 @@ class CarInterface(CarInterfaceBase): return ret def apply(self, c): - hud_control = c.hudControl - ret = self.CC.update(c, self.CS, self.frame, self.ext_bus, c.actuators, - hud_control.visualAlert, - hud_control.leftLaneVisible, - hud_control.rightLaneVisible, - hud_control.leftLaneDepart, - hud_control.rightLaneDepart) - self.frame += 1 - return ret + return self.CC.update(c, self.CS, self.ext_bus) diff --git a/selfdrive/car/volkswagen/values.py b/selfdrive/car/volkswagen/values.py index 1dc431eb7..6e64f705b 100755 --- a/selfdrive/car/volkswagen/values.py +++ b/selfdrive/car/volkswagen/values.py @@ -114,7 +114,6 @@ class Footnote(Enum): @dataclass class VWCarInfo(CarInfo): package: str = "Driver Assistance" - good_torque: bool = True harness: Enum = Harness.vw @@ -135,7 +134,7 @@ CAR_INFO: Dict[str, Union[VWCarInfo, List[VWCarInfo]]] = { VWCarInfo("Volkswagen Jetta 2018-21"), VWCarInfo("Volkswagen Jetta GLI 2021"), ], - CAR.PASSAT_MK8: VWCarInfo("Volkswagen Passat 2016-18", footnotes=[Footnote.PASSAT]), + CAR.PASSAT_MK8: VWCarInfo("Volkswagen Passat 2015-19", footnotes=[Footnote.PASSAT]), CAR.POLO_MK6: VWCarInfo("Volkswagen Polo 2020"), CAR.TAOS_MK1: VWCarInfo("Volkswagen Taos 2022", footnotes=[Footnote.VW_HARNESS], harness=Harness.j533), CAR.TCROSS_MK1: VWCarInfo("Volkswagen T-Cross 2021", footnotes=[Footnote.VW_HARNESS], harness=Harness.j533), @@ -438,32 +437,45 @@ FW_VERSIONS = { }, CAR.PASSAT_MK8: { (Ecu.engine, 0x7e0, None): [ + b'\xf1\x8703N906026E \xf1\x892114', b'\xf1\x8704E906023AH\xf1\x893379', + b'\xf1\x8704L906026ET\xf1\x891990', b'\xf1\x8704L906026GA\xf1\x892013', b'\xf1\x8704L906026KD\xf1\x894798', b'\xf1\x873G0906264 \xf1\x890004', ], (Ecu.transmission, 0x7e1, None): [ + b'\xf1\x870CW300043H \xf1\x891601', b'\xf1\x870CW300048R \xf1\x890610', b'\xf1\x870D9300014L \xf1\x895002', + b'\xf1\x870D9300041A \xf1\x894801', b'\xf1\x870DD300045T \xf1\x891601', + b'\xf1\x870DL300011H \xf1\x895201', b'\xf1\x870GC300042H \xf1\x891404', ], (Ecu.srs, 0x715, None): [ + b'\xf1\x873Q0959655AE\xf1\x890195\xf1\x82\r56140056130012416612124111', b'\xf1\x873Q0959655AN\xf1\x890306\xf1\x82\r58160058140013036914110311', + b'\xf1\x873Q0959655BA\xf1\x890195\xf1\x82\r56140056130012516612125111', b'\xf1\x873Q0959655BB\xf1\x890195\xf1\x82\r56140056130012026612120211', b'\xf1\x873Q0959655BK\xf1\x890703\xf1\x82\0165915005914001344701311442900', + b'\xf1\x873Q0959655CN\xf1\x890720\xf1\x82\x0e5915005914001305701311052900', b'\xf1\x875Q0959655S \xf1\x890870\xf1\x82\02315120011111200631145171716121691132111', ], (Ecu.eps, 0x712, None): [ + b'\xf1\x873Q0909144J \xf1\x895063\xf1\x82\x0566B00611A1', + b'\xf1\x875Q0909143M \xf1\x892041\xf1\x820522B0060803', b'\xf1\x875Q0909143M \xf1\x892041\xf1\x820522B0080803', b'\xf1\x875Q0909144AB\xf1\x891082\xf1\x82\00521B00606A1', b'\xf1\x875Q0909144S \xf1\x891063\xf1\x82\00516B00501A1', b'\xf1\x875Q0909144T \xf1\x891072\xf1\x82\00521B00703A1', + b'\xf1\x875Q0910143C \xf1\x892211\xf1\x82\x0567B0020600', ], (Ecu.fwdRadar, 0x757, None): [ + b'\xf1\x873Q0907572A \xf1\x890130', b'\xf1\x873Q0907572B \xf1\x890192', b'\xf1\x873Q0907572C \xf1\x890195', + b'\xf1\x873Q0907572C \xf1\x890196', b'\xf1\x875Q0907572R \xf1\x890771', ], }, @@ -524,6 +536,7 @@ FW_VERSIONS = { }, CAR.TIGUAN_MK2: { (Ecu.engine, 0x7e0, None): [ + b'\xf1\x8704E906027NB\xf1\x899504', b'\xf1\x8704L906026EJ\xf1\x893661', b'\xf1\x8704L906027G \xf1\x899893', b'\xf1\x875N0906259 \xf1\x890002', @@ -535,6 +548,7 @@ FW_VERSIONS = { b'\xf1\x8709G927158DT\xf1\x893698', b'\xf1\x8709G927158GC\xf1\x893821', b'\xf1\x8709G927158GD\xf1\x893820', + b'\xf1\x870D9300043 \xf1\x895202', b'\xf1\x870DL300011N \xf1\x892001', b'\xf1\x870DL300011N \xf1\x892012', b'\xf1\x870DL300013A \xf1\x893005', @@ -546,11 +560,14 @@ FW_VERSIONS = { b'\xf1\x875Q0959655BM\xf1\x890403\xf1\x82\02316143231313500314641011750179333423100', b'\xf1\x875Q0959655BT\xf1\x890403\xf1\x82\02312110031333300314240583752379333423100', b'\xf1\x875Q0959655BT\xf1\x890403\xf1\x82\02331310031333336313140013950399333423100', + b'\xf1\x875Q0959655BT\xf1\x890403\xf1\x82\x1331310031333334313140013750379333423100', b'\xf1\x875Q0959655BT\xf1\x890403\xf1\x82\x1331310031333334313140573752379333423100', b'\xf1\x875Q0959655CB\xf1\x890421\xf1\x82\x1316143231313500314647021750179333613100', ], (Ecu.eps, 0x712, None): [ b'\xf1\x875Q0909143M \xf1\x892041\xf1\x820529A6060603', + b'\xf1\x875Q0909144AB\xf1\x891082\xf1\x82\x0521A60604A1', + b'\xf1\x875Q0910143C \xf1\x892211\xf1\x82\x0567A6000600', b'\xf1\x875QF909144B \xf1\x895582\xf1\x82\00571A60634A1', b'\xf1\x875QM909144B \xf1\x891081\xf1\x82\x0521A60604A1', b'\xf1\x875QM909144C \xf1\x891082\xf1\x82\x0521A60604A1', diff --git a/selfdrive/controls/controlsd.py b/selfdrive/controls/controlsd.py index 5cb6874ca..a20a3a9f3 100755 --- a/selfdrive/controls/controlsd.py +++ b/selfdrive/controls/controlsd.py @@ -11,7 +11,8 @@ from common.params import Params, put_nonblocking import cereal.messaging as messaging from common.conversions import Conversions as CV from panda import ALTERNATIVE_EXPERIENCE -from selfdrive.swaglog import cloudlog +from system.swaglog import cloudlog +from system.version import get_short_branch from selfdrive.boardd.boardd import can_list_to_can_capnp from selfdrive.car.car_helpers import get_car, get_startup_event, get_one_can from selfdrive.controls.lib.lane_planner import CAMERA_OFFSET @@ -23,12 +24,11 @@ from selfdrive.controls.lib.latcontrol_pid import LatControlPID from selfdrive.controls.lib.latcontrol_indi import LatControlINDI from selfdrive.controls.lib.latcontrol_angle import LatControlAngle from selfdrive.controls.lib.latcontrol_torque import LatControlTorque -from selfdrive.controls.lib.latcontrol_lqr import LatControlLQR from selfdrive.controls.lib.events import Events, ET from selfdrive.controls.lib.alertmanager import AlertManager, set_offroad_alert from selfdrive.controls.lib.vehicle_model import VehicleModel from selfdrive.locationd.calibrationd import Calibration -from selfdrive.hardware import HARDWARE, TICI +from system.hardware import HARDWARE from selfdrive.manager.process_config import managed_processes SOFT_DISABLE_TIME = 3 # seconds @@ -39,7 +39,7 @@ REPLAY = "REPLAY" in os.environ SIMULATION = "SIMULATION" in os.environ NOSENSOR = "NOSENSOR" in os.environ IGNORE_PROCESSES = {"uploader", "deleter", "loggerd", "logmessaged", "tombstoned", "statsd", - "logcatd", "proclogd", "clocksd", "updated", "timezoned", "manage_athenad"} | \ + "logcatd", "proclogd", "clocksd", "updated", "timezoned", "manage_athenad", "laikad"} | \ {k for k, v in managed_processes.items() if not v.enabled} ThermalStatus = log.DeviceState.ThermalStatus @@ -63,23 +63,23 @@ class Controls: def __init__(self, sm=None, pm=None, can_sock=None, CI=None): config_realtime_process(4, Priority.CTRL_HIGH) + # Ensure the current branch is cached, otherwise the first iteration of controlsd lags + self.branch = get_short_branch("") + # Setup sockets self.pm = pm if self.pm is None: self.pm = messaging.PubMaster(['sendcan', 'controlsState', 'carState', 'carControl', 'carEvents', 'carParams']) - self.camera_packets = ["roadCameraState", "driverCameraState"] - if TICI: - self.camera_packets.append("wideRoadCameraState") + self.camera_packets = ["roadCameraState", "driverCameraState", "wideRoadCameraState"] self.can_sock = can_sock if can_sock is None: can_timeout = None if os.environ.get('NO_CAN_TIMEOUT', False) else 20 self.can_sock = messaging.sub_sock('can', timeout=can_timeout) - if TICI: - self.log_sock = messaging.sub_sock('androidLog') + self.log_sock = messaging.sub_sock('androidLog') if CI is None: # wait for one pandaState and one CAN packet @@ -96,7 +96,11 @@ class Controls: self.sm = sm if self.sm is None: - ignore = ['driverCameraState', 'managerState'] if SIMULATION else None + ignore = [] + if SIMULATION: + ignore += ['driverCameraState', 'managerState'] + if params.get_bool('WideCameraOnly'): + ignore += ['roadCameraState'] self.sm = messaging.SubMaster(['deviceState', 'pandaStates', 'peripheralState', 'modelV2', 'liveCalibration', 'driverMonitoringState', 'longitudinalPlan', 'lateralPlan', 'liveLocationKalman', 'managerState', 'liveParameters', 'radarState'] + self.camera_packets + joystick_packet, @@ -108,6 +112,9 @@ class Controls: if not self.disengage_on_accelerator: self.CP.alternativeExperience |= ALTERNATIVE_EXPERIENCE.DISABLE_DISENGAGE_ON_GAS + if self.CP.dashcamOnly and params.get_bool("DashcamOverride"): + self.CP.dashcamOnly = False + # read params self.is_metric = params.get_bool("IsMetric") self.is_ldw_enabled = params.get_bool("IsLdwEnabled") @@ -146,8 +153,6 @@ class Controls: self.LaC = LatControlPID(self.CP, self.CI) elif self.CP.lateralTuning.which() == 'indi': self.LaC = LatControlINDI(self.CP, self.CI) - elif self.CP.lateralTuning.which() == 'lqr': - self.LaC = LatControlLQR(self.CP, self.CI) elif self.CP.lateralTuning.which() == 'torque': self.LaC = LatControlTorque(self.CP, self.CI) @@ -223,12 +228,8 @@ class Controls: if not self.CP.notCar: self.events.add_from_msg(self.sm['driverMonitoringState'].events) - # Handle car events. Ignore when CAN is invalid - if CS.canTimeout: - self.events.add(EventName.canBusMissing) - elif not CS.canValid: - self.events.add(EventName.canError) - else: + # Add car events, ignore if CAN isn't valid + if CS.canValid: self.events.add_from_msg(CS.events) # Create events for temperature, disk space, and memory @@ -308,19 +309,24 @@ class Controls: self.events.add(EventName.cameraFrameRate) if self.rk.lagging: self.events.add(EventName.controlsdLagging) - if len(self.sm['radarState'].radarErrors): + if len(self.sm['radarState'].radarErrors) or not self.sm.all_checks(['radarState']): self.events.add(EventName.radarFault) if not self.sm.valid['pandaStates']: self.events.add(EventName.usbError) + if CS.canTimeout: + self.events.add(EventName.canBusMissing) + elif not CS.canValid: + self.events.add(EventName.canError) # generic catch-all. ideally, a more specific event should be added above instead - no_system_errors = len(self.events) != num_events - if (not self.sm.all_checks() or self.can_rcv_error) and no_system_errors and CS.canValid and not CS.canTimeout: + has_disable_events = self.events.any(ET.NO_ENTRY) and (self.events.any(ET.SOFT_DISABLE) or self.events.any(ET.IMMEDIATE_DISABLE)) + no_system_errors = (not has_disable_events) or (len(self.events) == num_events) + if (not self.sm.all_checks() or self.can_rcv_error) and no_system_errors: if not self.sm.all_alive(): self.events.add(EventName.commIssue) elif not self.sm.all_freq_ok(): self.events.add(EventName.commIssueAvgFreq) - else: # invalid or can_rcv_error. + else: # invalid or can_rcv_error. self.events.add(EventName.commIssue) logs = { @@ -361,17 +367,16 @@ class Controls: if planner_fcw or model_fcw: self.events.add(EventName.fcw) - if TICI: - for m in messaging.drain_sock(self.log_sock, wait_for_one=False): - try: - msg = m.androidLog.message - if any(err in msg for err in ("ERROR_CRC", "ERROR_ECC", "ERROR_STREAM_UNDERFLOW", "APPLY FAILED")): - csid = msg.split("CSID:")[-1].split(" ")[0] - evt = CSID_MAP.get(csid, None) - if evt is not None: - self.events.add(evt) - except UnicodeDecodeError: - pass + for m in messaging.drain_sock(self.log_sock, wait_for_one=False): + try: + msg = m.androidLog.message + if any(err in msg for err in ("ERROR_CRC", "ERROR_ECC", "ERROR_STREAM_UNDERFLOW", "APPLY FAILED")): + csid = msg.split("CSID:")[-1].split(" ")[0] + evt = CSID_MAP.get(csid, None) + if evt is not None: + self.events.add(evt) + except UnicodeDecodeError: + pass # TODO: fix simulator if not SIMULATION: @@ -406,7 +411,8 @@ class Controls: if not self.initialized: all_valid = CS.canValid and self.sm.all_checks() - if all_valid or self.sm.frame * DT_CTRL > 3.5 or SIMULATION: + timed_out = self.sm.frame * DT_CTRL > (6. if REPLAY else 3.5) + if all_valid or timed_out or SIMULATION: if not self.read_only: self.CI.init(self.CP, self.can_sock, self.pm.sock['sendcan']) self.initialized = True @@ -602,7 +608,14 @@ class Controls: lac_log.saturated = abs(actuators.steer) >= 0.9 # Send a "steering required alert" if saturation count has reached the limit - if lac_log.active and lac_log.saturated and not CS.steeringPressed: + if lac_log.active and not CS.steeringPressed and self.CP.lateralTuning.which() == 'torque' and not self.joystick_mode: + undershooting = abs(lac_log.desiredLateralAccel) / abs(1e-3 + lac_log.actualLateralAccel) > 1.2 + turning = abs(lac_log.desiredLateralAccel) > 1.0 + good_speed = CS.vEgo > 5 + max_torque = abs(self.last_actuators.steer) > 0.99 + if undershooting and turning and good_speed and max_torque: + self.events.add(EventName.steerSaturated) + elif lac_log.active and not CS.steeringPressed and lac_log.saturated: dpath_points = lat_plan.dPathPoints if len(dpath_points): # Check if we deviated from the path @@ -651,6 +664,10 @@ class Controls: if self.joystick_mode and self.sm.rcv_frame['testJoystick'] > 0 and self.sm['testJoystick'].buttons[0]: CC.cruiseControl.cancel = True + speeds = self.sm['longitudinalPlan'].speeds + if len(speeds): + CC.cruiseControl.resume = self.enabled and CS.cruiseState.standstill and speeds[-1] > 0.1 + hudControl = CC.hudControl hudControl.setSpeed = float(self.v_cruise_kph * CV.KPH_TO_MS) hudControl.speedVisible = self.enabled @@ -752,8 +769,6 @@ class Controls: controlsState.lateralControlState.pidState = lac_log elif lat_tuning == 'torque': controlsState.lateralControlState.torqueState = lac_log - elif lat_tuning == 'lqr': - controlsState.lateralControlState.lqrState = lac_log elif lat_tuning == 'indi': controlsState.lateralControlState.indiState = lac_log diff --git a/selfdrive/controls/lib/drive_helpers.py b/selfdrive/controls/lib/drive_helpers.py index 4afa8d89e..d79f94bbf 100644 --- a/selfdrive/controls/lib/drive_helpers.py +++ b/selfdrive/controls/lib/drive_helpers.py @@ -38,6 +38,16 @@ class MPC_COST_LAT: STEER_RATE = 1.0 +def apply_deadzone(error, deadzone): + if error > deadzone: + error -= deadzone + elif error < - deadzone: + error += deadzone + else: + error = 0. + return error + + def rate_limit(new_value, last_value, dw_step, up_step): return clip(new_value, last_value + dw_step, last_value + up_step) @@ -97,26 +107,27 @@ def get_lag_adjusted_curvature(CP, v_ego, psis, curvatures, curvature_rates): psis = [0.0]*CONTROL_N curvatures = [0.0]*CONTROL_N curvature_rates = [0.0]*CONTROL_N + v_ego = max(v_ego, 0.1) # TODO this needs more thought, use .2s extra for now to estimate other delays delay = CP.steerActuatorDelay + .2 - current_curvature = curvatures[0] - psi = interp(delay, T_IDXS[:CONTROL_N], psis) - desired_curvature_rate = curvature_rates[0] # MPC can plan to turn the wheel and turn back before t_delay. This means # in high delay cases some corrections never even get commanded. So just use # psi to calculate a simple linearization of desired curvature - curvature_diff_from_psi = psi / (max(v_ego, 1e-1) * delay) - current_curvature - desired_curvature = current_curvature + 2 * curvature_diff_from_psi + current_curvature_desired = curvatures[0] + psi = interp(delay, T_IDXS[:CONTROL_N], psis) + average_curvature_desired = psi / (v_ego * delay) + desired_curvature = 2 * average_curvature_desired - current_curvature_desired - v_ego = max(v_ego, 0.1) - max_curvature_rate = MAX_LATERAL_JERK / (v_ego**2) + # This is the "desired rate of the setpoint" not an actual desired rate + desired_curvature_rate = curvature_rates[0] + max_curvature_rate = MAX_LATERAL_JERK / (v_ego**2) # inexact calculation, check https://github.com/commaai/openpilot/pull/24755 safe_desired_curvature_rate = clip(desired_curvature_rate, -max_curvature_rate, max_curvature_rate) safe_desired_curvature = clip(desired_curvature, - current_curvature - max_curvature_rate * DT_MDL, - current_curvature + max_curvature_rate * DT_MDL) + current_curvature_desired - max_curvature_rate * DT_MDL, + current_curvature_desired + max_curvature_rate * DT_MDL) return safe_desired_curvature, safe_desired_curvature_rate diff --git a/selfdrive/controls/lib/events.py b/selfdrive/controls/lib/events.py index 8a37f11fd..95ccb7b7e 100644 --- a/selfdrive/controls/lib/events.py +++ b/selfdrive/controls/lib/events.py @@ -8,7 +8,7 @@ import cereal.messaging as messaging from common.conversions import Conversions as CV from common.realtime import DT_CTRL from selfdrive.locationd.calibrationd import MIN_SPEED_FILTER -from selfdrive.version import get_short_branch +from system.version import get_short_branch AlertSize = log.ControlsState.AlertSize AlertStatus = log.ControlsState.AlertStatus @@ -222,7 +222,7 @@ def user_soft_disable_alert(alert_text_2: str) -> AlertCallbackType: return func def startup_master_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int) -> Alert: - branch = get_short_branch("") + branch = get_short_branch("") # Ensure get_short_branch is cached to avoid lags on startup if "REPLAY" in os.environ: branch = "replay" @@ -284,7 +284,7 @@ def comm_issue_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaste def camera_malfunction_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int) -> Alert: all_cams = ('roadCameraState', 'driverCameraState', 'wideRoadCameraState') - bad_cams = [s for s in all_cams if s in sm.data.keys() and not sm.all_checks([s, ])] + bad_cams = [s.replace('State', '') for s in all_cams if s in sm.data.keys() and not sm.all_checks([s, ])] return NormalPermanentAlert("Camera Malfunction", ', '.join(bad_cams)) diff --git a/selfdrive/controls/lib/lane_planner.py b/selfdrive/controls/lib/lane_planner.py index aedf61a07..1facb66d6 100644 --- a/selfdrive/controls/lib/lane_planner.py +++ b/selfdrive/controls/lib/lane_planner.py @@ -3,20 +3,14 @@ from cereal import log from common.filter_simple import FirstOrderFilter from common.numpy_fast import interp from common.realtime import DT_MDL -from selfdrive.hardware import TICI -from selfdrive.swaglog import cloudlog +from system.swaglog import cloudlog TRAJECTORY_SIZE = 33 # camera offset is meters from center car to camera -# model path is in the frame of the camera. Empirically -# the model knows the difference between TICI and EON -# so a path offset is not needed +# model path is in the frame of the camera PATH_OFFSET = 0.00 -if TICI: - CAMERA_OFFSET = 0.04 -else: - CAMERA_OFFSET = 0.0 +CAMERA_OFFSET = 0.04 class LanePlanner: diff --git a/selfdrive/controls/lib/latcontrol_indi.py b/selfdrive/controls/lib/latcontrol_indi.py index b5041eb17..79c881d11 100644 --- a/selfdrive/controls/lib/latcontrol_indi.py +++ b/selfdrive/controls/lib/latcontrol_indi.py @@ -78,6 +78,7 @@ class LatControlINDI(LatControl): steers_des += math.radians(params.angleOffsetDeg) indi_log.steeringAngleDesiredDeg = math.degrees(steers_des) + # desired rate is the desired rate of change in the setpoint, not the absolute desired curvature rate_des = VM.get_steer_from_curvature(-desired_curvature_rate, CS.vEgo, 0) indi_log.steeringRateDesiredDeg = math.degrees(rate_des) diff --git a/selfdrive/controls/lib/latcontrol_lqr.py b/selfdrive/controls/lib/latcontrol_lqr.py deleted file mode 100644 index 5b4f65a03..000000000 --- a/selfdrive/controls/lib/latcontrol_lqr.py +++ /dev/null @@ -1,84 +0,0 @@ -import math -import numpy as np - -from common.numpy_fast import clip -from common.realtime import DT_CTRL -from cereal import log -from selfdrive.controls.lib.latcontrol import LatControl, MIN_STEER_SPEED - - -class LatControlLQR(LatControl): - def __init__(self, CP, CI): - super().__init__(CP, CI) - self.scale = CP.lateralTuning.lqr.scale - self.ki = CP.lateralTuning.lqr.ki - - self.A = np.array(CP.lateralTuning.lqr.a).reshape((2, 2)) - self.B = np.array(CP.lateralTuning.lqr.b).reshape((2, 1)) - self.C = np.array(CP.lateralTuning.lqr.c).reshape((1, 2)) - self.K = np.array(CP.lateralTuning.lqr.k).reshape((1, 2)) - self.L = np.array(CP.lateralTuning.lqr.l).reshape((2, 1)) - self.dc_gain = CP.lateralTuning.lqr.dcGain - - self.x_hat = np.array([[0], [0]]) - self.i_unwind_rate = 0.3 * DT_CTRL - self.i_rate = 1.0 * DT_CTRL - - self.reset() - - def reset(self): - super().reset() - self.i_lqr = 0.0 - - def update(self, active, CS, VM, params, last_actuators, desired_curvature, desired_curvature_rate, llk): - lqr_log = log.ControlsState.LateralLQRState.new_message() - - torque_scale = (0.45 + CS.vEgo / 60.0)**2 # Scale actuator model with speed - - # Subtract offset. Zero angle should correspond to zero torque - steering_angle_no_offset = CS.steeringAngleDeg - params.angleOffsetAverageDeg - - desired_angle = math.degrees(VM.get_steer_from_curvature(-desired_curvature, CS.vEgo, params.roll)) - - instant_offset = params.angleOffsetDeg - params.angleOffsetAverageDeg - desired_angle += instant_offset # Only add offset that originates from vehicle model errors - lqr_log.steeringAngleDesiredDeg = desired_angle - - # Update Kalman filter - angle_steers_k = float(self.C.dot(self.x_hat)) - e = steering_angle_no_offset - angle_steers_k - self.x_hat = self.A.dot(self.x_hat) + self.B.dot(CS.steeringTorqueEps / torque_scale) + self.L.dot(e) - - if CS.vEgo < MIN_STEER_SPEED or not active: - lqr_log.active = False - lqr_output = 0. - output_steer = 0. - self.reset() - else: - lqr_log.active = True - - # LQR - u_lqr = float(desired_angle / self.dc_gain - self.K.dot(self.x_hat)) - lqr_output = torque_scale * u_lqr / self.scale - - # Integrator - if CS.steeringPressed: - self.i_lqr -= self.i_unwind_rate * float(np.sign(self.i_lqr)) - else: - error = desired_angle - angle_steers_k - i = self.i_lqr + self.ki * self.i_rate * error - control = lqr_output + i - - if (error >= 0 and (control <= self.steer_max or i < 0.0)) or \ - (error <= 0 and (control >= -self.steer_max or i > 0.0)): - self.i_lqr = i - - output_steer = lqr_output + self.i_lqr - output_steer = clip(output_steer, -self.steer_max, self.steer_max) - - lqr_log.steeringAngleDeg = angle_steers_k - lqr_log.i = self.i_lqr - lqr_log.output = output_steer - lqr_log.lqrOutput = lqr_output - lqr_log.saturated = self._check_saturation(self.steer_max - abs(output_steer) < 1e-3, CS) - return output_steer, desired_angle, lqr_log diff --git a/selfdrive/controls/lib/latcontrol_torque.py b/selfdrive/controls/lib/latcontrol_torque.py index e3dbe373b..46caa41a9 100644 --- a/selfdrive/controls/lib/latcontrol_torque.py +++ b/selfdrive/controls/lib/latcontrol_torque.py @@ -4,6 +4,7 @@ from cereal import log from common.numpy_fast import interp from selfdrive.controls.lib.latcontrol import LatControl, MIN_STEER_SPEED from selfdrive.controls.lib.pid import PIDController +from selfdrive.controls.lib.drive_helpers import apply_deadzone from selfdrive.controls.lib.vehicle_model import ACCELERATION_DUE_TO_GRAVITY # At higher speeds (25+mph) we can assume: @@ -18,8 +19,7 @@ from selfdrive.controls.lib.vehicle_model import ACCELERATION_DUE_TO_GRAVITY # move it at all, this is compensated for too. -LOW_SPEED_FACTOR = 200 -JERK_THRESHOLD = 0.2 +FRICTION_THRESHOLD = 0.2 class LatControlTorque(LatControl): @@ -31,10 +31,7 @@ class LatControlTorque(LatControl): self.use_steering_angle = CP.lateralTuning.torque.useSteeringAngle self.friction = CP.lateralTuning.torque.friction self.kf = CP.lateralTuning.torque.kf - - def reset(self): - super().reset() - self.pid.reset() + self.steering_angle_deadzone_deg = CP.lateralTuning.torque.steeringAngleDeadzoneDeg def update(self, active, CS, VM, params, last_actuators, desired_curvature, desired_curvature_rate, llk): pid_log = log.ControlsState.LateralTorqueState.new_message() @@ -45,20 +42,29 @@ class LatControlTorque(LatControl): else: if self.use_steering_angle: actual_curvature = -VM.calc_curvature(math.radians(CS.steeringAngleDeg - params.angleOffsetDeg), CS.vEgo, params.roll) + curvature_deadzone = abs(VM.calc_curvature(math.radians(self.steering_angle_deadzone_deg), CS.vEgo, 0.0)) else: - actual_curvature = llk.angularVelocityCalibrated.value[2] / CS.vEgo + actual_curvature_vm = -VM.calc_curvature(math.radians(CS.steeringAngleDeg - params.angleOffsetDeg), CS.vEgo, params.roll) + actual_curvature_llk = llk.angularVelocityCalibrated.value[2] / CS.vEgo + actual_curvature = interp(CS.vEgo, [2.0, 5.0], [actual_curvature_vm, actual_curvature_llk]) + curvature_deadzone = 0.0 desired_lateral_accel = desired_curvature * CS.vEgo ** 2 - desired_lateral_jerk = desired_curvature_rate * CS.vEgo ** 2 - actual_lateral_accel = actual_curvature * CS.vEgo ** 2 - setpoint = desired_lateral_accel + LOW_SPEED_FACTOR * desired_curvature - measurement = actual_lateral_accel + LOW_SPEED_FACTOR * actual_curvature + # desired rate is the desired rate of change in the setpoint, not the absolute desired curvature + #desired_lateral_jerk = desired_curvature_rate * CS.vEgo ** 2 + actual_lateral_accel = actual_curvature * CS.vEgo ** 2 + lateral_accel_deadzone = curvature_deadzone * CS.vEgo ** 2 + + + low_speed_factor = interp(CS.vEgo, [0, 10, 20], [500, 500, 200]) + setpoint = desired_lateral_accel + low_speed_factor * desired_curvature + measurement = actual_lateral_accel + low_speed_factor * actual_curvature error = setpoint - measurement pid_log.error = error ff = desired_lateral_accel - params.roll * ACCELERATION_DUE_TO_GRAVITY # convert friction into lateral accel units for feedforward - friction_compensation = interp(desired_lateral_jerk, [-JERK_THRESHOLD, JERK_THRESHOLD], [-self.friction, self.friction]) + friction_compensation = interp(apply_deadzone(error, lateral_accel_deadzone), [-FRICTION_THRESHOLD, FRICTION_THRESHOLD], [-self.friction, self.friction]) ff += friction_compensation / self.kf freeze_integrator = CS.steeringRateLimited or CS.steeringPressed or CS.vEgo < 5 output_torque = self.pid.update(error, diff --git a/selfdrive/controls/lib/lateral_planner.py b/selfdrive/controls/lib/lateral_planner.py index f60b3159b..019a19fb1 100644 --- a/selfdrive/controls/lib/lateral_planner.py +++ b/selfdrive/controls/lib/lateral_planner.py @@ -1,7 +1,7 @@ import numpy as np from common.realtime import sec_since_boot, DT_MDL from common.numpy_fast import interp -from selfdrive.swaglog import cloudlog +from system.swaglog import cloudlog from selfdrive.controls.lib.lateral_mpc_lib.lat_mpc import LateralMpc from selfdrive.controls.lib.drive_helpers import CONTROL_N, MPC_COST_LAT, LAT_MPC_N, CAR_ROTATION_RADIUS from selfdrive.controls.lib.lane_planner import LanePlanner, TRAJECTORY_SIZE @@ -11,13 +11,12 @@ from cereal import log class LateralPlanner: - def __init__(self, CP, use_lanelines=True, wide_camera=False): + def __init__(self, use_lanelines=True, wide_camera=False): self.use_lanelines = use_lanelines self.LP = LanePlanner(wide_camera) self.DH = DesireHelper() self.last_cloudlog_t = 0 - self.steer_rate_cost = CP.steerRateCost self.solution_invalid_cnt = 0 self.path_xyz = np.zeros((TRAJECTORY_SIZE, 3)) @@ -59,13 +58,12 @@ class LateralPlanner: # Calculate final driving path and set MPC costs if self.use_lanelines: d_path_xyz = self.LP.get_d_path(v_ego, self.t_idxs, self.path_xyz) - self.lat_mpc.set_weights(MPC_COST_LAT.PATH, MPC_COST_LAT.HEADING, self.steer_rate_cost) + self.lat_mpc.set_weights(MPC_COST_LAT.PATH, MPC_COST_LAT.HEADING, MPC_COST_LAT.STEER_RATE) else: d_path_xyz = self.path_xyz - path_cost = np.clip(abs(self.path_xyz[0, 1] / self.path_xyz_stds[0, 1]), 0.5, 1.5) * MPC_COST_LAT.PATH # Heading cost is useful at low speed, otherwise end of plan can be off-heading - heading_cost = interp(v_ego, [5.0, 10.0], [MPC_COST_LAT.HEADING, 0.0]) - self.lat_mpc.set_weights(path_cost, heading_cost, self.steer_rate_cost) + heading_cost = interp(v_ego, [5.0, 10.0], [MPC_COST_LAT.HEADING, 0.15]) + self.lat_mpc.set_weights(MPC_COST_LAT.PATH, heading_cost, MPC_COST_LAT.STEER_RATE) y_pts = np.interp(v_ego * self.t_idxs[:LAT_MPC_N + 1], np.linalg.norm(d_path_xyz, axis=1), d_path_xyz[:, 1]) heading_pts = np.interp(v_ego * self.t_idxs[:LAT_MPC_N + 1], np.linalg.norm(self.path_xyz, axis=1), self.plan_yaw) @@ -80,6 +78,9 @@ class LateralPlanner: y_pts, heading_pts) # init state for next + # mpc.u_sol is the desired curvature rate given x0 curv state. + # with x0[3] = measured_curvature, this would be the actual desired rate. + # instead, interpolate x_sol so that x0[3] is the desired curvature for lat_control. self.x0[3] = interp(DT_MDL, self.t_idxs[:LAT_MPC_N + 1], self.lat_mpc.x_sol[:, 3]) # Check for infeasible MPC solution diff --git a/selfdrive/controls/lib/longcontrol.py b/selfdrive/controls/lib/longcontrol.py index f2cd28219..e9458607d 100644 --- a/selfdrive/controls/lib/longcontrol.py +++ b/selfdrive/controls/lib/longcontrol.py @@ -1,7 +1,7 @@ from cereal import car from common.numpy_fast import clip, interp from common.realtime import DT_CTRL -from selfdrive.controls.lib.drive_helpers import CONTROL_N +from selfdrive.controls.lib.drive_helpers import CONTROL_N, apply_deadzone from selfdrive.controls.lib.pid import PIDController from selfdrive.modeld.constants import T_IDXS @@ -12,16 +12,6 @@ ACCEL_MIN_ISO = -3.5 # m/s^2 ACCEL_MAX_ISO = 2.0 # m/s^2 -def apply_deadzone(error, deadzone): - if error > deadzone: - error -= deadzone - elif error < - deadzone: - error += deadzone - else: - error = 0. - return error - - def long_control_state_trans(CP, active, long_control_state, v_ego, v_target, v_target_future, brake_pressed, cruise_standstill): """Update longitudinal control state machine""" diff --git a/selfdrive/controls/lib/longitudinal_mpc_lib/long_mpc.py b/selfdrive/controls/lib/longitudinal_mpc_lib/long_mpc.py index b99ee9e9a..94efd7a87 100644 --- a/selfdrive/controls/lib/longitudinal_mpc_lib/long_mpc.py +++ b/selfdrive/controls/lib/longitudinal_mpc_lib/long_mpc.py @@ -4,7 +4,7 @@ import numpy as np from common.realtime import sec_since_boot from common.numpy_fast import clip, interp -from selfdrive.swaglog import cloudlog +from system.swaglog import cloudlog from selfdrive.modeld.constants import index_function from selfdrive.controls.lib.radar_helpers import _LEAD_ACCEL_TAU @@ -251,7 +251,7 @@ class LongitudinalMpc: self.solver.cost_set(i, 'Zl', Zl) def set_weights_for_xva_policy(self): - W = np.asfortranarray(np.diag([0., 10., 1., 10., 0.0, 1.])) + W = np.asfortranarray(np.diag([0., 0.2, 0.25, 1., 0.0, .1])) for i in range(N): self.solver.cost_set(i, 'W', W) # Setting the slice without the copy make the array not contiguous, diff --git a/selfdrive/controls/lib/longitudinal_planner.py b/selfdrive/controls/lib/longitudinal_planner.py index 0d21c519a..cf5113677 100755 --- a/selfdrive/controls/lib/longitudinal_planner.py +++ b/selfdrive/controls/lib/longitudinal_planner.py @@ -12,7 +12,7 @@ from selfdrive.controls.lib.longcontrol import LongCtrlState from selfdrive.controls.lib.longitudinal_mpc_lib.long_mpc import LongitudinalMpc from selfdrive.controls.lib.longitudinal_mpc_lib.long_mpc import T_IDXS as T_IDXS_MPC from selfdrive.controls.lib.drive_helpers import V_CRUISE_MAX, CONTROL_N -from selfdrive.swaglog import cloudlog +from system.swaglog import cloudlog LON_MPC_STEP = 0.2 # first step is 0.2s AWARENESS_DECEL = -0.2 # car smoothly decel at .2m/s^2 when user is distracted @@ -66,11 +66,11 @@ class Planner: v_cruise_kph = min(v_cruise_kph, V_CRUISE_MAX) v_cruise = v_cruise_kph * CV.KPH_TO_MS - long_control_state = sm['controlsState'].longControlState + long_control_off = sm['controlsState'].longControlState == LongCtrlState.off force_slow_decel = sm['controlsState'].forceDecel # Reset current state when not engaged, or user is controlling the speed - reset_state = long_control_state == LongCtrlState.off + reset_state = long_control_off if self.CP.openpilotLongitudinalControl else not sm['controlsState'].enabled # No change cost when user is controlling the speed, or when standstill prev_accel_constraint = not (reset_state or sm['carState'].standstill) diff --git a/selfdrive/controls/plannerd.py b/selfdrive/controls/plannerd.py index 02f1c19a7..9356a55d8 100755 --- a/selfdrive/controls/plannerd.py +++ b/selfdrive/controls/plannerd.py @@ -2,28 +2,27 @@ from cereal import car from common.params import Params from common.realtime import Priority, config_realtime_process -from selfdrive.swaglog import cloudlog +from system.swaglog import cloudlog from selfdrive.controls.lib.longitudinal_planner import Planner from selfdrive.controls.lib.lateral_planner import LateralPlanner -from selfdrive.hardware import TICI import cereal.messaging as messaging def plannerd_thread(sm=None, pm=None): - config_realtime_process(5 if TICI else 2, Priority.CTRL_LOW) + config_realtime_process(5, Priority.CTRL_LOW) cloudlog.info("plannerd is waiting for CarParams") params = Params() CP = car.CarParams.from_bytes(params.get("CarParams", block=True)) cloudlog.info("plannerd got CarParams: %s", CP.carName) - use_lanelines = not params.get_bool('EndToEndToggle') - wide_camera = params.get_bool('EnableWideCamera') if TICI else False + use_lanelines = False + wide_camera = params.get_bool('WideCameraOnly') cloudlog.event("e2e mode", on=use_lanelines) longitudinal_planner = Planner(CP) - lateral_planner = LateralPlanner(CP, use_lanelines=use_lanelines, wide_camera=wide_camera) + lateral_planner = LateralPlanner(use_lanelines=use_lanelines, wide_camera=wide_camera) if sm is None: sm = messaging.SubMaster(['carState', 'controlsState', 'radarState', 'modelV2'], diff --git a/selfdrive/controls/radard.py b/selfdrive/controls/radard.py index cae9474fa..3d958139d 100755 --- a/selfdrive/controls/radard.py +++ b/selfdrive/controls/radard.py @@ -10,8 +10,7 @@ from common.params import Params from common.realtime import Ratekeeper, Priority, config_realtime_process from selfdrive.controls.lib.cluster.fastcluster_py import cluster_points_centroid from selfdrive.controls.lib.radar_helpers import Cluster, Track, RADAR_TO_CAMERA -from selfdrive.swaglog import cloudlog -from selfdrive.hardware import TICI +from system.swaglog import cloudlog class KalmanParams(): @@ -103,7 +102,7 @@ class RadarD(): self.ready = False - def update(self, sm, rr, enable_lead): + def update(self, sm, rr): self.current_time = 1e-9*max(sm.logMonoTime.values()) if sm.updated['carState']: @@ -170,17 +169,16 @@ class RadarD(): radarState.radarErrors = list(rr.errors) radarState.carStateMonoTime = sm.logMonoTime['carState'] - if enable_lead: - leads_v3 = sm['modelV2'].leadsV3 - if len(leads_v3) > 1: - radarState.leadOne = get_lead(self.v_ego, self.ready, clusters, leads_v3[0], low_speed_override=True) - radarState.leadTwo = get_lead(self.v_ego, self.ready, clusters, leads_v3[1], low_speed_override=False) + leads_v3 = sm['modelV2'].leadsV3 + if len(leads_v3) > 1: + radarState.leadOne = get_lead(self.v_ego, self.ready, clusters, leads_v3[0], low_speed_override=True) + radarState.leadTwo = get_lead(self.v_ego, self.ready, clusters, leads_v3[1], low_speed_override=False) return dat # fuses camera and radar data for best lead detection def radard_thread(sm=None, pm=None, can_sock=None): - config_realtime_process(5 if TICI else 2, Priority.CTRL_LOW) + config_realtime_process(5, Priority.CTRL_LOW) # wait for stats about the car to come in from controls cloudlog.info("radard is waiting for CarParams") @@ -204,9 +202,6 @@ def radard_thread(sm=None, pm=None, can_sock=None): rk = Ratekeeper(1.0 / CP.radarTimeStep, print_delay_threshold=None) RD = RadarD(CP.radarTimeStep, RI.delay) - # TODO: always log leads once we can hide them conditionally - enable_lead = CP.openpilotLongitudinalControl or not CP.radarOffCan - while 1: can_strings = messaging.drain_sock_raw(can_sock, wait_for_one=True) rr = RI.update(can_strings) @@ -216,7 +211,7 @@ def radard_thread(sm=None, pm=None, can_sock=None): sm.update(0) - dat = RD.update(sm, rr, enable_lead) + dat = RD.update(sm, rr) dat.radarState.cumLagMs = -rk.remaining*1000. pm.send('radarState', dat) diff --git a/selfdrive/debug/can_print_changes.py b/selfdrive/debug/can_print_changes.py deleted file mode 100755 index b883fbc23..000000000 --- a/selfdrive/debug/can_print_changes.py +++ /dev/null @@ -1,103 +0,0 @@ -#!/usr/bin/env python3 -import argparse -import binascii -import time -from collections import defaultdict - -import cereal.messaging as messaging -from selfdrive.debug.can_table import can_table -from tools.lib.logreader import logreader_from_route_or_segment - -RED = '\033[91m' -CLEAR = '\033[0m' - -def update(msgs, bus, dat, low_to_high, high_to_low, quiet=False): - for x in msgs: - if x.which() != 'can': - continue - - for y in x.can: - if y.src == bus: - dat[y.address] = y.dat - - i = int.from_bytes(y.dat, byteorder='big') - l_h = low_to_high[y.address] - h_l = high_to_low[y.address] - - change = None - if (i | l_h) != l_h: - low_to_high[y.address] = i | l_h - change = "+" - - if (~i | h_l) != h_l: - high_to_low[y.address] = ~i | h_l - change = "-" - - if change and not quiet: - print(f"{time.monotonic():.2f}\t{hex(y.address)} ({y.address})\t{change}{binascii.hexlify(y.dat)}") - - -def can_printer(bus=0, init_msgs=None, new_msgs=None, table=False): - logcan = messaging.sub_sock('can', timeout=10) - - dat = defaultdict(int) - low_to_high = defaultdict(int) - high_to_low = defaultdict(int) - - if init_msgs is not None: - update(init_msgs, bus, dat, low_to_high, high_to_low, quiet=True) - - low_to_high_init = low_to_high.copy() - high_to_low_init = high_to_low.copy() - - if new_msgs is not None: - update(new_msgs, bus, dat, low_to_high, high_to_low) - else: - # Live mode - try: - while 1: - can_recv = messaging.drain_sock(logcan) - update(can_recv, bus, dat, low_to_high, high_to_low) - time.sleep(0.02) - except KeyboardInterrupt: - pass - - print("\n\n") - tables = "" - for addr in sorted(dat.keys()): - init = low_to_high_init[addr] & high_to_low_init[addr] - now = low_to_high[addr] & high_to_low[addr] - d = now & ~init - if d == 0: - continue - b = d.to_bytes(len(dat[addr]), byteorder='big') - - byts = ''.join([(c if c == '0' else f'{RED}{c}{CLEAR}') for c in str(binascii.hexlify(b))[2:-1]]) - header = f"{hex(addr).ljust(6)}({str(addr).ljust(4)})" - print(header, byts) - tables += f"{header}\n" - tables += can_table(b) + "\n\n" - - if table: - print(tables) - -if __name__ == "__main__": - desc = """Collects messages and prints when a new bit transition is observed. - This is very useful to find signals based on user triggered actions, such as blinkers and seatbelt. - Leave the script running until no new transitions are seen, then perform the action.""" - parser = argparse.ArgumentParser(description=desc, - formatter_class=argparse.ArgumentDefaultsHelpFormatter) - parser.add_argument("--bus", type=int, help="CAN bus to print out", default=0) - parser.add_argument("--table", action="store_true", help="Print a cabana-like table") - parser.add_argument("init", type=str, nargs='?', help="Route or segment to initialize with") - parser.add_argument("comp", type=str, nargs='?', help="Route or segment to compare against init") - - args = parser.parse_args() - - init_lr, new_lr = None, None - if args.init: - init_lr = logreader_from_route_or_segment(args.init) - if args.comp: - new_lr = logreader_from_route_or_segment(args.comp) - - can_printer(args.bus, init_msgs=init_lr, new_msgs=new_lr, table=args.table) diff --git a/selfdrive/debug/can_table.py b/selfdrive/debug/can_table.py deleted file mode 100755 index e8cd084a3..000000000 --- a/selfdrive/debug/can_table.py +++ /dev/null @@ -1,45 +0,0 @@ -#!/usr/bin/env python3 -import argparse -import pandas as pd # pylint: disable=import-error - -import cereal.messaging as messaging - - -def can_table(dat): - rows = [] - for b in dat: - r = list(bin(b).lstrip('0b').zfill(8)) - r += [hex(b)] - rows.append(r) - - df = pd.DataFrame(data=rows) - df.columns = [str(n) for n in range(7, -1, -1)] + [' '] - table = df.to_markdown(tablefmt='grid') - return table - - -if __name__ == "__main__": - parser = argparse.ArgumentParser(description="Cabana-like table of bits for your terminal", - formatter_class=argparse.ArgumentDefaultsHelpFormatter) - parser.add_argument("addr", type=str, nargs=1) - parser.add_argument("bus", type=int, default=0, nargs='?') - - args = parser.parse_args() - - addr = int(args.addr[0], 0) - can = messaging.sub_sock('can', conflate=False, timeout=None) - - print(f"waiting for {hex(addr)} ({addr}) on bus {args.bus}...") - - latest = None - while True: - for msg in messaging.drain_sock(can, wait_for_one=True): - for m in msg.can: - if m.address == addr and m.src == args.bus: - latest = m - - if latest is None: - continue - - table = can_table(latest.dat) - print(f"\n\n{hex(addr)} ({addr}) on bus {args.bus}\n{table}") diff --git a/selfdrive/debug/check_freq.py b/selfdrive/debug/check_freq.py index 424ad67b6..b6f3c91bd 100755 --- a/selfdrive/debug/check_freq.py +++ b/selfdrive/debug/check_freq.py @@ -1,9 +1,9 @@ #!/usr/bin/env python3 -# type: ignore - import argparse import numpy as np from collections import defaultdict, deque +from typing import DefaultDict, Deque + from common.realtime import sec_since_boot import cereal.messaging as messaging @@ -19,8 +19,8 @@ if __name__ == "__main__": socket_names = args.socket sockets = {} - rcv_times = defaultdict(lambda: deque(maxlen=100)) - valids = defaultdict(lambda: deque(maxlen=100)) + rcv_times: DefaultDict[str, Deque[float]] = defaultdict(lambda: deque(maxlen=100)) + valids: DefaultDict[str, Deque[bool]] = defaultdict(lambda: deque(maxlen=100)) t = sec_since_boot() for name in socket_names: @@ -31,6 +31,9 @@ if __name__ == "__main__": while True: for socket in poller.poll(100): msg = messaging.recv_one(socket) + if msg is None: + continue + name = msg.which() t = sec_since_boot() diff --git a/selfdrive/debug/check_lag.py b/selfdrive/debug/check_lag.py deleted file mode 100755 index c92264298..000000000 --- a/selfdrive/debug/check_lag.py +++ /dev/null @@ -1,28 +0,0 @@ -#!/usr/bin/env python3 -# type: ignore - -import cereal.messaging as messaging -from cereal.services import service_list - -TO_CHECK = ['carState'] - - -if __name__ == "__main__": - sm = messaging.SubMaster(TO_CHECK) - - prev_t = {} - - while True: - sm.update() - - for s in TO_CHECK: - if sm.updated[s]: - t = sm.logMonoTime[s] / 1e9 - - if s in prev_t: - expected = 1.0 / (service_list[s].frequency) - dt = t - prev_t[s] - if dt > 10 * expected: - print(t, s, dt) - - prev_t[s] = t diff --git a/selfdrive/debug/check_timings.py b/selfdrive/debug/check_timings.py deleted file mode 100755 index 03e39fd70..000000000 --- a/selfdrive/debug/check_timings.py +++ /dev/null @@ -1,24 +0,0 @@ -#!/usr/bin/env python3 -# type: ignore -import sys -import time -import numpy as np -from collections import defaultdict, deque - -import cereal.messaging as messaging - -socks = {s: messaging.sub_sock(s, conflate=False) for s in sys.argv[1:]} -ts = defaultdict(lambda: deque(maxlen=100)) - -if __name__ == "__main__": - while True: - print() - for s, sock in socks.items(): - msgs = messaging.drain_sock(sock) - for m in msgs: - ts[s].append(m.logMonoTime / 1e6) - - if len(ts[s]): - d = np.diff(ts[s]) - print(f"{s:25} {np.mean(d):.2f} {np.std(d):.2f} {np.max(d):.2f} {np.min(d):.2f}") - time.sleep(1) diff --git a/selfdrive/debug/clear_dtc.py b/selfdrive/debug/clear_dtc.py deleted file mode 100755 index d84828079..000000000 --- a/selfdrive/debug/clear_dtc.py +++ /dev/null @@ -1,40 +0,0 @@ -#!/usr/bin/env python3 -import sys -import argparse -from subprocess import check_output, CalledProcessError -from panda import Panda -from panda.python.uds import UdsClient, MessageTimeoutError, SESSION_TYPE, DTC_GROUP_TYPE - -parser = argparse.ArgumentParser(description="clear DTC status") -parser.add_argument("addr", type=lambda x: int(x,0), nargs="?", default=0x7DF) # default is functional (broadcast) address -parser.add_argument("--bus", type=int, default=0) -parser.add_argument('--debug', action='store_true') -args = parser.parse_args() - -try: - check_output(["pidof", "boardd"]) - print("boardd is running, please kill openpilot before running this script! (aborted)") - sys.exit(1) -except CalledProcessError as e: - if e.returncode != 1: # 1 == no process found (boardd not running) - raise e - -panda = Panda() -panda.set_safety_mode(Panda.SAFETY_ELM327) -uds_client = UdsClient(panda, args.addr, bus=args.bus, debug=args.debug) -print("extended diagnostic session ...") -try: - uds_client.diagnostic_session_control(SESSION_TYPE.EXTENDED_DIAGNOSTIC) -except MessageTimeoutError: - # functional address isn't properly handled so a timeout occurs - if args.addr != 0x7DF: - raise -print("clear diagnostic info ...") -try: - uds_client.clear_diagnostic_information(DTC_GROUP_TYPE.ALL) -except MessageTimeoutError: - # functional address isn't properly handled so a timeout occurs - if args.addr != 0x7DF: - pass -print("") -print("you may need to power cycle your vehicle now") diff --git a/selfdrive/debug/count_events.py b/selfdrive/debug/count_events.py deleted file mode 100755 index 8b32ce9d2..000000000 --- a/selfdrive/debug/count_events.py +++ /dev/null @@ -1,32 +0,0 @@ -#!/usr/bin/env python3 -import sys -from collections import Counter -from pprint import pprint -from tqdm import tqdm - -from tools.lib.route import Route -from tools.lib.logreader import LogReader - -if __name__ == "__main__": - r = Route(sys.argv[1]) - - cnt_valid: Counter = Counter() - cnt_events: Counter = Counter() - - for q in tqdm(r.qlog_paths()): - if q is None: - continue - lr = list(LogReader(q)) - for msg in lr: - if msg.which() == 'carEvents': - for e in msg.carEvents: - cnt_events[e.name] += 1 - if not msg.valid: - cnt_valid[msg.which()] += 1 - - print("Events") - pprint(cnt_events) - - print("\n\n") - print("Not valid") - pprint(cnt_valid) diff --git a/selfdrive/debug/cpu_usage_stat.py b/selfdrive/debug/cpu_usage_stat.py deleted file mode 100755 index 76e809d2c..000000000 --- a/selfdrive/debug/cpu_usage_stat.py +++ /dev/null @@ -1,123 +0,0 @@ -#!/usr/bin/env python3 -# type: ignore -''' -System tools like top/htop can only show current cpu usage values, so I write this script to do statistics jobs. - Features: - Use psutil library to sample cpu usage(avergage for all cores) of openpilot processes, at a rate of 5 samples/sec. - Do cpu usage statistics periodically, 5 seconds as a cycle. - Caculate the average cpu usage within this cycle. - Caculate minumium/maximium/accumulated_average cpu usage as long term inspections. - Monitor multiple processes simuteneously. - Sample usage: - root@localhost:/data/openpilot$ python selfdrive/debug/cpu_usage_stat.py boardd,ubloxd - ('Add monitored proc:', './boardd') - ('Add monitored proc:', 'python locationd/ubloxd.py') - boardd: 1.96%, min: 1.96%, max: 1.96%, acc: 1.96% - ubloxd.py: 0.39%, min: 0.39%, max: 0.39%, acc: 0.39% -''' -import psutil -import time -import os -import sys -import numpy as np -import argparse -import re -from collections import defaultdict - -from selfdrive.manager.process_config import managed_processes - -# Do statistics every 5 seconds -PRINT_INTERVAL = 5 -SLEEP_INTERVAL = 0.2 - -monitored_proc_names = [ - # android procs - 'SurfaceFlinger', 'sensors.qcom' -] + list(managed_processes.keys()) - -cpu_time_names = ['user', 'system', 'children_user', 'children_system'] - -timer = getattr(time, 'monotonic', time.time) - - -def get_arg_parser(): - parser = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter) - - parser.add_argument("proc_names", nargs="?", default='', - help="Process names to be monitored, comma separated") - parser.add_argument("--list_all", action='store_true', - help="Show all running processes' cmdline") - parser.add_argument("--detailed_times", action='store_true', - help="show cpu time details (split by user, system, child user, child system)") - return parser - - -if __name__ == "__main__": - args = get_arg_parser().parse_args(sys.argv[1:]) - if args.list_all: - for p in psutil.process_iter(): - print('cmdline', p.cmdline(), 'name', p.name()) - sys.exit(0) - - if len(args.proc_names) > 0: - monitored_proc_names = args.proc_names.split(',') - monitored_procs = [] - stats = {} - for p in psutil.process_iter(): - if p == psutil.Process(): - continue - matched = any(l for l in p.cmdline() if any(pn for pn in monitored_proc_names if re.match(r'.*{}.*'.format(pn), l, re.M | re.I))) - if matched: - k = ' '.join(p.cmdline()) - print('Add monitored proc:', k) - stats[k] = {'cpu_samples': defaultdict(list), 'min': defaultdict(lambda: None), 'max': defaultdict(lambda: None), - 'avg': defaultdict(lambda: 0.0), 'last_cpu_times': None, 'last_sys_time': None} - stats[k]['last_sys_time'] = timer() - stats[k]['last_cpu_times'] = p.cpu_times() - monitored_procs.append(p) - i = 0 - interval_int = int(PRINT_INTERVAL / SLEEP_INTERVAL) - while True: - for p in monitored_procs: - k = ' '.join(p.cmdline()) - cur_sys_time = timer() - cur_cpu_times = p.cpu_times() - cpu_times = np.subtract(cur_cpu_times, stats[k]['last_cpu_times']) / (cur_sys_time - stats[k]['last_sys_time']) - stats[k]['last_sys_time'] = cur_sys_time - stats[k]['last_cpu_times'] = cur_cpu_times - cpu_percent = 0 - for num, name in enumerate(cpu_time_names): - stats[k]['cpu_samples'][name].append(cpu_times[num]) - cpu_percent += cpu_times[num] - stats[k]['cpu_samples']['total'].append(cpu_percent) - time.sleep(SLEEP_INTERVAL) - i += 1 - if i % interval_int == 0: - l = [] - for k, stat in stats.items(): - if len(stat['cpu_samples']) <= 0: - continue - for name, samples in stat['cpu_samples'].items(): - samples = np.array(samples) - avg = samples.mean() - c = samples.size - min_cpu = np.amin(samples) - max_cpu = np.amax(samples) - if stat['min'][name] is None or min_cpu < stat['min'][name]: - stat['min'][name] = min_cpu - if stat['max'][name] is None or max_cpu > stat['max'][name]: - stat['max'][name] = max_cpu - stat['avg'][name] = (stat['avg'][name] * (i - c) + avg * c) / (i) - stat['cpu_samples'][name] = [] - - msg = f"avg: {stat['avg']['total']:.2%}, min: {stat['min']['total']:.2%}, max: {stat['max']['total']:.2%} {os.path.basename(k)}" - if args.detailed_times: - for stat_type in ['avg', 'min', 'max']: - msg += f"\n {stat_type}: {[(name + ':' + str(round(stat[stat_type][name] * 100, 2))) for name in cpu_time_names]}" - l.append((os.path.basename(k), stat['avg']['total'], msg)) - l.sort(key=lambda x: -x[1]) - for x in l: - print(x[2]) - print('avg sum: {:.2%} over {} samples {} seconds\n'.format( - sum(stat['avg']['total'] for k, stat in stats.items()), i, i * SLEEP_INTERVAL - )) diff --git a/selfdrive/debug/cycle_alerts.py b/selfdrive/debug/cycle_alerts.py deleted file mode 100755 index b40c8e304..000000000 --- a/selfdrive/debug/cycle_alerts.py +++ /dev/null @@ -1,129 +0,0 @@ -#!/usr/bin/env python3 -import time -import random - -from cereal import car, log -import cereal.messaging as messaging -from common.realtime import DT_CTRL -from selfdrive.car.honda.interface import CarInterface -from selfdrive.controls.lib.events import ET, Events -from selfdrive.controls.lib.alertmanager import AlertManager -from selfdrive.manager.process_config import managed_processes - -EventName = car.CarEvent.EventName - -def randperc() -> float: - return 100. * random.random() - -def cycle_alerts(duration=200, is_metric=False): - # all alerts - #alerts = list(EVENTS.keys()) - - # this plays each type of audible alert - alerts = [ - (EventName.buttonEnable, ET.ENABLE), - (EventName.buttonCancel, ET.USER_DISABLE), - (EventName.wrongGear, ET.NO_ENTRY), - - (EventName.vehicleModelInvalid, ET.SOFT_DISABLE), - (EventName.accFaulted, ET.IMMEDIATE_DISABLE), - - # DM sequence - (EventName.preDriverDistracted, ET.WARNING), - (EventName.promptDriverDistracted, ET.WARNING), - (EventName.driverDistracted, ET.WARNING), - ] - - # debug alerts - alerts = [ - #(EventName.highCpuUsage, ET.NO_ENTRY), - #(EventName.lowMemory, ET.PERMANENT), - #(EventName.overheat, ET.PERMANENT), - #(EventName.outOfSpace, ET.PERMANENT), - #(EventName.modeldLagging, ET.PERMANENT), - #(EventName.processNotRunning, ET.NO_ENTRY), - #(EventName.commIssue, ET.NO_ENTRY), - #(EventName.calibrationInvalid, ET.PERMANENT), - (EventName.cameraMalfunction, ET.PERMANENT), - (EventName.cameraFrameRate, ET.PERMANENT), - ] - - cameras = ['roadCameraState', 'wideRoadCameraState', 'driverCameraState'] - - CS = car.CarState.new_message() - CP = CarInterface.get_params("HONDA CIVIC 2016") - sm = messaging.SubMaster(['deviceState', 'pandaStates', 'roadCameraState', 'modelV2', 'liveCalibration', - 'driverMonitoringState', 'longitudinalPlan', 'lateralPlan', 'liveLocationKalman', - 'managerState'] + cameras) - - pm = messaging.PubMaster(['controlsState', 'pandaStates', 'deviceState']) - - events = Events() - AM = AlertManager() - - frame = 0 - while True: - for alert, et in alerts: - events.clear() - events.add(alert) - - sm['deviceState'].freeSpacePercent = randperc() - sm['deviceState'].memoryUsagePercent = int(randperc()) - sm['deviceState'].cpuTempC = [randperc() for _ in range(3)] - sm['deviceState'].gpuTempC = [randperc() for _ in range(3)] - sm['deviceState'].cpuUsagePercent = [int(randperc()) for _ in range(8)] - sm['modelV2'].frameDropPerc = randperc() - - if random.random() > 0.25: - sm['modelV2'].velocity.x = [random.random(), ] - if random.random() > 0.25: - CS.vEgo = random.random() - - procs = [p.get_process_state_msg() for p in managed_processes.values()] - random.shuffle(procs) - for i in range(random.randint(0, 10)): - procs[i].shouldBeRunning = True - sm['managerState'].processes = procs - - sm['liveCalibration'].rpyCalib = [-1 * random.random() for _ in range(random.randint(0, 3))] - - for s in sm.data.keys(): - prob = 0.3 if s in cameras else 0.08 - sm.alive[s] = random.random() > prob - sm.valid[s] = random.random() > prob - sm.freq_ok[s] = random.random() > prob - - a = events.create_alerts([et, ], [CP, CS, sm, is_metric, 0]) - AM.add_many(frame, a) - alert = AM.process_alerts(frame, []) - print(alert) - for _ in range(duration): - dat = messaging.new_message() - dat.init('controlsState') - dat.controlsState.enabled = False - - if alert: - dat.controlsState.alertText1 = alert.alert_text_1 - dat.controlsState.alertText2 = alert.alert_text_2 - dat.controlsState.alertSize = alert.alert_size - dat.controlsState.alertStatus = alert.alert_status - dat.controlsState.alertBlinkingRate = alert.alert_rate - dat.controlsState.alertType = alert.alert_type - dat.controlsState.alertSound = alert.audible_alert - pm.send('controlsState', dat) - - dat = messaging.new_message() - dat.init('deviceState') - dat.deviceState.started = True - pm.send('deviceState', dat) - - dat = messaging.new_message('pandaStates', 1) - dat.pandaStates[0].ignitionLine = True - dat.pandaStates[0].pandaType = log.PandaState.PandaType.uno - pm.send('pandaStates', dat) - - frame += 1 - time.sleep(DT_CTRL) - -if __name__ == '__main__': - cycle_alerts() diff --git a/selfdrive/debug/disable_ecu.py b/selfdrive/debug/disable_ecu.py deleted file mode 100644 index f0faf4001..000000000 --- a/selfdrive/debug/disable_ecu.py +++ /dev/null @@ -1,40 +0,0 @@ -#!/usr/bin/env python3 -import traceback - -import cereal.messaging as messaging -from selfdrive.car.isotp_parallel_query import IsoTpParallelQuery -from selfdrive.swaglog import cloudlog - -EXT_DIAG_REQUEST = b'\x10\x03' -EXT_DIAG_RESPONSE = b'\x50\x03' -COM_CONT_REQUEST = b'\x28\x83\x03' -COM_CONT_RESPONSE = b'' - -def disable_ecu(ecu_addr, logcan, sendcan, bus, timeout=0.5, retry=5, debug=False): - print(f"ecu disable {hex(ecu_addr)} ...") - for i in range(retry): - try: - # enter extended diagnostic session - query = IsoTpParallelQuery(sendcan, logcan, bus, [ecu_addr], [EXT_DIAG_REQUEST], [EXT_DIAG_RESPONSE], debug=debug) - for addr, dat in query.get_data(timeout).items(): # pylint: disable=unused-variable - print("ecu communication control disable tx/rx ...") - # communication control disable tx and rx - query = IsoTpParallelQuery(sendcan, logcan, bus, [ecu_addr], [COM_CONT_REQUEST], [COM_CONT_RESPONSE], debug=debug) - query.get_data(0) - return True - print(f"ecu disable retry ({i+1}) ...") - except Exception: - cloudlog.warning(f"ecu disable exception: {traceback.format_exc()}") - - return False - - -if __name__ == "__main__": - import time - sendcan = messaging.pub_sock('sendcan') - logcan = messaging.sub_sock('can') - time.sleep(1) - - # honda bosch radar disable - disabled = disable_ecu(0x18DAB0F1, logcan, sendcan, 1, debug=False) - print(f"disabled: {disabled}") diff --git a/selfdrive/debug/fingerprint_from_route.py b/selfdrive/debug/fingerprint_from_route.py deleted file mode 100755 index 326e68f8e..000000000 --- a/selfdrive/debug/fingerprint_from_route.py +++ /dev/null @@ -1,45 +0,0 @@ -#!/usr/bin/env python3 - -import sys -from tools.lib.route import Route -from tools.lib.logreader import MultiLogIterator - - -def get_fingerprint(lr): - # TODO: make this a nice tool for car ports. should also work with qlogs for FW - - fw = None - msgs = {} - for msg in lr: - if msg.which() == 'carParams': - fw = msg.carParams.carFw - elif msg.which() == 'can': - for c in msg.can: - # read also msgs sent by EON on CAN bus 0x80 and filter out the - # addr with more than 11 bits - if c.src % 0x80 == 0 and c.address < 0x800: - msgs[c.address] = len(c.dat) - - # show CAN fingerprint - fingerprint = ', '.join("%d: %d" % v for v in sorted(msgs.items())) - print(f"\nfound {len(msgs)} messages. CAN fingerprint:\n") - print(fingerprint) - - # TODO: also print the fw fingerprint merged with the existing ones - # show FW fingerprint - print("\nFW fingerprint:\n") - for f in fw: - print(f" (Ecu.{f.ecu}, {hex(f.address)}, {None if f.subAddress == 0 else f.subAddress}): [") - print(f" {f.fwVersion},") - print(" ],") - print() - - -if __name__ == "__main__": - if len(sys.argv) < 2: - print("Usage: ./fingerprint_from_route.py ") - sys.exit(1) - - route = Route(sys.argv[1]) - lr = MultiLogIterator(route.log_paths()[:5]) - get_fingerprint(lr) diff --git a/selfdrive/debug/live_cpu_and_temp.py b/selfdrive/debug/live_cpu_and_temp.py deleted file mode 100755 index baeebb1c4..000000000 --- a/selfdrive/debug/live_cpu_and_temp.py +++ /dev/null @@ -1,106 +0,0 @@ -#!/usr/bin/env python3 -import argparse -import capnp - -from cereal.messaging import SubMaster -from common.numpy_fast import mean -from typing import Optional - -def cputime_total(ct): - return ct.user + ct.nice + ct.system + ct.idle + ct.iowait + ct.irq + ct.softirq - - -def cputime_busy(ct): - return ct.user + ct.nice + ct.system + ct.irq + ct.softirq - - -def proc_cputime_total(ct): - return ct.cpuUser + ct.cpuSystem + ct.cpuChildrenUser + ct.cpuChildrenSystem - - -def proc_name(proc): - name = proc.name - if len(proc.cmdline): - name = proc.cmdline[0] - if len(proc.exe): - name = proc.exe + " - " + name - - return name - - -if __name__ == "__main__": - parser = argparse.ArgumentParser() - parser.add_argument('--mem', action='store_true') - parser.add_argument('--cpu', action='store_true') - args = parser.parse_args() - - sm = SubMaster(['deviceState', 'procLog']) - - last_temp = 0.0 - last_mem = 0.0 - total_times = [0.]*8 - busy_times = [0.]*8 - - prev_proclog: Optional[capnp._DynamicStructReader] = None - prev_proclog_t: Optional[int] = None - - while True: - sm.update() - - if sm.updated['deviceState']: - t = sm['deviceState'] - last_temp = mean(t.cpuTempC) - last_mem = t.memoryUsagePercent - - if sm.updated['procLog']: - m = sm['procLog'] - - cores = [0.]*8 - total_times_new = [0.]*8 - busy_times_new = [0.]*8 - - for c in m.cpuTimes: - n = c.cpuNum - total_times_new[n] = cputime_total(c) - busy_times_new[n] = cputime_busy(c) - - for n in range(8): - t_busy = busy_times_new[n] - busy_times[n] - t_total = total_times_new[n] - total_times[n] - cores[n] = t_busy / t_total - - total_times = total_times_new[:] - busy_times = busy_times_new[:] - - print(f"CPU {100.0 * mean(cores):.2f}% - RAM: {last_mem:.2f}% - Temp {last_temp:.2f}C") - - if args.cpu and prev_proclog is not None and prev_proclog_t is not None: - procs = {} - dt = (sm.logMonoTime['procLog'] - prev_proclog_t) / 1e9 - for proc in m.procs: - try: - name = proc_name(proc) - prev_proc = [p for p in prev_proclog.procs if proc.pid == p.pid][0] - cpu_time = proc_cputime_total(proc) - proc_cputime_total(prev_proc) - cpu_usage = cpu_time / dt * 100. - procs[name] = cpu_usage - except IndexError: - pass - - print("Top CPU usage:") - for k, v in sorted(procs.items(), key=lambda item: item[1], reverse=True)[:10]: # type: ignore - print(f"{k.rjust(70)} {v:.2f} %") - print() - - if args.mem: - mems = {} - for proc in m.procs: - name = proc_name(proc) - mems[name] = float(proc.memRss) / 1e6 - print("Top memory usage:") - for k, v in sorted(mems.items(), key=lambda item: item[1], reverse=True)[:10]: - print(f"{k.rjust(70)} {v:.2f} MB") - print() - - prev_proclog = m - prev_proclog_t = sm.logMonoTime['procLog'] diff --git a/selfdrive/debug/read_dtc_status.py b/selfdrive/debug/read_dtc_status.py deleted file mode 100755 index 9ad556397..000000000 --- a/selfdrive/debug/read_dtc_status.py +++ /dev/null @@ -1,35 +0,0 @@ -#!/usr/bin/env python3 -import sys -import argparse -from subprocess import check_output, CalledProcessError -from panda import Panda -from panda.python.uds import UdsClient, SESSION_TYPE, DTC_REPORT_TYPE, DTC_STATUS_MASK_TYPE -from panda.python.uds import get_dtc_num_as_str, get_dtc_status_names - -parser = argparse.ArgumentParser(description="read DTC status") -parser.add_argument("addr", type=lambda x: int(x,0)) -parser.add_argument("--bus", type=int, default=0) -parser.add_argument('--debug', action='store_true') -args = parser.parse_args() - -try: - check_output(["pidof", "boardd"]) - print("boardd is running, please kill openpilot before running this script! (aborted)") - sys.exit(1) -except CalledProcessError as e: - if e.returncode != 1: # 1 == no process found (boardd not running) - raise e - -panda = Panda() -panda.set_safety_mode(Panda.SAFETY_ELM327) -uds_client = UdsClient(panda, args.addr, bus=args.bus, debug=args.debug) -print("extended diagnostic session ...") -uds_client.diagnostic_session_control(SESSION_TYPE.EXTENDED_DIAGNOSTIC) -print("read diagnostic codes ...") -data = uds_client.read_dtc_information(DTC_REPORT_TYPE.DTC_BY_STATUS_MASK, DTC_STATUS_MASK_TYPE.ALL) -print("status availability:", " ".join(get_dtc_status_names(data[0]))) -print("DTC status:") -for i in range(1, len(data), 4): - dtc_num = get_dtc_num_as_str(data[i:i+3]) - dtc_status = " ".join(get_dtc_status_names(data[i+3])) - print(dtc_num, dtc_status) diff --git a/selfdrive/debug/run_process_on_route.py b/selfdrive/debug/run_process_on_route.py deleted file mode 100755 index 63b0733bb..000000000 --- a/selfdrive/debug/run_process_on_route.py +++ /dev/null @@ -1,31 +0,0 @@ -#!/usr/bin/env python3 - -import argparse - -from selfdrive.test.process_replay.compare_logs import save_log -from selfdrive.test.process_replay.process_replay import CONFIGS, replay_process -from tools.lib.logreader import MultiLogIterator -from tools.lib.route import Route - -if __name__ == "__main__": - parser = argparse.ArgumentParser(description="Run process on route and create new logs", - formatter_class=argparse.ArgumentDefaultsHelpFormatter) - parser.add_argument("route", help="The route name to use") - parser.add_argument("process", help="The process to run") - args = parser.parse_args() - - cfg = [c for c in CONFIGS if c.proc_name == args.process][0] - - route = Route(args.route) - lr = MultiLogIterator(route.log_paths()) - inputs = list(lr) - - outputs = replay_process(cfg, inputs) - - # Remove message generated by the process under test and merge in the new messages - produces = {o.which() for o in outputs} - inputs = [i for i in inputs if i.which() not in produces] - outputs = sorted(inputs + outputs, key=lambda x: x.logMonoTime) # type: ignore - - fn = f"{args.route}_{args.process}.bz2" - save_log(fn, outputs) diff --git a/selfdrive/debug/set_car_params.py b/selfdrive/debug/set_car_params.py deleted file mode 100755 index bcdaed077..000000000 --- a/selfdrive/debug/set_car_params.py +++ /dev/null @@ -1,11 +0,0 @@ -#!/usr/bin/env python3 -import sys - -from common.params import Params -from tools.lib.route import Route -from tools.lib.logreader import LogReader - -if __name__ == "__main__": - r = Route(sys.argv[1]) - cp = [m for m in LogReader(r.qlog_paths()[0]) if m.which() == 'carParams'] - Params().put("CarParams", cp[0].carParams.as_builder().to_bytes()) diff --git a/selfdrive/debug/show_matching_cars.py b/selfdrive/debug/show_matching_cars.py deleted file mode 100755 index d5199b2a9..000000000 --- a/selfdrive/debug/show_matching_cars.py +++ /dev/null @@ -1,20 +0,0 @@ -#!/usr/bin/env python3 -from selfdrive.car.fingerprints import eliminate_incompatible_cars, all_legacy_fingerprint_cars -import cereal.messaging as messaging - - -# rav4 2019 and corolla tss2 -fingerprint = {896: 8, 898: 8, 900: 6, 976: 1, 1541: 8, 902: 6, 905: 8, 810: 2, 1164: 8, 1165: 8, 1166: 8, 1167: 8, 1552: 8, 1553: 8, 1556: 8, 1571: 8, 921: 8, 1056: 8, 544: 4, 1570: 8, 1059: 1, 36: 8, 37: 8, 550: 8, 935: 8, 552: 4, 170: 8, 812: 8, 944: 8, 945: 8, 562: 6, 180: 8, 1077: 8, 951: 8, 1592: 8, 1076: 8, 186: 4, 955: 8, 956: 8, 1001: 8, 705: 8, 452: 8, 1788: 8, 464: 8, 824: 8, 466: 8, 467: 8, 761: 8, 728: 8, 1572: 8, 1114: 8, 933: 8, 800: 8, 608: 8, 865: 8, 610: 8, 1595: 8, 934: 8, 998: 5, 1745: 8, 1000: 8, 764: 8, 1002: 8, 999: 7, 1789: 8, 1649: 8, 1779: 8, 1568: 8, 1017: 8, 1786: 8, 1787: 8, 1020: 8, 426: 6, 1279: 8} - -candidate_cars = all_legacy_fingerprint_cars() - - -for addr, l in fingerprint.items(): - dat = messaging.new_message('can', 1) - - msg = dat.can[0] - msg.address = addr - msg.dat = " " * l - - candidate_cars = eliminate_incompatible_cars(msg, candidate_cars) - print(candidate_cars) diff --git a/selfdrive/debug/test_fw_query_on_routes.py b/selfdrive/debug/test_fw_query_on_routes.py deleted file mode 100755 index 0f9e316cd..000000000 --- a/selfdrive/debug/test_fw_query_on_routes.py +++ /dev/null @@ -1,197 +0,0 @@ -#!/usr/bin/env python3 -# type: ignore - -from collections import defaultdict -import argparse -import os -import traceback -from tqdm import tqdm -from tools.lib.logreader import LogReader -from tools.lib.route import Route -from selfdrive.car.car_helpers import interface_names -from selfdrive.car.fw_versions import match_fw_to_car_exact, match_fw_to_car_fuzzy, build_fw_dict -from selfdrive.car.toyota.values import FW_VERSIONS as TOYOTA_FW_VERSIONS -from selfdrive.car.honda.values import FW_VERSIONS as HONDA_FW_VERSIONS -from selfdrive.car.hyundai.values import FW_VERSIONS as HYUNDAI_FW_VERSIONS -from selfdrive.car.volkswagen.values import FW_VERSIONS as VW_FW_VERSIONS -from selfdrive.car.mazda.values import FW_VERSIONS as MAZDA_FW_VERSIONS -from selfdrive.car.subaru.values import FW_VERSIONS as SUBARU_FW_VERSIONS - - -NO_API = "NO_API" in os.environ -SUPPORTED_CARS = set(interface_names['toyota']) -SUPPORTED_CARS |= set(interface_names['honda']) -SUPPORTED_CARS |= set(interface_names['hyundai']) -SUPPORTED_CARS |= set(interface_names['volkswagen']) -SUPPORTED_CARS |= set(interface_names['mazda']) -SUPPORTED_CARS |= set(interface_names['subaru']) -SUPPORTED_CARS |= set(interface_names['nissan']) - -try: - from xx.pipeline.c.CarState import migration -except ImportError: - migration = {} - -if __name__ == "__main__": - parser = argparse.ArgumentParser(description='Run FW fingerprint on Qlog of route or list of routes') - parser.add_argument('route', help='Route or file with list of routes') - parser.add_argument('--car', help='Force comparison fingerprint to known car') - args = parser.parse_args() - - if os.path.exists(args.route): - routes = list(open(args.route)) - else: - routes = [args.route] - - mismatches = defaultdict(list) - - not_fingerprinted = 0 - solved_by_fuzzy = 0 - - good_exact = 0 - wrong_fuzzy = 0 - good_fuzzy = 0 - - dongles = [] - for route in tqdm(routes): - route = route.rstrip() - dongle_id, time = route.split('|') - - if dongle_id in dongles: - continue - - if NO_API: - qlog_path = f"cd:/{dongle_id}/{time}/0/qlog.bz2" - else: - route = Route(route) - qlog_path = route.qlog_paths()[0] - - if qlog_path is None: - continue - - try: - lr = LogReader(qlog_path) - dongles.append(dongle_id) - - for msg in lr: - if msg.which() == "pandaStates": - if msg.pandaStates[0].pandaType not in ('uno', 'blackPanda', 'dos'): - print("wrong panda type") - break - - elif msg.which() == "carParams": - bts = msg.carParams.as_builder().to_bytes() - - car_fw = msg.carParams.carFw - if len(car_fw) == 0: - print("no fw") - break - - live_fingerprint = msg.carParams.carFingerprint - live_fingerprint = migration.get(live_fingerprint, live_fingerprint) - - if args.car is not None: - live_fingerprint = args.car - - if live_fingerprint not in SUPPORTED_CARS: - print("not in supported cars") - break - - fw_versions_dict = build_fw_dict(car_fw) - exact_matches = match_fw_to_car_exact(fw_versions_dict) - fuzzy_matches = match_fw_to_car_fuzzy(fw_versions_dict) - - if (len(exact_matches) == 1) and (list(exact_matches)[0] == live_fingerprint): - good_exact += 1 - print(f"Correct! Live: {live_fingerprint} - Fuzzy: {fuzzy_matches}") - - # Check if fuzzy match was correct - if len(fuzzy_matches) == 1: - if list(fuzzy_matches)[0] != live_fingerprint: - wrong_fuzzy += 1 - print(f"{dongle_id}|{time}") - print("Fuzzy match wrong! Fuzzy:", fuzzy_matches, "Live:", live_fingerprint) - else: - good_fuzzy += 1 - break - - print(f"{dongle_id}|{time}") - print("Old style:", live_fingerprint, "Vin", msg.carParams.carVin) - print("New style (exact):", exact_matches) - print("New style (fuzzy):", fuzzy_matches) - - for version in car_fw: - subaddr = None if version.subAddress == 0 else hex(version.subAddress) - print(f" (Ecu.{version.ecu}, {hex(version.address)}, {subaddr}): [{version.fwVersion}],") - - print("Mismatches") - found = False - for car_fws in [TOYOTA_FW_VERSIONS, HONDA_FW_VERSIONS, HYUNDAI_FW_VERSIONS, VW_FW_VERSIONS, MAZDA_FW_VERSIONS, SUBARU_FW_VERSIONS]: - if live_fingerprint in car_fws: - found = True - expected = car_fws[live_fingerprint] - for (_, expected_addr, expected_sub_addr), v in expected.items(): - for version in car_fw: - sub_addr = None if version.subAddress == 0 else version.subAddress - addr = version.address - - if (addr, sub_addr) == (expected_addr, expected_sub_addr): - if version.fwVersion not in v: - print(f"({hex(addr)}, {'None' if sub_addr is None else hex(sub_addr)}) - {version.fwVersion}") - - # Add to global list of mismatches - mismatch = (addr, sub_addr, version.fwVersion) - if mismatch not in mismatches[live_fingerprint]: - mismatches[live_fingerprint].append(mismatch) - - # No FW versions for this car yet, add them all to mismatch list - if not found: - for version in car_fw: - sub_addr = None if version.subAddress == 0 else version.subAddress - addr = version.address - mismatch = (addr, sub_addr, version.fwVersion) - if mismatch not in mismatches[live_fingerprint]: - mismatches[live_fingerprint].append(mismatch) - - print() - not_fingerprinted += 1 - - if len(fuzzy_matches) == 1: - if list(fuzzy_matches)[0] == live_fingerprint: - solved_by_fuzzy += 1 - else: - wrong_fuzzy += 1 - print("Fuzzy match wrong! Fuzzy:", fuzzy_matches, "Live:", live_fingerprint) - - break - except Exception: - traceback.print_exc() - except KeyboardInterrupt: - break - - print() - # Print FW versions that need to be added seperated out by car and address - for car, m in sorted(mismatches.items()): - print(car) - addrs = defaultdict(list) - for (addr, sub_addr, version) in m: - addrs[(addr, sub_addr)].append(version) - - for (addr, sub_addr), versions in addrs.items(): - print(f" ({hex(addr)}, {'None' if sub_addr is None else hex(sub_addr)}): [") - for v in versions: - print(f" {v},") - print(" ]") - print() - - print() - print(f"Number of dongle ids checked: {len(dongles)}") - print(f"Fingerprinted: {good_exact}") - print(f"Not fingerprinted: {not_fingerprinted}") - print(f" of which had a fuzzy match: {solved_by_fuzzy}") - - print() - print(f"Correct fuzzy matches: {good_fuzzy}") - print(f"Wrong fuzzy matches: {wrong_fuzzy}") - print() - diff --git a/selfdrive/debug/toyota_eps_factor.py b/selfdrive/debug/toyota_eps_factor.py deleted file mode 100755 index 0a459bb71..000000000 --- a/selfdrive/debug/toyota_eps_factor.py +++ /dev/null @@ -1,64 +0,0 @@ -#!/usr/bin/env python3 -import sys -import numpy as np -import matplotlib.pyplot as plt -from sklearn import linear_model # pylint: disable=import-error -from selfdrive.car.toyota.values import STEER_THRESHOLD - -from tools.lib.route import Route -from tools.lib.logreader import MultiLogIterator - -MIN_SAMPLES = 30 * 100 - - -def to_signed(n, bits): - if n >= (1 << max((bits - 1), 0)): - n = n - (1 << max(bits, 0)) - return n - - -def get_eps_factor(lr, plot=False): - engaged = False - steering_pressed = False - torque_cmd, eps_torque = None, None - cmds, eps = [], [] - - for msg in lr: - if msg.which() != 'can': - continue - - for m in msg.can: - if m.address == 0x2e4 and m.src == 128: - engaged = bool(m.dat[0] & 1) - torque_cmd = to_signed((m.dat[1] << 8) | m.dat[2], 16) - elif m.address == 0x260 and m.src == 0: - eps_torque = to_signed((m.dat[5] << 8) | m.dat[6], 16) - steering_pressed = abs(to_signed((m.dat[1] << 8) | m.dat[2], 16)) > STEER_THRESHOLD - - if engaged and torque_cmd is not None and eps_torque is not None and not steering_pressed: - cmds.append(torque_cmd) - eps.append(eps_torque) - else: - if len(cmds) > MIN_SAMPLES: - break - cmds, eps = [], [] - - if len(cmds) < MIN_SAMPLES: - raise Exception("too few samples found in route") - - lm = linear_model.LinearRegression(fit_intercept=False) - lm.fit(np.array(cmds).reshape(-1, 1), eps) - scale_factor = 1. / lm.coef_[0] - - if plot: - plt.plot(np.array(eps) * scale_factor) - plt.plot(cmds) - plt.show() - return scale_factor - - -if __name__ == "__main__": - r = Route(sys.argv[1]) - lr = MultiLogIterator(r.log_paths()) - n = get_eps_factor(lr, plot="--plot" in sys.argv) - print("EPS torque factor: ", n) diff --git a/selfdrive/hardware b/selfdrive/hardware new file mode 120000 index 000000000..02a42c502 --- /dev/null +++ b/selfdrive/hardware @@ -0,0 +1 @@ +../system/hardware/ \ No newline at end of file diff --git a/selfdrive/locationd/calibrationd.py b/selfdrive/locationd/calibrationd.py index 6a8707a6c..81bcc6ce1 100755 --- a/selfdrive/locationd/calibrationd.py +++ b/selfdrive/locationd/calibrationd.py @@ -12,7 +12,7 @@ import capnp import numpy as np from typing import List, NoReturn, Optional -from cereal import car, log +from cereal import log import cereal.messaging as messaging from common.conversions import Conversions as CV from common.params import Params, put_nonblocking @@ -20,8 +20,7 @@ from common.realtime import set_realtime_priority from common.transformations.model import model_height from common.transformations.camera import get_view_frame_from_road_frame from common.transformations.orientation import rot_from_euler, euler_from_rot -from selfdrive.hardware import TICI -from selfdrive.swaglog import cloudlog +from system.swaglog import cloudlog MIN_SPEED_FILTER = 15 * CV.MPH_TO_MS MAX_VEL_ANGLE_STD = np.radians(0.25) @@ -63,12 +62,12 @@ class Calibrator: def __init__(self, param_put: bool = False): self.param_put = param_put - self.CP = car.CarParams.from_bytes(Params().get("CarParams", block=True)) + self.not_car = False # Read saved calibration params = Params() calibration_params = params.get("CalibrationParams") - self.wide_camera = TICI and params.get_bool('EnableWideCamera') + self.wide_camera = params.get_bool('WideCameraOnly') rpy_init = RPY_INIT valid_blocks = 0 @@ -193,7 +192,7 @@ class Calibrator: liveCalibration.rpyCalib = smooth_rpy.tolist() liveCalibration.rpyCalibSpread = self.calib_spread.tolist() - if self.CP.notCar: + if self.not_car: extrinsic_matrix = get_view_frame_from_road_frame(0, 0, 0, model_height) liveCalibration.validBlocks = INPUTS_NEEDED liveCalibration.calStatus = Calibration.CALIBRATED @@ -213,7 +212,7 @@ def calibrationd_thread(sm: Optional[messaging.SubMaster] = None, pm: Optional[m set_realtime_priority(1) if sm is None: - sm = messaging.SubMaster(['cameraOdometry', 'carState'], poll=['cameraOdometry']) + sm = messaging.SubMaster(['cameraOdometry', 'carState', 'carParams'], poll=['cameraOdometry']) if pm is None: pm = messaging.PubMaster(['liveCalibration']) @@ -224,6 +223,8 @@ def calibrationd_thread(sm: Optional[messaging.SubMaster] = None, pm: Optional[m timeout = 0 if sm.frame == -1 else 100 sm.update(timeout) + calibrator.not_car = sm['carParams'].notCar + if sm.updated['cameraOdometry']: calibrator.handle_v_ego(sm['carState'].vEgo) new_rpy = calibrator.handle_cam_odom(sm['cameraOdometry'].trans, diff --git a/selfdrive/locationd/laikad.py b/selfdrive/locationd/laikad.py new file mode 100755 index 000000000..4868e8ae5 --- /dev/null +++ b/selfdrive/locationd/laikad.py @@ -0,0 +1,322 @@ +#!/usr/bin/env python3 +import json +import math +import os +import time +from collections import defaultdict +from concurrent.futures import Future, ProcessPoolExecutor +from datetime import datetime +from enum import IntEnum +from typing import List, Optional + +import numpy as np + +from cereal import log, messaging +from common.params import Params, put_nonblocking +from laika import AstroDog +from laika.constants import SECS_IN_HR, SECS_IN_MIN +from laika.ephemeris import Ephemeris, EphemerisType, convert_ublox_ephem +from laika.gps_time import GPSTime +from laika.helpers import ConstellationId +from laika.raw_gnss import GNSSMeasurement, correct_measurements, process_measurements, read_raw_ublox +from selfdrive.locationd.laikad_helpers import calc_pos_fix_gauss_newton, get_posfix_sympy_fun +from selfdrive.locationd.models.constants import GENERATED_DIR, ObservationKind +from selfdrive.locationd.models.gnss_kf import GNSSKalman +from selfdrive.locationd.models.gnss_kf import States as GStates +from system.swaglog import cloudlog + +MAX_TIME_GAP = 10 +EPHEMERIS_CACHE = 'LaikadEphemeris' +DOWNLOADS_CACHE_FOLDER = "/tmp/comma_download_cache" +CACHE_VERSION = 0.1 +POS_FIX_RESIDUAL_THRESHOLD = 100.0 + + +class Laikad: + def __init__(self, valid_const=("GPS", "GLONASS"), auto_fetch_orbits=True, auto_update=False, + valid_ephem_types=(EphemerisType.ULTRA_RAPID_ORBIT, EphemerisType.NAV), + save_ephemeris=False): + """ + valid_const: GNSS constellation which can be used + auto_fetch_orbits: If true fetch orbits from internet when needed + auto_update: If true download AstroDog will download all files needed. This can be ephemeris or correction data like ionosphere. + valid_ephem_types: Valid ephemeris types to be used by AstroDog + save_ephemeris: If true saves and loads nav and orbit ephemeris to cache. + """ + self.astro_dog = AstroDog(valid_const=valid_const, auto_update=auto_update, valid_ephem_types=valid_ephem_types, clear_old_ephemeris=True, cache_dir=DOWNLOADS_CACHE_FOLDER) + self.gnss_kf = GNSSKalman(GENERATED_DIR, cython=True) + + self.auto_fetch_orbits = auto_fetch_orbits + self.orbit_fetch_executor: Optional[ProcessPoolExecutor] = None + self.orbit_fetch_future: Optional[Future] = None + + self.last_fetch_orbits_t = None + self.got_first_ublox_msg = False + self.last_cached_t = None + self.save_ephemeris = save_ephemeris + self.load_cache() + + self.posfix_functions = {constellation: get_posfix_sympy_fun(constellation) for constellation in (ConstellationId.GPS, ConstellationId.GLONASS)} + self.last_pos_fix = [] + self.last_pos_residual = [] + self.last_pos_fix_t = None + + def load_cache(self): + if not self.save_ephemeris: + return + + cache = Params().get(EPHEMERIS_CACHE) + if not cache: + return + + try: + cache = json.loads(cache, object_hook=deserialize_hook) + self.astro_dog.add_orbits(cache['orbits']) + self.astro_dog.add_navs(cache['nav']) + self.last_fetch_orbits_t = cache['last_fetch_orbits_t'] + except json.decoder.JSONDecodeError: + cloudlog.exception("Error parsing cache") + timestamp = self.last_fetch_orbits_t.as_datetime() if self.last_fetch_orbits_t is not None else 'Nan' + cloudlog.debug( + f"Loaded nav and orbits cache with timestamp: {timestamp}. Unique orbit and nav sats: {list(cache['orbits'].keys())} {list(cache['nav'].keys())} " + + f"Total: {sum([len(v) for v in cache['orbits']])} and {sum([len(v) for v in cache['nav']])}") + + def cache_ephemeris(self, t: GPSTime): + if self.save_ephemeris and (self.last_cached_t is None or t - self.last_cached_t > SECS_IN_MIN): + put_nonblocking(EPHEMERIS_CACHE, json.dumps( + {'version': CACHE_VERSION, 'last_fetch_orbits_t': self.last_fetch_orbits_t, 'orbits': self.astro_dog.orbits, 'nav': self.astro_dog.nav}, + cls=CacheSerializer)) + cloudlog.debug("Cache saved") + self.last_cached_t = t + + def get_est_pos(self, t, processed_measurements): + if self.last_pos_fix_t is None or abs(self.last_pos_fix_t - t) >= 2: + min_measurements = 6 if any(p.constellation_id == ConstellationId.GLONASS for p in processed_measurements) else 5 + pos_fix, pos_fix_residual = calc_pos_fix_gauss_newton(processed_measurements, self.posfix_functions, min_measurements=min_measurements) + if len(pos_fix) > 0 and np.median(np.abs(pos_fix_residual)) < POS_FIX_RESIDUAL_THRESHOLD: + self.last_pos_fix = pos_fix[:3] + self.last_pos_residual = pos_fix_residual + self.last_pos_fix_t = t + return self.last_pos_fix + + def process_ublox_msg(self, ublox_msg, ublox_mono_time: int, block=False): + if ublox_msg.which == 'measurementReport': + t = ublox_mono_time * 1e-9 + report = ublox_msg.measurementReport + if report.gpsWeek > 0: + self.got_first_ublox_msg = True + latest_msg_t = GPSTime(report.gpsWeek, report.rcvTow) + if self.auto_fetch_orbits: + self.fetch_orbits(latest_msg_t, block) + + new_meas = read_raw_ublox(report) + # Filter measurements with unexpected pseudoranges for GPS and GLONASS satellites + new_meas = [m for m in new_meas if 1e7 < m.observables['C1C'] < 3e7] + + processed_measurements = process_measurements(new_meas, self.astro_dog) + + est_pos = self.get_est_pos(t, processed_measurements) + + corrected_measurements = correct_measurements(processed_measurements, est_pos, self.astro_dog) if len(est_pos) > 0 else [] + + self.update_localizer(est_pos, t, corrected_measurements) + kf_valid = all(self.kf_valid(t)) + ecef_pos = self.gnss_kf.x[GStates.ECEF_POS] + ecef_vel = self.gnss_kf.x[GStates.ECEF_VELOCITY] + + p = self.gnss_kf.P.diagonal() + pos_std = np.sqrt(p[GStates.ECEF_POS]) + vel_std = np.sqrt(p[GStates.ECEF_VELOCITY]) + + meas_msgs = [create_measurement_msg(m) for m in corrected_measurements] + dat = messaging.new_message("gnssMeasurements") + measurement_msg = log.LiveLocationKalman.Measurement.new_message + dat.gnssMeasurements = { + "gpsWeek": report.gpsWeek, + "gpsTimeOfWeek": report.rcvTow, + "positionECEF": measurement_msg(value=ecef_pos.tolist(), std=pos_std.tolist(), valid=kf_valid), + "velocityECEF": measurement_msg(value=ecef_vel.tolist(), std=vel_std.tolist(), valid=kf_valid), + "positionFixECEF": measurement_msg(value=self.last_pos_fix, std=self.last_pos_residual, valid=self.last_pos_fix_t == t), + "ubloxMonoTime": ublox_mono_time, + "correctedMeasurements": meas_msgs + } + return dat + elif ublox_msg.which == 'ephemeris': + ephem = convert_ublox_ephem(ublox_msg.ephemeris) + self.astro_dog.add_navs({ephem.prn: [ephem]}) + self.cache_ephemeris(t=ephem.epoch) + # elif ublox_msg.which == 'ionoData': + # todo add this. Needed to better correct messages offline. First fix ublox_msg.cc to sent them. + + def update_localizer(self, est_pos, t: float, measurements: List[GNSSMeasurement]): + # Check time and outputs are valid + valid = self.kf_valid(t) + if not all(valid): + if not valid[0]: # Filter not initialized + pass + elif not valid[1]: + cloudlog.error("Time gap of over 10s detected, gnss kalman reset") + elif not valid[2]: + cloudlog.error("Gnss kalman filter state is nan") + if len(est_pos) > 0: + cloudlog.info(f"Reset kalman filter with {est_pos}") + self.init_gnss_localizer(est_pos) + else: + return + if len(measurements) > 0: + kf_add_observations(self.gnss_kf, t, measurements) + else: + # Ensure gnss filter is updated even with no new measurements + self.gnss_kf.predict(t) + + def kf_valid(self, t: float) -> List[bool]: + filter_time = self.gnss_kf.filter.get_filter_time() + return [not math.isnan(filter_time), + abs(t - filter_time) < MAX_TIME_GAP, + all(np.isfinite(self.gnss_kf.x[GStates.ECEF_POS]))] + + def init_gnss_localizer(self, est_pos): + x_initial, p_initial_diag = np.copy(GNSSKalman.x_initial), np.copy(np.diagonal(GNSSKalman.P_initial)) + x_initial[GStates.ECEF_POS] = est_pos + p_initial_diag[GStates.ECEF_POS] = 1000 ** 2 + self.gnss_kf.init_state(x_initial, covs_diag=p_initial_diag) + + def fetch_orbits(self, t: GPSTime, block): + # Download new orbits if 1 hour of orbits data left + if t + SECS_IN_HR not in self.astro_dog.orbit_fetched_times and (self.last_fetch_orbits_t is None or abs(t - self.last_fetch_orbits_t) > SECS_IN_MIN): + astro_dog_vars = self.astro_dog.valid_const, self.astro_dog.auto_update, self.astro_dog.valid_ephem_types, self.astro_dog.cache_dir + ret = None + + if block: # Used for testing purposes + ret = get_orbit_data(t, *astro_dog_vars) + elif self.orbit_fetch_future is None: + self.orbit_fetch_executor = ProcessPoolExecutor(max_workers=1) + self.orbit_fetch_future = self.orbit_fetch_executor.submit(get_orbit_data, t, *astro_dog_vars) + elif self.orbit_fetch_future.done(): + ret = self.orbit_fetch_future.result() + self.orbit_fetch_executor = self.orbit_fetch_future = None + + if ret is not None: + if ret[0] is None: + self.last_fetch_orbits_t = ret[2] + else: + self.astro_dog.orbits, self.astro_dog.orbit_fetched_times, self.last_fetch_orbits_t = ret + self.cache_ephemeris(t=t) + + +def get_orbit_data(t: GPSTime, valid_const, auto_update, valid_ephem_types, cache_dir): + astro_dog = AstroDog(valid_const=valid_const, auto_update=auto_update, valid_ephem_types=valid_ephem_types, cache_dir=cache_dir) + cloudlog.info(f"Start to download/parse orbits for time {t.as_datetime()}") + start_time = time.monotonic() + try: + astro_dog.get_orbit_data(t, only_predictions=True) + cloudlog.info(f"Done parsing orbits. Took {time.monotonic() - start_time:.1f}s") + return astro_dog.orbits, astro_dog.orbit_fetched_times, t + except (RuntimeError, ValueError, IOError) as e: + cloudlog.warning(f"No orbit data found or parsing failure: {e}") + return None, None, t + + +def create_measurement_msg(meas: GNSSMeasurement): + c = log.GnssMeasurements.CorrectedMeasurement.new_message() + c.constellationId = meas.constellation_id.value + c.svId = meas.sv_id + c.glonassFrequency = meas.glonass_freq if meas.constellation_id == ConstellationId.GLONASS else 0 + c.pseudorange = float(meas.observables_final['C1C']) + c.pseudorangeStd = float(meas.observables_std['C1C']) + c.pseudorangeRate = float(meas.observables_final['D1C']) + c.pseudorangeRateStd = float(meas.observables_std['D1C']) + c.satPos = meas.sat_pos_final.tolist() + c.satVel = meas.sat_vel.tolist() + c.satVel = meas.sat_vel.tolist() + ephem = meas.sat_ephemeris + assert ephem is not None + if ephem.eph_type == EphemerisType.NAV: + source_type = EphemerisSourceType.nav + week, time_of_week = -1, -1 + else: + assert ephem.file_epoch is not None + week = ephem.file_epoch.week + time_of_week = ephem.file_epoch.tow + file_src = ephem.file_source + if file_src == 'igu': # example nasa: '2214/igu22144_00.sp3.Z' + source_type = EphemerisSourceType.nasaUltraRapid + elif file_src == 'Sta': # example nasa: '22166/ultra/Stark_1D_22061518.sp3' + source_type = EphemerisSourceType.glonassIacUltraRapid + else: + raise Exception(f"Didn't expect file source {file_src}") + + c.ephemerisSource.type = source_type.value + c.ephemerisSource.gpsWeek = week + c.ephemerisSource.gpsTimeOfWeek = int(time_of_week) + return c + + +def kf_add_observations(gnss_kf: GNSSKalman, t: float, measurements: List[GNSSMeasurement]): + ekf_data = defaultdict(list) + for m in measurements: + m_arr = m.as_array() + if m.constellation_id == ConstellationId.GPS: + ekf_data[ObservationKind.PSEUDORANGE_GPS].append(m_arr) + elif m.constellation_id == ConstellationId.GLONASS: + ekf_data[ObservationKind.PSEUDORANGE_GLONASS].append(m_arr) + ekf_data[ObservationKind.PSEUDORANGE_RATE_GPS] = ekf_data[ObservationKind.PSEUDORANGE_GPS] + ekf_data[ObservationKind.PSEUDORANGE_RATE_GLONASS] = ekf_data[ObservationKind.PSEUDORANGE_GLONASS] + for kind, data in ekf_data.items(): + if len(data) > 0: + gnss_kf.predict_and_observe(t, kind, data) + + +class CacheSerializer(json.JSONEncoder): + + def default(self, o): + if isinstance(o, Ephemeris): + return o.to_json() + if isinstance(o, GPSTime): + return o.__dict__ + if isinstance(o, np.ndarray): + return o.tolist() + return json.JSONEncoder.default(self, o) + + +def deserialize_hook(dct): + if 'ephemeris' in dct: + return Ephemeris.from_json(dct) + if 'week' in dct: + return GPSTime(dct['week'], dct['tow']) + return dct + + +class EphemerisSourceType(IntEnum): + nav = 0 + nasaUltraRapid = 1 + glonassIacUltraRapid = 2 + + +def main(sm=None, pm=None): + if sm is None: + sm = messaging.SubMaster(['ubloxGnss', 'clocks']) + if pm is None: + pm = messaging.PubMaster(['gnssMeasurements']) + + replay = "REPLAY" in os.environ + use_internet = "LAIKAD_NO_INTERNET" not in os.environ + laikad = Laikad(save_ephemeris=not replay, auto_fetch_orbits=use_internet) + + while True: + sm.update() + + if sm.updated['ubloxGnss']: + ublox_msg = sm['ubloxGnss'] + msg = laikad.process_ublox_msg(ublox_msg, sm.logMonoTime['ubloxGnss'], block=replay) + if msg is not None: + pm.send('gnssMeasurements', msg) + if not laikad.got_first_ublox_msg and sm.updated['clocks']: + clocks_msg = sm['clocks'] + t = GPSTime.from_datetime(datetime.utcfromtimestamp(clocks_msg.wallTimeNanos * 1E-9)) + if laikad.auto_fetch_orbits: + laikad.fetch_orbits(t, block=replay) + + +if __name__ == "__main__": + main() diff --git a/selfdrive/locationd/laikad_helpers.py b/selfdrive/locationd/laikad_helpers.py new file mode 100644 index 000000000..f13e8e73b --- /dev/null +++ b/selfdrive/locationd/laikad_helpers.py @@ -0,0 +1,89 @@ +import numpy as np +import sympy + +from laika.constants import EARTH_ROTATION_RATE, SPEED_OF_LIGHT +from laika.helpers import ConstellationId + + +def calc_pos_fix_gauss_newton(measurements, posfix_functions, x0=None, signal='C1C', min_measurements=6): + ''' + Calculates gps fix using gauss newton method + To solve the problem a minimal of 4 measurements are required. + If Glonass is included 5 are required to solve for the additional free variable. + returns: + 0 -> list with positions + ''' + if x0 is None: + x0 = [0, 0, 0, 0, 0] + n = len(measurements) + if n < min_measurements: + return [], [] + + Fx_pos = pr_residual(measurements, posfix_functions, signal=signal) + x = gauss_newton(Fx_pos, x0) + residual, _ = Fx_pos(x, weight=1.0) + return x.tolist(), residual.tolist() + + +def pr_residual(measurements, posfix_functions, signal='C1C'): + def Fx_pos(inp, weight=None): + vals, gradients = [], [] + + for meas in measurements: + pr = meas.observables[signal] + pr += meas.sat_clock_err * SPEED_OF_LIGHT + + w = (1 / meas.observables_std[signal]) if weight is None else weight + + val, *gradient = posfix_functions[meas.constellation_id](*inp, pr, *meas.sat_pos, w) + vals.append(val) + gradients.append(gradient) + return np.asarray(vals), np.asarray(gradients) + + return Fx_pos + + +def gauss_newton(fun, b, xtol=1e-8, max_n=25): + for _ in range(max_n): + # Compute function and jacobian on current estimate + r, J = fun(b) + + # Update estimate + delta = np.linalg.pinv(J) @ r + b -= delta + + # Check step size for stopping condition + if np.linalg.norm(delta) < xtol: + break + return b + + +def get_posfix_sympy_fun(constellation): + # Unknowns + x, y, z = sympy.Symbol('x'), sympy.Symbol('y'), sympy.Symbol('z') + bc = sympy.Symbol('bc') + bg = sympy.Symbol('bg') + var = [x, y, z, bc, bg] + + # Knowns + pr = sympy.Symbol('pr') + sat_x, sat_y, sat_z = sympy.Symbol('sat_x'), sympy.Symbol('sat_y'), sympy.Symbol('sat_z') + weight = sympy.Symbol('weight') + + theta = EARTH_ROTATION_RATE * (pr - bc) / SPEED_OF_LIGHT + val = sympy.sqrt( + (sat_x * sympy.cos(theta) + sat_y * sympy.sin(theta) - x) ** 2 + + (sat_y * sympy.cos(theta) - sat_x * sympy.sin(theta) - y) ** 2 + + (sat_z - z) ** 2 + ) + + if constellation == ConstellationId.GLONASS: + res = weight * (val - (pr - bc - bg)) + elif constellation == ConstellationId.GPS: + res = weight * (val - (pr - bc)) + else: + raise NotImplementedError(f"Constellation {constellation} not supported") + + res = [res] + [sympy.diff(res, v) for v in var] + + return sympy.lambdify([x, y, z, bc, bg, pr, sat_x, sat_y, sat_z, weight], res, modules=["numpy"]) diff --git a/selfdrive/debug/__init__.py b/selfdrive/locationd/models/__init__.py similarity index 100% rename from selfdrive/debug/__init__.py rename to selfdrive/locationd/models/__init__.py diff --git a/selfdrive/locationd/models/car_kf.py b/selfdrive/locationd/models/car_kf.py index 75534efa5..3faf4f8d4 100755 --- a/selfdrive/locationd/models/car_kf.py +++ b/selfdrive/locationd/models/car_kf.py @@ -7,7 +7,7 @@ import numpy as np from selfdrive.controls.lib.vehicle_model import ACCELERATION_DUE_TO_GRAVITY from selfdrive.locationd.models.constants import ObservationKind -from selfdrive.swaglog import cloudlog +from system.swaglog import cloudlog from rednose.helpers.kalmanfilter import KalmanFilter @@ -15,7 +15,7 @@ if __name__ == '__main__': # Generating sympy import sympy as sp from rednose.helpers.ekf_sym import gen_code else: - from rednose.helpers.ekf_sym_pyx import EKF_sym # pylint: disable=no-name-in-module, import-error + from rednose.helpers.ekf_sym_pyx import EKF_sym_pyx # pylint: disable=no-name-in-module, import-error i = 0 @@ -171,7 +171,7 @@ class CarKalman(KalmanFilter): if P_initial is not None: self.P_initial = P_initial # init filter - self.filter = EKF_sym(generated_dir, self.name, self.Q, self.initial_x, self.P_initial, dim_state, dim_state_err, global_vars=self.global_vars, logger=cloudlog) + self.filter = EKF_sym_pyx(generated_dir, self.name, self.Q, self.initial_x, self.P_initial, dim_state, dim_state_err, global_vars=self.global_vars, logger=cloudlog) if __name__ == "__main__": diff --git a/selfdrive/locationd/models/gnss_helpers.py b/selfdrive/locationd/models/gnss_helpers.py new file mode 100644 index 000000000..b6c1771ec --- /dev/null +++ b/selfdrive/locationd/models/gnss_helpers.py @@ -0,0 +1,19 @@ +import numpy as np +from laika.raw_gnss import GNSSMeasurement + +def parse_prr(m): + sat_pos_vel_i = np.concatenate((m[GNSSMeasurement.SAT_POS], + m[GNSSMeasurement.SAT_VEL])) + R_i = np.atleast_2d(m[GNSSMeasurement.PRR_STD]**2) + z_i = m[GNSSMeasurement.PRR] + return z_i, R_i, sat_pos_vel_i + +def parse_pr(m): + pseudorange = m[GNSSMeasurement.PR] + pseudorange_stdev = m[GNSSMeasurement.PR_STD] + sat_pos_freq_i = np.concatenate((m[GNSSMeasurement.SAT_POS], + np.array([m[GNSSMeasurement.GLONASS_FREQ]]))) + z_i = np.atleast_1d(pseudorange) + R_i = np.atleast_2d(pseudorange_stdev**2) + return z_i, R_i, sat_pos_freq_i + diff --git a/selfdrive/locationd/models/gnss_kf.py b/selfdrive/locationd/models/gnss_kf.py new file mode 100755 index 000000000..5c9cb4b3d --- /dev/null +++ b/selfdrive/locationd/models/gnss_kf.py @@ -0,0 +1,189 @@ +#!/usr/bin/env python3 +import sys +from typing import List + +import numpy as np + +from selfdrive.locationd.models.constants import ObservationKind +from selfdrive.locationd.models.gnss_helpers import parse_pr, parse_prr + +if __name__ == '__main__': # Generating sympy + import sympy as sp + from rednose.helpers.ekf_sym import gen_code +else: + from rednose.helpers.ekf_sym_pyx import EKF_sym_pyx # pylint: disable=no-name-in-module,import-error + from rednose.helpers.ekf_sym import EKF_sym # pylint: disable=no-name-in-module,import-error + + +class States(): + ECEF_POS = slice(0, 3) # x, y and z in ECEF in meters + ECEF_VELOCITY = slice(3, 6) + CLOCK_BIAS = slice(6, 7) # clock bias in light-meters, + CLOCK_DRIFT = slice(7, 8) # clock drift in light-meters/s, + CLOCK_ACCELERATION = slice(8, 9) # clock acceleration in light-meters/s**2 + GLONASS_BIAS = slice(9, 10) # clock drift in light-meters/s, + GLONASS_FREQ_SLOPE = slice(10, 11) # GLONASS bias in m expressed as bias + freq_num*freq_slope + + +class GNSSKalman(): + name = 'gnss' + + x_initial = np.array([-2712700.6008, -4281600.6679, 3859300.1830, + 0, 0, 0, + 0, 0, 0, + 0, 0]) + + # state covariance + P_initial = np.diag([1e16, 1e16, 1e16, + 10**2, 10**2, 10**2, + 1e14, (100)**2, (0.2)**2, + (10)**2, (1)**2]) + + # process noise + Q = np.diag([0.03**2, 0.03**2, 0.03**2, + 3**2, 3**2, 3**2, + (.1)**2, (0)**2, (0.005)**2, + .1**2, (.01)**2]) + + maha_test_kinds: List[int] = [] # ObservationKind.PSEUDORANGE_RATE, ObservationKind.PSEUDORANGE, ObservationKind.PSEUDORANGE_GLONASS] + + @staticmethod + def generate_code(generated_dir): + dim_state = GNSSKalman.x_initial.shape[0] + name = GNSSKalman.name + maha_test_kinds = GNSSKalman.maha_test_kinds + + # make functions and jacobians with sympy + # state variables + state_sym = sp.MatrixSymbol('state', dim_state, 1) + state = sp.Matrix(state_sym) + x, y, z = state[0:3, :] + v = state[3:6, :] + vx, vy, vz = v + cb, cd, ca = state[6:9, :] + glonass_bias, glonass_freq_slope = state[9:11, :] + + dt = sp.Symbol('dt') + + state_dot = sp.Matrix(np.zeros((dim_state, 1))) + state_dot[:3, :] = v + state_dot[6, 0] = cd + state_dot[7, 0] = ca + + # Basic descretization, 1st order integrator + # Can be pretty bad if dt is big + f_sym = state + dt * state_dot + + # + # Observation functions + # + + # extra args + sat_pos_freq_sym = sp.MatrixSymbol('sat_pos', 4, 1) + sat_pos_vel_sym = sp.MatrixSymbol('sat_pos_vel', 6, 1) + # sat_los_sym = sp.MatrixSymbol('sat_los', 3, 1) + # orb_epos_sym = sp.MatrixSymbol('orb_epos_sym', 3, 1) + + # expand extra args + sat_x, sat_y, sat_z, glonass_freq = sat_pos_freq_sym + sat_vx, sat_vy, sat_vz = sat_pos_vel_sym[3:] + # los_x, los_y, los_z = sat_los_sym + # orb_x, orb_y, orb_z = orb_epos_sym + + h_pseudorange_sym = sp.Matrix([ + sp.sqrt( + (x - sat_x)**2 + + (y - sat_y)**2 + + (z - sat_z)**2 + ) + cb + ]) + + h_pseudorange_glonass_sym = sp.Matrix([ + sp.sqrt( + (x - sat_x)**2 + + (y - sat_y)**2 + + (z - sat_z)**2 + ) + cb + glonass_bias + glonass_freq_slope * glonass_freq + ]) + + los_vector = (sp.Matrix(sat_pos_vel_sym[0:3]) - sp.Matrix([x, y, z])) + los_vector = los_vector / sp.sqrt(los_vector[0]**2 + los_vector[1]**2 + los_vector[2]**2) + h_pseudorange_rate_sym = sp.Matrix([los_vector[0] * (sat_vx - vx) + + los_vector[1] * (sat_vy - vy) + + los_vector[2] * (sat_vz - vz) + + cd]) + + obs_eqs = [[h_pseudorange_sym, ObservationKind.PSEUDORANGE_GPS, sat_pos_freq_sym], + [h_pseudorange_glonass_sym, ObservationKind.PSEUDORANGE_GLONASS, sat_pos_freq_sym], + [h_pseudorange_rate_sym, ObservationKind.PSEUDORANGE_RATE_GPS, sat_pos_vel_sym], + [h_pseudorange_rate_sym, ObservationKind.PSEUDORANGE_RATE_GLONASS, sat_pos_vel_sym]] + + gen_code(generated_dir, name, f_sym, dt, state_sym, obs_eqs, dim_state, dim_state, maha_test_kinds=maha_test_kinds) + + def __init__(self, generated_dir, cython=False): + self.dim_state = self.x_initial.shape[0] + + # init filter + filter_cls = EKF_sym_pyx if cython else EKF_sym + self.filter = filter_cls(generated_dir, self.name, self.Q, self.x_initial, self.P_initial, self.dim_state, + self.dim_state, maha_test_kinds=self.maha_test_kinds) + self.init_state(GNSSKalman.x_initial, covs=GNSSKalman.P_initial) + + @property + def x(self): + return self.filter.state() + + @property + def P(self): + return self.filter.covs() + + def predict(self, t): + return self.filter.predict(t) + + def rts_smooth(self, estimates): + return self.filter.rts_smooth(estimates, norm_quats=False) + + def init_state(self, state, covs_diag=None, covs=None, filter_time=None): + if covs_diag is not None: + P = np.diag(covs_diag) + elif covs is not None: + P = covs + else: + P = self.filter.covs() + self.filter.init_state(state, P, filter_time) + + def predict_and_observe(self, t, kind, data): + if len(data) > 0: + data = np.atleast_2d(data) + if kind == ObservationKind.PSEUDORANGE_GPS or kind == ObservationKind.PSEUDORANGE_GLONASS: + r = self.predict_and_update_pseudorange(data, t, kind) + elif kind == ObservationKind.PSEUDORANGE_RATE_GPS or kind == ObservationKind.PSEUDORANGE_RATE_GLONASS: + r = self.predict_and_update_pseudorange_rate(data, t, kind) + return r + + def predict_and_update_pseudorange(self, meas, t, kind): + R = np.zeros((len(meas), 1, 1)) + sat_pos_freq = np.zeros((len(meas), 4)) + z = np.zeros((len(meas), 1)) + for i, m in enumerate(meas): + z_i, R_i, sat_pos_freq_i = parse_pr(m) + sat_pos_freq[i, :] = sat_pos_freq_i + z[i, :] = z_i + R[i, :, :] = R_i + return self.filter.predict_and_update_batch(t, kind, z, R, sat_pos_freq) + + def predict_and_update_pseudorange_rate(self, meas, t, kind): + R = np.zeros((len(meas), 1, 1)) + z = np.zeros((len(meas), 1)) + sat_pos_vel = np.zeros((len(meas), 6)) + for i, m in enumerate(meas): + z_i, R_i, sat_pos_vel_i = parse_prr(m) + sat_pos_vel[i] = sat_pos_vel_i + R[i, :, :] = R_i + z[i, :] = z_i + return self.filter.predict_and_update_batch(t, kind, z, R, sat_pos_vel) + + +if __name__ == "__main__": + generated_dir = sys.argv[2] + GNSSKalman.generate_code(generated_dir) diff --git a/selfdrive/locationd/paramsd.py b/selfdrive/locationd/paramsd.py index 0e83728e5..ae67dc28a 100755 --- a/selfdrive/locationd/paramsd.py +++ b/selfdrive/locationd/paramsd.py @@ -10,7 +10,7 @@ from common.realtime import config_realtime_process, DT_MDL from common.numpy_fast import clip from selfdrive.locationd.models.car_kf import CarKalman, ObservationKind, States from selfdrive.locationd.models.constants import GENERATED_DIR -from selfdrive.swaglog import cloudlog +from system.swaglog import cloudlog MAX_ANGLE_OFFSET_DELTA = 20 * DT_MDL # Max 20 deg/s diff --git a/selfdrive/loggerd/.gitignore b/selfdrive/loggerd/.gitignore new file mode 100644 index 000000000..53dc24e6f --- /dev/null +++ b/selfdrive/loggerd/.gitignore @@ -0,0 +1,4 @@ +loggerd +encoderd +bootlog +tests/test_logger diff --git a/selfdrive/loggerd/bootlog.cc b/selfdrive/loggerd/bootlog.cc index eee6b8672..882b749fe 100644 --- a/selfdrive/loggerd/bootlog.cc +++ b/selfdrive/loggerd/bootlog.cc @@ -8,7 +8,7 @@ static kj::Array build_boot_log() { std::vector bootlog_commands; - if (Hardware::TICI()) { + if (Hardware::AGNOS()) { bootlog_commands.push_back("journalctl"); bootlog_commands.push_back("sudo nvme smart-log --output-format=json /dev/nvme0"); } diff --git a/selfdrive/loggerd/config.py b/selfdrive/loggerd/config.py index 6cd20a68a..168c9fba9 100644 --- a/selfdrive/loggerd/config.py +++ b/selfdrive/loggerd/config.py @@ -1,6 +1,6 @@ import os from pathlib import Path -from selfdrive.hardware import PC +from system.hardware import PC if os.environ.get('LOG_ROOT', False): ROOT = os.environ['LOG_ROOT'] diff --git a/selfdrive/loggerd/deleter.py b/selfdrive/loggerd/deleter.py index d745e91fb..560628802 100644 --- a/selfdrive/loggerd/deleter.py +++ b/selfdrive/loggerd/deleter.py @@ -2,7 +2,7 @@ import os import shutil import threading -from selfdrive.swaglog import cloudlog +from system.swaglog import cloudlog from selfdrive.loggerd.config import ROOT, get_available_bytes, get_available_percent from selfdrive.loggerd.uploader import listdir_by_creation diff --git a/selfdrive/loggerd/encoder/encoder.h b/selfdrive/loggerd/encoder/encoder.h index 9792d61c4..21ef65cf1 100644 --- a/selfdrive/loggerd/encoder/encoder.h +++ b/selfdrive/loggerd/encoder/encoder.h @@ -8,7 +8,7 @@ #include "cereal/visionipc/visionipc.h" #include "common/queue.h" #include "selfdrive/loggerd/video_writer.h" -#include "selfdrive/camerad/cameras/camera_common.h" +#include "system/camerad/cameras/camera_common.h" #define V4L2_BUF_FLAG_KEYFRAME 8 @@ -19,8 +19,7 @@ public: : filename(filename), type(type), in_width(in_width), in_height(in_height), fps(fps), bitrate(bitrate), codec(codec), out_width(out_width), out_height(out_height), write(write) { } virtual ~VideoEncoder(); - virtual int encode_frame(const uint8_t *y_ptr, const uint8_t *u_ptr, const uint8_t *v_ptr, - int in_width, int in_height, VisionIpcBufExtra *extra) = 0; + virtual int encode_frame(VisionBuf* buf, VisionIpcBufExtra *extra) = 0; virtual void encoder_open(const char* path) = 0; virtual void encoder_close() = 0; diff --git a/selfdrive/loggerd/encoder/ffmpeg_encoder.cc b/selfdrive/loggerd/encoder/ffmpeg_encoder.cc index a2b14ef3f..5f8d140e8 100644 --- a/selfdrive/loggerd/encoder/ffmpeg_encoder.cc +++ b/selfdrive/loggerd/encoder/ffmpeg_encoder.cc @@ -34,6 +34,8 @@ void FfmpegEncoder::encoder_init() { frame->linesize[1] = out_width/2; frame->linesize[2] = out_width/2; + convert_buf.resize(in_width * in_height * 3 / 2); + if (in_width != out_width || in_height != out_height) { downscale_buf.resize(out_width * out_height * 3 / 2); } @@ -66,22 +68,33 @@ void FfmpegEncoder::encoder_open(const char* path) { void FfmpegEncoder::encoder_close() { if (!is_open) return; + writer_close(); + avcodec_free_context(&codec_ctx); is_open = false; } -int FfmpegEncoder::encode_frame(const uint8_t *y_ptr, const uint8_t *u_ptr, const uint8_t *v_ptr, - int in_width_, int in_height_, VisionIpcBufExtra *extra) { - assert(in_width_ == this->in_width); - assert(in_height_ == this->in_height); +int FfmpegEncoder::encode_frame(VisionBuf* buf, VisionIpcBufExtra *extra) { + assert(buf->width == this->in_width); + assert(buf->height == this->in_height); + + uint8_t *cy = convert_buf.data(); + uint8_t *cu = cy + in_width * in_height; + uint8_t *cv = cu + (in_width / 2) * (in_height / 2); + libyuv::NV12ToI420(buf->y, buf->stride, + buf->uv, buf->stride, + cy, in_width, + cu, in_width/2, + cv, in_width/2, + in_width, in_height); if (downscale_buf.size() > 0) { uint8_t *out_y = downscale_buf.data(); uint8_t *out_u = out_y + frame->width * frame->height; uint8_t *out_v = out_u + (frame->width / 2) * (frame->height / 2); - libyuv::I420Scale(y_ptr, in_width, - u_ptr, in_width/2, - v_ptr, in_width/2, + libyuv::I420Scale(cy, in_width, + cu, in_width/2, + cv, in_width/2, in_width, in_height, out_y, frame->width, out_u, frame->width/2, @@ -92,9 +105,9 @@ int FfmpegEncoder::encode_frame(const uint8_t *y_ptr, const uint8_t *u_ptr, cons frame->data[1] = out_u; frame->data[2] = out_v; } else { - frame->data[0] = (uint8_t*)y_ptr; - frame->data[1] = (uint8_t*)u_ptr; - frame->data[2] = (uint8_t*)v_ptr; + frame->data[0] = cy; + frame->data[1] = cu; + frame->data[2] = cv; } frame->pts = counter*50*1000; // 50ms per frame diff --git a/selfdrive/loggerd/encoder/ffmpeg_encoder.h b/selfdrive/loggerd/encoder/ffmpeg_encoder.h index ae41abc69..497a28b65 100644 --- a/selfdrive/loggerd/encoder/ffmpeg_encoder.h +++ b/selfdrive/loggerd/encoder/ffmpeg_encoder.h @@ -21,8 +21,7 @@ class FfmpegEncoder : public VideoEncoder { VideoEncoder(filename, type, in_width, in_height, fps, bitrate, cereal::EncodeIndex::Type::BIG_BOX_LOSSLESS, out_width, out_height, write) { encoder_init(); } ~FfmpegEncoder(); void encoder_init(); - int encode_frame(const uint8_t *y_ptr, const uint8_t *u_ptr, const uint8_t *v_ptr, - int in_width_, int in_height_, VisionIpcBufExtra *extra); + int encode_frame(VisionBuf* buf, VisionIpcBufExtra *extra); void encoder_open(const char* path); void encoder_close(); @@ -33,5 +32,6 @@ private: AVCodecContext *codec_ctx; AVFrame *frame = NULL; + std::vector convert_buf; std::vector downscale_buf; }; diff --git a/selfdrive/loggerd/encoder/v4l_encoder.cc b/selfdrive/loggerd/encoder/v4l_encoder.cc index 5fb9cc8de..b3bd692b1 100644 --- a/selfdrive/loggerd/encoder/v4l_encoder.cc +++ b/selfdrive/loggerd/encoder/v4l_encoder.cc @@ -248,7 +248,6 @@ void V4LEncoder::encoder_init() { } // queue up input buffers for (unsigned int i = 0; i < BUF_IN_COUNT; i++) { - buf_in[i].allocate(fmt_in.fmt.pix_mp.plane_fmt[0].sizeimage); free_buf_in.push(i); } @@ -262,41 +261,19 @@ void V4LEncoder::encoder_open(const char* path) { this->counter = 0; } -int V4LEncoder::encode_frame(const uint8_t *y_ptr, const uint8_t *u_ptr, const uint8_t *v_ptr, - int in_width_, int in_height_, VisionIpcBufExtra *extra) { - assert(in_width == in_width_); - assert(in_height == in_height_); - assert(is_open); - - // reserve buffer - int buffer_in = free_buf_in.pop(); - - uint8_t *in_y_ptr = (uint8_t*)buf_in[buffer_in].addr; - int in_y_stride = VENUS_Y_STRIDE(COLOR_FMT_NV12, in_width); - int in_uv_stride = VENUS_UV_STRIDE(COLOR_FMT_NV12, in_width); - uint8_t *in_uv_ptr = in_y_ptr + (in_y_stride * VENUS_Y_SCANLINES(COLOR_FMT_NV12, in_height)); - - - // GRRR COPY - int err = libyuv::I420ToNV12(y_ptr, in_width, - u_ptr, in_width/2, - v_ptr, in_width/2, - in_y_ptr, in_y_stride, - in_uv_ptr, in_uv_stride, - in_width, in_height); - assert(err == 0); - +int V4LEncoder::encode_frame(VisionBuf* buf, VisionIpcBufExtra *extra) { struct timeval timestamp { .tv_sec = (long)(extra->timestamp_eof/1000000000), .tv_usec = (long)((extra->timestamp_eof/1000) % 1000000), }; + // reserve buffer + int buffer_in = free_buf_in.pop(); + // push buffer extras.push(*extra); - buf_in[buffer_in].sync(VISIONBUF_SYNC_TO_DEVICE); - int bytesused = VENUS_Y_STRIDE(COLOR_FMT_NV12, in_width) * VENUS_Y_SCANLINES(COLOR_FMT_NV12, in_height) + - VENUS_UV_STRIDE(COLOR_FMT_NV12, in_width) * VENUS_UV_SCANLINES(COLOR_FMT_NV12, in_height); - queue_buffer(fd, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, buffer_in, &buf_in[buffer_in], timestamp, bytesused); + //buf->sync(VISIONBUF_SYNC_TO_DEVICE); + queue_buffer(fd, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, buffer_in, buf, timestamp); return this->counter++; } diff --git a/selfdrive/loggerd/encoder/v4l_encoder.h b/selfdrive/loggerd/encoder/v4l_encoder.h index 9f267cfb0..b7c378be8 100644 --- a/selfdrive/loggerd/encoder/v4l_encoder.h +++ b/selfdrive/loggerd/encoder/v4l_encoder.h @@ -13,8 +13,7 @@ public: VideoEncoder(filename, type, in_width, in_height, fps, bitrate, codec, out_width, out_height, write) { encoder_init(); } ~V4LEncoder(); void encoder_init(); - int encode_frame(const uint8_t *y_ptr, const uint8_t *u_ptr, const uint8_t *v_ptr, - int in_width, int in_height, VisionIpcBufExtra *extra); + int encode_frame(VisionBuf* buf, VisionIpcBufExtra *extra); void encoder_open(const char* path); void encoder_close(); private: diff --git a/selfdrive/loggerd/encoderd.cc b/selfdrive/loggerd/encoderd.cc index 297eaf88d..9bd8e2f1d 100644 --- a/selfdrive/loggerd/encoderd.cc +++ b/selfdrive/loggerd/encoderd.cc @@ -103,8 +103,7 @@ void encoder_thread(EncoderdState *s, const LogCameraInfo &cam_info) { // encode a frame for (int i = 0; i < encoders.size(); ++i) { - int out_id = encoders[i]->encode_frame(buf->y, buf->u, buf->v, - buf->width, buf->height, &extra); + int out_id = encoders[i]->encode_frame(buf, &extra); if (out_id == -1) { LOGE("Failed to encode frame. frame_id: %d", extra.frame_id); @@ -125,16 +124,14 @@ void encoderd_thread() { std::vector encoder_threads; for (const auto &cam : cameras_logged) { - if (cam.enable) { - encoder_threads.push_back(std::thread(encoder_thread, &s, cam)); - s.max_waiting++; - } + encoder_threads.push_back(std::thread(encoder_thread, &s, cam)); + s.max_waiting++; } for (auto &t : encoder_threads) t.join(); } int main() { - if (Hardware::TICI()) { + if (!Hardware::PC()) { int ret; ret = util::set_realtime_priority(52); assert(ret == 0); diff --git a/selfdrive/loggerd/logger.cc b/selfdrive/loggerd/logger.cc index d8a409e93..8038f1926 100644 --- a/selfdrive/loggerd/logger.cc +++ b/selfdrive/loggerd/logger.cc @@ -19,26 +19,12 @@ #include "common/swaglog.h" #include "common/version.h" -// ***** logging helpers ***** - -void append_property(const char* key, const char* value, void *cookie) { - std::vector > *properties = - (std::vector > *)cookie; - - properties->push_back(std::make_pair(std::string(key), std::string(value))); -} - // ***** log metadata ***** kj::Array logger_build_init_data() { MessageBuilder msg; auto init = msg.initEvent().initInitData(); - if (Hardware::TICI()) { - init.setDeviceType(cereal::InitData::DeviceType::TICI); - } else { - init.setDeviceType(cereal::InitData::DeviceType::PC); - } - + init.setDeviceType(Hardware::get_device_type()); init.setVersion(COMMA_VERSION); std::ifstream cmdline_stream("/proc/cmdline"); diff --git a/selfdrive/loggerd/logger.h b/selfdrive/loggerd/logger.h index ca08e6471..e7594cee8 100644 --- a/selfdrive/loggerd/logger.h +++ b/selfdrive/loggerd/logger.h @@ -13,7 +13,7 @@ #include "cereal/messaging/messaging.h" #include "common/util.h" #include "common/swaglog.h" -#include "selfdrive/hardware/hw.h" +#include "system/hardware/hw.h" const std::string LOG_ROOT = Path::log_root(); diff --git a/selfdrive/loggerd/loggerd.cc b/selfdrive/loggerd/loggerd.cc index 1a10ab0c0..e0892e68b 100644 --- a/selfdrive/loggerd/loggerd.cc +++ b/selfdrive/loggerd/loggerd.cc @@ -6,7 +6,6 @@ ExitHandler do_exit; struct LoggerdState { LoggerState logger = {}; char segment_path[4096]; - std::mutex rotate_lock; std::atomic rotate_segment; std::atomic last_camera_seen_tms; std::atomic ready_to_rotate; // count of encoders ready to rotate @@ -15,15 +14,12 @@ struct LoggerdState { }; void logger_rotate(LoggerdState *s) { - { - std::unique_lock lk(s->rotate_lock); - int segment = -1; - int err = logger_next(&s->logger, LOG_ROOT.c_str(), s->segment_path, sizeof(s->segment_path), &segment); - assert(err == 0); - s->rotate_segment = segment; - s->ready_to_rotate = 0; - s->last_rotate_tms = millis_since_boot(); - } + int segment = -1; + int err = logger_next(&s->logger, LOG_ROOT.c_str(), s->segment_path, sizeof(s->segment_path), &segment); + assert(err == 0); + s->rotate_segment = segment; + s->ready_to_rotate = 0; + s->last_rotate_tms = millis_since_boot(); LOGW((s->logger.part == 0) ? "logging to %s" : "rotated to %s", s->segment_path); } @@ -208,10 +204,8 @@ void loggerd_thread() { // init encoders s.last_camera_seen_tms = millis_since_boot(); for (const auto &cam : cameras_logged) { - if (cam.enable) { - s.max_waiting++; - if (cam.has_qcamera) { s.max_waiting++; } - } + s.max_waiting++; + if (cam.has_qcamera) { s.max_waiting++; } } uint64_t msg_count = 0, bytes_count = 0; @@ -267,7 +261,7 @@ void loggerd_thread() { } int main(int argc, char** argv) { - if (Hardware::TICI()) { + if (!Hardware::PC()) { int ret; ret = util::set_core_affinity({0, 1, 2, 3}); assert(ret == 0); @@ -279,4 +273,4 @@ int main(int argc, char** argv) { loggerd_thread(); return 0; -} \ No newline at end of file +} diff --git a/selfdrive/loggerd/loggerd.h b/selfdrive/loggerd/loggerd.h index 21aa66ea1..6eafbe08d 100644 --- a/selfdrive/loggerd/loggerd.h +++ b/selfdrive/loggerd/loggerd.h @@ -15,12 +15,12 @@ #include "cereal/services.h" #include "cereal/visionipc/visionipc.h" #include "cereal/visionipc/visionipc_client.h" -#include "selfdrive/camerad/cameras/camera_common.h" +#include "system/camerad/cameras/camera_common.h" #include "common/params.h" #include "common/swaglog.h" #include "common/timing.h" #include "common/util.h" -#include "selfdrive/hardware/hw.h" +#include "system/hardware/hw.h" #include "selfdrive/loggerd/encoder/encoder.h" #include "selfdrive/loggerd/logger.h" @@ -33,8 +33,8 @@ #endif constexpr int MAIN_FPS = 20; -const int MAIN_BITRATE = Hardware::TICI() ? 10000000 : 5000000; -const int DCAM_BITRATE = Hardware::TICI() ? MAIN_BITRATE : 2500000; +const int MAIN_BITRATE = 10000000; +const int DCAM_BITRATE = MAIN_BITRATE; #define NO_CAMERA_PATIENCE 500 // fall back to time-based rotation if all cameras are dead @@ -50,7 +50,6 @@ struct LogCameraInfo { int bitrate; bool is_h265; bool has_qcamera; - bool enable; bool record; }; @@ -63,7 +62,6 @@ const LogCameraInfo cameras_logged[] = { .bitrate = MAIN_BITRATE, .is_h265 = true, .has_qcamera = true, - .enable = true, .record = true, .frame_width = 1928, .frame_height = 1208, @@ -76,7 +74,6 @@ const LogCameraInfo cameras_logged[] = { .bitrate = DCAM_BITRATE, .is_h265 = true, .has_qcamera = false, - .enable = true, .record = Params().getBool("RecordFront"), .frame_width = 1928, .frame_height = 1208, @@ -89,8 +86,7 @@ const LogCameraInfo cameras_logged[] = { .bitrate = MAIN_BITRATE, .is_h265 = true, .has_qcamera = false, - .enable = Hardware::TICI(), - .record = Hardware::TICI(), + .record = true, .frame_width = 1928, .frame_height = 1208, }, @@ -100,8 +96,7 @@ const LogCameraInfo qcam_info = { .fps = MAIN_FPS, .bitrate = 256000, .is_h265 = false, - .enable = true, .record = true, - .frame_width = Hardware::TICI() ? 526 : 480, - .frame_height = Hardware::TICI() ? 330 : 360 // keep pixel count the same? + .frame_width = 526, + .frame_height = 330, }; diff --git a/selfdrive/loggerd/uploader.py b/selfdrive/loggerd/uploader.py index b7d8df861..f97bafecb 100644 --- a/selfdrive/loggerd/uploader.py +++ b/selfdrive/loggerd/uploader.py @@ -15,10 +15,10 @@ import cereal.messaging as messaging from common.api import Api from common.params import Params from common.realtime import set_core_affinity -from selfdrive.hardware import TICI +from system.hardware import TICI from selfdrive.loggerd.xattr_cache import getxattr, setxattr from selfdrive.loggerd.config import ROOT -from selfdrive.swaglog import cloudlog +from system.swaglog import cloudlog NetworkType = log.DeviceState.NetworkType UPLOAD_ATTR_NAME = 'user.upload' diff --git a/selfdrive/manager/build.py b/selfdrive/manager/build.py index 806aa58bb..12f894061 100755 --- a/selfdrive/manager/build.py +++ b/selfdrive/manager/build.py @@ -1,8 +1,6 @@ #!/usr/bin/env python3 import os import subprocess -import sys -import time import textwrap from pathlib import Path @@ -10,14 +8,14 @@ from pathlib import Path from common.basedir import BASEDIR from common.spinner import Spinner from common.text_window import TextWindow -from selfdrive.hardware import TICI -from selfdrive.swaglog import cloudlog, add_file_handler -from selfdrive.version import is_dirty +from system.hardware import AGNOS +from system.swaglog import cloudlog, add_file_handler +from system.version import is_dirty MAX_CACHE_SIZE = 4e9 if "CI" in os.environ else 2e9 -CACHE_DIR = Path("/data/scons_cache" if TICI else "/tmp/scons_cache") +CACHE_DIR = Path("/data/scons_cache" if AGNOS else "/tmp/scons_cache") -TOTAL_SCONS_NODES = 2405 +TOTAL_SCONS_NODES = 2035 MAX_BUILD_PROGRESS = 100 PREBUILT = os.path.exists(os.path.join(BASEDIR, 'prebuilt')) @@ -28,62 +26,49 @@ def build(spinner: Spinner, dirty: bool = False) -> None: nproc = os.cpu_count() j_flag = "" if nproc is None else f"-j{nproc - 1}" - for retry in [True, False]: - scons: subprocess.Popen = subprocess.Popen(["scons", j_flag, "--cache-populate"], cwd=BASEDIR, env=env, stderr=subprocess.PIPE) - assert scons.stderr is not None + scons: subprocess.Popen = subprocess.Popen(["scons", j_flag, "--cache-populate"], cwd=BASEDIR, env=env, stderr=subprocess.PIPE) + assert scons.stderr is not None - compile_output = [] + compile_output = [] - # Read progress from stderr and update spinner - while scons.poll() is None: - try: - line = scons.stderr.readline() - if line is None: - continue - line = line.rstrip() + # Read progress from stderr and update spinner + while scons.poll() is None: + try: + line = scons.stderr.readline() + if line is None: + continue + line = line.rstrip() - prefix = b'progress: ' - if line.startswith(prefix): - i = int(line[len(prefix):]) - spinner.update_progress(MAX_BUILD_PROGRESS * min(1., i / TOTAL_SCONS_NODES), 100.) - elif len(line): - compile_output.append(line) - print(line.decode('utf8', 'replace')) - except Exception: - pass + prefix = b'progress: ' + if line.startswith(prefix): + i = int(line[len(prefix):]) + spinner.update_progress(MAX_BUILD_PROGRESS * min(1., i / TOTAL_SCONS_NODES), 100.) + elif len(line): + compile_output.append(line) + print(line.decode('utf8', 'replace')) + except Exception: + pass - if scons.returncode != 0: - # Read remaining output - r = scons.stderr.read().split(b'\n') - compile_output += r + if scons.returncode != 0: + # Read remaining output + r = scons.stderr.read().split(b'\n') + compile_output += r - if retry and (not dirty): - if not os.getenv("CI"): - print("scons build failed, cleaning in") - for i in range(3, -1, -1): - print("....%d" % i) - time.sleep(1) - subprocess.check_call(["scons", "-c"], cwd=BASEDIR, env=env) - else: - print("scons build failed after retry") - sys.exit(1) - else: - # Build failed log errors - errors = [line.decode('utf8', 'replace') for line in compile_output - if any(err in line for err in [b'error: ', b'not found, needed by target'])] - error_s = "\n".join(errors) - add_file_handler(cloudlog) - cloudlog.error("scons build failed\n" + error_s) + # Build failed log errors + errors = [line.decode('utf8', 'replace') for line in compile_output + if any(err in line for err in [b'error: ', b'not found, needed by target'])] + error_s = "\n".join(errors) + add_file_handler(cloudlog) + cloudlog.error("scons build failed\n" + error_s) + + # Show TextWindow + spinner.close() + if not os.getenv("CI"): + error_s = "\n \n".join("\n".join(textwrap.wrap(e, 65)) for e in errors) + with TextWindow("openpilot failed to build\n \n" + error_s) as t: + t.wait_for_exit() + exit(1) - # Show TextWindow - spinner.close() - if not os.getenv("CI"): - error_s = "\n \n".join("\n".join(textwrap.wrap(e, 65)) for e in errors) - with TextWindow("openpilot failed to build\n \n" + error_s) as t: - t.wait_for_exit() - exit(1) - else: - break # enforce max cache size cache_files = [f for f in CACHE_DIR.rglob('*') if f.is_file()] diff --git a/selfdrive/manager/manager.py b/selfdrive/manager/manager.py index cd7817fa9..140c7f1d4 100755 --- a/selfdrive/manager/manager.py +++ b/selfdrive/manager/manager.py @@ -13,13 +13,13 @@ from common.basedir import BASEDIR from common.params import Params, ParamKeyType from common.text_window import TextWindow from selfdrive.boardd.set_time import set_time -from selfdrive.hardware import HARDWARE, PC +from system.hardware import HARDWARE, PC from selfdrive.manager.helpers import unblock_stdout from selfdrive.manager.process import ensure_running from selfdrive.manager.process_config import managed_processes from selfdrive.athena.registration import register, UNREGISTERED_DONGLE_ID -from selfdrive.swaglog import cloudlog, add_file_handler -from selfdrive.version import is_dirty, get_commit, get_version, get_origin, get_short_branch, \ +from system.swaglog import cloudlog, add_file_handler +from system.version import is_dirty, get_commit, get_version, get_origin, get_short_branch, \ terms_version, training_version diff --git a/selfdrive/manager/process.py b/selfdrive/manager/process.py index e2bb41c21..dabfbe4ee 100644 --- a/selfdrive/manager/process.py +++ b/selfdrive/manager/process.py @@ -16,8 +16,8 @@ from cereal import car from common.basedir import BASEDIR from common.params import Params from common.realtime import sec_since_boot -from selfdrive.swaglog import cloudlog -from selfdrive.hardware import HARDWARE +from system.swaglog import cloudlog +from system.hardware import HARDWARE from cereal import log WATCHDOG_FN = "/dev/shm/wd_" diff --git a/selfdrive/manager/process_config.py b/selfdrive/manager/process_config.py index ad3cabbbd..dec51966a 100644 --- a/selfdrive/manager/process_config.py +++ b/selfdrive/manager/process_config.py @@ -2,7 +2,7 @@ import os from cereal import car from common.params import Params -from selfdrive.hardware import TICI, PC +from system.hardware import PC from selfdrive.manager.process import PythonProcess, NativeProcess, DaemonProcess WEBCAM = os.getenv("USE_WEBCAM") is not None @@ -18,20 +18,22 @@ def logging(started, params, CP: car.CarParams) -> bool: return started and run procs = [ - DaemonProcess("manage_athenad", "selfdrive.athena.manage_athenad", "AthenadPid"), # due to qualcomm kernel bugs SIGKILLing camerad sometimes causes page table corruption - NativeProcess("camerad", "selfdrive/camerad", ["./camerad"], unkillable=True, callback=driverview), - NativeProcess("clocksd", "selfdrive/clocksd", ["./clocksd"]), + NativeProcess("camerad", "system/camerad", ["./camerad"], unkillable=True, callback=driverview), + NativeProcess("clocksd", "system/clocksd", ["./clocksd"]), + NativeProcess("logcatd", "system/logcatd", ["./logcatd"]), + NativeProcess("proclogd", "system/proclogd", ["./proclogd"]), + PythonProcess("logmessaged", "system.logmessaged", offroad=True), + PythonProcess("timezoned", "system.timezoned", enabled=not PC, offroad=True), + + DaemonProcess("manage_athenad", "selfdrive.athena.manage_athenad", "AthenadPid"), NativeProcess("dmonitoringmodeld", "selfdrive/modeld", ["./dmonitoringmodeld"], enabled=(not PC or WEBCAM), callback=driverview), - NativeProcess("logcatd", "selfdrive/logcatd", ["./logcatd"]), NativeProcess("encoderd", "selfdrive/loggerd", ["./encoderd"]), NativeProcess("loggerd", "selfdrive/loggerd", ["./loggerd"], onroad=False, callback=logging), NativeProcess("modeld", "selfdrive/modeld", ["./modeld"]), - NativeProcess("navd", "selfdrive/ui/navd", ["./navd"], offroad=True), - NativeProcess("proclogd", "selfdrive/proclogd", ["./proclogd"]), NativeProcess("sensord", "selfdrive/sensord", ["./sensord"], enabled=not PC), NativeProcess("ubloxd", "selfdrive/locationd", ["./ubloxd"], enabled=(not PC or WEBCAM)), - NativeProcess("ui", "selfdrive/ui", ["./ui"], offroad=True, watchdog_max_dt=(5 if TICI else None)), + NativeProcess("ui", "selfdrive/ui", ["./ui"], offroad=True, watchdog_max_dt=(5 if not PC else None)), NativeProcess("soundd", "selfdrive/ui/soundd", ["./soundd"], offroad=True), NativeProcess("locationd", "selfdrive/locationd", ["./locationd"]), NativeProcess("boardd", "selfdrive/boardd", ["./boardd"], enabled=False), @@ -39,13 +41,13 @@ procs = [ PythonProcess("controlsd", "selfdrive.controls.controlsd"), PythonProcess("deleter", "selfdrive.loggerd.deleter", offroad=True), PythonProcess("dmonitoringd", "selfdrive.monitoring.dmonitoringd", enabled=(not PC or WEBCAM), callback=driverview), - PythonProcess("logmessaged", "selfdrive.logmessaged", offroad=True), + PythonProcess("laikad", "selfdrive.locationd.laikad"), + PythonProcess("navd", "selfdrive.navd.navd"), PythonProcess("pandad", "selfdrive.boardd.pandad", offroad=True), PythonProcess("paramsd", "selfdrive.locationd.paramsd"), PythonProcess("plannerd", "selfdrive.controls.plannerd"), PythonProcess("radard", "selfdrive.controls.radard"), PythonProcess("thermald", "selfdrive.thermald.thermald", offroad=True), - PythonProcess("timezoned", "selfdrive.timezoned", enabled=TICI, offroad=True), PythonProcess("tombstoned", "selfdrive.tombstoned", enabled=not PC, offroad=True), PythonProcess("updated", "selfdrive.updated", enabled=not PC, onroad=False, offroad=True), PythonProcess("uploader", "selfdrive.loggerd.uploader", offroad=True), diff --git a/selfdrive/manager/test/test_manager.py b/selfdrive/manager/test/test_manager.py index 1750c81b2..a84ff264d 100755 --- a/selfdrive/manager/test/test_manager.py +++ b/selfdrive/manager/test/test_manager.py @@ -5,14 +5,13 @@ import time import unittest import selfdrive.manager.manager as manager -from selfdrive.hardware import TICI, HARDWARE from selfdrive.manager.process import DaemonProcess from selfdrive.manager.process_config import managed_processes +from system.hardware import AGNOS, HARDWARE os.environ['FAKEUPLOAD'] = "1" -# TODO: make eon fast -MAX_STARTUP_TIME = 15 +MAX_STARTUP_TIME = 3 ALL_PROCESSES = [p.name for p in managed_processes.values() if (type(p) is not DaemonProcess) and p.enabled and (p.name not in ['updated', 'pandad'])] @@ -50,13 +49,10 @@ class TestManager(unittest.TestCase): self.assertTrue(state.running, f"{p} not running") exit_code = managed_processes[p].stop(retry=False) - if (TICI and p in ['ui', 'navd']): + if (AGNOS and p in ['ui',]): # TODO: make Qt UI exit gracefully continue - # Make sure the process is actually dead - managed_processes[p].stop() - # TODO: interrupted blocking read exits with 1 in cereal. use a more unique return code exit_codes = [0, 1] if managed_processes[p].sigkill: diff --git a/selfdrive/modeld/SConscript b/selfdrive/modeld/SConscript index 1f1c661c8..3e9738d86 100644 --- a/selfdrive/modeld/SConscript +++ b/selfdrive/modeld/SConscript @@ -65,22 +65,14 @@ common_model = lenv.Object(common_src) if use_thneed and arch == "larch64": fn = File("models/supercombo").abspath compiler = lenv.Program('thneed/compile', ["thneed/compile.cc"]+common_model, LIBS=libs) - cmd = f"cd {Dir('.').abspath} && {compiler[0].abspath} --in {fn}.dlc --out {fn}_badweights.thneed --binary --optimize" + cmd = f"cd {Dir('.').abspath} && {compiler[0].abspath} --in {fn}.dlc --out {fn}.thneed --binary --optimize" lib_paths = ':'.join(Dir(p).abspath for p in lenv["LIBPATH"]) kernel_path = os.path.join(Dir('.').abspath, "thneed", "kernels") cenv = Environment(ENV={'LD_LIBRARY_PATH': f"{lib_paths}:{lenv['ENV']['LD_LIBRARY_PATH']}", 'KERNEL_PATH': kernel_path}) kernels = [os.path.join(kernel_path, x) for x in os.listdir(kernel_path) if x.endswith(".cl")] - cenv.Command(fn + "_badweights.thneed", [fn + ".dlc", kernels, compiler], cmd) - - from selfdrive.modeld.thneed.weights_fixup import weights_fixup - def weights_fixup_action(target, source, env): - weights_fixup(target[0].abspath, source[0].abspath, source[1].abspath) - - env = Environment(BUILDERS = {'WeightFixup' : Builder(action = weights_fixup_action)}) - env.WeightFixup(target=fn + ".thneed", source=[fn+"_badweights.thneed", fn+".dlc"]) - + cenv.Command(fn + ".thneed", [fn + ".dlc", kernels, compiler], cmd) lenv.Program('_dmonitoringmodeld', [ "dmonitoringmodeld.cc", diff --git a/selfdrive/hardware/pc/__init__.py b/selfdrive/modeld/__init__.py similarity index 100% rename from selfdrive/hardware/pc/__init__.py rename to selfdrive/modeld/__init__.py diff --git a/selfdrive/modeld/dmonitoringmodeld.cc b/selfdrive/modeld/dmonitoringmodeld.cc index 61f3343c8..cde13a9be 100644 --- a/selfdrive/modeld/dmonitoringmodeld.cc +++ b/selfdrive/modeld/dmonitoringmodeld.cc @@ -12,7 +12,7 @@ ExitHandler do_exit; void run_model(DMonitoringModelState &model, VisionIpcClient &vipc_client) { - PubMaster pm({"driverState"}); + PubMaster pm({"driverStateV2"}); SubMaster sm({"liveCalibration"}); float calib[CALIB_LEN] = {0}; double last = 0; @@ -31,11 +31,11 @@ void run_model(DMonitoringModelState &model, VisionIpcClient &vipc_client) { } double t1 = millis_since_boot(); - DMonitoringResult res = dmonitoring_eval_frame(&model, buf->addr, buf->width, buf->height, calib); + DMonitoringModelResult model_res = dmonitoring_eval_frame(&model, buf->addr, buf->width, buf->height, buf->stride, buf->uv_offset, calib); double t2 = millis_since_boot(); // send dm packet - dmonitoring_publish(pm, extra.frame_id, res, (t2 - t1) / 1000.0, model.output); + dmonitoring_publish(pm, extra.frame_id, model_res, (t2 - t1) / 1000.0, model.output); //printf("dmonitoring process: %.2fms, from last %.2fms\n", t2 - t1, t1 - last); last = t1; diff --git a/selfdrive/modeld/modeld.cc b/selfdrive/modeld/modeld.cc index 03d0ec432..0aac9b3c4 100644 --- a/selfdrive/modeld/modeld.cc +++ b/selfdrive/modeld/modeld.cc @@ -11,7 +11,7 @@ #include "common/params.h" #include "common/swaglog.h" #include "common/util.h" -#include "selfdrive/hardware/hw.h" +#include "system/hardware/hw.h" #include "selfdrive/modeld/models/driving.h" ExitHandler do_exit; @@ -163,7 +163,7 @@ void run_model(ModelState &model, VisionIpcClient &vipc_client_main, VisionIpcCl } int main(int argc, char **argv) { - if (Hardware::TICI()) { + if (!Hardware::PC()) { int ret; ret = util::set_realtime_priority(54); assert(ret == 0); @@ -171,7 +171,7 @@ int main(int argc, char **argv) { assert(ret == 0); } - bool main_wide_camera = Params().getBool("EnableWideCamera"); + bool main_wide_camera = Params().getBool("WideCameraOnly"); bool use_extra_client = !main_wide_camera; // set for single camera mode // cl init diff --git a/selfdrive/modeld/models/commonmodel.cc b/selfdrive/modeld/models/commonmodel.cc index bc013395d..b7c9051c6 100644 --- a/selfdrive/modeld/models/commonmodel.cc +++ b/selfdrive/modeld/models/commonmodel.cc @@ -22,9 +22,9 @@ ModelFrame::ModelFrame(cl_device_id device_id, cl_context context) { loadyuv_init(&loadyuv, context, device_id, MODEL_WIDTH, MODEL_HEIGHT); } -float* ModelFrame::prepare(cl_mem yuv_cl, int frame_width, int frame_height, const mat3 &projection, cl_mem *output) { +float* ModelFrame::prepare(cl_mem yuv_cl, int frame_width, int frame_height, int frame_stride, int frame_uv_offset, const mat3 &projection, cl_mem *output) { transform_queue(&this->transform, q, - yuv_cl, frame_width, frame_height, + yuv_cl, frame_width, frame_height, frame_stride, frame_uv_offset, y_cl, u_cl, v_cl, MODEL_WIDTH, MODEL_HEIGHT, projection); if (output == NULL) { diff --git a/selfdrive/modeld/models/commonmodel.h b/selfdrive/modeld/models/commonmodel.h index 059a85e7d..40c82a8c2 100644 --- a/selfdrive/modeld/models/commonmodel.h +++ b/selfdrive/modeld/models/commonmodel.h @@ -25,7 +25,7 @@ class ModelFrame { public: ModelFrame(cl_device_id device_id, cl_context context); ~ModelFrame(); - float* prepare(cl_mem yuv_cl, int width, int height, const mat3& transform, cl_mem *output); + float* prepare(cl_mem yuv_cl, int width, int height, int frame_stride, int frame_uv_offset, const mat3& transform, cl_mem *output); const int MODEL_WIDTH = 512; const int MODEL_HEIGHT = 256; diff --git a/selfdrive/modeld/models/dmonitoring.cc b/selfdrive/modeld/models/dmonitoring.cc index 4792806c8..e7e6d4661 100644 --- a/selfdrive/modeld/models/dmonitoring.cc +++ b/selfdrive/modeld/models/dmonitoring.cc @@ -6,12 +6,12 @@ #include "common/modeldata.h" #include "common/params.h" #include "common/timing.h" -#include "selfdrive/hardware/hw.h" +#include "system/hardware/hw.h" #include "selfdrive/modeld/models/dmonitoring.h" -constexpr int MODEL_WIDTH = 320; -constexpr int MODEL_HEIGHT = 640; +constexpr int MODEL_WIDTH = 1440; +constexpr int MODEL_HEIGHT = 960; template static inline T *get_buffer(std::vector &buf, const size_t size) { @@ -19,222 +19,114 @@ static inline T *get_buffer(std::vector &buf, const size_t size) { return buf.data(); } -static inline void init_yuv_buf(std::vector &buf, const int width, int height) { - uint8_t *y = get_buffer(buf, width * height * 3 / 2); - uint8_t *u = y + width * height; - uint8_t *v = u + (width / 2) * (height / 2); - - // needed on comma two to make the padded border black - // equivalent to RGB(0,0,0) in YUV space - memset(y, 16, width * height); - memset(u, 128, (width / 2) * (height / 2)); - memset(v, 128, (width / 2) * (height / 2)); -} - void dmonitoring_init(DMonitoringModelState* s) { - s->is_rhd = Params().getBool("IsRHD"); - for (int x = 0; x < std::size(s->tensor); ++x) { - s->tensor[x] = (x - 128.f) * 0.0078125f; - } - init_yuv_buf(s->resized_buf, MODEL_WIDTH, MODEL_HEIGHT); #ifdef USE_ONNX_MODEL - s->m = new ONNXModel("models/dmonitoring_model.onnx", &s->output[0], OUTPUT_SIZE, USE_DSP_RUNTIME); + s->m = new ONNXModel("models/dmonitoring_model.onnx", &s->output[0], OUTPUT_SIZE, USE_DSP_RUNTIME, false, true); #else - s->m = new SNPEModel("models/dmonitoring_model_q.dlc", &s->output[0], OUTPUT_SIZE, USE_DSP_RUNTIME); + s->m = new SNPEModel("models/dmonitoring_model_q.dlc", &s->output[0], OUTPUT_SIZE, USE_DSP_RUNTIME, false, true); #endif s->m->addCalib(s->calib, CALIB_LEN); } -static inline auto get_yuv_buf(std::vector &buf, const int width, int height) { - uint8_t *y = get_buffer(buf, width * height * 3 / 2); - uint8_t *u = y + width * height; - uint8_t *v = u + (width /2) * (height / 2); - return std::make_tuple(y, u, v); +void parse_driver_data(DriverStateResult &ds_res, const DMonitoringModelState* s, int out_idx_offset) { + for (int i = 0; i < 3; ++i) { + ds_res.face_orientation[i] = s->output[out_idx_offset+i] * REG_SCALE; + ds_res.face_orientation_std[i] = exp(s->output[out_idx_offset+6+i]); + } + for (int i = 0; i < 2; ++i) { + ds_res.face_position[i] = s->output[out_idx_offset+3+i] * REG_SCALE; + ds_res.face_position_std[i] = exp(s->output[out_idx_offset+9+i]); + } + for (int i = 0; i < 4; ++i) { + ds_res.ready_prob[i] = sigmoid(s->output[out_idx_offset+35+i]); + } + for (int i = 0; i < 2; ++i) { + ds_res.not_ready_prob[i] = sigmoid(s->output[out_idx_offset+39+i]); + } + ds_res.face_prob = sigmoid(s->output[out_idx_offset+12]); + ds_res.left_eye_prob = sigmoid(s->output[out_idx_offset+21]); + ds_res.right_eye_prob = sigmoid(s->output[out_idx_offset+30]); + ds_res.left_blink_prob = sigmoid(s->output[out_idx_offset+31]); + ds_res.right_blink_prob = sigmoid(s->output[out_idx_offset+32]); + ds_res.sunglasses_prob = sigmoid(s->output[out_idx_offset+33]); + ds_res.occluded_prob = sigmoid(s->output[out_idx_offset+34]); } -struct Rect {int x, y, w, h;}; -void crop_yuv(uint8_t *raw, int width, int height, uint8_t *y, uint8_t *u, uint8_t *v, const Rect &rect) { - uint8_t *raw_y = raw; - uint8_t *raw_u = raw_y + (width * height); - uint8_t *raw_v = raw_u + ((width / 2) * (height / 2)); - for (int r = 0; r < rect.h / 2; r++) { - memcpy(y + 2 * r * rect.w, raw_y + (2 * r + rect.y) * width + rect.x, rect.w); - memcpy(y + (2 * r + 1) * rect.w, raw_y + (2 * r + rect.y + 1) * width + rect.x, rect.w); - memcpy(u + r * (rect.w / 2), raw_u + (r + (rect.y / 2)) * width / 2 + (rect.x / 2), rect.w / 2); - memcpy(v + r * (rect.w / 2), raw_v + (r + (rect.y / 2)) * width / 2 + (rect.x / 2), rect.w / 2); - } +void fill_driver_data(cereal::DriverStateV2::DriverData::Builder ddata, const DriverStateResult &ds_res) { + ddata.setFaceOrientation(ds_res.face_orientation); + ddata.setFaceOrientationStd(ds_res.face_orientation_std); + ddata.setFacePosition(ds_res.face_position); + ddata.setFacePositionStd(ds_res.face_position_std); + ddata.setFaceProb(ds_res.face_prob); + ddata.setLeftEyeProb(ds_res.left_eye_prob); + ddata.setRightEyeProb(ds_res.right_eye_prob); + ddata.setLeftBlinkProb(ds_res.left_blink_prob); + ddata.setRightBlinkProb(ds_res.right_blink_prob); + ddata.setSunglassesProb(ds_res.sunglasses_prob); + ddata.setOccludedProb(ds_res.occluded_prob); + ddata.setReadyProb(ds_res.ready_prob); + ddata.setNotReadyProb(ds_res.not_ready_prob); } -DMonitoringResult dmonitoring_eval_frame(DMonitoringModelState* s, void* stream_buf, int width, int height, float *calib) { - Rect crop_rect; - if (width == TICI_CAM_WIDTH) { - const int cropped_height = tici_dm_crop::width / 1.33; - crop_rect = {width / 2 - tici_dm_crop::width / 2 + tici_dm_crop::x_offset, - height / 2 - cropped_height / 2 + tici_dm_crop::y_offset, - cropped_height / 2, - cropped_height}; - if (!s->is_rhd) { - crop_rect.x += tici_dm_crop::width - crop_rect.w; - } - } else { - const int adapt_width = 372; - crop_rect = {0, 0, adapt_width, height}; - if (!s->is_rhd) { - crop_rect.x += width - crop_rect.w; - } +DMonitoringModelResult dmonitoring_eval_frame(DMonitoringModelState* s, void* stream_buf, int width, int height, int stride, int uv_offset, float *calib) { + int v_off = height - MODEL_HEIGHT; + int h_off = (width - MODEL_WIDTH) / 2; + int yuv_buf_len = MODEL_WIDTH * MODEL_HEIGHT; + + uint8_t *raw_buf = (uint8_t *) stream_buf; + // vertical crop free + uint8_t *raw_y_start = raw_buf + stride * v_off; + + uint8_t *net_input_buf = get_buffer(s->net_input_buf, yuv_buf_len); + + // here makes a uint8 copy + for (int r = 0; r < MODEL_HEIGHT; ++r) { + memcpy(net_input_buf + r * MODEL_WIDTH, raw_y_start + r * stride + h_off, MODEL_WIDTH); } - int resized_width = MODEL_WIDTH; - int resized_height = MODEL_HEIGHT; - - auto [cropped_y, cropped_u, cropped_v] = get_yuv_buf(s->cropped_buf, crop_rect.w, crop_rect.h); - if (!s->is_rhd) { - crop_yuv((uint8_t *)stream_buf, width, height, cropped_y, cropped_u, cropped_v, crop_rect); - } else { - auto [mirror_y, mirror_u, mirror_v] = get_yuv_buf(s->premirror_cropped_buf, crop_rect.w, crop_rect.h); - crop_yuv((uint8_t *)stream_buf, width, height, mirror_y, mirror_u, mirror_v, crop_rect); - libyuv::I420Mirror(mirror_y, crop_rect.w, - mirror_u, crop_rect.w / 2, - mirror_v, crop_rect.w / 2, - cropped_y, crop_rect.w, - cropped_u, crop_rect.w / 2, - cropped_v, crop_rect.w / 2, - crop_rect.w, crop_rect.h); - } - - auto [resized_buf, resized_u, resized_v] = get_yuv_buf(s->resized_buf, resized_width, resized_height); - uint8_t *resized_y = resized_buf; - libyuv::FilterMode mode = libyuv::FilterModeEnum::kFilterBilinear; - if (Hardware::TICI()) { - libyuv::I420Scale(cropped_y, crop_rect.w, - cropped_u, crop_rect.w / 2, - cropped_v, crop_rect.w / 2, - crop_rect.w, crop_rect.h, - resized_y, resized_width, - resized_u, resized_width / 2, - resized_v, resized_width / 2, - resized_width, resized_height, - mode); - } else { - const int source_height = 0.7*resized_height; - const int extra_height = (resized_height - source_height) / 2; - const int extra_width = (resized_width - source_height / 2) / 2; - const int source_width = source_height / 2 + extra_width; - libyuv::I420Scale(cropped_y, crop_rect.w, - cropped_u, crop_rect.w / 2, - cropped_v, crop_rect.w / 2, - crop_rect.w, crop_rect.h, - resized_y + extra_height * resized_width, resized_width, - resized_u + extra_height / 2 * resized_width / 2, resized_width / 2, - resized_v + extra_height / 2 * resized_width / 2, resized_width / 2, - source_width, source_height, - mode); - } - - int yuv_buf_len = (MODEL_WIDTH/2) * (MODEL_HEIGHT/2) * 6; // Y|u|v -> y|y|y|y|u|v - float *net_input_buf = get_buffer(s->net_input_buf, yuv_buf_len); - // one shot conversion, O(n) anyway - // yuvframe2tensor, normalize - for (int r = 0; r < MODEL_HEIGHT/2; r++) { - for (int c = 0; c < MODEL_WIDTH/2; c++) { - // Y_ul - net_input_buf[(r*MODEL_WIDTH/2) + c + (0*(MODEL_WIDTH/2)*(MODEL_HEIGHT/2))] = s->tensor[resized_y[(2*r)*resized_width + 2*c]]; - // Y_dl - net_input_buf[(r*MODEL_WIDTH/2) + c + (1*(MODEL_WIDTH/2)*(MODEL_HEIGHT/2))] = s->tensor[resized_y[(2*r+1)*resized_width + 2*c]]; - // Y_ur - net_input_buf[(r*MODEL_WIDTH/2) + c + (2*(MODEL_WIDTH/2)*(MODEL_HEIGHT/2))] = s->tensor[resized_y[(2*r)*resized_width + 2*c+1]]; - // Y_dr - net_input_buf[(r*MODEL_WIDTH/2) + c + (3*(MODEL_WIDTH/2)*(MODEL_HEIGHT/2))] = s->tensor[resized_y[(2*r+1)*resized_width + 2*c+1]]; - // U - net_input_buf[(r*MODEL_WIDTH/2) + c + (4*(MODEL_WIDTH/2)*(MODEL_HEIGHT/2))] = s->tensor[resized_u[r*resized_width/2 + c]]; - // V - net_input_buf[(r*MODEL_WIDTH/2) + c + (5*(MODEL_WIDTH/2)*(MODEL_HEIGHT/2))] = s->tensor[resized_v[r*resized_width/2 + c]]; - } - } - - //printf("preprocess completed. %d \n", yuv_buf_len); - //FILE *dump_yuv_file = fopen("/tmp/rawdump.yuv", "wb"); - //fwrite(resized_buf, yuv_buf_len, sizeof(uint8_t), dump_yuv_file); - //fclose(dump_yuv_file); - - // *** testing *** - // idat = np.frombuffer(open("/tmp/inputdump.yuv", "rb").read(), np.float32).reshape(6, 160, 320) - // imshow(cv2.cvtColor(tensor_to_frames(idat[None]/0.0078125+128)[0], cv2.COLOR_YUV2RGB_I420)) - - //FILE *dump_yuv_file2 = fopen("/tmp/inputdump.yuv", "wb"); - //fwrite(net_input_buf, MODEL_HEIGHT*MODEL_WIDTH*3/2, sizeof(float), dump_yuv_file2); - //fclose(dump_yuv_file2); + // printf("preprocess completed. %d \n", yuv_buf_len); + // FILE *dump_yuv_file = fopen("/tmp/rawdump.yuv", "wb"); + // fwrite(net_input_buf, yuv_buf_len, sizeof(uint8_t), dump_yuv_file); + // fclose(dump_yuv_file); double t1 = millis_since_boot(); - s->m->addImage(net_input_buf, yuv_buf_len); + s->m->addImage((float*)net_input_buf, yuv_buf_len / 4); for (int i = 0; i < CALIB_LEN; i++) { s->calib[i] = calib[i]; } s->m->execute(); double t2 = millis_since_boot(); - DMonitoringResult ret = {0}; - for (int i = 0; i < 3; ++i) { - ret.face_orientation[i] = s->output[i] * REG_SCALE; - ret.face_orientation_meta[i] = exp(s->output[6 + i]); - } - for (int i = 0; i < 2; ++i) { - ret.face_position[i] = s->output[3 + i] * REG_SCALE; - ret.face_position_meta[i] = exp(s->output[9 + i]); - } - for (int i = 0; i < 4; ++i) { - ret.ready_prob[i] = sigmoid(s->output[39 + i]); - } - for (int i = 0; i < 2; ++i) { - ret.not_ready_prob[i] = sigmoid(s->output[43 + i]); - } - ret.face_prob = sigmoid(s->output[12]); - ret.left_eye_prob = sigmoid(s->output[21]); - ret.right_eye_prob = sigmoid(s->output[30]); - ret.left_blink_prob = sigmoid(s->output[31]); - ret.right_blink_prob = sigmoid(s->output[32]); - ret.sg_prob = sigmoid(s->output[33]); - ret.poor_vision = sigmoid(s->output[34]); - ret.partial_face = sigmoid(s->output[35]); - ret.distracted_pose = sigmoid(s->output[36]); - ret.distracted_eyes = sigmoid(s->output[37]); - ret.occluded_prob = sigmoid(s->output[38]); - ret.dsp_execution_time = (t2 - t1) / 1000.; - return ret; + DMonitoringModelResult model_res = {0}; + parse_driver_data(model_res.driver_state_lhd, s, 0); + parse_driver_data(model_res.driver_state_rhd, s, 41); + model_res.poor_vision_prob = sigmoid(s->output[82]); + model_res.wheel_on_right_prob = sigmoid(s->output[83]); + model_res.dsp_execution_time = (t2 - t1) / 1000.; + + return model_res; } -void dmonitoring_publish(PubMaster &pm, uint32_t frame_id, const DMonitoringResult &res, float execution_time, kj::ArrayPtr raw_pred) { +void dmonitoring_publish(PubMaster &pm, uint32_t frame_id, const DMonitoringModelResult &model_res, float execution_time, kj::ArrayPtr raw_pred) { // make msg MessageBuilder msg; - auto framed = msg.initEvent().initDriverState(); + auto framed = msg.initEvent().initDriverStateV2(); framed.setFrameId(frame_id); framed.setModelExecutionTime(execution_time); - framed.setDspExecutionTime(res.dsp_execution_time); + framed.setDspExecutionTime(model_res.dsp_execution_time); + + framed.setPoorVisionProb(model_res.poor_vision_prob); + framed.setWheelOnRightProb(model_res.wheel_on_right_prob); + fill_driver_data(framed.initLeftDriverData(), model_res.driver_state_lhd); + fill_driver_data(framed.initRightDriverData(), model_res.driver_state_rhd); - framed.setFaceOrientation(res.face_orientation); - framed.setFaceOrientationStd(res.face_orientation_meta); - framed.setFacePosition(res.face_position); - framed.setFacePositionStd(res.face_position_meta); - framed.setFaceProb(res.face_prob); - framed.setLeftEyeProb(res.left_eye_prob); - framed.setRightEyeProb(res.right_eye_prob); - framed.setLeftBlinkProb(res.left_blink_prob); - framed.setRightBlinkProb(res.right_blink_prob); - framed.setSunglassesProb(res.sg_prob); - framed.setPoorVision(res.poor_vision); - framed.setPartialFace(res.partial_face); - framed.setDistractedPose(res.distracted_pose); - framed.setDistractedEyes(res.distracted_eyes); - framed.setOccludedProb(res.occluded_prob); - framed.setReadyProb(res.ready_prob); - framed.setNotReadyProb(res.not_ready_prob); if (send_raw_pred) { framed.setRawPredictions(raw_pred.asBytes()); } - pm.send("driverState", msg); + pm.send("driverStateV2", msg); } void dmonitoring_free(DMonitoringModelState* s) { diff --git a/selfdrive/modeld/models/dmonitoring.h b/selfdrive/modeld/models/dmonitoring.h index db4e2ef72..ae2bf0539 100644 --- a/selfdrive/modeld/models/dmonitoring.h +++ b/selfdrive/modeld/models/dmonitoring.h @@ -9,44 +9,42 @@ #define CALIB_LEN 3 -#define OUTPUT_SIZE 45 +#define OUTPUT_SIZE 84 #define REG_SCALE 0.25f -typedef struct DMonitoringResult { +typedef struct DriverStateResult { float face_orientation[3]; - float face_orientation_meta[3]; + float face_orientation_std[3]; float face_position[2]; - float face_position_meta[2]; + float face_position_std[2]; float face_prob; float left_eye_prob; float right_eye_prob; float left_blink_prob; float right_blink_prob; - float sg_prob; - float poor_vision; - float partial_face; - float distracted_pose; - float distracted_eyes; + float sunglasses_prob; float occluded_prob; float ready_prob[4]; float not_ready_prob[2]; +} DriverStateResult; + +typedef struct DMonitoringModelResult { + DriverStateResult driver_state_lhd; + DriverStateResult driver_state_rhd; + float poor_vision_prob; + float wheel_on_right_prob; float dsp_execution_time; -} DMonitoringResult; +} DMonitoringModelResult; typedef struct DMonitoringModelState { RunModel *m; - bool is_rhd; float output[OUTPUT_SIZE]; - std::vector resized_buf; - std::vector cropped_buf; - std::vector premirror_cropped_buf; - std::vector net_input_buf; + std::vector net_input_buf; float calib[CALIB_LEN]; - float tensor[UINT8_MAX + 1]; } DMonitoringModelState; void dmonitoring_init(DMonitoringModelState* s); -DMonitoringResult dmonitoring_eval_frame(DMonitoringModelState* s, void* stream_buf, int width, int height, float *calib); -void dmonitoring_publish(PubMaster &pm, uint32_t frame_id, const DMonitoringResult &res, float execution_time, kj::ArrayPtr raw_pred); +DMonitoringModelResult dmonitoring_eval_frame(DMonitoringModelState* s, void* stream_buf, int width, int height, int stride, int uv_offset, float *calib); +void dmonitoring_publish(PubMaster &pm, uint32_t frame_id, const DMonitoringModelResult &model_res, float execution_time, kj::ArrayPtr raw_pred); void dmonitoring_free(DMonitoringModelState* s); diff --git a/selfdrive/modeld/models/dmonitoring_model_q.dlc b/selfdrive/modeld/models/dmonitoring_model_q.dlc index 03f82c577..5253e359f 100644 Binary files a/selfdrive/modeld/models/dmonitoring_model_q.dlc and b/selfdrive/modeld/models/dmonitoring_model_q.dlc differ diff --git a/selfdrive/modeld/models/driving.cc b/selfdrive/modeld/models/driving.cc index b601c4898..59c0b249d 100644 --- a/selfdrive/modeld/models/driving.cc +++ b/selfdrive/modeld/models/driving.cc @@ -73,12 +73,12 @@ ModelOutput* model_eval_frame(ModelState* s, VisionBuf* buf, VisionBuf* wbuf, #endif // if getInputBuf is not NULL, net_input_buf will be - auto net_input_buf = s->frame->prepare(buf->buf_cl, buf->width, buf->height, transform, static_cast(s->m->getInputBuf())); + auto net_input_buf = s->frame->prepare(buf->buf_cl, buf->width, buf->height, buf->stride, buf->uv_offset, transform, static_cast(s->m->getInputBuf())); s->m->addImage(net_input_buf, s->frame->buf_size); LOGT("Image added"); if (wbuf != nullptr) { - auto net_extra_buf = s->wide_frame->prepare(wbuf->buf_cl, wbuf->width, wbuf->height, transform_wide, static_cast(s->m->getExtraBuf())); + auto net_extra_buf = s->wide_frame->prepare(wbuf->buf_cl, wbuf->width, wbuf->height, wbuf->stride, wbuf->uv_offset, transform_wide, static_cast(s->m->getExtraBuf())); s->m->addExtra(net_extra_buf, s->wide_frame->buf_size); LOGT("Extra image added"); } diff --git a/selfdrive/modeld/models/supercombo.dlc b/selfdrive/modeld/models/supercombo.dlc index a00a5a112..96ad6144e 100644 Binary files a/selfdrive/modeld/models/supercombo.dlc and b/selfdrive/modeld/models/supercombo.dlc differ diff --git a/selfdrive/modeld/runners/onnx_runner.py b/selfdrive/modeld/runners/onnx_runner.py index 2a6238c1d..ac7cc6881 100755 --- a/selfdrive/modeld/runners/onnx_runner.py +++ b/selfdrive/modeld/runners/onnx_runner.py @@ -9,20 +9,24 @@ os.environ["OMP_WAIT_POLICY"] = "PASSIVE" import onnxruntime as ort # pylint: disable=import-error -def read(sz): +def read(sz, tf8=False): dd = [] gt = 0 - while gt < sz * 4: - st = os.read(0, sz * 4 - gt) + szof = 1 if tf8 else 4 + while gt < sz * szof: + st = os.read(0, sz * szof - gt) assert(len(st) > 0) dd.append(st) gt += len(st) - return np.frombuffer(b''.join(dd), dtype=np.float32) + r = np.frombuffer(b''.join(dd), dtype=np.uint8 if tf8 else np.float32).astype(np.float32) + if tf8: + r = r / 255. + return r def write(d): os.write(1, d.tobytes()) -def run_loop(m): +def run_loop(m, tf8_input=False): ishapes = [[1]+ii.shape[1:] for ii in m.get_inputs()] keys = [x.name for x in m.get_inputs()] @@ -33,10 +37,10 @@ def run_loop(m): print("ready to run onnx model", keys, ishapes, file=sys.stderr) while 1: inputs = [] - for shp in ishapes: + for k, shp in zip(keys, ishapes): ts = np.product(shp) #print("reshaping %s with offset %d" % (str(shp), offset), file=sys.stderr) - inputs.append(read(ts).reshape(shp)) + inputs.append(read(ts, (k=='input_img' and tf8_input)).reshape(shp)) ret = m.run(None, dict(zip(keys, inputs))) #print(ret, file=sys.stderr) for r in ret: @@ -44,6 +48,7 @@ def run_loop(m): if __name__ == "__main__": + print(sys.argv, file=sys.stderr) print("Onnx available providers: ", ort.get_available_providers(), file=sys.stderr) options = ort.SessionOptions() options.graph_optimization_level = ort.GraphOptimizationLevel.ORT_DISABLE_ALL @@ -59,7 +64,10 @@ if __name__ == "__main__": options.graph_optimization_level = ort.GraphOptimizationLevel.ORT_ENABLE_ALL provider = 'CPUExecutionProvider' - print("Onnx selected provider: ", [provider], file=sys.stderr) - ort_session = ort.InferenceSession(sys.argv[1], options, providers=[provider]) - print("Onnx using ", ort_session.get_providers(), file=sys.stderr) - run_loop(ort_session) + try: + print("Onnx selected provider: ", [provider], file=sys.stderr) + ort_session = ort.InferenceSession(sys.argv[1], options, providers=[provider]) + print("Onnx using ", ort_session.get_providers(), file=sys.stderr) + run_loop(ort_session, tf8_input=("--use_tf8" in sys.argv)) + except KeyboardInterrupt: + pass diff --git a/selfdrive/modeld/runners/onnxmodel.cc b/selfdrive/modeld/runners/onnxmodel.cc index 9b4d6fd01..1f9f551ab 100644 --- a/selfdrive/modeld/runners/onnxmodel.cc +++ b/selfdrive/modeld/runners/onnxmodel.cc @@ -14,12 +14,13 @@ #include "common/swaglog.h" #include "common/util.h" -ONNXModel::ONNXModel(const char *path, float *_output, size_t _output_size, int runtime, bool _use_extra) { +ONNXModel::ONNXModel(const char *path, float *_output, size_t _output_size, int runtime, bool _use_extra, bool _use_tf8) { LOGD("loading model %s", path); output = _output; output_size = _output_size; use_extra = _use_extra; + use_tf8 = _use_tf8; int err = pipe(pipein); assert(err == 0); @@ -28,11 +29,12 @@ ONNXModel::ONNXModel(const char *path, float *_output, size_t _output_size, int std::string exe_dir = util::dir_name(util::readlink("/proc/self/exe")); std::string onnx_runner = exe_dir + "/runners/onnx_runner.py"; + std::string tf8_arg = use_tf8 ? "--use_tf8" : ""; proc_pid = fork(); if (proc_pid == 0) { LOGD("spawning onnx process %s", onnx_runner.c_str()); - char *argv[] = {(char*)onnx_runner.c_str(), (char*)path, nullptr}; + char *argv[] = {(char*)onnx_runner.c_str(), (char*)path, (char*)tf8_arg.c_str(), nullptr}; dup2(pipein[0], 0); dup2(pipeout[1], 1); close(pipein[0]); diff --git a/selfdrive/modeld/runners/onnxmodel.h b/selfdrive/modeld/runners/onnxmodel.h index 567d81d29..4ac599e2a 100644 --- a/selfdrive/modeld/runners/onnxmodel.h +++ b/selfdrive/modeld/runners/onnxmodel.h @@ -6,7 +6,7 @@ class ONNXModel : public RunModel { public: - ONNXModel(const char *path, float *output, size_t output_size, int runtime, bool use_extra = false); + ONNXModel(const char *path, float *output, size_t output_size, int runtime, bool use_extra = false, bool _use_tf8 = false); ~ONNXModel(); void addRecurrent(float *state, int state_size); void addDesire(float *state, int state_size); @@ -31,6 +31,7 @@ private: int calib_size; float *image_input_buf = NULL; int image_buf_size; + bool use_tf8; float *extra_input_buf = NULL; int extra_buf_size; bool use_extra; diff --git a/selfdrive/modeld/runners/snpemodel.cc b/selfdrive/modeld/runners/snpemodel.cc index 1861494d5..4d6917e89 100644 --- a/selfdrive/modeld/runners/snpemodel.cc +++ b/selfdrive/modeld/runners/snpemodel.cc @@ -14,10 +14,11 @@ void PrintErrorStringAndExit() { std::exit(EXIT_FAILURE); } -SNPEModel::SNPEModel(const char *path, float *loutput, size_t loutput_size, int runtime, bool luse_extra) { +SNPEModel::SNPEModel(const char *path, float *loutput, size_t loutput_size, int runtime, bool luse_extra, bool luse_tf8) { output = loutput; output_size = loutput_size; use_extra = luse_extra; + use_tf8 = luse_tf8; #ifdef QCOM2 if (runtime==USE_GPU_RUNTIME) { Runtime = zdl::DlSystem::Runtime_t::GPU; @@ -70,14 +71,16 @@ SNPEModel::SNPEModel(const char *path, float *loutput, size_t loutput_size, int printf("model: %s -> %s\n", input_tensor_name, output_tensor_name); zdl::DlSystem::UserBufferEncodingFloat userBufferEncodingFloat; + zdl::DlSystem::UserBufferEncodingTf8 userBufferEncodingTf8(0, 1./255); // network takes 0-1 zdl::DlSystem::IUserBufferFactory& ubFactory = zdl::SNPE::SNPEFactory::getUserBufferFactory(); + size_t size_of_input = use_tf8 ? sizeof(uint8_t) : sizeof(float); // create input buffer { const auto &inputDims_opt = snpe->getInputDimensions(input_tensor_name); const zdl::DlSystem::TensorShape& bufferShape = *inputDims_opt; std::vector strides(bufferShape.rank()); - strides[strides.size() - 1] = sizeof(float); + strides[strides.size() - 1] = size_of_input; size_t product = 1; for (size_t i = 0; i < bufferShape.rank(); i++) product *= bufferShape[i]; size_t stride = strides[strides.size() - 1]; @@ -86,7 +89,10 @@ SNPEModel::SNPEModel(const char *path, float *loutput, size_t loutput_size, int strides[i-1] = stride; } printf("input product is %lu\n", product); - inputBuffer = ubFactory.createUserBuffer(NULL, product*sizeof(float), strides, &userBufferEncodingFloat); + inputBuffer = ubFactory.createUserBuffer(NULL, + product*size_of_input, + strides, + use_tf8 ? (zdl::DlSystem::UserBufferEncoding*)&userBufferEncodingTf8 : (zdl::DlSystem::UserBufferEncoding*)&userBufferEncodingFloat); inputMap.add(input_tensor_name, inputBuffer.get()); } diff --git a/selfdrive/modeld/runners/snpemodel.h b/selfdrive/modeld/runners/snpemodel.h index ba51fdced..ed9d58d1e 100644 --- a/selfdrive/modeld/runners/snpemodel.h +++ b/selfdrive/modeld/runners/snpemodel.h @@ -23,7 +23,7 @@ class SNPEModel : public RunModel { public: - SNPEModel(const char *path, float *loutput, size_t loutput_size, int runtime, bool luse_extra = false); + SNPEModel(const char *path, float *loutput, size_t loutput_size, int runtime, bool luse_extra = false, bool use_tf8 = false); void addRecurrent(float *state, int state_size); void addTrafficConvention(float *state, int state_size); void addCalib(float *state, int state_size); @@ -52,6 +52,7 @@ private: std::unique_ptr inputBuffer; float *input; size_t input_size; + bool use_tf8; // snpe output stuff zdl::DlSystem::UserBufferMap outputMap; diff --git a/selfdrive/modeld/thneed/compile.cc b/selfdrive/modeld/thneed/compile.cc index 8698ce482..f76c63b2b 100644 --- a/selfdrive/modeld/thneed/compile.cc +++ b/selfdrive/modeld/thneed/compile.cc @@ -3,7 +3,7 @@ #include "selfdrive/modeld/runners/snpemodel.h" #include "selfdrive/modeld/thneed/thneed.h" -#include "selfdrive/hardware/hw.h" +#include "system/hardware/hw.h" #define TEMPORAL_SIZE 512 #define DESIRE_LEN 8 diff --git a/selfdrive/modeld/transforms/transform.cc b/selfdrive/modeld/transforms/transform.cc index 8929f56a9..cabc58a46 100644 --- a/selfdrive/modeld/transforms/transform.cc +++ b/selfdrive/modeld/transforms/transform.cc @@ -25,7 +25,7 @@ void transform_destroy(Transform* s) { void transform_queue(Transform* s, cl_command_queue q, - cl_mem in_yuv, int in_width, int in_height, + cl_mem in_yuv, int in_width, int in_height, int in_stride, int in_uv_offset, cl_mem out_y, cl_mem out_u, cl_mem out_v, int out_width, int out_height, const mat3& projection) { @@ -44,28 +44,30 @@ void transform_queue(Transform* s, const int in_y_width = in_width; const int in_y_height = in_height; + const int in_y_px_stride = 1; const int in_uv_width = in_width/2; const int in_uv_height = in_height/2; - const int in_y_offset = 0; - const int in_u_offset = in_y_offset + in_y_width*in_y_height; - const int in_v_offset = in_u_offset + in_uv_width*in_uv_height; + const int in_uv_px_stride = 2; + const int in_u_offset = in_uv_offset; + const int in_v_offset = in_uv_offset + 1; const int out_y_width = out_width; const int out_y_height = out_height; const int out_uv_width = out_width/2; const int out_uv_height = out_height/2; - CL_CHECK(clSetKernelArg(s->krnl, 0, sizeof(cl_mem), &in_yuv)); - CL_CHECK(clSetKernelArg(s->krnl, 1, sizeof(cl_int), &in_y_width)); - CL_CHECK(clSetKernelArg(s->krnl, 2, sizeof(cl_int), &in_y_offset)); - CL_CHECK(clSetKernelArg(s->krnl, 3, sizeof(cl_int), &in_y_height)); - CL_CHECK(clSetKernelArg(s->krnl, 4, sizeof(cl_int), &in_y_width)); - CL_CHECK(clSetKernelArg(s->krnl, 5, sizeof(cl_mem), &out_y)); - CL_CHECK(clSetKernelArg(s->krnl, 6, sizeof(cl_int), &out_y_width)); - CL_CHECK(clSetKernelArg(s->krnl, 7, sizeof(cl_int), &zero)); - CL_CHECK(clSetKernelArg(s->krnl, 8, sizeof(cl_int), &out_y_height)); - CL_CHECK(clSetKernelArg(s->krnl, 9, sizeof(cl_int), &out_y_width)); - CL_CHECK(clSetKernelArg(s->krnl, 10, sizeof(cl_mem), &s->m_y_cl)); + CL_CHECK(clSetKernelArg(s->krnl, 0, sizeof(cl_mem), &in_yuv)); // src + CL_CHECK(clSetKernelArg(s->krnl, 1, sizeof(cl_int), &in_stride)); // src_row_stride + CL_CHECK(clSetKernelArg(s->krnl, 2, sizeof(cl_int), &in_y_px_stride)); // src_px_stride + CL_CHECK(clSetKernelArg(s->krnl, 3, sizeof(cl_int), &zero)); // src_offset + CL_CHECK(clSetKernelArg(s->krnl, 4, sizeof(cl_int), &in_y_height)); // src_rows + CL_CHECK(clSetKernelArg(s->krnl, 5, sizeof(cl_int), &in_y_width)); // src_cols + CL_CHECK(clSetKernelArg(s->krnl, 6, sizeof(cl_mem), &out_y)); // dst + CL_CHECK(clSetKernelArg(s->krnl, 7, sizeof(cl_int), &out_y_width)); // dst_row_stride + CL_CHECK(clSetKernelArg(s->krnl, 8, sizeof(cl_int), &zero)); // dst_offset + CL_CHECK(clSetKernelArg(s->krnl, 9, sizeof(cl_int), &out_y_height)); // dst_rows + CL_CHECK(clSetKernelArg(s->krnl, 10, sizeof(cl_int), &out_y_width)); // dst_cols + CL_CHECK(clSetKernelArg(s->krnl, 11, sizeof(cl_mem), &s->m_y_cl)); // M const size_t work_size_y[2] = {(size_t)out_y_width, (size_t)out_y_height}; @@ -74,21 +76,21 @@ void transform_queue(Transform* s, const size_t work_size_uv[2] = {(size_t)out_uv_width, (size_t)out_uv_height}; - CL_CHECK(clSetKernelArg(s->krnl, 1, sizeof(cl_int), &in_uv_width)); - CL_CHECK(clSetKernelArg(s->krnl, 2, sizeof(cl_int), &in_u_offset)); - CL_CHECK(clSetKernelArg(s->krnl, 3, sizeof(cl_int), &in_uv_height)); - CL_CHECK(clSetKernelArg(s->krnl, 4, sizeof(cl_int), &in_uv_width)); - CL_CHECK(clSetKernelArg(s->krnl, 5, sizeof(cl_mem), &out_u)); - CL_CHECK(clSetKernelArg(s->krnl, 6, sizeof(cl_int), &out_uv_width)); - CL_CHECK(clSetKernelArg(s->krnl, 7, sizeof(cl_int), &zero)); - CL_CHECK(clSetKernelArg(s->krnl, 8, sizeof(cl_int), &out_uv_height)); - CL_CHECK(clSetKernelArg(s->krnl, 9, sizeof(cl_int), &out_uv_width)); - CL_CHECK(clSetKernelArg(s->krnl, 10, sizeof(cl_mem), &s->m_uv_cl)); - + CL_CHECK(clSetKernelArg(s->krnl, 2, sizeof(cl_int), &in_uv_px_stride)); // src_px_stride + CL_CHECK(clSetKernelArg(s->krnl, 3, sizeof(cl_int), &in_u_offset)); // src_offset + CL_CHECK(clSetKernelArg(s->krnl, 4, sizeof(cl_int), &in_uv_height)); // src_rows + CL_CHECK(clSetKernelArg(s->krnl, 5, sizeof(cl_int), &in_uv_width)); // src_cols + CL_CHECK(clSetKernelArg(s->krnl, 6, sizeof(cl_mem), &out_u)); // dst + CL_CHECK(clSetKernelArg(s->krnl, 7, sizeof(cl_int), &out_uv_width)); // dst_row_stride + CL_CHECK(clSetKernelArg(s->krnl, 8, sizeof(cl_int), &zero)); // dst_offset + CL_CHECK(clSetKernelArg(s->krnl, 9, sizeof(cl_int), &out_uv_height)); // dst_rows + CL_CHECK(clSetKernelArg(s->krnl, 10, sizeof(cl_int), &out_uv_width)); // dst_cols + CL_CHECK(clSetKernelArg(s->krnl, 11, sizeof(cl_mem), &s->m_uv_cl)); // M + CL_CHECK(clEnqueueNDRangeKernel(q, s->krnl, 2, NULL, (const size_t*)&work_size_uv, NULL, 0, 0, NULL)); - CL_CHECK(clSetKernelArg(s->krnl, 2, sizeof(cl_int), &in_v_offset)); - CL_CHECK(clSetKernelArg(s->krnl, 5, sizeof(cl_mem), &out_v)); + CL_CHECK(clSetKernelArg(s->krnl, 3, sizeof(cl_int), &in_v_offset)); // src_ofset + CL_CHECK(clSetKernelArg(s->krnl, 6, sizeof(cl_mem), &out_v)); // dst CL_CHECK(clEnqueueNDRangeKernel(q, s->krnl, 2, NULL, (const size_t*)&work_size_uv, NULL, 0, 0, NULL)); diff --git a/selfdrive/modeld/transforms/transform.cl b/selfdrive/modeld/transforms/transform.cl index 8ad186935..357ef8732 100644 --- a/selfdrive/modeld/transforms/transform.cl +++ b/selfdrive/modeld/transforms/transform.cl @@ -6,9 +6,9 @@ #define INTER_REMAP_COEF_SCALE (1 << INTER_REMAP_COEF_BITS) __kernel void warpPerspective(__global const uchar * src, - int src_step, int src_offset, int src_rows, int src_cols, + int src_row_stride, int src_px_stride, int src_offset, int src_rows, int src_cols, __global uchar * dst, - int dst_step, int dst_offset, int dst_rows, int dst_cols, + int dst_row_stride, int dst_offset, int dst_rows, int dst_cols, __constant float * M) { int dx = get_global_id(0); @@ -28,18 +28,18 @@ __kernel void warpPerspective(__global const uchar * src, short ax = (short)(X & (INTER_TAB_SIZE - 1)); int v0 = (sx >= 0 && sx < src_cols && sy >= 0 && sy < src_rows) ? - convert_int(src[mad24(sy, src_step, src_offset + sx)]) : 0; + convert_int(src[mad24(sy, src_row_stride, src_offset + sx*src_px_stride)]) : 0; int v1 = (sx+1 >= 0 && sx+1 < src_cols && sy >= 0 && sy < src_rows) ? - convert_int(src[mad24(sy, src_step, src_offset + (sx+1))]) : 0; + convert_int(src[mad24(sy, src_row_stride, src_offset + (sx+1)*src_px_stride)]) : 0; int v2 = (sx >= 0 && sx < src_cols && sy+1 >= 0 && sy+1 < src_rows) ? - convert_int(src[mad24(sy+1, src_step, src_offset + sx)]) : 0; + convert_int(src[mad24(sy+1, src_row_stride, src_offset + sx*src_px_stride)]) : 0; int v3 = (sx+1 >= 0 && sx+1 < src_cols && sy+1 >= 0 && sy+1 < src_rows) ? - convert_int(src[mad24(sy+1, src_step, src_offset + (sx+1))]) : 0; + convert_int(src[mad24(sy+1, src_row_stride, src_offset + (sx+1)*src_px_stride)]) : 0; float taby = 1.f/INTER_TAB_SIZE*ay; float tabx = 1.f/INTER_TAB_SIZE*ax; - int dst_index = mad24(dy, dst_step, dst_offset + dx); + int dst_index = mad24(dy, dst_row_stride, dst_offset + dx); int itab0 = convert_short_sat_rte( (1.0f-taby)*(1.0f-tabx) * INTER_REMAP_COEF_SCALE ); int itab1 = convert_short_sat_rte( (1.0f-taby)*tabx * INTER_REMAP_COEF_SCALE ); diff --git a/selfdrive/modeld/transforms/transform.h b/selfdrive/modeld/transforms/transform.h index a1629a517..771a7054b 100644 --- a/selfdrive/modeld/transforms/transform.h +++ b/selfdrive/modeld/transforms/transform.h @@ -19,7 +19,7 @@ void transform_init(Transform* s, cl_context ctx, cl_device_id device_id); void transform_destroy(Transform* transform); void transform_queue(Transform* s, cl_command_queue q, - cl_mem yuv, int in_width, int in_height, + cl_mem yuv, int in_width, int in_height, int in_stride, int in_uv_offset, cl_mem out_y, cl_mem out_u, cl_mem out_v, int out_width, int out_height, const mat3& projection); diff --git a/selfdrive/monitoring/dmonitoringd.py b/selfdrive/monitoring/dmonitoringd.py index 9a3196030..c31e9da43 100755 --- a/selfdrive/monitoring/dmonitoringd.py +++ b/selfdrive/monitoring/dmonitoringd.py @@ -18,7 +18,7 @@ def dmonitoringd_thread(sm=None, pm=None): pm = messaging.PubMaster(['driverMonitoringState']) if sm is None: - sm = messaging.SubMaster(['driverState', 'liveCalibration', 'carState', 'controlsState', 'modelV2'], poll=['driverState']) + sm = messaging.SubMaster(['driverStateV2', 'liveCalibration', 'carState', 'controlsState', 'modelV2'], poll=['driverStateV2']) driver_status = DriverStatus(rhd=Params().get_bool("IsRHD")) @@ -34,7 +34,7 @@ def dmonitoringd_thread(sm=None, pm=None): while True: sm.update() - if not sm.updated['driverState']: + if not sm.updated['driverStateV2']: continue # Get interaction @@ -51,7 +51,7 @@ def dmonitoringd_thread(sm=None, pm=None): # Get data from dmonitoringmodeld events = Events() - driver_status.update_states(sm['driverState'], sm['liveCalibration'].rpyCalib, sm['carState'].vEgo, sm['controlsState'].enabled) + driver_status.update_states(sm['driverStateV2'], sm['liveCalibration'].rpyCalib, sm['carState'].vEgo, sm['controlsState'].enabled) # Block engaging after max number of distrations if driver_status.terminal_alert_cnt >= driver_status.settings._MAX_TERMINAL_ALERTS or \ @@ -79,6 +79,7 @@ def dmonitoringd_thread(sm=None, pm=None): "isLowStd": driver_status.pose.low_std, "hiStdCount": driver_status.hi_stds, "isActiveMode": driver_status.active_monitoring_mode, + "isRHD": driver_status.wheel_on_right, } pm.send('driverMonitoringState', dat) diff --git a/selfdrive/monitoring/driver_monitor.py b/selfdrive/monitoring/driver_monitor.py index 66b28c39b..48d4303aa 100644 --- a/selfdrive/monitoring/driver_monitor.py +++ b/selfdrive/monitoring/driver_monitor.py @@ -3,9 +3,9 @@ from math import atan2 from cereal import car from common.numpy_fast import interp from common.realtime import DT_DMON -from selfdrive.hardware import TICI from common.filter_simple import FirstOrderFilter from common.stat_live import RunningStatFilter +from common.transformations.camera import tici_d_frame_size EventName = car.CarEvent.EventName @@ -16,7 +16,7 @@ EventName = car.CarEvent.EventName # ****************************************************************************************** class DRIVER_MONITOR_SETTINGS(): - def __init__(self, TICI=TICI, DT_DMON=DT_DMON): + def __init__(self): self._DT_DMON = DT_DMON # ref (page15-16): https://eur-lex.europa.eu/legal-content/EN/TXT/PDF/?uri=CELEX:42018X1947&rid=2 self._AWARENESS_TIME = 30. # passive wheeltouch total timeout @@ -26,33 +26,30 @@ class DRIVER_MONITOR_SETTINGS(): self._DISTRACTED_PRE_TIME_TILL_TERMINAL = 8. self._DISTRACTED_PROMPT_TIME_TILL_TERMINAL = 6. - self._FACE_THRESHOLD = 0.5 - self._PARTIAL_FACE_THRESHOLD = 0.8 if TICI else 0.45 - self._EYE_THRESHOLD = 0.65 if TICI else 0.6 - self._SG_THRESHOLD = 0.925 if TICI else 0.91 - self._BLINK_THRESHOLD = 0.8 if TICI else 0.55 - self._BLINK_THRESHOLD_SLACK = 0.9 if TICI else 0.7 - self._BLINK_THRESHOLD_STRICT = self._BLINK_THRESHOLD + self._FACE_THRESHOLD = 0.7 + self._EYE_THRESHOLD = 0.65 + self._SG_THRESHOLD = 0.9 + self._BLINK_THRESHOLD = 0.87 - self._EE_THRESH11 = 0.75 if TICI else 0.4 - self._EE_THRESH12 = 3.25 if TICI else 2.45 + self._EE_THRESH11 = 0.75 + self._EE_THRESH12 = 3.25 self._EE_THRESH21 = 0.01 self._EE_THRESH22 = 0.35 - self._POSE_PITCH_THRESHOLD = 0.3237 - self._POSE_PITCH_THRESHOLD_SLACK = 0.3657 + self._POSE_PITCH_THRESHOLD = 0.3133 + self._POSE_PITCH_THRESHOLD_SLACK = 0.3237 self._POSE_PITCH_THRESHOLD_STRICT = self._POSE_PITCH_THRESHOLD - self._POSE_YAW_THRESHOLD = 0.3109 - self._POSE_YAW_THRESHOLD_SLACK = 0.4294 + self._POSE_YAW_THRESHOLD = 0.4020 + self._POSE_YAW_THRESHOLD_SLACK = 0.5042 self._POSE_YAW_THRESHOLD_STRICT = self._POSE_YAW_THRESHOLD - self._PITCH_NATURAL_OFFSET = 0.057 # initial value before offset is learned - self._YAW_NATURAL_OFFSET = 0.11 # initial value before offset is learned + self._PITCH_NATURAL_OFFSET = 0.029 # initial value before offset is learned + self._YAW_NATURAL_OFFSET = 0.097 # initial value before offset is learned self._PITCH_MAX_OFFSET = 0.124 self._PITCH_MIN_OFFSET = -0.0881 self._YAW_MAX_OFFSET = 0.289 self._YAW_MIN_OFFSET = -0.0246 - self._POSESTD_THRESHOLD = 0.315 + self._POSESTD_THRESHOLD = 0.3 self._HI_STD_FALLBACK_TIME = int(10 / self._DT_DMON) # fall back to wheel touch if model is uncertain for 10s self._DISTRACTED_FILTER_TS = 0.25 # 0.6Hz @@ -60,6 +57,9 @@ class DRIVER_MONITOR_SETTINGS(): self._POSE_OFFSET_MIN_COUNT = int(60 / self._DT_DMON) # valid data counts before calibration completes, 1min cumulative self._POSE_OFFSET_MAX_COUNT = int(360 / self._DT_DMON) # stop deweighting new data after 6 min, aka "short term memory" + self._WHEELPOS_THRESHOLD = 0.5 + self._WHEELPOS_FILTER_MIN_COUNT = int(5 / self._DT_DMON) + self._RECOVERY_FACTOR_MAX = 5. # relative to minus step change self._RECOVERY_FACTOR_MIN = 1.25 # relative to minus step change @@ -67,9 +67,9 @@ class DRIVER_MONITOR_SETTINGS(): self._MAX_TERMINAL_DURATION = int(30 / self._DT_DMON) # not allowed to engage after 30s of terminal alerts -# model output refers to center of cropped image, so need to apply the x displacement offset -RESIZED_FOCAL = 320.0 -H, W, FULL_W = 320, 160, 426 +# model output refers to center of undistorted+leveled image +EFL = 598.0 # focal length in K +W, H = tici_d_frame_size # corrected image has same size as raw class DistractedType: NOT_DISTRACTED = 0 @@ -77,22 +77,22 @@ class DistractedType: DISTRACTED_BLINK = 2 DISTRACTED_E2E = 4 -def face_orientation_from_net(angles_desc, pos_desc, rpy_calib, is_rhd): +def face_orientation_from_net(angles_desc, pos_desc, rpy_calib): # the output of these angles are in device frame # so from driver's perspective, pitch is up and yaw is right pitch_net, yaw_net, roll_net = angles_desc - face_pixel_position = ((pos_desc[0] + .5)*W - W + FULL_W, (pos_desc[1]+.5)*H) - yaw_focal_angle = atan2(face_pixel_position[0] - FULL_W//2, RESIZED_FOCAL) - pitch_focal_angle = atan2(face_pixel_position[1] - H//2, RESIZED_FOCAL) + face_pixel_position = ((pos_desc[0]+0.5)*W, (pos_desc[1]+0.5)*H) + yaw_focal_angle = atan2(face_pixel_position[0] - W//2, EFL) + pitch_focal_angle = atan2(face_pixel_position[1] - H//2, EFL) pitch = pitch_net + pitch_focal_angle yaw = -yaw_net + yaw_focal_angle # no calib for roll pitch -= rpy_calib[1] - yaw -= rpy_calib[2] * (1 - 2 * int(is_rhd)) # lhd -> -=, rhd -> += + yaw -= rpy_calib[2] return roll_net, pitch, yaw class DriverPose(): @@ -113,7 +113,6 @@ class DriverBlink(): def __init__(self): self.left_blink = 0. self.right_blink = 0. - self.cfactor = 1. class DriverStatus(): def __init__(self, rhd=False, settings=DRIVER_MONITOR_SETTINGS()): @@ -121,7 +120,7 @@ class DriverStatus(): self.settings = settings # init driver status - self.is_rhd_region = rhd + self.wheelpos_learner = RunningStatFilter() self.pose = DriverPose(self.settings._POSE_OFFSET_MAX_COUNT) self.pose_calibrated = False self.blink = DriverBlink() @@ -138,8 +137,9 @@ class DriverStatus(): self.distracted_types = [] self.driver_distracted = False self.driver_distraction_filter = FirstOrderFilter(0., self.settings._DISTRACTED_FILTER_TS, self.settings._DT_DMON) + self.wheel_on_right = False + self.rhd_toggled = rhd self.face_detected = False - self.face_partial = False self.terminal_alert_cnt = 0 self.terminal_time = 0 self.step_change = 0. @@ -198,7 +198,7 @@ class DriverStatus(): yaw_error > self.settings._POSE_YAW_THRESHOLD*self.pose.cfactor_yaw: distracted_types.append(DistractedType.DISTRACTED_POSE) - if (self.blink.left_blink + self.blink.right_blink)*0.5 > self.settings._BLINK_THRESHOLD*self.blink.cfactor: + if (self.blink.left_blink + self.blink.right_blink)*0.5 > self.settings._BLINK_THRESHOLD: distracted_types.append(DistractedType.DISTRACTED_BLINK) if self.ee1_calibrated: @@ -215,13 +215,7 @@ class DriverStatus(): return distracted_types def set_policy(self, model_data, car_speed): - ep = min(model_data.meta.engagedProb, 0.8) / 0.8 # engaged prob bp = model_data.meta.disengagePredictions.brakeDisengageProbs[0] # brake disengage prob in next 2s - # TODO: retune adaptive blink - self.blink.cfactor = interp(ep, [0, 0.5, 1], - [self.settings._BLINK_THRESHOLD_STRICT, - self.settings._BLINK_THRESHOLD, - self.settings._BLINK_THRESHOLD_SLACK]) / self.settings._BLINK_THRESHOLD k1 = max(-0.00156*((car_speed-16)**2)+0.6, 0.2) bp_normal = max(min(bp / k1, 0.5),0) self.pose.cfactor_pitch = interp(bp_normal, [0, 0.5], @@ -232,28 +226,36 @@ class DriverStatus(): self.settings._POSE_YAW_THRESHOLD_STRICT]) / self.settings._POSE_YAW_THRESHOLD def update_states(self, driver_state, cal_rpy, car_speed, op_engaged): - if not all(len(x) > 0 for x in (driver_state.faceOrientation, driver_state.facePosition, - driver_state.faceOrientationStd, driver_state.facePositionStd, - driver_state.readyProb, driver_state.notReadyProb)): + rhd_pred = driver_state.wheelOnRightProb + if car_speed > 0.01: + self.wheelpos_learner.push_and_update(rhd_pred) + if self.wheelpos_learner.filtered_stat.n > self.settings._WHEELPOS_FILTER_MIN_COUNT: + self.wheel_on_right = self.wheelpos_learner.filtered_stat.M > self.settings._WHEELPOS_THRESHOLD + else: + self.wheel_on_right = rhd_pred > self.settings._WHEELPOS_THRESHOLD + driver_data = driver_state.rightDriverData if self.rhd_toggled else driver_state.leftDriverData + if not all(len(x) > 0 for x in (driver_data.faceOrientation, driver_data.facePosition, + driver_data.faceOrientationStd, driver_data.facePositionStd, + driver_data.readyProb, driver_data.notReadyProb)): return - self.face_partial = driver_state.partialFace > self.settings._PARTIAL_FACE_THRESHOLD - self.face_detected = driver_state.faceProb > self.settings._FACE_THRESHOLD or self.face_partial - self.pose.roll, self.pose.pitch, self.pose.yaw = face_orientation_from_net(driver_state.faceOrientation, driver_state.facePosition, cal_rpy, self.is_rhd_region) - self.pose.pitch_std = driver_state.faceOrientationStd[0] - self.pose.yaw_std = driver_state.faceOrientationStd[1] - # self.pose.roll_std = driver_state.faceOrientationStd[2] + self.face_detected = driver_data.faceProb > self.settings._FACE_THRESHOLD + self.pose.roll, self.pose.pitch, self.pose.yaw = face_orientation_from_net(driver_data.faceOrientation, driver_data.facePosition, cal_rpy) + if self.wheel_on_right: + self.pose.yaw *= -1 + self.pose.pitch_std = driver_data.faceOrientationStd[0] + self.pose.yaw_std = driver_data.faceOrientationStd[1] model_std_max = max(self.pose.pitch_std, self.pose.yaw_std) - self.pose.low_std = model_std_max < self.settings._POSESTD_THRESHOLD and not self.face_partial - self.blink.left_blink = driver_state.leftBlinkProb * (driver_state.leftEyeProb > self.settings._EYE_THRESHOLD) * (driver_state.sunglassesProb < self.settings._SG_THRESHOLD) - self.blink.right_blink = driver_state.rightBlinkProb * (driver_state.rightEyeProb > self.settings._EYE_THRESHOLD) * (driver_state.sunglassesProb < self.settings._SG_THRESHOLD) - self.eev1 = driver_state.notReadyProb[1] - self.eev2 = driver_state.readyProb[0] + self.pose.low_std = model_std_max < self.settings._POSESTD_THRESHOLD + self.blink.left_blink = driver_data.leftBlinkProb * (driver_data.leftEyeProb > self.settings._EYE_THRESHOLD) * (driver_data.sunglassesProb < self.settings._SG_THRESHOLD) + self.blink.right_blink = driver_data.rightBlinkProb * (driver_data.rightEyeProb > self.settings._EYE_THRESHOLD) * (driver_data.sunglassesProb < self.settings._SG_THRESHOLD) + self.eev1 = driver_data.notReadyProb[1] + self.eev2 = driver_data.readyProb[0] self.distracted_types = self._get_distracted_types() self.driver_distracted = (DistractedType.DISTRACTED_POSE in self.distracted_types or DistractedType.DISTRACTED_BLINK in self.distracted_types) and \ - driver_state.faceProb > self.settings._FACE_THRESHOLD and self.pose.low_std + driver_data.faceProb > self.settings._FACE_THRESHOLD and self.pose.low_std self.driver_distraction_filter.update(self.driver_distracted) # update offseter diff --git a/selfdrive/hardware/tici/__init__.py b/selfdrive/navd/__init__.py similarity index 100% rename from selfdrive/hardware/tici/__init__.py rename to selfdrive/navd/__init__.py diff --git a/selfdrive/navd/helpers.py b/selfdrive/navd/helpers.py new file mode 100644 index 000000000..eda813154 --- /dev/null +++ b/selfdrive/navd/helpers.py @@ -0,0 +1,174 @@ +from __future__ import annotations + +import json +import math +from typing import Any, Dict, List, Optional, Tuple, Union, cast + +from common.conversions import Conversions +from common.numpy_fast import clip +from common.params import Params + +EARTH_MEAN_RADIUS = 6371007.2 +SPEED_CONVERSIONS = { + 'km/h': Conversions.KPH_TO_MS, + 'mph': Conversions.MPH_TO_MS, + } + + +class Coordinate: + def __init__(self, latitude: float, longitude: float) -> None: + self.latitude = latitude + self.longitude = longitude + self.annotations: Dict[str, float] = {} + + @classmethod + def from_mapbox_tuple(cls, t: Tuple[float, float]) -> Coordinate: + return cls(t[1], t[0]) + + def as_dict(self) -> Dict[str, float]: + return {'latitude': self.latitude, 'longitude': self.longitude} + + def __str__(self) -> str: + return f"({self.latitude}, {self.longitude})" + + def __eq__(self, other) -> bool: + if not isinstance(other, Coordinate): + return False + return (self.latitude == other.latitude) and (self.longitude == other.longitude) + + def __sub__(self, other: Coordinate) -> Coordinate: + return Coordinate(self.latitude - other.latitude, self.longitude - other.longitude) + + def __add__(self, other: Coordinate) -> Coordinate: + return Coordinate(self.latitude + other.latitude, self.longitude + other.longitude) + + def __mul__(self, c: float) -> Coordinate: + return Coordinate(self.latitude * c, self.longitude * c) + + def dot(self, other: Coordinate) -> float: + return self.latitude * other.latitude + self.longitude * other.longitude + + def distance_to(self, other: Coordinate) -> float: + # Haversine formula + dlat = math.radians(other.latitude - self.latitude) + dlon = math.radians(other.longitude - self.longitude) + + haversine_dlat = math.sin(dlat / 2.0) + haversine_dlat *= haversine_dlat + haversine_dlon = math.sin(dlon / 2.0) + haversine_dlon *= haversine_dlon + + y = haversine_dlat \ + + math.cos(math.radians(self.latitude)) \ + * math.cos(math.radians(other.latitude)) \ + * haversine_dlon + x = 2 * math.asin(math.sqrt(y)) + return x * EARTH_MEAN_RADIUS + + +def minimum_distance(a: Coordinate, b: Coordinate, p: Coordinate): + if a.distance_to(b) < 0.01: + return a.distance_to(p) + + ap = p - a + ab = b - a + t = clip(ap.dot(ab) / ab.dot(ab), 0.0, 1.0) + projection = a + ab * t + return projection.distance_to(p) + + +def distance_along_geometry(geometry: List[Coordinate], pos: Coordinate) -> float: + if len(geometry) <= 2: + return geometry[0].distance_to(pos) + + # 1. Find segment that is closest to current position + # 2. Total distance is sum of distance to start of closest segment + # + all previous segments + total_distance = 0.0 + total_distance_closest = 0.0 + closest_distance = 1e9 + + for i in range(len(geometry) - 1): + d = minimum_distance(geometry[i], geometry[i + 1], pos) + + if d < closest_distance: + closest_distance = d + total_distance_closest = total_distance + geometry[i].distance_to(pos) + + total_distance += geometry[i].distance_to(geometry[i + 1]) + + return total_distance_closest + + +def coordinate_from_param(param: str, params: Optional[Params] = None) -> Optional[Coordinate]: + if params is None: + params = Params() + + json_str = params.get(param) + if json_str is None: + return None + + pos = json.loads(json_str) + if 'latitude' not in pos or 'longitude' not in pos: + return None + + return Coordinate(pos['latitude'], pos['longitude']) + + +def string_to_direction(direction: str) -> str: + for d in ['left', 'right', 'straight']: + if d in direction: + return d + return 'none' + + +def maxspeed_to_ms(maxspeed: Dict[str, Union[str, float]]) -> float: + unit = cast(str, maxspeed['unit']) + speed = cast(float, maxspeed['speed']) + return SPEED_CONVERSIONS[unit] * speed + + +def parse_banner_instructions(instruction: Any, banners: Any, distance_to_maneuver: float = 0.0) -> None: + if not len(banners): + return + + current_banner = banners[0] + + # A segment can contain multiple banners, find one that we need to show now + for banner in banners: + if distance_to_maneuver < banner['distanceAlongGeometry']: + current_banner = banner + + # Only show banner when close enough to maneuver + instruction.showFull = distance_to_maneuver < current_banner['distanceAlongGeometry'] + + # Primary + p = current_banner['primary'] + if 'text' in p: + instruction.maneuverPrimaryText = p['text'] + if 'type' in p: + instruction.maneuverType = p['type'] + if 'modifier' in p: + instruction.maneuverModifier = p['modifier'] + + # Secondary + if 'secondary' in current_banner: + instruction.maneuverSecondaryText = current_banner['secondary']['text'] + + # Lane lines + if 'sub' in current_banner: + lanes = [] + for component in current_banner['sub']['components']: + if component['type'] != 'lane': + continue + + lane = { + 'active': component['active'], + 'directions': [string_to_direction(d) for d in component['directions']], + } + + if 'active_direction' in component: + lane['activeDirection'] = string_to_direction(component['active_direction']) + + lanes.append(lane) + instruction.lanes = lanes diff --git a/selfdrive/navd/navd.py b/selfdrive/navd/navd.py new file mode 100755 index 000000000..89a1c9bdf --- /dev/null +++ b/selfdrive/navd/navd.py @@ -0,0 +1,302 @@ +#!/usr/bin/env python3 +import math +import os +import threading + +import requests +import numpy as np + +import cereal.messaging as messaging +from cereal import log +from common.api import Api +from common.params import Params +from common.realtime import Ratekeeper +from common.transformations.coordinates import ecef2geodetic +from selfdrive.navd.helpers import (Coordinate, coordinate_from_param, + distance_along_geometry, maxspeed_to_ms, + minimum_distance, + parse_banner_instructions) +from system.swaglog import cloudlog + +REROUTE_DISTANCE = 25 +MANEUVER_TRANSITION_THRESHOLD = 10 +VALID_POS_STD = 50.0 + + +class RouteEngine: + def __init__(self, sm, pm): + self.sm = sm + self.pm = pm + + self.params = Params() + + # Get last gps position from params + self.last_position = coordinate_from_param("LastGPSPosition", self.params) + self.last_bearing = None + + self.gps_ok = False + self.localizer_valid = False + + self.nav_destination = None + self.step_idx = None + self.route = None + self.route_geometry = None + + self.recompute_backoff = 0 + self.recompute_countdown = 0 + + self.ui_pid = None + + if "MAPBOX_TOKEN" in os.environ: + self.mapbox_token = os.environ["MAPBOX_TOKEN"] + self.mapbox_host = "https://api.mapbox.com" + else: + try: + self.mapbox_token = Api(self.params.get("DongleId", encoding='utf8')).get_token(expiry_hours=4 * 7 * 24) + except FileNotFoundError: + cloudlog.exception("Failed to generate mapbox token due to missing private key. Ensure device is registered.") + self.mapbox_token = "" + self.mapbox_host = "https://maps.comma.ai" + + def update(self): + self.sm.update(0) + + if self.sm.updated["managerState"]: + ui_pid = [p.pid for p in self.sm["managerState"].processes if p.name == "ui" and p.running] + if ui_pid: + if self.ui_pid and self.ui_pid != ui_pid[0]: + cloudlog.warning("UI restarting, sending route") + threading.Timer(5.0, self.send_route).start() + self.ui_pid = ui_pid[0] + + self.update_location() + self.recompute_route() + self.send_instruction() + + def update_location(self): + location = self.sm['liveLocationKalman'] + laikad = self.sm['gnssMeasurements'] + + locationd_valid = (location.status == log.LiveLocationKalman.Status.valid) and location.positionGeodetic.valid + laikad_valid = laikad.positionECEF.valid and np.linalg.norm(laikad.positionECEF.std) < VALID_POS_STD + + self.localizer_valid = locationd_valid or laikad_valid + self.gps_ok = location.gpsOK or laikad_valid + + if locationd_valid: + self.last_bearing = math.degrees(location.calibratedOrientationNED.value[2]) + self.last_position = Coordinate(location.positionGeodetic.value[0], location.positionGeodetic.value[1]) + elif laikad_valid: + geodetic = ecef2geodetic(laikad.positionECEF.value) + self.last_position = Coordinate(geodetic[0], geodetic[1]) + self.last_bearing = None + + def recompute_route(self): + if self.last_position is None: + return + + new_destination = coordinate_from_param("NavDestination", self.params) + if new_destination is None: + self.clear_route() + return + + should_recompute = self.should_recompute() + if new_destination != self.nav_destination: + cloudlog.warning(f"Got new destination from NavDestination param {new_destination}") + should_recompute = True + + # Don't recompute when GPS drifts in tunnels + if not self.gps_ok and self.step_idx is not None: + return + + if self.recompute_countdown == 0 and should_recompute: + self.recompute_countdown = 2**self.recompute_backoff + self.recompute_backoff = min(6, self.recompute_backoff + 1) + self.calculate_route(new_destination) + else: + self.recompute_countdown = max(0, self.recompute_countdown - 1) + + def calculate_route(self, destination): + cloudlog.warning(f"Calculating route {self.last_position} -> {destination}") + self.nav_destination = destination + + params = { + 'access_token': self.mapbox_token, + 'annotations': 'maxspeed', + 'geometries': 'geojson', + 'overview': 'full', + 'steps': 'true', + 'banner_instructions': 'true', + 'alternatives': 'false', + } + + if self.last_bearing is not None: + params['bearings'] = f"{(self.last_bearing + 360) % 360:.0f},90;" + + url = self.mapbox_host + f'/directions/v5/mapbox/driving-traffic/{self.last_position.longitude},{self.last_position.latitude};{destination.longitude},{destination.latitude}' + try: + resp = requests.get(url, params=params) + resp.raise_for_status() + + r = resp.json() + if len(r['routes']): + self.route = r['routes'][0]['legs'][0]['steps'] + self.route_geometry = [] + + maxspeed_idx = 0 + maxspeeds = r['routes'][0]['legs'][0]['annotation']['maxspeed'] + + # Convert coordinates + for step in self.route: + coords = [] + + for c in step['geometry']['coordinates']: + coord = Coordinate.from_mapbox_tuple(c) + + # Last step does not have maxspeed + if (maxspeed_idx < len(maxspeeds)): + maxspeed = maxspeeds[maxspeed_idx] + if ('unknown' not in maxspeed) and ('none' not in maxspeed): + coord.annotations['maxspeed'] = maxspeed_to_ms(maxspeed) + + coords.append(coord) + maxspeed_idx += 1 + + self.route_geometry.append(coords) + maxspeed_idx -= 1 # Every segment ends with the same coordinate as the start of the next + + self.step_idx = 0 + else: + cloudlog.warning("Got empty route response") + self.clear_route() + + except requests.exceptions.RequestException: + cloudlog.exception("failed to get route") + self.clear_route() + + self.send_route() + + def send_instruction(self): + msg = messaging.new_message('navInstruction') + + if self.step_idx is None: + msg.valid = False + self.pm.send('navInstruction', msg) + return + + step = self.route[self.step_idx] + geometry = self.route_geometry[self.step_idx] + along_geometry = distance_along_geometry(geometry, self.last_position) + distance_to_maneuver_along_geometry = step['distance'] - along_geometry + + # Current instruction + msg.navInstruction.maneuverDistance = distance_to_maneuver_along_geometry + parse_banner_instructions(msg.navInstruction, step['bannerInstructions'], distance_to_maneuver_along_geometry) + + # Compute total remaining time and distance + remaning = 1.0 - along_geometry / max(step['distance'], 1) + total_distance = step['distance'] * remaning + total_time = step['duration'] * remaning + total_time_typical = step['duration_typical'] * remaning + + # Add up totals for future steps + for i in range(self.step_idx + 1, len(self.route)): + total_distance += self.route[i]['distance'] + total_time += self.route[i]['duration'] + total_time_typical += self.route[i]['duration_typical'] + + msg.navInstruction.distanceRemaining = total_distance + msg.navInstruction.timeRemaining = total_time + msg.navInstruction.timeRemainingTypical = total_time_typical + + # Speed limit + closest_idx, closest = min(enumerate(geometry), key=lambda p: p[1].distance_to(self.last_position)) + if closest_idx > 0: + # If we are not past the closest point, show previous + if along_geometry < distance_along_geometry(geometry, geometry[closest_idx]): + closest = geometry[closest_idx - 1] + + if ('maxspeed' in closest.annotations) and self.localizer_valid: + msg.navInstruction.speedLimit = closest.annotations['maxspeed'] + + # Speed limit sign type + if 'speedLimitSign' in step: + if step['speedLimitSign'] == 'mutcd': + msg.navInstruction.speedLimitSign = log.NavInstruction.SpeedLimitSign.mutcd + elif step['speedLimitSign'] == 'vienna': + msg.navInstruction.speedLimitSign = log.NavInstruction.SpeedLimitSign.vienna + + self.pm.send('navInstruction', msg) + + # Transition to next route segment + if distance_to_maneuver_along_geometry < -MANEUVER_TRANSITION_THRESHOLD: + if self.step_idx + 1 < len(self.route): + self.step_idx += 1 + self.recompute_backoff = 0 + self.recompute_countdown = 0 + else: + cloudlog.warning("Destination reached") + Params().delete("NavDestination") + + # Clear route if driving away from destination + dist = self.nav_destination.distance_to(self.last_position) + if dist > REROUTE_DISTANCE: + self.clear_route() + + def send_route(self): + coords = [] + + if self.route is not None: + for path in self.route_geometry: + coords += [c.as_dict() for c in path] + + msg = messaging.new_message('navRoute') + msg.navRoute.coordinates = coords + self.pm.send('navRoute', msg) + + def clear_route(self): + self.route = None + self.route_geometry = None + self.step_idx = None + self.nav_destination = None + + def should_recompute(self): + if self.step_idx is None or self.route is None: + return True + + # Don't recompute in last segment, assume destination is reached + if self.step_idx == len(self.route) - 1: + return False + + # Compute closest distance to all line segments in the current path + min_d = REROUTE_DISTANCE + 1 + path = self.route_geometry[self.step_idx] + for i in range(len(path) - 1): + a = path[i] + b = path[i + 1] + + if a.distance_to(b) < 1.0: + continue + + min_d = min(min_d, minimum_distance(a, b, self.last_position)) + + return min_d > REROUTE_DISTANCE + + # TODO: Check for going wrong way in segment + + +def main(sm=None, pm=None): + if sm is None: + sm = messaging.SubMaster(['liveLocationKalman', 'gnssMeasurements', 'managerState']) + if pm is None: + pm = messaging.PubMaster(['navInstruction', 'navRoute']) + + rk = Ratekeeper(1.0) + route_engine = RouteEngine(sm, pm) + while True: + route_engine.update() + rk.keep_time() + + +if __name__ == "__main__": + main() diff --git a/selfdrive/sentry.py b/selfdrive/sentry.py index 5f22bf18e..aa409ea39 100644 --- a/selfdrive/sentry.py +++ b/selfdrive/sentry.py @@ -5,9 +5,9 @@ from sentry_sdk.integrations.threading import ThreadingIntegration from common.params import Params from selfdrive.athena.registration import is_registered_device -from selfdrive.hardware import HARDWARE, PC -from selfdrive.swaglog import cloudlog -from selfdrive.version import get_branch, get_commit, get_origin, get_version, \ +from system.hardware import HARDWARE, PC +from system.swaglog import cloudlog +from system.version import get_branch, get_commit, get_origin, get_version, \ is_comma_remote, is_dirty, is_tested_branch diff --git a/selfdrive/statsd.py b/selfdrive/statsd.py index 5755e5111..7dc002727 100755 --- a/selfdrive/statsd.py +++ b/selfdrive/statsd.py @@ -9,10 +9,10 @@ from typing import NoReturn, Union, List, Dict from common.params import Params from cereal.messaging import SubMaster -from selfdrive.swaglog import cloudlog -from selfdrive.hardware import HARDWARE +from system.swaglog import cloudlog +from system.hardware import HARDWARE from common.file_helpers import atomic_write_in_dir -from selfdrive.version import get_normalized_origin, get_short_branch, get_short_version, is_dirty +from system.version import get_normalized_origin, get_short_branch, get_short_version, is_dirty from selfdrive.loggerd.config import STATS_DIR, STATS_DIR_FILE_LIMIT, STATS_SOCKET, STATS_FLUSH_TIME_S diff --git a/selfdrive/test/helpers.py b/selfdrive/test/helpers.py index 5abc0d964..8cc996c28 100644 --- a/selfdrive/test/helpers.py +++ b/selfdrive/test/helpers.py @@ -2,9 +2,9 @@ import os import time from functools import wraps -from selfdrive.hardware import PC +from system.hardware import PC from selfdrive.manager.process_config import managed_processes -from selfdrive.version import training_version, terms_version +from system.version import training_version, terms_version def set_params_enabled(): diff --git a/selfdrive/test/setup_device_ci.sh b/selfdrive/test/setup_device_ci.sh index 99acc050e..2e5ffeacc 100755 --- a/selfdrive/test/setup_device_ci.sh +++ b/selfdrive/test/setup_device_ci.sh @@ -36,6 +36,8 @@ fi tee $CONTINUE_PATH << EOF #!/usr/bin/bash +sudo abctl --set_success + while true; do if ! sudo systemctl is-active -q ssh; then sudo systemctl start ssh diff --git a/selfdrive/test/test_onroad.py b/selfdrive/test/test_onroad.py index 725b0dd1e..8007afb84 100755 --- a/selfdrive/test/test_onroad.py +++ b/selfdrive/test/test_onroad.py @@ -23,23 +23,23 @@ from tools.lib.logreader import LogReader PROCS = { "selfdrive.controls.controlsd": 35.0, "./loggerd": 10.0, - "./encoderd": 37.3, - "./camerad": 16.5, + "./encoderd": 12.5, + "./camerad": 14.5, "./locationd": 9.1, "selfdrive.controls.plannerd": 11.7, - "./_ui": 21.0, + "./_ui": 19.2, "selfdrive.locationd.paramsd": 9.0, "./_sensord": 6.17, "selfdrive.controls.radard": 4.5, "./_modeld": 4.48, "./boardd": 3.63, - "./_dmonitoringmodeld": 10.0, + "./_dmonitoringmodeld": 5.0, "selfdrive.thermald.thermald": 3.87, "selfdrive.locationd.calibrationd": 2.0, "./_soundd": 1.0, "selfdrive.monitoring.dmonitoringd": 1.90, "./proclogd": 1.54, - "selfdrive.logmessaged": 0.2, + "system.logmessaged": 0.2, "./clocksd": 0.02, "./ubloxd": 0.02, "selfdrive.tombstoned": 0, @@ -60,7 +60,7 @@ TIMINGS = { "roadCameraState": [2.5, 0.35], "driverCameraState": [2.5, 0.35], "modelV2": [2.5, 0.35], - "driverState": [2.5, 0.40], + "driverStateV2": [2.5, 0.40], "liveLocationKalman": [2.5, 0.35], "wideRoadCameraState": [1.5, 0.35], } @@ -118,6 +118,7 @@ class TestOnroad(unittest.TestCase): os.environ['REPLAY'] = "1" os.environ['SKIP_FW_QUERY'] = "1" os.environ['FINGERPRINT'] = "TOYOTA COROLLA TSS2 2019" + os.environ['LOGPRINT'] = 'debug' params = Params() params.clear_all() @@ -221,7 +222,7 @@ class TestOnroad(unittest.TestCase): # TODO: this went up when plannerd cpu usage increased, why? cfgs = [ ("modelV2", 0.050, 0.036), - ("driverState", 0.050, 0.026), + ("driverStateV2", 0.050, 0.026), ] for (s, instant_max, avg_max) in cfgs: ts = [getattr(getattr(m, s), "modelExecutionTime") for m in self.lr if m.which() == s] diff --git a/selfdrive/thermald/fan_controller.py b/selfdrive/thermald/fan_controller.py index b1c701329..2094faeaa 100644 --- a/selfdrive/thermald/fan_controller.py +++ b/selfdrive/thermald/fan_controller.py @@ -3,7 +3,7 @@ from abc import ABC, abstractmethod from common.realtime import DT_TRML from common.numpy_fast import interp -from selfdrive.swaglog import cloudlog +from system.swaglog import cloudlog from selfdrive.controls.lib.pid import PIDController class BaseFanController(ABC): diff --git a/selfdrive/thermald/power_monitoring.py b/selfdrive/thermald/power_monitoring.py index d7a1f2a36..9f009d326 100644 --- a/selfdrive/thermald/power_monitoring.py +++ b/selfdrive/thermald/power_monitoring.py @@ -4,8 +4,8 @@ from typing import Optional from cereal import log from common.params import Params, put_nonblocking from common.realtime import sec_since_boot -from selfdrive.hardware import HARDWARE -from selfdrive.swaglog import cloudlog +from system.hardware import HARDWARE +from system.swaglog import cloudlog from selfdrive.statsd import statlog CAR_VOLTAGE_LOW_PASS_K = 0.091 # LPF gain for 5s tau (dt/tau / (dt/tau + 1)) diff --git a/selfdrive/thermald/thermald.py b/selfdrive/thermald/thermald.py index 7c88dd579..f20c649b4 100755 --- a/selfdrive/thermald/thermald.py +++ b/selfdrive/thermald/thermald.py @@ -17,13 +17,13 @@ from common.filter_simple import FirstOrderFilter from common.params import Params from common.realtime import DT_TRML, sec_since_boot from selfdrive.controls.lib.alertmanager import set_offroad_alert -from selfdrive.hardware import HARDWARE, TICI +from system.hardware import HARDWARE, TICI, AGNOS from selfdrive.loggerd.config import get_available_percent from selfdrive.statsd import statlog -from selfdrive.swaglog import cloudlog +from system.swaglog import cloudlog from selfdrive.thermald.power_monitoring import PowerMonitoring from selfdrive.thermald.fan_controller import TiciFanController -from selfdrive.version import terms_version, training_version +from system.version import terms_version, training_version ThermalStatus = log.DeviceState.ThermalStatus NetworkType = log.DeviceState.NetworkType @@ -31,7 +31,7 @@ NetworkStrength = log.DeviceState.NetworkStrength CURRENT_TAU = 15. # 15s time constant TEMP_TAU = 5. # 5s time constant DISCONNECT_TIMEOUT = 5. # wait 5 seconds before going offroad after disconnect so you get an alert -PANDA_STATES_TIMEOUT = int(1000 * 2.5 * DT_TRML) # 2.5x the expected pandaState frequency +PANDA_STATES_TIMEOUT = int(1000 * 1.5 * DT_TRML) # 1.5x the expected pandaState frequency ThermalBand = namedtuple("ThermalBand", ['min_temp', 'max_temp']) HardwareState = namedtuple("HardwareState", ['network_type', 'network_metered', 'network_strength', 'network_info', 'nvme_temps', 'modem_temps']) @@ -113,7 +113,7 @@ def hw_state_thread(end_event, hw_queue): modem_temps = prev_hw_state.modem_temps # Log modem version once - if TICI and ((modem_version is None) or (modem_nv is None)): + if AGNOS and ((modem_version is None) or (modem_nv is None)): modem_version = HARDWARE.get_modem_version() # pylint: disable=assignment-from-none modem_nv = HARDWARE.get_modem_nv() # pylint: disable=assignment-from-none @@ -134,7 +134,7 @@ def hw_state_thread(end_event, hw_queue): except queue.Full: pass - if TICI and (hw_state.network_info is not None) and (hw_state.network_info.get('state', None) == "REGISTERED"): + if AGNOS and (hw_state.network_info is not None) and (hw_state.network_info.get('state', None) == "REGISTERED"): registered_count += 1 else: registered_count = 0 @@ -220,6 +220,11 @@ def thermald_thread(end_event, hw_queue): if TICI: fan_controller = TiciFanController() + elif (sec_since_boot() - sm.rcv_time['pandaStates']) > DISCONNECT_TIMEOUT: + if onroad_conditions["ignition"]: + onroad_conditions["ignition"] = False + cloudlog.error("panda timed out onroad") + try: last_hw_state = hw_queue.get_nowait() except queue.Empty: @@ -343,12 +348,13 @@ def thermald_thread(end_event, hw_queue): power_monitor.calculate(peripheralState, onroad_conditions["ignition"]) msg.deviceState.offroadPowerUsageUwh = power_monitor.get_power_used() msg.deviceState.carBatteryCapacityUwh = max(0, power_monitor.get_car_battery_capacity()) - current_power_draw = HARDWARE.get_current_power_draw() # pylint: disable=assignment-from-none - if current_power_draw is not None: - statlog.sample("power_draw", current_power_draw) - msg.deviceState.powerDrawW = current_power_draw - else: - msg.deviceState.powerDrawW = 0 + current_power_draw = HARDWARE.get_current_power_draw() + statlog.sample("power_draw", current_power_draw) + msg.deviceState.powerDrawW = current_power_draw + + som_power_draw = HARDWARE.get_som_power_draw() + statlog.sample("som_power_draw", som_power_draw) + msg.deviceState.somPowerDrawW = som_power_draw # Check if we need to disable charging (handled by boardd) msg.deviceState.chargingDisabled = power_monitor.should_disable_charging(onroad_conditions["ignition"], in_car, off_ts) diff --git a/selfdrive/tombstoned.py b/selfdrive/tombstoned.py index dcf3ee0f6..0045e0766 100755 --- a/selfdrive/tombstoned.py +++ b/selfdrive/tombstoned.py @@ -10,15 +10,12 @@ import glob from typing import NoReturn from common.file_helpers import mkdirs_exists_ok -from selfdrive.hardware import TICI from selfdrive.loggerd.config import ROOT import selfdrive.sentry as sentry -from selfdrive.swaglog import cloudlog -from selfdrive.version import get_commit +from system.swaglog import cloudlog +from system.version import get_commit -MAX_SIZE = 100000 * 10 # mal size is 40-100k, allow up to 1M -if TICI: - MAX_SIZE = MAX_SIZE * 100 # Allow larger size for tici since files contain coredump +MAX_SIZE = 1_000_000 * 100 # allow up to 100M MAX_TOMBSTONE_FN_LEN = 62 # 85 - 23 ("/crash/") TOMBSTONE_DIR = "/data/tombstones/" diff --git a/selfdrive/ui/SConscript b/selfdrive/ui/SConscript index 1ea13d556..0835c1637 100644 --- a/selfdrive/ui/SConscript +++ b/selfdrive/ui/SConscript @@ -1,10 +1,13 @@ import os -Import('qt_env', 'arch', 'common', 'messaging', 'gpucommon', 'visionipc', +Import('qt_env', 'arch', 'common', 'messaging', 'visionipc', 'cereal', 'transformations') -base_libs = [gpucommon, common, messaging, cereal, visionipc, transformations, 'zmq', +base_libs = [common, messaging, cereal, visionipc, transformations, 'zmq', 'capnp', 'kj', 'm', 'OpenCL', 'ssl', 'crypto', 'pthread'] + qt_env["LIBS"] +if arch == 'larch64': + base_libs.append('EGL') + maps = arch in ['larch64', 'x86_64'] if maps and arch == 'x86_64': @@ -15,10 +18,11 @@ if arch == "Darwin": del base_libs[base_libs.index('OpenCL')] qt_env['FRAMEWORKS'] += ['OpenCL'] -widgets_src = ["ui.cc", "qt/util.cc", "qt/widgets/input.cc", "qt/widgets/drive_stats.cc", +qt_util = qt_env.Library("qt_util", ["#selfdrive/ui/qt/api.cc", "#selfdrive/ui/qt/util.cc"], LIBS=base_libs) +widgets_src = ["ui.cc", "qt/widgets/input.cc", "qt/widgets/drive_stats.cc", "qt/widgets/ssh_keys.cc", "qt/widgets/toggle.cc", "qt/widgets/controls.cc", "qt/widgets/offroad_alerts.cc", "qt/widgets/prime.cc", "qt/widgets/keyboard.cc", - "qt/widgets/scrollview.cc", "qt/widgets/cameraview.cc", "#third_party/qrcode/QrCode.cc", "qt/api.cc", + "qt/widgets/scrollview.cc", "qt/widgets/cameraview.cc", "#third_party/qrcode/QrCode.cc", "qt/request_repeater.cc", "qt/qt_window.cc", "qt/offroad/networking.cc", "qt/offroad/wifiManager.cc"] qt_env['CPPDEFINES'] = [] @@ -28,7 +32,7 @@ if maps: qt_env['CPPDEFINES'] += ["ENABLE_MAPS"] widgets = qt_env.Library("qt_widgets", widgets_src, LIBS=base_libs) -qt_libs = [widgets] + base_libs +qt_libs = [widgets, qt_util] + base_libs # build assets assets = "#selfdrive/assets/assets.cc" @@ -54,6 +58,9 @@ qt_src = ["main.cc", "qt/sidebar.cc", "qt/onroad.cc", "qt/body.cc", "qt/window.cc", "qt/home.cc", "qt/offroad/settings.cc", "qt/offroad/onboarding.cc", "qt/offroad/driverview.cc"] qt_env.Program("_ui", qt_src + [asset_obj], LIBS=qt_libs) +if GetOption('test'): + qt_src.remove("main.cc") # replaced by test_runner + qt_env.Program('tests/test_translations', [asset_obj, 'tests/test_runner.cc', 'tests/test_translations.cc'] + qt_src, LIBS=qt_libs) # setup and factory resetter @@ -104,25 +111,6 @@ if GetOption('extras'): # keep installers small assert f[0].get_size() < 300*1e3 - -# build headless replay +# build watch3 if arch in ['x86_64', 'Darwin'] or GetOption('extras'): - qt_env['CXXFLAGS'] += ["-Wno-deprecated-declarations"] - - replay_lib_src = ["replay/replay.cc", "replay/consoleui.cc", "replay/camera.cc", "replay/filereader.cc", "replay/logreader.cc", "replay/framereader.cc", "replay/route.cc", "replay/util.cc"] - - replay_lib = qt_env.Library("qt_replay", replay_lib_src, LIBS=base_libs) - replay_libs = [replay_lib, 'avutil', 'avcodec', 'avformat', 'bz2', 'curl', 'yuv', 'ncurses'] + qt_libs - qt_env.Program("replay/replay", ["replay/main.cc"], LIBS=replay_libs) qt_env.Program("watch3", ["watch3.cc"], LIBS=qt_libs + ['common', 'json11', 'zmq', 'visionipc', 'messaging']) - - if GetOption('test'): - qt_env.Program('replay/tests/test_replay', ['replay/tests/test_runner.cc', 'replay/tests/test_replay.cc'], LIBS=[replay_libs]) - -# navd -if maps: - navd_src = ["navd/main.cc", "navd/route_engine.cc", "navd/map_renderer.cc"] - qt_env.Program("navd/_navd", navd_src, LIBS=qt_libs + ['common', 'json11']) - - if GetOption('extras'): - qt_env.SharedLibrary("navd/map_renderer", ["navd/map_renderer.cc"], LIBS=qt_libs + ['common', 'messaging']) diff --git a/selfdrive/ui/main.cc b/selfdrive/ui/main.cc index cffa45962..ed54d5aa1 100644 --- a/selfdrive/ui/main.cc +++ b/selfdrive/ui/main.cc @@ -1,8 +1,9 @@ #include #include +#include -#include "selfdrive/hardware/hw.h" +#include "system/hardware/hw.h" #include "selfdrive/ui/qt/qt_window.h" #include "selfdrive/ui/qt/util.h" #include "selfdrive/ui/qt/window.h" @@ -13,7 +14,15 @@ int main(int argc, char *argv[]) { qInstallMessageHandler(swagLogMessageHandler); initApp(argc, argv); + QTranslator translator; + QString translation_file = QString::fromStdString(Params().get("LanguageSetting")); + if (!translator.load(translation_file, "translations") && translation_file.length()) { + qCritical() << "Failed to load translation file:" << translation_file; + } + QApplication a(argc, argv); + a.installTranslator(&translator); + MainWindow w; setMainWindow(&w); a.installEventFilter(&w); diff --git a/selfdrive/ui/navd/.gitignore b/selfdrive/ui/navd/.gitignore deleted file mode 100644 index 0fa7b173e..000000000 --- a/selfdrive/ui/navd/.gitignore +++ /dev/null @@ -1 +0,0 @@ -_navd diff --git a/selfdrive/ui/navd/main.cc b/selfdrive/ui/navd/main.cc deleted file mode 100644 index fe354b7b7..000000000 --- a/selfdrive/ui/navd/main.cc +++ /dev/null @@ -1,45 +0,0 @@ -#include -#include -#include -#include -#include - -#include "selfdrive/ui/qt/util.h" -#include "selfdrive/ui/qt/maps/map_helpers.h" -#include "selfdrive/ui/navd/route_engine.h" -#include "selfdrive/ui/navd/map_renderer.h" -#include "selfdrive/hardware/hw.h" -#include "common/params.h" - -void sigHandler(int s) { - qInfo() << "Shutting down"; - std::signal(s, SIG_DFL); - - qApp->quit(); -} - - -int main(int argc, char *argv[]) { - qInstallMessageHandler(swagLogMessageHandler); - - QApplication app(argc, argv); - std::signal(SIGINT, sigHandler); - std::signal(SIGTERM, sigHandler); - - QCommandLineParser parser; - parser.setApplicationDescription("Navigation server. Runs stand-alone, or using pre-computer route"); - parser.addHelpOption(); - parser.process(app); - const QStringList args = parser.positionalArguments(); - - - RouteEngine* route_engine = new RouteEngine(); - - if (Params().getBool("NavdRender")) { - MapRenderer * m = new MapRenderer(get_mapbox_settings()); - QObject::connect(route_engine, &RouteEngine::positionUpdated, m, &MapRenderer::updatePosition); - QObject::connect(route_engine, &RouteEngine::routeUpdated, m, &MapRenderer::updateRoute); - } - - return app.exec(); -} diff --git a/selfdrive/ui/navd/map_renderer.cc b/selfdrive/ui/navd/map_renderer.cc deleted file mode 100644 index 8d2a8810c..000000000 --- a/selfdrive/ui/navd/map_renderer.cc +++ /dev/null @@ -1,200 +0,0 @@ -#include "selfdrive/ui/navd/map_renderer.h" - -#include -#include -#include - -#include "selfdrive/ui/qt/maps/map_helpers.h" -#include "common/timing.h" - -const float ZOOM = 13.5; // Don't go below 13 or features will start to disappear -const int WIDTH = 256; -const int HEIGHT = WIDTH; - -const int NUM_VIPC_BUFFERS = 4; - -MapRenderer::MapRenderer(const QMapboxGLSettings &settings, bool enable_vipc) : m_settings(settings) { - QSurfaceFormat fmt; - fmt.setRenderableType(QSurfaceFormat::OpenGLES); - - ctx = std::make_unique(); - ctx->setFormat(fmt); - ctx->create(); - assert(ctx->isValid()); - - surface = std::make_unique(); - surface->setFormat(ctx->format()); - surface->create(); - - ctx->makeCurrent(surface.get()); - assert(QOpenGLContext::currentContext() == ctx.get()); - - gl_functions.reset(ctx->functions()); - gl_functions->initializeOpenGLFunctions(); - - QOpenGLFramebufferObjectFormat fbo_format; - fbo.reset(new QOpenGLFramebufferObject(WIDTH, HEIGHT, fbo_format)); - - m_map.reset(new QMapboxGL(nullptr, m_settings, fbo->size(), 1)); - m_map->setCoordinateZoom(QMapbox::Coordinate(0, 0), ZOOM); - m_map->setStyleUrl("mapbox://styles/commaai/ckvmksrpd4n0a14pfdo5heqzr"); - m_map->createRenderer(); - - m_map->resize(fbo->size()); - m_map->setFramebufferObject(fbo->handle(), fbo->size()); - gl_functions->glViewport(0, 0, WIDTH, HEIGHT); - - if (enable_vipc) { - qWarning() << "Enabling navd map rendering"; - vipc_server.reset(new VisionIpcServer("navd")); - vipc_server->create_buffers(VisionStreamType::VISION_STREAM_RGB_MAP, NUM_VIPC_BUFFERS, true, WIDTH, HEIGHT); - vipc_server->start_listener(); - - pm.reset(new PubMaster({"navThumbnail"})); - } -} - -void MapRenderer::updatePosition(QMapbox::Coordinate position, float bearing) { - if (m_map.isNull()) { - return; - } - - m_map->setCoordinate(position); - m_map->setBearing(bearing); - update(); -} - -bool MapRenderer::loaded() { - return m_map->isFullyLoaded(); -} - -void MapRenderer::update() { - gl_functions->glClear(GL_COLOR_BUFFER_BIT); - m_map->render(); - gl_functions->glFlush(); - - sendVipc(); -} - -void MapRenderer::sendVipc() { - if (!vipc_server || !loaded()) { - return; - } - - QImage cap = fbo->toImage().convertToFormat(QImage::Format_RGB888, Qt::AutoColor); - uint64_t ts = nanos_since_boot(); - VisionBuf* buf = vipc_server->get_buffer(VisionStreamType::VISION_STREAM_RGB_MAP); - VisionIpcBufExtra extra = { - .frame_id = frame_id, - .timestamp_sof = ts, - .timestamp_eof = ts, - }; - - assert(cap.sizeInBytes() == buf->len); - memcpy(buf->addr, cap.bits(), buf->len); - vipc_server->send(buf, &extra); - - if (frame_id % 100 == 0) { - // Write jpeg into buffer - QByteArray buffer_bytes; - QBuffer buffer(&buffer_bytes); - buffer.open(QIODevice::WriteOnly); - cap.save(&buffer, "JPG", 50); - - kj::Array buffer_kj = kj::heapArray((const capnp::byte*)buffer_bytes.constData(), buffer_bytes.size()); - - // Send thumbnail - MessageBuilder msg; - auto thumbnaild = msg.initEvent().initNavThumbnail(); - thumbnaild.setFrameId(frame_id); - thumbnaild.setTimestampEof(ts); - thumbnaild.setThumbnail(buffer_kj); - pm->send("navThumbnail", msg); - } - - frame_id++; -} - -uint8_t* MapRenderer::getImage() { - QImage cap = fbo->toImage().convertToFormat(QImage::Format_RGB888, Qt::AutoColor); - uint8_t* buf = new uint8_t[cap.sizeInBytes()]; - memcpy(buf, cap.bits(), cap.sizeInBytes()); - - return buf; -} - -void MapRenderer::updateRoute(QList coordinates) { - if (m_map.isNull()) return; - initLayers(); - - auto route_points = coordinate_list_to_collection(coordinates); - QMapbox::Feature feature(QMapbox::Feature::LineStringType, route_points, {}, {}); - QVariantMap navSource; - navSource["type"] = "geojson"; - navSource["data"] = QVariant::fromValue(feature); - m_map->updateSource("navSource", navSource); - m_map->setLayoutProperty("navLayer", "visibility", "visible"); -} - -void MapRenderer::initLayers() { - if (!m_map->layerExists("navLayer")) { - QVariantMap nav; - nav["id"] = "navLayer"; - nav["type"] = "line"; - nav["source"] = "navSource"; - m_map->addLayer(nav, "road-intersection"); - m_map->setPaintProperty("navLayer", "line-color", QColor("blue")); - m_map->setPaintProperty("navLayer", "line-width", 3); - m_map->setLayoutProperty("navLayer", "line-cap", "round"); - } -} - -MapRenderer::~MapRenderer() { -} - -extern "C" { - MapRenderer* map_renderer_init() { - char *argv[] = { - (char*)"navd", - nullptr - }; - int argc = 0; - QApplication *app = new QApplication(argc, argv); - assert(app); - - QMapboxGLSettings settings; - settings.setApiBaseUrl(MAPS_HOST); - settings.setAccessToken(get_mapbox_token()); - - return new MapRenderer(settings, false); - } - - void map_renderer_update_position(MapRenderer *inst, float lat, float lon, float bearing) { - inst->updatePosition({lat, lon}, bearing); - QApplication::processEvents(); - } - - void map_renderer_update_route(MapRenderer *inst, char* polyline) { - inst->updateRoute(polyline_to_coordinate_list(QString::fromUtf8(polyline))); - } - - void map_renderer_update(MapRenderer *inst) { - inst->update(); - } - - void map_renderer_process(MapRenderer *inst) { - QApplication::processEvents(); - } - - bool map_renderer_loaded(MapRenderer *inst) { - return inst->loaded(); - } - - uint8_t * map_renderer_get_image(MapRenderer *inst) { - return inst->getImage(); - } - - void map_renderer_free_image(MapRenderer *inst, uint8_t * buf) { - delete[] buf; - } -} diff --git a/selfdrive/ui/navd/map_renderer.h b/selfdrive/ui/navd/map_renderer.h deleted file mode 100644 index 1746e7669..000000000 --- a/selfdrive/ui/navd/map_renderer.h +++ /dev/null @@ -1,49 +0,0 @@ -#pragma once - -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "cereal/visionipc/visionipc_server.h" -#include "cereal/messaging/messaging.h" - - -class MapRenderer : public QObject { - Q_OBJECT - -public: - MapRenderer(const QMapboxGLSettings &, bool enable_vipc=true); - uint8_t* getImage(); - void update(); - bool loaded(); - ~MapRenderer(); - - -private: - std::unique_ptr ctx; - std::unique_ptr surface; - std::unique_ptr gl_functions; - std::unique_ptr fbo; - - std::unique_ptr vipc_server; - std::unique_ptr pm; - void sendVipc(); - - QMapboxGLSettings m_settings; - QScopedPointer m_map; - - void initLayers(); - - uint32_t frame_id = 0; - -public slots: - void updatePosition(QMapbox::Coordinate position, float bearing); - void updateRoute(QList coordinates); -}; diff --git a/selfdrive/ui/navd/navd b/selfdrive/ui/navd/navd deleted file mode 100755 index ff3103bd9..000000000 --- a/selfdrive/ui/navd/navd +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/sh -cd "$(dirname "$0")" -export QT_PLUGIN_PATH="../../../third_party/qt-plugins/$(uname -m)" -exec ./_navd diff --git a/selfdrive/ui/navd/route_engine.cc b/selfdrive/ui/navd/route_engine.cc deleted file mode 100644 index 577f267c9..000000000 --- a/selfdrive/ui/navd/route_engine.cc +++ /dev/null @@ -1,359 +0,0 @@ -#include "selfdrive/ui/navd/route_engine.h" - -#include - -#include "selfdrive/ui/qt/maps/map.h" -#include "selfdrive/ui/qt/maps/map_helpers.h" -#include "selfdrive/ui/qt/api.h" - -#include "common/params.h" - -const qreal REROUTE_DISTANCE = 25; -const float MANEUVER_TRANSITION_THRESHOLD = 10; - -static float get_time_typical(const QGeoRouteSegment &segment) { - auto maneuver = segment.maneuver(); - auto attrs = maneuver.extendedAttributes(); - return attrs.contains("mapbox.duration_typical") ? attrs["mapbox.duration_typical"].toDouble() : segment.travelTime(); -} - -static cereal::NavInstruction::Direction string_to_direction(QString d) { - if (d.contains("left")) { - return cereal::NavInstruction::Direction::LEFT; - } else if (d.contains("right")) { - return cereal::NavInstruction::Direction::RIGHT; - } else if (d.contains("straight")) { - return cereal::NavInstruction::Direction::STRAIGHT; - } - - return cereal::NavInstruction::Direction::NONE; -} - -static void parse_banner(cereal::NavInstruction::Builder &instruction, const QMap &banner, bool full) { - QString primary_str, secondary_str; - - auto p = banner["primary"].toMap(); - primary_str += p["text"].toString(); - - instruction.setShowFull(full); - - if (p.contains("type")) { - instruction.setManeuverType(p["type"].toString().toStdString()); - } - - if (p.contains("modifier")) { - instruction.setManeuverModifier(p["modifier"].toString().toStdString()); - } - - if (banner.contains("secondary")) { - auto s = banner["secondary"].toMap(); - secondary_str += s["text"].toString(); - } - - instruction.setManeuverPrimaryText(primary_str.toStdString()); - instruction.setManeuverSecondaryText(secondary_str.toStdString()); - - if (banner.contains("sub")) { - auto s = banner["sub"].toMap(); - auto components = s["components"].toList(); - - size_t num_lanes = 0; - for (auto &c : components) { - auto cc = c.toMap(); - if (cc["type"].toString() == "lane") { - num_lanes += 1; - } - } - - auto lanes = instruction.initLanes(num_lanes); - - size_t i = 0; - for (auto &c : components) { - auto cc = c.toMap(); - if (cc["type"].toString() == "lane") { - auto lane = lanes[i]; - lane.setActive(cc["active"].toBool()); - - if (cc.contains("active_direction")) { - lane.setActiveDirection(string_to_direction(cc["active_direction"].toString())); - } - - auto directions = lane.initDirections(cc["directions"].toList().size()); - - size_t j = 0; - for (auto &dir : cc["directions"].toList()) { - directions.set(j, string_to_direction(dir.toString())); - j++; - } - - - i++; - } - } - } -} - -RouteEngine::RouteEngine() { - sm = new SubMaster({"liveLocationKalman", "managerState"}); - pm = new PubMaster({"navInstruction", "navRoute"}); - - // Timers - route_timer = new QTimer(this); - QObject::connect(route_timer, SIGNAL(timeout()), this, SLOT(routeUpdate())); - route_timer->start(1000); - - msg_timer = new QTimer(this); - QObject::connect(msg_timer, SIGNAL(timeout()), this, SLOT(msgUpdate())); - msg_timer->start(50); - - // Build routing engine - QVariantMap parameters; - parameters["mapbox.access_token"] = get_mapbox_token(); - parameters["mapbox.directions_api_url"] = MAPS_HOST + "/directions/v5/mapbox/"; - - geoservice_provider = new QGeoServiceProvider("mapbox", parameters); - routing_manager = geoservice_provider->routingManager(); - if (routing_manager == nullptr) { - qWarning() << geoservice_provider->errorString(); - assert(routing_manager); - } - QObject::connect(routing_manager, &QGeoRoutingManager::finished, this, &RouteEngine::routeCalculated); - - // Get last gps position from params - auto last_gps_position = coordinate_from_param("LastGPSPosition"); - if (last_gps_position) { - last_position = *last_gps_position; - } -} - -void RouteEngine::msgUpdate() { - sm->update(1000); - if (!sm->updated("liveLocationKalman")) { - active = false; - return; - } - - - if (sm->updated("managerState")) { - for (auto const &p : (*sm)["managerState"].getManagerState().getProcesses()) { - if (p.getName() == "ui" && p.getRunning()) { - if (ui_pid && *ui_pid != p.getPid()){ - qWarning() << "UI restarting, sending route"; - QTimer::singleShot(5000, this, &RouteEngine::sendRoute); - } - ui_pid = p.getPid(); - } - } - } - - auto location = (*sm)["liveLocationKalman"].getLiveLocationKalman(); - auto pos = location.getPositionGeodetic(); - auto orientation = location.getCalibratedOrientationNED(); - - gps_ok = location.getGpsOK(); - - localizer_valid = (location.getStatus() == cereal::LiveLocationKalman::Status::VALID) && pos.getValid(); - - if (localizer_valid) { - last_bearing = RAD2DEG(orientation.getValue()[2]); - last_position = QMapbox::Coordinate(pos.getValue()[0], pos.getValue()[1]); - emit positionUpdated(*last_position, *last_bearing); - } - - active = true; -} - -void RouteEngine::routeUpdate() { - if (!active) { - return; - } - - recomputeRoute(); - - MessageBuilder msg; - cereal::Event::Builder evt = msg.initEvent(segment.isValid()); - cereal::NavInstruction::Builder instruction = evt.initNavInstruction(); - - // Show route instructions - if (segment.isValid()) { - auto cur_maneuver = segment.maneuver(); - auto attrs = cur_maneuver.extendedAttributes(); - if (cur_maneuver.isValid() && attrs.contains("mapbox.banner_instructions")) { - float along_geometry = distance_along_geometry(segment.path(), to_QGeoCoordinate(*last_position)); - float distance_to_maneuver_along_geometry = segment.distance() - along_geometry; - - auto banners = attrs["mapbox.banner_instructions"].toList(); - if (banners.size()) { - auto banner = banners[0].toMap(); - - for (auto &b : banners) { - auto bb = b.toMap(); - if (distance_to_maneuver_along_geometry < bb["distance_along_geometry"].toDouble()) { - banner = bb; - } - } - - instruction.setManeuverDistance(distance_to_maneuver_along_geometry); - parse_banner(instruction, banner, distance_to_maneuver_along_geometry < banner["distance_along_geometry"].toDouble()); - - // ETA - float progress = distance_along_geometry(segment.path(), to_QGeoCoordinate(*last_position)) / segment.distance(); - float total_distance = segment.distance() * (1.0 - progress); - float total_time = segment.travelTime() * (1.0 - progress); - float total_time_typical = get_time_typical(segment) * (1.0 - progress); - - auto s = segment.nextRouteSegment(); - while (s.isValid()) { - total_distance += s.distance(); - total_time += s.travelTime(); - total_time_typical += get_time_typical(s); - - s = s.nextRouteSegment(); - } - instruction.setTimeRemaining(total_time); - instruction.setTimeRemainingTypical(total_time_typical); - instruction.setDistanceRemaining(total_distance); - } - - // Transition to next route segment - if (distance_to_maneuver_along_geometry < -MANEUVER_TRANSITION_THRESHOLD) { - auto next_segment = segment.nextRouteSegment(); - if (next_segment.isValid()) { - segment = next_segment; - - recompute_backoff = 0; - recompute_countdown = 0; - } else { - qWarning() << "Destination reached"; - Params().remove("NavDestination"); - - // Clear route if driving away from destination - float d = segment.maneuver().position().distanceTo(to_QGeoCoordinate(*last_position)); - if (d > REROUTE_DISTANCE) { - clearRoute(); - } - } - } - } - } - - pm->send("navInstruction", msg); -} - -void RouteEngine::clearRoute() { - route = QGeoRoute(); - segment = QGeoRouteSegment(); - nav_destination = QMapbox::Coordinate(); -} - -bool RouteEngine::shouldRecompute() { - if (!segment.isValid()) { - return true; - } - - // Don't recompute in last segment, assume destination is reached - if (!segment.nextRouteSegment().isValid()) { - return false; - } - - // Compute closest distance to all line segments in the current path - float min_d = REROUTE_DISTANCE + 1; - auto path = segment.path(); - auto cur = to_QGeoCoordinate(*last_position); - for (size_t i = 0; i < path.size() - 1; i++) { - auto a = path[i]; - auto b = path[i+1]; - if (a.distanceTo(b) < 1.0) { - continue; - } - min_d = std::min(min_d, minimum_distance(a, b, cur)); - } - return min_d > REROUTE_DISTANCE; - - // TODO: Check for going wrong way in segment -} - -void RouteEngine::recomputeRoute() { - if (!last_position) { - return; - } - - auto new_destination = coordinate_from_param("NavDestination"); - if (!new_destination) { - clearRoute(); - return; - } - - bool should_recompute = shouldRecompute(); - if (*new_destination != nav_destination) { - qWarning() << "Got new destination from NavDestination param" << *new_destination; - should_recompute = true; - } - - if (!gps_ok && segment.isValid()) return; // Don't recompute when gps drifts in tunnels - - if (recompute_countdown == 0 && should_recompute) { - recompute_countdown = std::pow(2, recompute_backoff); - recompute_backoff = std::min(6, recompute_backoff + 1); - calculateRoute(*new_destination); - } else { - recompute_countdown = std::max(0, recompute_countdown - 1); - } -} - -void RouteEngine::calculateRoute(QMapbox::Coordinate destination) { - qWarning() << "Calculating route" << *last_position << "->" << destination; - - nav_destination = destination; - QGeoRouteRequest request(to_QGeoCoordinate(*last_position), to_QGeoCoordinate(destination)); - request.setFeatureWeight(QGeoRouteRequest::TrafficFeature, QGeoRouteRequest::AvoidFeatureWeight); - - if (last_bearing) { - QVariantMap params; - int bearing = ((int)(*last_bearing) + 360) % 360; - params["bearing"] = bearing; - request.setWaypointsMetadata({params}); - } - - routing_manager->calculateRoute(request); -} - -void RouteEngine::routeCalculated(QGeoRouteReply *reply) { - if (reply->error() == QGeoRouteReply::NoError) { - if (reply->routes().size() != 0) { - qWarning() << "Got route response"; - - route = reply->routes().at(0); - segment = route.firstRouteSegment(); - - auto path = route.path(); - emit routeUpdated(path); - } else { - qWarning() << "Got empty route response"; - } - } else { - qWarning() << "Got error in route reply" << reply->errorString(); - } - - sendRoute(); - - reply->deleteLater(); -} - -void RouteEngine::sendRoute() { - MessageBuilder msg; - cereal::Event::Builder evt = msg.initEvent(); - cereal::NavRoute::Builder nav_route = evt.initNavRoute(); - - auto path = route.path(); - auto coordinates = nav_route.initCoordinates(path.size()); - - size_t i = 0; - for (auto const &c : route.path()) { - coordinates[i].setLatitude(c.latitude()); - coordinates[i].setLongitude(c.longitude()); - i++; - } - - pm->send("navRoute", msg); -} diff --git a/selfdrive/ui/navd/route_engine.h b/selfdrive/ui/navd/route_engine.h deleted file mode 100644 index 33cbc7910..000000000 --- a/selfdrive/ui/navd/route_engine.h +++ /dev/null @@ -1,62 +0,0 @@ -#pragma once - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "cereal/messaging/messaging.h" - -class RouteEngine : public QObject { - Q_OBJECT - -public: - RouteEngine(); - - SubMaster *sm; - PubMaster *pm; - - QTimer* msg_timer; - QTimer* route_timer; - - std::optional ui_pid; - - // Route - bool gps_ok = false; - QGeoServiceProvider *geoservice_provider; - QGeoRoutingManager *routing_manager; - QGeoRoute route; - QGeoRouteSegment segment; - QMapbox::Coordinate nav_destination; - - // Position - std::optional last_position; - std::optional last_bearing; - bool localizer_valid = false; - - // Route recompute - bool active = false; - int recompute_backoff = 0; - int recompute_countdown = 0; - void calculateRoute(QMapbox::Coordinate destination); - void clearRoute(); - bool shouldRecompute(); - -private slots: - void routeUpdate(); - void msgUpdate(); - void routeCalculated(QGeoRouteReply *reply); - void recomputeRoute(); - void sendRoute(); - -signals: - void positionUpdated(QMapbox::Coordinate position, float bearing); - void routeUpdated(QList coordinates); -}; diff --git a/selfdrive/ui/qt/api.cc b/selfdrive/ui/qt/api.cc index 94beb1218..84e1a4032 100644 --- a/selfdrive/ui/qt/api.cc +++ b/selfdrive/ui/qt/api.cc @@ -14,7 +14,7 @@ #include "common/params.h" #include "common/util.h" -#include "selfdrive/hardware/hw.h" +#include "system/hardware/hw.h" #include "selfdrive/ui/qt/util.h" namespace CommaApi { diff --git a/selfdrive/ui/qt/home.cc b/selfdrive/ui/qt/home.cc index e522d6160..0edeb252b 100644 --- a/selfdrive/ui/qt/home.cc +++ b/selfdrive/ui/qt/home.cc @@ -85,6 +85,7 @@ void HomeWindow::mousePressEvent(QMouseEvent* e) { } void HomeWindow::mouseDoubleClickEvent(QMouseEvent* e) { + HomeWindow::mousePressEvent(e); const SubMaster &sm = *(uiState()->sm); if (sm["carParams"].getCarParams().getNotCar()) { if (onroad->isVisible()) { @@ -92,6 +93,7 @@ void HomeWindow::mouseDoubleClickEvent(QMouseEvent* e) { } else if (body->isVisible()) { slayout->setCurrentWidget(onroad); } + showSidebar(false); } } @@ -109,7 +111,7 @@ OffroadHome::OffroadHome(QWidget* parent) : QFrame(parent) { date = new QLabel(); header_layout->addWidget(date, 1, Qt::AlignHCenter | Qt::AlignLeft); - update_notif = new QPushButton("UPDATE"); + update_notif = new QPushButton(tr("UPDATE")); update_notif->setVisible(false); update_notif->setStyleSheet("background-color: #364DEF;"); QObject::connect(update_notif, &QPushButton::clicked, [=]() { center_layout->setCurrentIndex(1); }); @@ -200,6 +202,6 @@ void OffroadHome::refresh() { update_notif->setVisible(updateAvailable); alert_notif->setVisible(alerts); if (alerts) { - alert_notif->setText(QString::number(alerts) + (alerts > 1 ? " ALERTS" : " ALERT")); + alert_notif->setText(QString::number(alerts) + (alerts > 1 ? tr(" ALERTS") : tr(" ALERT"))); } } diff --git a/selfdrive/ui/qt/maps/map.cc b/selfdrive/ui/qt/maps/map.cc index 8e5a02f8a..3967416f7 100644 --- a/selfdrive/ui/qt/maps/map.cc +++ b/selfdrive/ui/qt/maps/map.cc @@ -1,16 +1,18 @@ #include "selfdrive/ui/qt/maps/map.h" +#include #include #include -#include #include +#include #include "common/swaglog.h" -#include "selfdrive/ui/ui.h" -#include "selfdrive/ui/qt/util.h" +#include "common/transformations/coordinates.hpp" #include "selfdrive/ui/qt/maps/map_helpers.h" #include "selfdrive/ui/qt/request_repeater.h" +#include "selfdrive/ui/qt/util.h" +#include "selfdrive/ui/ui.h" const int PAN_TIMEOUT = 100; @@ -22,18 +24,12 @@ const float MAX_PITCH = 50; const float MIN_PITCH = 0; const float MAP_SCALE = 2; +const float VALID_POS_STD = 50.0; // m + const QString ICON_SUFFIX = ".png"; -MapWindow::MapWindow(const QMapboxGLSettings &settings) : - m_settings(settings), velocity_filter(0, 10, 0.05) { - sm = new SubMaster({"liveLocationKalman", "navInstruction", "navRoute"}); - - // Connect now, so any navRoutes sent while the map is initializing are not dropped - sm->update(0); - - timer = new QTimer(this); - QObject::connect(timer, SIGNAL(timeout()), this, SLOT(timerUpdate())); - timer->start(50); +MapWindow::MapWindow(const QMapboxGLSettings &settings) : m_settings(settings), velocity_filter(0, 10, 0.05) { + QObject::connect(uiState(), &UIState::uiUpdate, this, &MapWindow::updateState); // Instructions map_instructions = new MapInstructions(this); @@ -105,31 +101,65 @@ void MapWindow::initLayers() { } } -void MapWindow::timerUpdate() { +void MapWindow::updateState(const UIState &s) { if (!uiState()->scene.started) { return; } - + const SubMaster &sm = *(s.sm); update(); - sm->update(0); - if (sm->updated("liveLocationKalman")) { - auto location = (*sm)["liveLocationKalman"].getLiveLocationKalman(); - auto pos = location.getPositionGeodetic(); - auto orientation = location.getCalibratedOrientationNED(); - auto velocity = location.getVelocityCalibrated(); + if (sm.updated("liveLocationKalman")) { + auto locationd_location = sm["liveLocationKalman"].getLiveLocationKalman(); + auto locationd_pos = locationd_location.getPositionGeodetic(); + auto locationd_orientation = locationd_location.getCalibratedOrientationNED(); + auto locationd_velocity = locationd_location.getVelocityCalibrated(); - localizer_valid = (location.getStatus() == cereal::LiveLocationKalman::Status::VALID) && - pos.getValid() && orientation.getValid() && velocity.getValid(); + locationd_valid = (locationd_location.getStatus() == cereal::LiveLocationKalman::Status::VALID) && + locationd_pos.getValid() && locationd_orientation.getValid() && locationd_velocity.getValid(); - if (localizer_valid) { - last_position = QMapbox::Coordinate(pos.getValue()[0], pos.getValue()[1]); - last_bearing = RAD2DEG(orientation.getValue()[2]); - velocity_filter.update(velocity.getValue()[0]); + if (locationd_valid) { + last_position = QMapbox::Coordinate(locationd_pos.getValue()[0], locationd_pos.getValue()[1]); + last_bearing = RAD2DEG(locationd_orientation.getValue()[2]); + velocity_filter.update(locationd_velocity.getValue()[0]); } } - if (sm->updated("navRoute") && (*sm)["navRoute"].getNavRoute().getCoordinates().size()) { + if (sm.updated("gnssMeasurements")) { + auto laikad_location = sm["gnssMeasurements"].getGnssMeasurements(); + auto laikad_pos = laikad_location.getPositionECEF(); + auto laikad_pos_ecef = laikad_pos.getValue(); + auto laikad_pos_std = laikad_pos.getStd(); + auto laikad_velocity_ecef = laikad_location.getVelocityECEF().getValue(); + + laikad_valid = laikad_pos.getValid() && Eigen::Vector3d(laikad_pos_std[0], laikad_pos_std[1], laikad_pos_std[2]).norm() < VALID_POS_STD; + + if (laikad_valid && !locationd_valid) { + ECEF ecef = {.x = laikad_pos_ecef[0], .y = laikad_pos_ecef[1], .z = laikad_pos_ecef[2]}; + Geodetic laikad_pos_geodetic = ecef2geodetic(ecef); + last_position = QMapbox::Coordinate(laikad_pos_geodetic.lat, laikad_pos_geodetic.lon); + + // Compute NED velocity + LocalCoord converter(ecef); + ECEF next_ecef = {.x = ecef.x + laikad_velocity_ecef[0], .y = ecef.y + laikad_velocity_ecef[1], .z = ecef.z + laikad_velocity_ecef[2]}; + Eigen::VectorXd ned_vel = converter.ecef2ned(next_ecef).to_vector() - converter.ecef2ned(ecef).to_vector(); + + float velocity = ned_vel.norm(); + velocity_filter.update(velocity); + + // Convert NED velocity to angle + if (velocity > 1.0) { + float new_bearing = fmod(RAD2DEG(atan2(ned_vel[1], ned_vel[0])) + 360.0, 360.0); + if (last_bearing) { + float delta = 0.1 * angle_difference(*last_bearing, new_bearing); // Smooth heading + last_bearing = fmod(*last_bearing + delta + 360.0, 360.0); + } else { + last_bearing = new_bearing; + } + } + } + } + + if (sm.updated("navRoute") && sm["navRoute"].getNavRoute().getCoordinates().size()) { qWarning() << "Got new navRoute from navd. Opening map:" << allow_open; // Only open the map on setting destination the first time @@ -145,15 +175,13 @@ void MapWindow::timerUpdate() { loaded_once = loaded_once || m_map->isFullyLoaded(); if (!loaded_once) { - map_instructions->showError("Map Loading"); + map_instructions->showError(tr("Map Loading")); return; } initLayers(); - if (!localizer_valid) { - map_instructions->showError("Waiting for GPS"); - } else { + if (locationd_valid || laikad_valid) { map_instructions->noError(); // Update current location marker @@ -163,6 +191,8 @@ void MapWindow::timerUpdate() { carPosSource["type"] = "geojson"; carPosSource["data"] = QVariant::fromValue(feature1); m_map->updateSource("carPosSource", carPosSource); + } else { + map_instructions->showError(tr("Waiting for GPS")); } if (pan_counter == 0) { @@ -178,12 +208,12 @@ void MapWindow::timerUpdate() { zoom_counter--; } - if (sm->updated("navInstruction")) { - if (sm->valid("navInstruction")) { - auto i = (*sm)["navInstruction"].getNavInstruction(); + if (sm.updated("navInstruction")) { + if (sm.valid("navInstruction")) { + auto i = sm["navInstruction"].getNavInstruction(); emit ETAChanged(i.getTimeRemaining(), i.getTimeRemainingTypical(), i.getDistanceRemaining()); - if (localizer_valid) { + if (locationd_valid || laikad_valid) { m_map->setPitch(MAX_PITCH); // TODO: smooth pitching based on maneuver distance emit distanceChanged(i.getManeuverDistance()); // TODO: combine with instructionsChanged emit instructionsChanged(i); @@ -194,9 +224,9 @@ void MapWindow::timerUpdate() { } } - if (sm->rcv_frame("navRoute") != route_rcv_frame) { + if (sm.rcv_frame("navRoute") != route_rcv_frame) { qWarning() << "Updating navLayer with new route"; - auto route = (*sm)["navRoute"].getNavRoute(); + auto route = sm["navRoute"].getNavRoute(); auto route_points = capnp_coordinate_list_to_collection(route.getCoordinates()); QMapbox::Feature feature(QMapbox::Feature::LineStringType, route_points, {}, {}); QVariantMap navSource; @@ -205,7 +235,7 @@ void MapWindow::timerUpdate() { m_map->updateSource("navSource", navSource); m_map->setLayoutProperty("navLayer", "visibility", "visible"); - route_rcv_frame = sm->rcv_frame("navRoute"); + route_rcv_frame = sm.rcv_frame("navRoute"); } } @@ -388,10 +418,10 @@ void MapInstructions::updateDistance(float d) { if (uiState()->scene.is_metric) { if (d > 500) { distance_str.setNum(d / 1000, 'f', 1); - distance_str += " km"; + distance_str += tr(" km"); } else { distance_str.setNum(50 * int(d / 50)); - distance_str += " m"; + distance_str += tr(" m"); } } else { float miles = d * METER_TO_MILE; @@ -399,10 +429,10 @@ void MapInstructions::updateDistance(float d) { if (feet > 500) { distance_str.setNum(miles, 'f', 1); - distance_str += " mi"; + distance_str += tr(" mi"); } else { distance_str.setNum(50 * int(feet / 50)); - distance_str += " ft"; + distance_str += tr(" ft"); } } @@ -496,6 +526,10 @@ void MapInstructions::updateInstructions(cereal::NavInstruction::Reader instruct fn += "turn_straight"; } + if (!active) { + fn += "_inactive"; + } + auto icon = new QLabel; int wh = active ? 125 : 75; icon->setPixmap(loadPixmap(fn + ICON_SUFFIX, {wh, wh}, Qt::IgnoreAspectRatio)); @@ -585,7 +619,7 @@ void MapETA::updateETA(float s, float s_typical, float d) { auto eta_time = QDateTime::currentDateTime().addSecs(s).time(); if (params.getBool("NavSettingTime24h")) { eta->setText(eta_time.toString("HH:mm")); - eta_unit->setText("eta"); + eta_unit->setText(tr("eta")); } else { auto t = eta_time.toString("h:mm a").split(' '); eta->setText(t[0]); @@ -595,11 +629,11 @@ void MapETA::updateETA(float s, float s_typical, float d) { // Remaining time if (s < 3600) { time->setText(QString::number(int(s / 60))); - time_unit->setText("min"); + time_unit->setText(tr("min")); } else { int hours = int(s) / 3600; time->setText(QString::number(hours) + ":" + QString::number(int((s - hours * 3600) / 60)).rightJustified(2, '0')); - time_unit->setText("hr"); + time_unit->setText(tr("hr")); } QString color; @@ -619,10 +653,10 @@ void MapETA::updateETA(float s, float s_typical, float d) { float num = 0; if (uiState()->scene.is_metric) { num = d / 1000.0; - distance_unit->setText("km"); + distance_unit->setText(tr("km")); } else { num = d * METER_TO_MILE; - distance_unit->setText("mi"); + distance_unit->setText(tr("mi")); } distance_str.setNum(num, 'f', num < 100 ? 1 : 0); diff --git a/selfdrive/ui/qt/maps/map.h b/selfdrive/ui/qt/maps/map.h index 01a13f3b7..ecba867ed 100644 --- a/selfdrive/ui/qt/maps/map.h +++ b/selfdrive/ui/qt/maps/map.h @@ -5,22 +5,22 @@ #include #include #include -#include #include +#include #include #include #include +#include #include #include -#include -#include +#include #include -#include -#include +#include +#include "cereal/messaging/messaging.h" #include "common/params.h" #include "common/util.h" -#include "cereal/messaging/messaging.h" +#include "selfdrive/ui/ui.h" class MapInstructions : public QWidget { Q_OBJECT @@ -91,8 +91,6 @@ private: void pinchTriggered(QPinchGesture *gesture); bool m_sourceAdded = false; - SubMaster *sm; - QTimer* timer; bool loaded_once = false; bool allow_open = true; @@ -106,7 +104,8 @@ private: std::optional last_position; std::optional last_bearing; FirstOrderFilter velocity_filter; - bool localizer_valid = false; + bool laikad_valid = false; + bool locationd_valid = false; MapInstructions* map_instructions; MapETA* map_eta; @@ -115,7 +114,7 @@ private: uint64_t route_rcv_frame = 0; private slots: - void timerUpdate(); + void updateState(const UIState &s); public slots: void offroadTransition(bool offroad); diff --git a/selfdrive/ui/qt/maps/map_helpers.cc b/selfdrive/ui/qt/maps/map_helpers.cc index be1098994..66acb7a25 100644 --- a/selfdrive/ui/qt/maps/map_helpers.cc +++ b/selfdrive/ui/qt/maps/map_helpers.cc @@ -4,7 +4,7 @@ #include #include "common/params.h" -#include "selfdrive/hardware/hw.h" +#include "system/hardware/hw.h" #include "selfdrive/ui/qt/api.h" QString get_mapbox_token() { @@ -101,101 +101,6 @@ QMapbox::CoordinatesCollections coordinate_list_to_collection(QList polyline_to_coordinate_list(const QString &polylineString) { - QList path; - if (polylineString.isEmpty()) - return path; - - QByteArray data = polylineString.toLatin1(); - - bool parsingLatitude = true; - - int shift = 0; - int value = 0; - - QGeoCoordinate coord(0, 0); - - for (int i = 0; i < data.length(); ++i) { - unsigned char c = data.at(i) - 63; - - value |= (c & 0x1f) << shift; - shift += 5; - - // another chunk - if (c & 0x20) - continue; - - int diff = (value & 1) ? ~(value >> 1) : (value >> 1); - - if (parsingLatitude) { - coord.setLatitude(coord.latitude() + (double)diff/1e6); - } else { - coord.setLongitude(coord.longitude() + (double)diff/1e6); - path.append(coord); - } - - parsingLatitude = !parsingLatitude; - - value = 0; - shift = 0; - } - - return path; -} - -static QGeoCoordinate sub(QGeoCoordinate v, QGeoCoordinate w) { - return QGeoCoordinate(v.latitude() - w.latitude(), v.longitude() - w.longitude()); -} - -static QGeoCoordinate add(QGeoCoordinate v, QGeoCoordinate w) { - return QGeoCoordinate(v.latitude() + w.latitude(), v.longitude() + w.longitude()); -} - -static QGeoCoordinate mul(QGeoCoordinate v, float c) { - return QGeoCoordinate(c * v.latitude(), c * v.longitude()); -} - -static float dot(QGeoCoordinate v, QGeoCoordinate w) { - return v.latitude() * w.latitude() + v.longitude() * w.longitude(); -} - -float minimum_distance(QGeoCoordinate a, QGeoCoordinate b, QGeoCoordinate p) { - // If a and b are the same coordinate the computation below doesn't work - if (a.distanceTo(b) < 0.01) { - return a.distanceTo(p); - } - - const QGeoCoordinate ap = sub(p, a); - const QGeoCoordinate ab = sub(b, a); - const float t = std::clamp(dot(ap, ab) / dot(ab, ab), 0.0f, 1.0f); - const QGeoCoordinate projection = add(a, mul(ab, t)); - return projection.distanceTo(p); -} - -float distance_along_geometry(QList geometry, QGeoCoordinate pos) { - if (geometry.size() <= 2) { - return geometry[0].distanceTo(pos); - } - - // 1. Find segment that is closest to current position - // 2. Total distance is sum of distance to start of closest segment - // + all previous segments - double total_distance = 0; - double total_distance_closest = 0; - double closest_distance = std::numeric_limits::max(); - - for (int i = 0; i < geometry.size() - 1; i++) { - double d = minimum_distance(geometry[i], geometry[i+1], pos); - if (d < closest_distance) { - closest_distance = d; - total_distance_closest = total_distance + geometry[i].distanceTo(pos); - } - total_distance += geometry[i].distanceTo(geometry[i+1]); - } - - return total_distance_closest; -} - std::optional coordinate_from_param(std::string param) { QString json_str = QString::fromStdString(Params().get(param)); if (json_str.isEmpty()) return {}; @@ -211,3 +116,8 @@ std::optional coordinate_from_param(std::string param) { return {}; } } + +double angle_difference(double angle1, double angle2) { + double diff = fmod(angle2 - angle1 + 180.0, 360.0) - 180.0; + return diff < -180.0 ? diff + 360.0 : diff; +} diff --git a/selfdrive/ui/qt/maps/map_helpers.h b/selfdrive/ui/qt/maps/map_helpers.h index cda4cd1cf..1c08c541c 100644 --- a/selfdrive/ui/qt/maps/map_helpers.h +++ b/selfdrive/ui/qt/maps/map_helpers.h @@ -24,8 +24,6 @@ QMapbox::CoordinatesCollections model_to_collection( QMapbox::CoordinatesCollections coordinate_to_collection(QMapbox::Coordinate c); QMapbox::CoordinatesCollections capnp_coordinate_list_to_collection(const capnp::List::Reader &coordinate_list); QMapbox::CoordinatesCollections coordinate_list_to_collection(QList coordinate_list); -QList polyline_to_coordinate_list(const QString &polylineString); -float minimum_distance(QGeoCoordinate a, QGeoCoordinate b, QGeoCoordinate p); std::optional coordinate_from_param(std::string param); -float distance_along_geometry(QList geometry, QGeoCoordinate pos); +double angle_difference(double angle1, double angle2); diff --git a/selfdrive/ui/qt/maps/map_settings.cc b/selfdrive/ui/qt/maps/map_settings.cc index 674c7fa7c..d143b44e7 100644 --- a/selfdrive/ui/qt/maps/map_settings.cc +++ b/selfdrive/ui/qt/maps/map_settings.cc @@ -59,11 +59,11 @@ MapPanel::MapPanel(QWidget* parent) : QWidget(parent) { current_widget = new QWidget(this); QVBoxLayout *current_layout = new QVBoxLayout(current_widget); - QLabel *title = new QLabel("Current Destination"); + QLabel *title = new QLabel(tr("Current Destination")); title->setStyleSheet("font-size: 55px"); current_layout->addWidget(title); - current_route = new ButtonControl("", "CLEAR"); + current_route = new ButtonControl("", tr("CLEAR")); current_route->setStyleSheet("padding-left: 40px;"); current_layout->addWidget(current_route); QObject::connect(current_route, &ButtonControl::clicked, [=]() { @@ -78,7 +78,7 @@ MapPanel::MapPanel(QWidget* parent) : QWidget(parent) { main_layout->addWidget(current_widget); // Recents - QLabel *recents_title = new QLabel("Recent Destinations"); + QLabel *recents_title = new QLabel(tr("Recent Destinations")); recents_title->setStyleSheet("font-size: 55px"); main_layout->addWidget(recents_title); main_layout->addSpacing(20); @@ -92,7 +92,7 @@ MapPanel::MapPanel(QWidget* parent) : QWidget(parent) { QWidget * no_prime_widget = new QWidget; { QVBoxLayout *no_prime_layout = new QVBoxLayout(no_prime_widget); - QLabel *signup_header = new QLabel("Try the Navigation Beta"); + QLabel *signup_header = new QLabel(tr("Try the Navigation Beta")); signup_header->setStyleSheet(R"(font-size: 75px; color: white; font-weight:600;)"); signup_header->setAlignment(Qt::AlignCenter); @@ -104,7 +104,7 @@ MapPanel::MapPanel(QWidget* parent) : QWidget(parent) { screenshot->setPixmap(pm.scaledToWidth(1080, Qt::SmoothTransformation)); no_prime_layout->addWidget(screenshot, 0, Qt::AlignHCenter); - QLabel *signup = new QLabel("Get turn-by-turn directions displayed and more with a comma \nprime subscription. Sign up now: https://connect.comma.ai"); + QLabel *signup = new QLabel(tr("Get turn-by-turn directions displayed and more with a comma\nprime subscription. Sign up now: https://connect.comma.ai")); signup->setStyleSheet(R"(font-size: 45px; color: white; font-weight:300;)"); signup->setAlignment(Qt::AlignCenter); @@ -161,12 +161,12 @@ void MapPanel::showEvent(QShowEvent *event) { void MapPanel::clear() { home_button->setIcon(QPixmap("../assets/navigation/home_inactive.png")); home_address->setStyleSheet(R"(font-size: 50px; color: grey;)"); - home_address->setText("No home\nlocation set"); + home_address->setText(tr("No home\nlocation set")); home_button->disconnect(); work_button->setIcon(QPixmap("../assets/navigation/work_inactive.png")); work_address->setStyleSheet(R"(font-size: 50px; color: grey;)"); - work_address->setText("No work\nlocation set"); + work_address->setText(tr("No work\nlocation set")); work_button->disconnect(); clearLayout(recent_layout); @@ -279,7 +279,7 @@ void MapPanel::parseResponse(const QString &response, bool success) { } if (!has_recents) { - QLabel *no_recents = new QLabel("no recent destinations"); + QLabel *no_recents = new QLabel(tr("no recent destinations")); no_recents->setStyleSheet(R"(font-size: 50px; color: #9c9c9c)"); recent_layout->addWidget(no_recents); } diff --git a/selfdrive/ui/qt/offroad/driverview.cc b/selfdrive/ui/qt/offroad/driverview.cc index 3302687bf..5bf70584a 100644 --- a/selfdrive/ui/qt/offroad/driverview.cc +++ b/selfdrive/ui/qt/offroad/driverview.cc @@ -25,7 +25,7 @@ void DriverViewWindow::mouseReleaseEvent(QMouseEvent* e) { emit done(); } -DriverViewScene::DriverViewScene(QWidget* parent) : sm({"driverState"}), QWidget(parent) { +DriverViewScene::DriverViewScene(QWidget* parent) : sm({"driverStateV2"}), QWidget(parent) { face_img = loadPixmap("../assets/img_driver_face.png", {FACE_IMG_SIZE, FACE_IMG_SIZE}); } @@ -53,49 +53,40 @@ void DriverViewScene::paintEvent(QPaintEvent* event) { p.setPen(Qt::white); p.setRenderHint(QPainter::TextAntialiasing); configFont(p, "Inter", 100, "Bold"); - p.drawText(geometry(), Qt::AlignCenter, "camera starting"); + p.drawText(geometry(), Qt::AlignCenter, tr("camera starting")); return; } - const int width = 4 * height() / 3; - const QRect rect2 = {rect().center().x() - width / 2, rect().top(), width, rect().height()}; - const QRect valid_rect = {is_rhd ? rect2.right() - rect2.height() / 2 : rect2.left(), rect2.top(), rect2.height() / 2, rect2.height()}; + cereal::DriverStateV2::Reader driver_state = sm["driverStateV2"].getDriverStateV2(); + cereal::DriverStateV2::DriverData::Reader driver_data; - // blackout - const QColor bg(0, 0, 0, 140); - const QRect& blackout_rect = Hardware::TICI() ? rect() : rect2; - p.fillRect(blackout_rect.adjusted(0, 0, valid_rect.left() - blackout_rect.right(), 0), bg); - p.fillRect(blackout_rect.adjusted(valid_rect.right() - blackout_rect.left(), 0, 0, 0), bg); - if (Hardware::TICI()) { - p.fillRect(blackout_rect.adjusted(valid_rect.left()-blackout_rect.left()+1, 0, valid_rect.right()-blackout_rect.right()-1, -valid_rect.height()*7/10), bg); // top dz - } + // is_rhd = driver_state.getWheelOnRightProb() > 0.5; + driver_data = is_rhd ? driver_state.getRightDriverData() : driver_state.getLeftDriverData(); - // face bounding box - cereal::DriverState::Reader driver_state = sm["driverState"].getDriverState(); - bool face_detected = driver_state.getFaceProb() > 0.5; + bool face_detected = driver_data.getFaceProb() > 0.7; if (face_detected) { - auto fxy_list = driver_state.getFacePosition(); - auto std_list = driver_state.getFaceOrientationStd(); + auto fxy_list = driver_data.getFacePosition(); + auto std_list = driver_data.getFaceOrientationStd(); float face_x = fxy_list[0]; float face_y = fxy_list[1]; float face_std = std::max(std_list[0], std_list[1]); float alpha = 0.7; - if (face_std > 0.08) { - alpha = std::max(0.7 - (face_std-0.08)*7, 0.0); + if (face_std > 0.15) { + alpha = std::max(0.7 - (face_std-0.15)*3.5, 0.0); } - const int box_size = 0.6 * rect2.height() / 2; - const float rhd_offset = 0.05; // lhd is shifted, so rhd is not mirrored - int fbox_x = valid_rect.center().x() + (is_rhd ? (face_x + rhd_offset) : -face_x) * valid_rect.width(); - int fbox_y = valid_rect.center().y() + face_y * valid_rect.height(); + const int box_size = 220; + // use approx instead of distort_points + int fbox_x = 1080.0 - 1714.0 * face_x; + int fbox_y = -135.0 + (504.0 + std::abs(face_x)*112.0) + (1205.0 - std::abs(face_x)*724.0) * face_y; p.setPen(QPen(QColor(255, 255, 255, alpha * 255), 10)); p.drawRoundedRect(fbox_x - box_size / 2, fbox_y - box_size / 2, box_size, box_size, 35.0, 35.0); } // icon - const int img_offset = 30; - const int img_x = is_rhd ? rect2.right() - FACE_IMG_SIZE - img_offset : rect2.left() + img_offset; - const int img_y = rect2.bottom() - FACE_IMG_SIZE - img_offset; - p.setOpacity(face_detected ? 1.0 : 0.3); + const int img_offset = 60; + const int img_x = rect().left() + img_offset; + const int img_y = rect().bottom() - FACE_IMG_SIZE - img_offset; + p.setOpacity(face_detected ? 1.0 : 0.2); p.drawPixmap(img_x, img_y, face_img); } diff --git a/selfdrive/ui/qt/offroad/networking.cc b/selfdrive/ui/qt/offroad/networking.cc index 9c2088b1d..c7341d198 100644 --- a/selfdrive/ui/qt/offroad/networking.cc +++ b/selfdrive/ui/qt/offroad/networking.cc @@ -27,10 +27,10 @@ Networking::Networking(QWidget* parent, bool show_advanced) : QFrame(parent) { QVBoxLayout* vlayout = new QVBoxLayout(wifiScreen); vlayout->setContentsMargins(20, 20, 20, 20); if (show_advanced) { - QPushButton* advancedSettings = new QPushButton("Advanced"); - advancedSettings->setObjectName("advancedBtn"); + QPushButton* advancedSettings = new QPushButton(tr("Advanced")); + advancedSettings->setObjectName("advanced_btn"); advancedSettings->setStyleSheet("margin-right: 30px;"); - advancedSettings->setFixedSize(350, 100); + advancedSettings->setFixedSize(400, 100); connect(advancedSettings, &QPushButton::clicked, [=]() { main_layout->setCurrentWidget(an); }); vlayout->addSpacing(10); vlayout->addWidget(advancedSettings, 0, Qt::AlignRight); @@ -55,16 +55,18 @@ Networking::Networking(QWidget* parent, bool show_advanced) : QFrame(parent) { setAutoFillBackground(true); setPalette(pal); - // TODO: revisit pressed colors setStyleSheet(R"( - #wifiWidget > QPushButton, #back_btn, #advancedBtn { + #wifiWidget > QPushButton, #back_btn, #advanced_btn { font-size: 50px; margin: 0px; padding: 15px; border-width: 0; border-radius: 30px; color: #dddddd; - background-color: #444444; + background-color: #393939; + } + #back_btn:pressed, #advanced_btn:pressed { + background-color: #4a4a4a; } )"); main_layout->setCurrentWidget(wifiScreen); @@ -82,7 +84,7 @@ void Networking::connectToNetwork(const Network &n) { } else if (n.security_type == SecurityType::OPEN) { wifi->connect(n); } else if (n.security_type == SecurityType::WPA) { - QString pass = InputDialog::getText("Enter password", this, "for \"" + n.ssid + "\"", true, 8); + QString pass = InputDialog::getText(tr("Enter password"), this, tr("for \"%1\"").arg(QString::fromUtf8(n.ssid)), true, 8); if (!pass.isEmpty()) { wifi->connect(n, pass); } @@ -92,7 +94,7 @@ void Networking::connectToNetwork(const Network &n) { void Networking::wrongPassword(const QString &ssid) { if (wifi->seenNetworks.contains(ssid)) { const Network &n = wifi->seenNetworks.value(ssid); - QString pass = InputDialog::getText("Wrong password", this, "for \"" + n.ssid +"\"", true, 8); + QString pass = InputDialog::getText(tr("Wrong password"), this, tr("for \"%1\"").arg(QString::fromUtf8(n.ssid)), true, 8); if (!pass.isEmpty()) { wifi->connect(n, pass); } @@ -116,22 +118,22 @@ AdvancedNetworking::AdvancedNetworking(QWidget* parent, WifiManager* wifi): QWid main_layout->setSpacing(20); // Back button - QPushButton* back = new QPushButton("Back"); + QPushButton* back = new QPushButton(tr("Back")); back->setObjectName("back_btn"); - back->setFixedSize(500, 100); + back->setFixedSize(400, 100); connect(back, &QPushButton::clicked, [=]() { emit backPress(); }); main_layout->addWidget(back, 0, Qt::AlignLeft); ListWidget *list = new ListWidget(this); // Enable tethering layout - tetheringToggle = new ToggleControl("Enable Tethering", "", "", wifi->isTetheringEnabled()); + tetheringToggle = new ToggleControl(tr("Enable Tethering"), "", "", wifi->isTetheringEnabled()); list->addItem(tetheringToggle); QObject::connect(tetheringToggle, &ToggleControl::toggleFlipped, this, &AdvancedNetworking::toggleTethering); // Change tethering password - ButtonControl *editPasswordButton = new ButtonControl("Tethering Password", "EDIT"); + ButtonControl *editPasswordButton = new ButtonControl(tr("Tethering Password"), tr("EDIT")); connect(editPasswordButton, &ButtonControl::clicked, [=]() { - QString pass = InputDialog::getText("Enter new tethering password", this, "", true, 8, wifi->getTetheringPassword()); + QString pass = InputDialog::getText(tr("Enter new tethering password"), this, "", true, 8, wifi->getTetheringPassword()); if (!pass.isEmpty()) { wifi->changeTetheringPassword(pass); } @@ -139,7 +141,7 @@ AdvancedNetworking::AdvancedNetworking(QWidget* parent, WifiManager* wifi): QWid list->addItem(editPasswordButton); // IP address - ipLabel = new LabelControl("IP Address", wifi->ipv4_address); + ipLabel = new LabelControl(tr("IP Address"), wifi->ipv4_address); list->addItem(ipLabel); // SSH keys @@ -148,7 +150,7 @@ AdvancedNetworking::AdvancedNetworking(QWidget* parent, WifiManager* wifi): QWid // Roaming toggle const bool roamingEnabled = params.getBool("GsmRoaming"); - ToggleControl *roamingToggle = new ToggleControl("Enable Roaming", "", "", roamingEnabled); + ToggleControl *roamingToggle = new ToggleControl(tr("Enable Roaming"), "", "", roamingEnabled); QObject::connect(roamingToggle, &SshToggle::toggleFlipped, [=](bool state) { params.putBool("GsmRoaming", state); wifi->updateGsmSettings(state, QString::fromStdString(params.get("GsmApn"))); @@ -156,11 +158,11 @@ AdvancedNetworking::AdvancedNetworking(QWidget* parent, WifiManager* wifi): QWid list->addItem(roamingToggle); // APN settings - ButtonControl *editApnButton = new ButtonControl("APN Setting", "EDIT"); + ButtonControl *editApnButton = new ButtonControl(tr("APN Setting"), tr("EDIT")); connect(editApnButton, &ButtonControl::clicked, [=]() { const bool roamingEnabled = params.getBool("GsmRoaming"); const QString cur_apn = QString::fromStdString(params.get("GsmApn")); - QString apn = InputDialog::getText("Enter APN", this, "leave blank for automatic configuration", false, -1, cur_apn).trimmed(); + QString apn = InputDialog::getText(tr("Enter APN"), this, tr("leave blank for automatic configuration"), false, -1, cur_apn).trimmed(); if (apn.isEmpty()) { params.remove("GsmApn"); @@ -172,7 +174,7 @@ AdvancedNetworking::AdvancedNetworking(QWidget* parent, WifiManager* wifi): QWid list->addItem(editApnButton); // Set initial config - wifi->updateGsmSettings(roamingEnabled, QString::fromStdString(params.get("GsmApn"))); + wifi->updateGsmSettings(roamingEnabled, QString::fromStdString(params.get("GsmApn"))); main_layout->addWidget(new ScrollView(list, this)); main_layout->addStretch(1); @@ -205,7 +207,7 @@ WifiUI::WifiUI(QWidget *parent, WifiManager* wifi) : QWidget(parent), wifi(wifi) checkmark = QPixmap(ASSET_PATH + "offroad/icon_checkmark.svg").scaledToWidth(49, Qt::SmoothTransformation); circled_slash = QPixmap(ASSET_PATH + "img_circled_slash.svg").scaledToWidth(49, Qt::SmoothTransformation); - QLabel *scanning = new QLabel("Scanning for networks..."); + QLabel *scanning = new QLabel(tr("Scanning for networks...")); scanning->setStyleSheet("font-size: 65px;"); main_layout->addWidget(scanning, 0, Qt::AlignCenter); @@ -258,7 +260,7 @@ void WifiUI::refresh() { clearLayout(main_layout); if (wifi->seenNetworks.size() == 0) { - QLabel *scanning = new QLabel("Scanning for networks..."); + QLabel *scanning = new QLabel(tr("Scanning for networks...")); scanning->setStyleSheet("font-size: 65px;"); main_layout->addWidget(scanning, 0, Qt::AlignCenter); return; @@ -284,17 +286,17 @@ void WifiUI::refresh() { hlayout->addWidget(ssidLabel, network.connected == ConnectedType::CONNECTING ? 0 : 1); if (network.connected == ConnectedType::CONNECTING) { - QPushButton *connecting = new QPushButton("CONNECTING..."); + QPushButton *connecting = new QPushButton(tr("CONNECTING...")); connecting->setObjectName("connecting"); hlayout->addWidget(connecting, 2, Qt::AlignLeft); } // Forget button if (wifi->isKnownConnection(network.ssid) && !wifi->isTetheringEnabled()) { - QPushButton *forgetBtn = new QPushButton("FORGET"); + QPushButton *forgetBtn = new QPushButton(tr("FORGET")); forgetBtn->setObjectName("forgetBtn"); QObject::connect(forgetBtn, &QPushButton::clicked, [=]() { - if (ConfirmationDialog::confirm("Forget Wi-Fi Network \"" + QString::fromUtf8(network.ssid) + "\"?", this)) { + if (ConfirmationDialog::confirm(tr("Forget Wi-Fi Network \"%1\"?").arg(QString::fromUtf8(network.ssid)), this)) { wifi->forgetConnection(network.ssid); } }); diff --git a/selfdrive/ui/qt/offroad/onboarding.cc b/selfdrive/ui/qt/offroad/onboarding.cc index d32cc26e4..f3e50b572 100644 --- a/selfdrive/ui/qt/offroad/onboarding.cc +++ b/selfdrive/ui/qt/offroad/onboarding.cc @@ -76,7 +76,7 @@ void TermsPage::showEvent(QShowEvent *event) { main_layout->setContentsMargins(45, 35, 45, 45); main_layout->setSpacing(0); - QLabel *title = new QLabel("Terms & Conditions"); + QLabel *title = new QLabel(tr("Terms & Conditions")); title->setStyleSheet("font-size: 90px; font-weight: 600;"); main_layout->addWidget(title); @@ -104,11 +104,11 @@ void TermsPage::showEvent(QShowEvent *event) { buttons->setSpacing(45); main_layout->addLayout(buttons); - QPushButton *decline_btn = new QPushButton("Decline"); + QPushButton *decline_btn = new QPushButton(tr("Decline")); buttons->addWidget(decline_btn); QObject::connect(decline_btn, &QPushButton::clicked, this, &TermsPage::declinedTerms); - accept_btn = new QPushButton("Scroll to accept"); + accept_btn = new QPushButton(tr("Scroll to accept")); accept_btn->setEnabled(false); accept_btn->setStyleSheet(R"( QPushButton { @@ -123,7 +123,7 @@ void TermsPage::showEvent(QShowEvent *event) { } void TermsPage::enableAccept() { - accept_btn->setText("Agree"); + accept_btn->setText(tr("Agree")); accept_btn->setEnabled(true); } @@ -137,7 +137,7 @@ void DeclinePage::showEvent(QShowEvent *event) { main_layout->setSpacing(40); QLabel *text = new QLabel(this); - text->setText("You must accept the Terms and Conditions in order to use openpilot."); + text->setText(tr("You must accept the Terms and Conditions in order to use openpilot.")); text->setStyleSheet(R"(font-size: 80px; font-weight: 300; margin: 200px;)"); text->setWordWrap(true); main_layout->addWidget(text, 0, Qt::AlignCenter); @@ -146,12 +146,12 @@ void DeclinePage::showEvent(QShowEvent *event) { buttons->setSpacing(45); main_layout->addLayout(buttons); - QPushButton *back_btn = new QPushButton("Back"); + QPushButton *back_btn = new QPushButton(tr("Back")); buttons->addWidget(back_btn); QObject::connect(back_btn, &QPushButton::clicked, this, &DeclinePage::getBack); - QPushButton *uninstall_btn = new QPushButton(QString("Decline, uninstall %1").arg(getBrand())); + QPushButton *uninstall_btn = new QPushButton(tr("Decline, uninstall %1").arg(getBrand())); uninstall_btn->setStyleSheet("background-color: #B73D3D"); buttons->addWidget(uninstall_btn); QObject::connect(uninstall_btn, &QPushButton::clicked, [=]() { diff --git a/selfdrive/ui/qt/offroad/settings.cc b/selfdrive/ui/qt/offroad/settings.cc index 00e120d86..9a6e20396 100644 --- a/selfdrive/ui/qt/offroad/settings.cc +++ b/selfdrive/ui/qt/offroad/settings.cc @@ -13,8 +13,9 @@ #endif #include "common/params.h" +#include "common/watchdog.h" #include "common/util.h" -#include "selfdrive/hardware/hw.h" +#include "system/hardware/hw.h" #include "selfdrive/ui/qt/widgets/controls.h" #include "selfdrive/ui/qt/widgets/input.h" #include "selfdrive/ui/qt/widgets/scrollview.h" @@ -29,51 +30,45 @@ TogglesPanel::TogglesPanel(SettingsWindow *parent) : ListWidget(parent) { std::vector> toggles{ { "OpenpilotEnabledToggle", - "Enable openpilot", - "Use the openpilot system for adaptive cruise control and lane keep driver assistance. Your attention is required at all times to use this feature. Changing this setting takes effect when the car is powered off.", + tr("Enable openpilot"), + tr("Use the openpilot system for adaptive cruise control and lane keep driver assistance. Your attention is required at all times to use this feature. Changing this setting takes effect when the car is powered off."), "../assets/offroad/icon_openpilot.png", }, { "IsLdwEnabled", - "Enable Lane Departure Warnings", - "Receive alerts to steer back into the lane when your vehicle drifts over a detected lane line without a turn signal activated while driving over 31 mph (50 km/h).", + tr("Enable Lane Departure Warnings"), + tr("Receive alerts to steer back into the lane when your vehicle drifts over a detected lane line without a turn signal activated while driving over 31 mph (50 km/h)."), "../assets/offroad/icon_warning.png", }, { "IsRHD", - "Enable Right-Hand Drive", - "Allow openpilot to obey left-hand traffic conventions and perform driver monitoring on right driver seat.", + tr("Enable Right-Hand Drive"), + tr("Allow openpilot to obey left-hand traffic conventions and perform driver monitoring on right driver seat."), "../assets/offroad/icon_openpilot_mirrored.png", }, { "IsMetric", - "Use Metric System", - "Display speed in km/h instead of mph.", + tr("Use Metric System"), + tr("Display speed in km/h instead of mph."), "../assets/offroad/icon_metric.png", }, { "RecordFront", - "Record and Upload Driver Camera", - "Upload data from the driver facing camera and help improve the driver monitoring algorithm.", + tr("Record and Upload Driver Camera"), + tr("Upload data from the driver facing camera and help improve the driver monitoring algorithm."), "../assets/offroad/icon_monitoring.png", }, - { - "EndToEndToggle", - "\U0001f96c Disable use of lanelines (Alpha) \U0001f96c", - "In this mode openpilot will ignore lanelines and just drive how it thinks a human would.", - "../assets/offroad/icon_road.png", - }, { "DisengageOnAccelerator", - "Disengage On Accelerator Pedal", - "When enabled, pressing the accelerator pedal will disengage openpilot.", + tr("Disengage On Accelerator Pedal"), + tr("When enabled, pressing the accelerator pedal will disengage openpilot."), "../assets/offroad/icon_disengage_on_accelerator.svg", }, #ifdef ENABLE_MAPS { "NavSettingTime24h", - "Show ETA in 24h format", - "Use 24h format instead of am/pm", + tr("Show ETA in 24h format"), + tr("Use 24h format instead of am/pm"), "../assets/offroad/icon_metric.png", }, #endif @@ -85,8 +80,8 @@ TogglesPanel::TogglesPanel(SettingsWindow *parent) : ListWidget(parent) { if (params.getBool("DisableRadar_Allow")) { toggles.push_back({ "DisableRadar", - "openpilot Longitudinal Control", - "openpilot will disable the car's radar and will take over control of gas and brakes. Warning: this disables AEB!", + tr("openpilot Longitudinal Control"), + tr("openpilot will disable the car's radar and will take over control of gas and brakes. Warning: this disables AEB!"), "../assets/offroad/icon_speed_limit.png", }); } @@ -95,38 +90,35 @@ TogglesPanel::TogglesPanel(SettingsWindow *parent) : ListWidget(parent) { auto toggle = new ParamControl(param, title, desc, icon, this); bool locked = params.getBool((param + "Lock").toStdString()); toggle->setEnabled(!locked); - if (!locked) { - connect(uiState(), &UIState::offroadTransition, toggle, &ParamControl::setEnabled); - } addItem(toggle); } } DevicePanel::DevicePanel(SettingsWindow *parent) : ListWidget(parent) { setSpacing(50); - addItem(new LabelControl("Dongle ID", getDongleId().value_or("N/A"))); - addItem(new LabelControl("Serial", params.get("HardwareSerial").c_str())); + addItem(new LabelControl(tr("Dongle ID"), getDongleId().value_or(tr("N/A")))); + addItem(new LabelControl(tr("Serial"), params.get("HardwareSerial").c_str())); // offroad-only buttons - auto dcamBtn = new ButtonControl("Driver Camera", "PREVIEW", - "Preview the driver facing camera to help optimize device mounting position for best driver monitoring experience. (vehicle must be off)"); + auto dcamBtn = new ButtonControl(tr("Driver Camera"), tr("PREVIEW"), + tr("Preview the driver facing camera to ensure that driver monitoring has good visibility. (vehicle must be off)")); connect(dcamBtn, &ButtonControl::clicked, [=]() { emit showDriverView(); }); addItem(dcamBtn); - auto resetCalibBtn = new ButtonControl("Reset Calibration", "RESET", " "); + auto resetCalibBtn = new ButtonControl(tr("Reset Calibration"), tr("RESET"), ""); connect(resetCalibBtn, &ButtonControl::showDescription, this, &DevicePanel::updateCalibDescription); connect(resetCalibBtn, &ButtonControl::clicked, [&]() { - if (ConfirmationDialog::confirm("Are you sure you want to reset calibration?", this)) { + if (ConfirmationDialog::confirm(tr("Are you sure you want to reset calibration?"), this)) { params.remove("CalibrationParams"); } }); addItem(resetCalibBtn); if (!params.getBool("Passive")) { - auto retrainingBtn = new ButtonControl("Review Training Guide", "REVIEW", "Review the rules, features, and limitations of openpilot"); + auto retrainingBtn = new ButtonControl(tr("Review Training Guide"), tr("REVIEW"), tr("Review the rules, features, and limitations of openpilot")); connect(retrainingBtn, &ButtonControl::clicked, [=]() { - if (ConfirmationDialog::confirm("Are you sure you want to review the training guide?", this)) { + if (ConfirmationDialog::confirm(tr("Are you sure you want to review the training guide?"), this)) { emit reviewTrainingGuide(); } }); @@ -134,7 +126,7 @@ DevicePanel::DevicePanel(SettingsWindow *parent) : ListWidget(parent) { } if (Hardware::TICI()) { - auto regulatoryBtn = new ButtonControl("Regulatory", "VIEW", ""); + auto regulatoryBtn = new ButtonControl(tr("Regulatory"), tr("VIEW"), ""); connect(regulatoryBtn, &ButtonControl::clicked, [=]() { const std::string txt = util::read_file("../assets/offroad/fcc.html"); RichTextDialog::alert(QString::fromStdString(txt), this); @@ -142,6 +134,20 @@ DevicePanel::DevicePanel(SettingsWindow *parent) : ListWidget(parent) { addItem(regulatoryBtn); } + auto translateBtn = new ButtonControl(tr("Change Language"), tr("CHANGE"), ""); + connect(translateBtn, &ButtonControl::clicked, [=]() { + QMap langs = getSupportedLanguages(); + QString currentLang = QString::fromStdString(Params().get("LanguageSetting")); + QString selection = MultiOptionDialog::getSelection(tr("Select a language"), langs.keys(), langs.key(currentLang), this); + if (!selection.isEmpty()) { + // put language setting, exit Qt UI, and trigger fast restart + Params().put("LanguageSetting", langs[selection].toStdString()); + qApp->exit(18); + watchdog_kick(0); + } + }); + addItem(translateBtn); + QObject::connect(uiState(), &UIState::offroadTransition, [=](bool offroad) { for (auto btn : findChildren()) { btn->setEnabled(offroad); @@ -152,17 +158,17 @@ DevicePanel::DevicePanel(SettingsWindow *parent) : ListWidget(parent) { QHBoxLayout *power_layout = new QHBoxLayout(); power_layout->setSpacing(30); - QPushButton *reboot_btn = new QPushButton("Reboot"); + QPushButton *reboot_btn = new QPushButton(tr("Reboot")); reboot_btn->setObjectName("reboot_btn"); power_layout->addWidget(reboot_btn); QObject::connect(reboot_btn, &QPushButton::clicked, this, &DevicePanel::reboot); - QPushButton *poweroff_btn = new QPushButton("Power Off"); + QPushButton *poweroff_btn = new QPushButton(tr("Power Off")); poweroff_btn->setObjectName("poweroff_btn"); power_layout->addWidget(poweroff_btn); QObject::connect(poweroff_btn, &QPushButton::clicked, this, &DevicePanel::poweroff); - if (Hardware::TICI()) { + if (!Hardware::PC()) { connect(uiState(), &UIState::offroadTransition, poweroff_btn, &QPushButton::setVisible); } @@ -177,8 +183,8 @@ DevicePanel::DevicePanel(SettingsWindow *parent) : ListWidget(parent) { void DevicePanel::updateCalibDescription() { QString desc = - "openpilot requires the device to be mounted within 4° left or right and " - "within 5° up or 8° down. openpilot is continuously calibrating, resetting is rarely required."; + tr("openpilot requires the device to be mounted within 4° left or right and " + "within 5° up or 8° down. openpilot is continuously calibrating, resetting is rarely required."); std::string calib_bytes = Params().get("CalibrationParams"); if (!calib_bytes.empty()) { try { @@ -188,9 +194,9 @@ void DevicePanel::updateCalibDescription() { if (calib.getCalStatus() != 0) { double pitch = calib.getRpyCalib()[1] * (180 / M_PI); double yaw = calib.getRpyCalib()[2] * (180 / M_PI); - desc += QString(" Your device is pointed %1° %2 and %3° %4.") - .arg(QString::number(std::abs(pitch), 'g', 1), pitch > 0 ? "down" : "up", - QString::number(std::abs(yaw), 'g', 1), yaw > 0 ? "left" : "right"); + desc += tr(" Your device is pointed %1° %2 and %3° %4.") + .arg(QString::number(std::abs(pitch), 'g', 1), pitch > 0 ? tr("down") : tr("up"), + QString::number(std::abs(yaw), 'g', 1), yaw > 0 ? tr("left") : tr("right")); } } catch (kj::Exception) { qInfo() << "invalid CalibrationParams"; @@ -201,51 +207,51 @@ void DevicePanel::updateCalibDescription() { void DevicePanel::reboot() { if (!uiState()->engaged()) { - if (ConfirmationDialog::confirm("Are you sure you want to reboot?", this)) { + if (ConfirmationDialog::confirm(tr("Are you sure you want to reboot?"), this)) { // Check engaged again in case it changed while the dialog was open if (!uiState()->engaged()) { Params().putBool("DoReboot", true); } } } else { - ConfirmationDialog::alert("Disengage to Reboot", this); + ConfirmationDialog::alert(tr("Disengage to Reboot"), this); } } void DevicePanel::poweroff() { if (!uiState()->engaged()) { - if (ConfirmationDialog::confirm("Are you sure you want to power off?", this)) { + if (ConfirmationDialog::confirm(tr("Are you sure you want to power off?"), this)) { // Check engaged again in case it changed while the dialog was open if (!uiState()->engaged()) { Params().putBool("DoShutdown", true); } } } else { - ConfirmationDialog::alert("Disengage to Power Off", this); + ConfirmationDialog::alert(tr("Disengage to Power Off"), this); } } SoftwarePanel::SoftwarePanel(QWidget* parent) : ListWidget(parent) { - gitBranchLbl = new LabelControl("Git Branch"); - gitCommitLbl = new LabelControl("Git Commit"); - osVersionLbl = new LabelControl("OS Version"); - versionLbl = new LabelControl("Version", "", QString::fromStdString(params.get("ReleaseNotes")).trimmed()); - lastUpdateLbl = new LabelControl("Last Update Check", "", "The last time openpilot successfully checked for an update. The updater only runs while the car is off."); - updateBtn = new ButtonControl("Check for Update", ""); + gitBranchLbl = new LabelControl(tr("Git Branch")); + gitCommitLbl = new LabelControl(tr("Git Commit")); + osVersionLbl = new LabelControl(tr("OS Version")); + versionLbl = new LabelControl(tr("Version"), "", QString::fromStdString(params.get("ReleaseNotes")).trimmed()); + lastUpdateLbl = new LabelControl(tr("Last Update Check"), "", tr("The last time openpilot successfully checked for an update. The updater only runs while the car is off.")); + updateBtn = new ButtonControl(tr("Check for Update"), ""); connect(updateBtn, &ButtonControl::clicked, [=]() { if (params.getBool("IsOffroad")) { fs_watch->addPath(QString::fromStdString(params.getParamPath("LastUpdateTime"))); fs_watch->addPath(QString::fromStdString(params.getParamPath("UpdateFailedCount"))); - updateBtn->setText("CHECKING"); + updateBtn->setText(tr("CHECKING")); updateBtn->setEnabled(false); } std::system("pkill -1 -f selfdrive.updated"); }); - auto uninstallBtn = new ButtonControl("Uninstall " + getBrand(), "UNINSTALL"); + auto uninstallBtn = new ButtonControl(tr("Uninstall %1").arg(getBrand()), tr("UNINSTALL")); connect(uninstallBtn, &ButtonControl::clicked, [&]() { - if (ConfirmationDialog::confirm("Are you sure you want to uninstall?", this)) { + if (ConfirmationDialog::confirm(tr("Are you sure you want to uninstall?"), this)) { params.putBool("DoUninstall", true); } }); @@ -259,8 +265,8 @@ SoftwarePanel::SoftwarePanel(QWidget* parent) : ListWidget(parent) { fs_watch = new QFileSystemWatcher(this); QObject::connect(fs_watch, &QFileSystemWatcher::fileChanged, [=](const QString path) { if (path.contains("UpdateFailedCount") && std::atoi(params.get("UpdateFailedCount").c_str()) > 0) { - lastUpdateLbl->setText("failed to fetch update"); - updateBtn->setText("CHECK"); + lastUpdateLbl->setText(tr("failed to fetch update")); + updateBtn->setText(tr("CHECK")); updateBtn->setEnabled(true); } else if (path.contains("LastUpdateTime")) { updateLabels(); @@ -281,17 +287,13 @@ void SoftwarePanel::updateLabels() { versionLbl->setText(getBrandVersion()); lastUpdateLbl->setText(lastUpdate); - updateBtn->setText("CHECK"); + updateBtn->setText(tr("CHECK")); updateBtn->setEnabled(true); gitBranchLbl->setText(QString::fromStdString(params.get("GitBranch"))); gitCommitLbl->setText(QString::fromStdString(params.get("GitCommit")).left(10)); osVersionLbl->setText(QString::fromStdString(Hardware::get_os_version()).trimmed()); } -QWidget *network_panel(QWidget *parent) { - return new Networking(parent); -} - void SettingsWindow::showEvent(QShowEvent *event) { panel_widget->setCurrentIndex(0); nav_btns->buttons()[0]->setChecked(true); @@ -310,7 +312,7 @@ SettingsWindow::SettingsWindow(QWidget *parent) : QFrame(parent) { )"); // close button - QPushButton *close_btn = new QPushButton("×"); + QPushButton *close_btn = new QPushButton(tr("×")); close_btn->setStyleSheet(R"( QPushButton { font-size: 140px; @@ -336,15 +338,15 @@ SettingsWindow::SettingsWindow(QWidget *parent) : QFrame(parent) { QObject::connect(device, &DevicePanel::showDriverView, this, &SettingsWindow::showDriverView); QList> panels = { - {"Device", device}, - {"Network", network_panel(this)}, - {"Toggles", new TogglesPanel(this)}, - {"Software", new SoftwarePanel(this)}, + {tr("Device"), device}, + {tr("Network"), new Networking(this)}, + {tr("Toggles"), new TogglesPanel(this)}, + {tr("Software"), new SoftwarePanel(this)}, }; #ifdef ENABLE_MAPS auto map_panel = new MapPanel(this); - panels.push_back({"Navigation", map_panel}); + panels.push_back({tr("Navigation"), map_panel}); QObject::connect(map_panel, &MapPanel::closeSettings, this, &SettingsWindow::closeSettings); #endif @@ -376,7 +378,7 @@ SettingsWindow::SettingsWindow(QWidget *parent) : QFrame(parent) { nav_btns->addButton(btn); sidebar_layout->addWidget(btn, 0, Qt::AlignRight); - const int lr_margin = name != "Network" ? 50 : 0; // Network panel handles its own margins + const int lr_margin = name != tr("Network") ? 50 : 0; // Network panel handles its own margins panel->setContentsMargins(lr_margin, 25, lr_margin, 25); ScrollView *panel_frame = new ScrollView(panel, this); diff --git a/selfdrive/ui/qt/onroad.cc b/selfdrive/ui/qt/onroad.cc index 91e2a76f0..ca39a89ae 100644 --- a/selfdrive/ui/qt/onroad.cc +++ b/selfdrive/ui/qt/onroad.cc @@ -91,7 +91,7 @@ void OnroadWindow::offroadTransition(bool offroad) { alerts->updateAlert({}, bg); // update stream type - bool wide_cam = Hardware::TICI() && Params().getBool("EnableWideCamera"); + bool wide_cam = Params().getBool("WideCameraOnly"); nvg->setStreamType(wide_cam ? VISION_STREAM_WIDE_ROAD : VISION_STREAM_ROAD); } @@ -146,18 +146,18 @@ void OnroadAlerts::paintEvent(QPaintEvent *event) { p.setPen(QColor(0xff, 0xff, 0xff)); p.setRenderHint(QPainter::TextAntialiasing); if (alert.size == cereal::ControlsState::AlertSize::SMALL) { - configFont(p, "Open Sans", 74, "SemiBold"); + configFont(p, "Inter", 74, "SemiBold"); p.drawText(r, Qt::AlignCenter, alert.text1); } else if (alert.size == cereal::ControlsState::AlertSize::MID) { - configFont(p, "Open Sans", 88, "Bold"); + configFont(p, "Inter", 88, "Bold"); p.drawText(QRect(0, c.y() - 125, width(), 150), Qt::AlignHCenter | Qt::AlignTop, alert.text1); - configFont(p, "Open Sans", 66, "Regular"); + configFont(p, "Inter", 66, "Regular"); p.drawText(QRect(0, c.y() + 21, width(), 90), Qt::AlignHCenter, alert.text2); } else if (alert.size == cereal::ControlsState::AlertSize::FULL) { bool l = alert.text1.length() > 15; - configFont(p, "Open Sans", l ? 132 : 177, "Bold"); + configFont(p, "Inter", l ? 132 : 177, "Bold"); p.drawText(QRect(0, r.y() + (l ? 240 : 270), width(), 600), Qt::AlignHCenter | Qt::TextWordWrap, alert.text1); - configFont(p, "Open Sans", 88, "Regular"); + configFont(p, "Inter", 88, "Regular"); p.drawText(QRect(0, r.height() - (l ? 361 : 420), width(), 300), Qt::AlignHCenter | Qt::TextWordWrap, alert.text2); } } @@ -172,20 +172,34 @@ NvgWindow::NvgWindow(VisionStreamType type, QWidget* parent) : fps_filter(UI_FRE void NvgWindow::updateState(const UIState &s) { const int SET_SPEED_NA = 255; const SubMaster &sm = *(s.sm); + + const bool cs_alive = sm.alive("controlsState"); + const bool nav_alive = sm.alive("navInstruction") && sm["navInstruction"].getValid(); + const auto cs = sm["controlsState"].getControlsState(); - float maxspeed = cs.getVCruise(); - bool cruise_set = maxspeed > 0 && (int)maxspeed != SET_SPEED_NA; + float set_speed = cs_alive ? cs.getVCruise() : SET_SPEED_NA; + bool cruise_set = set_speed > 0 && (int)set_speed != SET_SPEED_NA; if (cruise_set && !s.scene.is_metric) { - maxspeed *= KM_TO_MILE; + set_speed *= KM_TO_MILE; } - QString maxspeed_str = cruise_set ? QString::number(std::nearbyint(maxspeed)) : "N/A"; - float cur_speed = std::max(0.0, sm["carState"].getCarState().getVEgo() * (s.scene.is_metric ? MS_TO_KPH : MS_TO_MPH)); + + float cur_speed = cs_alive ? std::max(0.0, sm["carState"].getCarState().getVEgo()) : 0.0; + cur_speed *= s.scene.is_metric ? MS_TO_KPH : MS_TO_MPH; + + auto speed_limit_sign = sm["navInstruction"].getNavInstruction().getSpeedLimitSign(); + float speed_limit = nav_alive ? sm["navInstruction"].getNavInstruction().getSpeedLimit() : 0.0; + speed_limit *= (s.scene.is_metric ? MS_TO_KPH : MS_TO_MPH); + + setProperty("speedLimit", speed_limit); + setProperty("has_us_speed_limit", nav_alive && speed_limit_sign == cereal::NavInstruction::SpeedLimitSign::MUTCD); + setProperty("has_eu_speed_limit", nav_alive && speed_limit_sign == cereal::NavInstruction::SpeedLimitSign::VIENNA); setProperty("is_cruise_set", cruise_set); - setProperty("speed", QString::number(std::nearbyint(cur_speed))); - setProperty("maxSpeed", maxspeed_str); - setProperty("speedUnit", s.scene.is_metric ? "km/h" : "mph"); + setProperty("is_metric", s.scene.is_metric); + setProperty("speed", cur_speed); + setProperty("setSpeed", set_speed); + setProperty("speedUnit", s.scene.is_metric ? tr("km/h") : tr("mph")); setProperty("hideDM", cs.getAlertSize() != cereal::ControlsState::AlertSize::NONE); setProperty("status", s.status); @@ -194,6 +208,12 @@ void NvgWindow::updateState(const UIState &s) { setProperty("engageable", cs.getEngageable() || cs.getEnabled()); setProperty("dmActive", sm["driverMonitoringState"].getDriverMonitoringState().getIsActiveMode()); } + + if (s.scene.calibration_valid) { + CameraViewWidget::updateCalibration(s.scene.view_from_calib); + } else { + CameraViewWidget::updateCalibration(DEFAULT_CALIBRATION); + } } void NvgWindow::drawHud(QPainter &p) { @@ -205,27 +225,142 @@ void NvgWindow::drawHud(QPainter &p) { bg.setColorAt(1, QColor::fromRgbF(0, 0, 0, 0)); p.fillRect(0, 0, width(), header_h, bg); - // max speed - QRect rc(bdr_s * 2, bdr_s * 1.5, 184, 202); - p.setPen(QPen(QColor(0xff, 0xff, 0xff, 100), 10)); - p.setBrush(QColor(0, 0, 0, 100)); - p.drawRoundedRect(rc, 20, 20); - p.setPen(Qt::NoPen); + QString speedLimitStr = (speedLimit > 1) ? QString::number(std::nearbyint(speedLimit)) : "–"; + QString speedStr = QString::number(std::nearbyint(speed)); + QString setSpeedStr = is_cruise_set ? QString::number(std::nearbyint(setSpeed)) : "–"; - configFont(p, "Open Sans", 48, "Regular"); - drawText(p, rc.center().x(), 118, "MAX", is_cruise_set ? 200 : 100); + // Draw outer box + border to contain set speed and speed limit + int default_rect_width = 172; + int rect_width = default_rect_width; + if (is_metric || has_eu_speed_limit) rect_width = 200; + if (has_us_speed_limit && speedLimitStr.size() >= 3) rect_width = 223; + + int rect_height = 204; + if (has_us_speed_limit) rect_height = 402; + else if (has_eu_speed_limit) rect_height = 392; + + int top_radius = 32; + int bottom_radius = has_eu_speed_limit ? 100 : 32; + + QRect set_speed_rect(60 + default_rect_width / 2 - rect_width / 2, 45, rect_width, rect_height); + p.setPen(QPen(whiteColor(75), 6)); + p.setBrush(blackColor(166)); + drawRoundedRect(p, set_speed_rect, top_radius, top_radius, bottom_radius, bottom_radius); + + // Draw MAX if (is_cruise_set) { - configFont(p, "Open Sans", 88, "Bold"); - drawText(p, rc.center().x(), 212, maxSpeed, 255); + if (status == STATUS_DISENGAGED) { + p.setPen(whiteColor()); + } else if (status == STATUS_OVERRIDE) { + p.setPen(QColor(0x91, 0x9b, 0x95, 0xff)); + } else if (speedLimit > 0) { + p.setPen(interpColor( + setSpeed, + {speedLimit + 5, speedLimit + 15, speedLimit + 25}, + {QColor(0x80, 0xd8, 0xa6, 0xff), QColor(0xff, 0xe4, 0xbf, 0xff), QColor(0xff, 0xbf, 0xbf, 0xff)} + )); + } else { + p.setPen(QColor(0x80, 0xd8, 0xa6, 0xff)); + } } else { - configFont(p, "Open Sans", 80, "SemiBold"); - drawText(p, rc.center().x(), 212, maxSpeed, 100); + p.setPen(QColor(0xa6, 0xa6, 0xa6, 0xff)); + } + configFont(p, "Inter", 40, "SemiBold"); + QRect max_rect = getTextRect(p, Qt::AlignCenter, tr("MAX")); + max_rect.moveCenter({set_speed_rect.center().x(), 0}); + max_rect.moveTop(set_speed_rect.top() + 27); + p.drawText(max_rect, Qt::AlignCenter, tr("MAX")); + + // Draw set speed + if (is_cruise_set) { + if (speedLimit > 0 && status != STATUS_DISENGAGED && status != STATUS_OVERRIDE) { + p.setPen(interpColor( + setSpeed, + {speedLimit + 5, speedLimit + 15, speedLimit + 25}, + {whiteColor(), QColor(0xff, 0x95, 0x00, 0xff), QColor(0xff, 0x00, 0x00, 0xff)} + )); + } else { + p.setPen(whiteColor()); + } + } else { + p.setPen(QColor(0x72, 0x72, 0x72, 0xff)); + } + configFont(p, "Inter", 90, "Bold"); + QRect speed_rect = getTextRect(p, Qt::AlignCenter, setSpeedStr); + speed_rect.moveCenter({set_speed_rect.center().x(), 0}); + speed_rect.moveTop(set_speed_rect.top() + 77); + p.drawText(speed_rect, Qt::AlignCenter, setSpeedStr); + + + + // US/Canada (MUTCD style) sign + if (has_us_speed_limit) { + const int border_width = 6; + const int sign_width = rect_width - 24; + const int sign_height = 186; + + // White outer square + QRect sign_rect_outer(set_speed_rect.left() + 12, set_speed_rect.bottom() - 11 - sign_height, sign_width, sign_height); + p.setPen(Qt::NoPen); + p.setBrush(whiteColor()); + p.drawRoundedRect(sign_rect_outer, 24, 24); + + // Smaller white square with black border + QRect sign_rect(sign_rect_outer.left() + 1.5 * border_width, sign_rect_outer.top() + 1.5 * border_width, sign_width - 3 * border_width, sign_height - 3 * border_width); + p.setPen(QPen(blackColor(), border_width)); + p.setBrush(whiteColor()); + p.drawRoundedRect(sign_rect, 16, 16); + + // "SPEED" + configFont(p, "Inter", 28, "SemiBold"); + QRect text_speed_rect = getTextRect(p, Qt::AlignCenter, tr("SPEED")); + text_speed_rect.moveCenter({sign_rect.center().x(), 0}); + text_speed_rect.moveTop(sign_rect_outer.top() + 22); + p.drawText(text_speed_rect, Qt::AlignCenter, tr("SPEED")); + + // "LIMIT" + QRect text_limit_rect = getTextRect(p, Qt::AlignCenter, tr("LIMIT")); + text_limit_rect.moveCenter({sign_rect.center().x(), 0}); + text_limit_rect.moveTop(sign_rect_outer.top() + 51); + p.drawText(text_limit_rect, Qt::AlignCenter, tr("LIMIT")); + + // Speed limit value + configFont(p, "Inter", 70, "Bold"); + QRect speed_limit_rect = getTextRect(p, Qt::AlignCenter, speedLimitStr); + speed_limit_rect.moveCenter({sign_rect.center().x(), 0}); + speed_limit_rect.moveTop(sign_rect_outer.top() + 85); + p.drawText(speed_limit_rect, Qt::AlignCenter, speedLimitStr); + } + + // EU (Vienna style) sign + if (has_eu_speed_limit) { + int outer_radius = 176 / 2; + int inner_radius_1 = outer_radius - 6; // White outer border + int inner_radius_2 = inner_radius_1 - 20; // Red circle + + // Draw white circle with red border + QPoint center(set_speed_rect.center().x() + 1, set_speed_rect.top() + 204 + outer_radius); + p.setPen(Qt::NoPen); + p.setBrush(whiteColor()); + p.drawEllipse(center, outer_radius, outer_radius); + p.setBrush(QColor(255, 0, 0, 255)); + p.drawEllipse(center, inner_radius_1, inner_radius_1); + p.setBrush(whiteColor()); + p.drawEllipse(center, inner_radius_2, inner_radius_2); + + // Speed limit value + int font_size = (speedLimitStr.size() >= 3) ? 60 : 70; + configFont(p, "Inter", font_size, "Bold"); + QRect speed_limit_rect = getTextRect(p, Qt::AlignCenter, speedLimitStr); + speed_limit_rect.moveCenter(center); + p.setPen(blackColor()); + p.drawText(speed_limit_rect, Qt::AlignCenter, speedLimitStr); } // current speed - configFont(p, "Open Sans", 176, "Bold"); - drawText(p, rect().center().x(), 210, speed); - configFont(p, "Open Sans", 66, "Regular"); + configFont(p, "Inter", 176, "Bold"); + drawText(p, rect().center().x(), 210, speedStr); + configFont(p, "Inter", 66, "Regular"); drawText(p, rect().center().x(), 290, speedUnit, 200); // engage-ability icon @@ -237,15 +372,13 @@ void NvgWindow::drawHud(QPainter &p) { // dm icon if (!hideDM) { drawIcon(p, radius / 2 + (bdr_s * 2), rect().bottom() - footer_h / 2, - dm_img, QColor(0, 0, 0, 70), dmActive ? 1.0 : 0.2); + dm_img, blackColor(70), dmActive ? 1.0 : 0.2); } p.restore(); } void NvgWindow::drawText(QPainter &p, int x, int y, const QString &text, int alpha) { - QFontMetrics fm(p.font()); - QRect init_rect = fm.boundingRect(text); - QRect real_rect = fm.boundingRect(init_rect, 0, text); + QRect real_rect = getTextRect(p, 0, text); real_rect.moveCenter({x, y - real_rect.height() / 2}); p.setPen(QColor(0xff, 0xff, 0xff, alpha)); @@ -272,23 +405,20 @@ void NvgWindow::initializeGL() { setBackgroundColor(bg_colors[STATUS_DISENGAGED]); } -void NvgWindow::updateFrameMat(int w, int h) { - CameraViewWidget::updateFrameMat(w, h); - +void NvgWindow::updateFrameMat() { + CameraViewWidget::updateFrameMat(); UIState *s = uiState(); + int w = width(), h = height(); + s->fb_w = w; s->fb_h = h; - auto intrinsic_matrix = s->wide_camera ? ecam_intrinsic_matrix : fcam_intrinsic_matrix; - float zoom = ZOOM / intrinsic_matrix.v[0]; - if (s->wide_camera) { - zoom *= 0.5; - } + // Apply transformation such that video pixel coordinates match video // 1) Put (0, 0) in the middle of the video // 2) Apply same scaling as video // 3) Put (0, 0) in top left corner of video s->car_space_transform.reset(); - s->car_space_transform.translate(w / 2, h / 2 + y_offset) + s->car_space_transform.translate(w / 2 - x_offset, h / 2 - y_offset) .scale(zoom, zoom) .translate(-intrinsic_matrix.v[2], -intrinsic_matrix.v[5]); } @@ -300,37 +430,32 @@ void NvgWindow::drawLaneLines(QPainter &painter, const UIState *s) { // lanelines for (int i = 0; i < std::size(scene.lane_line_vertices); ++i) { painter.setBrush(QColor::fromRgbF(1.0, 1.0, 1.0, std::clamp(scene.lane_line_probs[i], 0.0, 0.7))); - painter.drawPolygon(scene.lane_line_vertices[i].v, scene.lane_line_vertices[i].cnt); + painter.drawPolygon(scene.lane_line_vertices[i]); } // road edges for (int i = 0; i < std::size(scene.road_edge_vertices); ++i) { painter.setBrush(QColor::fromRgbF(1.0, 0, 0, std::clamp(1.0 - scene.road_edge_stds[i], 0.0, 1.0))); - painter.drawPolygon(scene.road_edge_vertices[i].v, scene.road_edge_vertices[i].cnt); + painter.drawPolygon(scene.road_edge_vertices[i]); } // paint path QLinearGradient bg(0, height(), 0, height() / 4); - if (scene.end_to_end) { - const auto &orientation = (*s->sm)["modelV2"].getModelV2().getOrientation(); - float orientation_future = 0; - if (orientation.getZ().size() > 16) { - orientation_future = std::abs(orientation.getZ()[16]); // 2.5 seconds - } - // straight: 112, in turns: 70 - float curve_hue = fmax(70, 112 - (orientation_future * 420)); - // FIXME: painter.drawPolygon can be slow if hue is not rounded - curve_hue = int(curve_hue * 100 + 0.5) / 100; - - bg.setColorAt(0.0, QColor::fromHslF(148 / 360., 0.94, 0.51, 0.4)); - bg.setColorAt(0.75 / 1.5, QColor::fromHslF(curve_hue / 360., 1.0, 0.68, 0.35)); - bg.setColorAt(1.0, QColor::fromHslF(curve_hue / 360., 1.0, 0.68, 0.0)); - } else { - bg.setColorAt(0, whiteColor()); - bg.setColorAt(1, whiteColor(0)); + const auto &orientation = (*s->sm)["modelV2"].getModelV2().getOrientation(); + float orientation_future = 0; + if (orientation.getZ().size() > 16) { + orientation_future = std::abs(orientation.getZ()[16]); // 2.5 seconds } + // straight: 112, in turns: 70 + float curve_hue = fmax(70, 112 - (orientation_future * 420)); + // FIXME: painter.drawPolygon can be slow if hue is not rounded + curve_hue = int(curve_hue * 100 + 0.5) / 100; + + bg.setColorAt(0.0, QColor::fromHslF(148 / 360., 0.94, 0.51, 0.4)); + bg.setColorAt(0.75 / 1.5, QColor::fromHslF(curve_hue / 360., 1.0, 0.68, 0.35)); + bg.setColorAt(1.0, QColor::fromHslF(curve_hue / 360., 1.0, 0.68, 0.0)); painter.setBrush(bg); - painter.drawPolygon(scene.track_vertices.v, scene.track_vertices.cnt); + painter.drawPolygon(scene.track_vertices); painter.restore(); } @@ -372,19 +497,21 @@ void NvgWindow::drawLead(QPainter &painter, const cereal::ModelDataV2::LeadDataV } void NvgWindow::paintGL() { + UIState *s = uiState(); + const cereal::ModelDataV2::Reader &model = (*s->sm)["modelV2"].getModelV2(); + CameraViewWidget::setFrameId(model.getFrameId()); CameraViewWidget::paintGL(); QPainter painter(this); painter.setRenderHint(QPainter::Antialiasing); painter.setPen(Qt::NoPen); - UIState *s = uiState(); if (s->worldObjectsVisible()) { drawLaneLines(painter, s); if (s->scene.longitudinal_control) { - auto leads = (*s->sm)["modelV2"].getModelV2().getLeadsV3(); + const auto leads = model.getLeadsV3(); if (leads[0].getProb() > .5) { drawLead(painter, leads[0], s->scene.lead_vertices[0]); } diff --git a/selfdrive/ui/qt/onroad.h b/selfdrive/ui/qt/onroad.h index 6ca2b3c73..dc1e69da2 100644 --- a/selfdrive/ui/qt/onroad.h +++ b/selfdrive/ui/qt/onroad.h @@ -27,10 +27,15 @@ private: // container window for the NVG UI class NvgWindow : public CameraViewWidget { Q_OBJECT - Q_PROPERTY(QString speed MEMBER speed); + Q_PROPERTY(float speed MEMBER speed); Q_PROPERTY(QString speedUnit MEMBER speedUnit); - Q_PROPERTY(QString maxSpeed MEMBER maxSpeed); + Q_PROPERTY(float setSpeed MEMBER setSpeed); + Q_PROPERTY(float speedLimit MEMBER speedLimit); Q_PROPERTY(bool is_cruise_set MEMBER is_cruise_set); + Q_PROPERTY(bool has_eu_speed_limit MEMBER has_eu_speed_limit); + Q_PROPERTY(bool has_us_speed_limit MEMBER has_us_speed_limit); + Q_PROPERTY(bool is_metric MEMBER is_metric); + Q_PROPERTY(bool engageable MEMBER engageable); Q_PROPERTY(bool dmActive MEMBER dmActive); Q_PROPERTY(bool hideDM MEMBER hideDM); @@ -48,25 +53,30 @@ private: QPixmap dm_img; const int radius = 192; const int img_size = (radius / 2) * 1.5; - QString speed; + float speed; QString speedUnit; - QString maxSpeed; + float setSpeed; + float speedLimit; bool is_cruise_set = false; + bool is_metric = false; bool engageable = false; bool dmActive = false; bool hideDM = false; + bool has_us_speed_limit = false; + bool has_eu_speed_limit = false; int status = STATUS_DISENGAGED; protected: void paintGL() override; void initializeGL() override; void showEvent(QShowEvent *event) override; - void updateFrameMat(int w, int h) override; + void updateFrameMat() override; void drawLaneLines(QPainter &painter, const UIState *s); void drawLead(QPainter &painter, const cereal::ModelDataV2::LeadDataV3::Reader &lead_data, const QPointF &vd); void drawHud(QPainter &p); inline QColor redColor(int alpha = 255) { return QColor(201, 34, 49, alpha); } inline QColor whiteColor(int alpha = 255) { return QColor(255, 255, 255, alpha); } + inline QColor blackColor(int alpha = 255) { return QColor(0, 0, 0, alpha); } double prev_draw_t = 0; FirstOrderFilter fps_filter; diff --git a/selfdrive/ui/qt/qt_window.h b/selfdrive/ui/qt/qt_window.h index 2c9a24e55..02d127e7f 100644 --- a/selfdrive/ui/qt/qt_window.h +++ b/selfdrive/ui/qt/qt_window.h @@ -12,7 +12,7 @@ #include #endif -#include "selfdrive/hardware/hw.h" +#include "system/hardware/hw.h" const QString ASSET_PATH = ":/"; diff --git a/selfdrive/ui/qt/sidebar.cc b/selfdrive/ui/qt/sidebar.cc index cdce7b828..accab67bf 100644 --- a/selfdrive/ui/qt/sidebar.cc +++ b/selfdrive/ui/qt/sidebar.cc @@ -4,13 +4,13 @@ #include "selfdrive/ui/qt/util.h" -void Sidebar::drawMetric(QPainter &p, const QString &label, QColor c, int y) { - const QRect rect = {30, y, 240, label.contains("\n") ? 124 : 100}; +void Sidebar::drawMetric(QPainter &p, const QPair &label, QColor c, int y) { + const QRect rect = {30, y, 240, 126}; p.setPen(Qt::NoPen); p.setBrush(QBrush(c)); - p.setClipRect(rect.x() + 6, rect.y(), 18, rect.height(), Qt::ClipOperation::ReplaceClip); - p.drawRoundedRect(QRect(rect.x() + 6, rect.y() + 6, 100, rect.height() - 12), 10, 10); + p.setClipRect(rect.x() + 4, rect.y(), 18, rect.height(), Qt::ClipOperation::ReplaceClip); + p.drawRoundedRect(QRect(rect.x() + 4, rect.y() + 4, 100, 118), 18, 18); p.setClipping(false); QPen pen = QPen(QColor(0xff, 0xff, 0xff, 0x55)); @@ -20,9 +20,16 @@ void Sidebar::drawMetric(QPainter &p, const QString &label, QColor c, int y) { p.drawRoundedRect(rect, 20, 20); p.setPen(QColor(0xff, 0xff, 0xff)); - configFont(p, "Open Sans", 35, "Bold"); - const QRect r = QRect(rect.x() + 30, rect.y(), rect.width() - 40, rect.height()); - p.drawText(r, Qt::AlignCenter, label); + configFont(p, "Inter", 35, "SemiBold"); + + QRect label_rect = getTextRect(p, Qt::AlignCenter, label.first); + label_rect.setWidth(218); + label_rect.moveLeft(rect.left() + 22); + label_rect.moveTop(rect.top() + 19); + p.drawText(label_rect, Qt::AlignCenter, label.first); + + label_rect.moveTop(rect.top() + 65); + p.drawText(label_rect, Qt::AlignCenter, label.second); } Sidebar::Sidebar(QWidget *parent) : QFrame(parent) { @@ -57,26 +64,26 @@ void Sidebar::updateState(const UIState &s) { ItemStatus connectStatus; auto last_ping = deviceState.getLastAthenaPingTime(); if (last_ping == 0) { - connectStatus = params.getBool("PrimeRedirected") ? ItemStatus{"NO\nPRIME", danger_color} : ItemStatus{"CONNECT\nOFFLINE", warning_color}; + connectStatus = ItemStatus{{tr("CONNECT"), tr("OFFLINE")}, warning_color}; } else { - connectStatus = nanos_since_boot() - last_ping < 80e9 ? ItemStatus{"CONNECT\nONLINE", good_color} : ItemStatus{"CONNECT\nERROR", danger_color}; + connectStatus = nanos_since_boot() - last_ping < 80e9 ? ItemStatus{{tr("CONNECT"), tr("ONLINE")}, good_color} : ItemStatus{{tr("CONNECT"), tr("ERROR")}, danger_color}; } setProperty("connectStatus", QVariant::fromValue(connectStatus)); - ItemStatus tempStatus = {"TEMP\nHIGH", danger_color}; + ItemStatus tempStatus = {{tr("TEMP"), tr("HIGH")}, danger_color}; auto ts = deviceState.getThermalStatus(); if (ts == cereal::DeviceState::ThermalStatus::GREEN) { - tempStatus = {"TEMP\nGOOD", good_color}; + tempStatus = {{tr("TEMP"), tr("GOOD")}, good_color}; } else if (ts == cereal::DeviceState::ThermalStatus::YELLOW) { - tempStatus = {"TEMP\nOK", warning_color}; + tempStatus = {{tr("TEMP"), tr("OK")}, warning_color}; } setProperty("tempStatus", QVariant::fromValue(tempStatus)); - ItemStatus pandaStatus = {"VEHICLE\nONLINE", good_color}; + ItemStatus pandaStatus = {{tr("VEHICLE"), tr("ONLINE")}, good_color}; if (s.scene.pandaType == cereal::PandaState::PandaType::UNKNOWN) { - pandaStatus = {"NO\nPANDA", danger_color}; + pandaStatus = {{tr("NO"), tr("PANDA")}, danger_color}; } else if (s.scene.started && !sm["liveLocationKalman"].getLiveLocationKalman().getGpsOK()) { - pandaStatus = {"GPS\nSEARCH", warning_color}; + pandaStatus = {{tr("GPS"), tr("SEARCH")}, warning_color}; } setProperty("pandaStatus", QVariant::fromValue(pandaStatus)); } @@ -103,7 +110,7 @@ void Sidebar::paintEvent(QPaintEvent *event) { x += 37; } - configFont(p, "Open Sans", 35, "Regular"); + configFont(p, "Inter", 35, "Regular"); p.setPen(QColor(0xff, 0xff, 0xff)); const QRect r = QRect(50, 247, 100, 50); p.drawText(r, Qt::AlignCenter, net_type); diff --git a/selfdrive/ui/qt/sidebar.h b/selfdrive/ui/qt/sidebar.h index ab3e990e6..4c6d8f47e 100644 --- a/selfdrive/ui/qt/sidebar.h +++ b/selfdrive/ui/qt/sidebar.h @@ -6,7 +6,7 @@ #include "common/params.h" #include "selfdrive/ui/ui.h" -typedef QPair ItemStatus; +typedef QPair, QColor> ItemStatus; Q_DECLARE_METATYPE(ItemStatus); class Sidebar : public QFrame { @@ -30,17 +30,17 @@ public slots: protected: void paintEvent(QPaintEvent *event) override; void mouseReleaseEvent(QMouseEvent *event) override; - void drawMetric(QPainter &p, const QString &label, QColor c, int y); + void drawMetric(QPainter &p, const QPair &label, QColor c, int y); QPixmap home_img, settings_img; const QMap network_type = { - {cereal::DeviceState::NetworkType::NONE, "--"}, - {cereal::DeviceState::NetworkType::WIFI, "Wi-Fi"}, - {cereal::DeviceState::NetworkType::ETHERNET, "ETH"}, - {cereal::DeviceState::NetworkType::CELL2_G, "2G"}, - {cereal::DeviceState::NetworkType::CELL3_G, "3G"}, - {cereal::DeviceState::NetworkType::CELL4_G, "LTE"}, - {cereal::DeviceState::NetworkType::CELL5_G, "5G"} + {cereal::DeviceState::NetworkType::NONE, tr("--")}, + {cereal::DeviceState::NetworkType::WIFI, tr("Wi-Fi")}, + {cereal::DeviceState::NetworkType::ETHERNET, tr("ETH")}, + {cereal::DeviceState::NetworkType::CELL2_G, tr("2G")}, + {cereal::DeviceState::NetworkType::CELL3_G, tr("3G")}, + {cereal::DeviceState::NetworkType::CELL4_G, tr("LTE")}, + {cereal::DeviceState::NetworkType::CELL5_G, tr("5G")} }; const QRect settings_btn = QRect(50, 35, 200, 117); diff --git a/selfdrive/ui/qt/spinner.cc b/selfdrive/ui/qt/spinner.cc index e0b263227..8f13576fb 100644 --- a/selfdrive/ui/qt/spinner.cc +++ b/selfdrive/ui/qt/spinner.cc @@ -10,7 +10,7 @@ #include #include -#include "selfdrive/hardware/hw.h" +#include "system/hardware/hw.h" #include "selfdrive/ui/qt/qt_window.h" #include "selfdrive/ui/qt/util.h" diff --git a/selfdrive/ui/qt/text.cc b/selfdrive/ui/qt/text.cc index 04fda3548..21ec5eedc 100644 --- a/selfdrive/ui/qt/text.cc +++ b/selfdrive/ui/qt/text.cc @@ -5,7 +5,7 @@ #include #include -#include "selfdrive/hardware/hw.h" +#include "system/hardware/hw.h" #include "selfdrive/ui/qt/util.h" #include "selfdrive/ui/qt/qt_window.h" #include "selfdrive/ui/qt/widgets/scrollview.h" @@ -33,12 +33,12 @@ int main(int argc, char *argv[]) { QPushButton *btn = new QPushButton(); #ifdef __aarch64__ - btn->setText("Reboot"); + btn->setText(QObject::tr("Reboot")); QObject::connect(btn, &QPushButton::clicked, [=]() { Hardware::reboot(); }); #else - btn->setText("Exit"); + btn->setText(QObject::tr("Exit")); QObject::connect(btn, &QPushButton::clicked, &a, &QApplication::quit); #endif main_layout->addWidget(btn, 0, 0, Qt::AlignRight | Qt::AlignBottom); diff --git a/selfdrive/ui/qt/util.cc b/selfdrive/ui/qt/util.cc index 7ce7a267d..a7d5438ae 100644 --- a/selfdrive/ui/qt/util.cc +++ b/selfdrive/ui/qt/util.cc @@ -1,12 +1,16 @@ #include "selfdrive/ui/qt/util.h" #include +#include +#include +#include #include #include +#include #include "common/params.h" #include "common/swaglog.h" -#include "selfdrive/hardware/hw.h" +#include "system/hardware/hw.h" QString getVersion() { static QString version = QString::fromStdString(Params().get("Version")); @@ -14,7 +18,7 @@ QString getVersion() { } QString getBrand() { - return Params().getBool("Passive") ? "dashcam" : "openpilot"; + return Params().getBool("Passive") ? QObject::tr("dashcam") : QObject::tr("openpilot"); } QString getBrandVersion() { @@ -35,6 +39,19 @@ std::optional getDongleId() { } } +QMap getSupportedLanguages() { + QFile f("translations/languages.json"); + f.open(QIODevice::ReadOnly | QIODevice::Text); + QString val = f.readAll(); + + QJsonObject obj = QJsonDocument::fromJson(val.toUtf8()).object(); + QMap map; + for (auto key : obj.keys()) { + map[key] = obj[key].toString(); + } + return map; +} + void configFont(QPainter &p, const QString &family, int size, const QString &style) { QFont f(family); f.setPixelSize(size); @@ -62,13 +79,13 @@ QString timeAgo(const QDateTime &date) { s = "now"; } else if (diff < 60 * 60) { int minutes = diff / 60; - s = QString("%1 minute%2 ago").arg(minutes).arg(minutes > 1 ? "s" : ""); + s = QObject::tr("%1 minute%2 ago").arg(minutes).arg(minutes > 1 ? "s" : ""); } else if (diff < 60 * 60 * 24) { int hours = diff / (60 * 60); - s = QString("%1 hour%2 ago").arg(hours).arg(hours > 1 ? "s" : ""); + s = QObject::tr("%1 hour%2 ago").arg(hours).arg(hours > 1 ? "s" : ""); } else if (diff < 3600 * 24 * 7) { int days = diff / (60 * 60 * 24); - s = QString("%1 day%2 ago").arg(days).arg(days > 1 ? "s" : ""); + s = QObject::tr("%1 day%2 ago").arg(days).arg(days > 1 ? "s" : ""); } else { s = date.date().toString(); } @@ -135,3 +152,64 @@ QPixmap loadPixmap(const QString &fileName, const QSize &size, Qt::AspectRatioMo return QPixmap(fileName).scaled(size, aspectRatioMode, Qt::SmoothTransformation); } } + +QRect getTextRect(QPainter &p, int flags, QString text) { + QFontMetrics fm(p.font()); + QRect init_rect = fm.boundingRect(text); + return fm.boundingRect(init_rect, flags, text); +} + +void drawRoundedRect(QPainter &painter, const QRectF &rect, qreal xRadiusTop, qreal yRadiusTop, qreal xRadiusBottom, qreal yRadiusBottom){ + qreal w_2 = rect.width() / 2; + qreal h_2 = rect.height() / 2; + + xRadiusTop = 100 * qMin(xRadiusTop, w_2) / w_2; + yRadiusTop = 100 * qMin(yRadiusTop, h_2) / h_2; + + xRadiusBottom = 100 * qMin(xRadiusBottom, w_2) / w_2; + yRadiusBottom = 100 * qMin(yRadiusBottom, h_2) / h_2; + + qreal x = rect.x(); + qreal y = rect.y(); + qreal w = rect.width(); + qreal h = rect.height(); + + qreal rxx2Top = w*xRadiusTop/100; + qreal ryy2Top = h*yRadiusTop/100; + + qreal rxx2Bottom = w*xRadiusBottom/100; + qreal ryy2Bottom = h*yRadiusBottom/100; + + QPainterPath path; + path.arcMoveTo(x, y, rxx2Top, ryy2Top, 180); + path.arcTo(x, y, rxx2Top, ryy2Top, 180, -90); + path.arcTo(x+w-rxx2Top, y, rxx2Top, ryy2Top, 90, -90); + path.arcTo(x+w-rxx2Bottom, y+h-ryy2Bottom, rxx2Bottom, ryy2Bottom, 0, -90); + path.arcTo(x, y+h-ryy2Bottom, rxx2Bottom, ryy2Bottom, 270, -90); + path.closeSubpath(); + + painter.drawPath(path); +} + +QColor interpColor(float xv, std::vector xp, std::vector fp) { + assert(xp.size() == fp.size()); + + int N = xp.size(); + int hi = 0; + + while (hi < N and xv > xp[hi]) hi++; + int low = hi - 1; + + if (hi == N && xv > xp[low]) { + return fp[fp.size() - 1]; + } else if (hi == 0){ + return fp[0]; + } else { + return QColor( + (xv - xp[low]) * (fp[hi].red() - fp[low].red()) / (xp[hi] - xp[low]) + fp[low].red(), + (xv - xp[low]) * (fp[hi].green() - fp[low].green()) / (xp[hi] - xp[low]) + fp[low].green(), + (xv - xp[low]) * (fp[hi].blue() - fp[low].blue()) / (xp[hi] - xp[low]) + fp[low].blue(), + (xv - xp[low]) * (fp[hi].alpha() - fp[low].alpha()) / (xp[hi] - xp[low]) + fp[low].alpha() + ); + } +} diff --git a/selfdrive/ui/qt/util.h b/selfdrive/ui/qt/util.h index 813777710..f0e57526c 100644 --- a/selfdrive/ui/qt/util.h +++ b/selfdrive/ui/qt/util.h @@ -14,6 +14,7 @@ QString getBrand(); QString getBrandVersion(); QString getUserAgent(); std::optional getDongleId(); +QMap getSupportedLanguages(); void configFont(QPainter &p, const QString &family, int size, const QString &style); void clearLayout(QLayout* layout); void setQtSurfaceFormat(); @@ -22,3 +23,7 @@ void swagLogMessageHandler(QtMsgType type, const QMessageLogContext &context, co void initApp(int argc, char *argv[]); QWidget* topWidget (QWidget* widget); QPixmap loadPixmap(const QString &fileName, const QSize &size = {}, Qt::AspectRatioMode aspectRatioMode = Qt::KeepAspectRatio); + +QRect getTextRect(QPainter &p, int flags, QString text); +void drawRoundedRect(QPainter &painter, const QRectF &rect, qreal xRadiusTop, qreal yRadiusTop, qreal xRadiusBottom, qreal yRadiusBottom); +QColor interpColor(float xv, std::vector xp, std::vector fp); diff --git a/selfdrive/ui/qt/widgets/cameraview.cc b/selfdrive/ui/qt/widgets/cameraview.cc index 646d286ee..63d15660a 100644 --- a/selfdrive/ui/qt/widgets/cameraview.cc +++ b/selfdrive/ui/qt/widgets/cameraview.cc @@ -6,6 +6,8 @@ #include #endif +#include + #include #include @@ -26,6 +28,18 @@ const char frame_vertex_shader[] = " vTexCoord = aTexCoord;\n" "}\n"; +#ifdef QCOM2 +const char frame_fragment_shader[] = + "#version 300 es\n" + "#extension GL_OES_EGL_image_external_essl3 : enable\n" + "precision mediump float;\n" + "uniform samplerExternalOES uTexture;\n" + "in vec2 vTexCoord;\n" + "out vec4 colorOut;\n" + "void main() {\n" + " colorOut = texture(uTexture, vTexCoord);\n" + "}\n"; +#else const char frame_fragment_shader[] = #ifdef __APPLE__ "#version 330 core\n" @@ -34,48 +48,29 @@ const char frame_fragment_shader[] = "precision mediump float;\n" #endif "uniform sampler2D uTextureY;\n" - "uniform sampler2D uTextureU;\n" - "uniform sampler2D uTextureV;\n" + "uniform sampler2D uTextureUV;\n" "in vec2 vTexCoord;\n" "out vec4 colorOut;\n" "void main() {\n" " float y = texture(uTextureY, vTexCoord).r;\n" - " float u = texture(uTextureU, vTexCoord).r - 0.5;\n" - " float v = texture(uTextureV, vTexCoord).r - 0.5;\n" - " float r = y + 1.402 * v;\n" - " float g = y - 0.344 * u - 0.714 * v;\n" - " float b = y + 1.772 * u;\n" + " vec2 uv = texture(uTextureUV, vTexCoord).rg - 0.5;\n" + " float r = y + 1.402 * uv.y;\n" + " float g = y - 0.344 * uv.x - 0.714 * uv.y;\n" + " float b = y + 1.772 * uv.x;\n" " colorOut = vec4(r, g, b, 1.0);\n" "}\n"; - -const mat4 device_transform = {{ - 1.0, 0.0, 0.0, 0.0, - 0.0, 1.0, 0.0, 0.0, - 0.0, 0.0, 1.0, 0.0, - 0.0, 0.0, 0.0, 1.0, -}}; +#endif mat4 get_driver_view_transform(int screen_width, int screen_height, int stream_width, int stream_height) { - const float driver_view_ratio = 1.333; - mat4 transform; - if (stream_width == TICI_CAM_WIDTH) { - const float yscale = stream_height * driver_view_ratio / tici_dm_crop::width; - const float xscale = yscale*screen_height/screen_width*stream_width/stream_height; - transform = (mat4){{ - xscale, 0.0, 0.0, xscale*tici_dm_crop::x_offset/stream_width*2, - 0.0, yscale, 0.0, yscale*tici_dm_crop::y_offset/stream_height*2, - 0.0, 0.0, 1.0, 0.0, - 0.0, 0.0, 0.0, 1.0, - }}; - } else { - // frame from 4/3 to 16/9 display - transform = (mat4){{ - driver_view_ratio * screen_height / screen_width, 0.0, 0.0, 0.0, - 0.0, 1.0, 0.0, 0.0, - 0.0, 0.0, 1.0, 0.0, - 0.0, 0.0, 0.0, 1.0, - }}; - } + const float driver_view_ratio = 2.0; + const float yscale = stream_height * driver_view_ratio / stream_width; + const float xscale = yscale*screen_height/screen_width*stream_width/stream_height; + mat4 transform = (mat4){{ + xscale, 0.0, 0.0, 0.0, + 0.0, yscale, 0.0, 0.0, + 0.0, 0.0, 1.0, 0.0, + 0.0, 0.0, 0.0, 1.0, + }}; return transform; } @@ -111,7 +106,7 @@ CameraViewWidget::~CameraViewWidget() { glDeleteVertexArrays(1, &frame_vao); glDeleteBuffers(1, &frame_vbo); glDeleteBuffers(1, &frame_ibo); - glDeleteBuffers(3, textures); + glDeleteBuffers(2, textures); } doneCurrent(); } @@ -155,15 +150,19 @@ void CameraViewWidget::initializeGL() { glBindBuffer(GL_ARRAY_BUFFER, 0); glBindVertexArray(0); - glGenTextures(3, textures); glUseProgram(program->programId()); + +#ifdef QCOM2 + glUniform1i(program->uniformLocation("uTexture"), 0); +#else + glGenTextures(2, textures); glUniform1i(program->uniformLocation("uTextureY"), 0); - glUniform1i(program->uniformLocation("uTextureU"), 1); - glUniform1i(program->uniformLocation("uTextureV"), 2); + glUniform1i(program->uniformLocation("uTextureUV"), 1); +#endif } void CameraViewWidget::showEvent(QShowEvent *event) { - latest_frame = nullptr; + frames.clear(); if (!vipc_thread) { vipc_thread = new QThread(); connect(vipc_thread, &QThread::started, [=]() { vipcThread(); }); @@ -181,58 +180,102 @@ void CameraViewWidget::hideEvent(QHideEvent *event) { } } -void CameraViewWidget::updateFrameMat(int w, int h) { +void CameraViewWidget::updateFrameMat() { + int w = width(), h = height(); + if (zoomed_view) { if (stream_type == VISION_STREAM_DRIVER) { - frame_mat = matmul(device_transform, get_driver_view_transform(w, h, stream_width, stream_height)); + frame_mat = get_driver_view_transform(w, h, stream_width, stream_height); } else { - auto intrinsic_matrix = stream_type == VISION_STREAM_WIDE_ROAD ? ecam_intrinsic_matrix : fcam_intrinsic_matrix; - float zoom = ZOOM / intrinsic_matrix.v[0]; - if (stream_type == VISION_STREAM_WIDE_ROAD) { - zoom *= 0.5; - } + intrinsic_matrix = (stream_type == VISION_STREAM_WIDE_ROAD) ? ecam_intrinsic_matrix : fcam_intrinsic_matrix; + zoom = (stream_type == VISION_STREAM_WIDE_ROAD) ? 2.5 : 1.1; + + // Project point at "infinity" to compute x and y offsets + // to ensure this ends up in the middle of the screen + // TODO: use proper perspective transform? + const vec3 inf = {{1000., 0., 0.}}; + const vec3 Ep = matvecmul3(calibration, inf); + const vec3 Kep = matvecmul3(intrinsic_matrix, Ep); + + float x_offset_ = (Kep.v[0] / Kep.v[2] - intrinsic_matrix.v[2]) * zoom; + float y_offset_ = (Kep.v[1] / Kep.v[2] - intrinsic_matrix.v[5]) * zoom; + + float max_x_offset = intrinsic_matrix.v[2] * zoom - w / 2 - 5; + float max_y_offset = intrinsic_matrix.v[5] * zoom - h / 2 - 5; + + x_offset = std::clamp(x_offset_, -max_x_offset, max_x_offset); + y_offset = std::clamp(y_offset_, -max_y_offset, max_y_offset); + float zx = zoom * 2 * intrinsic_matrix.v[2] / width(); float zy = zoom * 2 * intrinsic_matrix.v[5] / height(); - const mat4 frame_transform = {{ - zx, 0.0, 0.0, 0.0, - 0.0, zy, 0.0, -y_offset / height() * 2, + zx, 0.0, 0.0, -x_offset / width() * 2, + 0.0, zy, 0.0, y_offset / height() * 2, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, }}; - frame_mat = matmul(device_transform, frame_transform); + frame_mat = frame_transform; } } else if (stream_width > 0 && stream_height > 0) { // fit frame to widget size float widget_aspect_ratio = (float)width() / height(); float frame_aspect_ratio = (float)stream_width / stream_height; - frame_mat = matmul(device_transform, get_fit_view_transform(widget_aspect_ratio, frame_aspect_ratio)); + frame_mat = get_fit_view_transform(widget_aspect_ratio, frame_aspect_ratio); } } +void CameraViewWidget::updateCalibration(const mat3 &calib) { + calibration = calib; + updateFrameMat(); +} + void CameraViewWidget::paintGL() { glClearColor(bg.redF(), bg.greenF(), bg.blueF(), bg.alphaF()); glClear(GL_STENCIL_BUFFER_BIT | GL_COLOR_BUFFER_BIT); - if (latest_frame == nullptr) return; + if (frames.empty()) return; + + int frame_idx = frames.size() - 1; + + // Always draw latest frame until sync logic is more stable + // for (frame_idx = 0; frame_idx < frames.size() - 1; frame_idx++) { + // if (frames[frame_idx].first == draw_frame_id) break; + // } + + // Log duplicate/dropped frames + if (frames[frame_idx].first == prev_frame_id) { + qDebug() << "Drawing same frame twice" << frames[frame_idx].first; + } else if (frames[frame_idx].first != prev_frame_id + 1) { + qDebug() << "Skipped frame" << frames[frame_idx].first; + } + prev_frame_id = frames[frame_idx].first; - glPixelStorei(GL_UNPACK_ALIGNMENT, 1); glViewport(0, 0, width(), height()); glBindVertexArray(frame_vao); - glUseProgram(program->programId()); - uint8_t *address[3] = {latest_frame->y, latest_frame->u, latest_frame->v}; - for (int i = 0; i < 3; ++i) { - glActiveTexture(GL_TEXTURE0 + i); - glBindTexture(GL_TEXTURE_2D, textures[i]); - int width = i == 0 ? stream_width : stream_width / 2; - int height = i == 0 ? stream_height : stream_height / 2; - glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RED, GL_UNSIGNED_BYTE, address[i]); - assert(glGetError() == GL_NO_ERROR); - } + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + + VisionBuf *frame = frames[frame_idx].second; + +#ifdef QCOM2 + glActiveTexture(GL_TEXTURE0); + glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, egl_images[frame->idx]); + assert(glGetError() == GL_NO_ERROR); +#else + glPixelStorei(GL_UNPACK_ROW_LENGTH, stream_stride); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, textures[0]); + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, stream_width, stream_height, GL_RED, GL_UNSIGNED_BYTE, frame->y); + assert(glGetError() == GL_NO_ERROR); + + glPixelStorei(GL_UNPACK_ROW_LENGTH, stream_stride/2); + glActiveTexture(GL_TEXTURE0 + 1); + glBindTexture(GL_TEXTURE_2D, textures[1]); + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, stream_width/2, stream_height/2, GL_RG, GL_UNSIGNED_BYTE, frame->uv); + assert(glGetError() == GL_NO_ERROR); +#endif glUniformMatrix4fv(program->uniformLocation("uTransform"), 1, GL_TRUE, frame_mat.v); - assert(glGetError() == GL_NO_ERROR); glEnableVertexAttribArray(0); glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_BYTE, (const void *)0); glDisableVertexAttribArray(0); @@ -240,42 +283,79 @@ void CameraViewWidget::paintGL() { glBindTexture(GL_TEXTURE_2D, 0); glActiveTexture(GL_TEXTURE0); glPixelStorei(GL_UNPACK_ALIGNMENT, 4); + glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); } void CameraViewWidget::vipcConnected(VisionIpcClient *vipc_client) { makeCurrent(); - latest_frame = nullptr; + frames.clear(); stream_width = vipc_client->buffers[0].width; stream_height = vipc_client->buffers[0].height; + stream_stride = vipc_client->buffers[0].stride; - for (int i = 0; i < 3; ++i) { - glBindTexture(GL_TEXTURE_2D, textures[i]); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - int width = i == 0 ? stream_width : stream_width / 2; - int height = i == 0 ? stream_height : stream_height / 2; - glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, width, height, 0, GL_RED, GL_UNSIGNED_BYTE, nullptr); - assert(glGetError() == GL_NO_ERROR); +#ifdef QCOM2 + egl_display = eglGetCurrentDisplay(); + + for (auto &pair : egl_images) { + eglDestroyImageKHR(egl_display, pair.second); } + egl_images.clear(); - updateFrameMat(width(), height()); + for (int i = 0; i < vipc_client->num_buffers; i++) { // import buffers into OpenGL + int fd = dup(vipc_client->buffers[i].fd); // eglDestroyImageKHR will close, so duplicate + EGLint img_attrs[] = { + EGL_WIDTH, (int)vipc_client->buffers[i].width, + EGL_HEIGHT, (int)vipc_client->buffers[i].height, + EGL_LINUX_DRM_FOURCC_EXT, DRM_FORMAT_NV12, + EGL_DMA_BUF_PLANE0_FD_EXT, fd, + EGL_DMA_BUF_PLANE0_OFFSET_EXT, 0, + EGL_DMA_BUF_PLANE0_PITCH_EXT, (int)vipc_client->buffers[i].stride, + EGL_DMA_BUF_PLANE1_FD_EXT, fd, + EGL_DMA_BUF_PLANE1_OFFSET_EXT, (int)vipc_client->buffers[i].uv_offset, + EGL_DMA_BUF_PLANE1_PITCH_EXT, (int)vipc_client->buffers[i].stride, + EGL_NONE + }; + egl_images[i] = eglCreateImageKHR(egl_display, EGL_NO_CONTEXT, EGL_LINUX_DMA_BUF_EXT, 0, img_attrs); + assert(eglGetError() == EGL_SUCCESS); + } +#else + glBindTexture(GL_TEXTURE_2D, textures[0]); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, stream_width, stream_height, 0, GL_RED, GL_UNSIGNED_BYTE, nullptr); + assert(glGetError() == GL_NO_ERROR); + + glBindTexture(GL_TEXTURE_2D, textures[1]); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RG8, stream_width/2, stream_height/2, 0, GL_RG, GL_UNSIGNED_BYTE, nullptr); + assert(glGetError() == GL_NO_ERROR); +#endif + + updateFrameMat(); } -void CameraViewWidget::vipcFrameReceived(VisionBuf *buf) { - latest_frame = buf; +void CameraViewWidget::vipcFrameReceived(VisionBuf *buf, uint32_t frame_id) { + frames.push_back(std::make_pair(frame_id, buf)); + while (frames.size() > FRAME_BUFFER_SIZE) { + frames.pop_front(); + } update(); } void CameraViewWidget::vipcThread() { VisionStreamType cur_stream_type = stream_type; std::unique_ptr vipc_client; + VisionIpcBufExtra meta_main = {0}; while (!QThread::currentThread()->isInterruptionRequested()) { if (!vipc_client || cur_stream_type != stream_type) { cur_stream_type = stream_type; - vipc_client.reset(new VisionIpcClient(stream_name, cur_stream_type, true)); + vipc_client.reset(new VisionIpcClient(stream_name, cur_stream_type, false)); } if (!vipc_client->connected) { @@ -286,8 +366,15 @@ void CameraViewWidget::vipcThread() { emit vipcThreadConnected(vipc_client.get()); } - if (VisionBuf *buf = vipc_client->recv(nullptr, 1000)) { - emit vipcThreadFrameReceived(buf); + if (VisionBuf *buf = vipc_client->recv(&meta_main, 1000)) { + emit vipcThreadFrameReceived(buf, meta_main.frame_id); } } + +#ifdef QCOM2 + for (auto &pair : egl_images) { + eglDestroyImageKHR(egl_display, pair.second); + } + egl_images.clear(); +#endif } diff --git a/selfdrive/ui/qt/widgets/cameraview.h b/selfdrive/ui/qt/widgets/cameraview.h index 6873e8e99..016522b05 100644 --- a/selfdrive/ui/qt/widgets/cameraview.h +++ b/selfdrive/ui/qt/widgets/cameraview.h @@ -6,10 +6,23 @@ #include #include #include + +#ifdef QCOM2 +#define EGL_EGLEXT_PROTOTYPES +#define EGL_NO_X11 +#define GL_TEXTURE_EXTERNAL_OES 0x8D65 +#include +#include +#include +#endif + #include "cereal/visionipc/visionipc_client.h" -#include "selfdrive/camerad/cameras/camera_common.h" +#include "system/camerad/cameras/camera_common.h" #include "selfdrive/ui/ui.h" +const int FRAME_BUFFER_SIZE = 5; +static_assert(FRAME_BUFFER_SIZE <= YUV_BUFFER_COUNT); + class CameraViewWidget : public QOpenGLWidget, protected QOpenGLFunctions { Q_OBJECT @@ -19,38 +32,55 @@ public: ~CameraViewWidget(); void setStreamType(VisionStreamType type) { stream_type = type; } void setBackgroundColor(const QColor &color) { bg = color; } + void setFrameId(int frame_id) { draw_frame_id = frame_id; } signals: void clicked(); void vipcThreadConnected(VisionIpcClient *); - void vipcThreadFrameReceived(VisionBuf *); + void vipcThreadFrameReceived(VisionBuf *, quint32); protected: void paintGL() override; void initializeGL() override; - void resizeGL(int w, int h) override { updateFrameMat(w, h); } + void resizeGL(int w, int h) override { updateFrameMat(); } void showEvent(QShowEvent *event) override; void hideEvent(QHideEvent *event) override; void mouseReleaseEvent(QMouseEvent *event) override { emit clicked(); } - virtual void updateFrameMat(int w, int h); + virtual void updateFrameMat(); + void updateCalibration(const mat3 &calib); void vipcThread(); bool zoomed_view; - VisionBuf *latest_frame = nullptr; GLuint frame_vao, frame_vbo, frame_ibo; + GLuint textures[2]; mat4 frame_mat; std::unique_ptr program; QColor bg = QColor("#000000"); +#ifdef QCOM2 + EGLDisplay egl_display; + std::map egl_images; +#endif + std::string stream_name; int stream_width = 0; int stream_height = 0; + int stream_stride = 0; std::atomic stream_type; QThread *vipc_thread = nullptr; - GLuint textures[3]; + // Calibration + float x_offset = 0; + float y_offset = 0; + float zoom = 1.0; + mat3 calibration = DEFAULT_CALIBRATION; + mat3 intrinsic_matrix = fcam_intrinsic_matrix; + + std::deque> frames; + uint32_t draw_frame_id = 0; + int prev_frame_id = 0; protected slots: void vipcConnected(VisionIpcClient *vipc_client); - void vipcFrameReceived(VisionBuf *vipc_client); + void vipcFrameReceived(VisionBuf *vipc_client, uint32_t frame_id); }; diff --git a/selfdrive/ui/qt/widgets/controls.cc b/selfdrive/ui/qt/widgets/controls.cc index 89c95843f..3264fd3aa 100644 --- a/selfdrive/ui/qt/widgets/controls.cc +++ b/selfdrive/ui/qt/widgets/controls.cc @@ -45,21 +45,23 @@ AbstractControl::AbstractControl(const QString &title, const QString &desc, cons main_layout->addLayout(hlayout); // description - if (!desc.isEmpty()) { - description = new QLabel(desc); - description->setContentsMargins(40, 20, 40, 20); - description->setStyleSheet("font-size: 40px; color: grey"); - description->setWordWrap(true); - description->setVisible(false); - main_layout->addWidget(description); + description = new QLabel(desc); + description->setContentsMargins(40, 20, 40, 20); + description->setStyleSheet("font-size: 40px; color: grey"); + description->setWordWrap(true); + description->setVisible(false); + main_layout->addWidget(description); - connect(title_label, &QPushButton::clicked, [=]() { - if (!description->isVisible()) { - emit showDescription(); - } + connect(title_label, &QPushButton::clicked, [=]() { + if (!description->isVisible()) { + emit showDescription(); + } + + if (!description->text().isEmpty()) { description->setVisible(!description->isVisible()); - }); - } + } + }); + main_layout->addStretch(); } @@ -125,7 +127,9 @@ void ElidedLabel::paintEvent(QPaintEvent *event) { ClickableWidget::ClickableWidget(QWidget *parent) : QWidget(parent) { } void ClickableWidget::mouseReleaseEvent(QMouseEvent *event) { - emit clicked(); + if (rect().contains(event->pos())) { + emit clicked(); + } } // Fix stylesheets diff --git a/selfdrive/ui/qt/widgets/controls.h b/selfdrive/ui/qt/widgets/controls.h index b6684e28b..aed99edae 100644 --- a/selfdrive/ui/qt/widgets/controls.h +++ b/selfdrive/ui/qt/widgets/controls.h @@ -24,7 +24,11 @@ signals: protected: void paintEvent(QPaintEvent *event) override; void resizeEvent(QResizeEvent* event) override; - void mouseReleaseEvent(QMouseEvent *event) override { emit clicked(); } + void mouseReleaseEvent(QMouseEvent *event) override { + if (rect().contains(event->pos())) { + emit clicked(); + } + } QString lastText_, elidedText_; }; diff --git a/selfdrive/ui/qt/widgets/drive_stats.cc b/selfdrive/ui/qt/widgets/drive_stats.cc index 61d47db1c..31009f03c 100644 --- a/selfdrive/ui/qt/widgets/drive_stats.cc +++ b/selfdrive/ui/qt/widgets/drive_stats.cc @@ -34,16 +34,16 @@ DriveStats::DriveStats(QWidget* parent) : QFrame(parent) { grid_layout->addWidget(labels.distance = newLabel("0", "number"), row, 1, Qt::AlignLeft); grid_layout->addWidget(labels.hours = newLabel("0", "number"), row, 2, Qt::AlignLeft); - grid_layout->addWidget(newLabel("Drives", "unit"), row + 1, 0, Qt::AlignLeft); + grid_layout->addWidget(newLabel((tr("Drives")), "unit"), row + 1, 0, Qt::AlignLeft); grid_layout->addWidget(labels.distance_unit = newLabel(getDistanceUnit(), "unit"), row + 1, 1, Qt::AlignLeft); - grid_layout->addWidget(newLabel("Hours ", "unit"), row + 1, 2, Qt::AlignLeft); + grid_layout->addWidget(newLabel(tr("Hours"), "unit"), row + 1, 2, Qt::AlignLeft); main_layout->addLayout(grid_layout); }; - add_stats_layouts("ALL TIME", all_); + add_stats_layouts(tr("ALL TIME"), all_); main_layout->addStretch(); - add_stats_layouts("PAST WEEK", week_); + add_stats_layouts(tr("PAST WEEK"), week_); if (auto dongleId = getDongleId()) { QString url = CommaApi::BASE_URL + "/v1.1/devices/" + *dongleId + "/stats"; diff --git a/selfdrive/ui/qt/widgets/drive_stats.h b/selfdrive/ui/qt/widgets/drive_stats.h index 944629451..5e2d96b24 100644 --- a/selfdrive/ui/qt/widgets/drive_stats.h +++ b/selfdrive/ui/qt/widgets/drive_stats.h @@ -12,7 +12,7 @@ public: private: void showEvent(QShowEvent *event) override; void updateStats(); - inline QString getDistanceUnit() const { return metric_ ? "KM" : "Miles"; } + inline QString getDistanceUnit() const { return metric_ ? tr("KM") : tr("Miles"); } bool metric_; QJsonDocument stats_; diff --git a/selfdrive/ui/qt/widgets/input.cc b/selfdrive/ui/qt/widgets/input.cc index 1122faf4c..dc54a3621 100644 --- a/selfdrive/ui/qt/widgets/input.cc +++ b/selfdrive/ui/qt/widgets/input.cc @@ -1,8 +1,9 @@ #include "selfdrive/ui/qt/widgets/input.h" #include +#include -#include "selfdrive/hardware/hw.h" +#include "system/hardware/hw.h" #include "selfdrive/ui/qt/util.h" #include "selfdrive/ui/qt/qt_window.h" #include "selfdrive/ui/qt/widgets/scrollview.h" @@ -67,7 +68,7 @@ InputDialog::InputDialog(const QString &title, QWidget *parent, const QString &s vlayout->addWidget(sublabel, 1, Qt::AlignTop | Qt::AlignLeft); } - QPushButton* cancel_btn = new QPushButton("Cancel"); + QPushButton* cancel_btn = new QPushButton(tr("Cancel")); cancel_btn->setFixedSize(386, 125); cancel_btn->setStyleSheet(R"( font-size: 48px; @@ -164,7 +165,7 @@ void InputDialog::handleEnter() { done(QDialog::Accepted); emitText(line->text()); } else { - setMessage("Need at least "+QString::number(minLength)+" characters!", false); + setMessage(tr("Need at least %1 characters!").arg(minLength), false); } } @@ -217,12 +218,12 @@ ConfirmationDialog::ConfirmationDialog(const QString &prompt_text, const QString } bool ConfirmationDialog::alert(const QString &prompt_text, QWidget *parent) { - ConfirmationDialog d = ConfirmationDialog(prompt_text, "Ok", "", parent); + ConfirmationDialog d = ConfirmationDialog(prompt_text, tr("Ok"), "", parent); return d.exec(); } bool ConfirmationDialog::confirm(const QString &prompt_text, QWidget *parent) { - ConfirmationDialog d = ConfirmationDialog(prompt_text, "Ok", "Cancel", parent); + ConfirmationDialog d = ConfirmationDialog(prompt_text, tr("Ok"), tr("Cancel"), parent); return d.exec(); } @@ -254,6 +255,96 @@ RichTextDialog::RichTextDialog(const QString &prompt_text, const QString &btn_te } bool RichTextDialog::alert(const QString &prompt_text, QWidget *parent) { - auto d = RichTextDialog(prompt_text, "Ok", parent); + auto d = RichTextDialog(prompt_text, tr("Ok"), parent); return d.exec(); } + +// MultiOptionDialog + +MultiOptionDialog::MultiOptionDialog(const QString &prompt_text, const QStringList &l, const QString ¤t, QWidget *parent) : QDialogBase(parent) { + QFrame *container = new QFrame(this); + container->setStyleSheet(R"( + QFrame { background-color: #1B1B1B; } + #confirm_btn[enabled="false"] { background-color: #2B2B2B; } + #confirm_btn:enabled { background-color: #465BEA; } + #confirm_btn:enabled:pressed { background-color: #3049F4; } + )"); + + QVBoxLayout *main_layout = new QVBoxLayout(container); + main_layout->setContentsMargins(55, 50, 55, 50); + + QLabel *title = new QLabel(prompt_text, this); + title->setStyleSheet("font-size: 70px; font-weight: 500;"); + main_layout->addWidget(title, 0, Qt::AlignLeft | Qt::AlignTop); + main_layout->addSpacing(25); + + QWidget *listWidget = new QWidget(this); + QVBoxLayout *listLayout = new QVBoxLayout(listWidget); + listLayout->setSpacing(20); + listWidget->setStyleSheet(R"( + QPushButton { + height: 135; + padding: 0px 50px; + text-align: left; + font-size: 55px; + font-weight: 300; + border-radius: 10px; + background-color: #4F4F4F; + } + QPushButton:checked { background-color: #465BEA; } + )"); + + QButtonGroup *group = new QButtonGroup(listWidget); + group->setExclusive(true); + + QPushButton *confirm_btn = new QPushButton(tr("Select")); + confirm_btn->setObjectName("confirm_btn"); + confirm_btn->setEnabled(false); + + for (const QString &s : l) { + QPushButton *selectionLabel = new QPushButton(s); + selectionLabel->setCheckable(true); + selectionLabel->setChecked(s == current); + QObject::connect(selectionLabel, &QPushButton::toggled, [=](bool checked) { + if (checked) selection = s; + if (selection != current) { + confirm_btn->setEnabled(true); + } else { + confirm_btn->setEnabled(false); + } + }); + + group->addButton(selectionLabel); + listLayout->addWidget(selectionLabel); + } + + ScrollView *scroll_view = new ScrollView(listWidget, this); + scroll_view->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); + + main_layout->addWidget(scroll_view); + main_layout->addStretch(1); + main_layout->addSpacing(35); + + // cancel + confirm buttons + QHBoxLayout *blayout = new QHBoxLayout; + main_layout->addLayout(blayout); + blayout->setSpacing(50); + + QPushButton *cancel_btn = new QPushButton(tr("Cancel")); + QObject::connect(cancel_btn, &QPushButton::clicked, this, &ConfirmationDialog::reject); + QObject::connect(confirm_btn, &QPushButton::clicked, this, &ConfirmationDialog::accept); + blayout->addWidget(cancel_btn); + blayout->addWidget(confirm_btn); + + QVBoxLayout *outer_layout = new QVBoxLayout(this); + outer_layout->setContentsMargins(50, 50, 50, 50); + outer_layout->addWidget(container); +} + +QString MultiOptionDialog::getSelection(const QString &prompt_text, const QStringList &l, const QString ¤t, QWidget *parent) { + MultiOptionDialog d = MultiOptionDialog(prompt_text, l, current, parent); + if (d.exec()) { + return d.selection; + } + return ""; +} diff --git a/selfdrive/ui/qt/widgets/input.h b/selfdrive/ui/qt/widgets/input.h index f81211d0e..6c47a31d8 100644 --- a/selfdrive/ui/qt/widgets/input.h +++ b/selfdrive/ui/qt/widgets/input.h @@ -68,3 +68,12 @@ public: explicit RichTextDialog(const QString &prompt_text, const QString &btn_text, QWidget* parent); static bool alert(const QString &prompt_text, QWidget *parent); }; + +class MultiOptionDialog : public QDialogBase { + Q_OBJECT + +public: + explicit MultiOptionDialog(const QString &prompt_text, const QStringList &l, const QString ¤t, QWidget *parent); + static QString getSelection(const QString &prompt_text, const QStringList &l, const QString ¤t, QWidget *parent); + QString selection; +}; diff --git a/selfdrive/ui/qt/widgets/offroad_alerts.cc b/selfdrive/ui/qt/widgets/offroad_alerts.cc index 53f7eb677..937ea02f8 100644 --- a/selfdrive/ui/qt/widgets/offroad_alerts.cc +++ b/selfdrive/ui/qt/widgets/offroad_alerts.cc @@ -5,7 +5,7 @@ #include #include "common/util.h" -#include "selfdrive/hardware/hw.h" +#include "system/hardware/hw.h" #include "selfdrive/ui/qt/widgets/scrollview.h" AbstractAlert::AbstractAlert(bool hasRebootBtn, QWidget *parent) : QFrame(parent) { @@ -22,12 +22,12 @@ AbstractAlert::AbstractAlert(bool hasRebootBtn, QWidget *parent) : QFrame(parent QHBoxLayout *footer_layout = new QHBoxLayout(); main_layout->addLayout(footer_layout); - QPushButton *dismiss_btn = new QPushButton("Close"); + QPushButton *dismiss_btn = new QPushButton(tr("Close")); dismiss_btn->setFixedSize(400, 125); footer_layout->addWidget(dismiss_btn, 0, Qt::AlignBottom | Qt::AlignLeft); QObject::connect(dismiss_btn, &QPushButton::clicked, this, &AbstractAlert::dismiss); - snooze_btn = new QPushButton("Snooze Update"); + snooze_btn = new QPushButton(tr("Snooze Update")); snooze_btn->setVisible(false); snooze_btn->setFixedSize(550, 125); footer_layout->addWidget(snooze_btn, 0, Qt::AlignBottom | Qt::AlignRight); @@ -38,7 +38,7 @@ AbstractAlert::AbstractAlert(bool hasRebootBtn, QWidget *parent) : QFrame(parent snooze_btn->setStyleSheet(R"(color: white; background-color: #4F4F4F;)"); if (hasRebootBtn) { - QPushButton *rebootBtn = new QPushButton("Reboot and Update"); + QPushButton *rebootBtn = new QPushButton(tr("Reboot and Update")); rebootBtn->setFixedSize(600, 125); footer_layout->addWidget(rebootBtn, 0, Qt::AlignBottom | Qt::AlignRight); QObject::connect(rebootBtn, &QPushButton::clicked, [=]() { Hardware::reboot(); }); diff --git a/selfdrive/ui/qt/widgets/prime.cc b/selfdrive/ui/qt/widgets/prime.cc index 27d472535..541947526 100644 --- a/selfdrive/ui/qt/widgets/prime.cc +++ b/selfdrive/ui/qt/widgets/prime.cc @@ -83,18 +83,21 @@ PairingPopup::PairingPopup(QWidget *parent) : QDialogBase(parent) { vlayout->addSpacing(30); - QLabel *title = new QLabel("Pair your device to your comma account", this); + QLabel *title = new QLabel(tr("Pair your device to your comma account"), this); title->setStyleSheet("font-size: 75px; color: black;"); title->setWordWrap(true); vlayout->addWidget(title); - QLabel *instructions = new QLabel(R"( + QLabel *instructions = new QLabel(QString(R"(
    -
  1. Go to https://connect.comma.ai on your phone
  2. -
  3. Click "add new device" and scan the QR code on the right
  4. -
  5. Bookmark connect.comma.ai to your home screen to use it like an app
  6. +
  7. %1
  8. +
  9. %2
  10. +
  11. %3
- )", this); + )").arg(tr("Go to https://connect.comma.ai on your phone")) + .arg(tr("Click \"add new device\" and scan the QR code on the right")) + .arg(tr("Bookmark connect.comma.ai to your home screen to use it like an app")), this); + instructions->setStyleSheet("font-size: 47px; font-weight: bold; color: black;"); instructions->setWordWrap(true); vlayout->addWidget(instructions); @@ -120,19 +123,19 @@ PrimeUserWidget::PrimeUserWidget(QWidget* parent) : QWidget(parent) { primeLayout->setMargin(0); primeWidget->setContentsMargins(60, 50, 60, 50); - QLabel* subscribed = new QLabel("✓ SUBSCRIBED"); + QLabel* subscribed = new QLabel(tr("✓ SUBSCRIBED")); subscribed->setStyleSheet("font-size: 41px; font-weight: bold; color: #86FF4E;"); primeLayout->addWidget(subscribed, 0, Qt::AlignTop); primeLayout->addSpacing(60); - QLabel* commaPrime = new QLabel("comma prime"); + QLabel* commaPrime = new QLabel(tr("comma prime")); commaPrime->setStyleSheet("font-size: 75px; font-weight: bold;"); primeLayout->addWidget(commaPrime, 0, Qt::AlignTop); primeLayout->addSpacing(20); - QLabel* connectUrl = new QLabel("CONNECT.COMMA.AI"); + QLabel* connectUrl = new QLabel(tr("CONNECT.COMMA.AI")); connectUrl->setStyleSheet("font-size: 41px; font-family: Inter SemiBold; color: #A0A0A0;"); primeLayout->addWidget(connectUrl, 0, Qt::AlignTop); @@ -145,7 +148,7 @@ PrimeUserWidget::PrimeUserWidget(QWidget* parent) : QWidget(parent) { pointsLayout->setMargin(0); pointsWidget->setContentsMargins(60, 50, 60, 50); - QLabel* commaPoints = new QLabel("COMMA POINTS"); + QLabel* commaPoints = new QLabel(tr("COMMA POINTS")); commaPoints->setStyleSheet("font-size: 41px; font-family: Inter SemiBold;"); pointsLayout->addWidget(commaPoints, 0, Qt::AlignTop); @@ -181,24 +184,24 @@ PrimeAdWidget::PrimeAdWidget(QWidget* parent) : QFrame(parent) { main_layout->setContentsMargins(80, 90, 80, 60); main_layout->setSpacing(0); - QLabel *upgrade = new QLabel("Upgrade Now"); + QLabel *upgrade = new QLabel(tr("Upgrade Now")); upgrade->setStyleSheet("font-size: 75px; font-weight: bold;"); main_layout->addWidget(upgrade, 0, Qt::AlignTop); main_layout->addSpacing(50); - QLabel *description = new QLabel("Become a comma prime member at connect.comma.ai"); + QLabel *description = new QLabel(tr("Become a comma prime member at connect.comma.ai")); description->setStyleSheet("font-size: 60px; font-weight: light; color: white;"); description->setWordWrap(true); main_layout->addWidget(description, 0, Qt::AlignTop); main_layout->addStretch(); - QLabel *features = new QLabel("PRIME FEATURES:"); + QLabel *features = new QLabel(tr("PRIME FEATURES:")); features->setStyleSheet("font-size: 41px; font-weight: bold; color: #E5E5E5;"); main_layout->addWidget(features, 0, Qt::AlignBottom); main_layout->addSpacing(30); - QVector bullets = {"Remote access", "14 days of storage", "Developer perks"}; + QVector bullets = {tr("Remote access"), tr("1 year of storage"), tr("Developer perks")}; for (auto &b: bullets) { const QString check = " "; QLabel *l = new QLabel(check + b); @@ -227,20 +230,20 @@ SetupWidget::SetupWidget(QWidget* parent) : QFrame(parent) { finishRegistationLayout->setContentsMargins(30, 75, 30, 45); finishRegistationLayout->setSpacing(0); - QLabel* registrationTitle = new QLabel("Finish Setup"); + QLabel* registrationTitle = new QLabel(tr("Finish Setup")); registrationTitle->setStyleSheet("font-size: 75px; font-weight: bold; margin-left: 55px;"); finishRegistationLayout->addWidget(registrationTitle); finishRegistationLayout->addSpacing(30); - QLabel* registrationDescription = new QLabel("Pair your device with comma connect (connect.comma.ai) and claim your comma prime offer."); + QLabel* registrationDescription = new QLabel(tr("Pair your device with comma connect (connect.comma.ai) and claim your comma prime offer.")); registrationDescription->setWordWrap(true); registrationDescription->setStyleSheet("font-size: 55px; font-weight: light; margin-left: 55px;"); finishRegistationLayout->addWidget(registrationDescription); finishRegistationLayout->addStretch(); - QPushButton* pair = new QPushButton("Pair device"); + QPushButton* pair = new QPushButton(tr("Pair device")); pair->setFixedHeight(220); pair->setStyleSheet(R"( QPushButton { diff --git a/selfdrive/ui/qt/widgets/scrollview.cc b/selfdrive/ui/qt/widgets/scrollview.cc index 1aa05b415..bd4309d8d 100644 --- a/selfdrive/ui/qt/widgets/scrollview.cc +++ b/selfdrive/ui/qt/widgets/scrollview.cc @@ -37,7 +37,7 @@ ScrollView::ScrollView(QWidget *w, QWidget *parent) : QScrollArea(parent) { sp.setScrollMetric(QScrollerProperties::VerticalOvershootPolicy, QVariant::fromValue(QScrollerProperties::OvershootAlwaysOff)); sp.setScrollMetric(QScrollerProperties::HorizontalOvershootPolicy, QVariant::fromValue(QScrollerProperties::OvershootAlwaysOff)); - + sp.setScrollMetric(QScrollerProperties::MousePressEventDelay, 0.01); scroller->grabGesture(this->viewport(), QScroller::LeftMouseButtonGesture); scroller->setScrollerProperties(sp); } diff --git a/selfdrive/ui/qt/widgets/ssh_keys.cc b/selfdrive/ui/qt/widgets/ssh_keys.cc index 46ccf6aee..f17604b3e 100644 --- a/selfdrive/ui/qt/widgets/ssh_keys.cc +++ b/selfdrive/ui/qt/widgets/ssh_keys.cc @@ -4,16 +4,16 @@ #include "selfdrive/ui/qt/api.h" #include "selfdrive/ui/qt/widgets/input.h" -SshControl::SshControl() : ButtonControl("SSH Keys", "", "Warning: This grants SSH access to all public keys in your GitHub settings. Never enter a GitHub username other than your own. A comma employee will NEVER ask you to add their GitHub username.") { +SshControl::SshControl() : ButtonControl(tr("SSH Keys"), "", tr("Warning: This grants SSH access to all public keys in your GitHub settings. Never enter a GitHub username other than your own. A comma employee will NEVER ask you to add their GitHub username.")) { username_label.setAlignment(Qt::AlignRight | Qt::AlignVCenter); username_label.setStyleSheet("color: #aaaaaa"); hlayout->insertWidget(1, &username_label); QObject::connect(this, &ButtonControl::clicked, [=]() { - if (text() == "ADD") { - QString username = InputDialog::getText("Enter your GitHub username", this); + if (text() == tr("ADD")) { + QString username = InputDialog::getText(tr("Enter your GitHub username"), this); if (username.length() > 0) { - setText("LOADING"); + setText(tr("LOADING")); setEnabled(false); getUserKeys(username); } @@ -31,10 +31,10 @@ void SshControl::refresh() { QString param = QString::fromStdString(params.get("GithubSshKeys")); if (param.length()) { username_label.setText(QString::fromStdString(params.get("GithubUsername"))); - setText("REMOVE"); + setText(tr("REMOVE")); } else { username_label.setText(""); - setText("ADD"); + setText(tr("ADD")); } setEnabled(true); } @@ -47,13 +47,13 @@ void SshControl::getUserKeys(const QString &username) { params.put("GithubUsername", username.toStdString()); params.put("GithubSshKeys", resp.toStdString()); } else { - ConfirmationDialog::alert(QString("Username '%1' has no keys on GitHub").arg(username), this); + ConfirmationDialog::alert(tr("Username '%1' has no keys on GitHub").arg(username), this); } } else { if (request->timeout()) { - ConfirmationDialog::alert("Request timed out", this); + ConfirmationDialog::alert(tr("Request timed out"), this); } else { - ConfirmationDialog::alert(QString("Username '%1' doesn't exist on GitHub").arg(username), this); + ConfirmationDialog::alert(tr("Username '%1' doesn't exist on GitHub").arg(username), this); } } diff --git a/selfdrive/ui/qt/widgets/ssh_keys.h b/selfdrive/ui/qt/widgets/ssh_keys.h index 0614e8c1d..01e2ab83c 100644 --- a/selfdrive/ui/qt/widgets/ssh_keys.h +++ b/selfdrive/ui/qt/widgets/ssh_keys.h @@ -2,7 +2,7 @@ #include -#include "selfdrive/hardware/hw.h" +#include "system/hardware/hw.h" #include "selfdrive/ui/qt/widgets/controls.h" // SSH enable toggle @@ -10,7 +10,7 @@ class SshToggle : public ToggleControl { Q_OBJECT public: - SshToggle() : ToggleControl("Enable SSH", "", "", Hardware::get_ssh_enabled()) { + SshToggle() : ToggleControl(tr("Enable SSH"), "", "", Hardware::get_ssh_enabled()) { QObject::connect(this, &SshToggle::toggleFlipped, [=](bool state) { Hardware::set_ssh_enabled(state); }); diff --git a/selfdrive/ui/qt/window.cc b/selfdrive/ui/qt/window.cc index fb21f6f7a..04ce15ef2 100644 --- a/selfdrive/ui/qt/window.cc +++ b/selfdrive/ui/qt/window.cc @@ -2,7 +2,7 @@ #include -#include "selfdrive/hardware/hw.h" +#include "system/hardware/hw.h" MainWindow::MainWindow(QWidget *parent) : QWidget(parent) { main_layout = new QStackedLayout(this); @@ -45,9 +45,6 @@ MainWindow::MainWindow(QWidget *parent) : QWidget(parent) { }); // load fonts - QFontDatabase::addApplicationFont("../assets/fonts/opensans_regular.ttf"); - QFontDatabase::addApplicationFont("../assets/fonts/opensans_bold.ttf"); - QFontDatabase::addApplicationFont("../assets/fonts/opensans_semibold.ttf"); QFontDatabase::addApplicationFont("../assets/fonts/Inter-Black.ttf"); QFontDatabase::addApplicationFont("../assets/fonts/Inter-Bold.ttf"); QFontDatabase::addApplicationFont("../assets/fonts/Inter-ExtraBold.ttf"); diff --git a/selfdrive/ui/soundd/sound.h b/selfdrive/ui/soundd/sound.h index 82b360fd3..7e009d28a 100644 --- a/selfdrive/ui/soundd/sound.h +++ b/selfdrive/ui/soundd/sound.h @@ -2,7 +2,7 @@ #include #include -#include "selfdrive/hardware/hw.h" +#include "system/hardware/hw.h" #include "selfdrive/ui/ui.h" const std::tuple sound_list[] = { diff --git a/selfdrive/ui/translations/languages.json b/selfdrive/ui/translations/languages.json new file mode 100644 index 000000000..48f094867 --- /dev/null +++ b/selfdrive/ui/translations/languages.json @@ -0,0 +1,6 @@ +{ + "English": "", + "中文(繁體)": "main_zh-CHT", + "中文(简体)": "main_zh-CHS", + "한국어": "main_ko" +} diff --git a/selfdrive/ui/translations/main_ja.qm b/selfdrive/ui/translations/main_ja.qm new file mode 100644 index 000000000..552545cf8 Binary files /dev/null and b/selfdrive/ui/translations/main_ja.qm differ diff --git a/selfdrive/ui/translations/main_ja.ts b/selfdrive/ui/translations/main_ja.ts new file mode 100644 index 000000000..f3b873312 --- /dev/null +++ b/selfdrive/ui/translations/main_ja.ts @@ -0,0 +1,1207 @@ + + + + + AbstractAlert + + + Close + 閉じる + + + + Snooze Update + 更新停止 + + + + Reboot and Update + 再起動してアップデート + + + + AdvancedNetworking + + + Back + もどる + + + + Enable Tethering + テザリングを有効化 + + + + Tethering Password + テザリングパスワード + + + + + EDIT + 編集 + + + + Enter new tethering password + 新しいテザリングパスワードを入力 + + + + IP Address + IPアドレス + + + + Enable Roaming + ローミングを有効化 + + + + APN Setting + APN 設定 + + + + Enter APN + APN 入力 + + + + leave blank for automatic configuration + 空欄で自動設定 + + + + ConfirmationDialog + + + + Ok + OK + + + + Cancel + キャンセル + + + + DeclinePage + + + You must accept the Terms and Conditions in order to use openpilot. + openpilotを利用するためには、利用規約に同意する必要があります。 + + + + Back + 戻る + + + + Decline, uninstall %1 + 拒否してアンインストール %1 + + + + DevicePanel + + + Dongle ID + ドングル ID + + + + N/A + N/A + + + + Serial + シリアル + + + + Driver Camera + ドライバーカメラ + + + + PREVIEW + プレビュー + + + + Preview the driver facing camera to ensure that driver monitoring has good visibility. (vehicle must be off) + ドライバー向けカメラをプレビューする、ドライバーモニタリングの視認性を確保します。(車両の電源を切る必要があります) + + + + Reset Calibration + キャリブレーションリセット + + + + RESET + リセット + + + + Are you sure you want to reset calibration? + 本当にキャリブレーションをリセットしますか? + + + + Review Training Guide + トレーニングガイドを見る + + + + REVIEW + レビュー + + + + Review the rules, features, and limitations of openpilot + openpilot 規約 機能 制約を見る + + + + Are you sure you want to review the training guide? + 本当にトレーニングガイドを見ますか? + + + + Regulatory + レギュレーション + + + + VIEW + ビュー + + + Change Language + 言語を変更 + + + CHANGE + 変更 + + + Select a language + 言語を選択する + + + + Reboot + 再起動 + + + + Power Off + 電源を切る + + + + openpilot requires the device to be mounted within 4° left or right and within 5° up or 8° down. openpilot is continuously calibrating, resetting is rarely required. + openpilot は、左右に4°、上に5°、下に8°の範囲に設置する必要があります。openpilot は継続的に校正されているので、手動でリセットする必要はほとんどありません。 + + + + Your device is pointed %1° %2 and %3° %4. + デバイスは %1° %2 と %3° %4を示しています。 + + + + down + + + + + up + + + + + left + + + + + right + + + + + Are you sure you want to reboot? + 本当に再起動しますか? + + + + Disengage to Reboot + 再起動するために離脱します + + + + Are you sure you want to power off? + 本当に電源を切っても良いですか? + + + + Disengage to Power Off + 電源を切るために離脱します + + + + DriveStats + + + Drives + ドライブ + + + + Hours + 時間 + + + + ALL TIME + 累計 + + + + PAST WEEK + 先週 + + + + KM + km + + + + Miles + マイル + + + + DriverViewScene + + + camera starting + カメラ起動 + + + + InputDialog + + + Cancel + キャンセル + + + + Need at least + 最低限必要なもの + + + + characters! + 記号! + + + + Installer + + + Installing... + インストール... + + + + Receiving objects: + オブジェクトを受信中: + + + + Resolving deltas: + リゾルブ解決中: + + + + Updating files: + ファイルを更新中: + + + + MapPanel + + + Current Destination + 現在の目的地 + + + + CLEAR + クリア + + + + Recent Destinations + 最近の目的地 + + + + Try the Navigation Beta + ベータ版ナビゲーション + + + + Get turn-by-turn directions displayed and more with a comma +prime subscription. Sign up now: https://connect.comma.ai + より詳細な案内や表示に関する情報を得ることができます。 詳しくはこちら:https://connect.comma.ai + + + + No home +location set + 自宅の登録なし +位置を設定 + + + + No work +location set + 職場の登録なし +位置を設定 + + + + no recent destinations + 最寄りの目的地がありません + + + + MultiOptionDialog + + Select + 選択 + + + + Networking + + + Advanced + 詳細 + + + + Enter password + パスワードを入力 + + + + + for " + のため " + + + + Wrong password + パスワードが間違っています + + + + NvgWindow + + + km/h + km/時 + + + + mph + マイル/時 + + + + + MAX + 最大 + + + + + SPEED + 速度 + + + + + LIMIT + 制限 + + + + OffroadHome + + + UPDATE + 更新 + + + + ALERTS + 警告 + + + + ALERT + 警告 + + + + PairingPopup + + + Pair your device to your comma account + デバイスとアカウントを連携する + + + + + <ol type='1' style='margin-left: 15px;'> + <li style='margin-bottom: 50px;'>Go to https://connect.comma.ai on your phone</li> + <li style='margin-bottom: 50px;'>Click "add new device" and scan the QR code on the right</li> + <li style='margin-bottom: 50px;'>Bookmark connect.comma.ai to your home screen to use it like an app</li> + </ol> + + + <ol type='1' style='margin-left: 15px;'> + <li style='margin-bottom: 50px;'>モバイルでアクセス https://connect.comma.ai</li> + <li style='margin-bottom: 50px;'>“新しいデバイスを追加”をクリックし,QRコードを読み込みます</li> + <li style='margin-bottom: 50px;'>connect.comma.aiをホーム画面にブックマークして、アプリのように使うことができます</li> + </ol> + + + + + PrimeAdWidget + + + Upgrade Now + いますぐアップグレード + + + + Become a comma prime member at connect.comma.ai + connect.comma.ai のプライムメンバーになる + + + + PRIME FEATURES: + 主な機能: + + + + Remote access + リモートアクセス + + + + 1 year of storage + 1年の保存期間 + + + + Developer perks + 開発者特典 + + + + PrimeUserWidget + + + ✓ SUBSCRIBED + ✓ 購読しました + + + + comma prime + comma prime + + + + CONNECT.COMMA.AI + CONNECT.COMMA.AI + + + + COMMA POINTS + COMMA POINTS + + + + QObject + + + Reboot + 再起動 + + + + Exit + 退出 + + + + dashcam + ダッシュカム + + + + openpilot + オープンパイロット + + + + %1 minute%2 ago + %1 分%2 前 + + + + %1 hour%2 ago + %1 時間%2 前 + + + + %1 day%2 ago + %1 日%2 前 + + + + Reset + + + Reset failed. Reboot to try again. + 初期化に失敗しました。再起動後に再試行してください。 + + + + Are you sure you want to reset your device? + 本当に初期化しますか? + + + + Resetting device... + デバイスが初期化されます... + + + + System Reset + システムを初期化 + + + + System reset triggered. Press confirm to erase all content and settings. Press cancel to resume boot. + システムを初期化させます。 すべてのコンテンツと設定が削除されます。 キャンセルを押すと再起動します。 + + + + Cancel + キャンセル + + + + Reboot + 再起動 + + + + Confirm + 確認 + + + + Unable to mount data partition. Press confirm to reset your device. + dataパーティションをマウントできません。 確認を押すとデバイスが初期化されます。 + + + + RichTextDialog + + + Ok + OK + + + + SettingsWindow + + + × + × + + + + Device + デバイス + + + + + Network + ネットワーク + + + + Toggles + 切り替え + + + + Software + ソフトウェア + + + + Navigation + ナビゲーション + + + + Setup + + + WARNING: Low Voltage + 警告:低電圧 + + + + Power your device in a car with a harness or proceed at your own risk. + 自己責任でハーネスから電源を供給してください。 + + + + Power off + 電源を切る + + + + + + Continue + 続ける + + + + Getting Started + はじめに + + + + Before we get on the road, let’s finish installation and cover some details. + その前に、インストールを完了し、いくつかの詳細を説明します。 + + + + Connect to Wi-Fi + Wi-Fiに接続 + + + + + Back + 戻る + + + + Continue without Wi-Fi + Wi-Fi に未接続で続行 + + + + Waiting for internet + インターネット接続を待機中 + + + + Choose Software to Install + インストールするソフトウェアを選びます + + + + Dashcam + ダッシュカム + + + + Custom Software + カスタムソフトウェア + + + + Enter URL + URLを入力 + + + + for Custom Software + カスタムソフトウェア + + + + Downloading... + ダウンロード中... + + + + Download Failed + ダウンロード失敗 + + + + Ensure the entered URL is valid, and the device’s internet connection is good. + 入力されたURLが有効であること、デバイスがインターネットに接続されていることを確認してください。 + + + + Reboot device + デバイスを再起動 + + + + Start over + 再スタート + + + + SetupWidget + + + Finish Setup + セットアップ完了 + + + + Pair your device with comma connect (connect.comma.ai) and claim your comma prime offer. + デバイスを comma connect (connect.comma.ai)でペアリングし comma prime 特典を申請してください。 + + + + Pair device + デバイスをペアリング + + + + Sidebar + + + + CONNECT + 接続 + + + + OFFLINE + オフライン + + + + + ONLINE + オンライン + + + + ERROR + エラー + + + + + + TEMP + 温度 + + + + HIGH + 高温 + + + + GOOD + 最適 + + + + OK + OK + + + + VEHICLE + 車両 + + + + NO + NO + + + + PANDA + パンダ + + + + GPS + GPS + + + + SEARCH + 検索 + + + + -- + -- + + + + Wi-Fi + Wi-Fi + + + + ETH + ETH + + + + 2G + 2G + + + + 3G + 3G + + + + LTE + LTE + + + + 5G + 5G + + + + SoftwarePanel + + + Git Branch + Git ブランチ + + + + Git Commit + Git コミット + + + + OS Version + OS バージョン + + + + Version + バージョン + + + + Last Update Check + 最終更新確認 + + + + The last time openpilot successfully checked for an update. The updater only runs while the car is off. + openpilotが最後にアップデートの確認に成功してからの時間です。アップデート処理は、車の電源が切れているときのみ実行されます。 + + + + Check for Update + 更新確認 + + + + CHECKING + 確認中 + + + + Uninstall + アンインストール + + + + UNINSTALL + アンインストール + + + + Are you sure you want to uninstall? + 本当にアンインストールしますか? + + + + failed to fetch update + 更新の取得に失敗しました + + + + + CHECK + 確認 + + + + SshControl + + + SSH Keys + SSH 鍵 + + + + Warning: This grants SSH access to all public keys in your GitHub settings. Never enter a GitHub username other than your own. A comma employee will NEVER ask you to add their GitHub username. + 警告: これは、GitHub の設定にあるすべての公開鍵への SSH アクセスを許可するものです。自分以外のGitHubのユーザー名を入力しないでください。コンマのスタッフがGitHubのユーザー名を追加するようお願いすることはありません。 + + + + + ADD + 追加 + + + + Enter your GitHub username + GitHubのユーザー名を入力してください + + + + LOADING + ローディング + + + + REMOVE + 削除 + + + + Username '%1' has no keys on GitHub + ユーザー名“%1”は GitHub に鍵がありません + + + + Request timed out + リクエストタイムアウト + + + + Username '%1' doesn't exist on GitHub + ユーザー名 '%1' は GitHub に存在しません + + + + SshToggle + + + Enable SSH + SSH を有効化 + + + + TermsPage + + + Terms & Conditions + 利用規約 + + + + Decline + 拒否 + + + + Scroll to accept + スクロールして同意 + + + + Agree + 同意 + + + + TogglesPanel + + + Enable openpilot + openpilot を有効化 + + + + Use the openpilot system for adaptive cruise control and lane keep driver assistance. Your attention is required at all times to use this feature. Changing this setting takes effect when the car is powered off. + アダプティブクルーズコントロールとレーンキーピングドライバーアシスト(openpilotシステム)。この機能を使用するには、常に注意が必要です。この設定を変更すると、車の電源が切れたときに有効になります。 + + + + Enable Lane Departure Warnings + 車線逸脱警報機能を有効化 + + + + Receive alerts to steer back into the lane when your vehicle drifts over a detected lane line without a turn signal activated while driving over 31 mph (50 km/h). + 時速31マイル(50km)を超えるスピードで走行中、方向指示器を作動させずに検出された車線ライン上に車両が触れた場合、車線に戻るアラートを受信します。 + + + + Enable Right-Hand Drive + 右ハンドルを有効化 + + + + Allow openpilot to obey left-hand traffic conventions and perform driver monitoring on right driver seat. + openpilotが左側通行規則を遵守し、右側の運転席でドライバーの監視を行うことを可能にします。 + + + + Use Metric System + メートル法を有効化 + + + + Display speed in km/h instead of mph. + 速度はmphではなくkm/hで表示されます。 + + + + Record and Upload Driver Camera + ドライバーカメラの録画とアップロード + + + + Upload data from the driver facing camera and help improve the driver monitoring algorithm. + ドライバーカメラからのデータをアップロードし、ドライバー監視のアルゴリズム向上に役立てます。 + + + + Disengage On Accelerator Pedal + アクセルペダルで解除する + + + + When enabled, pressing the accelerator pedal will disengage openpilot. + 有効な場合 openpilot はアクセルペダルを踏むと解除されます。 + + + + Show ETA in 24h format + 24時間表示 + + + + Use 24h format instead of am/pm + AM/PMの代わりに24時間形式を使用します + + + + openpilot Longitudinal Control + openpilot による垂直方向の制御 + + + + openpilot will disable the car's radar and will take over control of gas and brakes. Warning: this disables AEB! + openpilotは、車のレーダーを無効化し、アクセルとブレーキの制御を引き継ぎます。注意:AEBを無効にします! + + + + Updater + + + Update Required + 更新が必要です + + + + An operating system update is required. Connect your device to Wi-Fi for the fastest update experience. The download size is approximately 1GB. + OSのアップデートが必要です。Wi-Fiに接続することで、最速のアップデートを体験できます。ダウンロードサイズは約1GBです。 + + + + Connect to Wi-Fi + Wi-Fiに接続 + + + + Install + インストール + + + + Back + 戻る + + + + Loading... + 読み込み中... + + + + Reboot + 再起動 + + + + Update failed + 更新失敗 + + + + WifiUI + + + + Scanning for networks... + ネットワークをスキャン中... + + + + CONNECTING... + 接続中... + + + + FORGET + 削除 + + + + Forget Wi-Fi Network " + Wi-Fiを削除する” + + + diff --git a/selfdrive/ui/translations/main_ko.qm b/selfdrive/ui/translations/main_ko.qm new file mode 100644 index 000000000..d59698e07 Binary files /dev/null and b/selfdrive/ui/translations/main_ko.qm differ diff --git a/selfdrive/ui/translations/main_ko.ts b/selfdrive/ui/translations/main_ko.ts new file mode 100644 index 000000000..07d687746 --- /dev/null +++ b/selfdrive/ui/translations/main_ko.ts @@ -0,0 +1,1274 @@ + + + + + AbstractAlert + + + Close + 닫기 + + + + Snooze Update + 업데이트 일시중지 + + + + Reboot and Update + 업데이트 및 재부팅 + + + + AdvancedNetworking + + + Back + 뒤로 + + + + Enable Tethering + 테더링 사용 + + + + Tethering Password + 테더링 비밀번호 + + + + + EDIT + 편집 + + + + Enter new tethering password + 새 테더링 비밀번호를 입력하세요 + + + + IP Address + IP 주소 + + + + Enable Roaming + 로밍 사용 + + + + APN Setting + APN 설정 + + + + Enter APN + APN 입력 + + + + leave blank for automatic configuration + 자동설정을 하려면 공백으로 두세요 + + + + ConfirmationDialog + + + + Ok + 확인 + + + + Cancel + 취소 + + + + DeclinePage + + + You must accept the Terms and Conditions in order to use openpilot. + openpilot을 사용하려면 이용 약관에 동의해야 합니다. + + + + Back + 뒤로 + + + + Decline, uninstall %1 + 거절, %1 제거 + + + + DevicePanel + + + Dongle ID + Dongle ID + + + + N/A + N/A + + + + Serial + Serial + + + + Driver Camera + 운전자 카메라 + + + + PREVIEW + 미리보기 + + + + Preview the driver facing camera to ensure that driver monitoring has good visibility. (vehicle must be off) + 운전자 모니터링이 좋은 가시성을 갖도록 운전자를 향한 카메라를 미리 봅니다. (차량연결은 해제되어있어야 합니다) + + + + Reset Calibration + 캘리브레이션 재설정 + + + + RESET + 재설정 + + + + Are you sure you want to reset calibration? + 캘리브레이션을 재설정하시겠습니까? + + + + Review Training Guide + 트레이닝 가이드 다시보기 + + + + REVIEW + 다시보기 + + + + Review the rules, features, and limitations of openpilot + openpilot의 규칙, 기능 및 제한 다시보기 + + + + Are you sure you want to review the training guide? + 트레이닝 가이드를 다시보시겠습니까? + + + + Regulatory + 규제 + + + + VIEW + 보기 + + + + Change Language + 언어 변경 + + + + CHANGE + 변경 + + + + Select a language + 언어를 선택하세요 + + + + Reboot + 재부팅 + + + + Power Off + 전원 종료 + + + + openpilot requires the device to be mounted within 4° left or right and within 5° up or 8° down. openpilot is continuously calibrating, resetting is rarely required. + openpilot은 장치를 좌측 또는 우측은 4° 이내, 위쪽 5° 또는 아래쪽은 8° 이내로 설치해야 합니다. openpilot은 지속적으로 보정되므로 리셋이 거의 필요하지 않습니다. + + + + Your device is pointed %1° %2 and %3° %4. + 사용자의 장치가 %1° %2 및 %3° %4를 가리키고 있습니다. + + + + down + 아래로 + + + + up + 위로 + + + + left + 좌측으로 + + + + right + 우측으로 + + + + Are you sure you want to reboot? + 재부팅 하시겠습니까? + + + + Disengage to Reboot + 재부팅 하려면 해제하세요 + + + + Are you sure you want to power off? + 전원을 종료하시겠습니까? + + + + Disengage to Power Off + 전원을 종료하려면 해제하세요 + + + + DriveStats + + + Drives + 주행 + + + + Hours + 시간 + + + + ALL TIME + 전체 + + + + PAST WEEK + 지난주 + + + + KM + Km + + + + Miles + Miles + + + + DriverViewScene + + + camera starting + 카메라 시작중 + + + + InputDialog + + + Cancel + 취소 + + + + Need at least %1 characters! + 최소 %1 자가 필요합니다! + + + + Installer + + + Installing... + 설치중... + + + + Receiving objects: + 수신중: + + + + Resolving deltas: + 델타병합: + + + + Updating files: + 파일갱신: + + + + MapETA + + + eta + 도착 + + + + min + + + + + hr + 시간 + + + + km + km + + + + mi + mi + + + + MapInstructions + + + km + km + + + + m + m + + + + mi + mi + + + + ft + ft + + + + MapPanel + + + Current Destination + 현재 목적지 + + + + CLEAR + 삭제 + + + + Recent Destinations + 최근 목적지 + + + + Try the Navigation Beta + 네비게이션(베타)를 사용해보세요 + + + + Get turn-by-turn directions displayed and more with a comma +prime subscription. Sign up now: https://connect.comma.ai + 자세한 경로안내를 원하시면 comma prime을 구독하세요. +등록:https://connect.comma.ai + + + + No home +location set + 집 +설정되지않음 + + + + No work +location set + 회사 +설정되지않음 + + + + no recent destinations + 최근 목적지 없음 + + + + MapWindow + + + Map Loading + 지도 로딩 + + + + Waiting for GPS + GPS를 기다리는 중 + + + + MultiOptionDialog + + + Select + 선택 + + + + Cancel + 취소 + + + + Networking + + + Advanced + 고급 설정 + + + + Enter password + 비밀번호를 입력하세요 + + + + + for "%1" + 하기위한 "%1" + + + + Wrong password + 비밀번호가 틀렸습니다 + + + + NvgWindow + + + km/h + km/h + + + + mph + mph + + + + + MAX + MAX + + + + + SPEED + SPEED + + + + + LIMIT + LIMIT + + + + OffroadHome + + + UPDATE + 업데이트 + + + + ALERTS + 알림 + + + + ALERT + 알림 + + + + PairingPopup + + + Pair your device to your comma account + 장치를 콤마 계정과 페어링합니다 + + + + Go to https://connect.comma.ai on your phone + https://connect.comma.ai에 접속하세요 + + + + Click "add new device" and scan the QR code on the right + "새 장치 추가"를 클릭하고 오른쪽 QR 코드를 검색합니다 + + + + Bookmark connect.comma.ai to your home screen to use it like an app + connect.comma.ai을 앱처럼 사용하려면 홈 화면에 바로가기를 만드십시오 + + + + PrimeAdWidget + + + Upgrade Now + 지금 업그레이드 + + + + Become a comma prime member at connect.comma.ai + connect.comma.ai에서 comma prime에 가입합니다 + + + + PRIME FEATURES: + PRIME 기능: + + + + Remote access + 원격 접속 + + + + 1 year of storage + 1년간 저장 + + + + Developer perks + 개발자 혜택 + + + + PrimeUserWidget + + + ✓ SUBSCRIBED + ✓ 구독함 + + + + comma prime + comma prime + + + + CONNECT.COMMA.AI + CONNECT.COMMA.AI + + + + COMMA POINTS + COMMA POINTS + + + + QObject + + + Reboot + 재부팅 + + + + Exit + 종료 + + + + dashcam + dashcam + + + + openpilot + openpilot + + + + %1 minute%2 ago + %1 분%2 전 + + + + %1 hour%2 ago + %1 시간%2 전 + + + + %1 day%2 ago + %1 일%2 전 + + + + Reset + + + Reset failed. Reboot to try again. + 초기화 실패. 재부팅후 다시 시도하세요. + + + + Are you sure you want to reset your device? + 장치를 초기화 하시겠습니까? + + + + Resetting device... + 장치 초기화중... + + + + System Reset + 장치 초기화 + + + + System reset triggered. Press confirm to erase all content and settings. Press cancel to resume boot. + 장치를 초기화 합니다. 확인버튼을 누르면 모든 내용과 설정이 초기화됩니다. 취소를 누르면 다시 부팅합니다. + + + + Cancel + 취소 + + + + Reboot + 재부팅 + + + + Confirm + 확인 + + + + Unable to mount data partition. Press confirm to reset your device. + 데이터 파티션을 마운트할 수 없습니다. 확인 버튼을 눌러 장치를 리셋합니다. + + + + RichTextDialog + + + Ok + 확인 + + + + SettingsWindow + + + × + × + + + + Device + 장치 + + + + + Network + 네트워크 + + + + Toggles + 토글 + + + + Software + 소프트웨어 + + + + Navigation + 네비게이션 + + + + Setup + + + WARNING: Low Voltage + 경고: 전압이 낮습니다 + + + + Power your device in a car with a harness or proceed at your own risk. + 하네스 보드에 차량의 전원을 연결하세요. + + + + Power off + 전원 종료 + + + + + + Continue + 계속 + + + + Getting Started + 설정 시작 + + + + Before we get on the road, let’s finish installation and cover some details. + 출발하기 전에 설정을 완료하고 몇 가지 세부 사항을 살펴보겠습니다. + + + + Connect to Wi-Fi + wifi 연결 + + + + + Back + 뒤로 + + + + Continue without Wi-Fi + wifi 없이 계속 + + + + Waiting for internet + 네트워크 접속을 기다립니다 + + + + Choose Software to Install + 설치할 소프트웨어를 선택하세요 + + + + Dashcam + Dashcam + + + + Custom Software + Custom Software + + + + Enter URL + URL 입력 + + + + for Custom Software + for Custom Software + + + + Downloading... + 다운로드중... + + + + Download Failed + 다운로드 실패 + + + + Ensure the entered URL is valid, and the device’s internet connection is good. + 입력된 URL이 유효하고 장치의 인터넷 연결이 잘 되어 있는지 확인합니다. + + + + Reboot device + 재부팅 + + + + Start over + 다시 시작 + + + + SetupWidget + + + Finish Setup + 설정 완료 + + + + Pair your device with comma connect (connect.comma.ai) and claim your comma prime offer. + 장치를 (connect.comma.ai)에서 페어링하고 comma prime 오퍼를 청구합니다. + + + + Pair device + 장치 페어링 + + + + Sidebar + + + + CONNECT + 연결 + + + + OFFLINE + 오프라인 + + + + + ONLINE + 온라인 + + + + ERROR + 오류 + + + + + + TEMP + 온도 + + + + HIGH + 높음 + + + + GOOD + 좋음 + + + + OK + 경고 + + + + VEHICLE + 차량 + + + + NO + NO + + + + PANDA + PANDA + + + + GPS + GPS + + + + SEARCH + 검색중 + + + + -- + -- + + + + Wi-Fi + Wi-Fi + + + + ETH + 이더넷 + + + + 2G + 2G + + + + 3G + 3G + + + + LTE + LTE + + + + 5G + 5G + + + + SoftwarePanel + + + Git Branch + Git 브렌치 + + + + Git Commit + Git 커밋 + + + + OS Version + OS 버전 + + + + Version + 버전 + + + + Last Update Check + 최신 업데이트 검사 + + + + The last time openpilot successfully checked for an update. The updater only runs while the car is off. + 최근에 openpilot이 업데이트를 성공적으로 확인했습니다. 업데이트 프로그램은 차량 연결이 해제되었을때만 작동합니다. + + + + Check for Update + 업데이트 확인 + + + + CHECKING + 확인중 + + + + UNINSTALL + 제거 + + + + Uninstall %1 + 제거 %1 + + + + Are you sure you want to uninstall? + 제거하시겠습니까? + + + + failed to fetch update + 업데이트를 가져올수없습니다 + + + + + CHECK + 확인 + + + + SshControl + + + SSH Keys + SSH 키 + + + + Warning: This grants SSH access to all public keys in your GitHub settings. Never enter a GitHub username other than your own. A comma employee will NEVER ask you to add their GitHub username. + 경고:이렇게 하면 GitHub 설정의 모든 공용 키에 대한 SSH 액세스 권한이 부여됩니다. 자신의 사용자 이름이 아닌 GitHub 사용자 이름을 입력하지 마십시오. comma 직원은 GitHub 사용자 이름을 추가하도록 요청하지 않습니다. + + + + + ADD + 추가 + + + + Enter your GitHub username + GitHub 사용자 ID + + + + LOADING + 로딩 + + + + REMOVE + 제거 + + + + Username '%1' has no keys on GitHub + '%1'의 키가 GitHub에 없습니다 + + + + Request timed out + 요청 시간 초과 + + + + Username '%1' doesn't exist on GitHub + '%1'은 GitHub에 없습니다 + + + + SshToggle + + + Enable SSH + SSH 사용 + + + + TermsPage + + + Terms & Conditions + 약관 + + + + Decline + 거절 + + + + Scroll to accept + 허용하려면 아래로 스크롤하세요 + + + + Agree + 동의 + + + + TogglesPanel + + + Enable openpilot + openpilot 사용 + + + + Use the openpilot system for adaptive cruise control and lane keep driver assistance. Your attention is required at all times to use this feature. Changing this setting takes effect when the car is powered off. + 어댑티브 크루즈 컨트롤 및 차선 유지 운전자 보조를 위해 openpilot 시스템을 사용하십시오. 이 기능을 사용하려면 항상 주의를 기울여야 합니다. 이 설정을 변경하면 차량 전원이 꺼질 때 적용됩니다. + + + + Enable Lane Departure Warnings + 차선 이탈 경고 사용 + + + + Receive alerts to steer back into the lane when your vehicle drifts over a detected lane line without a turn signal activated while driving over 31 mph (50 km/h). + 차량이 50km/h(31mph) 이상의 속도로 주행하는 동안 방향 지시등이 활성화되지 않은 상태에서 감지된 차선 위를 주행할 경우 차선이탈 경고를 사용합니다. + + + + Enable Right-Hand Drive + 우측핸들 사용 + + + + Allow openpilot to obey left-hand traffic conventions and perform driver monitoring on right driver seat. + openpilot이 좌측 교통 규칙을 준수하고 우측 운전석에서 운전자 모니터링을 수행합니다. + + + + Use Metric System + 미터법 사용 + + + + Display speed in km/h instead of mph. + mph 대신 km/h로 속도를 표시합니다. + + + + Record and Upload Driver Camera + 운전자 카메라 녹화 및 업로드 + + + + Upload data from the driver facing camera and help improve the driver monitoring algorithm. + 운전자 카메라에서 데이터를 업로드하고 운전자 모니터링 알고리즘을 개선합니다. + + + + Disengage On Accelerator Pedal + 가속페달 조작시 해제 + + + + When enabled, pressing the accelerator pedal will disengage openpilot. + 활성화된 경우 가속 페달을 누르면 openpilot이 해제됩니다. + + + + Show ETA in 24h format + 24시간 형식으로 도착예정시간 표시 + + + + Use 24h format instead of am/pm + 오전/오후 대신 24시간 형식 사용 + + + + openpilot Longitudinal Control + openpilot Longitudinal Control + + + + openpilot will disable the car's radar and will take over control of gas and brakes. Warning: this disables AEB! + openpilot은 차량'의 레이더를 무력화시키고 가속페달과 브레이크의 제어를 인계받을 것이다. 경고: AEB를 비활성화합니다! + + + + Updater + + + Update Required + 업데이트 필요 + + + + An operating system update is required. Connect your device to Wi-Fi for the fastest update experience. The download size is approximately 1GB. + OS 업데이트가 필요합니다. 장치를 wifi에 연결하여 가장 빠른 업데이트 경험을 제공합니다. 다운로드 크기는 약 1GB입니다. + + + + Connect to Wi-Fi + wifi 연결 + + + + Install + 설치 + + + + Back + 뒤로 + + + + Loading... + 로딩중... + + + + Reboot + 재부팅 + + + + Update failed + 업데이트 실패 + + + + WifiUI + + + + Scanning for networks... + 네트워크 검색 중... + + + + CONNECTING... + 연결중... + + + + FORGET + 저장안함 + + + + Forget Wi-Fi Network "%1"? + wifi 네트워크 저장안함 "%1"? + + + diff --git a/selfdrive/ui/translations/main_zh-CHS.qm b/selfdrive/ui/translations/main_zh-CHS.qm new file mode 100644 index 000000000..eed52b277 Binary files /dev/null and b/selfdrive/ui/translations/main_zh-CHS.qm differ diff --git a/selfdrive/ui/translations/main_zh-CHS.ts b/selfdrive/ui/translations/main_zh-CHS.ts new file mode 100644 index 000000000..424488fc5 --- /dev/null +++ b/selfdrive/ui/translations/main_zh-CHS.ts @@ -0,0 +1,1272 @@ + + + + + AbstractAlert + + + Close + 关闭 + + + + Snooze Update + 暂停更新 + + + + Reboot and Update + 重启并更新 + + + + AdvancedNetworking + + + Back + 返回 + + + + Enable Tethering + 启用WiFi热点 + + + + Tethering Password + WiFi热点密码 + + + + + EDIT + 编辑 + + + + Enter new tethering password + 输入新的WiFi热点密码 + + + + IP Address + IP地址 + + + + Enable Roaming + 启用数据漫游 + + + + APN Setting + APN设置 + + + + Enter APN + 输入APN + + + + leave blank for automatic configuration + 留空以自动配置 + + + + ConfirmationDialog + + + + Ok + 好的 + + + + Cancel + 取消 + + + + DeclinePage + + + You must accept the Terms and Conditions in order to use openpilot. + 您必须接受条款和条件以使用openpilot。 + + + + Back + 返回 + + + + Decline, uninstall %1 + 拒绝并卸载%1 + + + + DevicePanel + + + Dongle ID + 设备ID(Dongle ID) + + + + N/A + N/A + + + + Serial + 序列号 + + + + Driver Camera + 驾驶员摄像头 + + + + PREVIEW + 预览 + + + + Preview the driver facing camera to ensure that driver monitoring has good visibility. (vehicle must be off) + 打开并预览驾驶员摄像头,以确保驾驶员监控具有良好视野。仅熄火时可用。 + + + + Reset Calibration + 重置设备校准 + + + + RESET + 重置 + + + + Are you sure you want to reset calibration? + 您确定要重置设备校准吗? + + + + Review Training Guide + 新手指南 + + + + REVIEW + 查看 + + + + Review the rules, features, and limitations of openpilot + 查看openpilot的使用规则,以及其功能和限制。 + + + + Are you sure you want to review the training guide? + 您确定要查看新手指南吗? + + + + Regulatory + 监管信息 + + + + VIEW + 查看 + + + + Change Language + 切换语言 + + + + CHANGE + 切换 + + + + Select a language + 选择语言 + + + + Reboot + 重启 + + + + Power Off + 关机 + + + + openpilot requires the device to be mounted within 4° left or right and within 5° up or 8° down. openpilot is continuously calibrating, resetting is rarely required. + openpilot要求设备安装的偏航角在左4°和右4°之间,俯仰角在上5°和下8°之间。一般来说,openpilot会持续更新校准,很少需要重置。 + + + + Your device is pointed %1° %2 and %3° %4. + 您的设备校准为%1° %2、%3° %4。 + + + + down + 朝下 + + + + up + 朝上 + + + + left + 朝左 + + + + right + 朝右 + + + + Are you sure you want to reboot? + 您确定要重新启动吗? + + + + Disengage to Reboot + 取消openpilot以重新启动 + + + + Are you sure you want to power off? + 您确定要关机吗? + + + + Disengage to Power Off + 取消openpilot以关机 + + + + DriveStats + + + Drives + 旅程数 + + + + Hours + 小时 + + + + ALL TIME + 全部 + + + + PAST WEEK + 过去一周 + + + + KM + 公里 + + + + Miles + 英里 + + + + DriverViewScene + + + camera starting + 正在启动相机 + + + + InputDialog + + + Cancel + 取消 + + + + Need at least %1 characters! + 至少需要 %1 个字符! + + + + Installer + + + Installing... + 正在安装…… + + + + Receiving objects: + 正在接收: + + + + Resolving deltas: + 正在处理: + + + + Updating files: + 正在更新文件: + + + + MapETA + + + eta + 埃塔 + + + + min + 分钟 + + + + hr + 小时 + + + + km + km + + + + mi + mi + + + + MapInstructions + + + km + km + + + + m + m + + + + mi + mi + + + + ft + ft + + + + MapPanel + + + Current Destination + 当前目的地 + + + + CLEAR + 清空 + + + + Recent Destinations + 最近目的地 + + + + Try the Navigation Beta + 试用导航测试版 + + + + Get turn-by-turn directions displayed and more with a comma +prime subscription. Sign up now: https://connect.comma.ai + 订阅comma prime以获取导航。 +立即注册:https://connect.comma.ai + + + + No home +location set + 家:未设定 + + + + No work +location set + 工作:未设定 + + + + no recent destinations + 无最近目的地 + + + + MapWindow + + + Map Loading + 地图加载中 + + + + Waiting for GPS + 等待 GPS + + + + MultiOptionDialog + + + Select + 选择 + + + + Cancel + 取消 + + + + Networking + + + Advanced + 高级 + + + + Enter password + 输入密码 + + + + + for "%1" + 网络名称:"%1" + + + + Wrong password + 密码错误 + + + + NvgWindow + + + km/h + km/h + + + + mph + mph + + + + + MAX + 最高定速 + + + + + SPEED + SPEED + + + + + LIMIT + LIMIT + + + + OffroadHome + + + UPDATE + 更新 + + + + ALERTS + 警报 + + + + ALERT + 警报 + + + + PairingPopup + + + Pair your device to your comma account + 将您的设备与comma账号配对 + + + + Go to https://connect.comma.ai on your phone + 在手机上访问 https://connect.comma.ai + + + + Click "add new device" and scan the QR code on the right + 点击“添加新设备”,扫描右侧二维码 + + + + Bookmark connect.comma.ai to your home screen to use it like an app + 将 connect.comma.ai 收藏到您的主屏幕,以便像应用程序一样使用它 + + + + PrimeAdWidget + + + Upgrade Now + 现在升级 + + + + Become a comma prime member at connect.comma.ai + 打开connect.comma.ai以注册comma prime会员 + + + + PRIME FEATURES: + comma prime特权: + + + + Remote access + 远程访问 + + + + 1 year of storage + 1年数据存储 + + + + Developer perks + 开发者福利 + + + + PrimeUserWidget + + + ✓ SUBSCRIBED + ✓ 已订阅 + + + + comma prime + comma prime + + + + CONNECT.COMMA.AI + CONNECT.COMMA.AI + + + + COMMA POINTS + COMMA POINTS点数 + + + + QObject + + + Reboot + 重启 + + + + Exit + 退出 + + + + dashcam + 行车记录仪 + + + + openpilot + openpilot + + + + %1 minute%2 ago + %1 分钟%2 前 + + + + %1 hour%2 ago + %1 小时%2 前 + + + + %1 day%2 ago + %1 天%2 前 + + + + Reset + + + Reset failed. Reboot to try again. + 重置失败。 重新启动以重试。 + + + + Are you sure you want to reset your device? + 您确定要重置您的设备吗? + + + + Resetting device... + 正在重置设备…… + + + + System Reset + 恢复出厂设置 + + + + System reset triggered. Press confirm to erase all content and settings. Press cancel to resume boot. + 已触发系统重置:确认以删除所有内容和设置。取消以正常启动设备。 + + + + Cancel + 取消 + + + + Reboot + 重启 + + + + Confirm + 确认 + + + + Unable to mount data partition. Press confirm to reset your device. + 无法挂载数据分区。 确认以重置您的设备。 + + + + RichTextDialog + + + Ok + 好的 + + + + SettingsWindow + + + × + × + + + + Device + 设备 + + + + + Network + 网络 + + + + Toggles + 设定 + + + + Software + 软件 + + + + Navigation + 导航 + + + + Setup + + + WARNING: Low Voltage + 警告:低电压 + + + + Power your device in a car with a harness or proceed at your own risk. + 请使用car harness线束为您的设备供电,或自行承担风险。 + + + + Power off + 关机 + + + + + + Continue + 继续 + + + + Getting Started + 开始设置 + + + + Before we get on the road, let’s finish installation and cover some details. + 开始旅程之前,让我们完成安装并介绍一些细节。 + + + + Connect to Wi-Fi + 连接到WiFi + + + + + Back + 返回 + + + + Continue without Wi-Fi + 不连接WiFi并继续 + + + + Waiting for internet + 等待网络连接 + + + + Choose Software to Install + 选择要安装的软件 + + + + Dashcam + Dashcam(行车记录仪) + + + + Custom Software + 自定义软件 + + + + Enter URL + 输入网址 + + + + for Custom Software + 以下载自定义软件 + + + + Downloading... + 正在下载…… + + + + Download Failed + 下载失败 + + + + Ensure the entered URL is valid, and the device’s internet connection is good. + 请确保互联网连接良好且输入的URL有效。 + + + + Reboot device + 重启设备 + + + + Start over + 重来 + + + + SetupWidget + + + Finish Setup + 完成设置 + + + + Pair your device with comma connect (connect.comma.ai) and claim your comma prime offer. + 将您的设备与comma connect (connect.comma.ai)配对并领取您的comma prime优惠。 + + + + Pair device + 配对设备 + + + + Sidebar + + + + CONNECT + CONNECT + + + + OFFLINE + 离线 + + + + + ONLINE + 在线 + + + + ERROR + 连接出错 + + + + + + TEMP + 设备温度 + + + + HIGH + 过热 + + + + GOOD + 良好 + + + + OK + 一般 + + + + VEHICLE + 车辆连接 + + + + NO + + + + + PANDA + PANDA + + + + GPS + GPS + + + + SEARCH + 搜索中 + + + + -- + -- + + + + Wi-Fi + Wi-Fi + + + + ETH + 以太网 + + + + 2G + 2G + + + + 3G + 3G + + + + LTE + LTE + + + + 5G + 5G + + + + SoftwarePanel + + + Git Branch + Git Branch + + + + Git Commit + Git Commit + + + + OS Version + 系统版本 + + + + Version + 软件版本 + + + + Last Update Check + 上次检查更新 + + + + The last time openpilot successfully checked for an update. The updater only runs while the car is off. + 上一次成功检查更新的时间。更新程序仅在汽车熄火时运行。 + + + + Check for Update + 检查更新 + + + + CHECKING + 正在检查更新 + + + + UNINSTALL + 卸载 + + + + Uninstall %1 + 卸载 %1 + + + + Are you sure you want to uninstall? + 您确定要卸载吗? + + + + failed to fetch update + 获取更新失败 + + + + + CHECK + 查看 + + + + SshControl + + + SSH Keys + SSH密钥 + + + + Warning: This grants SSH access to all public keys in your GitHub settings. Never enter a GitHub username other than your own. A comma employee will NEVER ask you to add their GitHub username. + 警告:这将授予SSH访问权限给您GitHub设置中的所有公钥。切勿输入您自己以外的GitHub用户名。comma员工永远不会要求您添加他们的GitHub用户名。 + + + + + ADD + 添加 + + + + Enter your GitHub username + 输入您的GitHub用户名 + + + + LOADING + 正在加载 + + + + REMOVE + 删除 + + + + Username '%1' has no keys on GitHub + 用户名“%1”在GitHub上没有密钥 + + + + Request timed out + 请求超时 + + + + Username '%1' doesn't exist on GitHub + GitHub上不存在用户名“%1” + + + + SshToggle + + + Enable SSH + 启用SSH + + + + TermsPage + + + Terms & Conditions + 条款和条件 + + + + Decline + 拒绝 + + + + Scroll to accept + 滑动以接受 + + + + Agree + 同意 + + + + TogglesPanel + + + Enable openpilot + 启用openpilot + + + + Use the openpilot system for adaptive cruise control and lane keep driver assistance. Your attention is required at all times to use this feature. Changing this setting takes effect when the car is powered off. + 使用openpilot进行自适应巡航和车道保持辅助。使用此功能时您必须时刻保持注意力。该设置的更改在熄火时生效。 + + + + Enable Lane Departure Warnings + 启用车道偏离警告 + + + + Receive alerts to steer back into the lane when your vehicle drifts over a detected lane line without a turn signal activated while driving over 31 mph (50 km/h). + 车速超过31mph(50km/h)时,若检测到车辆越过车道线且未打转向灯,系统将发出警告以提醒您返回车道。 + + + + Enable Right-Hand Drive + 启用右舵模式 + + + + Allow openpilot to obey left-hand traffic conventions and perform driver monitoring on right driver seat. + 允许openpilot遵守左侧交通惯例并在右侧驾驶座上执行驾驶员监控。 + + + + Use Metric System + 使用公制单位 + + + + Display speed in km/h instead of mph. + 显示车速时,以km/h代替mph。 + + + + Record and Upload Driver Camera + 录制并上传驾驶员摄像头 + + + + Upload data from the driver facing camera and help improve the driver monitoring algorithm. + 上传驾驶员摄像头的数据,帮助改进驾驶员监控算法。 + + + + Disengage On Accelerator Pedal + 踩油门时取消控制 + + + + When enabled, pressing the accelerator pedal will disengage openpilot. + 启用后,踩下油门踏板将取消openpilot。 + + + + Show ETA in 24h format + 以24小时格式显示预计到达时间 + + + + Use 24h format instead of am/pm + 使用24小时制代替am/pm + + + + openpilot Longitudinal Control + openpilot纵向控制 + + + + openpilot will disable the car's radar and will take over control of gas and brakes. Warning: this disables AEB! + openpilot将禁用车辆的雷达并接管油门和刹车的控制。警告:AEB将被禁用! + + + + Updater + + + Update Required + 需要更新 + + + + An operating system update is required. Connect your device to Wi-Fi for the fastest update experience. The download size is approximately 1GB. + 操作系统需要更新。请将您的设备连接到WiFi以获取更快的更新体验。下载大小约为1GB。 + + + + Connect to Wi-Fi + 连接到WiFi + + + + Install + 安装 + + + + Back + 返回 + + + + Loading... + 正在加载…… + + + + Reboot + 重启 + + + + Update failed + 更新失败 + + + + WifiUI + + + + Scanning for networks... + 正在扫描网络…… + + + + CONNECTING... + 正在连接…… + + + + FORGET + 忘记 + + + + Forget Wi-Fi Network "%1"? + 忘记WiFi网络 "%1"? + + + diff --git a/selfdrive/ui/translations/main_zh-CHT.qm b/selfdrive/ui/translations/main_zh-CHT.qm new file mode 100644 index 000000000..029332de2 Binary files /dev/null and b/selfdrive/ui/translations/main_zh-CHT.qm differ diff --git a/selfdrive/ui/translations/main_zh-CHT.ts b/selfdrive/ui/translations/main_zh-CHT.ts new file mode 100644 index 000000000..1eab75a6b --- /dev/null +++ b/selfdrive/ui/translations/main_zh-CHT.ts @@ -0,0 +1,1277 @@ + + + + + AbstractAlert + + + Close + 關閉 + + + + Snooze Update + 暫停更新 + + + + Reboot and Update + 重啟並更新 + + + + AdvancedNetworking + + + Back + 回上頁 + + + + Enable Tethering + 啟用網路分享 + + + + Tethering Password + 網路分享密碼 + + + + + EDIT + 編輯 + + + + Enter new tethering password + 輸入新的網路分享密碼 + + + + IP Address + IP 地址 + + + + Enable Roaming + 啟用漫遊 + + + + APN Setting + APN 設置 + + + + Enter APN + 輸入 APN + + + + leave blank for automatic configuration + 留空白將自動配置 + + + + ConfirmationDialog + + + + Ok + 確定 + + + + Cancel + 取消 + + + + DeclinePage + + + You must accept the Terms and Conditions in order to use openpilot. + 您必須先接受條款和條件才能使用 openpilot。 + + + + Back + 回上頁 + + + + Decline, uninstall %1 + 拒絕並卸載 %1 + + + + DevicePanel + + + Dongle ID + Dongle ID + + + + N/A + 無法使用 + + + + Serial + 序號 + + + + Driver Camera + 駕駛員攝像頭 + + + + PREVIEW + 預覽 + + + + Preview the driver facing camera to ensure that driver monitoring has good visibility. (vehicle must be off) + 預覽駕駛員監控鏡頭畫面,以確保其具有良好視野。僅在熄火時可用。 + + + + Reset Calibration + 重置校準 + + + + RESET + 重置 + + + + Are you sure you want to reset calibration? + 您確定要重置校準嗎? + + + + Review Training Guide + 觀看使用教學 + + + + REVIEW + 觀看 + + + + Review the rules, features, and limitations of openpilot + 觀看 openpilot 的使用規則、功能和限制 + + + + Are you sure you want to review the training guide? + 您確定要觀看使用教學嗎? + + + + Regulatory + 法規/監管 + + + + VIEW + 觀看 + + + + Change Language + 更改語言 + + + + CHANGE + 更改 + + + + Select a language + 選擇語言 + + + + Reboot + 重新啟動 + + + + Power Off + 關機 + + + + openpilot requires the device to be mounted within 4° left or right and within 5° up or 8° down. openpilot is continuously calibrating, resetting is rarely required. + openpilot 需要將裝置固定在左右偏差 4° 以內,朝上偏差 5° 以内或朝下偏差 8° 以内。鏡頭在後台會持續自動校準,很少有需要重置的情况。 + + + + Your device is pointed %1° %2 and %3° %4. + 你的設備目前朝%2 %1° 以及朝%4 %3° 。 + + + + down + + + + + up + + + + + left + + + + + right + + + + + Are you sure you want to reboot? + 您確定要重新啟動嗎? + + + + Disengage to Reboot + 請先取消控車才能重新啟動 + + + + Are you sure you want to power off? + 您確定您要關機嗎? + + + + Disengage to Power Off + 請先取消控車才能關機 + + + + DriveStats + + + Drives + 旅程 + + + + Hours + 小時 + + + + ALL TIME + 總共 + + + + PAST WEEK + 上周 + + + + KM + 公里 + + + + Miles + 英里 + + + + DriverViewScene + + + camera starting + 開啟相機中 + + + + InputDialog + + + Cancel + 取消 + + + + Need at least %1 characters! + 需要至少 %1 個字元! + + + + Installer + + + Installing... + 安裝中… + + + + Receiving objects: + 接收對象: + + + + Resolving deltas: + 分析差異: + + + + Updating files: + 更新檔案: + + + + MapETA + + + eta + 埃塔 + + + + min + 分鐘 + + + + hr + 小時 + + + + km + km + + + + mi + mi + + + + MapInstructions + + + km + km + + + + m + m + + + + mi + mi + + + + ft + ft + + + + MapPanel + + + Current Destination + 當前目的地 + + + + CLEAR + 清除 + + + + Recent Destinations + 最近目的地 + + + + Try the Navigation Beta + 試用導航功能 + + + + Get turn-by-turn directions displayed and more with a comma +prime subscription. Sign up now: https://connect.comma.ai + 成為 comma 高級會員來使用導航功能 +立即註冊:https://connect.comma.ai + + + + No home +location set + 未設定 +住家位置 + + + + No work +location set + 未設定 +工作位置 + + + + no recent destinations + 沒有最近的導航記錄 + + + + MapWindow + + + Map Loading + 地圖加載 + + + + Waiting for GPS + 等待 GPS + + + + MultiOptionDialog + + + Select + 選擇 + + + + Cancel + 取消 + + + + Networking + + + Advanced + 進階 + + + + Enter password + 輸入密碼 + + + + + for "%1" + 給 "%1" + + + + Wrong password + 密碼錯誤 + + + + NvgWindow + + + km/h + km/h + + + + mph + mph + + + + + MAX + 最高 + + + + + SPEED + 速度 + + + + + LIMIT + 速限 + + + + OffroadHome + + + UPDATE + 更新 + + + + ALERTS + 提醒 + + + + ALERT + 提醒 + + + + PairingPopup + + + Pair your device to your comma account + 將設備與您的 comma 帳號配對 + + + + Go to https://connect.comma.ai on your phone + 用手機連至 https://connect.comma.ai + + + + Click "add new device" and scan the QR code on the right + 點選 "add new device" 後掃描右邊的二維碼 + + + + Bookmark connect.comma.ai to your home screen to use it like an app + 將 connect.comma.ai 加入您的主屏幕,以便像手機 App 一樣使用它 + + + + PrimeAdWidget + + + Upgrade Now + 馬上升級 + + + + Become a comma prime member at connect.comma.ai + 成為 connect.comma.ai 的高級會員 + + + + PRIME FEATURES: + 高級會員特點: + + + + Remote access + 遠程訪問 + + + + 1 year of storage + 一年的雲端行車記錄 + + + + Developer perks + 開發者福利 + + + + PrimeUserWidget + + + ✓ SUBSCRIBED + ✓ 已訂閱 + + + + comma prime + comma 高級會員 + + + + CONNECT.COMMA.AI + CONNECT.COMMA.AI + + + + COMMA POINTS + COMMA 積分 + + + + QObject + + + Reboot + 重新啟動 + + + + Exit + 離開 + + + + dashcam + 行車記錄器 + + + + openpilot + openpilot + + + + %1 minute%2 ago + we don't need %2 + %1 分鐘前 + + + + %1 hour%2 ago + we don't need %2 + %1 小時前 + + + + %1 day%2 ago + we don't need %2 + %1 天前 + + + + Reset + + + Reset failed. Reboot to try again. + 重置失敗。請重新啟動後再試。 + + + + Are you sure you want to reset your device? + 您確定要重置你的設備嗎? + + + + Resetting device... + 重置設備中… + + + + System Reset + 系統重置 + + + + System reset triggered. Press confirm to erase all content and settings. Press cancel to resume boot. + 系統重置已觸發。請按確認刪除所有內容和設置。按取消恢復啟動。 + + + + Cancel + 取消 + + + + Reboot + 重新啟動 + + + + Confirm + 確認 + + + + Unable to mount data partition. Press confirm to reset your device. + 無法掛載數據分區。請按確認重置您的設備。 + + + + RichTextDialog + + + Ok + 確定 + + + + SettingsWindow + + + × + × + + + + Device + 設備 + + + + + Network + 網路 + + + + Toggles + 設定 + + + + Software + 軟體 + + + + Navigation + 導航 + + + + Setup + + + WARNING: Low Voltage + 警告:電壓過低 + + + + Power your device in a car with a harness or proceed at your own risk. + 請使用車上 harness 提供的電源,若繼續的話您需要自擔風險。 + + + + Power off + 關機 + + + + + + Continue + 繼續 + + + + Getting Started + 入門 + + + + Before we get on the road, let’s finish installation and cover some details. + 在我們上路之前,讓我們完成安裝並介紹一些細節。 + + + + Connect to Wi-Fi + 連接到無線網絡 + + + + + Back + 回上頁 + + + + Continue without Wi-Fi + 在沒有 Wi-Fi 的情況下繼續 + + + + Waiting for internet + 連接至網路中 + + + + Choose Software to Install + 選擇要安裝的軟體 + + + + Dashcam + 行車記錄器 + + + + Custom Software + 定制的軟體 + + + + Enter URL + 輸入網址 + + + + for Custom Software + 定制的軟體 + + + + Downloading... + 下載中… + + + + Download Failed + 下載失敗 + + + + Ensure the entered URL is valid, and the device’s internet connection is good. + 請確定您輸入的是有效的安裝網址,並且確定設備的網路連線狀態良好。 + + + + Reboot device + 重新啟動 + + + + Start over + 重新開始 + + + + SetupWidget + + + Finish Setup + 完成設置 + + + + Pair your device with comma connect (connect.comma.ai) and claim your comma prime offer. + 將您的設備與 comma connect (connect.comma.ai) 配對並領取您的 comma 高級會員優惠。 + + + + Pair device + 配對設備 + + + + Sidebar + + + + CONNECT + 雲端服務 + + + + OFFLINE + 已離線 + + + + + ONLINE + 已連線 + + + + ERROR + 錯誤 + + + + + + TEMP + 溫度 + + + + HIGH + 偏高 + + + + GOOD + 正常 + + + + OK + 一般 + + + + VEHICLE + 車輛通訊 + + + + NO + 未連線 + + + + PANDA + 車輛通訊 + + + + GPS + GPS + + + + SEARCH + 車輛通訊 + + + + -- + -- + + + + Wi-Fi + + + + + ETH + + + + + 2G + + + + + 3G + + + + + LTE + + + + + 5G + + + + + SoftwarePanel + + + Git Branch + Git 分支 + + + + Git Commit + Git 提交 + + + + OS Version + 系統版本 + + + + Version + 版本 + + + + Last Update Check + 上次檢查時間 + + + + The last time openpilot successfully checked for an update. The updater only runs while the car is off. + 上次成功檢查更新的時間。更新系統只會在車子熄火時執行。 + + + + Check for Update + 檢查更新 + + + + CHECKING + 檢查中 + + + + UNINSTALL + 卸載 + + + + Uninstall %1 + 卸載 %1 + + + + Are you sure you want to uninstall? + 您確定您要卸載嗎? + + + + failed to fetch update + 下載更新失敗 + + + + + CHECK + 檢查 + + + + SshControl + + + SSH Keys + SSH 密鑰 + + + + Warning: This grants SSH access to all public keys in your GitHub settings. Never enter a GitHub username other than your own. A comma employee will NEVER ask you to add their GitHub username. + 警告:這將授權給 GitHub 帳號中所有公鑰 SSH 訪問權限。切勿輸入非您自己的 GitHub 用戶名。comma 員工「永遠不會」要求您添加他們的 GitHub 用戶名。 + + + + + ADD + 新增 + + + + Enter your GitHub username + 請輸入您 GitHub 的用戶名 + + + + LOADING + 載入中 + + + + REMOVE + 移除 + + + + Username '%1' has no keys on GitHub + GitHub 用戶 '%1' 沒有設定任何密鑰 + + + + Request timed out + 請求超時 + + + + Username '%1' doesn't exist on GitHub + GitHub 用戶 '%1' 不存在 + + + + SshToggle + + + Enable SSH + 啟用 SSH 服務 + + + + TermsPage + + + Terms & Conditions + 條款和條件 + + + + Decline + 拒絕 + + + + Scroll to accept + 滑動至頁尾接受條款 + + + + Agree + 接受 + + + + TogglesPanel + + + Enable openpilot + 啟用 openpilot + + + + Use the openpilot system for adaptive cruise control and lane keep driver assistance. Your attention is required at all times to use this feature. Changing this setting takes effect when the car is powered off. + 使用 openpilot 的主動式巡航和車道保持功能,開啟後您需要持續集中注意力,設定變更在重新啟動車輛後生效。 + + + + Enable Lane Departure Warnings + 啟用車道偏離警告 + + + + Receive alerts to steer back into the lane when your vehicle drifts over a detected lane line without a turn signal activated while driving over 31 mph (50 km/h). + 車速在時速 50 公里 (31 英里) 以上且未打方向燈的情況下,如果偵測到車輛駛出目前車道線時,發出車道偏離警告。 + + + + Enable Right-Hand Drive + 啟用右駕模式 + + + + Allow openpilot to obey left-hand traffic conventions and perform driver monitoring on right driver seat. + openpilot 將對右側駕駛進行監控 (但仍遵守靠左駕的交通慣例)。 + + + + Use Metric System + 使用公制單位 + + + + Display speed in km/h instead of mph. + 啟用後,速度單位顯示將從 mp/h 改為 km/h。 + + + + Record and Upload Driver Camera + 記錄並上傳駕駛監控影像 + + + + Upload data from the driver facing camera and help improve the driver monitoring algorithm. + 上傳駕駛監控的錄像來協助我們提升駕駛監控的準確率。 + + + + Disengage On Accelerator Pedal + 油門取消控車 + + + + When enabled, pressing the accelerator pedal will disengage openpilot. + 啟用後,踩踏油門將會取消 openpilot 控制。 + + + + Show ETA in 24h format + 預計到達時間單位改用 24 小時制 + + + + Use 24h format instead of am/pm + 使用 24 小時制。(預設值為 12 小時制) + + + + openpilot Longitudinal Control + openpilot 縱向控制 + + + + openpilot will disable the car's radar and will take over control of gas and brakes. Warning: this disables AEB! + openpilot 將會關閉雷達訊號並接管油門和剎車的控制。注意:這也會關閉自動緊急煞車 (AEB) 系統! + + + + Updater + + + Update Required + 系統更新 + + + + An operating system update is required. Connect your device to Wi-Fi for the fastest update experience. The download size is approximately 1GB. + 設備的操作系統需要更新。請將您的設備連接到 Wi-Fi 以獲得最快的更新體驗。下載大小約為 1GB。 + + + + Connect to Wi-Fi + 連接到無線網絡 + + + + Install + 安裝 + + + + Back + 回上頁 + + + + Loading... + 載入中… + + + + Reboot + 重新啟動 + + + + Update failed + 更新失敗 + + + + WifiUI + + + + Scanning for networks... + 掃描無線網路中... + + + + CONNECTING... + 連線中... + + + + FORGET + 清除 + + + + Forget Wi-Fi Network "%1"? + 清除 Wi-Fi 網路 "%1"? + + + diff --git a/selfdrive/ui/ui b/selfdrive/ui/ui index 16ddbab05..c9f81c053 100755 --- a/selfdrive/ui/ui +++ b/selfdrive/ui/ui @@ -1,6 +1,5 @@ #!/bin/sh cd "$(dirname "$0")" export LD_LIBRARY_PATH="/system/lib64:$LD_LIBRARY_PATH" -export QT_PLUGIN_PATH="../../third_party/qt-plugins/$(uname -m)" export QT_DBL_CLICK_DIST=150 exec ./_ui diff --git a/selfdrive/ui/ui.cc b/selfdrive/ui/ui.cc index 985b0259d..7922714c1 100644 --- a/selfdrive/ui/ui.cc +++ b/selfdrive/ui/ui.cc @@ -10,7 +10,7 @@ #include "common/swaglog.h" #include "common/util.h" #include "common/watchdog.h" -#include "selfdrive/hardware/hw.h" +#include "system/hardware/hw.h" #define BACKLIGHT_DT 0.05 #define BACKLIGHT_TS 10.00 @@ -55,10 +55,13 @@ static void update_leads(UIState *s, const cereal::RadarState::Reader &radar_sta } static void update_line_data(const UIState *s, const cereal::ModelDataV2::XYZTData::Reader &line, - float y_off, float z_off, line_vertices_data *pvd, int max_idx, bool allow_invert=true) { + float y_off, float z_off, QPolygonF *pvd, int max_idx, bool allow_invert=true) { const auto line_x = line.getX(), line_y = line.getY(), line_z = line.getZ(); - std::vector left_points, right_points; + QPolygonF left_points, right_points; + left_points.reserve(max_idx + 1); + right_points.reserve(max_idx + 1); + for (int i = 0; i <= max_idx; i++) { QPointF left, right; bool l = calib_frame_to_full_frame(s, line_x[i], line_y[i] - y_off, line_z[i] + z_off, &left); @@ -69,19 +72,10 @@ static void update_line_data(const UIState *s, const cereal::ModelDataV2::XYZTDa continue; } left_points.push_back(left); - right_points.push_back(right); + right_points.push_front(right); } } - - pvd->cnt = 2 * left_points.size(); - assert(left_points.size() == right_points.size()); - assert(pvd->cnt <= std::size(pvd->v)); - - for (int left_idx = 0; left_idx < left_points.size(); left_idx++){ - int right_idx = 2 * left_points.size() - left_idx - 1; - pvd->v[left_idx] = left_points[left_idx]; - pvd->v[right_idx] = right_points[left_idx]; - } + *pvd = left_points + right_points; } static void update_model(UIState *s, const cereal::ModelDataV2::Reader &model) { @@ -114,7 +108,7 @@ static void update_model(UIState *s, const cereal::ModelDataV2::Reader &model) { max_distance = std::clamp((float)(lead_d - fmin(lead_d * 0.35, 10.)), 0.0f, max_distance); } max_idx = get_path_length_idx(model_position, max_distance); - update_line_data(s, model_position, scene.end_to_end ? 0.9 : 0.5, 1.22, &scene.track_vertices, max_idx, false); + update_line_data(s, model_position, 0.9, 1.22, &scene.track_vertices, max_idx, false); } static void update_sockets(UIState *s) { @@ -140,6 +134,7 @@ static void update_state(UIState *s) { scene.view_from_calib.v[i*3 + j] = view_from_calib(i,j); } } + scene.calibration_valid = sm["liveCalibration"].getLiveCalibration().getCalStatus() == 1; } if (s->worldObjectsVisible()) { if (sm.updated("modelV2")) { @@ -182,7 +177,7 @@ static void update_state(UIState *s) { } } } - if (Hardware::TICI() && sm.updated("wideRoadCameraState")) { + if (sm.updated("wideRoadCameraState")) { auto camera_state = sm["wideRoadCameraState"].getWideRoadCameraState(); float max_lines = 1618; @@ -221,8 +216,7 @@ void UIState::updateStatus() { if (scene.started) { status = STATUS_DISENGAGED; scene.started_frame = sm->frame; - scene.end_to_end = Params().getBool("EndToEndToggle"); - wide_camera = Hardware::TICI() ? Params().getBool("EnableWideCamera") : false; + wide_camera = Params().getBool("WideCameraOnly"); } started_prev = scene.started; emit offroadTransition(!scene.started); @@ -233,11 +227,11 @@ UIState::UIState(QObject *parent) : QObject(parent) { sm = std::make_unique>({ "modelV2", "controlsState", "liveCalibration", "radarState", "deviceState", "roadCameraState", "pandaStates", "carParams", "driverMonitoringState", "sensorEvents", "carState", "liveLocationKalman", - "wideRoadCameraState", "managerState", + "wideRoadCameraState", "managerState", "navInstruction", "navRoute", "gnssMeasurements", }); Params params; - wide_camera = Hardware::TICI() ? params.getBool("EnableWideCamera") : false; + wide_camera = params.getBool("WideCameraOnly"); prime_type = std::atoi(params.get("PrimeType").c_str()); // update timer @@ -252,7 +246,7 @@ void UIState::update() { updateStatus(); if (sm->frame % UI_FREQ == 0) { - watchdog_kick(); + watchdog_kick(nanos_since_boot()); } emit uiUpdate(*this); } diff --git a/selfdrive/ui/ui.h b/selfdrive/ui/ui.h index 51a1f6e21..1aee3df9a 100644 --- a/selfdrive/ui/ui.h +++ b/selfdrive/ui/ui.h @@ -8,6 +8,7 @@ #include #include #include +#include #include #include "cereal/messaging/messaging.h" @@ -22,10 +23,7 @@ const int footer_h = 280; const int UI_FREQ = 20; // Hz typedef cereal::CarControl::HUDControl::AudibleAlert AudibleAlert; -// TODO: this is also hardcoded in common/transformations/camera.py -// TODO: choose based on frame input size -const float y_offset = 150.0; -const float ZOOM = 2912.8; +const mat3 DEFAULT_CALIBRATION = {{ 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 1.0, 0.0, 0.0 }}; struct Alert { QString text1; @@ -54,7 +52,7 @@ struct Alert { return {"openpilot Unavailable", "Waiting for controls to start", "controlsWaiting", cereal::ControlsState::AlertSize::MID, AudibleAlert::NONE}; - } else if (controls_missing > CONTROLS_TIMEOUT) { + } else if (controls_missing > CONTROLS_TIMEOUT && !Hardware::PC()) { // car is started, but controls is lagging or died if (cs.getEnabled() && (controls_missing - CONTROLS_TIMEOUT) < 10) { return {"TAKE CONTROL IMMEDIATELY", "Controls Unresponsive", @@ -87,27 +85,23 @@ const QColor bg_colors [] = { [STATUS_ALERT] = QColor(0xC9, 0x22, 0x31, 0xf1), }; -typedef struct { - QPointF v[TRAJECTORY_SIZE * 2]; - int cnt; -} line_vertices_data; - typedef struct UIScene { - mat3 view_from_calib; + bool calibration_valid = false; + mat3 view_from_calib = DEFAULT_CALIBRATION; cereal::PandaState::PandaType pandaType; // modelV2 float lane_line_probs[4]; float road_edge_stds[2]; - line_vertices_data track_vertices; - line_vertices_data lane_line_vertices[4]; - line_vertices_data road_edge_vertices[2]; + QPolygonF track_vertices; + QPolygonF lane_line_vertices[4]; + QPolygonF road_edge_vertices[2]; // lead QPointF lead_vertices[2]; float light_sensor, accel_sensor, gyro_sensor; - bool started, ignition, is_metric, longitudinal_control, end_to_end; + bool started, ignition, is_metric, longitudinal_control; uint64_t started_frame; } UIScene; diff --git a/selfdrive/ui/watch3.cc b/selfdrive/ui/watch3.cc index c1d47d040..00d23ea97 100644 --- a/selfdrive/ui/watch3.cc +++ b/selfdrive/ui/watch3.cc @@ -19,8 +19,6 @@ int main(int argc, char *argv[]) { { QHBoxLayout *hlayout = new QHBoxLayout(); layout->addLayout(hlayout); - // TODO: make mapd output YUV - // hlayout->addWidget(new CameraViewWidget("navd", VISION_STREAM_MAP, false)); hlayout->addWidget(new CameraViewWidget("camerad", VISION_STREAM_ROAD, false)); } diff --git a/selfdrive/updated.py b/selfdrive/updated.py index 19dba9825..bdec383f5 100755 --- a/selfdrive/updated.py +++ b/selfdrive/updated.py @@ -37,10 +37,10 @@ from markdown_it import MarkdownIt from common.basedir import BASEDIR from common.params import Params -from selfdrive.hardware import TICI, HARDWARE -from selfdrive.swaglog import cloudlog +from system.hardware import AGNOS, HARDWARE +from system.swaglog import cloudlog from selfdrive.controls.lib.alertmanager import set_offroad_alert -from selfdrive.version import is_tested_branch +from system.version import is_tested_branch LOCK_FILE = os.getenv("UPDATER_LOCK_FILE", "/tmp/safe_staging_overlay.lock") STAGING_ROOT = os.getenv("UPDATER_STAGING_ROOT", "/data/safe_staging") @@ -265,7 +265,7 @@ def finalize_update(wait_helper: WaitTimeHelper) -> None: def handle_agnos_update(wait_helper: WaitTimeHelper) -> None: - from selfdrive.hardware.tici.agnos import flash_agnos_update, get_target_slot_number + from system.hardware.tici.agnos import flash_agnos_update, get_target_slot_number cur_version = HARDWARE.get_os_version() updated_version = run(["bash", "-c", r"unset AGNOS_VERSION && source launch_env.sh && \ @@ -281,7 +281,7 @@ def handle_agnos_update(wait_helper: WaitTimeHelper) -> None: cloudlog.info(f"Beginning background installation for AGNOS {updated_version}") set_offroad_alert("Offroad_NeosUpdate", True) - manifest_path = os.path.join(OVERLAY_MERGED, "selfdrive/hardware/tici/agnos.json") + manifest_path = os.path.join(OVERLAY_MERGED, "system/hardware/tici/agnos.json") target_slot_number = get_target_slot_number() flash_agnos_update(manifest_path, target_slot_number, cloudlog) set_offroad_alert("Offroad_NeosUpdate", False) @@ -328,7 +328,7 @@ def fetch_update(wait_helper: WaitTimeHelper) -> bool: ] cloudlog.info("git reset success: %s", '\n'.join(r)) - if TICI: + if AGNOS: handle_agnos_update(wait_helper) # Create the finalized, ready-to-swap update diff --git a/system/__init__.py b/system/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/selfdrive/camerad/SConscript b/system/camerad/SConscript similarity index 100% rename from selfdrive/camerad/SConscript rename to system/camerad/SConscript diff --git a/selfdrive/camerad/cameras/camera_common.cc b/system/camerad/cameras/camera_common.cc similarity index 83% rename from selfdrive/camerad/cameras/camera_common.cc rename to system/camerad/cameras/camera_common.cc index d3fd34f6e..04f313648 100644 --- a/selfdrive/camerad/cameras/camera_common.cc +++ b/system/camerad/cameras/camera_common.cc @@ -1,4 +1,4 @@ -#include "selfdrive/camerad/cameras/camera_common.h" +#include "system/camerad/cameras/camera_common.h" #include @@ -10,35 +10,36 @@ #include "libyuv.h" #include -#include "selfdrive/camerad/imgproc/utils.h" +#include "system/camerad/imgproc/utils.h" #include "common/clutil.h" #include "common/modeldata.h" #include "common/swaglog.h" #include "common/util.h" -#include "selfdrive/hardware/hw.h" +#include "system/hardware/hw.h" +#include "msm_media_info.h" #ifdef QCOM2 #include "CL/cl_ext_qcom.h" -#include "selfdrive/camerad/cameras/camera_qcom2.h" +#include "system/camerad/cameras/camera_qcom2.h" #else -#include "selfdrive/camerad/test/camera_test.h" +#include "system/camerad/test/camera_test.h" #endif ExitHandler do_exit; class Debayer { public: - Debayer(cl_device_id device_id, cl_context context, const CameraBuf *b, const CameraState *s) { + Debayer(cl_device_id device_id, cl_context context, const CameraBuf *b, const CameraState *s, int buf_width, int uv_offset) { char args[4096]; const CameraInfo *ci = &s->ci; hdr_ = ci->hdr; snprintf(args, sizeof(args), "-cl-fast-relaxed-math -cl-denorms-are-zero " "-DFRAME_WIDTH=%d -DFRAME_HEIGHT=%d -DFRAME_STRIDE=%d -DFRAME_OFFSET=%d " - "-DRGB_WIDTH=%d -DRGB_HEIGHT=%d -DRGB_STRIDE=%d " + "-DRGB_WIDTH=%d -DRGB_HEIGHT=%d -DRGB_STRIDE=%d -DYUV_STRIDE=%d -DUV_OFFSET=%d " "-DBAYER_FLIP=%d -DHDR=%d -DCAM_NUM=%d%s", ci->frame_width, ci->frame_height, ci->frame_stride, ci->frame_offset, - b->rgb_width, b->rgb_height, b->rgb_stride, + b->rgb_width, b->rgb_height, b->rgb_stride, buf_width, uv_offset, ci->bayer_flip, ci->hdr, s->camera_num, s->camera_num==1 ? " -DVIGNETTING" : ""); const char *cl_file = "cameras/real_debayer.cl"; cl_program prg_debayer = cl_program_from_file(context, device_id, cl_file, args); @@ -65,11 +66,10 @@ private: bool hdr_; }; -void CameraBuf::init(cl_device_id device_id, cl_context context, CameraState *s, VisionIpcServer * v, int frame_cnt, VisionStreamType init_rgb_type, VisionStreamType init_yuv_type, release_cb init_release_callback) { +void CameraBuf::init(cl_device_id device_id, cl_context context, CameraState *s, VisionIpcServer * v, int frame_cnt, VisionStreamType init_rgb_type, VisionStreamType init_yuv_type) { vipc_server = v; this->rgb_type = init_rgb_type; this->yuv_type = init_yuv_type; - this->release_callback = init_release_callback; const CameraInfo *ci = &s->ci; camera_state = s; @@ -89,23 +89,23 @@ void CameraBuf::init(cl_device_id device_id, cl_context context, CameraState *s, rgb_width = ci->frame_width; rgb_height = ci->frame_height; - if (!Hardware::TICI() && ci->bayer) { - // debayering does a 2x downscale - rgb_width = ci->frame_width / 2; - rgb_height = ci->frame_height / 2; - } - yuv_transform = get_model_yuv_transform(ci->bayer); vipc_server->create_buffers(rgb_type, UI_BUF_COUNT, true, rgb_width, rgb_height); rgb_stride = vipc_server->get_buffer(rgb_type)->stride; LOGD("created %d UI vipc buffers with size %dx%d", UI_BUF_COUNT, rgb_width, rgb_height); - vipc_server->create_buffers(yuv_type, YUV_BUFFER_COUNT, false, rgb_width, rgb_height); - LOGD("created %d YUV vipc buffers with size %dx%d", YUV_BUFFER_COUNT, rgb_width, rgb_height); + int nv12_width = VENUS_Y_STRIDE(COLOR_FMT_NV12, rgb_width); + int nv12_height = VENUS_Y_SCANLINES(COLOR_FMT_NV12, rgb_height); + assert(nv12_width == VENUS_UV_STRIDE(COLOR_FMT_NV12, rgb_width)); + assert(nv12_height/2 == VENUS_UV_SCANLINES(COLOR_FMT_NV12, rgb_height)); + size_t nv12_size = 2346 * nv12_width; // comes from v4l2_format.fmt.pix_mp.plane_fmt[0].sizeimage + size_t nv12_uv_offset = nv12_width * nv12_height; + vipc_server->create_buffers_with_sizes(yuv_type, YUV_BUFFER_COUNT, false, rgb_width, rgb_height, nv12_size, nv12_width, nv12_uv_offset); + LOGD("created %d YUV vipc buffers with size %dx%d", YUV_BUFFER_COUNT, nv12_width, nv12_height); if (ci->bayer) { - debayer = new Debayer(device_id, context, this, s); + debayer = new Debayer(device_id, context, this, s, nv12_width, nv12_uv_offset); } rgb2yuv = std::make_unique(context, device_id, rgb_width, rgb_height, rgb_stride); @@ -126,7 +126,7 @@ CameraBuf::~CameraBuf() { } bool CameraBuf::acquire() { - if (!safe_queue.try_pop(cur_buf_idx, 1)) return false; + if (!safe_queue.try_pop(cur_buf_idx, 50)) return false; if (camera_bufs_metadata[cur_buf_idx].frame_id == -1) { LOGE("no frame data? wtf"); @@ -168,9 +168,7 @@ bool CameraBuf::acquire() { } void CameraBuf::release() { - if (release_callback) { - release_callback((void*)camera_state, cur_buf_idx); - } + // Empty } void CameraBuf::queue(size_t buf_idx) { @@ -233,20 +231,28 @@ kj::Array get_raw_frame_image(const CameraBuf *b) { } static kj::Array yuv420_to_jpeg(const CameraBuf *b, int thumbnail_width, int thumbnail_height) { + int downscale = b->cur_yuv_buf->width / thumbnail_width; + assert(downscale * thumbnail_height == b->cur_yuv_buf->height); + int in_stride = b->cur_yuv_buf->stride; + // make the buffer big enough. jpeg_write_raw_data requires 16-pixels aligned height to be used. std::unique_ptr buf(new uint8_t[(thumbnail_width * ((thumbnail_height + 15) & ~15) * 3) / 2]); uint8_t *y_plane = buf.get(); uint8_t *u_plane = y_plane + thumbnail_width * thumbnail_height; uint8_t *v_plane = u_plane + (thumbnail_width * thumbnail_height) / 4; { - int result = libyuv::I420Scale( - b->cur_yuv_buf->y, b->rgb_width, b->cur_yuv_buf->u, b->rgb_width / 2, b->cur_yuv_buf->v, b->rgb_width / 2, - b->rgb_width, b->rgb_height, - y_plane, thumbnail_width, u_plane, thumbnail_width / 2, v_plane, thumbnail_width / 2, - thumbnail_width, thumbnail_height, libyuv::kFilterNone); - if (result != 0) { - LOGE("Generate YUV thumbnail failed."); - return {}; + // subsampled conversion from nv12 to yuv + for (int hy = 0; hy < thumbnail_height/2; hy++) { + for (int hx = 0; hx < thumbnail_width/2; hx++) { + int ix = hx * downscale + (downscale-1)/2; + int iy = hy * downscale + (downscale-1)/2; + y_plane[(hy*2 + 0)*thumbnail_width + (hx*2 + 0)] = b->cur_yuv_buf->y[(iy*2 + 0) * in_stride + ix*2 + 0]; + y_plane[(hy*2 + 0)*thumbnail_width + (hx*2 + 1)] = b->cur_yuv_buf->y[(iy*2 + 0) * in_stride + ix*2 + 1]; + y_plane[(hy*2 + 1)*thumbnail_width + (hx*2 + 0)] = b->cur_yuv_buf->y[(iy*2 + 1) * in_stride + ix*2 + 0]; + y_plane[(hy*2 + 1)*thumbnail_width + (hx*2 + 1)] = b->cur_yuv_buf->y[(iy*2 + 1) * in_stride + ix*2 + 1]; + u_plane[hy*thumbnail_width/2 + hx] = b->cur_yuv_buf->uv[iy*in_stride + ix*2 + 0]; + v_plane[hy*thumbnail_width/2 + hx] = b->cur_yuv_buf->uv[iy*in_stride + ix*2 + 1]; + } } } diff --git a/selfdrive/camerad/cameras/camera_common.h b/system/camerad/cameras/camera_common.h similarity index 91% rename from selfdrive/camerad/cameras/camera_common.h rename to system/camerad/cameras/camera_common.h index 85c55f4b0..4695d4e2c 100644 --- a/selfdrive/camerad/cameras/camera_common.h +++ b/system/camerad/cameras/camera_common.h @@ -9,12 +9,11 @@ #include "cereal/visionipc/visionbuf.h" #include "cereal/visionipc/visionipc.h" #include "cereal/visionipc/visionipc_server.h" -#include "selfdrive/camerad/transforms/rgb_to_yuv.h" +#include "system/camerad/transforms/rgb_to_yuv.h" #include "common/mat.h" #include "common/queue.h" #include "common/swaglog.h" -#include "common/visionimg.h" -#include "selfdrive/hardware/hw.h" +#include "system/hardware/hw.h" #define CAMERA_ID_IMX298 0 #define CAMERA_ID_IMX179 1 @@ -49,8 +48,6 @@ const bool env_disable_driver = getenv("DISABLE_DRIVER") != NULL; const bool env_debug_frames = getenv("DEBUG_FRAMES") != NULL; const bool env_log_raw_frames = getenv("LOG_RAW_FRAMES") != NULL; -typedef void (*release_cb)(void *cookie, int buf_idx); - typedef struct CameraInfo { uint32_t frame_width, frame_height; uint32_t frame_stride; @@ -86,11 +83,6 @@ typedef struct FrameMetadata { float processing_time; } FrameMetadata; -typedef struct CameraExpInfo { - int op_id; - float grey_frac; -} CameraExpInfo; - struct MultiCameraState; struct CameraState; class Debayer; @@ -109,7 +101,6 @@ private: SafeQueue safe_queue; int frame_buf_count; - release_cb release_callback; public: cl_command_queue q; @@ -125,7 +116,7 @@ public: CameraBuf() = default; ~CameraBuf(); - void init(cl_device_id device_id, cl_context context, CameraState *s, VisionIpcServer * v, int frame_cnt, VisionStreamType rgb_type, VisionStreamType yuv_type, release_cb release_callback=nullptr); + void init(cl_device_id device_id, cl_context context, CameraState *s, VisionIpcServer * v, int frame_cnt, VisionStreamType rgb_type, VisionStreamType yuv_type); bool acquire(); void release(); void queue(size_t buf_idx); diff --git a/selfdrive/camerad/cameras/camera_qcom2.cc b/system/camerad/cameras/camera_qcom2.cc similarity index 99% rename from selfdrive/camerad/cameras/camera_qcom2.cc rename to system/camerad/cameras/camera_qcom2.cc index 597085dd2..f001009b9 100644 --- a/selfdrive/camerad/cameras/camera_qcom2.cc +++ b/system/camerad/cameras/camera_qcom2.cc @@ -1,4 +1,4 @@ -#include "selfdrive/camerad/cameras/camera_qcom2.h" +#include "system/camerad/cameras/camera_qcom2.h" #include #include @@ -20,7 +20,7 @@ #include "media/cam_sensor_cmn_header.h" #include "media/cam_sync.h" #include "common/swaglog.h" -#include "selfdrive/camerad/cameras/sensor2_i2c.h" +#include "system/camerad/cameras/sensor2_i2c.h" // For debugging: // echo "4294967295" > /sys/module/cam_debug_util/parameters/debug_mdl @@ -74,7 +74,7 @@ const int ANALOG_GAIN_REC_IDX = 0x6; // 0.8x const int ANALOG_GAIN_MAX_IDX = 0xD; // 4.0x const int EXPOSURE_TIME_MIN = 2; // with HDR, fastest ss -const int EXPOSURE_TIME_MAX = 1618; // with HDR, slowest ss, 40ms +const int EXPOSURE_TIME_MAX = 0x0855; // with HDR, slowest ss, 40ms // ************** low level camera helpers **************** int do_cam_control(int fd, int op_code, void *handle, int size) { @@ -837,7 +837,6 @@ void cameras_init(VisionIpcServer *v, MultiCameraState *s, cl_device_id device_i s->road_cam.camera_init(s, v, CAMERA_ID_AR0231, 1, 20, device_id, ctx, VISION_STREAM_RGB_ROAD, VISION_STREAM_ROAD, !env_disable_road); s->wide_road_cam.camera_init(s, v, CAMERA_ID_AR0231, 0, 20, device_id, ctx, VISION_STREAM_RGB_WIDE_ROAD, VISION_STREAM_WIDE_ROAD, !env_disable_wide_road); - s->sm = new SubMaster({"driverState"}); s->pm = new PubMaster({"roadCameraState", "driverCameraState", "wideRoadCameraState", "thumbnail"}); } @@ -948,7 +947,6 @@ void cameras_close(MultiCameraState *s) { s->road_cam.camera_close(); s->wide_road_cam.camera_close(); - delete s->sm; delete s->pm; } @@ -1221,7 +1219,7 @@ static void ar0231_process_registers(MultiCameraState *s, CameraState *c, cereal framed.setTemperaturesC({temp_0, temp_1}); } -static void driver_cam_auto_exposure(CameraState *c, SubMaster &sm) { +static void driver_cam_auto_exposure(CameraState *c) { struct ExpRect {int x1, x2, x_skip, y1, y2, y_skip;}; const CameraBuf *b = &c->buf; static ExpRect rect = {96, 1832, 2, 242, 1148, 4}; @@ -1229,8 +1227,7 @@ static void driver_cam_auto_exposure(CameraState *c, SubMaster &sm) { } static void process_driver_camera(MultiCameraState *s, CameraState *c, int cnt) { - s->sm->update(0); - driver_cam_auto_exposure(c, *(s->sm)); + driver_cam_auto_exposure(c); MessageBuilder msg; auto framed = msg.initEvent().initDriverCameraState(); diff --git a/selfdrive/camerad/cameras/camera_qcom2.h b/system/camerad/cameras/camera_qcom2.h similarity index 97% rename from selfdrive/camerad/cameras/camera_qcom2.h rename to system/camerad/cameras/camera_qcom2.h index d869620e9..57fef8d49 100644 --- a/selfdrive/camerad/cameras/camera_qcom2.h +++ b/system/camerad/cameras/camera_qcom2.h @@ -6,7 +6,7 @@ #include -#include "selfdrive/camerad/cameras/camera_common.h" +#include "system/camerad/cameras/camera_common.h" #include "common/util.h" #define FRAME_BUF_COUNT 4 @@ -106,6 +106,5 @@ typedef struct MultiCameraState { CameraState wide_road_cam; CameraState driver_cam; - SubMaster *sm; PubMaster *pm; } MultiCameraState; diff --git a/selfdrive/camerad/cameras/real_debayer.cl b/system/camerad/cameras/real_debayer.cl similarity index 93% rename from selfdrive/camerad/cameras/real_debayer.cl rename to system/camerad/cameras/real_debayer.cl index dc6044ed5..fc9f41049 100644 --- a/selfdrive/camerad/cameras/real_debayer.cl +++ b/system/camerad/cameras/real_debayer.cl @@ -1,7 +1,5 @@ #define UV_WIDTH RGB_WIDTH / 2 #define UV_HEIGHT RGB_HEIGHT / 2 -#define U_OFFSET RGB_WIDTH * RGB_HEIGHT -#define V_OFFSET RGB_WIDTH * RGB_HEIGHT + UV_WIDTH * UV_HEIGHT #define RGB_TO_Y(r, g, b) ((((mul24(b, 13) + mul24(g, 65) + mul24(r, 33)) + 64) >> 7) + 16) #define RGB_TO_U(r, g, b) ((mul24(b, 56) - mul24(g, 37) - mul24(r, 19) + 0x8080) >> 8) @@ -141,17 +139,20 @@ __kernel void debayer10(const __global uchar * in, __global uchar * out) RGB_TO_Y(rgb_out[0].s0, rgb_out[0].s1, rgb_out[0].s2), RGB_TO_Y(rgb_out[1].s0, rgb_out[1].s1, rgb_out[1].s2) ); - vstore2(yy, 0, out + mad24(gid_y * 2, RGB_WIDTH, gid_x * 2)); + vstore2(yy, 0, out + mad24(gid_y * 2, YUV_STRIDE, gid_x * 2)); yy = (uchar2)( RGB_TO_Y(rgb_out[2].s0, rgb_out[2].s1, rgb_out[2].s2), RGB_TO_Y(rgb_out[3].s0, rgb_out[3].s1, rgb_out[3].s2) ); - vstore2(yy, 0, out + mad24(gid_y * 2 + 1, RGB_WIDTH, gid_x * 2)); + vstore2(yy, 0, out + mad24(gid_y * 2 + 1, YUV_STRIDE, gid_x * 2)); // write uvs const short ar = AVERAGE(rgb_out[0].s0, rgb_out[1].s0, rgb_out[2].s0, rgb_out[3].s0); const short ag = AVERAGE(rgb_out[0].s1, rgb_out[1].s1, rgb_out[2].s1, rgb_out[3].s1); const short ab = AVERAGE(rgb_out[0].s2, rgb_out[1].s2, rgb_out[2].s2, rgb_out[3].s2); - out[U_OFFSET + mad24(gid_y, UV_WIDTH, gid_x)] = RGB_TO_U(ar, ag, ab); - out[V_OFFSET + mad24(gid_y, UV_WIDTH, gid_x)] = RGB_TO_V(ar, ag, ab); + uchar2 uv = (uchar2)( + RGB_TO_U(ar, ag, ab), + RGB_TO_V(ar, ag, ab) + ); + vstore2(uv, 0, out + UV_OFFSET + mad24(gid_y, YUV_STRIDE, gid_x * 2)); } diff --git a/selfdrive/camerad/cameras/sensor2_i2c.h b/system/camerad/cameras/sensor2_i2c.h similarity index 90% rename from selfdrive/camerad/cameras/sensor2_i2c.h rename to system/camerad/cameras/sensor2_i2c.h index 650c17cdf..284347623 100644 --- a/selfdrive/camerad/cameras/sensor2_i2c.h +++ b/system/camerad/cameras/sensor2_i2c.h @@ -49,11 +49,15 @@ struct i2c_random_wr_payload init_array_ar0231[] = { {0x301A, 0x0018}, // RESET_REGISTER // CLOCK Settings + // input clock is 19.2 / 2 * 0x37 = 528 MHz + // pixclk is 528 / 6 = 88 MHz + // full roll time is 1000/(PIXCLK/(LINE_LENGTH_PCK*FRAME_LENGTH_LINES)) = 39.99 ms + // img roll time is 1000/(PIXCLK/(LINE_LENGTH_PCK*Y_OUTPUT_CONTROL)) = 22.85 ms {0x302A, 0x0006}, // VT_PIX_CLK_DIV {0x302C, 0x0001}, // VT_SYS_CLK_DIV {0x302E, 0x0002}, // PRE_PLL_CLK_DIV - {0x3030, 0x0032}, // PLL_MULTIPLIER - {0x3036, 0x000C}, // OP_WORD_CLK_DIV + {0x3030, 0x0037}, // PLL_MULTIPLIER + {0x3036, 0x000C}, // OP_PIX_CLK_DIV {0x3038, 0x0001}, // OP_SYS_CLK_DIV // FORMAT @@ -76,8 +80,8 @@ struct i2c_random_wr_payload init_array_ar0231[] = { {0x340C, 0x802}, // GPIO_HIDRV_EN | GPIO0_ISEL=2 // Readout timing - {0x300C, 0x07B9}, // LINE_LENGTH_PCK - {0x300A, 0x0652}, // FRAME_LENGTH_LINES + {0x300C, 0x0672}, // LINE_LENGTH_PCK (valid for 3-exposure HDR) + {0x300A, 0x0855}, // FRAME_LENGTH_LINES {0x3042, 0x0000}, // EXTRA_DELAY // Readout Settings @@ -117,6 +121,8 @@ struct i2c_random_wr_payload init_array_ar0231[] = { {0x100C, 0x0589}, // FINE_INTEGRATION_TIME2_MIN {0x100E, 0x07B1}, // FINE_INTEGRATION_TIME3_MIN {0x1010, 0x0139}, // FINE_INTEGRATION_TIME4_MIN + + // TODO: do these have to be lower than LINE_LENGTH_PCK? {0x3014, 0x08CB}, // FINE_INTEGRATION_TIME_ {0x321E, 0x0894}, // FINE_INTEGRATION_TIME2 diff --git a/selfdrive/camerad/imgproc/conv.cl b/system/camerad/imgproc/conv.cl similarity index 100% rename from selfdrive/camerad/imgproc/conv.cl rename to system/camerad/imgproc/conv.cl diff --git a/selfdrive/camerad/imgproc/pool.cl b/system/camerad/imgproc/pool.cl similarity index 100% rename from selfdrive/camerad/imgproc/pool.cl rename to system/camerad/imgproc/pool.cl diff --git a/selfdrive/camerad/imgproc/utils.cc b/system/camerad/imgproc/utils.cc similarity index 98% rename from selfdrive/camerad/imgproc/utils.cc rename to system/camerad/imgproc/utils.cc index a88b8f4bb..a7bbeb9e8 100644 --- a/selfdrive/camerad/imgproc/utils.cc +++ b/system/camerad/imgproc/utils.cc @@ -1,4 +1,4 @@ -#include "selfdrive/camerad/imgproc/utils.h" +#include "system/camerad/imgproc/utils.h" #include #include diff --git a/selfdrive/camerad/imgproc/utils.h b/system/camerad/imgproc/utils.h similarity index 100% rename from selfdrive/camerad/imgproc/utils.h rename to system/camerad/imgproc/utils.h diff --git a/selfdrive/camerad/include/media/cam_cpas.h b/system/camerad/include/media/cam_cpas.h similarity index 100% rename from selfdrive/camerad/include/media/cam_cpas.h rename to system/camerad/include/media/cam_cpas.h diff --git a/selfdrive/camerad/include/media/cam_defs.h b/system/camerad/include/media/cam_defs.h similarity index 100% rename from selfdrive/camerad/include/media/cam_defs.h rename to system/camerad/include/media/cam_defs.h diff --git a/selfdrive/camerad/include/media/cam_fd.h b/system/camerad/include/media/cam_fd.h similarity index 100% rename from selfdrive/camerad/include/media/cam_fd.h rename to system/camerad/include/media/cam_fd.h diff --git a/selfdrive/camerad/include/media/cam_icp.h b/system/camerad/include/media/cam_icp.h similarity index 100% rename from selfdrive/camerad/include/media/cam_icp.h rename to system/camerad/include/media/cam_icp.h diff --git a/selfdrive/camerad/include/media/cam_isp.h b/system/camerad/include/media/cam_isp.h similarity index 100% rename from selfdrive/camerad/include/media/cam_isp.h rename to system/camerad/include/media/cam_isp.h diff --git a/selfdrive/camerad/include/media/cam_isp_ife.h b/system/camerad/include/media/cam_isp_ife.h similarity index 100% rename from selfdrive/camerad/include/media/cam_isp_ife.h rename to system/camerad/include/media/cam_isp_ife.h diff --git a/selfdrive/camerad/include/media/cam_isp_vfe.h b/system/camerad/include/media/cam_isp_vfe.h similarity index 100% rename from selfdrive/camerad/include/media/cam_isp_vfe.h rename to system/camerad/include/media/cam_isp_vfe.h diff --git a/selfdrive/camerad/include/media/cam_jpeg.h b/system/camerad/include/media/cam_jpeg.h similarity index 100% rename from selfdrive/camerad/include/media/cam_jpeg.h rename to system/camerad/include/media/cam_jpeg.h diff --git a/selfdrive/camerad/include/media/cam_lrme.h b/system/camerad/include/media/cam_lrme.h similarity index 100% rename from selfdrive/camerad/include/media/cam_lrme.h rename to system/camerad/include/media/cam_lrme.h diff --git a/selfdrive/camerad/include/media/cam_req_mgr.h b/system/camerad/include/media/cam_req_mgr.h similarity index 100% rename from selfdrive/camerad/include/media/cam_req_mgr.h rename to system/camerad/include/media/cam_req_mgr.h diff --git a/selfdrive/camerad/include/media/cam_sensor.h b/system/camerad/include/media/cam_sensor.h similarity index 100% rename from selfdrive/camerad/include/media/cam_sensor.h rename to system/camerad/include/media/cam_sensor.h diff --git a/selfdrive/camerad/include/media/cam_sensor_cmn_header.h b/system/camerad/include/media/cam_sensor_cmn_header.h similarity index 100% rename from selfdrive/camerad/include/media/cam_sensor_cmn_header.h rename to system/camerad/include/media/cam_sensor_cmn_header.h diff --git a/selfdrive/camerad/include/media/cam_sync.h b/system/camerad/include/media/cam_sync.h similarity index 100% rename from selfdrive/camerad/include/media/cam_sync.h rename to system/camerad/include/media/cam_sync.h diff --git a/selfdrive/camerad/include/msm_cam_sensor.h b/system/camerad/include/msm_cam_sensor.h similarity index 100% rename from selfdrive/camerad/include/msm_cam_sensor.h rename to system/camerad/include/msm_cam_sensor.h diff --git a/selfdrive/camerad/include/msm_camsensor_sdk.h b/system/camerad/include/msm_camsensor_sdk.h similarity index 100% rename from selfdrive/camerad/include/msm_camsensor_sdk.h rename to system/camerad/include/msm_camsensor_sdk.h diff --git a/selfdrive/camerad/include/msmb_camera.h b/system/camerad/include/msmb_camera.h similarity index 100% rename from selfdrive/camerad/include/msmb_camera.h rename to system/camerad/include/msmb_camera.h diff --git a/selfdrive/camerad/include/msmb_isp.h b/system/camerad/include/msmb_isp.h similarity index 100% rename from selfdrive/camerad/include/msmb_isp.h rename to system/camerad/include/msmb_isp.h diff --git a/selfdrive/camerad/include/msmb_ispif.h b/system/camerad/include/msmb_ispif.h similarity index 100% rename from selfdrive/camerad/include/msmb_ispif.h rename to system/camerad/include/msmb_ispif.h diff --git a/selfdrive/camerad/main.cc b/system/camerad/main.cc similarity index 76% rename from selfdrive/camerad/main.cc rename to system/camerad/main.cc index f59e03f8f..c1f38f222 100644 --- a/selfdrive/camerad/main.cc +++ b/system/camerad/main.cc @@ -1,13 +1,13 @@ -#include "selfdrive/camerad/cameras/camera_common.h" +#include "system/camerad/cameras/camera_common.h" #include #include "common/params.h" #include "common/util.h" -#include "selfdrive/hardware/hw.h" +#include "system/hardware/hw.h" int main(int argc, char *argv[]) { - if (Hardware::TICI()) { + if (!Hardware::PC()) { int ret; ret = util::set_realtime_priority(53); assert(ret == 0); diff --git a/system/camerad/snapshot/__init__.py b/system/camerad/snapshot/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/selfdrive/camerad/snapshot/snapshot.py b/system/camerad/snapshot/snapshot.py similarity index 71% rename from selfdrive/camerad/snapshot/snapshot.py rename to system/camerad/snapshot/snapshot.py index 2e8f7093d..946c220a7 100755 --- a/selfdrive/camerad/snapshot/snapshot.py +++ b/system/camerad/snapshot/snapshot.py @@ -4,17 +4,16 @@ import time import numpy as np from PIL import Image -from typing import List import cereal.messaging as messaging -from cereal.visionipc.visionipc_pyx import VisionIpcClient, VisionStreamType # pylint: disable=no-name-in-module, import-error +from cereal.visionipc import VisionIpcClient, VisionStreamType from common.params import Params from common.realtime import DT_MDL -from selfdrive.hardware import TICI, PC +from system.hardware import PC from selfdrive.controls.lib.alertmanager import set_offroad_alert from selfdrive.manager.process_config import managed_processes -LM_THRESH = 120 # defined in selfdrive/camerad/imgproc/utils.h +LM_THRESH = 120 # defined in system/camerad/imgproc/utils.h VISION_STREAMS = { "roadCameraState": VisionStreamType.VISION_STREAM_ROAD, @@ -40,23 +39,19 @@ def yuv_to_rgb(y, u, v): [0.00000, -0.39465, 2.03211], [1.13983, -0.58060, 0.00000], ]) - rgb = np.dot(yuv, m) + rgb = np.dot(yuv, m).clip(0, 255) return rgb.astype(np.uint8) -def extract_image(buf, w, h): - y = np.array(buf[:w*h], dtype=np.uint8).reshape((h, w)) - u = np.array(buf[w*h: w*h+(w//2)*(h//2)], dtype=np.uint8).reshape((h//2, w//2)) - v = np.array(buf[w*h+(w//2)*(h//2):], dtype=np.uint8).reshape((h//2, w//2)) +def extract_image(buf, w, h, stride, uv_offset): + y = np.array(buf[:uv_offset], dtype=np.uint8).reshape((-1, stride))[:h, :w] + u = np.array(buf[uv_offset::2], dtype=np.uint8).reshape((-1, stride//2))[:h//2, :w//2] + v = np.array(buf[uv_offset+1::2], dtype=np.uint8).reshape((-1, stride//2))[:h//2, :w//2] return yuv_to_rgb(y, u, v) -def rois_in_focus(lapres: List[float]) -> float: - return sum(1. / len(lapres) for sharpness in lapres if sharpness >= LM_THRESH) - - -def get_snapshots(frame="roadCameraState", front_frame="driverCameraState", focus_perc_threshold=0.): +def get_snapshots(frame="roadCameraState", front_frame="driverCameraState"): sockets = [s for s in (frame, front_frame) if s is not None] sm = messaging.SubMaster(sockets) vipc_clients = {s: VisionIpcClient("camerad", VISION_STREAMS[s], True) for s in sockets} @@ -68,21 +63,14 @@ def get_snapshots(frame="roadCameraState", front_frame="driverCameraState", focu for client in vipc_clients.values(): client.connect(True) - # wait for focus - start_t = time.monotonic() - while time.monotonic() - start_t < 10: - sm.update(100) - if min(sm.rcv_frame.values()) > 1 and rois_in_focus(sm[frame].sharpnessScore) >= focus_perc_threshold: - break - # grab images rear, front = None, None if frame is not None: c = vipc_clients[frame] - rear = extract_image(c.recv(), c.width, c.height) + rear = extract_image(c.recv(), c.width, c.height, c.stride, c.uv_offset) if front_frame is not None: c = vipc_clients[front_frame] - front = extract_image(c.recv(), c.width, c.height) + front = extract_image(c.recv(), c.width, c.height, c.stride, c.uv_offset) return rear, front @@ -113,11 +101,9 @@ def snapshot(): if not PC: managed_processes['camerad'].start() - frame = "wideRoadCameraState" if TICI else "roadCameraState" + frame = "wideRoadCameraState" front_frame = "driverCameraState" if front_camera_allowed else None - focus_perc_threshold = 0. if TICI else 10 / 12. - - rear, front = get_snapshots(frame, front_frame, focus_perc_threshold) + rear, front = get_snapshots(frame, front_frame) finally: managed_processes['camerad'].stop() params.put_bool("IsTakingSnapshot", False) diff --git a/selfdrive/camerad/transforms/rgb_to_yuv.cc b/system/camerad/transforms/rgb_to_yuv.cc similarity index 96% rename from selfdrive/camerad/transforms/rgb_to_yuv.cc rename to system/camerad/transforms/rgb_to_yuv.cc index 63e032e2d..5e51579cf 100644 --- a/selfdrive/camerad/transforms/rgb_to_yuv.cc +++ b/system/camerad/transforms/rgb_to_yuv.cc @@ -1,4 +1,4 @@ -#include "selfdrive/camerad/transforms/rgb_to_yuv.h" +#include "system/camerad/transforms/rgb_to_yuv.h" #include #include diff --git a/selfdrive/camerad/transforms/rgb_to_yuv.cl b/system/camerad/transforms/rgb_to_yuv.cl similarity index 100% rename from selfdrive/camerad/transforms/rgb_to_yuv.cl rename to system/camerad/transforms/rgb_to_yuv.cl diff --git a/selfdrive/camerad/transforms/rgb_to_yuv.h b/system/camerad/transforms/rgb_to_yuv.h similarity index 100% rename from selfdrive/camerad/transforms/rgb_to_yuv.h rename to system/camerad/transforms/rgb_to_yuv.h diff --git a/selfdrive/camerad/transforms/rgb_to_yuv_test.cc b/system/camerad/transforms/rgb_to_yuv_test.cc similarity index 99% rename from selfdrive/camerad/transforms/rgb_to_yuv_test.cc rename to system/camerad/transforms/rgb_to_yuv_test.cc index c960d168d..2f909e3b7 100644 --- a/selfdrive/camerad/transforms/rgb_to_yuv_test.cc +++ b/system/camerad/transforms/rgb_to_yuv_test.cc @@ -30,7 +30,7 @@ #include #include "libyuv.h" -#include "selfdrive/camerad/transforms/rgb_to_yuv.h" +#include "system/camerad/transforms/rgb_to_yuv.h" #include "common/clutil.h" static inline double millis_since_boot() { diff --git a/selfdrive/clocksd/.gitignore b/system/clocksd/.gitignore similarity index 100% rename from selfdrive/clocksd/.gitignore rename to system/clocksd/.gitignore diff --git a/selfdrive/clocksd/SConscript b/system/clocksd/SConscript similarity index 100% rename from selfdrive/clocksd/SConscript rename to system/clocksd/SConscript diff --git a/selfdrive/clocksd/clocksd.cc b/system/clocksd/clocksd.cc similarity index 100% rename from selfdrive/clocksd/clocksd.cc rename to system/clocksd/clocksd.cc diff --git a/selfdrive/hardware/__init__.py b/system/hardware/__init__.py similarity index 51% rename from selfdrive/hardware/__init__.py rename to system/hardware/__init__.py index 03dfce86d..77bb0e5e2 100644 --- a/selfdrive/hardware/__init__.py +++ b/system/hardware/__init__.py @@ -1,11 +1,12 @@ import os from typing import cast -from selfdrive.hardware.base import HardwareBase -from selfdrive.hardware.tici.hardware import Tici -from selfdrive.hardware.pc.hardware import Pc +from system.hardware.base import HardwareBase +from system.hardware.tici.hardware import Tici +from system.hardware.pc.hardware import Pc TICI = os.path.isfile('/TICI') +AGNOS = os.path.isfile('/AGNOS') PC = not TICI diff --git a/selfdrive/hardware/base.h b/system/hardware/base.h similarity index 63% rename from selfdrive/hardware/base.h rename to system/hardware/base.h index 2826d0970..b70948d48 100644 --- a/selfdrive/hardware/base.h +++ b/system/hardware/base.h @@ -2,6 +2,7 @@ #include #include +#include "cereal/messaging/messaging.h" // no-op base hw class class HardwareNone { @@ -10,6 +11,10 @@ public: static constexpr float MIN_VOLUME = 0.2; static std::string get_os_version() { return ""; } + static std::string get_name() { return ""; }; + static cereal::InitData::DeviceType get_device_type() { return cereal::InitData::DeviceType::UNKNOWN; }; + static int get_voltage() { return 0; }; + static int get_current() { return 0; }; static void reboot() {} static void poweroff() {} @@ -21,4 +26,5 @@ public: static bool PC() { return false; } static bool TICI() { return false; } + static bool AGNOS() { return false; } }; diff --git a/selfdrive/hardware/base.py b/system/hardware/base.py similarity index 97% rename from selfdrive/hardware/base.py rename to system/hardware/base.py index 8684386f6..b052a48c5 100644 --- a/selfdrive/hardware/base.py +++ b/system/hardware/base.py @@ -86,6 +86,10 @@ class HardwareBase(ABC): def get_current_power_draw(self): pass + @abstractmethod + def get_som_power_draw(self): + pass + @abstractmethod def shutdown(self): pass diff --git a/selfdrive/hardware/hw.h b/system/hardware/hw.h similarity index 74% rename from selfdrive/hardware/hw.h rename to system/hardware/hw.h index 5f178ec0f..f50e94abe 100644 --- a/selfdrive/hardware/hw.h +++ b/system/hardware/hw.h @@ -1,17 +1,20 @@ #pragma once -#include "selfdrive/hardware/base.h" +#include "system/hardware/base.h" #include "common/util.h" #if QCOM2 -#include "selfdrive/hardware/tici/hardware.h" +#include "system/hardware/tici/hardware.h" #define Hardware HardwareTici #else class HardwarePC : public HardwareNone { public: static std::string get_os_version() { return "openpilot for PC"; } + static std::string get_name() { return "pc"; }; + static cereal::InitData::DeviceType get_device_type() { return cereal::InitData::DeviceType::PC; }; static bool PC() { return true; } static bool TICI() { return util::getenv("TICI", 0) == 1; } + static bool AGNOS() { return util::getenv("TICI", 0) == 1; } }; #define Hardware HardwarePC #endif diff --git a/system/hardware/pc/__init__.py b/system/hardware/pc/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/selfdrive/hardware/pc/hardware.py b/system/hardware/pc/hardware.py similarity index 93% rename from selfdrive/hardware/pc/hardware.py rename to system/hardware/pc/hardware.py index 2f5db925a..60d14e4a6 100644 --- a/selfdrive/hardware/pc/hardware.py +++ b/system/hardware/pc/hardware.py @@ -1,7 +1,7 @@ import random from cereal import log -from selfdrive.hardware.base import HardwareBase, ThermalConfig +from system.hardware.base import HardwareBase, ThermalConfig NetworkType = log.DeviceState.NetworkType NetworkStrength = log.DeviceState.NetworkStrength @@ -55,6 +55,9 @@ class Pc(HardwareBase): def get_current_power_draw(self): return 0 + + def get_som_power_draw(self): + return 0 def shutdown(self): print("SHUTDOWN!") diff --git a/system/hardware/tici/__init__.py b/system/hardware/tici/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/selfdrive/hardware/tici/agnos.json b/system/hardware/tici/agnos.json similarity index 70% rename from selfdrive/hardware/tici/agnos.json rename to system/hardware/tici/agnos.json index 6674ddaa5..7ccea95ee 100644 --- a/selfdrive/hardware/tici/agnos.json +++ b/system/hardware/tici/agnos.json @@ -1,10 +1,10 @@ [ { "name": "boot", - "url": "https://commadist.azureedge.net/agnosupdate/boot-0a86272196cb54607f8ba082f412fed4607baaf6ce3eb8dc8e950b4a1d763954.img.xz", - "hash": "0a86272196cb54607f8ba082f412fed4607baaf6ce3eb8dc8e950b4a1d763954", - "hash_raw": "0a86272196cb54607f8ba082f412fed4607baaf6ce3eb8dc8e950b4a1d763954", - "size": 14776320, + "url": "https://commadist.azureedge.net/agnosupdate/boot-243ddbb9e2256aa7af7fed0daf8cff4017a3c838c759373a634b8539f271bfb8.img.xz", + "hash": "243ddbb9e2256aa7af7fed0daf8cff4017a3c838c759373a634b8539f271bfb8", + "hash_raw": "243ddbb9e2256aa7af7fed0daf8cff4017a3c838c759373a634b8539f271bfb8", + "size": 14780416, "sparse": false, "full_check": true, "has_ab": true @@ -41,9 +41,9 @@ }, { "name": "system", - "url": "https://commadist.azureedge.net/agnosupdate/system-9b0b534ed0c35c25850dbb73d3f052611f2e2c9db32410edc25d75fbcfc6c15e.img.xz", - "hash": "b1f622f00037bbdb28c0a6016c0e42fa7f87e99591ff2417c757a67ff559b526", - "hash_raw": "9b0b534ed0c35c25850dbb73d3f052611f2e2c9db32410edc25d75fbcfc6c15e", + "url": "https://commadist.azureedge.net/agnosupdate/system-59622eddd068d49f2e9df69ef5115e3f205ad369539690a5b240c8c93796dd13.img.xz", + "hash": "44da205d17b44b2be7c94854a6bb3efb2928ec9a9889fe62af8b322d2295b74f", + "hash_raw": "59622eddd068d49f2e9df69ef5115e3f205ad369539690a5b240c8c93796dd13", "size": 10737418240, "sparse": true, "full_check": false, diff --git a/selfdrive/hardware/tici/agnos.py b/system/hardware/tici/agnos.py similarity index 71% rename from selfdrive/hardware/tici/agnos.py rename to system/hardware/tici/agnos.py index e664654c6..ca2498a00 100755 --- a/selfdrive/hardware/tici/agnos.py +++ b/system/hardware/tici/agnos.py @@ -1,15 +1,19 @@ #!/usr/bin/env python3 +import hashlib import json import lzma -import hashlib -import requests +import os import struct import subprocess import time -import os -from typing import Dict, Generator, Union +from typing import Dict, Generator, List, Tuple, Union + +import requests + +import system.hardware.tici.casync as casync SPARSE_CHUNK_FMT = struct.Struct('H2xI4x') +CAIBX_URL = "https://commadist.azureedge.net/agnosupdate/" class StreamingDecompressor: @@ -74,6 +78,7 @@ def unsparsify(f: StreamingDecompressor) -> Generator[bytes, None, None]: else: raise Exception("Unhandled sparse chunk type") + # noop wrapper with same API as unsparsify() for non sparse images def noop(f: StreamingDecompressor) -> Generator[bytes, None, None]: while not f.eof: @@ -99,28 +104,37 @@ def get_partition_path(target_slot_number: int, partition: dict) -> str: return path -def verify_partition(target_slot_number: int, partition: Dict[str, Union[str, int]]) -> bool: - full_check = partition['full_check'] +def get_raw_hash(path: str, partition_size: int) -> str: + raw_hash = hashlib.sha256() + pos, chunk_size = 0, 1024 * 1024 + + with open(path, 'rb+') as out: + while pos < partition_size: + n = min(chunk_size, partition_size - pos) + raw_hash.update(out.read(n)) + pos += n + + return raw_hash.hexdigest().lower() + + +def verify_partition(target_slot_number: int, partition: Dict[str, Union[str, int]], force_full_check: bool = False) -> bool: + full_check = partition['full_check'] or force_full_check path = get_partition_path(target_slot_number, partition) + if not isinstance(partition['size'], int): return False + partition_size: int = partition['size'] if not isinstance(partition['hash_raw'], str): return False + partition_hash: str = partition['hash_raw'] - with open(path, 'rb+') as out: - if full_check: - raw_hash = hashlib.sha256() - pos, chunk_size = 0, 1024 * 1024 - while pos < partition_size: - n = min(chunk_size, partition_size - pos) - raw_hash.update(out.read(n)) - pos += n - - return raw_hash.hexdigest().lower() == partition_hash.lower() - else: + if full_check: + return get_raw_hash(path, partition_size) == partition_hash.lower() + else: + with open(path, 'rb+') as out: out.seek(partition_size) return out.read(64) == partition_hash.lower().encode() @@ -135,21 +149,10 @@ def clear_partition_hash(target_slot_number: int, partition: dict) -> None: os.sync() -def flash_partition(target_slot_number: int, partition: dict, cloudlog): - cloudlog.info(f"Downloading and writing {partition['name']}") - - if verify_partition(target_slot_number, partition): - cloudlog.info(f"Already flashed {partition['name']}") - return - +def extract_compressed_image(target_slot_number: int, partition: dict, cloudlog): + path = get_partition_path(target_slot_number, partition) downloader = StreamingDecompressor(partition['url']) - # Clear hash before flashing in case we get interrupted - full_check = partition['full_check'] - if not full_check: - clear_partition_hash(target_slot_number, partition) - - path = get_partition_path(target_slot_number, partition) with open(path, 'wb+') as out: # Flash partition last_p = 0 @@ -172,9 +175,76 @@ def flash_partition(target_slot_number: int, partition: dict, cloudlog): if out.tell() != partition['size']: raise Exception("Uncompressed size mismatch") - # Write hash after successfull flash os.sync() - if not full_check: + + +def extract_casync_image(target_slot_number: int, partition: dict, cloudlog): + path = get_partition_path(target_slot_number, partition) + seed_path = path[:-1] + ('b' if path[-1] == 'a' else 'a') + + target = casync.parse_caibx(partition['casync_caibx']) + + sources: List[Tuple[str, casync.ChunkReader, casync.ChunkDict]] = [] + + # First source is the current partition. + try: + raw_hash = get_raw_hash(seed_path, partition['size']) + caibx_url = f"{CAIBX_URL}{partition['name']}-{raw_hash}.caibx" + + try: + cloudlog.info(f"casync fetching {caibx_url}") + sources += [('seed', casync.FileChunkReader(seed_path), casync.build_chunk_dict(casync.parse_caibx(caibx_url)))] + except requests.RequestException: + cloudlog.error(f"casync failed to load {caibx_url}") + except Exception: + cloudlog.exception("casync failed to hash seed partition") + + # Second source is the target partition, this allows for resuming + sources += [('target', casync.FileChunkReader(path), casync.build_chunk_dict(target))] + + # Finally we add the remote source to download any missing chunks + sources += [('remote', casync.RemoteChunkReader(partition['casync_store']), casync.build_chunk_dict(target))] + + last_p = 0 + + def progress(cur): + nonlocal last_p + p = int(cur / partition['size'] * 100) + if p != last_p: + last_p = p + print(f"Installing {partition['name']}: {p}", flush=True) + + stats = casync.extract(target, sources, path, progress) + cloudlog.error(f'casync done {json.dumps(stats)}') + + os.sync() + if not verify_partition(target_slot_number, partition, force_full_check=True): + raise Exception(f"Raw hash mismatch '{partition['hash_raw'].lower()}'") + + +def flash_partition(target_slot_number: int, partition: dict, cloudlog, standalone=False): + cloudlog.info(f"Downloading and writing {partition['name']}") + + if verify_partition(target_slot_number, partition): + cloudlog.info(f"Already flashed {partition['name']}") + return + + # Clear hash before flashing in case we get interrupted + full_check = partition['full_check'] + if not full_check: + clear_partition_hash(target_slot_number, partition) + + path = get_partition_path(target_slot_number, partition) + + if ('casync_caibx' in partition) and not standalone: + extract_casync_image(target_slot_number, partition, cloudlog) + else: + extract_compressed_image(target_slot_number, partition, cloudlog) + + # Write hash after successfull flash + if not full_check: + with open(path, 'wb+') as out: + out.seek(partition['size']) out.write(partition['hash_raw'].lower().encode()) @@ -193,7 +263,7 @@ def swap(manifest_path: str, target_slot_number: int, cloudlog) -> None: cloudlog.error(f"Swap failed {out}") -def flash_agnos_update(manifest_path: str, target_slot_number: int, cloudlog) -> None: +def flash_agnos_update(manifest_path: str, target_slot_number: int, cloudlog, standalone=False) -> None: update = json.load(open(manifest_path)) cloudlog.info(f"Target slot {target_slot_number}") @@ -206,7 +276,7 @@ def flash_agnos_update(manifest_path: str, target_slot_number: int, cloudlog) -> for retries in range(10): try: - flash_partition(target_slot_number, partition, cloudlog) + flash_partition(target_slot_number, partition, cloudlog, standalone) success = True break @@ -228,8 +298,8 @@ def verify_agnos_update(manifest_path: str, target_slot_number: int) -> bool: if __name__ == "__main__": - import logging import argparse + import logging parser = argparse.ArgumentParser(description="Flash and verify AGNOS update", formatter_class=argparse.ArgumentDefaultsHelpFormatter) @@ -250,9 +320,9 @@ if __name__ == "__main__": elif args.swap: while not verify_agnos_update(args.manifest, target_slot_number): logging.error("Verification failed. Flashing AGNOS") - flash_agnos_update(args.manifest, target_slot_number, logging) + flash_agnos_update(args.manifest, target_slot_number, logging, standalone=True) logging.warning(f"Verification succeeded. Swapping to slot {target_slot_number}") swap(args.manifest, target_slot_number, logging) else: - flash_agnos_update(args.manifest, target_slot_number, logging) + flash_agnos_update(args.manifest, target_slot_number, logging, standalone=True) diff --git a/selfdrive/hardware/tici/amplifier.py b/system/hardware/tici/amplifier.py similarity index 100% rename from selfdrive/hardware/tici/amplifier.py rename to system/hardware/tici/amplifier.py diff --git a/system/hardware/tici/casync.py b/system/hardware/tici/casync.py new file mode 100755 index 000000000..9dff64239 --- /dev/null +++ b/system/hardware/tici/casync.py @@ -0,0 +1,198 @@ +#!/usr/bin/env python3 +import io +import lzma +import os +import struct +import sys +import time +from abc import ABC, abstractmethod +from collections import defaultdict, namedtuple +from typing import Callable, Dict, List, Optional, Tuple + +import requests +from Crypto.Hash import SHA512 + +CA_FORMAT_INDEX = 0x96824d9c7b129ff9 +CA_FORMAT_TABLE = 0xe75b9e112f17417d +CA_FORMAT_TABLE_TAIL_MARKER = 0xe75b9e112f17417 +FLAGS = 0xb000000000000000 + +CA_HEADER_LEN = 48 +CA_TABLE_HEADER_LEN = 16 +CA_TABLE_ENTRY_LEN = 40 +CA_TABLE_MIN_LEN = CA_TABLE_HEADER_LEN + CA_TABLE_ENTRY_LEN + +CHUNK_DOWNLOAD_TIMEOUT = 60 +CHUNK_DOWNLOAD_RETRIES = 3 + +CAIBX_DOWNLOAD_TIMEOUT = 120 + +Chunk = namedtuple('Chunk', ['sha', 'offset', 'length']) +ChunkDict = Dict[bytes, Chunk] + + +class ChunkReader(ABC): + @abstractmethod + def read(self, chunk: Chunk) -> bytes: + ... + + +class FileChunkReader(ChunkReader): + """Reads chunks from a local file""" + def __init__(self, fn: str) -> None: + + super().__init__() + self.f = open(fn, 'rb') + + def read(self, chunk: Chunk) -> bytes: + self.f.seek(chunk.offset) + return self.f.read(chunk.length) + + +class RemoteChunkReader(ChunkReader): + """Reads lzma compressed chunks from a remote store""" + + def __init__(self, url: str) -> None: + super().__init__() + self.url = url + self.session = requests.Session() + + def read(self, chunk: Chunk) -> bytes: + sha_hex = chunk.sha.hex() + url = os.path.join(self.url, sha_hex[:4], sha_hex + ".cacnk") + + for i in range(CHUNK_DOWNLOAD_RETRIES): + try: + resp = self.session.get(url, timeout=CHUNK_DOWNLOAD_TIMEOUT) + break + except Exception: + if i == CHUNK_DOWNLOAD_RETRIES - 1: + raise + time.sleep(CHUNK_DOWNLOAD_TIMEOUT) + + resp.raise_for_status() + + decompressor = lzma.LZMADecompressor(format=lzma.FORMAT_AUTO) + return decompressor.decompress(resp.content) + + +def parse_caibx(caibx_path: str) -> List[Chunk]: + """Parses the chunks from a caibx file. Can handle both local and remote files. + Returns a list of chunks with hash, offset and length""" + if os.path.isfile(caibx_path): + caibx = open(caibx_path, 'rb') + else: + resp = requests.get(caibx_path, timeout=CAIBX_DOWNLOAD_TIMEOUT) + resp.raise_for_status() + caibx = io.BytesIO(resp.content) + + caibx.seek(0, os.SEEK_END) + caibx_len = caibx.tell() + caibx.seek(0, os.SEEK_SET) + + # Parse header + length, magic, flags, min_size, _, max_size = struct.unpack("= min_size + + chunks.append(Chunk(sha, offset, length)) + offset = new_offset + + return chunks + + +def build_chunk_dict(chunks: List[Chunk]) -> ChunkDict: + """Turn a list of chunks into a dict for faster lookups based on hash. + Keep first chunk since it's more likely to be already downloaded.""" + r = {} + for c in chunks: + if c.sha not in r: + r[c.sha] = c + return r + + +def extract(target: List[Chunk], + sources: List[Tuple[str, ChunkReader, ChunkDict]], + out_path: str, + progress: Optional[Callable[[int], None]] = None): + stats: Dict[str, int] = defaultdict(int) + + with open(out_path, 'wb') as out: + for cur_chunk in target: + + # Find source for desired chunk + for name, chunk_reader, store_chunks in sources: + if cur_chunk.sha in store_chunks: + bts = chunk_reader.read(store_chunks[cur_chunk.sha]) + + # Check length + if len(bts) != cur_chunk.length: + continue + + # Check hash + if SHA512.new(bts, truncate="256").digest() != cur_chunk.sha: + continue + + # Write to output + out.seek(cur_chunk.offset) + out.write(bts) + + stats[name] += cur_chunk.length + + if progress is not None: + progress(sum(stats.values())) + + break + else: + raise RuntimeError("Desired chunk not found in provided stores") + + return stats + + +def print_stats(stats: Dict[str, int]): + total_bytes = sum(stats.values()) + print(f"Total size: {total_bytes / 1024 / 1024:.2f} MB") + for name, total in stats.items(): + print(f" {name}: {total / 1024 / 1024:.2f} MB ({total / total_bytes * 100:.1f}%)") + + +def extract_simple(caibx_path, out_path, store_path): + # (name, callback, chunks) + target = parse_caibx(caibx_path) + sources = [ + # (store_path, RemoteChunkReader(store_path), build_chunk_dict(target)), + (store_path, FileChunkReader(store_path), build_chunk_dict(target)), + ] + + return extract(target, sources, out_path) + + +if __name__ == "__main__": + caibx = sys.argv[1] + out = sys.argv[2] + store = sys.argv[3] + + stats = extract_simple(caibx, out, store) + print_stats(stats) diff --git a/selfdrive/hardware/tici/hardware.h b/system/hardware/tici/hardware.h similarity index 72% rename from selfdrive/hardware/tici/hardware.h rename to system/hardware/tici/hardware.h index ec53e9ae8..dcccb9f3d 100644 --- a/selfdrive/hardware/tici/hardware.h +++ b/system/hardware/tici/hardware.h @@ -5,16 +5,22 @@ #include "common/params.h" #include "common/util.h" -#include "selfdrive/hardware/base.h" +#include "system/hardware/base.h" class HardwareTici : public HardwareNone { public: static constexpr float MAX_VOLUME = 0.9; static constexpr float MIN_VOLUME = 0.2; static bool TICI() { return true; } + static bool AGNOS() { return true; } static std::string get_os_version() { return "AGNOS " + util::read_file("/VERSION"); }; + static std::string get_name() { return "tici"; }; + static cereal::InitData::DeviceType get_device_type() { return cereal::InitData::DeviceType::TICI; }; + static int get_voltage() { return std::atoi(util::read_file("/sys/class/hwmon/hwmon1/in1_input").c_str()); }; + static int get_current() { return std::atoi(util::read_file("/sys/class/hwmon/hwmon1/curr1_input").c_str()); }; + static void reboot() { std::system("sudo reboot"); }; static void poweroff() { std::system("sudo poweroff"); }; diff --git a/selfdrive/hardware/tici/hardware.py b/system/hardware/tici/hardware.py similarity index 95% rename from selfdrive/hardware/tici/hardware.py rename to system/hardware/tici/hardware.py index 3a2e6ed03..66cb98c60 100644 --- a/selfdrive/hardware/tici/hardware.py +++ b/system/hardware/tici/hardware.py @@ -9,10 +9,10 @@ from pathlib import Path from cereal import log from common.gpio import gpio_set, gpio_init -from selfdrive.hardware.base import HardwareBase, ThermalConfig -from selfdrive.hardware.tici import iwlist -from selfdrive.hardware.tici.pins import GPIO -from selfdrive.hardware.tici.amplifier import Amplifier +from system.hardware.base import HardwareBase, ThermalConfig +from system.hardware.tici import iwlist +from system.hardware.tici.pins import GPIO +from system.hardware.tici.amplifier import Amplifier NM = 'org.freedesktop.NetworkManager' NM_CON_ACT = NM + '.Connection.Active' @@ -362,6 +362,9 @@ class Tici(HardwareBase): def get_current_power_draw(self): return (self.read_param_file("/sys/class/hwmon/hwmon1/power1_input", int) / 1e6) + def get_som_power_draw(self): + return (self.read_param_file("/sys/class/power_supply/bms/voltage_now", int) * self.read_param_file("/sys/class/power_supply/bms/current_now", int) / 1e12) + def shutdown(self): # Note that for this to work and have the device stay powered off, the panda needs to be in UsbPowerMode::CLIENT! os.system("sudo poweroff") @@ -457,22 +460,23 @@ class Tici(HardwareBase): def configure_modem(self): sim_id = self.get_sim_info().get('sim_id', '') + # configure modem as data-centric + cmds = [ + 'AT+QNVW=5280,0,"0102000000000000"', + 'AT+QNVFW="/nv/item_files/ims/IMS_enable",00', + 'AT+QNVFW="/nv/item_files/modem/mmode/ue_usage_setting",01', + ] + modem = self.get_modem() + for cmd in cmds: + try: + modem.Command(cmd, math.ceil(TIMEOUT), dbus_interface=MM_MODEM, timeout=TIMEOUT) + except Exception: + pass + # blue prime config if sim_id.startswith('8901410'): - cmds = [ - 'AT+QNVW=5280,0,"0102000000000000"', - 'AT+QNVFW="/nv/item_files/ims/IMS_enable",00', - 'AT+QNVFW="/nv/item_files/modem/mmode/ue_usage_setting",01', - ] - modem = self.get_modem() - for cmd in cmds: - try: - modem.Command(cmd, math.ceil(TIMEOUT), dbus_interface=MM_MODEM, timeout=TIMEOUT) - except Exception: - pass os.system('mmcli -m 0 --3gpp-set-initial-eps-bearer-settings="apn=Broadband"') - def get_networks(self): r = {} diff --git a/selfdrive/hardware/tici/iwlist.py b/system/hardware/tici/iwlist.py similarity index 100% rename from selfdrive/hardware/tici/iwlist.py rename to system/hardware/tici/iwlist.py diff --git a/selfdrive/hardware/tici/pins.py b/system/hardware/tici/pins.py similarity index 100% rename from selfdrive/hardware/tici/pins.py rename to system/hardware/tici/pins.py diff --git a/selfdrive/hardware/tici/updater b/system/hardware/tici/updater similarity index 100% rename from selfdrive/hardware/tici/updater rename to system/hardware/tici/updater diff --git a/system/logcatd/.gitignore b/system/logcatd/.gitignore new file mode 100644 index 000000000..c66f7622d --- /dev/null +++ b/system/logcatd/.gitignore @@ -0,0 +1 @@ +logcatd diff --git a/selfdrive/logcatd/SConscript b/system/logcatd/SConscript similarity index 100% rename from selfdrive/logcatd/SConscript rename to system/logcatd/SConscript diff --git a/selfdrive/logcatd/logcatd_systemd.cc b/system/logcatd/logcatd_systemd.cc similarity index 100% rename from selfdrive/logcatd/logcatd_systemd.cc rename to system/logcatd/logcatd_systemd.cc diff --git a/selfdrive/logmessaged.py b/system/logmessaged.py similarity index 95% rename from selfdrive/logmessaged.py rename to system/logmessaged.py index 1d1fab516..280a23cf1 100755 --- a/selfdrive/logmessaged.py +++ b/system/logmessaged.py @@ -4,7 +4,7 @@ from typing import NoReturn import cereal.messaging as messaging from common.logging_extra import SwagLogFileFormatter -from selfdrive.swaglog import get_file_handler +from system.swaglog import get_file_handler def main() -> NoReturn: diff --git a/selfdrive/proclogd/SConscript b/system/proclogd/SConscript similarity index 100% rename from selfdrive/proclogd/SConscript rename to system/proclogd/SConscript diff --git a/selfdrive/proclogd/main.cc b/system/proclogd/main.cc similarity index 89% rename from selfdrive/proclogd/main.cc rename to system/proclogd/main.cc index 288238dc9..c4faa916d 100644 --- a/selfdrive/proclogd/main.cc +++ b/system/proclogd/main.cc @@ -2,7 +2,7 @@ #include #include "common/util.h" -#include "selfdrive/proclogd/proclog.h" +#include "system/proclogd/proclog.h" ExitHandler do_exit; diff --git a/selfdrive/proclogd/proclog.cc b/system/proclogd/proclog.cc similarity index 99% rename from selfdrive/proclogd/proclog.cc rename to system/proclogd/proclog.cc index 215d9e9df..9064547d2 100644 --- a/selfdrive/proclogd/proclog.cc +++ b/system/proclogd/proclog.cc @@ -1,4 +1,4 @@ -#include "selfdrive/proclogd/proclog.h" +#include "system/proclogd/proclog.h" #include diff --git a/selfdrive/proclogd/proclog.h b/system/proclogd/proclog.h similarity index 100% rename from selfdrive/proclogd/proclog.h rename to system/proclogd/proclog.h diff --git a/selfdrive/swaglog.py b/system/swaglog.py similarity index 99% rename from selfdrive/swaglog.py rename to system/swaglog.py index a987bfe72..68664330a 100644 --- a/selfdrive/swaglog.py +++ b/system/swaglog.py @@ -7,7 +7,7 @@ from logging.handlers import BaseRotatingHandler import zmq from common.logging_extra import SwagLogger, SwagFormatter, SwagLogFileFormatter -from selfdrive.hardware import PC +from system.hardware import PC if PC: SWAGLOG_DIR = os.path.join(str(Path.home()), ".comma", "log") diff --git a/selfdrive/timezoned.py b/system/timezoned.py similarity index 96% rename from selfdrive/timezoned.py rename to system/timezoned.py index 6a7e9122c..884a5c381 100755 --- a/selfdrive/timezoned.py +++ b/system/timezoned.py @@ -9,8 +9,8 @@ import requests from timezonefinder import TimezoneFinder from common.params import Params -from selfdrive.hardware import TICI -from selfdrive.swaglog import cloudlog +from system.hardware import AGNOS +from system.swaglog import cloudlog def set_timezone(valid_timezones, timezone): @@ -20,7 +20,7 @@ def set_timezone(valid_timezones, timezone): cloudlog.debug(f"Setting timezone to {timezone}") try: - if TICI: + if AGNOS: tzpath = os.path.join("/usr/share/zoneinfo/", timezone) subprocess.check_call(f'sudo su -c "ln -snf {tzpath} /data/etc/tmptime && \ mv /data/etc/tmptime /data/etc/localtime"', shell=True) diff --git a/selfdrive/version.py b/system/version.py similarity index 99% rename from selfdrive/version.py rename to system/version.py index 08ab09734..f0817b3a9 100644 --- a/selfdrive/version.py +++ b/system/version.py @@ -5,7 +5,7 @@ from typing import List, Optional from functools import lru_cache from common.basedir import BASEDIR -from selfdrive.swaglog import cloudlog +from system.swaglog import cloudlog TESTED_BRANCHES = ['devel', 'release3-staging', 'dashcam3-staging', 'release3', 'dashcam3'] diff --git a/third_party/qt-plugins/x86_64/geoservices/libqtgeoservices_mapbox.so b/third_party/qt-plugins/x86_64/geoservices/libqtgeoservices_mapbox.so deleted file mode 100755 index 1fcc57e11..000000000 Binary files a/third_party/qt-plugins/x86_64/geoservices/libqtgeoservices_mapbox.so and /dev/null differ diff --git a/tools/__init__.py b/tools/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tools/lib/auth_config.py b/tools/lib/auth_config.py index 1699d94e5..7952fee49 100644 --- a/tools/lib/auth_config.py +++ b/tools/lib/auth_config.py @@ -1,7 +1,7 @@ import json import os from common.file_helpers import mkdirs_exists_ok -from selfdrive.hardware import PC +from system.hardware import PC class MissingAuthConfigError(Exception): diff --git a/tools/lib/framereader.py b/tools/lib/framereader.py index e333683b5..d9920ab12 100644 --- a/tools/lib/framereader.py +++ b/tools/lib/framereader.py @@ -185,20 +185,28 @@ def read_file_check_size(f, sz, cookie): return buff -def rgb24toyuv420(rgb): +def rgb24toyuv(rgb): yuv_from_rgb = np.array([[ 0.299 , 0.587 , 0.114 ], [-0.14714119, -0.28886916, 0.43601035 ], [ 0.61497538, -0.51496512, -0.10001026 ]]) img = np.dot(rgb.reshape(-1, 3), yuv_from_rgb.T).reshape(rgb.shape) - y_len = img.shape[0] * img.shape[1] - uv_len = y_len // 4 + ys = img[:, :, 0] us = (img[::2, ::2, 1] + img[1::2, ::2, 1] + img[::2, 1::2, 1] + img[1::2, 1::2, 1]) / 4 + 128 vs = (img[::2, ::2, 2] + img[1::2, ::2, 2] + img[::2, 1::2, 2] + img[1::2, 1::2, 2]) / 4 + 128 - yuv420 = np.empty(y_len + 2 * uv_len, dtype=img.dtype) + return ys, us, vs + + +def rgb24toyuv420(rgb): + ys, us, vs = rgb24toyuv(rgb) + + y_len = rgb.shape[0] * rgb.shape[1] + uv_len = y_len // 4 + + yuv420 = np.empty(y_len + 2 * uv_len, dtype=rgb.dtype) yuv420[:y_len] = ys.reshape(-1) yuv420[y_len:y_len + uv_len] = us.reshape(-1) yuv420[y_len + uv_len:y_len + 2 * uv_len] = vs.reshape(-1) @@ -206,6 +214,20 @@ def rgb24toyuv420(rgb): return yuv420.clip(0, 255).astype('uint8') +def rgb24tonv12(rgb): + ys, us, vs = rgb24toyuv(rgb) + + y_len = rgb.shape[0] * rgb.shape[1] + uv_len = y_len // 4 + + nv12 = np.empty(y_len + 2 * uv_len, dtype=rgb.dtype) + nv12[:y_len] = ys.reshape(-1) + nv12[y_len::2] = us.reshape(-1) + nv12[y_len+1::2] = vs.reshape(-1) + + return nv12.clip(0, 255).astype('uint8') + + def decompress_video_data(rawdat, vid_fmt, w, h, pix_fmt): # using a tempfile is much faster than proc.communicate for some reason @@ -237,6 +259,8 @@ def decompress_video_data(rawdat, vid_fmt, w, h, pix_fmt): if pix_fmt == "rgb24": ret = np.frombuffer(dat, dtype=np.uint8).reshape(-1, h, w, 3) + elif pix_fmt == "nv12": + ret = np.frombuffer(dat, dtype=np.uint8).reshape(-1, (h*w*3//2)) elif pix_fmt == "yuv420p": ret = np.frombuffer(dat, dtype=np.uint8).reshape(-1, (h*w*3//2)) elif pix_fmt == "yuv444p": @@ -304,7 +328,7 @@ class RawFrameReader(BaseFrameReader): assert self.frame_count is not None assert num+count <= self.frame_count - if pix_fmt not in ("yuv420p", "rgb24"): + if pix_fmt not in ("nv12", "yuv420p", "rgb24"): raise ValueError(f"Unsupported pixel format {pix_fmt!r}") app = [] @@ -313,6 +337,8 @@ class RawFrameReader(BaseFrameReader): rgb_dat = self.load_and_debayer(dat) if pix_fmt == "rgb24": app.append(rgb_dat) + elif pix_fmt == "nv12": + app.append(rgb24tonv12(rgb_dat)) elif pix_fmt == "yuv420p": app.append(rgb24toyuv420(rgb_dat)) else: @@ -329,7 +355,7 @@ class VideoStreamDecompressor: self.h = h self.pix_fmt = pix_fmt - if pix_fmt == "yuv420p": + if pix_fmt in ("nv12", "yuv420p"): self.out_size = w*h*3//2 # yuv420p elif pix_fmt in ("rgb24", "yuv444p"): self.out_size = w*h*3 @@ -387,6 +413,8 @@ class VideoStreamDecompressor: ret = np.frombuffer(dat, dtype=np.uint8).reshape((self.h, self.w, 3)) elif self.pix_fmt == "yuv420p": ret = np.frombuffer(dat, dtype=np.uint8) + elif self.pix_fmt == "nv12": + ret = np.frombuffer(dat, dtype=np.uint8) elif self.pix_fmt == "yuv444p": ret = np.frombuffer(dat, dtype=np.uint8).reshape((3, self.h, self.w)) else: @@ -549,7 +577,7 @@ class GOPFrameReader(BaseFrameReader): if num + count > self.frame_count: raise ValueError(f"{num + count} > {self.frame_count}") - if pix_fmt not in ("yuv420p", "rgb24", "yuv444p"): + if pix_fmt not in ("nv12", "yuv420p", "rgb24", "yuv444p"): raise ValueError(f"Unsupported pixel format {pix_fmt!r}") ret = [self._get_one(num + i, pix_fmt) for i in range(count)] diff --git a/tools/lib/logreader.py b/tools/lib/logreader.py index b4d3de67f..46c648ef1 100755 --- a/tools/lib/logreader.py +++ b/tools/lib/logreader.py @@ -74,22 +74,24 @@ class MultiLogIterator: class LogReader: - def __init__(self, fn, canonicalize=True, only_union_types=False, sort_by_time=False): + def __init__(self, fn, canonicalize=True, only_union_types=False, sort_by_time=False, dat=None): self.data_version = None self._only_union_types = only_union_types - _, ext = os.path.splitext(urllib.parse.urlparse(fn).path) - with FileReader(fn) as f: - dat = f.read() + ext = None + if not dat: + _, ext = os.path.splitext(urllib.parse.urlparse(fn).path) + if ext not in ('', '.bz2'): + # old rlogs weren't bz2 compressed + raise Exception(f"unknown extension {ext}") - if ext == "": - # old rlogs weren't bz2 compressed - ents = capnp_log.Event.read_multiple_bytes(dat) - elif ext == ".bz2": + with FileReader(fn) as f: + dat = f.read() + + if ext == ".bz2" or dat.startswith(b'BZh9'): dat = bz2.decompress(dat) - ents = capnp_log.Event.read_multiple_bytes(dat) - else: - raise Exception(f"unknown extension {ext}") + + ents = capnp_log.Event.read_multiple_bytes(dat) _ents = [] try: @@ -101,6 +103,10 @@ class LogReader: self._ents = list(sorted(_ents, key=lambda x: x.logMonoTime) if sort_by_time else _ents) self._ts = [x.logMonoTime for x in self._ents] + @classmethod + def from_bytes(cls, dat): + return cls("", dat=dat) + def __iter__(self): for ent in self._ents: if self._only_union_types: @@ -112,7 +118,6 @@ class LogReader: else: yield ent - def logreader_from_route_or_segment(r, sort_by_time=False): sn = SegmentName(r, allow_route_name=True) route = Route(sn.route_name.canonical_name) diff --git a/selfdrive/ui/replay/camera.cc b/tools/replay/camera.cc similarity index 96% rename from selfdrive/ui/replay/camera.cc rename to tools/replay/camera.cc index 2e8b68a41..87afe63a2 100644 --- a/selfdrive/ui/replay/camera.cc +++ b/tools/replay/camera.cc @@ -1,5 +1,5 @@ -#include "selfdrive/ui/replay/camera.h" -#include "selfdrive/ui/replay/util.h" +#include "tools/replay/camera.h" +#include "tools/replay/util.h" #include diff --git a/selfdrive/ui/replay/camera.h b/tools/replay/camera.h similarity index 92% rename from selfdrive/ui/replay/camera.h rename to tools/replay/camera.h index 7f078511c..66d33142f 100644 --- a/selfdrive/ui/replay/camera.h +++ b/tools/replay/camera.h @@ -3,8 +3,8 @@ #include #include "cereal/visionipc/visionipc_server.h" #include "common/queue.h" -#include "selfdrive/ui/replay/framereader.h" -#include "selfdrive/ui/replay/logreader.h" +#include "tools/replay/framereader.h" +#include "tools/replay/logreader.h" class CameraServer { public: diff --git a/selfdrive/ui/replay/consoleui.cc b/tools/replay/consoleui.cc similarity index 99% rename from selfdrive/ui/replay/consoleui.cc rename to tools/replay/consoleui.cc index 05eb09c9e..e4a3146a6 100644 --- a/selfdrive/ui/replay/consoleui.cc +++ b/tools/replay/consoleui.cc @@ -1,4 +1,4 @@ -#include "selfdrive/ui/replay/consoleui.h" +#include "tools/replay/consoleui.h" #include #include diff --git a/selfdrive/ui/replay/consoleui.h b/tools/replay/consoleui.h similarity index 96% rename from selfdrive/ui/replay/consoleui.h rename to tools/replay/consoleui.h index bce1146d4..20e07524d 100644 --- a/selfdrive/ui/replay/consoleui.h +++ b/tools/replay/consoleui.h @@ -7,7 +7,7 @@ #include #include -#include "selfdrive/ui/replay/replay.h" +#include "tools/replay/replay.h" #include class ConsoleUI : public QObject { diff --git a/selfdrive/ui/replay/filereader.cc b/tools/replay/filereader.cc similarity index 94% rename from selfdrive/ui/replay/filereader.cc rename to tools/replay/filereader.cc index 5f862bec3..88879067c 100644 --- a/selfdrive/ui/replay/filereader.cc +++ b/tools/replay/filereader.cc @@ -1,9 +1,9 @@ -#include "selfdrive/ui/replay/filereader.h" +#include "tools/replay/filereader.h" #include #include "common/util.h" -#include "selfdrive/ui/replay/util.h" +#include "tools/replay/util.h" std::string cacheFilePath(const std::string &url) { static std::string cache_path = [] { diff --git a/selfdrive/ui/replay/filereader.h b/tools/replay/filereader.h similarity index 100% rename from selfdrive/ui/replay/filereader.h rename to tools/replay/filereader.h diff --git a/selfdrive/ui/replay/framereader.cc b/tools/replay/framereader.cc similarity index 89% rename from selfdrive/ui/replay/framereader.cc rename to tools/replay/framereader.cc index ca839e681..ecde9ad5d 100644 --- a/selfdrive/ui/replay/framereader.cc +++ b/tools/replay/framereader.cc @@ -1,5 +1,5 @@ -#include "selfdrive/ui/replay/framereader.h" -#include "selfdrive/ui/replay/util.h" +#include "tools/replay/framereader.h" +#include "tools/replay/util.h" #include #include "libyuv.h" @@ -117,8 +117,6 @@ bool FrameReader::load(const std::byte *data, size_t size, bool no_hw_decoder, s if (has_hw_decoder && !no_hw_decoder) { if (!initHardwareDecoder(HW_DEVICE_TYPE)) { rWarning("No device with hardware decoder found. fallback to CPU decoding."); - } else { - nv12toyuv_buffer.resize(getYUVSize()); } } @@ -227,20 +225,22 @@ AVFrame *FrameReader::decodeFrame(AVPacket *pkt) { } bool FrameReader::copyBuffers(AVFrame *f, uint8_t *yuv) { + assert(f != nullptr && yuv != nullptr); + uint8_t *y = yuv; + uint8_t *uv = y + width * height; if (hw_pix_fmt == HW_PIX_FMT) { - uint8_t *y = yuv ? yuv : nv12toyuv_buffer.data(); - uint8_t *u = y + width * height; - uint8_t *v = u + (width / 2) * (height / 2); - libyuv::NV12ToI420(f->data[0], f->linesize[0], f->data[1], f->linesize[1], - y, width, u, width / 2, v, width / 2, width, height); + for (int i = 0; i < height/2; i++) { + memcpy(y + (i*2 + 0)*width, f->data[0] + (i*2 + 0)*f->linesize[0], width); + memcpy(y + (i*2 + 1)*width, f->data[0] + (i*2 + 1)*f->linesize[0], width); + memcpy(uv + i*width, f->data[1] + i*f->linesize[1], width); + } } else { - uint8_t *u = yuv + width * height; - uint8_t *v = u + (width / 2) * (height / 2); - libyuv::I420Copy(f->data[0], f->linesize[0], - f->data[1], f->linesize[1], - f->data[2], f->linesize[2], - yuv, width, u, width / 2, v, width / 2, - width, height); + libyuv::I420ToNV12(f->data[0], f->linesize[0], + f->data[1], f->linesize[1], + f->data[2], f->linesize[2], + y, width, + uv, width, + width, height); } return true; } diff --git a/selfdrive/ui/replay/framereader.h b/tools/replay/framereader.h similarity index 94% rename from selfdrive/ui/replay/framereader.h rename to tools/replay/framereader.h index 443636e27..e50b61d7f 100644 --- a/selfdrive/ui/replay/framereader.h +++ b/tools/replay/framereader.h @@ -4,7 +4,7 @@ #include #include -#include "selfdrive/ui/replay/filereader.h" +#include "tools/replay/filereader.h" extern "C" { #include @@ -46,7 +46,6 @@ private: AVPixelFormat hw_pix_fmt = AV_PIX_FMT_NONE; AVBufferRef *hw_device_ctx = nullptr; - std::vector nv12toyuv_buffer; int prev_idx = -1; inline static std::atomic has_hw_decoder = true; }; diff --git a/selfdrive/ui/replay/logreader.cc b/tools/replay/logreader.cc similarity index 97% rename from selfdrive/ui/replay/logreader.cc rename to tools/replay/logreader.cc index 579fe5064..f27224ac5 100644 --- a/selfdrive/ui/replay/logreader.cc +++ b/tools/replay/logreader.cc @@ -1,7 +1,7 @@ -#include "selfdrive/ui/replay/logreader.h" +#include "tools/replay/logreader.h" #include -#include "selfdrive/ui/replay/util.h" +#include "tools/replay/util.h" Event::Event(const kj::ArrayPtr &amsg, bool frame) : reader(amsg), frame(frame) { words = kj::ArrayPtr(amsg.begin(), reader.getEnd()); diff --git a/selfdrive/ui/replay/logreader.h b/tools/replay/logreader.h similarity index 95% rename from selfdrive/ui/replay/logreader.h rename to tools/replay/logreader.h index 7ada20605..fb63bf391 100644 --- a/selfdrive/ui/replay/logreader.h +++ b/tools/replay/logreader.h @@ -6,8 +6,8 @@ #endif #include "cereal/gen/cpp/log.capnp.h" -#include "selfdrive/camerad/cameras/camera_common.h" -#include "selfdrive/ui/replay/filereader.h" +#include "system/camerad/cameras/camera_common.h" +#include "tools/replay/filereader.h" const CameraType ALL_CAMERAS[] = {RoadCam, DriverCam, WideRoadCam}; const int MAX_CAMERAS = std::size(ALL_CAMERAS); diff --git a/selfdrive/ui/replay/main.cc b/tools/replay/main.cc similarity index 96% rename from selfdrive/ui/replay/main.cc rename to tools/replay/main.cc index e09587023..d3d689487 100644 --- a/selfdrive/ui/replay/main.cc +++ b/tools/replay/main.cc @@ -1,8 +1,8 @@ #include #include -#include "selfdrive/ui/replay/consoleui.h" -#include "selfdrive/ui/replay/replay.h" +#include "tools/replay/consoleui.h" +#include "tools/replay/replay.h" const QString DEMO_ROUTE = "4cf7a6ad03080c90|2021-09-29--13-46-36"; diff --git a/selfdrive/ui/replay/replay.cc b/tools/replay/replay.cc similarity index 98% rename from selfdrive/ui/replay/replay.cc rename to tools/replay/replay.cc index 4036086e1..c886a7e18 100644 --- a/selfdrive/ui/replay/replay.cc +++ b/tools/replay/replay.cc @@ -1,4 +1,4 @@ -#include "selfdrive/ui/replay/replay.h" +#include "tools/replay/replay.h" #include #include @@ -7,8 +7,8 @@ #include "cereal/services.h" #include "common/params.h" #include "common/timing.h" -#include "selfdrive/hardware/hw.h" -#include "selfdrive/ui/replay/util.h" +#include "system/hardware/hw.h" +#include "tools/replay/util.h" Replay::Replay(QString route, QStringList allow, QStringList block, SubMaster *sm_, uint32_t flags, QString data_dir, QObject *parent) : sm(sm_), flags_(flags), QObject(parent) { @@ -360,7 +360,8 @@ void Replay::stream() { setCurrentSegment(toSeconds(cur_mono_time_) / 60); // migration for pandaState -> pandaStates to keep UI working for old segments - if (cur_which == cereal::Event::Which::PANDA_STATE_D_E_P_R_E_C_A_T_E_D) { + if (cur_which == cereal::Event::Which::PANDA_STATE_D_E_P_R_E_C_A_T_E_D && + sockets_[cereal::Event::Which::PANDA_STATES] != nullptr) { MessageBuilder msg; auto ps = msg.initEvent().initPandaStates(1); ps[0].setIgnitionLine(true); diff --git a/selfdrive/ui/replay/replay.h b/tools/replay/replay.h similarity index 97% rename from selfdrive/ui/replay/replay.h rename to tools/replay/replay.h index c89a835f6..13269d3ec 100644 --- a/selfdrive/ui/replay/replay.h +++ b/tools/replay/replay.h @@ -4,8 +4,8 @@ #include -#include "selfdrive/ui/replay/camera.h" -#include "selfdrive/ui/replay/route.h" +#include "tools/replay/camera.h" +#include "tools/replay/route.h" // one segment uses about 100M of memory constexpr int FORWARD_SEGS = 5; diff --git a/selfdrive/ui/replay/route.cc b/tools/replay/route.cc similarity index 96% rename from selfdrive/ui/replay/route.cc rename to tools/replay/route.cc index ad93263ae..5b4709022 100644 --- a/selfdrive/ui/replay/route.cc +++ b/tools/replay/route.cc @@ -1,4 +1,4 @@ -#include "selfdrive/ui/replay/route.h" +#include "tools/replay/route.h" #include #include @@ -9,10 +9,10 @@ #include -#include "selfdrive/hardware/hw.h" +#include "system/hardware/hw.h" #include "selfdrive/ui/qt/api.h" -#include "selfdrive/ui/replay/replay.h" -#include "selfdrive/ui/replay/util.h" +#include "tools/replay/replay.h" +#include "tools/replay/util.h" Route::Route(const QString &route, const QString &data_dir) : data_dir_(data_dir) { route_ = parseRoute(route); diff --git a/selfdrive/ui/replay/route.h b/tools/replay/route.h similarity index 92% rename from selfdrive/ui/replay/route.h rename to tools/replay/route.h index ac8fba75c..6ca9c3b88 100644 --- a/selfdrive/ui/replay/route.h +++ b/tools/replay/route.h @@ -2,9 +2,9 @@ #include -#include "selfdrive/ui/replay/framereader.h" -#include "selfdrive/ui/replay/logreader.h" -#include "selfdrive/ui/replay/util.h" +#include "tools/replay/framereader.h" +#include "tools/replay/logreader.h" +#include "tools/replay/util.h" struct RouteIdentifier { QString dongle_id; diff --git a/selfdrive/ui/replay/util.cc b/tools/replay/util.cc similarity index 99% rename from selfdrive/ui/replay/util.cc rename to tools/replay/util.cc index 099219ccd..a6ebbec61 100644 --- a/selfdrive/ui/replay/util.cc +++ b/tools/replay/util.cc @@ -1,4 +1,4 @@ -#include "selfdrive/ui/replay/util.h" +#include "tools/replay/util.h" #include #include diff --git a/selfdrive/ui/replay/util.h b/tools/replay/util.h similarity index 100% rename from selfdrive/ui/replay/util.h rename to tools/replay/util.h