diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b8564c1e8..8f54cdf07 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -18,7 +18,7 @@ You can test your changes on your machine by running `run_docker_tests.sh`. This ### Automated Testing -All PRs and commits are automatically checked by Github Actions. Check out `.github/workflows/` for what Github Actions runs. Any new tests sould be added to Github Actions. +All PRs and commits are automatically checked by Github Actions. Check out `.github/workflows/` for what Github Actions runs. Any new tests should be added to Github Actions. ### Code Style and Linting @@ -28,7 +28,7 @@ Code is automatically checked for style by Github Actions as part of the automat We've released a [Model Port guide](https://medium.com/@comma_ai/openpilot-port-guide-for-toyota-models-e5467f4b5fe6) for porting to Toyota/Lexus models. -If you port openpilot to a substantially new car brand, see this more generic [Brand Port guide](https://medium.com/@comma_ai/how-to-write-a-car-port-for-openpilot-7ce0785eda84). You might also be eligible for a bounty. See our bounties at [comma.ai/bounties.html](https://comma.ai/bounties.html) +If you port openpilot to a substantially new car brand, see this more generic [Brand Port guide](https://medium.com/@comma_ai/how-to-write-a-car-port-for-openpilot-7ce0785eda84). You might also be eligible for a bounty. ## Pull Requests diff --git a/Jenkinsfile b/Jenkinsfile index dd0e2c12f..df52f59e1 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -70,7 +70,7 @@ pipeline { stage('PC tests') { agent { dockerfile { - filename 'Dockerfile.openpilot' + filename 'Dockerfile.openpilotci' args '--privileged --shm-size=1G --user=root' } } @@ -132,6 +132,7 @@ pipeline { ["build cereal", "SCONS_CACHE=1 scons -j4 cereal/"], ["test sounds", "nosetests -s selfdrive/test/test_sounds.py"], ["test boardd loopback", "nosetests -s selfdrive/boardd/tests/test_boardd_loopback.py"], + ["test loggerd", "CI=1 python selfdrive/loggerd/tests/test_loggerd.py"], //["test updater", "python installer/updater/test_updater.py"], ]) } @@ -140,6 +141,13 @@ pipeline { } } } + + post { + always { + cleanWs() + } + } + } } diff --git a/README.md b/README.md index f78a07088..17a095252 100644 --- a/README.md +++ b/README.md @@ -70,31 +70,30 @@ Supported Cars | Honda | Accord Hybrid 2018-20 | All | Stock | 0mph | 3mph | | Honda | Civic Hatchback 2017-19 | Honda Sensing | Stock | 0mph | 12mph | | Honda | Civic Sedan/Coupe 2016-18 | Honda Sensing | openpilot | 0mph | 12mph | -| Honda | Civic Sedan/Coupe 2019-20 | Honda Sensing | Stock | 0mph | 2mph2 | +| Honda | Civic Sedan/Coupe 2019-20 | All | Stock | 0mph | 2mph2 | | Honda | CR-V 2015-16 | Touring | openpilot | 25mph1 | 12mph | | Honda | CR-V 2017-20 | Honda Sensing | Stock | 0mph | 12mph | | Honda | CR-V Hybrid 2017-2019 | Honda Sensing | Stock | 0mph | 12mph | | Honda | Fit 2018-19 | Honda Sensing | openpilot | 25mph1 | 12mph | | Honda | HR-V 2019 | Honda Sensing | openpilot | 25mph1 | 12mph | -| Honda | Insight 2019-20 | Honda Sensing | Stock | 0mph | 3mph | +| Honda | Insight 2019-20 | All | Stock | 0mph | 3mph | | Honda | Odyssey 2018-20 | Honda Sensing | openpilot | 25mph1 | 0mph | | Honda | Passport 2019 | All | openpilot | 25mph1 | 12mph | -| Honda | Pilot 2016-18 | Honda Sensing | openpilot | 25mph1 | 12mph | -| Honda | Pilot 2019 | All | openpilot | 25mph1 | 12mph | +| Honda | Pilot 2016-19 | Honda Sensing | openpilot | 25mph1 | 12mph | | Honda | Ridgeline 2017-20 | Honda Sensing | openpilot | 25mph1 | 12mph | +| Hyundai | Palisade 2020 | All | Stock | 0mph | 0mph | | Hyundai | Sonata 2020 | All | Stock | 0mph | 0mph | -| Lexus | CT Hybrid 2017-18 | All | Stock3| 0mph | 0mph | +| Lexus | CT Hybrid 2017-18 | LSS | Stock3| 0mph | 0mph | | Lexus | ES 2019 | All | openpilot | 0mph | 0mph | | Lexus | ES Hybrid 2019 | All | openpilot | 0mph | 0mph | | Lexus | IS 2017-2019 | All | Stock | 22mph | 0mph | | Lexus | IS Hybrid 2017 | All | Stock | 0mph | 0mph | | Lexus | NX Hybrid 2018 | All | Stock3| 0mph | 0mph | -| Lexus | RX 2016-17 | All | Stock3| 0mph | 0mph | +| Lexus | RX 2016-18 | All | Stock3| 0mph | 0mph | | Lexus | RX 2020 | All | openpilot | 0mph | 0mph | | Lexus | RX Hybrid 2016-19 | All | Stock3| 0mph | 0mph | | Lexus | RX Hybrid 2020 | All | openpilot | 0mph | 0mph | -| Toyota | Avalon 2016 | TSS-P | Stock3| 20mph1 | 0mph | -| Toyota | Avalon 2017-18 | All | Stock3| 20mph1 | 0mph | +| Toyota | Avalon 2016-18 | TSS-P | Stock3| 20mph1 | 0mph | | Toyota | Camry 2018-20 | All | Stock | 0mph4 | 0mph | | Toyota | Camry Hybrid 2018-19 | All | Stock | 0mph4 | 0mph | | Toyota | C-HR 2017-19 | All | Stock | 0mph | 0mph | @@ -102,19 +101,16 @@ Supported Cars | Toyota | Corolla 2017-19 | All | Stock3| 20mph1 | 0mph | | Toyota | Corolla 2020 | All | openpilot | 0mph | 0mph | | Toyota | Corolla Hatchback 2019-20 | All | openpilot | 0mph | 0mph | -| Toyota | Corolla Hybrid 2020 | All | openpilot | 0mph | 0mph | +| Toyota | Corolla Hybrid 2020-21 | All | openpilot | 0mph | 0mph | | Toyota | Highlander 2017-19 | All | Stock3| 0mph | 0mph | -| Toyota | Highlander Hybrid 2017-19 | All | Stock3| 0mph | 0mph | | Toyota | Highlander 2020 | All | openpilot | 0mph | 0mph | +| Toyota | Highlander Hybrid 2017-19 | All | Stock3| 0mph | 0mph | | Toyota | Highlander Hybrid 2020 | All | openpilot | 0mph | 0mph | -| Toyota | Prius 2016 | TSS-P | Stock3| 0mph | 0mph | -| Toyota | Prius 2017-20 | All | Stock3| 0mph | 0mph | +| Toyota | Prius 2016-20 | TSS-P | Stock3| 0mph | 0mph | | Toyota | Prius Prime 2017-20 | All | Stock3| 0mph | 0mph | -| Toyota | Rav4 2016 | TSS-P | Stock3| 20mph1 | 0mph | -| Toyota | Rav4 2017-18 | All | Stock3| 20mph1 | 0mph | +| Toyota | Rav4 2016-18 | TSS-P | Stock3| 20mph1 | 0mph | | Toyota | Rav4 2019-20 | All | openpilot | 0mph | 0mph | -| Toyota | Rav4 Hybrid 2016 | TSS-P | Stock3| 0mph | 0mph | -| Toyota | Rav4 Hybrid 2017-18 | All | Stock3| 0mph | 0mph | +| Toyota | Rav4 Hybrid 2016-18 | TSS-P | Stock3| 0mph | 0mph | | Toyota | Rav4 Hybrid 2019-20 | All | openpilot | 0mph | 0mph | | Toyota | Sienna 2018-20 | All | Stock3| 0mph | 0mph | @@ -143,24 +139,22 @@ Community Maintained Cars and Features | Holden | Astra 20171 | Adaptive Cruise | openpilot | 0mph | 7mph | | Hyundai | Elantra 2017-19 | SCC + LKAS | Stock | 19mph | 34mph | | Hyundai | Genesis 2015-16 | SCC + LKAS | Stock | 19mph | 37mph | -| Hyundai | Ioniq Electric Premium SE 2020| SCC + LKAS | Stock | 0mph | 32mph | -| Hyundai | Ioniq Electric Limited 2019 | SCC + LKAS | Stock | 0mph | 32mph | +| Hyundai | Ioniq Electric 2019-20 | SCC + LKAS | Stock | 0mph | 32mph | | Hyundai | Kona 2020 | SCC + LKAS | Stock | 0mph | 0mph | | Hyundai | Kona EV 2019 | SCC + LKAS | Stock | 0mph | 0mph | -| Hyundai | Palisade 2020 | All | Stock | 0mph | 0mph | | Hyundai | Santa Fe 2019 | All | Stock | 0mph | 0mph | | Hyundai | Sonata 2019 | All | Stock | 0mph | 0mph | | Hyundai | Veloster 2019 | SCC + LKAS | Stock | 5mph | 0mph | | Jeep | Grand Cherokee 2016-18 | Adaptive Cruise | Stock | 0mph | 9mph | | Jeep | Grand Cherokee 2019-20 | Adaptive Cruise | Stock | 0mph | 39mph | | Kia | Forte 2018-19 | SCC + LKAS | Stock | 0mph | 0mph | -| Kia | Optima 2017 | SCC + LKAS/LDWS | Stock | 0mph | 32mph | +| Kia | Optima 2017 | SCC + LKAS | Stock | 0mph | 32mph | | Kia | Optima 2019 | SCC + LKAS | Stock | 0mph | 0mph | | Kia | Sorento 2018 | SCC + LKAS | Stock | 0mph | 0mph | | Kia | Stinger 2018 | SCC + LKAS | Stock | 0mph | 0mph | -| Nissan | Leaf 2018-19 | Propilot | Stock | 0mph | 0mph | -| Nissan | Rogue 2019 | Propilot | Stock | 0mph | 0mph | -| Nissan | X-Trail 2017 | Propilot | Stock | 0mph | 0mph | +| Nissan | Leaf 2018-19 | ProPILOT | Stock | 0mph | 0mph | +| Nissan | Rogue 2019 | ProPILOT | Stock | 0mph | 0mph | +| Nissan | X-Trail 2017 | ProPILOT | Stock | 0mph | 0mph | | Subaru | Ascent 2019 | EyeSight | Stock | 0mph | 0mph | | Subaru | Crosstrek 2018-19 | EyeSight | Stock | 0mph | 0mph | | Subaru | Forester 2019 | EyeSight | Stock | 0mph | 0mph | @@ -274,8 +268,23 @@ Safety and Testing Testing on PC ------ +For simplified development and experimentation, openpilot runs in the CARLA driving simulator, which allows you to develop openpilot without a car. + +Steps: +1) Start the CARLA server on first terminal +``` +bash -c "$(curl https://raw.githubusercontent.com/commaai/openpilot/master/tools/sim/start_carla.sh)" +``` +2) Start openpilot on second terminal +``` +bash -c "$(curl https://raw.githubusercontent.com/commaai/openpilot/master/tools/sim/start_openpilot_docker.sh)" +``` +3) Press 1 to engage openpilot + +See the full [README](tools/sim/README.md) + +You should also take a look at the tools directory in master: lots of tools you can use to replay driving data, test, and develop openpilot from your PC. -Check out the tools directory in master: lots of tools you can use to replay driving data, test and develop openpilot from your pc. Community and Contributing ------ diff --git a/RELEASES.md b/RELEASES.md index 0c6dd3d4f..e8a157567 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,3 +1,11 @@ +Version 0.7.9 (2020-10-09) +======================== + * Improved car battery power management + * Improved updater robustness + * Improved realtime performance + * Reduced UI and modeld lags + * Increased torque on 2020 Hyundai Sonata and Palisade + Version 0.7.8 (2020-08-19) ======================== * New driver monitoring model: improved face detection and better compatibility with sunglasses diff --git a/SConstruct b/SConstruct index 322bdd5f9..fc119262b 100644 --- a/SConstruct +++ b/SConstruct @@ -6,6 +6,8 @@ import subprocess import sys import platform +TICI = os.path.isfile('/TICI') + AddOption('--test', action='store_true', help='build test files') @@ -18,10 +20,11 @@ AddOption('--asan', cython_dependencies = [Value(v) for v in (sys.version, distutils.__version__, Cython.__version__)] Export('cython_dependencies') -arch = subprocess.check_output(["uname", "-m"], encoding='utf8').rstrip() +real_arch = arch = subprocess.check_output(["uname", "-m"], encoding='utf8').rstrip() if platform.system() == "Darwin": arch = "Darwin" -if arch == "aarch64" and not os.path.isdir("/system"): + +if arch == "aarch64" and TICI: arch = "larch64" webcam = bool(ARGUMENTS.get("use_webcam", 0)) @@ -44,7 +47,6 @@ if arch == "aarch64" or arch == "larch64": libpath = [ "/usr/lib", - "/data/data/com.termux/files/usr/lib", "/system/vendor/lib64", "/system/comma/usr/lib", "#phonelibs/nanovg", @@ -62,11 +64,12 @@ if arch == "aarch64" or arch == "larch64": else: libpath += [ "#phonelibs/snpe/aarch64", - "#phonelibs/libyuv/lib" + "#phonelibs/libyuv/lib", + "/system/vendor/lib64" ] cflags = ["-DQCOM", "-mcpu=cortex-a57"] cxxflags = ["-DQCOM", "-mcpu=cortex-a57"] - rpath = ["/system/vendor/lib64"] + rpath = [] if QCOM_REPLAY: cflags += ["-DQCOM_REPLAY"] @@ -131,8 +134,11 @@ env = Environment( "-O2", "-Wunused", "-Werror", + "-Wno-unknown-warning-option", "-Wno-deprecated-register", "-Wno-inconsistent-missing-override", + "-Wno-c99-designator", + "-Wno-reorder-init-list", ] + cflags + ccflags_asan, CPPPATH=cpppath + [ @@ -143,7 +149,6 @@ env = Environment( "#phonelibs/openmax/include", "#phonelibs/json11", "#phonelibs/curl/include", - #"#phonelibs/opencv/include", # use opencv4 instead "#phonelibs/libgralloc/include", "#phonelibs/android_frameworks_native/include", "#phonelibs/android_hardware_libhardware/include", @@ -156,6 +161,8 @@ env = Environment( "#selfdrive/camerad/include", "#selfdrive/loggerd/include", "#selfdrive/modeld", + "#selfdrive/sensord", + "#selfdrive/ui", "#cereal/messaging", "#cereal", "#opendbc/can", @@ -176,6 +183,44 @@ env = Environment( ] ) +qt_env = None +if arch in ["x86_64", "Darwin", "larch64"]: + qt_env = env.Clone() + + if arch == "Darwin": + qt_env['QTDIR'] = "/usr/local/opt/qt" + QT_BASE = "/usr/local/opt/qt/" + qt_dirs = [ + QT_BASE + "include/", + QT_BASE + "include/QtWidgets", + QT_BASE + "include/QtGui", + QT_BASE + "include/QtCore", + QT_BASE + "include/QtDBus", + QT_BASE + "include/QtMultimedia", + ] + qt_env["LINKFLAGS"] += ["-F" + QT_BASE + "lib"] + else: + qt_dirs = [ + f"/usr/include/{real_arch}-linux-gnu/qt5", + f"/usr/include/{real_arch}-linux-gnu/qt5/QtWidgets", + f"/usr/include/{real_arch}-linux-gnu/qt5/QtGui", + f"/usr/include/{real_arch}-linux-gnu/qt5/QtCore", + f"/usr/include/{real_arch}-linux-gnu/qt5/QtDBus", + f"/usr/include/{real_arch}-linux-gnu/qt5/QtMultimedia", + f"/usr/include/{real_arch}-linux-gnu/qt5/QtGui/5.5.1/QtGui", + ] + + qt_env.Tool('qt') + qt_env['CPPPATH'] += qt_dirs + qt_flags = [ + "-D_REENTRANT", + "-DQT_NO_DEBUG", + "-DQT_WIDGETS_LIB", + "-DQT_GUI_LIB", + "-DQT_CORE_LIB" + ] + qt_env['CXXFLAGS'] += qt_flags + if os.environ.get('SCONS_CACHE'): cache_dir = '/tmp/scons_cache' @@ -214,7 +259,7 @@ def abspath(x): # still needed for apks zmq = 'zmq' -Export('env', 'arch', 'zmq', 'SHARED', 'webcam', 'QCOM_REPLAY') +Export('env', 'qt_env', 'arch', 'zmq', 'SHARED', 'webcam', 'QCOM_REPLAY') # cereal and messaging are shared with the system SConscript(['cereal/SConscript']) @@ -255,16 +300,18 @@ SConscript(['selfdrive/controls/lib/longitudinal_mpc_model/SConscript']) SConscript(['selfdrive/boardd/SConscript']) SConscript(['selfdrive/proclogd/SConscript']) +SConscript(['selfdrive/clocksd/SConscript']) -SConscript(['selfdrive/ui/SConscript']) SConscript(['selfdrive/loggerd/SConscript']) SConscript(['selfdrive/locationd/SConscript']) SConscript(['selfdrive/locationd/models/SConscript']) +SConscript(['selfdrive/sensord/SConscript']) +SConscript(['selfdrive/ui/SConscript']) -if arch == "aarch64": +if arch != "Darwin": SConscript(['selfdrive/logcatd/SConscript']) - SConscript(['selfdrive/sensord/SConscript']) - SConscript(['selfdrive/clocksd/SConscript']) -else: + + +if arch == "x86_64": SConscript(['tools/lib/index_log/SConscript']) diff --git a/cereal/car.capnp b/cereal/car.capnp index 0db47f78b..17bc461b3 100644 --- a/cereal/car.capnp +++ b/cereal/car.capnp @@ -38,7 +38,6 @@ struct CarEvent @0x9b1657f34caf3ad3 { pedalPressed @13; cruiseDisabled @14; radarCanError @15; - dataNeededDEPRECATED @16; speedTooLow @17; outOfSpace @18; overheat @19; @@ -49,29 +48,22 @@ struct CarEvent @0x9b1657f34caf3ad3 { pcmDisable @24; noTarget @25; radarFault @26; - modelCommIssueDEPRECATED @27; brakeHold @28; parkBrake @29; manualRestart @30; lowSpeedLockout @31; plannerError @32; - ipasOverrideDEPRECATED @33; debugAlert @34; steerTempUnavailableMute @35; resumeRequired @36; preDriverDistracted @37; promptDriverDistracted @38; driverDistracted @39; - geofenceDEPRECATED @40; - driverMonitorOnDEPRECATED @41; - driverMonitorOffDEPRECATED @42; preDriverUnresponsive @43; promptDriverUnresponsive @44; driverUnresponsive @45; belowSteerSpeed @46; - calibrationProgressDEPRECATED @47; lowBattery @48; - invalidGiraffeHondaDEPRECATED @49; vehicleModelInvalid @50; controlsFailed @51; sensorDataInvalid @52; @@ -104,8 +96,6 @@ struct CarEvent @0x9b1657f34caf3ad3 { fcw @79; steerSaturated @80; whitePandaUnsupported @81; - startupWhitePanda @82; - canErrorPersistentDEPRECATED @83; belowEngageSpeed @84; noGps @85; focusRecoverActive @86; @@ -113,6 +103,17 @@ struct CarEvent @0x9b1657f34caf3ad3 { neosUpdateRequired @88; modeldLagging @89; deviceFalling @90; + + dataNeededDEPRECATED @16; + modelCommIssueDEPRECATED @27; + ipasOverrideDEPRECATED @33; + geofenceDEPRECATED @40; + driverMonitorOnDEPRECATED @41; + driverMonitorOffDEPRECATED @42; + calibrationProgressDEPRECATED @47; + invalidGiraffeHondaDEPRECATED @49; + canErrorPersistentDEPRECATED @83; + startupWhitePandaDEPRECATED @82; } } diff --git a/cereal/log.capnp b/cereal/log.capnp index 64a577bc5..c8419500a 100644 --- a/cereal/log.capnp +++ b/cereal/log.capnp @@ -186,6 +186,7 @@ struct SensorEventData { gyroUncalibrated @12 :SensorVec; proximity @13: Float32; light @14: Float32; + temperature @15: Float32; } source @8 :SensorSource; @@ -203,6 +204,8 @@ struct SensorEventData { lsm6ds3 @5; # accelerometer (c2) bmp280 @6; # barometer (c2) mmc3416x @7; # magnetometer (c2) + bmx055 @8; + rpr0521 @9; } } @@ -267,14 +270,15 @@ struct CanData { } struct ThermalData { - cpu0 @0 :UInt16; - cpu1 @1 :UInt16; - cpu2 @2 :UInt16; - cpu3 @3 :UInt16; - mem @4 :UInt16; - gpu @5 :UInt16; - bat @6 :UInt32; - pa0 @21 :UInt16; + # Deprecated + cpu0DEPRECATED @0 :UInt16; + cpu1DEPRECATED @1 :UInt16; + cpu2DEPRECATED @2 :UInt16; + cpu3DEPRECATED @3 :UInt16; + memDEPRECATED @4 :UInt16; + gpuDEPRECATED @5 :UInt16; + batDEPRECATED @6 :UInt32; + pa0DEPRECATED @21 :UInt16; # not thermal freeSpace @7 :Float32; @@ -286,6 +290,7 @@ struct ThermalData { networkType @22 :NetworkType; offroadPowerUsage @23 :UInt32; # Power usage since going offroad in uWh networkStrength @24 :NetworkStrength; + carBatteryCapacity @25 :UInt32; # Estimated remaining car battery capacity in uWh fanSpeed @10 :UInt16; started @11 :Bool; @@ -298,6 +303,12 @@ struct ThermalData { memUsedPercent @19 :Int8; cpuPerc @20 :Int8; + cpu @26 :List(Float32); + gpu @27 :List(Float32); + mem @28 :Float32; + bat @29 :Float32; + ambient @30 :Float32; + enum ThermalStatus { green @0; # all processes run yellow @1; # critical processes run (kill uploader), engage still allowed @@ -373,6 +384,8 @@ struct HealthData { interruptRateTim3 @17; registerDivergent @18; interruptRateKlineInit @19; + interruptRateClockSource @20; + interruptRateTim9 @21; # Update max fault type in boardd when adding faults } @@ -602,7 +615,7 @@ struct ControlsState @0x97ff69c53601abf1 { output @3 :Float32; lqrOutput @4 :Float32; saturated @5 :Bool; - } + } } struct LiveEventData { @@ -675,6 +688,52 @@ struct ModelData { } } + +struct ModelDataV2 { + frameId @0 :UInt32; + frameAge @1 :UInt32; + frameDropPerc @2 :Float32; + timestampEof @3 :UInt64; + + position @4 :XYZTData; + orientation @5 :XYZTData; + velocity @6 :XYZTData; + orientationRate @7 :XYZTData; + laneLines @8 :List(XYZTData); + laneLineProbs @9 :List(Float32); + roadEdges @10 :List(XYZTData); + leads @11 :List(LeadDataV2); + + meta @12 :MetaData; + + struct XYZTData { + x @0 :List(Float32); + y @1 :List(Float32); + z @2 :List(Float32); + t @3 :List(Float32); + xStd @4 :List(Float32); + yStd @5 :List(Float32); + zStd @6 :List(Float32); + } + + struct LeadDataV2 { + prob @0 :Float32; + t @1 :Float32; + xyva @2 :List(Float32); + xyvaStd @3 :List(Float32); + } + + struct MetaData { + engagedProb @0 :Float32; + desirePrediction @1 :List(Float32); + brakeDisengageProb @2 :Float32; + gasDisengageProb @3 :Float32; + steerOverrideProb @4 :Float32; + desireState @5 :List(Float32); + } +} + + struct CalibrationFeatures { frameId @0 :UInt32; @@ -1906,7 +1965,6 @@ struct DMonitoringState { isDistracted @2 :Bool; awarenessStatus @3 :Float32; isRHD @4 :Bool; - rhdChecked @5 :Bool; posePitchOffset @6 :Float32; posePitchValidCount @7 :UInt32; poseYawOffset @8 :Float32; @@ -1917,6 +1975,8 @@ struct DMonitoringState { isLowStd @13 :Bool; hiStdCount @14 :UInt32; isPreview @15 :Bool; + + rhdCheckedDEPRECATED @5 :Bool; } struct Boot { @@ -2062,5 +2122,7 @@ struct Event { dMonitoringState @71: DMonitoringState; liveLocationKalman @72 :LiveLocationKalman; sentinel @73 :Sentinel; + wideFrame @74: FrameData; + modelV2 @75 :ModelDataV2; } } diff --git a/cereal/messaging/__init__.py b/cereal/messaging/__init__.py index 5e36bd8d8..0b1204b47 100644 --- a/cereal/messaging/__init__.py +++ b/cereal/messaging/__init__.py @@ -3,6 +3,8 @@ from .messaging_pyx import Context, Poller, SubSocket, PubSocket # pylint: disa from .messaging_pyx import MultiplePublishersError, MessagingError # pylint: disable=no-name-in-module, import-error import capnp +from typing import Optional, List, Union + from cereal import log from cereal.services import service_list @@ -19,7 +21,7 @@ except ImportError: context = Context() -def new_message(service=None, size=None): +def new_message(service: Optional[str] = None, size: Optional[int] = None) -> capnp.lib.capnp._DynamicStructBuilder: dat = log.Event.new_message() dat.logMonoTime = int(sec_since_boot() * 1e9) dat.valid = True @@ -30,15 +32,15 @@ def new_message(service=None, size=None): dat.init(service, size) return dat -def pub_sock(endpoint): +def pub_sock(endpoint: str) -> PubSocket: sock = PubSocket() sock.connect(context, endpoint) return sock -def sub_sock(endpoint, poller=None, addr="127.0.0.1", conflate=False, timeout=None): +def sub_sock(endpoint: str, poller: Optional[Poller] = None, addr: str = "127.0.0.1", + conflate: bool = False, timeout: Optional[int] = None) -> SubSocket: sock = SubSocket() - addr = addr.encode('utf8') - sock.connect(context, endpoint, addr, conflate) + sock.connect(context, endpoint, addr.encode('utf8'), conflate) if timeout is not None: sock.setTimeout(timeout) @@ -48,9 +50,9 @@ def sub_sock(endpoint, poller=None, addr="127.0.0.1", conflate=False, timeout=No return sock -def drain_sock_raw(sock, wait_for_one=False): +def drain_sock_raw(sock: SubSocket, wait_for_one: bool = False) -> List[bytes]: """Receive all message currently available on the queue""" - ret = [] + ret: List[bytes] = [] while 1: if wait_for_one and len(ret) == 0: dat = sock.receive() @@ -64,9 +66,9 @@ def drain_sock_raw(sock, wait_for_one=False): return ret -def drain_sock(sock, wait_for_one=False): +def drain_sock(sock: SubSocket, wait_for_one: bool = False) -> List[capnp.lib.capnp._DynamicStructReader]: """Receive all message currently available on the queue""" - ret = [] + ret: List[capnp.lib.capnp._DynamicStructReader] = [] while 1: if wait_for_one and len(ret) == 0: dat = sock.receive() @@ -83,7 +85,7 @@ def drain_sock(sock, wait_for_one=False): # TODO: print when we drop packets? -def recv_sock(sock, wait=False): +def recv_sock(sock: SubSocket, wait: bool = False) -> Union[None, capnp.lib.capnp._DynamicStructReader]: """Same as drain sock, but only returns latest message. Consider using conflate instead.""" dat = None @@ -103,19 +105,19 @@ def recv_sock(sock, wait=False): return dat -def recv_one(sock): +def recv_one(sock: SubSocket) -> Union[None, capnp.lib.capnp._DynamicStructReader]: dat = sock.receive() if dat is not None: dat = log.Event.from_bytes(dat) return dat -def recv_one_or_none(sock): +def recv_one_or_none(sock: SubSocket) -> Union[None, capnp.lib.capnp._DynamicStructReader]: dat = sock.receive(non_blocking=True) if dat is not None: dat = log.Event.from_bytes(dat) return dat -def recv_one_retry(sock): +def recv_one_retry(sock: SubSocket) -> capnp.lib.capnp._DynamicStructReader: """Keep receiving until we get a message""" while True: dat = sock.receive() @@ -123,8 +125,8 @@ def recv_one_retry(sock): return log.Event.from_bytes(dat) class SubMaster(): - def __init__(self, services, ignore_alive=None, addr="127.0.0.1"): - self.poller = Poller() + def __init__(self, services: List[str], poll: Optional[List[str]] = None, + ignore_alive: Optional[List[str]] = None, addr:str ="127.0.0.1"): self.frame = -1 self.updated = {s: False for s in services} self.rcv_time = {s: 0. for s in services} @@ -133,8 +135,12 @@ class SubMaster(): self.sock = {} self.freq = {} self.data = {} - self.logMonoTime = {} self.valid = {} + self.logMonoTime = {} + + self.poller = Poller() + self.non_polled_services = [s for s in services if poll is not None and + len(poll) and s not in poll] if ignore_alive is not None: self.ignore_alive = ignore_alive @@ -143,30 +149,33 @@ class SubMaster(): for s in services: if addr is not None: - self.sock[s] = sub_sock(s, poller=self.poller, addr=addr, conflate=True) + p = self.poller if s not in self.non_polled_services else None + self.sock[s] = sub_sock(s, poller=p, addr=addr, conflate=True) self.freq[s] = service_list[s].frequency try: data = new_message(s) except capnp.lib.capnp.KjException: # pylint: disable=c-extension-no-member - # lists - data = new_message(s, 0) + data = new_message(s, 0) # lists self.data[s] = getattr(data, s) self.logMonoTime[s] = 0 self.valid[s] = data.valid - def __getitem__(self, s): + def __getitem__(self, s: str) -> capnp.lib.capnp._DynamicStructReader: return self.data[s] - def update(self, timeout=1000): + def update(self, timeout: int = 1000) -> None: msgs = [] for sock in self.poller.poll(timeout): msgs.append(recv_one_or_none(sock)) + + # non-blocking receive for non-polled sockets + for s in self.non_polled_services: + msgs.append(recv_one_or_none(self.sock[s])) self.update_msgs(sec_since_boot(), msgs) - def update_msgs(self, cur_time, msgs): - # TODO: add optional input that specify the service to wait for + def update_msgs(self, cur_time: float, msgs: List[capnp.lib.capnp._DynamicStructReader]) -> None: self.frame += 1 self.updated = dict.fromkeys(self.updated, False) for msg in msgs: @@ -189,30 +198,28 @@ class SubMaster(): else: self.alive[s] = True - def all_alive(self, service_list=None): + def all_alive(self, service_list=None) -> bool: if service_list is None: # check all service_list = self.alive.keys() return all(self.alive[s] for s in service_list if s not in self.ignore_alive) - def all_valid(self, service_list=None): + def all_valid(self, service_list=None) -> bool: if service_list is None: # check all service_list = self.valid.keys() return all(self.valid[s] for s in service_list) - def all_alive_and_valid(self, service_list=None): + def all_alive_and_valid(self, service_list=None) -> bool: if service_list is None: # check all service_list = self.alive.keys() return self.all_alive(service_list=service_list) and self.all_valid(service_list=service_list) - class PubMaster(): - def __init__(self, services): + def __init__(self, services: List[str]): self.sock = {} for s in services: self.sock[s] = pub_sock(s) - def send(self, s, dat): - # accept either bytes or capnp builder + def send(self, s: str, dat: Union[bytes, capnp.lib.capnp._DynamicStructBuilder]) -> None: if not isinstance(dat, bytes): dat = dat.to_bytes() self.sock[s].send(dat) diff --git a/cereal/messaging/impl_msgq.cc b/cereal/messaging/impl_msgq.cc index 0a51d12c1..7da9a227c 100644 --- a/cereal/messaging/impl_msgq.cc +++ b/cereal/messaging/impl_msgq.cc @@ -15,6 +15,18 @@ void sig_handler(int signal) { msgq_do_exit = 1; } +static size_t get_size(std::string endpoint){ + size_t sz = DEFAULT_SEGMENT_SIZE; + +#if !defined(QCOM) && !defined(QCOM2) + if (endpoint == "frame" || endpoint == "frontFrame" || endpoint == "wideFrame"){ + sz *= 10; + } +#endif + + return sz; +} + MSGQContext::MSGQContext() { } @@ -49,13 +61,12 @@ MSGQMessage::~MSGQMessage() { this->close(); } - int MSGQSubSocket::connect(Context *context, std::string endpoint, std::string address, bool conflate){ assert(context); assert(address == "127.0.0.1"); q = new msgq_queue_t; - int r = msgq_new_queue(q, endpoint.c_str(), DEFAULT_SEGMENT_SIZE); + int r = msgq_new_queue(q, endpoint.c_str(), get_size(endpoint)); if (r != 0){ return r; } @@ -143,7 +154,7 @@ int MSGQPubSocket::connect(Context *context, std::string endpoint){ assert(context); q = new msgq_queue_t; - int r = msgq_new_queue(q, endpoint.c_str(), DEFAULT_SEGMENT_SIZE); + int r = msgq_new_queue(q, endpoint.c_str(), get_size(endpoint)); if (r != 0){ return r; } diff --git a/cereal/messaging/messaging.hpp b/cereal/messaging/messaging.hpp index fb0f96af4..9ade8bf2b 100644 --- a/cereal/messaging/messaging.hpp +++ b/cereal/messaging/messaging.hpp @@ -6,6 +6,10 @@ #include #include "../gen/cpp/log.capnp.h" +#ifdef __APPLE__ +#define CLOCK_BOOTTIME CLOCK_MONOTONIC +#endif + #define MSG_MULTIPLE_PUBLISHERS 100 class Context { @@ -82,11 +86,34 @@ private: std::map services_; }; +class MessageBuilder : public capnp::MallocMessageBuilder { +public: + MessageBuilder() = default; + + cereal::Event::Builder initEvent(bool valid = true) { + cereal::Event::Builder event = initRoot(); + struct timespec t; + clock_gettime(CLOCK_BOOTTIME, &t); + uint64_t current_time = t.tv_sec * 1000000000ULL + t.tv_nsec; + event.setLogMonoTime(current_time); + event.setValid(valid); + return event; + } + + kj::ArrayPtr toBytes() { + heapArray_ = capnp::messageToFlatArray(*this); + return heapArray_.asBytes(); + } + +private: + kj::Array heapArray_; +}; + class PubMaster { public: PubMaster(const std::initializer_list &service_list); inline int send(const char *name, capnp::byte *data, size_t size) { return sockets_.at(name)->send((char *)data, size); } - int send(const char *name, capnp::MessageBuilder &msg); + int send(const char *name, MessageBuilder &msg); ~PubMaster(); private: diff --git a/cereal/messaging/messaging_pyx_setup.py b/cereal/messaging/messaging_pyx_setup.py index 992b39159..8e67a104c 100644 --- a/cereal/messaging/messaging_pyx_setup.py +++ b/cereal/messaging/messaging_pyx_setup.py @@ -6,6 +6,7 @@ from distutils.core import Extension, setup # pylint: disable=import-error,no-n from Cython.Build import cythonize from Cython.Distutils import build_ext +TICI = os.path.isfile('/TICI') def get_ext_filename_without_platform_suffix(filename): name, ext = os.path.splitext(filename) @@ -30,11 +31,11 @@ class BuildExtWithoutPlatformSuffix(build_ext): sourcefiles = ['messaging_pyx.pyx'] -extra_compile_args = ["-std=c++14"] +extra_compile_args = ["-std=c++14", "-Wno-nullability-completeness"] libraries = ['zmq'] ARCH = subprocess.check_output(["uname", "-m"], encoding='utf8').rstrip() # pylint: disable=unexpected-keyword-arg -if ARCH == "aarch64" and os.path.isdir("/system"): +if ARCH == "aarch64" and not TICI: # android extra_compile_args += ["-Wno-deprecated-register"] libraries += ['gnustl_shared'] diff --git a/cereal/messaging/msgq.cc b/cereal/messaging/msgq.cc index 263010168..53bdb9af0 100644 --- a/cereal/messaging/msgq.cc +++ b/cereal/messaging/msgq.cc @@ -21,6 +21,8 @@ #include +#include "services.h" + #include "msgq.hpp" void sigusr2_handler(int signal) { @@ -81,11 +83,20 @@ void msgq_wait_for_subscriber(msgq_queue_t *q){ return; } - +bool service_exists(std::string path){ + for (const auto& it : services) { + if (it.name == path) { + return true; + } + } + return false; +} int msgq_new_queue(msgq_queue_t * q, const char * path, size_t size){ assert(size < 0xFFFFFFFF); // Buffer must be smaller than 2^32 bytes - + if (!service_exists(std::string(path))){ + std::cout << "Warning, " << std::string(path) << " is not in service list." << std::endl; + } std::signal(SIGUSR2, sigusr2_handler); const char * prefix = "/dev/shm/"; @@ -102,15 +113,15 @@ int msgq_new_queue(msgq_queue_t * q, const char * path, size_t size){ delete[] full_path; int rc = ftruncate(fd, size + sizeof(msgq_header_t)); - if (rc < 0) + if (rc < 0){ return -1; - + } char * mem = (char*)mmap(NULL, size + sizeof(msgq_header_t), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); close(fd); - if (mem == NULL) + if (mem == NULL){ return -1; - + } q->mmap_p = mem; msgq_header_t *header = (msgq_header_t *)mem; @@ -418,8 +429,6 @@ int msgq_msg_recv(msgq_msg_t * msg, msgq_queue_t * q){ int msgq_poll(msgq_pollitem_t * items, size_t nitems, int timeout){ - assert(timeout >= 0); - int num = 0; // Check if messages ready diff --git a/cereal/messaging/socketmaster.cc b/cereal/messaging/socketmaster.cc index a0f6c1ef4..53821aa21 100644 --- a/cereal/messaging/socketmaster.cc +++ b/cereal/messaging/socketmaster.cc @@ -3,10 +3,6 @@ #include "messaging.hpp" #include "services.h" -#ifdef __APPLE__ -#define CLOCK_BOOTTIME CLOCK_MONOTONIC -#endif - static inline uint64_t nanos_since_boot() { struct timespec t; clock_gettime(CLOCK_BOOTTIME, &t); @@ -164,9 +160,8 @@ PubMaster::PubMaster(const std::initializer_list &service_list) { } } -int PubMaster::send(const char *name, capnp::MessageBuilder &msg) { - auto words = capnp::messageToFlatArray(msg); - auto bytes = words.asBytes(); +int PubMaster::send(const char *name, MessageBuilder &msg) { + auto bytes = msg.toBytes(); return send(name, bytes.begin(), bytes.size()); } diff --git a/cereal/service_list.yaml b/cereal/service_list.yaml index 490e903f1..b140251cf 100644 --- a/cereal/service_list.yaml +++ b/cereal/service_list.yaml @@ -78,6 +78,8 @@ frontFrame: [8072, true, 10.] dMonitoringState: [8073, true, 5., 1] offroadLayout: [8074, false, 0.] wideEncodeIdx: [8075, true, 20.] +wideFrame: [8076, true, 20.] +modelV2: [8077, true, 20., 20] testModel: [8040, false, 0.] testLiveLocation: [8045, false, 0.] diff --git a/common/android.py b/common/android.py deleted file mode 100644 index 43bb0f3c1..000000000 --- a/common/android.py +++ /dev/null @@ -1,286 +0,0 @@ -import os -import binascii -import itertools -import re -import struct -import subprocess -import random -from cereal import log - -NetworkType = log.ThermalData.NetworkType -NetworkStrength = log.ThermalData.NetworkStrength - -ANDROID = os.path.isfile('/EON') - -def get_sound_card_online(): - return (os.path.isfile('/proc/asound/card0/state') and - open('/proc/asound/card0/state').read().strip() == 'ONLINE') - -def getprop(key): - if not ANDROID: - return "" - return subprocess.check_output(["getprop", key], encoding='utf8').strip() - -def get_imei(slot): - slot = str(slot) - if slot not in ("0", "1"): - raise ValueError("SIM slot must be 0 or 1") - - ret = parse_service_call_string(service_call(["iphonesubinfo", "3" , "i32", str(slot)])) - if not ret: - # allow non android to be identified differently - ret = "%015d" % random.randint(0, 1 << 32) - return ret - -def get_serial(): - ret = getprop("ro.serialno") - if ret == "": - ret = "cccccccc" - return ret - -def get_subscriber_info(): - ret = parse_service_call_string(service_call(["iphonesubinfo", "7"])) - if ret is None or len(ret) < 8: - return "" - return ret - -def reboot(reason=None): - if reason is None: - reason_args = ["null"] - else: - reason_args = ["s16", reason] - - subprocess.check_output([ - "service", "call", "power", "16", # IPowerManager.reboot - "i32", "0", # no confirmation, - *reason_args, - "i32", "1" # wait - ]) - -def service_call(call): - if not ANDROID: - return None - - ret = subprocess.check_output(["service", "call", *call], encoding='utf8').strip() - if 'Parcel' not in ret: - return None - - return parse_service_call_bytes(ret) - -def parse_service_call_unpack(r, fmt): - try: - return struct.unpack(fmt, r)[0] - except Exception: - return None - -def parse_service_call_string(r): - try: - r = r[8:] # Cut off length field - r = r.decode('utf_16_be') - - # All pairs of two characters seem to be swapped. Not sure why - result = "" - for a, b, in itertools.zip_longest(r[::2], r[1::2], fillvalue='\x00'): - result += b + a - - result = result.replace('\x00', '') - - return result - except Exception: - return None - -def parse_service_call_bytes(ret): - try: - r = b"" - for hex_part in re.findall(r'[ (]([0-9a-f]{8})', ret): - r += binascii.unhexlify(hex_part) - return r - except Exception: - return None - -def get_network_type(): - if not ANDROID: - return NetworkType.none - - wifi_check = parse_service_call_string(service_call(["connectivity", "2"])) - if wifi_check is None: - return NetworkType.none - elif 'WIFI' in wifi_check: - return NetworkType.wifi - else: - cell_check = parse_service_call_unpack(service_call(['phone', '59']), ">q") - # from TelephonyManager.java - cell_networks = { - 0: NetworkType.none, - 1: NetworkType.cell2G, - 2: NetworkType.cell2G, - 3: NetworkType.cell3G, - 4: NetworkType.cell2G, - 5: NetworkType.cell3G, - 6: NetworkType.cell3G, - 7: NetworkType.cell3G, - 8: NetworkType.cell3G, - 9: NetworkType.cell3G, - 10: NetworkType.cell3G, - 11: NetworkType.cell2G, - 12: NetworkType.cell3G, - 13: NetworkType.cell4G, - 14: NetworkType.cell4G, - 15: NetworkType.cell3G, - 16: NetworkType.cell2G, - 17: NetworkType.cell3G, - 18: NetworkType.cell4G, - 19: NetworkType.cell4G - } - return cell_networks.get(cell_check, NetworkType.none) - -def get_network_strength(network_type): - network_strength = NetworkStrength.unknown - - # from SignalStrength.java - def get_lte_level(rsrp, rssnr): - INT_MAX = 2147483647 - if rsrp == INT_MAX: - lvl_rsrp = NetworkStrength.unknown - elif rsrp >= -95: - lvl_rsrp = NetworkStrength.great - elif rsrp >= -105: - lvl_rsrp = NetworkStrength.good - elif rsrp >= -115: - lvl_rsrp = NetworkStrength.moderate - else: - lvl_rsrp = NetworkStrength.poor - if rssnr == INT_MAX: - lvl_rssnr = NetworkStrength.unknown - elif rssnr >= 45: - lvl_rssnr = NetworkStrength.great - elif rssnr >= 10: - lvl_rssnr = NetworkStrength.good - elif rssnr >= -30: - lvl_rssnr = NetworkStrength.moderate - else: - lvl_rssnr = NetworkStrength.poor - return max(lvl_rsrp, lvl_rssnr) - - def get_tdscdma_level(tdscmadbm): - lvl = NetworkStrength.unknown - if tdscmadbm > -25: - lvl = NetworkStrength.unknown - elif tdscmadbm >= -49: - lvl = NetworkStrength.great - elif tdscmadbm >= -73: - lvl = NetworkStrength.good - elif tdscmadbm >= -97: - lvl = NetworkStrength.moderate - elif tdscmadbm >= -110: - lvl = NetworkStrength.poor - return lvl - - def get_gsm_level(asu): - if asu <= 2 or asu == 99: - lvl = NetworkStrength.unknown - elif asu >= 12: - lvl = NetworkStrength.great - elif asu >= 8: - lvl = NetworkStrength.good - elif asu >= 5: - lvl = NetworkStrength.moderate - else: - lvl = NetworkStrength.poor - return lvl - - def get_evdo_level(evdodbm, evdosnr): - lvl_evdodbm = NetworkStrength.unknown - lvl_evdosnr = NetworkStrength.unknown - if evdodbm >= -65: - lvl_evdodbm = NetworkStrength.great - elif evdodbm >= -75: - lvl_evdodbm = NetworkStrength.good - elif evdodbm >= -90: - lvl_evdodbm = NetworkStrength.moderate - elif evdodbm >= -105: - lvl_evdodbm = NetworkStrength.poor - if evdosnr >= 7: - lvl_evdosnr = NetworkStrength.great - elif evdosnr >= 5: - lvl_evdosnr = NetworkStrength.good - elif evdosnr >= 3: - lvl_evdosnr = NetworkStrength.moderate - elif evdosnr >= 1: - lvl_evdosnr = NetworkStrength.poor - return max(lvl_evdodbm, lvl_evdosnr) - - def get_cdma_level(cdmadbm, cdmaecio): - lvl_cdmadbm = NetworkStrength.unknown - lvl_cdmaecio = NetworkStrength.unknown - if cdmadbm >= -75: - lvl_cdmadbm = NetworkStrength.great - elif cdmadbm >= -85: - lvl_cdmadbm = NetworkStrength.good - elif cdmadbm >= -95: - lvl_cdmadbm = NetworkStrength.moderate - elif cdmadbm >= -100: - lvl_cdmadbm = NetworkStrength.poor - if cdmaecio >= -90: - lvl_cdmaecio = NetworkStrength.great - elif cdmaecio >= -110: - lvl_cdmaecio = NetworkStrength.good - elif cdmaecio >= -130: - lvl_cdmaecio = NetworkStrength.moderate - elif cdmaecio >= -150: - lvl_cdmaecio = NetworkStrength.poor - return max(lvl_cdmadbm, lvl_cdmaecio) - - if network_type == NetworkType.none: - return network_strength - if network_type == NetworkType.wifi: - out = subprocess.check_output('dumpsys connectivity', shell=True).decode('utf-8') - network_strength = NetworkStrength.unknown - for line in out.split('\n'): - signal_str = "SignalStrength: " - if signal_str in line: - lvl_idx_start = line.find(signal_str) + len(signal_str) - lvl_idx_end = line.find(']', lvl_idx_start) - lvl = int(line[lvl_idx_start : lvl_idx_end]) - if lvl >= -50: - network_strength = NetworkStrength.great - elif lvl >= -60: - network_strength = NetworkStrength.good - elif lvl >= -70: - network_strength = NetworkStrength.moderate - else: - network_strength = NetworkStrength.poor - return network_strength - else: - # check cell strength - out = subprocess.check_output('dumpsys telephony.registry', shell=True).decode('utf-8') - for line in out.split('\n'): - if "mSignalStrength" in line: - arr = line.split(' ') - ns = 0 - if ("gsm" in arr[14]): - rsrp = int(arr[9]) - rssnr = int(arr[11]) - ns = get_lte_level(rsrp, rssnr) - if ns == NetworkStrength.unknown: - tdscmadbm = int(arr[13]) - ns = get_tdscdma_level(tdscmadbm) - if ns == NetworkStrength.unknown: - asu = int(arr[1]) - ns = get_gsm_level(asu) - else: - cdmadbm = int(arr[3]) - cdmaecio = int(arr[4]) - evdodbm = int(arr[5]) - evdosnr = int(arr[7]) - lvl_cdma = get_cdma_level(cdmadbm, cdmaecio) - lvl_edmo = get_evdo_level(evdodbm, evdosnr) - if lvl_edmo == NetworkStrength.unknown: - ns = lvl_cdma - elif lvl_cdma == NetworkStrength.unknown: - ns = lvl_edmo - else: - ns = min(lvl_cdma, lvl_edmo) - network_strength = max(network_strength, ns) - - return network_strength diff --git a/common/basedir.py b/common/basedir.py index 4d62fdc19..d98509ed6 100644 --- a/common/basedir.py +++ b/common/basedir.py @@ -1,10 +1,10 @@ import os BASEDIR = os.path.abspath(os.path.join(os.path.dirname(os.path.realpath(__file__)), "../")) -from common.android import ANDROID -if ANDROID: - PERSIST = "/persist" - PARAMS = "/data/params" -else: +from common.hardware import PC +if PC: PERSIST = os.path.join(BASEDIR, "persist") PARAMS = os.path.join(BASEDIR, "persist", "params") +else: + PERSIST = "/persist" + PARAMS = "/data/params" diff --git a/common/gpio.py b/common/gpio.py new file mode 100644 index 000000000..73603f262 --- /dev/null +++ b/common/gpio.py @@ -0,0 +1,22 @@ +GPIO_HUB_RST_N = 30 +GPIO_UBLOX_RST_N = 32 +GPIO_UBLOX_SAFEBOOT_N = 33 +GPIO_UBLOX_PWR_EN = 34 +GPIO_STM_RST_N = 124 +GPIO_STM_BOOT0 = 134 + + +def gpio_init(pin, output): + try: + with open(f"/sys/class/gpio/gpio{pin}/direction", 'wb') as f: + f.write(b"out" if output else b"in") + except Exception as e: + print(f"Failed to set gpio {pin} direction: {e}") + + +def gpio_set(pin, high): + try: + with open(f"/sys/class/gpio/gpio{pin}/value", 'wb') as f: + f.write(b"1" if high else b"0") + except Exception as e: + print(f"Failed to set gpio {pin} value: {e}") diff --git a/common/hardware.py b/common/hardware.py new file mode 100644 index 000000000..67d0c44d1 --- /dev/null +++ b/common/hardware.py @@ -0,0 +1,57 @@ +import os +import random +from typing import cast + +from cereal import log +from common.hardware_android import Android +from common.hardware_tici import Tici +from common.hardware_base import HardwareBase + +EON = os.path.isfile('/EON') +TICI = os.path.isfile('/TICI') +PC = not (EON or TICI) +ANDROID = EON + + +NetworkType = log.ThermalData.NetworkType +NetworkStrength = log.ThermalData.NetworkStrength + + +class Pc(HardwareBase): + def get_sound_card_online(self): + return True + + def get_imei(self, slot): + return "%015d" % random.randint(0, 1 << 32) + + def get_serial(self): + return "cccccccc" + + def get_subscriber_info(self): + return "" + + def reboot(self, reason=None): + print("REBOOT!") + + def get_network_type(self): + return NetworkType.wifi + + def get_sim_info(self): + return { + 'sim_id': '', + 'mcc_mnc': None, + 'network_type': ["Unknown"], + 'sim_state': ["ABSENT"], + 'data_connected': False + } + + def get_network_strength(self, network_type): + return NetworkStrength.unknown + + +if EON: + HARDWARE = cast(HardwareBase, Android()) +elif TICI: + HARDWARE = cast(HardwareBase, Tici()) +else: + HARDWARE = cast(HardwareBase, Pc()) diff --git a/common/hardware_android.py b/common/hardware_android.py new file mode 100644 index 000000000..91e1d1423 --- /dev/null +++ b/common/hardware_android.py @@ -0,0 +1,302 @@ +import binascii +import itertools +import os +import re +import struct +import subprocess + +from cereal import log +from common.hardware_base import HardwareBase + +NetworkType = log.ThermalData.NetworkType +NetworkStrength = log.ThermalData.NetworkStrength + + +def service_call(call): + try: + ret = subprocess.check_output(["service", "call", *call], encoding='utf8').strip() + if 'Parcel' not in ret: + return None + return parse_service_call_bytes(ret) + except subprocess.CalledProcessError: + return None + + +def parse_service_call_unpack(r, fmt): + try: + return struct.unpack(fmt, r)[0] + except Exception: + return None + + +def parse_service_call_string(r): + try: + r = r[8:] # Cut off length field + r = r.decode('utf_16_be') + + # All pairs of two characters seem to be swapped. Not sure why + result = "" + for a, b, in itertools.zip_longest(r[::2], r[1::2], fillvalue='\x00'): + result += b + a + + result = result.replace('\x00', '') + + return result + except Exception: + return None + + +def parse_service_call_bytes(ret): + try: + r = b"" + for hex_part in re.findall(r'[ (]([0-9a-f]{8})', ret): + r += binascii.unhexlify(hex_part) + return r + except Exception: + return None + + +def getprop(key): + return subprocess.check_output(["getprop", key], encoding='utf8').strip() + + +class Android(HardwareBase): + def get_sound_card_online(self): + return (os.path.isfile('/proc/asound/card0/state') and + open('/proc/asound/card0/state').read().strip() == 'ONLINE') + + def get_imei(self, slot): + slot = str(slot) + if slot not in ("0", "1"): + raise ValueError("SIM slot must be 0 or 1") + + return parse_service_call_string(service_call(["iphonesubinfo", "3", "i32", str(slot)])) + + def get_serial(self): + ret = getprop("ro.serialno") + if ret == "": + ret = "cccccccc" + return ret + + def get_subscriber_info(self): + ret = parse_service_call_string(service_call(["iphonesubinfo", "7"])) + if ret is None or len(ret) < 8: + return "" + return ret + + def reboot(self, reason=None): + # e.g. reason="recovery" to go into recover mode + if reason is None: + reason_args = ["null"] + else: + reason_args = ["s16", reason] + + subprocess.check_output([ + "service", "call", "power", "16", # IPowerManager.reboot + "i32", "0", # no confirmation, + *reason_args, + "i32", "1" # wait + ]) + + def get_sim_info(self): + # Used for athena + # TODO: build using methods from this class + sim_state = getprop("gsm.sim.state").split(",") + network_type = getprop("gsm.network.type").split(',') + mcc_mnc = getprop("gsm.sim.operator.numeric") or None + + sim_id = parse_service_call_string(service_call(['iphonesubinfo', '11'])) + cell_data_state = parse_service_call_unpack(service_call(['phone', '46']), ">q") + cell_data_connected = (cell_data_state == 2) + + return { + 'sim_id': sim_id, + 'mcc_mnc': mcc_mnc, + 'network_type': network_type, + 'sim_state': sim_state, + 'data_connected': cell_data_connected + } + + def get_network_type(self): + wifi_check = parse_service_call_string(service_call(["connectivity", "2"])) + if wifi_check is None: + return NetworkType.none + elif 'WIFI' in wifi_check: + return NetworkType.wifi + else: + cell_check = parse_service_call_unpack(service_call(['phone', '59']), ">q") + # from TelephonyManager.java + cell_networks = { + 0: NetworkType.none, + 1: NetworkType.cell2G, + 2: NetworkType.cell2G, + 3: NetworkType.cell3G, + 4: NetworkType.cell2G, + 5: NetworkType.cell3G, + 6: NetworkType.cell3G, + 7: NetworkType.cell3G, + 8: NetworkType.cell3G, + 9: NetworkType.cell3G, + 10: NetworkType.cell3G, + 11: NetworkType.cell2G, + 12: NetworkType.cell3G, + 13: NetworkType.cell4G, + 14: NetworkType.cell4G, + 15: NetworkType.cell3G, + 16: NetworkType.cell2G, + 17: NetworkType.cell3G, + 18: NetworkType.cell4G, + 19: NetworkType.cell4G + } + return cell_networks.get(cell_check, NetworkType.none) + + def get_network_strength(self, network_type): + network_strength = NetworkStrength.unknown + + # from SignalStrength.java + def get_lte_level(rsrp, rssnr): + INT_MAX = 2147483647 + if rsrp == INT_MAX: + lvl_rsrp = NetworkStrength.unknown + elif rsrp >= -95: + lvl_rsrp = NetworkStrength.great + elif rsrp >= -105: + lvl_rsrp = NetworkStrength.good + elif rsrp >= -115: + lvl_rsrp = NetworkStrength.moderate + else: + lvl_rsrp = NetworkStrength.poor + if rssnr == INT_MAX: + lvl_rssnr = NetworkStrength.unknown + elif rssnr >= 45: + lvl_rssnr = NetworkStrength.great + elif rssnr >= 10: + lvl_rssnr = NetworkStrength.good + elif rssnr >= -30: + lvl_rssnr = NetworkStrength.moderate + else: + lvl_rssnr = NetworkStrength.poor + return max(lvl_rsrp, lvl_rssnr) + + def get_tdscdma_level(tdscmadbm): + lvl = NetworkStrength.unknown + if tdscmadbm > -25: + lvl = NetworkStrength.unknown + elif tdscmadbm >= -49: + lvl = NetworkStrength.great + elif tdscmadbm >= -73: + lvl = NetworkStrength.good + elif tdscmadbm >= -97: + lvl = NetworkStrength.moderate + elif tdscmadbm >= -110: + lvl = NetworkStrength.poor + return lvl + + def get_gsm_level(asu): + if asu <= 2 or asu == 99: + lvl = NetworkStrength.unknown + elif asu >= 12: + lvl = NetworkStrength.great + elif asu >= 8: + lvl = NetworkStrength.good + elif asu >= 5: + lvl = NetworkStrength.moderate + else: + lvl = NetworkStrength.poor + return lvl + + def get_evdo_level(evdodbm, evdosnr): + lvl_evdodbm = NetworkStrength.unknown + lvl_evdosnr = NetworkStrength.unknown + if evdodbm >= -65: + lvl_evdodbm = NetworkStrength.great + elif evdodbm >= -75: + lvl_evdodbm = NetworkStrength.good + elif evdodbm >= -90: + lvl_evdodbm = NetworkStrength.moderate + elif evdodbm >= -105: + lvl_evdodbm = NetworkStrength.poor + if evdosnr >= 7: + lvl_evdosnr = NetworkStrength.great + elif evdosnr >= 5: + lvl_evdosnr = NetworkStrength.good + elif evdosnr >= 3: + lvl_evdosnr = NetworkStrength.moderate + elif evdosnr >= 1: + lvl_evdosnr = NetworkStrength.poor + return max(lvl_evdodbm, lvl_evdosnr) + + def get_cdma_level(cdmadbm, cdmaecio): + lvl_cdmadbm = NetworkStrength.unknown + lvl_cdmaecio = NetworkStrength.unknown + if cdmadbm >= -75: + lvl_cdmadbm = NetworkStrength.great + elif cdmadbm >= -85: + lvl_cdmadbm = NetworkStrength.good + elif cdmadbm >= -95: + lvl_cdmadbm = NetworkStrength.moderate + elif cdmadbm >= -100: + lvl_cdmadbm = NetworkStrength.poor + if cdmaecio >= -90: + lvl_cdmaecio = NetworkStrength.great + elif cdmaecio >= -110: + lvl_cdmaecio = NetworkStrength.good + elif cdmaecio >= -130: + lvl_cdmaecio = NetworkStrength.moderate + elif cdmaecio >= -150: + lvl_cdmaecio = NetworkStrength.poor + return max(lvl_cdmadbm, lvl_cdmaecio) + + if network_type == NetworkType.none: + return network_strength + if network_type == NetworkType.wifi: + out = subprocess.check_output('dumpsys connectivity', shell=True).decode('utf-8') + network_strength = NetworkStrength.unknown + for line in out.split('\n'): + signal_str = "SignalStrength: " + if signal_str in line: + lvl_idx_start = line.find(signal_str) + len(signal_str) + lvl_idx_end = line.find(']', lvl_idx_start) + lvl = int(line[lvl_idx_start : lvl_idx_end]) + if lvl >= -50: + network_strength = NetworkStrength.great + elif lvl >= -60: + network_strength = NetworkStrength.good + elif lvl >= -70: + network_strength = NetworkStrength.moderate + else: + network_strength = NetworkStrength.poor + return network_strength + else: + # check cell strength + out = subprocess.check_output('dumpsys telephony.registry', shell=True).decode('utf-8') + for line in out.split('\n'): + if "mSignalStrength" in line: + arr = line.split(' ') + ns = 0 + if ("gsm" in arr[14]): + rsrp = int(arr[9]) + rssnr = int(arr[11]) + ns = get_lte_level(rsrp, rssnr) + if ns == NetworkStrength.unknown: + tdscmadbm = int(arr[13]) + ns = get_tdscdma_level(tdscmadbm) + if ns == NetworkStrength.unknown: + asu = int(arr[1]) + ns = get_gsm_level(asu) + else: + cdmadbm = int(arr[3]) + cdmaecio = int(arr[4]) + evdodbm = int(arr[5]) + evdosnr = int(arr[7]) + lvl_cdma = get_cdma_level(cdmadbm, cdmaecio) + lvl_edmo = get_evdo_level(evdodbm, evdosnr) + if lvl_edmo == NetworkStrength.unknown: + ns = lvl_cdma + elif lvl_cdma == NetworkStrength.unknown: + ns = lvl_edmo + else: + ns = min(lvl_cdma, lvl_edmo) + network_strength = max(network_strength, ns) + + return network_strength diff --git a/common/hardware_base.py b/common/hardware_base.py new file mode 100644 index 000000000..24bb52a5e --- /dev/null +++ b/common/hardware_base.py @@ -0,0 +1,41 @@ +from abc import abstractmethod + + +class HardwareBase: + @staticmethod + def get_cmdline(): + with open('/proc/cmdline') as f: + cmdline = f.read() + return {kv[0]: kv[1] for kv in [s.split('=') for s in cmdline.split(' ')] if len(kv) == 2} + + @abstractmethod + def get_sound_card_online(self): + pass + + @abstractmethod + def get_imei(self, slot): + pass + + @abstractmethod + def get_serial(self): + pass + + @abstractmethod + def get_subscriber_info(self): + pass + + @abstractmethod + def reboot(self, reason=None): + pass + + @abstractmethod + def get_network_type(self): + pass + + @abstractmethod + def get_sim_info(self): + pass + + @abstractmethod + def get_network_strength(self, network_type): + pass diff --git a/common/hardware_tici.py b/common/hardware_tici.py new file mode 100644 index 000000000..223bfc1cc --- /dev/null +++ b/common/hardware_tici.py @@ -0,0 +1,58 @@ +import serial + +from common.hardware_base import HardwareBase +from cereal import log + + +NetworkType = log.ThermalData.NetworkType +NetworkStrength = log.ThermalData.NetworkStrength + + +def run_at_command(cmd, timeout=0.1): + with serial.Serial("/dev/ttyUSB2", timeout=timeout) as ser: + ser.write(cmd + b"\r\n") + ser.readline() # Modem echos request + return ser.readline().decode().rstrip() + + +class Tici(HardwareBase): + def get_sound_card_online(self): + return True + + def get_imei(self, slot): + if slot != 0: + return "" + + for _ in range(10): + try: + imei = run_at_command(b"AT+CGSN") + if len(imei) == 15: + return imei + except serial.SerialException: + pass + + raise RuntimeError("Error getting IMEI") + + def get_serial(self): + return self.get_cmdline()['androidboot.serialno'] + + def get_subscriber_info(self): + return "" + + def reboot(self, reason=None): + print("REBOOT!") + + def get_network_type(self): + return NetworkType.wifi + + def get_sim_info(self): + return { + 'sim_id': '', + 'mcc_mnc': None, + 'network_type': ["Unknown"], + 'sim_state': ["ABSENT"], + 'data_connected': False + } + + def get_network_strength(self, network_type): + return NetworkStrength.unknown diff --git a/common/params.py b/common/params.py index e10670502..d30f2d0df 100755 --- a/common/params.py +++ b/common/params.py @@ -53,12 +53,12 @@ keys = { "AccessToken": [TxType.CLEAR_ON_MANAGER_START], "AthenadPid": [TxType.PERSISTENT], "CalibrationParams": [TxType.PERSISTENT], + "CarBatteryCapacity": [TxType.PERSISTENT], "CarParams": [TxType.CLEAR_ON_MANAGER_START, TxType.CLEAR_ON_PANDA_DISCONNECT], "CarParamsCache": [TxType.CLEAR_ON_MANAGER_START, TxType.CLEAR_ON_PANDA_DISCONNECT], "CarVin": [TxType.CLEAR_ON_MANAGER_START, TxType.CLEAR_ON_PANDA_DISCONNECT], "CommunityFeaturesToggle": [TxType.PERSISTENT], "CompletedTrainingVersion": [TxType.PERSISTENT], - "ControlsParams": [TxType.PERSISTENT], "DisablePowerDown": [TxType.PERSISTENT], "DisableUpdates": [TxType.PERSISTENT], "DoUninstall": [TxType.CLEAR_ON_MANAGER_START], @@ -71,7 +71,6 @@ keys = { "HasCompletedSetup": [TxType.PERSISTENT], "IsDriverViewEnabled": [TxType.CLEAR_ON_MANAGER_START], "IsLdwEnabled": [TxType.PERSISTENT], - "IsGeofenceEnabled": [TxType.PERSISTENT], "IsMetric": [TxType.PERSISTENT], "IsOffroad": [TxType.CLEAR_ON_MANAGER_START], "IsRHD": [TxType.PERSISTENT], @@ -81,10 +80,7 @@ keys = { "LastAthenaPingTime": [TxType.PERSISTENT], "LastUpdateTime": [TxType.PERSISTENT], "LastUpdateException": [TxType.PERSISTENT], - "LimitSetSpeed": [TxType.PERSISTENT], - "LimitSetSpeedNeural": [TxType.PERSISTENT], "LiveParameters": [TxType.PERSISTENT], - "LongitudinalControl": [TxType.PERSISTENT], "OpenpilotEnabledToggle": [TxType.PERSISTENT], "LaneChangeEnabled": [TxType.PERSISTENT], "PandaFirmware": [TxType.CLEAR_ON_MANAGER_START, TxType.CLEAR_ON_PANDA_DISCONNECT], @@ -94,7 +90,6 @@ keys = { "RecordFront": [TxType.PERSISTENT], "ReleaseNotes": [TxType.PERSISTENT], "ShouldDoUpdate": [TxType.CLEAR_ON_MANAGER_START], - "SpeedLimitOffset": [TxType.PERSISTENT], "SubscriberInfo": [TxType.PERSISTENT], "TermsVersion": [TxType.PERSISTENT], "TrainingVersion": [TxType.PERSISTENT], @@ -122,14 +117,15 @@ def fsync_dir(path): class FileLock(): - def __init__(self, path, create): + def __init__(self, path, create, lock_ex): self._path = path self._create = create self._fd = None + self._lock_ex = lock_ex def acquire(self): self._fd = os.open(self._path, os.O_CREAT if self._create else 0) - fcntl.flock(self._fd, fcntl.LOCK_EX) + fcntl.flock(self._fd, fcntl.LOCK_EX if self._lock_ex else fcntl.LOCK_SH) def release(self): if self._fd is not None: @@ -157,8 +153,8 @@ class DBAccessor(): except KeyError: return None - def _get_lock(self, create): - lock = FileLock(os.path.join(self._path, ".lock"), create) + def _get_lock(self, create, lock_ex): + lock = FileLock(os.path.join(self._path, ".lock"), create, lock_ex) lock.acquire() return lock @@ -190,7 +186,7 @@ class DBAccessor(): class DBReader(DBAccessor): def __enter__(self): try: - lock = self._get_lock(False) + lock = self._get_lock(False, False) except OSError as e: # Do not create lock if it does not exist. if e.errno == errno.ENOENT: @@ -228,7 +224,7 @@ class DBWriter(DBAccessor): try: os.chmod(self._path, 0o777) - self._lock = self._get_lock(True) + self._lock = self._get_lock(True, True) self._vals = self._read_values_locked() except Exception: os.umask(self._prev_umask) @@ -317,7 +313,7 @@ def write_db(params_path, key, value): value = value.encode('utf8') prev_umask = os.umask(0) - lock = FileLock(params_path + "/.lock", True) + lock = FileLock(params_path + "/.lock", True, True) lock.acquire() try: diff --git a/common/realtime.py b/common/realtime.py index e73443864..7f4ad5c0d 100644 --- a/common/realtime.py +++ b/common/realtime.py @@ -1,12 +1,10 @@ """Utilities for reading real time clocks and keeping soft real time constraints.""" +import gc import os import time -import platform -import subprocess import multiprocessing -from cffi import FFI -from common.android import ANDROID +from common.hardware import PC from common.common_pyx import sec_since_boot # pylint: disable=no-name-in-module, import-error @@ -17,37 +15,26 @@ DT_DMON = 0.1 # driver monitoring DT_TRML = 0.5 # thermald and manager -ffi = FFI() -ffi.cdef("long syscall(long number, ...);") -libc = ffi.dlopen(None) - -def _get_tid(): - if platform.machine() == "x86_64": - NR_gettid = 186 - elif platform.machine() == "aarch64": - NR_gettid = 178 - else: - raise NotImplementedError - - return libc.syscall(NR_gettid) +class Priority: + MIN_REALTIME = 52 # highest android process priority is 51 + CTRL_LOW = MIN_REALTIME + CTRL_HIGH = MIN_REALTIME + 1 def set_realtime_priority(level): - if os.getuid() != 0: - print("not setting priority, not root") - return + if not PC: + os.sched_setscheduler(0, os.SCHED_FIFO, os.sched_param(level)) - return subprocess.call(['chrt', '-f', '-p', str(level), str(_get_tid())]) def set_core_affinity(core): - if os.getuid() != 0: - print("not setting affinity, not root") - return + if not PC: + os.sched_setaffinity(0, [core,]) - if ANDROID: - return subprocess.call(['taskset', '-p', str(core), str(_get_tid())]) - else: - return -1 + +def config_rt_process(core, priority): + gc.disable() + set_realtime_priority(priority) + set_core_affinity(core) class Ratekeeper(): diff --git a/common/text_window.py b/common/text_window.py index b815d8022..1aa3b885e 100755 --- a/common/text_window.py +++ b/common/text_window.py @@ -5,7 +5,7 @@ import subprocess from common.basedir import BASEDIR -class TextWindow(): +class TextWindow: def __init__(self, s, noop=False): # text window is only implemented for android currently self.text_proc = None diff --git a/launch_chffrplus.sh b/launch_chffrplus.sh index af5483562..7124ef3c4 100755 --- a/launch_chffrplus.sh +++ b/launch_chffrplus.sh @@ -8,6 +8,60 @@ source "$BASEDIR/launch_env.sh" DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )" +function two_init { + # Restrict Android and other system processes to the first two cores + echo 0-1 > /dev/cpuset/background/cpus + echo 0-1 > /dev/cpuset/system-background/cpus + echo 0-1 > /dev/cpuset/foreground/boost/cpus + echo 0-1 > /dev/cpuset/foreground/cpus + echo 0-1 > /dev/cpuset/android/cpus + + # openpilot gets all the cores + echo 0-3 > /dev/cpuset/app/cpus + + # Collect RIL and other possibly long-running I/O interrupts onto CPU 1 + echo 1 > /proc/irq/78/smp_affinity_list # qcom,smd-modem (LTE radio) + echo 1 > /proc/irq/33/smp_affinity_list # ufshcd (flash storage) + echo 1 > /proc/irq/35/smp_affinity_list # wifi (wlan_pci) + # USB traffic needs realtime handling on cpu 3 + [ -d "/proc/irq/733" ] && echo 3 > /proc/irq/733/smp_affinity_list # USB for LeEco + [ -d "/proc/irq/736" ] && echo 3 > /proc/irq/736/smp_affinity_list # USB for OP3T + + + # Check for NEOS update + if [ $(< /VERSION) != "$REQUIRED_NEOS_VERSION" ]; then + if [ -f "$DIR/scripts/continue.sh" ]; then + cp "$DIR/scripts/continue.sh" "/data/data/com.termux/files/continue.sh" + fi + + if [ ! -f "$BASEDIR/prebuilt" ]; then + # Clean old build products, but preserve the scons cache + cd $DIR + scons --clean + git clean -xdf + git submodule foreach --recursive git clean -xdf + fi + + "$DIR/installer/updater/updater" "file://$DIR/installer/updater/update.json" + else + if [[ $(uname -v) == "#1 SMP PREEMPT Wed Jun 10 12:40:53 PDT 2020" ]]; then + "$DIR/installer/updater/updater" "file://$DIR/installer/updater/update_kernel.json" + fi + fi + + # One-time fix for a subset of OP3T with gyro orientation offsets. + # Remove and regenerate qcom sensor registry. Only done on OP3T mainboards. + # Performed exactly once. The old registry is preserved just-in-case, and + # doubles as a flag denoting we've already done the reset. + if ! $(grep -q "letv" /proc/cmdline) && [ ! -f "/persist/comma/op3t-sns-reg-backup" ]; then + echo "Performing OP3T sensor registry reset" + mv /persist/sensors/sns.reg /persist/comma/op3t-sns-reg-backup && + rm -f /persist/sensors/sensors_settings /persist/sensors/error_log /persist/sensors/gyro_sensitity_cal && + echo "restart" > /sys/kernel/debug/msm_subsys/slpi && + sleep 5 # Give Android sensor subsystem a moment to recover + fi +} + function launch { # Wifi scan wpa_cli IFNAME=wlan0 SCAN @@ -54,56 +108,9 @@ function launch { fi fi - # Android and other system processes are not permitted to run on CPU 3 - # NEOS installed app processes can run anywhere - echo 0-2 > /dev/cpuset/background/cpus - echo 0-2 > /dev/cpuset/system-background/cpus - [ -d "/dev/cpuset/foreground/boost/cpus" ] && echo 0-2 > /dev/cpuset/foreground/boost/cpus # Not present in < NEOS 15 - echo 0-2 > /dev/cpuset/foreground/cpus - echo 0-2 > /dev/cpuset/android/cpus - echo 0-3 > /dev/cpuset/app/cpus - - # Collect RIL and other possibly long-running I/O interrupts onto CPU 1 - echo 1 > /proc/irq/78/smp_affinity_list # qcom,smd-modem (LTE radio) - echo 1 > /proc/irq/33/smp_affinity_list # ufshcd (flash storage) - echo 1 > /proc/irq/35/smp_affinity_list # wifi (wlan_pci) - # USB traffic needs realtime handling on cpu 3 - [ -d "/proc/irq/733" ] && echo 3 > /proc/irq/733/smp_affinity_list # USB for LeEco - [ -d "/proc/irq/736" ] && echo 3 > /proc/irq/736/smp_affinity_list # USB for OP3T - - - # Check for NEOS update - if [ $(< /VERSION) != "$REQUIRED_NEOS_VERSION" ]; then - if [ -f "$DIR/scripts/continue.sh" ]; then - cp "$DIR/scripts/continue.sh" "/data/data/com.termux/files/continue.sh" - fi - - if [ ! -f "$BASEDIR/prebuilt" ]; then - # Clean old build products, but preserve the scons cache - cd $DIR - scons --clean - git clean -xdf - git submodule foreach --recursive git clean -xdf - fi - - "$DIR/installer/updater/updater" "file://$DIR/installer/updater/update.json" - else - if [[ $(uname -v) == "#1 SMP PREEMPT Wed Jun 10 12:40:53 PDT 2020" ]]; then - "$DIR/installer/updater/updater" "file://$DIR/installer/updater/update_kernel.json" - fi - fi - - # One-time fix for a subset of OP3T with gyro orientation offsets. - # Remove and regenerate qcom sensor registry. Only done on OP3T mainboards. - # Performed exactly once. The old registry is preserved just-in-case, and - # doubles as a flag denoting we've already done the reset. - # TODO: we should really grow per-platform detect and setup routines - if ! $(grep -q "letv" /proc/cmdline) && [ ! -f "/persist/comma/op3t-sns-reg-backup" ]; then - echo "Performing OP3T sensor registry reset" - mv /persist/sensors/sns.reg /persist/comma/op3t-sns-reg-backup && - rm -f /persist/sensors/sensors_settings /persist/sensors/error_log /persist/sensors/gyro_sensitity_cal && - echo "restart" > /sys/kernel/debug/msm_subsys/slpi && - sleep 5 # Give Android sensor subsystem a moment to recover + # comma two init + if [ -f /EON ]; then + two_init fi # handle pythonpath diff --git a/opendbc/can/common_pyx_setup.py b/opendbc/can/common_pyx_setup.py index 72a0cde7c..04f95359a 100644 --- a/opendbc/can/common_pyx_setup.py +++ b/opendbc/can/common_pyx_setup.py @@ -34,7 +34,7 @@ class BuildExtWithoutPlatformSuffix(build_ext): return get_ext_filename_without_platform_suffix(filename) -extra_compile_args = ["-std=c++14"] +extra_compile_args = ["-std=c++14", "-Wno-nullability-completeness"] ARCH = subprocess.check_output(["uname", "-m"], encoding='utf8').rstrip() # pylint: disable=unexpected-keyword-arg if ARCH == "aarch64": extra_compile_args += ["-Wno-deprecated-register"] diff --git a/opendbc/can/packer_pyx.pyx b/opendbc/can/packer_pyx.pyx index 00f83eca6..14e2f81d3 100644 --- a/opendbc/can/packer_pyx.pyx +++ b/opendbc/can/packer_pyx.pyx @@ -20,9 +20,11 @@ cdef class CANPacker: map[int, int] address_to_size def __init__(self, dbc_name): - self.packer = new cpp_CANPacker(dbc_name) self.dbc = dbc_lookup(dbc_name) - + if not self.dbc: + raise RuntimeError("Can't lookup" + dbc_name) + + self.packer = new cpp_CANPacker(dbc_name) num_msgs = self.dbc[0].num_msgs for i in range(num_msgs): msg = self.dbc[0].msgs[i] @@ -37,7 +39,7 @@ cdef class CANPacker: for name, value in values.iteritems(): n = name.encode('utf8') - names.append(n) # TODO: find better way to keep reference to temp string arround + names.append(n) # TODO: find better way to keep reference to temp string around spv.name = n spv.value = value diff --git a/opendbc/can/parser.cc b/opendbc/can/parser.cc index 5addd4168..c6d85d9ee 100644 --- a/opendbc/can/parser.cc +++ b/opendbc/can/parser.cc @@ -187,7 +187,7 @@ void CANParser::UpdateCans(uint64_t sec, const capnp::List::Rea continue; } - if (cmsg.getDat().size() > 8) continue; //shouldnt ever happen + if (cmsg.getDat().size() > 8) continue; //shouldn't ever happen uint8_t dat[8] = {0}; memcpy(dat, cmsg.getDat().begin(), cmsg.getDat().size()); diff --git a/opendbc/can/parser.py b/opendbc/can/parser.py index 107a839b1..04722115c 100644 --- a/opendbc/can/parser.py +++ b/opendbc/can/parser.py @@ -1,2 +1,2 @@ -from opendbc.can.parser_pyx import CANParser # pylint: disable=no-name-in-module, import-error -assert CANParser +from opendbc.can.parser_pyx import CANParser, CANDefine # pylint: disable=no-name-in-module, import-error +assert CANParser, CANDefine diff --git a/opendbc/can/parser_pyx.pyx b/opendbc/can/parser_pyx.pyx index bd5adf6ff..913f58230 100644 --- a/opendbc/can/parser_pyx.pyx +++ b/opendbc/can/parser_pyx.pyx @@ -17,7 +17,6 @@ from collections import defaultdict cdef int CAN_INVALID_CNT = 5 - cdef class CANParser: cdef: cpp_CANParser *can @@ -37,10 +36,11 @@ cdef class CANParser: def __init__(self, dbc_name, signals, checks=None, bus=0): if checks is None: checks = [] - self.can_valid = True self.dbc_name = dbc_name self.dbc = dbc_lookup(dbc_name) + if not self.dbc: + raise RuntimeError("Can't lookup" + dbc_name) self.vl = {} self.ts = {} @@ -110,7 +110,7 @@ cdef class CANParser: for cv in can_values: - # Cast char * directly to unicde + # Cast char * directly to unicode name = self.address_to_msg_name[cv.address].c_str() cv_name = cv.name @@ -148,7 +148,9 @@ cdef class CANDefine(): def __init__(self, dbc_name): self.dbc_name = dbc_name self.dbc = dbc_lookup(dbc_name) - + if not self.dbc: + raise RuntimeError("Can't lookup" + dbc_name) + num_vals = self.dbc[0].num_vals address_to_msg_name = {} diff --git a/opendbc/can/process_dbc.py b/opendbc/can/process_dbc.py index 5564021af..9025331dd 100755 --- a/opendbc/can/process_dbc.py +++ b/opendbc/can/process_dbc.py @@ -81,7 +81,7 @@ def process(in_fn, out_fn): if sig.start_bit % 8 != checksum_start_bit: sys.exit("%s: CHECKSUM starts at wrong bit" % dbc_msg_name) if little_endian != sig.is_little_endian: - sys.exit("%s: CHECKSUM has wrong endianess" % dbc_msg_name) + sys.exit("%s: CHECKSUM has wrong endianness" % dbc_msg_name) # counter rules if sig.name == "COUNTER": if counter_size is not None and sig.size != counter_size: @@ -90,7 +90,7 @@ def process(in_fn, out_fn): print(counter_start_bit, sig.start_bit) sys.exit("%s: COUNTER starts at wrong bit" % dbc_msg_name) if little_endian != sig.is_little_endian: - sys.exit("%s: COUNTER has wrong endianess" % dbc_msg_name) + sys.exit("%s: COUNTER has wrong endianness" % dbc_msg_name) # pedal rules if address in [0x200, 0x201]: if sig.name == "COUNTER_PEDAL" and sig.size != 4: diff --git a/opendbc/chrysler_pacifica_2017_hybrid.dbc b/opendbc/chrysler_pacifica_2017_hybrid.dbc index 4bd919655..54e4819f2 100644 --- a/opendbc/chrysler_pacifica_2017_hybrid.dbc +++ b/opendbc/chrysler_pacifica_2017_hybrid.dbc @@ -409,7 +409,7 @@ 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"; -CM_ SG_ 291 ENERGY_SMOOTHER_CURVE "unusre what it is, but smoother"; +CM_ SG_ 291 ENERGY_SMOOTHER_CURVE "unsure what it is, but smoother"; CM_ SG_ 308 ACCEL_134 "only set when human presses accel pedal"; CM_ SG_ 532 NOISY_SLOWLY_DECREASING "perhaps battery but do not know"; CM_ SG_ 816 TRACTION_OFF "set when traction off button is enabled"; diff --git a/opendbc/gm_global_a_object.dbc b/opendbc/gm_global_a_object.dbc index 1532e90da..4643a640a 100644 --- a/opendbc/gm_global_a_object.dbc +++ b/opendbc/gm_global_a_object.dbc @@ -37,7 +37,7 @@ BU_: K109_FCM B233B_LRR NEO VIS_FO VIS2_FO K124_ASCM Vector__XXX EOCM_F_FO EOCM2 VAL_TABLE_ RangeMode 1 "Active" 0 "Inactive" ; VAL_TABLE_ TrkConf 3 "Confident" 2 "Speculative" 1 "Highly speculative" 0 "Invalid" ; VAL_TABLE_ TrkMeasStatus 3 "Measured current cycle" 2 "Latent track not detected" 1 "New object" 0 "No object" ; -VAL_TABLE_ TrkDynProp 4 "Moving in opposite direction" 3 "Moving in same direction" 2 "Has moved but currenty stopped" 1 "Has never moved," 0 "Unkown" ; +VAL_TABLE_ TrkDynProp 4 "Moving in opposite direction" 3 "Moving in same direction" 2 "Has moved but currently stopped" 1 "Has never moved," 0 "Unknown" ; VAL_TABLE_ FrntVsnInPthVehBrkNwSt 10 "Active" 5 "Inactive" ; VAL_TABLE_ FrntVsnClostPedBrkNwSt 10 "Active" 5 "Inactive" ; VAL_TABLE_ LaneSnsLLnPosValid 1 "Invalid" 0 "Valid" ; diff --git a/opendbc/lexus_ct200h_2018_pt_generated.dbc b/opendbc/lexus_ct200h_2018_pt_generated.dbc index 72ce7b1c6..6cc7eba41 100644 --- a/opendbc/lexus_ct200h_2018_pt_generated.dbc +++ b/opendbc/lexus_ct200h_2018_pt_generated.dbc @@ -311,7 +311,7 @@ CM_ SG_ 36 STEERING_TORQUE "does not seem the steer torque, tbd"; CM_ SG_ 37 STEER_FRACTION "1/15th of the signal STEER_ANGLE, which is 1.5 deg; note that 0x8 is never set"; CM_ SG_ 37 STEER_RATE "factor is tbd"; CM_ SG_ 466 ACCEL_NET "net acceleration produced by the system, given ACCEL_CMD, road grade and other factors"; -CM_ SG_ 467 SET_SPEED "43 kph are shown as 28mph, so conversion isnt perfect"; +CM_ SG_ 467 SET_SPEED "43 kph are shown as 28mph, so conversion isn't perfect"; CM_ SG_ 467 LOW_SPEED_LOCKOUT "in low speed lockout, system would always disengage below 28mph"; CM_ SG_ 560 BRAKE_PRESSED "another brake pressed?"; CM_ SG_ 608 STEER_TORQUE_DRIVER "driver torque"; diff --git a/opendbc/lexus_is_2018_pt_generated.dbc b/opendbc/lexus_is_2018_pt_generated.dbc index c08b1fee6..e1e009ef0 100644 --- a/opendbc/lexus_is_2018_pt_generated.dbc +++ b/opendbc/lexus_is_2018_pt_generated.dbc @@ -311,7 +311,7 @@ CM_ SG_ 36 STEERING_TORQUE "does not seem the steer torque, tbd"; CM_ SG_ 37 STEER_FRACTION "1/15th of the signal STEER_ANGLE, which is 1.5 deg; note that 0x8 is never set"; CM_ SG_ 37 STEER_RATE "factor is tbd"; CM_ SG_ 466 ACCEL_NET "net acceleration produced by the system, given ACCEL_CMD, road grade and other factors"; -CM_ SG_ 467 SET_SPEED "43 kph are shown as 28mph, so conversion isnt perfect"; +CM_ SG_ 467 SET_SPEED "43 kph are shown as 28mph, so conversion isn't perfect"; CM_ SG_ 467 LOW_SPEED_LOCKOUT "in low speed lockout, system would always disengage below 28mph"; CM_ SG_ 560 BRAKE_PRESSED "another brake pressed?"; CM_ SG_ 608 STEER_TORQUE_DRIVER "driver torque"; diff --git a/opendbc/lexus_nx300h_2018_pt_generated.dbc b/opendbc/lexus_nx300h_2018_pt_generated.dbc index 278bb2a2f..7e422ad2a 100644 --- a/opendbc/lexus_nx300h_2018_pt_generated.dbc +++ b/opendbc/lexus_nx300h_2018_pt_generated.dbc @@ -311,7 +311,7 @@ CM_ SG_ 36 STEERING_TORQUE "does not seem the steer torque, tbd"; CM_ SG_ 37 STEER_FRACTION "1/15th of the signal STEER_ANGLE, which is 1.5 deg; note that 0x8 is never set"; CM_ SG_ 37 STEER_RATE "factor is tbd"; CM_ SG_ 466 ACCEL_NET "net acceleration produced by the system, given ACCEL_CMD, road grade and other factors"; -CM_ SG_ 467 SET_SPEED "43 kph are shown as 28mph, so conversion isnt perfect"; +CM_ SG_ 467 SET_SPEED "43 kph are shown as 28mph, so conversion isn't perfect"; CM_ SG_ 467 LOW_SPEED_LOCKOUT "in low speed lockout, system would always disengage below 28mph"; CM_ SG_ 560 BRAKE_PRESSED "another brake pressed?"; CM_ SG_ 608 STEER_TORQUE_DRIVER "driver torque"; diff --git a/opendbc/lexus_rx_350_2016_pt_generated.dbc b/opendbc/lexus_rx_350_2016_pt_generated.dbc index 55b268be8..c8bb8c387 100644 --- a/opendbc/lexus_rx_350_2016_pt_generated.dbc +++ b/opendbc/lexus_rx_350_2016_pt_generated.dbc @@ -311,7 +311,7 @@ CM_ SG_ 36 STEERING_TORQUE "does not seem the steer torque, tbd"; CM_ SG_ 37 STEER_FRACTION "1/15th of the signal STEER_ANGLE, which is 1.5 deg; note that 0x8 is never set"; CM_ SG_ 37 STEER_RATE "factor is tbd"; CM_ SG_ 466 ACCEL_NET "net acceleration produced by the system, given ACCEL_CMD, road grade and other factors"; -CM_ SG_ 467 SET_SPEED "43 kph are shown as 28mph, so conversion isnt perfect"; +CM_ SG_ 467 SET_SPEED "43 kph are shown as 28mph, so conversion isn't perfect"; CM_ SG_ 467 LOW_SPEED_LOCKOUT "in low speed lockout, system would always disengage below 28mph"; CM_ SG_ 560 BRAKE_PRESSED "another brake pressed?"; CM_ SG_ 608 STEER_TORQUE_DRIVER "driver torque"; diff --git a/opendbc/lexus_rx_hybrid_2017_pt_generated.dbc b/opendbc/lexus_rx_hybrid_2017_pt_generated.dbc index c353ab475..4fe9f00aa 100644 --- a/opendbc/lexus_rx_hybrid_2017_pt_generated.dbc +++ b/opendbc/lexus_rx_hybrid_2017_pt_generated.dbc @@ -311,7 +311,7 @@ CM_ SG_ 36 STEERING_TORQUE "does not seem the steer torque, tbd"; CM_ SG_ 37 STEER_FRACTION "1/15th of the signal STEER_ANGLE, which is 1.5 deg; note that 0x8 is never set"; CM_ SG_ 37 STEER_RATE "factor is tbd"; CM_ SG_ 466 ACCEL_NET "net acceleration produced by the system, given ACCEL_CMD, road grade and other factors"; -CM_ SG_ 467 SET_SPEED "43 kph are shown as 28mph, so conversion isnt perfect"; +CM_ SG_ 467 SET_SPEED "43 kph are shown as 28mph, so conversion isn't perfect"; CM_ SG_ 467 LOW_SPEED_LOCKOUT "in low speed lockout, system would always disengage below 28mph"; CM_ SG_ 560 BRAKE_PRESSED "another brake pressed?"; CM_ SG_ 608 STEER_TORQUE_DRIVER "driver torque"; diff --git a/opendbc/toyota_avalon_2017_pt_generated.dbc b/opendbc/toyota_avalon_2017_pt_generated.dbc index d6f546e3b..295bb102f 100644 --- a/opendbc/toyota_avalon_2017_pt_generated.dbc +++ b/opendbc/toyota_avalon_2017_pt_generated.dbc @@ -311,7 +311,7 @@ CM_ SG_ 36 STEERING_TORQUE "does not seem the steer torque, tbd"; CM_ SG_ 37 STEER_FRACTION "1/15th of the signal STEER_ANGLE, which is 1.5 deg; note that 0x8 is never set"; CM_ SG_ 37 STEER_RATE "factor is tbd"; CM_ SG_ 466 ACCEL_NET "net acceleration produced by the system, given ACCEL_CMD, road grade and other factors"; -CM_ SG_ 467 SET_SPEED "43 kph are shown as 28mph, so conversion isnt perfect"; +CM_ SG_ 467 SET_SPEED "43 kph are shown as 28mph, so conversion isn't perfect"; CM_ SG_ 467 LOW_SPEED_LOCKOUT "in low speed lockout, system would always disengage below 28mph"; CM_ SG_ 560 BRAKE_PRESSED "another brake pressed?"; CM_ SG_ 608 STEER_TORQUE_DRIVER "driver torque"; diff --git a/opendbc/toyota_camry_hybrid_2018_pt_generated.dbc b/opendbc/toyota_camry_hybrid_2018_pt_generated.dbc index 393a47828..56fb32ff6 100644 --- a/opendbc/toyota_camry_hybrid_2018_pt_generated.dbc +++ b/opendbc/toyota_camry_hybrid_2018_pt_generated.dbc @@ -311,7 +311,7 @@ CM_ SG_ 36 STEERING_TORQUE "does not seem the steer torque, tbd"; CM_ SG_ 37 STEER_FRACTION "1/15th of the signal STEER_ANGLE, which is 1.5 deg; note that 0x8 is never set"; CM_ SG_ 37 STEER_RATE "factor is tbd"; CM_ SG_ 466 ACCEL_NET "net acceleration produced by the system, given ACCEL_CMD, road grade and other factors"; -CM_ SG_ 467 SET_SPEED "43 kph are shown as 28mph, so conversion isnt perfect"; +CM_ SG_ 467 SET_SPEED "43 kph are shown as 28mph, so conversion isn't perfect"; CM_ SG_ 467 LOW_SPEED_LOCKOUT "in low speed lockout, system would always disengage below 28mph"; CM_ SG_ 560 BRAKE_PRESSED "another brake pressed?"; CM_ SG_ 608 STEER_TORQUE_DRIVER "driver torque"; diff --git a/opendbc/toyota_corolla_2017_pt_generated.dbc b/opendbc/toyota_corolla_2017_pt_generated.dbc index 70be61d02..7ad41ef3a 100644 --- a/opendbc/toyota_corolla_2017_pt_generated.dbc +++ b/opendbc/toyota_corolla_2017_pt_generated.dbc @@ -311,7 +311,7 @@ CM_ SG_ 36 STEERING_TORQUE "does not seem the steer torque, tbd"; CM_ SG_ 37 STEER_FRACTION "1/15th of the signal STEER_ANGLE, which is 1.5 deg; note that 0x8 is never set"; CM_ SG_ 37 STEER_RATE "factor is tbd"; CM_ SG_ 466 ACCEL_NET "net acceleration produced by the system, given ACCEL_CMD, road grade and other factors"; -CM_ SG_ 467 SET_SPEED "43 kph are shown as 28mph, so conversion isnt perfect"; +CM_ SG_ 467 SET_SPEED "43 kph are shown as 28mph, so conversion isn't perfect"; CM_ SG_ 467 LOW_SPEED_LOCKOUT "in low speed lockout, system would always disengage below 28mph"; CM_ SG_ 560 BRAKE_PRESSED "another brake pressed?"; CM_ SG_ 608 STEER_TORQUE_DRIVER "driver torque"; @@ -375,7 +375,7 @@ BO_ 705 GAS_PEDAL: 8 XXX SG_ GAS_PEDAL : 55|8@0+ (0.005,0) [0|1] "" XXX BO_ 608 STEER_TORQUE_SENSOR: 8 XXX - SG_ STEER_TORQUE_EPS : 47|16@0- (1.0,0) [-20000|20000] "" XXX + SG_ STEER_TORQUE_EPS : 47|16@0- (0.88,0) [-20000|20000] "" XXX SG_ STEER_TORQUE_DRIVER : 15|16@0- (1,0) [-32768|32767] "" XXX SG_ STEER_OVERRIDE : 0|1@0+ (1,0) [0|1] "" XXX SG_ CHECKSUM : 63|8@0+ (1,0) [0|255] "" XXX diff --git a/opendbc/toyota_highlander_2017_pt_generated.dbc b/opendbc/toyota_highlander_2017_pt_generated.dbc index 4c7d18386..8db51ffdb 100644 --- a/opendbc/toyota_highlander_2017_pt_generated.dbc +++ b/opendbc/toyota_highlander_2017_pt_generated.dbc @@ -311,7 +311,7 @@ CM_ SG_ 36 STEERING_TORQUE "does not seem the steer torque, tbd"; CM_ SG_ 37 STEER_FRACTION "1/15th of the signal STEER_ANGLE, which is 1.5 deg; note that 0x8 is never set"; CM_ SG_ 37 STEER_RATE "factor is tbd"; CM_ SG_ 466 ACCEL_NET "net acceleration produced by the system, given ACCEL_CMD, road grade and other factors"; -CM_ SG_ 467 SET_SPEED "43 kph are shown as 28mph, so conversion isnt perfect"; +CM_ SG_ 467 SET_SPEED "43 kph are shown as 28mph, so conversion isn't perfect"; CM_ SG_ 467 LOW_SPEED_LOCKOUT "in low speed lockout, system would always disengage below 28mph"; CM_ SG_ 560 BRAKE_PRESSED "another brake pressed?"; CM_ SG_ 608 STEER_TORQUE_DRIVER "driver torque"; diff --git a/opendbc/toyota_highlander_hybrid_2018_pt_generated.dbc b/opendbc/toyota_highlander_hybrid_2018_pt_generated.dbc index ccaf67791..b9dd821ef 100644 --- a/opendbc/toyota_highlander_hybrid_2018_pt_generated.dbc +++ b/opendbc/toyota_highlander_hybrid_2018_pt_generated.dbc @@ -311,7 +311,7 @@ CM_ SG_ 36 STEERING_TORQUE "does not seem the steer torque, tbd"; CM_ SG_ 37 STEER_FRACTION "1/15th of the signal STEER_ANGLE, which is 1.5 deg; note that 0x8 is never set"; CM_ SG_ 37 STEER_RATE "factor is tbd"; CM_ SG_ 466 ACCEL_NET "net acceleration produced by the system, given ACCEL_CMD, road grade and other factors"; -CM_ SG_ 467 SET_SPEED "43 kph are shown as 28mph, so conversion isnt perfect"; +CM_ SG_ 467 SET_SPEED "43 kph are shown as 28mph, so conversion isn't perfect"; CM_ SG_ 467 LOW_SPEED_LOCKOUT "in low speed lockout, system would always disengage below 28mph"; CM_ SG_ 560 BRAKE_PRESSED "another brake pressed?"; CM_ SG_ 608 STEER_TORQUE_DRIVER "driver torque"; diff --git a/opendbc/toyota_nodsu_hybrid_pt_generated.dbc b/opendbc/toyota_nodsu_hybrid_pt_generated.dbc index 09fe04b34..0569da070 100644 --- a/opendbc/toyota_nodsu_hybrid_pt_generated.dbc +++ b/opendbc/toyota_nodsu_hybrid_pt_generated.dbc @@ -328,7 +328,7 @@ CM_ SG_ 36 STEERING_TORQUE "does not seem the steer torque, tbd"; CM_ SG_ 37 STEER_FRACTION "1/15th of the signal STEER_ANGLE, which is 1.5 deg; note that 0x8 is never set"; CM_ SG_ 37 STEER_RATE "factor is tbd"; CM_ SG_ 466 ACCEL_NET "net acceleration produced by the system, given ACCEL_CMD, road grade and other factors"; -CM_ SG_ 467 SET_SPEED "43 kph are shown as 28mph, so conversion isnt perfect"; +CM_ SG_ 467 SET_SPEED "43 kph are shown as 28mph, so conversion isn't perfect"; CM_ SG_ 467 LOW_SPEED_LOCKOUT "in low speed lockout, system would always disengage below 28mph"; CM_ SG_ 560 BRAKE_PRESSED "another brake pressed?"; CM_ SG_ 608 STEER_TORQUE_DRIVER "driver torque"; diff --git a/opendbc/toyota_nodsu_pt_generated.dbc b/opendbc/toyota_nodsu_pt_generated.dbc index e6f4a5305..ce38bf50a 100644 --- a/opendbc/toyota_nodsu_pt_generated.dbc +++ b/opendbc/toyota_nodsu_pt_generated.dbc @@ -328,7 +328,7 @@ CM_ SG_ 36 STEERING_TORQUE "does not seem the steer torque, tbd"; CM_ SG_ 37 STEER_FRACTION "1/15th of the signal STEER_ANGLE, which is 1.5 deg; note that 0x8 is never set"; CM_ SG_ 37 STEER_RATE "factor is tbd"; CM_ SG_ 466 ACCEL_NET "net acceleration produced by the system, given ACCEL_CMD, road grade and other factors"; -CM_ SG_ 467 SET_SPEED "43 kph are shown as 28mph, so conversion isnt perfect"; +CM_ SG_ 467 SET_SPEED "43 kph are shown as 28mph, so conversion isn't perfect"; CM_ SG_ 467 LOW_SPEED_LOCKOUT "in low speed lockout, system would always disengage below 28mph"; CM_ SG_ 560 BRAKE_PRESSED "another brake pressed?"; CM_ SG_ 608 STEER_TORQUE_DRIVER "driver torque"; diff --git a/opendbc/toyota_prius_2017_pt_generated.dbc b/opendbc/toyota_prius_2017_pt_generated.dbc index 419a88bb9..aef24541c 100644 --- a/opendbc/toyota_prius_2017_pt_generated.dbc +++ b/opendbc/toyota_prius_2017_pt_generated.dbc @@ -311,7 +311,7 @@ CM_ SG_ 36 STEERING_TORQUE "does not seem the steer torque, tbd"; CM_ SG_ 37 STEER_FRACTION "1/15th of the signal STEER_ANGLE, which is 1.5 deg; note that 0x8 is never set"; CM_ SG_ 37 STEER_RATE "factor is tbd"; CM_ SG_ 466 ACCEL_NET "net acceleration produced by the system, given ACCEL_CMD, road grade and other factors"; -CM_ SG_ 467 SET_SPEED "43 kph are shown as 28mph, so conversion isnt perfect"; +CM_ SG_ 467 SET_SPEED "43 kph are shown as 28mph, so conversion isn't perfect"; CM_ SG_ 467 LOW_SPEED_LOCKOUT "in low speed lockout, system would always disengage below 28mph"; CM_ SG_ 560 BRAKE_PRESSED "another brake pressed?"; CM_ SG_ 608 STEER_TORQUE_DRIVER "driver torque"; diff --git a/opendbc/toyota_rav4_2017_pt_generated.dbc b/opendbc/toyota_rav4_2017_pt_generated.dbc index 2b7e38b72..ece2f15ee 100644 --- a/opendbc/toyota_rav4_2017_pt_generated.dbc +++ b/opendbc/toyota_rav4_2017_pt_generated.dbc @@ -311,7 +311,7 @@ CM_ SG_ 36 STEERING_TORQUE "does not seem the steer torque, tbd"; CM_ SG_ 37 STEER_FRACTION "1/15th of the signal STEER_ANGLE, which is 1.5 deg; note that 0x8 is never set"; CM_ SG_ 37 STEER_RATE "factor is tbd"; CM_ SG_ 466 ACCEL_NET "net acceleration produced by the system, given ACCEL_CMD, road grade and other factors"; -CM_ SG_ 467 SET_SPEED "43 kph are shown as 28mph, so conversion isnt perfect"; +CM_ SG_ 467 SET_SPEED "43 kph are shown as 28mph, so conversion isn't perfect"; CM_ SG_ 467 LOW_SPEED_LOCKOUT "in low speed lockout, system would always disengage below 28mph"; CM_ SG_ 560 BRAKE_PRESSED "another brake pressed?"; CM_ SG_ 608 STEER_TORQUE_DRIVER "driver torque"; diff --git a/opendbc/toyota_rav4_hybrid_2017_pt_generated.dbc b/opendbc/toyota_rav4_hybrid_2017_pt_generated.dbc index ae1985f6c..4822e1260 100644 --- a/opendbc/toyota_rav4_hybrid_2017_pt_generated.dbc +++ b/opendbc/toyota_rav4_hybrid_2017_pt_generated.dbc @@ -311,7 +311,7 @@ CM_ SG_ 36 STEERING_TORQUE "does not seem the steer torque, tbd"; CM_ SG_ 37 STEER_FRACTION "1/15th of the signal STEER_ANGLE, which is 1.5 deg; note that 0x8 is never set"; CM_ SG_ 37 STEER_RATE "factor is tbd"; CM_ SG_ 466 ACCEL_NET "net acceleration produced by the system, given ACCEL_CMD, road grade and other factors"; -CM_ SG_ 467 SET_SPEED "43 kph are shown as 28mph, so conversion isnt perfect"; +CM_ SG_ 467 SET_SPEED "43 kph are shown as 28mph, so conversion isn't perfect"; CM_ SG_ 467 LOW_SPEED_LOCKOUT "in low speed lockout, system would always disengage below 28mph"; CM_ SG_ 560 BRAKE_PRESSED "another brake pressed?"; CM_ SG_ 608 STEER_TORQUE_DRIVER "driver torque"; diff --git a/opendbc/toyota_sienna_xle_2018_pt_generated.dbc b/opendbc/toyota_sienna_xle_2018_pt_generated.dbc index da5337fef..49735696f 100644 --- a/opendbc/toyota_sienna_xle_2018_pt_generated.dbc +++ b/opendbc/toyota_sienna_xle_2018_pt_generated.dbc @@ -311,7 +311,7 @@ CM_ SG_ 36 STEERING_TORQUE "does not seem the steer torque, tbd"; CM_ SG_ 37 STEER_FRACTION "1/15th of the signal STEER_ANGLE, which is 1.5 deg; note that 0x8 is never set"; CM_ SG_ 37 STEER_RATE "factor is tbd"; CM_ SG_ 466 ACCEL_NET "net acceleration produced by the system, given ACCEL_CMD, road grade and other factors"; -CM_ SG_ 467 SET_SPEED "43 kph are shown as 28mph, so conversion isnt perfect"; +CM_ SG_ 467 SET_SPEED "43 kph are shown as 28mph, so conversion isn't perfect"; CM_ SG_ 467 LOW_SPEED_LOCKOUT "in low speed lockout, system would always disengage below 28mph"; CM_ SG_ 560 BRAKE_PRESSED "another brake pressed?"; CM_ SG_ 608 STEER_TORQUE_DRIVER "driver torque"; diff --git a/panda/__init__.py b/panda/__init__.py index deeea46eb..09466ddd5 100644 --- a/panda/__init__.py +++ b/panda/__init__.py @@ -1,3 +1,3 @@ # flake8: noqa # pylint: skip-file -from .python import Panda, PandaWifiStreaming, PandaDFU, ESPROM, CesantaFlasher, flash_release, BASEDIR, ensure_st_up_to_date, build_st, PandaSerial +from .python import Panda, PandaWifiStreaming, PandaDFU, flash_release, BASEDIR, ensure_st_up_to_date, build_st, PandaSerial diff --git a/panda/board/board.h b/panda/board/board.h index 84fca5469..19392c9ea 100644 --- a/panda/board/board.h +++ b/panda/board/board.h @@ -9,6 +9,7 @@ #ifdef PANDA #include "drivers/fan.h" #include "drivers/rtc.h" + #include "drivers/clock_source.h" #include "boards/white.h" #include "boards/grey.h" #include "boards/black.h" @@ -23,7 +24,7 @@ void detect_board_type(void) { // SPI lines floating: white (TODO: is this reliable? Not really, we have to enable ESP/GPS to be able to detect this on the UART) set_gpio_output(GPIOC, 14, 1); set_gpio_output(GPIOC, 5, 1); - if(!detect_with_pull(GPIOB, 1, PULL_UP) && detect_with_pull(GPIOB, 15, PULL_UP)){ + if(!detect_with_pull(GPIOB, 1, PULL_UP) && !detect_with_pull(GPIOB, 7, PULL_UP)){ hw_type = HW_TYPE_DOS; current_board = &board_dos; } else if((detect_with_pull(GPIOA, 4, PULL_DOWN)) || (detect_with_pull(GPIOA, 5, PULL_DOWN)) || (detect_with_pull(GPIOA, 6, PULL_DOWN)) || (detect_with_pull(GPIOA, 7, PULL_DOWN))){ @@ -53,22 +54,10 @@ void detect_board_type(void) { // ///// Configuration detection ///// // bool has_external_debug_serial = 0; -bool is_entering_bootmode = 0; void detect_configuration(void) { // detect if external serial debugging is present has_external_debug_serial = detect_with_pull(GPIOA, 3, PULL_DOWN); - - #ifdef PANDA - if(hw_type == HW_TYPE_WHITE_PANDA) { - // check if the ESP is trying to put me in boot mode - is_entering_bootmode = !detect_with_pull(GPIOB, 0, PULL_UP); - } else { - is_entering_bootmode = 0; - } - #else - is_entering_bootmode = 0; - #endif } // ///// Board functions ///// // diff --git a/panda/board/board_declarations.h b/panda/board/board_declarations.h index d5e9e06a8..963539e00 100644 --- a/panda/board/board_declarations.h +++ b/panda/board/board_declarations.h @@ -1,10 +1,10 @@ // ******************** Prototypes ******************** typedef void (*board_init)(void); -typedef void (*board_enable_can_transciever)(uint8_t transciever, bool enabled); -typedef void (*board_enable_can_transcievers)(bool enabled); +typedef void (*board_enable_can_transceiver)(uint8_t transceiver, bool enabled); +typedef void (*board_enable_can_transceivers)(bool enabled); typedef void (*board_set_led)(uint8_t color, bool enabled); typedef void (*board_set_usb_power_mode)(uint8_t mode); -typedef void (*board_set_esp_gps_mode)(uint8_t mode); +typedef void (*board_set_gps_mode)(uint8_t mode); typedef void (*board_set_can_mode)(uint8_t mode); typedef void (*board_usb_power_mode_tick)(uint32_t uptime); typedef bool (*board_check_ignition)(void); @@ -12,16 +12,18 @@ typedef uint32_t (*board_read_current)(void); typedef void (*board_set_ir_power)(uint8_t percentage); typedef void (*board_set_fan_power)(uint8_t percentage); typedef void (*board_set_phone_power)(bool enabled); +typedef void (*board_set_clock_source_mode)(uint8_t mode); +typedef void (*board_set_siren)(bool enabled); struct board { const char *board_type; const harness_configuration *harness_config; board_init init; - board_enable_can_transciever enable_can_transciever; - board_enable_can_transcievers enable_can_transcievers; + board_enable_can_transceiver enable_can_transceiver; + board_enable_can_transceivers enable_can_transceivers; board_set_led set_led; board_set_usb_power_mode set_usb_power_mode; - board_set_esp_gps_mode set_esp_gps_mode; + board_set_gps_mode set_gps_mode; board_set_can_mode set_can_mode; board_usb_power_mode_tick usb_power_mode_tick; board_check_ignition check_ignition; @@ -29,6 +31,8 @@ struct board { board_set_ir_power set_ir_power; board_set_fan_power set_fan_power; board_set_phone_power set_phone_power; + board_set_clock_source_mode set_clock_source_mode; + board_set_siren set_siren; }; // ******************* Definitions ******************** @@ -52,10 +56,10 @@ struct board { #define USB_POWER_CDP 2U #define USB_POWER_DCP 3U -// ESP modes -#define ESP_GPS_DISABLED 0U -#define ESP_GPS_ENABLED 1U -#define ESP_GPS_BOOTMODE 2U +// GPS modes +#define GPS_DISABLED 0U +#define GPS_ENABLED 1U +#define GPS_BOOTMODE 2U // CAN modes #define CAN_MODE_NORMAL 0U @@ -72,4 +76,4 @@ bool board_has_gmlan(void); bool board_has_obd(void); bool board_has_lin(void); bool board_has_rtc(void); -bool board_has_relay(void); \ No newline at end of file +bool board_has_relay(void); diff --git a/panda/board/boards/black.h b/panda/board/boards/black.h index 57305980c..fa5a542d0 100644 --- a/panda/board/boards/black.h +++ b/panda/board/boards/black.h @@ -2,8 +2,8 @@ // Black Panda + Harness // // ///////////////////// // -void black_enable_can_transciever(uint8_t transciever, bool enabled) { - switch (transciever){ +void black_enable_can_transceiver(uint8_t transceiver, bool enabled) { + switch (transceiver){ case 1U: set_gpio_output(GPIOC, 1, !enabled); break; @@ -17,18 +17,18 @@ void black_enable_can_transciever(uint8_t transciever, bool enabled) { set_gpio_output(GPIOB, 10, !enabled); break; default: - puts("Invalid CAN transciever ("); puth(transciever); puts("): enabling failed\n"); + puts("Invalid CAN transceiver ("); puth(transceiver); puts("): enabling failed\n"); break; } } -void black_enable_can_transcievers(bool enabled) { +void black_enable_can_transceivers(bool enabled) { for(uint8_t i=1U; i<=4U; i++){ // Leave main CAN always on for CAN-based ignition detection if((car_harness_status == HARNESS_STATUS_FLIPPED) ? (i == 3U) : (i == 1U)){ - black_enable_can_transciever(i, true); + black_enable_can_transceiver(i, true); } else { - black_enable_can_transciever(i, enabled); + black_enable_can_transceiver(i, enabled); } } } @@ -77,24 +77,24 @@ void black_set_usb_power_mode(uint8_t mode) { } } -void black_set_esp_gps_mode(uint8_t mode) { +void black_set_gps_mode(uint8_t mode) { switch (mode) { - case ESP_GPS_DISABLED: + case GPS_DISABLED: // GPS OFF set_gpio_output(GPIOC, 14, 0); set_gpio_output(GPIOC, 5, 0); break; - case ESP_GPS_ENABLED: + case GPS_ENABLED: // GPS ON set_gpio_output(GPIOC, 14, 1); set_gpio_output(GPIOC, 5, 1); break; - case ESP_GPS_BOOTMODE: + case GPS_BOOTMODE: set_gpio_output(GPIOC, 14, 1); set_gpio_output(GPIOC, 5, 0); break; default: - puts("Invalid ESP/GPS mode\n"); + puts("Invalid GPS mode\n"); break; } } @@ -154,6 +154,14 @@ void black_set_phone_power(bool enabled){ UNUSED(enabled); } +void black_set_clock_source_mode(uint8_t mode){ + UNUSED(mode); +} + +void black_set_siren(bool enabled){ + UNUSED(enabled); +} + void black_init(void) { common_init_gpio(); @@ -167,7 +175,7 @@ void black_init(void) { set_gpio_mode(GPIOC, 3, MODE_ANALOG); // Set default state of GPS - current_board->set_esp_gps_mode(ESP_GPS_ENABLED); + current_board->set_gps_mode(GPS_ENABLED); // C10: OBD_SBU1_RELAY (harness relay driving output) // C11: OBD_SBU2_RELAY (harness relay driving output) @@ -190,8 +198,8 @@ void black_init(void) { // Initialize harness harness_init(); - // Enable CAN transcievers - black_enable_can_transcievers(true); + // Enable CAN transceivers + black_enable_can_transceivers(true); // Disable LEDs black_set_led(LED_RED, false); @@ -228,16 +236,18 @@ const board board_black = { .board_type = "Black", .harness_config = &black_harness_config, .init = black_init, - .enable_can_transciever = black_enable_can_transciever, - .enable_can_transcievers = black_enable_can_transcievers, + .enable_can_transceiver = black_enable_can_transceiver, + .enable_can_transceivers = black_enable_can_transceivers, .set_led = black_set_led, .set_usb_power_mode = black_set_usb_power_mode, - .set_esp_gps_mode = black_set_esp_gps_mode, + .set_gps_mode = black_set_gps_mode, .set_can_mode = black_set_can_mode, .usb_power_mode_tick = black_usb_power_mode_tick, .check_ignition = black_check_ignition, .read_current = black_read_current, .set_fan_power = black_set_fan_power, .set_ir_power = black_set_ir_power, - .set_phone_power = black_set_phone_power + .set_phone_power = black_set_phone_power, + .set_clock_source_mode = black_set_clock_source_mode, + .set_siren = black_set_siren }; diff --git a/panda/board/boards/common.h b/panda/board/boards/common.h index 734380962..2f35c34fe 100644 --- a/panda/board/boards/common.h +++ b/panda/board/boards/common.h @@ -23,7 +23,7 @@ void common_init_gpio(void){ set_gpio_alternate(GPIOA, 12, GPIO_AF10_OTG_FS); GPIOA->OSPEEDR = GPIO_OSPEEDER_OSPEEDR11 | GPIO_OSPEEDER_OSPEEDR12; - // A9,A10: USART 1 for talking to the ESP / GPS + // A9,A10: USART 1 for talking to the GPS set_gpio_alternate(GPIOA, 9, GPIO_AF7_USART1); set_gpio_alternate(GPIOA, 10, GPIO_AF7_USART1); @@ -65,7 +65,7 @@ void peripherals_init(void){ RCC->APB1ENR |= RCC_APB1ENR_PWREN; // for RTC config RCC->APB2ENR |= RCC_APB2ENR_USART1EN; RCC->AHB2ENR |= RCC_AHB2ENR_OTGFSEN; - //RCC->APB2ENR |= RCC_APB2ENR_TIM1EN; + RCC->APB2ENR |= RCC_APB2ENR_TIM1EN; // clock source timer RCC->APB2ENR |= RCC_APB2ENR_ADC1EN; RCC->APB2ENR |= RCC_APB2ENR_SPI1EN; RCC->APB2ENR |= RCC_APB2ENR_SYSCFGEN; @@ -81,4 +81,4 @@ bool detect_with_pull(GPIO_TypeDef *GPIO, int pin, int mode) { bool ret = get_gpio_input(GPIO, pin); set_gpio_pullup(GPIO, pin, PULL_NONE); return ret; -} \ No newline at end of file +} diff --git a/panda/board/boards/dos.h b/panda/board/boards/dos.h index afccd373d..307ae7247 100644 --- a/panda/board/boards/dos.h +++ b/panda/board/boards/dos.h @@ -2,8 +2,8 @@ // Dos + Harness // // ///////////// // -void dos_enable_can_transciever(uint8_t transciever, bool enabled) { - switch (transciever){ +void dos_enable_can_transceiver(uint8_t transceiver, bool enabled) { + switch (transceiver){ case 1U: set_gpio_output(GPIOC, 1, !enabled); break; @@ -17,18 +17,18 @@ void dos_enable_can_transciever(uint8_t transciever, bool enabled) { set_gpio_output(GPIOB, 10, !enabled); break; default: - puts("Invalid CAN transciever ("); puth(transciever); puts("): enabling failed\n"); + puts("Invalid CAN transceiver ("); puth(transceiver); puts("): enabling failed\n"); break; } } -void dos_enable_can_transcievers(bool enabled) { +void dos_enable_can_transceivers(bool enabled) { for(uint8_t i=1U; i<=4U; i++){ // Leave main CAN always on for CAN-based ignition detection if((car_harness_status == HARNESS_STATUS_FLIPPED) ? (i == 3U) : (i == 1U)){ - uno_enable_can_transciever(i, true); + uno_enable_can_transceiver(i, true); } else { - uno_enable_can_transciever(i, enabled); + uno_enable_can_transceiver(i, enabled); } } } @@ -54,20 +54,18 @@ void dos_set_gps_load_switch(bool enabled) { } void dos_set_bootkick(bool enabled){ - UNUSED(enabled); + set_gpio_output(GPIOC, 4, !enabled); } -void dos_bootkick(void) {} - void dos_set_phone_power(bool enabled){ UNUSED(enabled); } void dos_set_usb_power_mode(uint8_t mode) { - UNUSED(mode); + dos_set_bootkick(mode == USB_POWER_CDP); } -void dos_set_esp_gps_mode(uint8_t mode) { +void dos_set_gps_mode(uint8_t mode) { UNUSED(mode); } @@ -101,11 +99,6 @@ void dos_set_can_mode(uint8_t mode){ void dos_usb_power_mode_tick(uint32_t uptime){ UNUSED(uptime); - if(bootkick_timer != 0U){ - bootkick_timer--; - } else { - dos_set_bootkick(false); - } } bool dos_check_ignition(void){ @@ -132,6 +125,14 @@ uint32_t dos_read_current(void){ return 0U; } +void dos_set_clock_source_mode(uint8_t mode){ + clock_source_init(mode); +} + +void dos_set_siren(bool enabled){ + set_gpio_output(GPIOC, 12, enabled); +} + void dos_init(void) { common_init_gpio(); @@ -171,8 +172,8 @@ void dos_init(void) { // Initialize RTC rtc_init(); - // Enable CAN transcievers - dos_enable_can_transcievers(true); + // Enable CAN transceivers + dos_enable_can_transceivers(true); // Disable LEDs dos_set_led(LED_RED, false); @@ -189,6 +190,9 @@ void dos_init(void) { // init multiplexer can_set_obd(car_harness_status, false); + + // Init clock source as internal free running + dos_set_clock_source_mode(CLOCK_SOURCE_MODE_FREE_RUNNING); } const harness_configuration dos_harness_config = { @@ -209,16 +213,18 @@ const board board_dos = { .board_type = "Dos", .harness_config = &dos_harness_config, .init = dos_init, - .enable_can_transciever = dos_enable_can_transciever, - .enable_can_transcievers = dos_enable_can_transcievers, + .enable_can_transceiver = dos_enable_can_transceiver, + .enable_can_transceivers = dos_enable_can_transceivers, .set_led = dos_set_led, .set_usb_power_mode = dos_set_usb_power_mode, - .set_esp_gps_mode = dos_set_esp_gps_mode, + .set_gps_mode = dos_set_gps_mode, .set_can_mode = dos_set_can_mode, .usb_power_mode_tick = dos_usb_power_mode_tick, .check_ignition = dos_check_ignition, .read_current = dos_read_current, .set_fan_power = dos_set_fan_power, .set_ir_power = dos_set_ir_power, - .set_phone_power = dos_set_phone_power + .set_phone_power = dos_set_phone_power, + .set_clock_source_mode = dos_set_clock_source_mode, + .set_siren = dos_set_siren }; diff --git a/panda/board/boards/grey.h b/panda/board/boards/grey.h index 1c7278d27..bd5b1559e 100644 --- a/panda/board/boards/grey.h +++ b/panda/board/boards/grey.h @@ -8,22 +8,22 @@ void grey_init(void) { white_grey_common_init(); // Set default state of GPS - current_board->set_esp_gps_mode(ESP_GPS_ENABLED); + current_board->set_gps_mode(GPS_ENABLED); } -void grey_set_esp_gps_mode(uint8_t mode) { +void grey_set_gps_mode(uint8_t mode) { switch (mode) { - case ESP_GPS_DISABLED: + case GPS_DISABLED: // GPS OFF set_gpio_output(GPIOC, 14, 0); set_gpio_output(GPIOC, 5, 0); break; - case ESP_GPS_ENABLED: + case GPS_ENABLED: // GPS ON set_gpio_output(GPIOC, 14, 1); set_gpio_output(GPIOC, 5, 1); break; - case ESP_GPS_BOOTMODE: + case GPS_BOOTMODE: set_gpio_output(GPIOC, 14, 1); set_gpio_output(GPIOC, 5, 0); break; @@ -37,16 +37,18 @@ const board board_grey = { .board_type = "Grey", .harness_config = &white_harness_config, .init = grey_init, - .enable_can_transciever = white_enable_can_transciever, - .enable_can_transcievers = white_enable_can_transcievers, + .enable_can_transceiver = white_enable_can_transceiver, + .enable_can_transceivers = white_enable_can_transceivers, .set_led = white_set_led, .set_usb_power_mode = white_set_usb_power_mode, - .set_esp_gps_mode = grey_set_esp_gps_mode, + .set_gps_mode = grey_set_gps_mode, .set_can_mode = white_set_can_mode, .usb_power_mode_tick = white_usb_power_mode_tick, .check_ignition = white_check_ignition, .read_current = white_read_current, .set_fan_power = white_set_fan_power, .set_ir_power = white_set_ir_power, - .set_phone_power = white_set_phone_power -}; \ No newline at end of file + .set_phone_power = white_set_phone_power, + .set_clock_source_mode = white_set_clock_source_mode, + .set_siren = white_set_siren +}; diff --git a/panda/board/boards/pedal.h b/panda/board/boards/pedal.h index c67d39151..31abe9c5c 100644 --- a/panda/board/boards/pedal.h +++ b/panda/board/boards/pedal.h @@ -2,19 +2,19 @@ // Pedal // // ///// // -void pedal_enable_can_transciever(uint8_t transciever, bool enabled) { - switch (transciever){ +void pedal_enable_can_transceiver(uint8_t transceiver, bool enabled) { + switch (transceiver){ case 1: set_gpio_output(GPIOB, 3, !enabled); break; default: - puts("Invalid CAN transciever ("); puth(transciever); puts("): enabling failed\n"); + puts("Invalid CAN transceiver ("); puth(transceiver); puts("): enabling failed\n"); break; } } -void pedal_enable_can_transcievers(bool enabled) { - pedal_enable_can_transciever(1U, enabled); +void pedal_enable_can_transceivers(bool enabled) { + pedal_enable_can_transceiver(1U, enabled); } void pedal_set_led(uint8_t color, bool enabled) { @@ -35,7 +35,7 @@ void pedal_set_usb_power_mode(uint8_t mode){ puts("Trying to set USB power mode on pedal. This is not supported.\n"); } -void pedal_set_esp_gps_mode(uint8_t mode) { +void pedal_set_gps_mode(uint8_t mode) { UNUSED(mode); puts("Trying to set ESP/GPS mode on pedal. This is not supported.\n"); } @@ -77,6 +77,14 @@ void pedal_set_phone_power(bool enabled){ UNUSED(enabled); } +void pedal_set_clock_source_mode(uint8_t mode){ + UNUSED(mode); +} + +void pedal_set_siren(bool enabled){ + UNUSED(enabled); +} + void pedal_init(void) { common_init_gpio(); @@ -86,8 +94,8 @@ void pedal_init(void) { // DAC outputs on A4 and A5 // apparently they don't need GPIO setup - // Enable transciever - pedal_enable_can_transcievers(true); + // Enable transceiver + pedal_enable_can_transceivers(true); // Disable LEDs pedal_set_led(LED_RED, false); @@ -102,16 +110,18 @@ const board board_pedal = { .board_type = "Pedal", .harness_config = &pedal_harness_config, .init = pedal_init, - .enable_can_transciever = pedal_enable_can_transciever, - .enable_can_transcievers = pedal_enable_can_transcievers, + .enable_can_transceiver = pedal_enable_can_transceiver, + .enable_can_transceivers = pedal_enable_can_transceivers, .set_led = pedal_set_led, .set_usb_power_mode = pedal_set_usb_power_mode, - .set_esp_gps_mode = pedal_set_esp_gps_mode, + .set_gps_mode = pedal_set_gps_mode, .set_can_mode = pedal_set_can_mode, .usb_power_mode_tick = pedal_usb_power_mode_tick, .check_ignition = pedal_check_ignition, .read_current = pedal_read_current, .set_fan_power = pedal_set_fan_power, .set_ir_power = pedal_set_ir_power, - .set_phone_power = pedal_set_phone_power -}; \ No newline at end of file + .set_phone_power = pedal_set_phone_power, + .set_clock_source_mode = pedal_set_clock_source_mode, + .set_siren = pedal_set_siren +}; diff --git a/panda/board/boards/uno.h b/panda/board/boards/uno.h index de430cb57..96eb6268b 100644 --- a/panda/board/boards/uno.h +++ b/panda/board/boards/uno.h @@ -4,8 +4,8 @@ #define BOOTKICK_TIME 3U uint8_t bootkick_timer = 0U; -void uno_enable_can_transciever(uint8_t transciever, bool enabled) { - switch (transciever){ +void uno_enable_can_transceiver(uint8_t transceiver, bool enabled) { + switch (transceiver){ case 1U: set_gpio_output(GPIOC, 1, !enabled); break; @@ -19,18 +19,18 @@ void uno_enable_can_transciever(uint8_t transciever, bool enabled) { set_gpio_output(GPIOB, 10, !enabled); break; default: - puts("Invalid CAN transciever ("); puth(transciever); puts("): enabling failed\n"); + puts("Invalid CAN transceiver ("); puth(transceiver); puts("): enabling failed\n"); break; } } -void uno_enable_can_transcievers(bool enabled) { +void uno_enable_can_transceivers(bool enabled) { for(uint8_t i=1U; i<=4U; i++){ // Leave main CAN always on for CAN-based ignition detection if((car_harness_status == HARNESS_STATUS_FLIPPED) ? (i == 3U) : (i == 1U)){ - uno_enable_can_transciever(i, true); + uno_enable_can_transceiver(i, true); } else { - uno_enable_can_transciever(i, enabled); + uno_enable_can_transceiver(i, enabled); } } } @@ -89,21 +89,21 @@ void uno_set_usb_power_mode(uint8_t mode) { } } -void uno_set_esp_gps_mode(uint8_t mode) { +void uno_set_gps_mode(uint8_t mode) { switch (mode) { - case ESP_GPS_DISABLED: + case GPS_DISABLED: // GPS OFF set_gpio_output(GPIOB, 1, 0); set_gpio_output(GPIOC, 5, 0); uno_set_gps_load_switch(false); break; - case ESP_GPS_ENABLED: + case GPS_ENABLED: // GPS ON set_gpio_output(GPIOB, 1, 1); set_gpio_output(GPIOC, 5, 1); uno_set_gps_load_switch(true); break; - case ESP_GPS_BOOTMODE: + case GPS_BOOTMODE: set_gpio_output(GPIOB, 1, 1); set_gpio_output(GPIOC, 5, 0); uno_set_gps_load_switch(true); @@ -175,6 +175,14 @@ uint32_t uno_read_current(void){ return 0U; } +void uno_set_clock_source_mode(uint8_t mode){ + UNUSED(mode); +} + +void uno_set_siren(bool enabled){ + UNUSED(enabled); +} + void uno_init(void) { common_init_gpio(); @@ -188,7 +196,7 @@ void uno_init(void) { set_gpio_mode(GPIOC, 3, MODE_ANALOG); // Set default state of GPS - current_board->set_esp_gps_mode(ESP_GPS_ENABLED); + current_board->set_gps_mode(GPS_ENABLED); // C10: OBD_SBU1_RELAY (harness relay driving output) // C11: OBD_SBU2_RELAY (harness relay driving output) @@ -223,8 +231,8 @@ void uno_init(void) { // Initialize RTC rtc_init(); - // Enable CAN transcievers - uno_enable_can_transcievers(true); + // Enable CAN transceivers + uno_enable_can_transceivers(true); // Disable LEDs uno_set_led(LED_RED, false); @@ -271,16 +279,18 @@ const board board_uno = { .board_type = "Uno", .harness_config = &uno_harness_config, .init = uno_init, - .enable_can_transciever = uno_enable_can_transciever, - .enable_can_transcievers = uno_enable_can_transcievers, + .enable_can_transceiver = uno_enable_can_transceiver, + .enable_can_transceivers = uno_enable_can_transceivers, .set_led = uno_set_led, .set_usb_power_mode = uno_set_usb_power_mode, - .set_esp_gps_mode = uno_set_esp_gps_mode, + .set_gps_mode = uno_set_gps_mode, .set_can_mode = uno_set_can_mode, .usb_power_mode_tick = uno_usb_power_mode_tick, .check_ignition = uno_check_ignition, .read_current = uno_read_current, .set_fan_power = uno_set_fan_power, .set_ir_power = uno_set_ir_power, - .set_phone_power = uno_set_phone_power + .set_phone_power = uno_set_phone_power, + .set_clock_source_mode = uno_set_clock_source_mode, + .set_siren = uno_set_siren }; diff --git a/panda/board/boards/white.h b/panda/board/boards/white.h index 1be0702f9..44d8f512b 100644 --- a/panda/board/boards/white.h +++ b/panda/board/boards/white.h @@ -2,8 +2,8 @@ // White Panda // // /////////// // -void white_enable_can_transciever(uint8_t transciever, bool enabled) { - switch (transciever){ +void white_enable_can_transceiver(uint8_t transceiver, bool enabled) { + switch (transceiver){ case 1U: set_gpio_output(GPIOC, 1, !enabled); break; @@ -14,15 +14,15 @@ void white_enable_can_transciever(uint8_t transciever, bool enabled) { set_gpio_output(GPIOA, 0, !enabled); break; default: - puts("Invalid CAN transciever ("); puth(transciever); puts("): enabling failed\n"); + puts("Invalid CAN transceiver ("); puth(transceiver); puts("): enabling failed\n"); break; } } -void white_enable_can_transcievers(bool enabled) { - uint8_t t1 = enabled ? 1U : 2U; // leave transciever 1 enabled to detect CAN ignition +void white_enable_can_transceivers(bool enabled) { + uint8_t t1 = enabled ? 1U : 2U; // leave transceiver 1 enabled to detect CAN ignition for(uint8_t i=t1; i<=3U; i++) { - white_enable_can_transciever(i, enabled); + white_enable_can_transceiver(i, enabled); } } @@ -71,21 +71,21 @@ void white_set_usb_power_mode(uint8_t mode){ } } -void white_set_esp_gps_mode(uint8_t mode) { +void white_set_gps_mode(uint8_t mode) { switch (mode) { - case ESP_GPS_DISABLED: + case GPS_DISABLED: // ESP OFF set_gpio_output(GPIOC, 14, 0); set_gpio_output(GPIOC, 5, 0); break; #ifndef EON - case ESP_GPS_ENABLED: + case GPS_ENABLED: // ESP ON set_gpio_output(GPIOC, 14, 1); set_gpio_output(GPIOC, 5, 1); break; #endif - case ESP_GPS_BOOTMODE: + case GPS_BOOTMODE: set_gpio_output(GPIOC, 14, 1); set_gpio_output(GPIOC, 5, 0); break; @@ -242,6 +242,14 @@ void white_set_phone_power(bool enabled){ UNUSED(enabled); } +void white_set_clock_source_mode(uint8_t mode){ + UNUSED(mode); +} + +void white_set_siren(bool enabled){ + UNUSED(enabled); +} + void white_grey_common_init(void) { common_init_gpio(); @@ -291,8 +299,8 @@ void white_grey_common_init(void) { set_gpio_alternate(GPIOC, 11, GPIO_AF7_USART3); set_gpio_pullup(GPIOC, 11, PULL_UP); - // Enable CAN transcievers - white_enable_can_transcievers(true); + // Enable CAN transceivers + white_enable_can_transceivers(true); // Disable LEDs white_set_led(LED_RED, false); @@ -316,12 +324,8 @@ void white_grey_common_init(void) { void white_init(void) { white_grey_common_init(); - // Set default state of ESP - #ifdef EON - current_board->set_esp_gps_mode(ESP_GPS_DISABLED); - #else - current_board->set_esp_gps_mode(ESP_GPS_ENABLED); - #endif + // Set ESP off by default + current_board->set_gps_mode(GPS_DISABLED); } const harness_configuration white_harness_config = { @@ -332,16 +336,18 @@ const board board_white = { .board_type = "White", .harness_config = &white_harness_config, .init = white_init, - .enable_can_transciever = white_enable_can_transciever, - .enable_can_transcievers = white_enable_can_transcievers, + .enable_can_transceiver = white_enable_can_transceiver, + .enable_can_transceivers = white_enable_can_transceivers, .set_led = white_set_led, .set_usb_power_mode = white_set_usb_power_mode, - .set_esp_gps_mode = white_set_esp_gps_mode, + .set_gps_mode = white_set_gps_mode, .set_can_mode = white_set_can_mode, .usb_power_mode_tick = white_usb_power_mode_tick, .check_ignition = white_check_ignition, .read_current = white_read_current, .set_fan_power = white_set_fan_power, .set_ir_power = white_set_ir_power, - .set_phone_power = white_set_phone_power + .set_phone_power = white_set_phone_power, + .set_clock_source_mode = white_set_clock_source_mode, + .set_siren = white_set_siren }; diff --git a/panda/board/drivers/clock_source.h b/panda/board/drivers/clock_source.h new file mode 100644 index 000000000..966dee452 --- /dev/null +++ b/panda/board/drivers/clock_source.h @@ -0,0 +1,100 @@ + +#define CLOCK_SOURCE_MODE_DISABLED 0U +#define CLOCK_SOURCE_MODE_FREE_RUNNING 1U +#define CLOCK_SOURCE_MODE_EXTERNAL_SYNC 2U + +#define CLOCK_SOURCE_PERIOD_MS 50U +#define CLOCK_SOURCE_PULSE_LEN_MS 2U + +uint8_t clock_source_mode = CLOCK_SOURCE_MODE_DISABLED; + +void EXTI0_IRQ_Handler(void) { + volatile unsigned int pr = EXTI->PR & (1U << 0); + if (pr != 0U) { + if(clock_source_mode == CLOCK_SOURCE_MODE_EXTERNAL_SYNC){ + // TODO: Implement! + } + } + EXTI->PR = (1U << 0); +} + +void TIM1_UP_TIM10_IRQ_Handler(void) { + if((TIM1->SR & TIM_SR_UIF) != 0) { + if(clock_source_mode != CLOCK_SOURCE_MODE_DISABLED) { + // Start clock pulse + set_gpio_output(GPIOB, 14, true); + set_gpio_output(GPIOB, 15, true); + } + + // Reset interrupt + TIM1->SR &= ~(TIM_SR_UIF); + } +} + +void TIM1_CC_IRQ_Handler(void) { + if((TIM1->SR & TIM_SR_CC1IF) != 0) { + if(clock_source_mode != CLOCK_SOURCE_MODE_DISABLED) { + // End clock pulse + set_gpio_output(GPIOB, 14, false); + set_gpio_output(GPIOB, 15, false); + } + + // Reset interrupt + TIM1->SR &= ~(TIM_SR_CC1IF); + } +} + +void clock_source_init(uint8_t mode){ + // Setup external clock signal interrupt + REGISTER_INTERRUPT(EXTI0_IRQn, EXTI0_IRQ_Handler, 110U, FAULT_INTERRUPT_RATE_CLOCK_SOURCE) + register_set(&(SYSCFG->EXTICR[0]), SYSCFG_EXTICR1_EXTI0_PB, 0xFU); + register_set_bits(&(EXTI->IMR), (1U << 0)); + register_set_bits(&(EXTI->RTSR), (1U << 0)); + register_clear_bits(&(EXTI->FTSR), (1U << 0)); + + // Setup timer + REGISTER_INTERRUPT(TIM1_UP_TIM10_IRQn, TIM1_UP_TIM10_IRQ_Handler, (1200U / CLOCK_SOURCE_PERIOD_MS) , FAULT_INTERRUPT_RATE_TIM1) + REGISTER_INTERRUPT(TIM1_CC_IRQn, TIM1_CC_IRQ_Handler, (1200U / CLOCK_SOURCE_PERIOD_MS) , FAULT_INTERRUPT_RATE_TIM1) + register_set(&(TIM1->PSC), (9600-1), 0xFFFFU); // Tick on 0.1 ms + register_set(&(TIM1->ARR), ((CLOCK_SOURCE_PERIOD_MS*10U) - 1U), 0xFFFFU); // Period + register_set(&(TIM1->CCMR1), 0U, 0xFFFFU); // No output on compare + register_set_bits(&(TIM1->CCER), TIM_CCER_CC1E); // Enable compare 1 + register_set(&(TIM1->CCR1), (CLOCK_SOURCE_PULSE_LEN_MS*10U), 0xFFFFU); // Compare 1 value + register_set_bits(&(TIM1->DIER), TIM_DIER_UIE | TIM_DIER_CC1IE); // Enable interrupts + register_set(&(TIM1->CR1), TIM_CR1_CEN, 0x3FU); // Enable timer + + // Set mode + switch(mode) { + case CLOCK_SOURCE_MODE_DISABLED: + // No clock signal + NVIC_DisableIRQ(EXTI0_IRQn); + NVIC_DisableIRQ(TIM1_UP_TIM10_IRQn); + NVIC_DisableIRQ(TIM1_CC_IRQn); + + // Disable pulse if we were in the middle of it + set_gpio_output(GPIOB, 14, false); + set_gpio_output(GPIOB, 15, false); + + clock_source_mode = CLOCK_SOURCE_MODE_DISABLED; + break; + case CLOCK_SOURCE_MODE_FREE_RUNNING: + // Clock signal is based on internal timer + NVIC_DisableIRQ(EXTI0_IRQn); + NVIC_EnableIRQ(TIM1_UP_TIM10_IRQn); + NVIC_EnableIRQ(TIM1_CC_IRQn); + + clock_source_mode = CLOCK_SOURCE_MODE_FREE_RUNNING; + break; + case CLOCK_SOURCE_MODE_EXTERNAL_SYNC: + // Clock signal is based on external timer + NVIC_EnableIRQ(EXTI0_IRQn); + NVIC_EnableIRQ(TIM1_UP_TIM10_IRQn); + NVIC_EnableIRQ(TIM1_CC_IRQn); + + clock_source_mode = CLOCK_SOURCE_MODE_EXTERNAL_SYNC; + break; + default: + puts("Unknown clock source mode: "); puth(mode); puts("\n"); + break; + } +} \ No newline at end of file diff --git a/panda/board/drivers/uart.h b/panda/board/drivers/uart.h index 4ab791f60..e4f23ead0 100644 --- a/panda/board/drivers/uart.h +++ b/panda/board/drivers/uart.h @@ -50,8 +50,8 @@ void debug_ring_callback(uart_ring *ring); // ******************************** UART buffers ******************************** -// esp_gps = USART1 -UART_BUFFER(esp_gps, FIFO_SIZE_DMA, FIFO_SIZE_INT, USART1, NULL, true) +// gps = USART1 +UART_BUFFER(gps, FIFO_SIZE_DMA, FIFO_SIZE_INT, USART1, NULL, true) // lin1, K-LINE = UART5 // lin2, L-LINE = USART3 @@ -68,7 +68,7 @@ uart_ring *get_ring_by_number(int a) { ring = &uart_ring_debug; break; case 1: - ring = &uart_ring_esp_gps; + ring = &uart_ring_gps; break; case 2: ring = &uart_ring_lin1; @@ -185,8 +185,8 @@ void uart_interrupt_handler(uart_ring *q) { // Reset IDLE flag UART_READ_DR(q->uart) - if(q == &uart_ring_esp_gps){ - dma_pointer_handler(&uart_ring_esp_gps, DMA2_Stream5->NDTR); + if(q == &uart_ring_gps){ + dma_pointer_handler(&uart_ring_gps, DMA2_Stream5->NDTR); } else { #ifdef DEBUG_UART puts("No IDLE dma_pointer_handler implemented for this UART."); @@ -197,7 +197,7 @@ void uart_interrupt_handler(uart_ring *q) { EXIT_CRITICAL(); } -void USART1_IRQ_Handler(void) { uart_interrupt_handler(&uart_ring_esp_gps); } +void USART1_IRQ_Handler(void) { uart_interrupt_handler(&uart_ring_gps); } void USART2_IRQ_Handler(void) { uart_interrupt_handler(&uart_ring_debug); } void USART3_IRQ_Handler(void) { uart_interrupt_handler(&uart_ring_lin2); } void UART5_IRQ_Handler(void) { uart_interrupt_handler(&uart_ring_lin1); } @@ -219,7 +219,7 @@ void DMA2_Stream5_IRQ_Handler(void) { } // Re-calculate write pointer and reset flags - dma_pointer_handler(&uart_ring_esp_gps, DMA2_Stream5->NDTR); + dma_pointer_handler(&uart_ring_gps, DMA2_Stream5->NDTR); DMA2->HIFCR = DMA_HIFCR_CTCIF5 | DMA_HIFCR_CHTIF5; EXIT_CRITICAL(); @@ -229,7 +229,7 @@ void DMA2_Stream5_IRQ_Handler(void) { void dma_rx_init(uart_ring *q) { // Initialization is UART-dependent - if(q == &uart_ring_esp_gps){ + if(q == &uart_ring_gps){ // DMA2, stream 5, channel 4 // Disable FIFO mode (enable direct) diff --git a/panda/board/faults.h b/panda/board/faults.h index 2c031cf29..0825586d1 100644 --- a/panda/board/faults.h +++ b/panda/board/faults.h @@ -3,26 +3,28 @@ #define FAULT_STATUS_PERMANENT 2U // Fault types -#define FAULT_RELAY_MALFUNCTION (1U << 0) -#define FAULT_UNUSED_INTERRUPT_HANDLED (1U << 1) -#define FAULT_INTERRUPT_RATE_CAN_1 (1U << 2) -#define FAULT_INTERRUPT_RATE_CAN_2 (1U << 3) -#define FAULT_INTERRUPT_RATE_CAN_3 (1U << 4) -#define FAULT_INTERRUPT_RATE_TACH (1U << 5) -#define FAULT_INTERRUPT_RATE_GMLAN (1U << 6) -#define FAULT_INTERRUPT_RATE_INTERRUPTS (1U << 7) -#define FAULT_INTERRUPT_RATE_SPI_DMA (1U << 8) -#define FAULT_INTERRUPT_RATE_SPI_CS (1U << 9) -#define FAULT_INTERRUPT_RATE_UART_1 (1U << 10) -#define FAULT_INTERRUPT_RATE_UART_2 (1U << 11) -#define FAULT_INTERRUPT_RATE_UART_3 (1U << 12) -#define FAULT_INTERRUPT_RATE_UART_5 (1U << 13) -#define FAULT_INTERRUPT_RATE_UART_DMA (1U << 14) -#define FAULT_INTERRUPT_RATE_USB (1U << 15) -#define FAULT_INTERRUPT_RATE_TIM1 (1U << 16) -#define FAULT_INTERRUPT_RATE_TIM3 (1U << 17) -#define FAULT_REGISTER_DIVERGENT (1U << 18) -#define FAULT_INTERRUPT_RATE_KLINE_INIT (1U << 19) +#define FAULT_RELAY_MALFUNCTION (1U << 0) +#define FAULT_UNUSED_INTERRUPT_HANDLED (1U << 1) +#define FAULT_INTERRUPT_RATE_CAN_1 (1U << 2) +#define FAULT_INTERRUPT_RATE_CAN_2 (1U << 3) +#define FAULT_INTERRUPT_RATE_CAN_3 (1U << 4) +#define FAULT_INTERRUPT_RATE_TACH (1U << 5) +#define FAULT_INTERRUPT_RATE_GMLAN (1U << 6) +#define FAULT_INTERRUPT_RATE_INTERRUPTS (1U << 7) +#define FAULT_INTERRUPT_RATE_SPI_DMA (1U << 8) +#define FAULT_INTERRUPT_RATE_SPI_CS (1U << 9) +#define FAULT_INTERRUPT_RATE_UART_1 (1U << 10) +#define FAULT_INTERRUPT_RATE_UART_2 (1U << 11) +#define FAULT_INTERRUPT_RATE_UART_3 (1U << 12) +#define FAULT_INTERRUPT_RATE_UART_5 (1U << 13) +#define FAULT_INTERRUPT_RATE_UART_DMA (1U << 14) +#define FAULT_INTERRUPT_RATE_USB (1U << 15) +#define FAULT_INTERRUPT_RATE_TIM1 (1U << 16) +#define FAULT_INTERRUPT_RATE_TIM3 (1U << 17) +#define FAULT_REGISTER_DIVERGENT (1U << 18) +#define FAULT_INTERRUPT_RATE_KLINE_INIT (1U << 19) +#define FAULT_INTERRUPT_RATE_CLOCK_SOURCE (1U << 20) +#define FAULT_INTERRUPT_RATE_TIM9 (1U << 21) // Permanent faults #define PERMANENT_FAULTS 0U diff --git a/panda/board/gpio.h b/panda/board/gpio.h index 2b937eced..b50de50c3 100644 --- a/panda/board/gpio.h +++ b/panda/board/gpio.h @@ -64,13 +64,9 @@ void early(void) { if (enter_bootloader_mode == ENTER_BOOTLOADER_MAGIC) { #ifdef PANDA - current_board->set_esp_gps_mode(ESP_GPS_DISABLED); + current_board->set_gps_mode(GPS_DISABLED); #endif current_board->set_led(LED_GREEN, 1); jump_to_bootloader(); } - - if (is_entering_bootmode) { - enter_bootloader_mode = ENTER_SOFTLOADER_MAGIC; - } -} \ No newline at end of file +} diff --git a/panda/board/main.c b/panda/board/main.c index 497ea5dcd..c6e4f0fce 100644 --- a/panda/board/main.c +++ b/panda/board/main.c @@ -400,24 +400,24 @@ int usb_cb_control_msg(USB_Setup_TypeDef *setup, uint8_t *resp, bool hardwired) // **** 0xd9: set ESP power case 0xd9: if (setup->b.wValue.w == 1U) { - current_board->set_esp_gps_mode(ESP_GPS_ENABLED); + current_board->set_gps_mode(GPS_ENABLED); } else if (setup->b.wValue.w == 2U) { - current_board->set_esp_gps_mode(ESP_GPS_BOOTMODE); + current_board->set_gps_mode(GPS_BOOTMODE); } else { - current_board->set_esp_gps_mode(ESP_GPS_DISABLED); + current_board->set_gps_mode(GPS_DISABLED); } break; // **** 0xda: reset ESP, with optional boot mode case 0xda: - current_board->set_esp_gps_mode(ESP_GPS_DISABLED); + current_board->set_gps_mode(GPS_DISABLED); delay(1000000); if (setup->b.wValue.w == 1U) { - current_board->set_esp_gps_mode(ESP_GPS_BOOTMODE); + current_board->set_gps_mode(GPS_BOOTMODE); } else { - current_board->set_esp_gps_mode(ESP_GPS_ENABLED); + current_board->set_gps_mode(GPS_ENABLED); } delay(1000000); - current_board->set_esp_gps_mode(ESP_GPS_ENABLED); + current_board->set_gps_mode(GPS_ENABLED); break; // **** 0xdb: set GMLAN (white/grey) or OBD CAN (black) multiplexing mode case 0xdb: @@ -493,7 +493,7 @@ int usb_cb_control_msg(USB_Setup_TypeDef *setup, uint8_t *resp, bool hardwired) } // TODO: Remove this again and fix boardd code to hande the message bursts instead of single chars - if (ur == &uart_ring_esp_gps) { + if (ur == &uart_ring_gps) { dma_pointer_handler(ur, DMA2_Stream5->NDTR); } @@ -606,6 +606,14 @@ int usb_cb_control_msg(USB_Setup_TypeDef *setup, uint8_t *resp, bool hardwired) } } break; + // **** 0xf5: set clock source mode + case 0xf5: + current_board->set_clock_source_mode(setup->b.wValue.w); + break; + // **** 0xf6: set siren enabled + case 0xf6: + siren_enabled = (setup->b.wValue.w != 0U); + break; default: puts("NO HANDLER "); puth(setup->b.bRequest); @@ -663,87 +671,97 @@ void __attribute__ ((noinline)) enable_fpu(void) { #define EON_HEARTBEAT_IGNITION_CNT_ON 5U #define EON_HEARTBEAT_IGNITION_CNT_OFF 2U -// called at 1Hz +// called at 8Hz +uint8_t loop_counter = 0U; void TIM1_BRK_TIM9_IRQ_Handler(void) { if (TIM9->SR != 0) { - can_live = pending_can_live; + // siren + current_board->set_siren((loop_counter & 1U) && siren_enabled); - current_board->usb_power_mode_tick(uptime_cnt); + // decimated to 1Hz + if(loop_counter == 0U){ + can_live = pending_can_live; - //puth(usart1_dma); puts(" "); puth(DMA2_Stream5->M0AR); puts(" "); puth(DMA2_Stream5->NDTR); puts("\n"); + current_board->usb_power_mode_tick(uptime_cnt); - // reset this every 16th pass - if ((uptime_cnt & 0xFU) == 0U) { - pending_can_live = 0; - } - #ifdef DEBUG - puts("** blink "); - puth(can_rx_q.r_ptr); puts(" "); puth(can_rx_q.w_ptr); puts(" "); - puth(can_tx1_q.r_ptr); puts(" "); puth(can_tx1_q.w_ptr); puts(" "); - puth(can_tx2_q.r_ptr); puts(" "); puth(can_tx2_q.w_ptr); puts("\n"); - #endif + //puth(usart1_dma); puts(" "); puth(DMA2_Stream5->M0AR); puts(" "); puth(DMA2_Stream5->NDTR); puts("\n"); - // Tick drivers - fan_tick(); - - // set green LED to be controls allowed - current_board->set_led(LED_GREEN, controls_allowed); - - // turn off the blue LED, turned on by CAN - // unless we are in power saving mode - current_board->set_led(LED_BLUE, (uptime_cnt & 1U) && (power_save_status == POWER_SAVE_STATUS_ENABLED)); - - // increase heartbeat counter and cap it at the uint32 limit - if (heartbeat_counter < __UINT32_MAX__) { - heartbeat_counter += 1U; - } - - #ifdef EON - // check heartbeat counter if we are running EON code. - // if the heartbeat has been gone for a while, go to SILENT safety mode and enter power save - if (heartbeat_counter >= (check_started() ? EON_HEARTBEAT_IGNITION_CNT_ON : EON_HEARTBEAT_IGNITION_CNT_OFF)) { - puts("EON hasn't sent a heartbeat for 0x"); - puth(heartbeat_counter); - puts(" seconds. Safety is set to SILENT mode.\n"); - if (current_safety_mode != SAFETY_SILENT) { - set_safety_mode(SAFETY_SILENT, 0U); + // reset this every 16th pass + if ((uptime_cnt & 0xFU) == 0U) { + pending_can_live = 0; } - if (power_save_status != POWER_SAVE_STATUS_ENABLED) { - set_power_save_state(POWER_SAVE_STATUS_ENABLED); + #ifdef DEBUG + puts("** blink "); + puth(can_rx_q.r_ptr); puts(" "); puth(can_rx_q.w_ptr); puts(" "); + puth(can_tx1_q.r_ptr); puts(" "); puth(can_tx1_q.w_ptr); puts(" "); + puth(can_tx2_q.r_ptr); puts(" "); puth(can_tx2_q.w_ptr); puts("\n"); + #endif + + // Tick drivers + fan_tick(); + + // set green LED to be controls allowed + current_board->set_led(LED_GREEN, controls_allowed); + + // turn off the blue LED, turned on by CAN + // unless we are in power saving mode + current_board->set_led(LED_BLUE, (uptime_cnt & 1U) && (power_save_status == POWER_SAVE_STATUS_ENABLED)); + + // increase heartbeat counter and cap it at the uint32 limit + if (heartbeat_counter < __UINT32_MAX__) { + heartbeat_counter += 1U; } - // Also disable IR when the heartbeat goes missing - current_board->set_ir_power(0U); + #ifdef EON + // check heartbeat counter if we are running EON code. + // if the heartbeat has been gone for a while, go to SILENT safety mode and enter power save + if (heartbeat_counter >= (check_started() ? EON_HEARTBEAT_IGNITION_CNT_ON : EON_HEARTBEAT_IGNITION_CNT_OFF)) { + puts("EON hasn't sent a heartbeat for 0x"); + puth(heartbeat_counter); + puts(" seconds. Safety is set to SILENT mode.\n"); + if (current_safety_mode != SAFETY_SILENT) { + set_safety_mode(SAFETY_SILENT, 0U); + } + if (power_save_status != POWER_SAVE_STATUS_ENABLED) { + set_power_save_state(POWER_SAVE_STATUS_ENABLED); + } - // If enumerated but no heartbeat (phone up, boardd not running), turn the fan on to cool the device - if(usb_enumerated()){ - current_board->set_fan_power(50U); - } else { - current_board->set_fan_power(0U); + // Also disable IR when the heartbeat goes missing + current_board->set_ir_power(0U); + + // If enumerated but no heartbeat (phone up, boardd not running), turn the fan on to cool the device + if(usb_enumerated()){ + current_board->set_fan_power(50U); + } else { + current_board->set_fan_power(0U); + } } + + // enter CDP mode when car starts to ensure we are charging a turned off EON + if (check_started() && (usb_power_mode != USB_POWER_CDP)) { + current_board->set_usb_power_mode(USB_POWER_CDP); + } + #endif + + // check registers + check_registers(); + + // set ignition_can to false after 2s of no CAN seen + if (ignition_can_cnt > 2U) { + ignition_can = false; + }; + + // on to the next one + uptime_cnt += 1U; + safety_mode_cnt += 1U; + ignition_can_cnt += 1U; + + // synchronous safety check + safety_tick(current_hooks); } - // enter CDP mode when car starts to ensure we are charging a turned off EON - if (check_started() && (usb_power_mode != USB_POWER_CDP)) { - current_board->set_usb_power_mode(USB_POWER_CDP); - } - #endif - - // check registers - check_registers(); - - // set ignition_can to false after 2s of no CAN seen - if (ignition_can_cnt > 2U) { - ignition_can = false; - }; - - // on to the next one - uptime_cnt += 1U; - safety_mode_cnt += 1U; - ignition_can_cnt += 1U; - - // synchronous safety check - safety_tick(current_hooks); + loop_counter++; + loop_counter %= 8U; } TIM9->SR = 0; } @@ -753,8 +771,8 @@ int main(void) { // Init interrupt table init_interrupts(true); - // 1s timer - REGISTER_INTERRUPT(TIM1_BRK_TIM9_IRQn, TIM1_BRK_TIM9_IRQ_Handler, 2U, FAULT_INTERRUPT_RATE_TIM1) + // 8Hz timer + REGISTER_INTERRUPT(TIM1_BRK_TIM9_IRQn, TIM1_BRK_TIM9_IRQ_Handler, 10U, FAULT_INTERRUPT_RATE_TIM9) // shouldn't have interrupts here, but just in case disable_interrupts(); @@ -778,7 +796,6 @@ int main(void) { puts("Config:\n"); puts(" Board type: "); puts(current_board->board_type); puts("\n"); puts(has_external_debug_serial ? " Real serial\n" : " USB serial\n"); - puts(is_entering_bootmode ? " ESP wants bootmode\n" : " No bootmode\n"); // init board current_board->init(); @@ -794,10 +811,10 @@ int main(void) { } if (board_has_gps()) { - uart_init(&uart_ring_esp_gps, 9600); + uart_init(&uart_ring_gps, 9600); } else { // enable ESP uart - uart_init(&uart_ring_esp_gps, 115200); + uart_init(&uart_ring_gps, 115200); } if(board_has_lin()){ @@ -820,14 +837,14 @@ int main(void) { set_safety_mode(SAFETY_SILENT, 0); // enable CAN TXs - current_board->enable_can_transcievers(true); + current_board->enable_can_transceivers(true); #ifndef EON spi_init(); #endif - // 1hz - timer_init(TIM9, 1464); + // 8hz + timer_init(TIM9, 183); NVIC_EnableIRQ(TIM1_BRK_TIM9_IRQn); #ifdef DEBUG diff --git a/panda/board/main_declarations.h b/panda/board/main_declarations.h index 363a992db..7fa2c4bbb 100644 --- a/panda/board/main_declarations.h +++ b/panda/board/main_declarations.h @@ -13,3 +13,4 @@ const board *current_board; bool is_enumerated = 0; uint32_t heartbeat_counter = 0; uint32_t uptime_cnt = 0; +bool siren_enabled = false; diff --git a/panda/board/power_saving.h b/panda/board/power_saving.h index 3df750c10..2cb79cb61 100644 --- a/panda/board/power_saving.h +++ b/panda/board/power_saving.h @@ -28,13 +28,13 @@ void set_power_save_state(int state) { enable = true; } - current_board->enable_can_transcievers(enable); + current_board->enable_can_transceivers(enable); // Switch EPS/GPS if (enable) { - current_board->set_esp_gps_mode(ESP_GPS_ENABLED); + current_board->set_gps_mode(GPS_ENABLED); } else { - current_board->set_esp_gps_mode(ESP_GPS_DISABLED); + current_board->set_gps_mode(GPS_DISABLED); } if(board_has_gmlan()){ diff --git a/panda/board/safety/safety_hyundai.h b/panda/board/safety/safety_hyundai.h index 93a437523..a4a6d4c2a 100644 --- a/panda/board/safety/safety_hyundai.h +++ b/panda/board/safety/safety_hyundai.h @@ -1,4 +1,4 @@ -const int HYUNDAI_MAX_STEER = 255; // like stock +const int HYUNDAI_MAX_STEER = 384; // like stock const int HYUNDAI_MAX_RT_DELTA = 112; // max delta torque allowed for real time checks const uint32_t HYUNDAI_RT_INTERVAL = 250000; // 250ms between real time checks const int HYUNDAI_MAX_RATE_UP = 3; diff --git a/panda/board/spi_flasher.h b/panda/board/spi_flasher.h index b0511c14e..2636bf535 100644 --- a/panda/board/spi_flasher.h +++ b/panda/board/spi_flasher.h @@ -290,7 +290,7 @@ void soft_flasher_start(void) { // B8,B9: CAN 1 set_gpio_alternate(GPIOB, 8, GPIO_AF9_CAN1); set_gpio_alternate(GPIOB, 9, GPIO_AF9_CAN1); - current_board->enable_can_transciever(1, true); + current_board->enable_can_transceiver(1, true); // init can llcan_set_speed(CAN1, 5000, false, false); diff --git a/panda/certs/debugesp b/panda/certs/debugesp deleted file mode 100644 index 789beaac1..000000000 --- a/panda/certs/debugesp +++ /dev/null @@ -1,15 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIICXAIBAAKBgQCjIHvrSCWN0Nec6ozbImYik30PIF7JSWgdwDKTxSJ05RM3pj5E -LQEGt3qcaVrTokO68tpt5Gu1p6ZsNqWg7iVTW9M7Qj7IH45YDzQP/PSRjgSosQA6 -6f5Gokba5QrW38myqimvj+0p+YH+CNGCBRlTUQGCO8uLCspMZneRSLPW9QIDAQAB -AoGADaUn+HRef9BaWMvd4G6uMHI54cwJYbj8NpDfKjExQqnuw5bqWnWRQmiSnwbJ -DC7kj3zE/LBAuj890ot3q1CAWqh47ZICZfoX9Qbi5TpvIHFCGy6YkOliF6iIQhR2 -4+zNKTAA0zNKskOM25PdI+grK1Ni/bEofSA6TrqvEwsmxnkCQQDVp9FUUor2Bo/h -/3oAIP51LTw7vfpztYbJr+BDV63czV2DLXzSwzeNrwH4sA3oy1mjUgMBBgAarNGE -DYlc4H5jAkEAw3UCHzzXPlxkw2QGp7nBly5y3p80Uqc31NuYz8rdX/U8KTngi2No -Ft/SGCEXNpeYbToj+WK3RJJ2Ey0mK8+IxwJAcpGd/5CPsaQNLcw4WK9Yo+8Q2Jxk -G/4gfDCSmqn+smNxnLEcuUwzkwdgkEGgA9BfjeOhdsAH+EXpx90WZrZ/LwJBAK0k -jq+rTqUQZbZsejTEKYjJ/bnV4BzDwoKN0Q1pkLc7X4LJoW74rTFuLgdv8MdMfRtt -IIb/eoeFEpGkMicnHesCQHgR7BTUGBM6Uxam7RCdsgVsxoHBma21E/44ivWUMZzN -3oVt0mPnjS4speOlqwED5pCJ7yw7jwLPFMs8kNxuIKU= ------END RSA PRIVATE KEY----- diff --git a/panda/certs/debugesp.pub b/panda/certs/debugesp.pub deleted file mode 100644 index 3afcf3988..000000000 --- a/panda/certs/debugesp.pub +++ /dev/null @@ -1 +0,0 @@ -ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQCjIHvrSCWN0Nec6ozbImYik30PIF7JSWgdwDKTxSJ05RM3pj5ELQEGt3qcaVrTokO68tpt5Gu1p6ZsNqWg7iVTW9M7Qj7IH45YDzQP/PSRjgSosQA66f5Gokba5QrW38myqimvj+0p+YH+CNGCBRlTUQGCO8uLCspMZneRSLPW9Q== batman@y840 diff --git a/panda/certs/releaseesp.pub b/panda/certs/releaseesp.pub deleted file mode 100644 index 1d1d54bb7..000000000 --- a/panda/certs/releaseesp.pub +++ /dev/null @@ -1 +0,0 @@ -ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQDN4pVyGuJJSde1l3Fjay8qPxog09DsAJZtYPk+armoYO1L6YKReUTcMNyHQYZZMZFmhCdgjCgTIF2QYWMoP4KSe8l6JF04YPP51dIgefc6UXjtlSI8Pyutr0v9xXjSfsVm3RAJxDSHgzs9AoMsluKCL+LhAR1nd7cuHXITJ80O4w== batman@y840 diff --git a/panda/python/__init__.py b/panda/python/__init__.py index 605977db0..936c975f0 100644 --- a/panda/python/__init__.py +++ b/panda/python/__init__.py @@ -10,7 +10,6 @@ import traceback import subprocess import sys from .dfu import PandaDFU # pylint: disable=import-error -from .esptool import ESPROM, CesantaFlasher # noqa pylint: disable=import-error from .flash_release import flash_release # noqa pylint: disable=import-error from .update import ensure_st_up_to_date # noqa pylint: disable=import-error from .serial import PandaSerial # noqa pylint: disable=import-error @@ -148,6 +147,10 @@ class Panda(object): HW_TYPE_PEDAL = b'\x04' HW_TYPE_UNO = b'\x05' + CLOCK_SOURCE_MODE_DISABLED = 0 + CLOCK_SOURCE_MODE_FREE_RUNNING = 1 + CLOCK_SOURCE_MODE_EXTERNAL_SYNC = 2 + def __init__(self, serial=None, claim=True): self._serial = serial self._handle = None @@ -458,7 +461,7 @@ class Panda(object): self._handle.controlWrite(Panda.REQUEST_OUT, 0xe5, int(enable), 0, b'') def set_can_enable(self, bus_num, enable): - # sets the can transciever enable pin + # sets the can transceiver enable pin self._handle.controlWrite(Panda.REQUEST_OUT, 0xf4, int(bus_num), int(enable), b'') def set_can_speed_kbps(self, bus, speed): @@ -665,3 +668,11 @@ class Panda(object): # ****************** Phone ***************** def set_phone_power(self, enabled): self._handle.controlWrite(Panda.REQUEST_OUT, 0xb3, int(enabled), 0, b'') + + # ************** Clock Source ************** + def set_clock_source_mode(self, mode): + self._handle.controlWrite(Panda.REQUEST_OUT, 0xf5, int(mode), 0, b'') + + # ****************** Siren ***************** + def set_siren(self, enabled): + self._handle.controlWrite(Panda.REQUEST_OUT, 0xf6, int(enabled), 0, b'') diff --git a/panda/python/dfu.py b/panda/python/dfu.py index 658b6a9e8..a7f51ecda 100644 --- a/panda/python/dfu.py +++ b/panda/python/dfu.py @@ -24,7 +24,7 @@ class PandaDFU(object): self._handle = device.open() self.legacy = "07*128Kg" in self._handle.getASCIIStringDescriptor(4) return - raise Exception("failed to open " + dfu_serial) + raise Exception("failed to open " + dfu_serial if dfu_serial is not None else "DFU device") @staticmethod def list(): diff --git a/panda/python/esptool.py b/panda/python/esptool.py deleted file mode 100755 index 4a7dabfa2..000000000 --- a/panda/python/esptool.py +++ /dev/null @@ -1,1317 +0,0 @@ -#!/usr/bin/env python -# NB: Before sending a PR to change the above line to '#!/usr/bin/env python3', please read https://github.com/themadinventor/esptool/issues/21 -# -# ESP8266 ROM Bootloader Utility -# https://github.com/themadinventor/esptool -# -# Copyright (C) 2014-2016 Fredrik Ahlberg, Angus Gratton, other contributors as noted. -# -# This program is free software; you can redistribute it and/or modify it under -# the terms of the GNU General Public License as published by the Free Software -# Foundation; either version 2 of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License along with -# this program; if not, write to the Free Software Foundation, Inc., 51 Franklin -# Street, Fifth Floor, Boston, MA 02110-1301 USA. - -# pylint: skip-file -# flake8: noqa - -import argparse -import hashlib -import inspect -import json -import os -#import serial -import struct -import subprocess -import sys -import tempfile -import time -#import traceback -import usb1 - -__version__ = "1.2" - -class FakePort(object): - def __init__(self, serial=None): - from panda import Panda - self.panda = Panda(serial) - - # will only work on new st, old ones will stay @ 921600 - self.baudrate = 230400 - - @property - def baudrate(self): - return self.baudrate - - @baudrate.setter - def baudrate(self, x): - print("set baud to", x) - self.panda.set_uart_baud(1, x) - - def write(self, buf): - SEND_STEP = 0x20 - for i in range(0, len(buf), SEND_STEP): - self.panda.serial_write(1, buf[i:i+SEND_STEP]) - - def flushInput(self): - self.panda.serial_clear(1) - - def flushOutput(self): - self.panda.serial_clear(1) - - def read(self, llen): - ret = self.panda._handle.controlRead(usb1.TYPE_VENDOR | usb1.RECIPIENT_DEVICE, 0xe0, 1, 0, 1) - if ret == '': - time.sleep(0.1) - ret = self.panda._handle.controlRead(usb1.TYPE_VENDOR | usb1.RECIPIENT_DEVICE, 0xe0, 1, 0, 1) - return str(ret) - - def reset(self): - self.panda.esp_reset(1) - - def inWaiting(self): - return False - -class ESPROM(object): - # These are the currently known commands supported by the ROM - ESP_FLASH_BEGIN = 0x02 - ESP_FLASH_DATA = 0x03 - ESP_FLASH_END = 0x04 - ESP_MEM_BEGIN = 0x05 - ESP_MEM_END = 0x06 - ESP_MEM_DATA = 0x07 - ESP_SYNC = 0x08 - ESP_WRITE_REG = 0x09 - ESP_READ_REG = 0x0a - - # Maximum block sized for RAM and Flash writes, respectively. - ESP_RAM_BLOCK = 0x1800 - ESP_FLASH_BLOCK = 0x400 - - # Default baudrate. The ROM auto-bauds, so we can use more or less whatever we want. - ESP_ROM_BAUD = 115200 - - # First byte of the application image - ESP_IMAGE_MAGIC = 0xe9 - - # Initial state for the checksum routine - ESP_CHECKSUM_MAGIC = 0xef - - # OTP ROM addresses - ESP_OTP_MAC0 = 0x3ff00050 - ESP_OTP_MAC1 = 0x3ff00054 - ESP_OTP_MAC3 = 0x3ff0005c - - # Flash sector size, minimum unit of erase. - ESP_FLASH_SECTOR = 0x1000 - - def __init__(self, port=None, baud=ESP_ROM_BAUD): - self._port = FakePort(port) - self._slip_reader = slip_reader(self._port) - - """ Read a SLIP packet from the serial port """ - def read(self): - return next(self._slip_reader) - - """ Write bytes to the serial port while performing SLIP escaping """ - def write(self, packet): - buf = '\xc0' \ - + (packet.replace('\xdb','\xdb\xdd').replace('\xc0','\xdb\xdc')) \ - + '\xc0' - self._port.write(buf) - - """ Calculate checksum of a blob, as it is defined by the ROM """ - @staticmethod - def checksum(data, state=ESP_CHECKSUM_MAGIC): - for b in data: - state ^= ord(b) - return state - - """ Send a request and read the response """ - def command(self, op=None, data=None, chk=0): - if op is not None: - pkt = struct.pack('> 16) & 0xff, (mac3 >> 8) & 0xff, mac3 & 0xff) - elif ((mac1 >> 16) & 0xff) == 0: - oui = (0x18, 0xfe, 0x34) - elif ((mac1 >> 16) & 0xff) == 1: - oui = (0xac, 0xd0, 0x74) - else: - raise FatalError("Unknown OUI") - return oui + ((mac1 >> 8) & 0xff, mac1 & 0xff, (mac0 >> 24) & 0xff) - - """ Read Chip ID from OTP ROM - see http://esp8266-re.foogod.com/wiki/System_get_chip_id_%28IoT_RTOS_SDK_0.9.9%29 """ - def chip_id(self): - id0 = self.read_reg(self.ESP_OTP_MAC0) - id1 = self.read_reg(self.ESP_OTP_MAC1) - return (id0 >> 24) | ((id1 & 0xffffff) << 8) - - """ Read SPI flash manufacturer and device id """ - def flash_id(self): - self.flash_begin(0, 0) - self.write_reg(0x60000240, 0x0, 0xffffffff) - self.write_reg(0x60000200, 0x10000000, 0xffffffff) - flash_id = self.read_reg(0x60000240) - return flash_id - - """ Abuse the loader protocol to force flash to be left in write mode """ - def flash_unlock_dio(self): - # Enable flash write mode - self.flash_begin(0, 0) - # Reset the chip rather than call flash_finish(), which would have - # write protected the chip again (why oh why does it do that?!) - self.mem_begin(0,0,0,0x40100000) - self.mem_finish(0x40000080) - - """ Perform a chip erase of SPI flash """ - def flash_erase(self): - # Trick ROM to initialize SFlash - self.flash_begin(0, 0) - - # This is hacky: we don't have a custom stub, instead we trick - # the bootloader to jump to the SPIEraseChip() routine and then halt/crash - # when it tries to boot an unconfigured system. - self.mem_begin(0,0,0,0x40100000) - self.mem_finish(0x40004984) - - # Yup - there's no good way to detect if we succeeded. - # It it on the other hand unlikely to fail. - - def run_stub(self, stub, params, read_output=True): - stub = dict(stub) - stub['code'] = unhexify(stub['code']) - if 'data' in stub: - stub['data'] = unhexify(stub['data']) - - if stub['num_params'] != len(params): - raise FatalError('Stub requires %d params, %d provided' - % (stub['num_params'], len(params))) - - params = struct.pack('<' + ('I' * stub['num_params']), *params) - pc = params + stub['code'] - - # Upload - self.mem_begin(len(pc), 1, len(pc), stub['params_start']) - self.mem_block(pc, 0) - if 'data' in stub: - self.mem_begin(len(stub['data']), 1, len(stub['data']), stub['data_start']) - self.mem_block(stub['data'], 0) - self.mem_finish(stub['entry']) - - if read_output: - print('Stub executed, reading response:') - while True: - p = self.read() - print(hexify(p)) - if p == '': - return - - -class ESPBOOTLOADER(object): - """ These are constants related to software ESP bootloader, working with 'v2' image files """ - - # First byte of the "v2" application image - IMAGE_V2_MAGIC = 0xea - - # First 'segment' value in a "v2" application image, appears to be a constant version value? - IMAGE_V2_SEGMENT = 4 - - -def LoadFirmwareImage(filename): - """ Load a firmware image, without knowing what kind of file (v1 or v2) it is. - - Returns a BaseFirmwareImage subclass, either ESPFirmwareImage (v1) or OTAFirmwareImage (v2). - """ - with open(filename, 'rb') as f: - magic = ord(f.read(1)) - f.seek(0) - if magic == ESPROM.ESP_IMAGE_MAGIC: - return ESPFirmwareImage(f) - elif magic == ESPBOOTLOADER.IMAGE_V2_MAGIC: - return OTAFirmwareImage(f) - else: - raise FatalError("Invalid image magic number: %d" % magic) - - -class BaseFirmwareImage(object): - """ Base class with common firmware image functions """ - def __init__(self): - self.segments = [] - self.entrypoint = 0 - - def add_segment(self, addr, data, pad_to=4): - """ Add a segment to the image, with specified address & data - (padded to a boundary of pad_to size) """ - # Data should be aligned on word boundary - l = len(data) - if l % pad_to: - data += b"\x00" * (pad_to - l % pad_to) - if l > 0: - self.segments.append((addr, len(data), data)) - - def load_segment(self, f, is_irom_segment=False): - """ Load the next segment from the image file """ - (offset, size) = struct.unpack(' 0x40200000 or offset < 0x3ffe0000 or size > 65536: - raise FatalError('Suspicious segment 0x%x, length %d' % (offset, size)) - segment_data = f.read(size) - if len(segment_data) < size: - raise FatalError('End of file reading segment 0x%x, length %d (actual length %d)' % (offset, size, len(segment_data))) - segment = (offset, size, segment_data) - self.segments.append(segment) - return segment - - def save_segment(self, f, segment, checksum=None): - """ Save the next segment to the image file, return next checksum value if provided """ - (offset, size, data) = segment - f.write(struct.pack(' 16: - raise FatalError('Invalid firmware image magic=%d segments=%d' % (magic, segments)) - - for i in range(segments): - self.load_segment(load_file) - self.checksum = self.read_checksum(load_file) - - def save(self, filename): - with open(filename, 'wb') as f: - self.write_v1_header(f, self.segments) - checksum = ESPROM.ESP_CHECKSUM_MAGIC - for segment in self.segments: - checksum = self.save_segment(f, segment, checksum) - self.append_checksum(f, checksum) - - -class OTAFirmwareImage(BaseFirmwareImage): - """ 'Version 2' firmware image, segments loaded by software bootloader stub - (ie Espressif bootloader or rboot) - """ - def __init__(self, load_file=None): - super(OTAFirmwareImage, self).__init__() - self.version = 2 - if load_file is not None: - (magic, segments, first_flash_mode, first_flash_size_freq, first_entrypoint) = struct.unpack(' 16: - raise FatalError('Invalid V2 second header magic=%d segments=%d' % (magic, segments)) - - # load all the usual segments - for _ in range(segments): - self.load_segment(load_file) - self.checksum = self.read_checksum(load_file) - - def save(self, filename): - with open(filename, 'wb') as f: - # Save first header for irom0 segment - f.write(struct.pack(' 0: - esp._port.baudrate = baud_rate - # Read the greeting. - p = esp.read() - if p != 'OHAI': - raise FatalError('Failed to connect to the flasher (got %s)' % hexify(p)) - - def flash_write(self, addr, data, show_progress=False): - assert addr % self._esp.ESP_FLASH_SECTOR == 0, 'Address must be sector-aligned' - assert len(data) % self._esp.ESP_FLASH_SECTOR == 0, 'Length must be sector-aligned' - sys.stdout.write('Writing %d @ 0x%x... ' % (len(data), addr)) - sys.stdout.flush() - self._esp.write(struct.pack(' length: - raise FatalError('Read more than expected') - p = self._esp.read() - if len(p) != 16: - raise FatalError('Expected digest, got: %s' % hexify(p)) - expected_digest = hexify(p).upper() - digest = hashlib.md5(data).hexdigest().upper() - print() - if digest != expected_digest: - raise FatalError('Digest mismatch: expected %s, got %s' % (expected_digest, digest)) - p = self._esp.read() - if len(p) != 1: - raise FatalError('Expected status, got: %s' % hexify(p)) - status_code = struct.unpack(', ) or a single -# argument. - -def load_ram(esp, args): - image = LoadFirmwareImage(args.filename) - - print('RAM boot...') - for (offset, size, data) in image.segments: - print('Downloading %d bytes at %08x...' % (size, offset), end=' ') - sys.stdout.flush() - esp.mem_begin(size, div_roundup(size, esp.ESP_RAM_BLOCK), esp.ESP_RAM_BLOCK, offset) - - seq = 0 - while len(data) > 0: - esp.mem_block(data[0:esp.ESP_RAM_BLOCK], seq) - data = data[esp.ESP_RAM_BLOCK:] - seq += 1 - print('done!') - - print('All segments done, executing at %08x' % image.entrypoint) - esp.mem_finish(image.entrypoint) - - -def read_mem(esp, args): - print('0x%08x = 0x%08x' % (args.address, esp.read_reg(args.address))) - - -def write_mem(esp, args): - esp.write_reg(args.address, args.value, args.mask, 0) - print('Wrote %08x, mask %08x to %08x' % (args.value, args.mask, args.address)) - - -def dump_mem(esp, args): - f = open(args.filename, 'wb') - for i in range(args.size / 4): - d = esp.read_reg(args.address + (i * 4)) - f.write(struct.pack('> 16 - args.flash_size = {18: '2m', 19: '4m', 20: '8m', 21: '16m', 22: '32m'}.get(size_id) - if args.flash_size is None: - print('Warning: Could not auto-detect Flash size (FlashID=0x%x, SizeID=0x%x), defaulting to 4m' % (flash_id, size_id)) - args.flash_size = '4m' - else: - print('Auto-detected Flash size:', args.flash_size) - - -def write_flash(esp, args): - detect_flash_size(esp, args) - flash_mode = {'qio':0, 'qout':1, 'dio':2, 'dout': 3}[args.flash_mode] - flash_size_freq = {'4m':0x00, '2m':0x10, '8m':0x20, '16m':0x30, '32m':0x40, '16m-c1': 0x50, '32m-c1':0x60, '32m-c2':0x70}[args.flash_size] - flash_size_freq += {'40m':0, '26m':1, '20m':2, '80m': 0xf}[args.flash_freq] - flash_params = struct.pack('BB', flash_mode, flash_size_freq) - - flasher = CesantaFlasher(esp, args.baud) - - for address, argfile in args.addr_filename: - image = argfile.read() - argfile.seek(0) # rewind in case we need it again - if address + len(image) > int(args.flash_size.split('m')[0]) * (1 << 17): - print('WARNING: Unlikely to work as data goes beyond end of flash. Hint: Use --flash_size') - # Fix sflash config data. - if address == 0 and image[0] == '\xe9': - print('Flash params set to 0x%02x%02x' % (flash_mode, flash_size_freq)) - image = image[0:2] + flash_params + image[4:] - # Pad to sector size, which is the minimum unit of writing (erasing really). - if len(image) % esp.ESP_FLASH_SECTOR != 0: - image += '\xff' * (esp.ESP_FLASH_SECTOR - (len(image) % esp.ESP_FLASH_SECTOR)) - t = time.time() - flasher.flash_write(address, image, not args.no_progress) - t = time.time() - t - print('\rWrote %d bytes at 0x%x in %.1f seconds (%.1f kbit/s)...' - % (len(image), address, t, len(image) / t * 8 / 1000)) - print('Leaving...') - if args.verify: - print('Verifying just-written flash...') - _verify_flash(flasher, args, flash_params) - flasher.boot_fw() - - -def image_info(args): - image = LoadFirmwareImage(args.filename) - print('Image version: %d' % image.version) - print(('Entry point: %08x' % image.entrypoint) if image.entrypoint != 0 else 'Entry point not set') - print('%d segments' % len(image.segments)) - print() - checksum = ESPROM.ESP_CHECKSUM_MAGIC - for (idx, (offset, size, data)) in enumerate(image.segments): - if image.version == 2 and idx == 0: - print('Segment 1: %d bytes IROM0 (no load address)' % size) - else: - print('Segment %d: %5d bytes at %08x' % (idx + 1, size, offset)) - checksum = ESPROM.checksum(data, checksum) - print() - print('Checksum: %02x (%s)' % (image.checksum, 'valid' if image.checksum == checksum else 'invalid!')) - - -def make_image(args): - image = ESPFirmwareImage() - if len(args.segfile) == 0: - raise FatalError('No segments specified') - if len(args.segfile) != len(args.segaddr): - raise FatalError('Number of specified files does not match number of specified addresses') - for (seg, addr) in zip(args.segfile, args.segaddr): - data = open(seg, 'rb').read() - image.add_segment(addr, data) - image.entrypoint = args.entrypoint - image.save(args.output) - - -def elf2image(args): - e = ELFFile(args.input) - if args.version == '1': - image = ESPFirmwareImage() - else: - image = OTAFirmwareImage() - irom_data = e.load_section('.irom0.text') - if len(irom_data) == 0: - raise FatalError(".irom0.text section not found in ELF file - can't create V2 image.") - image.add_segment(0, irom_data, 16) - image.entrypoint = e.get_entry_point() - for section, start in ((".text", "_text_start"), (".data", "_data_start"), (".rodata", "_rodata_start")): - data = e.load_section(section) - image.add_segment(e.get_symbol_addr(start), data) - - image.flash_mode = {'qio':0, 'qout':1, 'dio':2, 'dout': 3}[args.flash_mode] - image.flash_size_freq = {'4m':0x00, '2m':0x10, '8m':0x20, '16m':0x30, '32m':0x40, '16m-c1': 0x50, '32m-c1':0x60, '32m-c2':0x70}[args.flash_size] - image.flash_size_freq += {'40m':0, '26m':1, '20m':2, '80m': 0xf}[args.flash_freq] - - irom_offs = e.get_symbol_addr("_irom0_text_start") - 0x40200000 - - if args.version == '1': - if args.output is None: - args.output = args.input + '-' - image.save(args.output + "0x00000.bin") - data = e.load_section(".irom0.text") - if irom_offs < 0: - raise FatalError('Address of symbol _irom0_text_start in ELF is located before flash mapping address. Bad linker script?') - if (irom_offs & 0xFFF) != 0: # irom0 isn't flash sector aligned - print("WARNING: irom0 section offset is 0x%08x. ELF is probably linked for 'elf2image --version=2'" % irom_offs) - with open(args.output + "0x%05x.bin" % irom_offs, "wb") as f: - f.write(data) - f.close() - else: # V2 OTA image - if args.output is None: - args.output = "%s-0x%05x.bin" % (os.path.splitext(args.input)[0], irom_offs & ~(ESPROM.ESP_FLASH_SECTOR - 1)) - image.save(args.output) - - -def read_mac(esp, args): - mac = esp.read_mac() - print('MAC: %s' % ':'.join(['%02x' % x for x in mac])) - - -def chip_id(esp, args): - chipid = esp.chip_id() - print('Chip ID: 0x%08x' % chipid) - - -def erase_flash(esp, args): - flasher = CesantaFlasher(esp, args.baud) - print('Erasing flash (this may take a while)...') - t = time.time() - flasher.flash_erase_chip() - t = time.time() - t - print('Erase took %.1f seconds' % t) - - -def run(esp, args): - esp.run() - - -def flash_id(esp, args): - flash_id = esp.flash_id() - esp.flash_finish(False) - print('Manufacturer: %02x' % (flash_id & 0xff)) - print('Device: %02x%02x' % ((flash_id >> 8) & 0xff, (flash_id >> 16) & 0xff)) - - -def read_flash(esp, args): - flasher = CesantaFlasher(esp, args.baud) - t = time.time() - data = flasher.flash_read(args.address, args.size, not args.no_progress) - t = time.time() - t - print('\rRead %d bytes at 0x%x in %.1f seconds (%.1f kbit/s)...' - % (len(data), args.address, t, len(data) / t * 8 / 1000)) - open(args.filename, 'wb').write(data) - - -def _verify_flash(flasher, args, flash_params=None): - differences = False - for address, argfile in args.addr_filename: - image = argfile.read() - argfile.seek(0) # rewind in case we need it again - if address == 0 and image[0] == '\xe9' and flash_params is not None: - image = image[0:2] + flash_params + image[4:] - image_size = len(image) - print('Verifying 0x%x (%d) bytes @ 0x%08x in flash against %s...' % (image_size, image_size, address, argfile.name)) - # Try digest first, only read if there are differences. - digest, _ = flasher.flash_digest(address, image_size) - digest = hexify(digest).upper() - expected_digest = hashlib.md5(image).hexdigest().upper() - if digest == expected_digest: - print('-- verify OK (digest matched)') - continue - else: - differences = True - if getattr(args, 'diff', 'no') != 'yes': - print('-- verify FAILED (digest mismatch)') - continue - - flash = flasher.flash_read(address, image_size) - assert flash != image - diff = [i for i in range(image_size) if flash[i] != image[i]] - print('-- verify FAILED: %d differences, first @ 0x%08x' % (len(diff), address + diff[0])) - for d in diff: - print(' %08x %02x %02x' % (address + d, ord(flash[d]), ord(image[d]))) - if differences: - raise FatalError("Verify failed.") - - -def verify_flash(esp, args, flash_params=None): - flasher = CesantaFlasher(esp) - _verify_flash(flasher, args, flash_params) - - -def version(args): - print(__version__) - -# -# End of operations functions -# - - -def main(): - parser = argparse.ArgumentParser(description='esptool.py v%s - ESP8266 ROM Bootloader Utility' % __version__, prog='esptool') - - parser.add_argument( - '--port', '-p', - help='Serial port device', - default=os.environ.get('ESPTOOL_PORT', None)) - - parser.add_argument( - '--baud', '-b', - help='Serial port baud rate used when flashing/reading', - type=arg_auto_int, - default=os.environ.get('ESPTOOL_BAUD', ESPROM.ESP_ROM_BAUD)) - - subparsers = parser.add_subparsers( - dest='operation', - help='Run esptool {command} -h for additional help') - - parser_load_ram = subparsers.add_parser( - 'load_ram', - help='Download an image to RAM and execute') - parser_load_ram.add_argument('filename', help='Firmware image') - - parser_dump_mem = subparsers.add_parser( - 'dump_mem', - help='Dump arbitrary memory to disk') - parser_dump_mem.add_argument('address', help='Base address', type=arg_auto_int) - parser_dump_mem.add_argument('size', help='Size of region to dump', type=arg_auto_int) - parser_dump_mem.add_argument('filename', help='Name of binary dump') - - parser_read_mem = subparsers.add_parser( - 'read_mem', - help='Read arbitrary memory location') - parser_read_mem.add_argument('address', help='Address to read', type=arg_auto_int) - - parser_write_mem = subparsers.add_parser( - 'write_mem', - help='Read-modify-write to arbitrary memory location') - parser_write_mem.add_argument('address', help='Address to write', type=arg_auto_int) - parser_write_mem.add_argument('value', help='Value', type=arg_auto_int) - parser_write_mem.add_argument('mask', help='Mask of bits to write', type=arg_auto_int) - - def add_spi_flash_subparsers(parent, auto_detect=False): - """ Add common parser arguments for SPI flash properties """ - parent.add_argument('--flash_freq', '-ff', help='SPI Flash frequency', - choices=['40m', '26m', '20m', '80m'], - default=os.environ.get('ESPTOOL_FF', '40m')) - parent.add_argument('--flash_mode', '-fm', help='SPI Flash mode', - choices=['qio', 'qout', 'dio', 'dout'], - default=os.environ.get('ESPTOOL_FM', 'qio')) - choices = ['4m', '2m', '8m', '16m', '32m', '16m-c1', '32m-c1', '32m-c2'] - default = '4m' - if auto_detect: - default = 'detect' - choices.insert(0, 'detect') - parent.add_argument('--flash_size', '-fs', help='SPI Flash size in Mbit', type=str.lower, - choices=choices, - default=os.environ.get('ESPTOOL_FS', default)) - - parser_write_flash = subparsers.add_parser( - 'write_flash', - help='Write a binary blob to flash') - parser_write_flash.add_argument('addr_filename', metavar='
', help='Address followed by binary filename, separated by space', - action=AddrFilenamePairAction) - add_spi_flash_subparsers(parser_write_flash, auto_detect=True) - parser_write_flash.add_argument('--no-progress', '-p', help='Suppress progress output', action="store_true") - parser_write_flash.add_argument('--verify', help='Verify just-written data (only necessary if very cautious, data is already CRCed', action='store_true') - - subparsers.add_parser( - 'run', - help='Run application code in flash') - - parser_image_info = subparsers.add_parser( - 'image_info', - help='Dump headers from an application image') - parser_image_info.add_argument('filename', help='Image file to parse') - - parser_make_image = subparsers.add_parser( - 'make_image', - help='Create an application image from binary files') - parser_make_image.add_argument('output', help='Output image file') - parser_make_image.add_argument('--segfile', '-f', action='append', help='Segment input file') - parser_make_image.add_argument('--segaddr', '-a', action='append', help='Segment base address', type=arg_auto_int) - parser_make_image.add_argument('--entrypoint', '-e', help='Address of entry point', type=arg_auto_int, default=0) - - parser_elf2image = subparsers.add_parser( - 'elf2image', - help='Create an application image from ELF file') - parser_elf2image.add_argument('input', help='Input ELF file') - parser_elf2image.add_argument('--output', '-o', help='Output filename prefix (for version 1 image), or filename (for version 2 single image)', type=str) - parser_elf2image.add_argument('--version', '-e', help='Output image version', choices=['1','2'], default='1') - add_spi_flash_subparsers(parser_elf2image) - - subparsers.add_parser( - 'read_mac', - help='Read MAC address from OTP ROM') - - subparsers.add_parser( - 'chip_id', - help='Read Chip ID from OTP ROM') - - subparsers.add_parser( - 'flash_id', - help='Read SPI flash manufacturer and device ID') - - parser_read_flash = subparsers.add_parser( - 'read_flash', - help='Read SPI flash content') - parser_read_flash.add_argument('address', help='Start address', type=arg_auto_int) - parser_read_flash.add_argument('size', help='Size of region to dump', type=arg_auto_int) - parser_read_flash.add_argument('filename', help='Name of binary dump') - parser_read_flash.add_argument('--no-progress', '-p', help='Suppress progress output', action="store_true") - - parser_verify_flash = subparsers.add_parser( - 'verify_flash', - help='Verify a binary blob against flash') - parser_verify_flash.add_argument('addr_filename', help='Address and binary file to verify there, separated by space', - action=AddrFilenamePairAction) - parser_verify_flash.add_argument('--diff', '-d', help='Show differences', - choices=['no', 'yes'], default='no') - - subparsers.add_parser( - 'erase_flash', - help='Perform Chip Erase on SPI flash') - - subparsers.add_parser( - 'version', help='Print esptool version') - - # internal sanity check - every operation matches a module function of the same name - for operation in list(subparsers.choices.keys()): - assert operation in globals(), "%s should be a module function" % operation - - args = parser.parse_args() - - print('esptool.py v%s' % __version__) - - # operation function can take 1 arg (args), 2 args (esp, arg) - # or be a member function of the ESPROM class. - - operation_func = globals()[args.operation] - operation_args,_,_,_ = inspect.getargspec(operation_func) - if operation_args[0] == 'esp': # operation function takes an ESPROM connection object - initial_baud = min(ESPROM.ESP_ROM_BAUD, args.baud) # don't sync faster than the default baud rate - esp = ESPROM(args.port, initial_baud) - esp.connect() - operation_func(esp, args) - else: - operation_func(args) - - -class AddrFilenamePairAction(argparse.Action): - """ Custom parser class for the address/filename pairs passed as arguments """ - def __init__(self, option_strings, dest, nargs='+', **kwargs): - super(AddrFilenamePairAction, self).__init__(option_strings, dest, nargs, **kwargs) - - def __call__(self, parser, namespace, values, option_string=None): - # validate pair arguments - pairs = [] - for i in range(0,len(values),2): - try: - address = int(values[i],0) - except ValueError: - raise argparse.ArgumentError(self,'Address "%s" must be a number' % values[i]) - try: - argfile = open(values[i + 1], 'rb') - except IOError as e: - raise argparse.ArgumentError(self, e) - except IndexError: - raise argparse.ArgumentError(self,'Must be pairs of an address and the binary filename to write there') - pairs.append((address, argfile)) - setattr(namespace, self.dest, pairs) - -# This is "wrapped" stub_flasher.c, to be loaded using run_stub. -_CESANTA_FLASHER_STUB = """\ -{"code_start": 1074790404, "code": "080000601C000060000000601000006031FCFF71FCFF\ -81FCFFC02000680332D218C020004807404074DCC48608005823C0200098081BA5A9239245005803\ -1B555903582337350129230B446604DFC6F3FF21EEFFC0200069020DF0000000010078480040004A\ -0040B449004012C1F0C921D911E901DD0209312020B4ED033C2C56C2073020B43C3C56420701F5FF\ -C000003C4C569206CD0EEADD860300202C4101F1FFC0000056A204C2DCF0C02DC0CC6CCAE2D1EAFF\ -0606002030F456D3FD86FBFF00002020F501E8FFC00000EC82D0CCC0C02EC0C73DEB2ADC46030020\ -2C4101E1FFC00000DC42C2DCF0C02DC056BCFEC602003C5C8601003C6C4600003C7C08312D0CD811\ -C821E80112C1100DF0000C180000140010400C0000607418000064180000801800008C1800008418\ -0000881800009018000018980040880F0040A80F0040349800404C4A0040740F0040800F0040980F\ -00400099004012C1E091F5FFC961CD0221EFFFE941F9310971D9519011C01A223902E2D1180C0222\ -6E1D21E4FF31E9FF2AF11A332D0F42630001EAFFC00000C030B43C2256A31621E1FF1A2228022030\ -B43C3256B31501ADFFC00000DD023C4256ED1431D6FF4D010C52D90E192E126E0101DDFFC0000021\ -D2FF32A101C020004802303420C0200039022C0201D7FFC00000463300000031CDFF1A333803D023\ -C03199FF27B31ADC7F31CBFF1A3328030198FFC0000056C20E2193FF2ADD060E000031C6FF1A3328\ -030191FFC0000056820DD2DD10460800000021BEFF1A2228029CE231BCFFC020F51A33290331BBFF\ -C02C411A332903C0F0F4222E1D22D204273D9332A3FFC02000280E27B3F721ABFF381E1A2242A400\ -01B5FFC00000381E2D0C42A40001B3FFC0000056120801B2FFC00000C02000280EC2DC0422D2FCC0\ -2000290E01ADFFC00000222E1D22D204226E1D281E22D204E7B204291E860000126E012198FF32A0\ -042A21C54C003198FF222E1D1A33380337B202C6D6FF2C02019FFFC000002191FF318CFF1A223A31\ -019CFFC00000218DFF1C031A22C549000C02060300003C528601003C624600003C72918BFF9A1108\ -71C861D851E841F83112C1200DF00010000068100000581000007010000074100000781000007C10\ -0000801000001C4B0040803C004091FDFF12C1E061F7FFC961E941F9310971D9519011C01A662906\ -21F3FFC2D1101A22390231F2FF0C0F1A33590331EAFFF26C1AED045C2247B3028636002D0C016DFF\ -C0000021E5FF41EAFF2A611A4469040622000021E4FF1A222802F0D2C0D7BE01DD0E31E0FF4D0D1A\ -3328033D0101E2FFC00000561209D03D2010212001DFFFC000004D0D2D0C3D01015DFFC0000041D5\ -FFDAFF1A444804D0648041D2FF1A4462640061D1FF106680622600673F1331D0FF10338028030C43\ -853A002642164613000041CAFF222C1A1A444804202FC047328006F6FF222C1A273F3861C2FF222C\ -1A1A6668066732B921BDFF3D0C1022800148FFC0000021BAFF1C031A2201BFFFC000000C02460300\ -5C3206020000005C424600005C5291B7FF9A110871C861D851E841F83112C1200DF0B0100000C010\ -0000D010000012C1E091FEFFC961D951E9410971F931CD039011C0ED02DD0431A1FF9C1422A06247\ -B302062D0021F4FF1A22490286010021F1FF1A223902219CFF2AF12D0F011FFFC00000461C0022D1\ -10011CFFC0000021E9FFFD0C1A222802C7B20621E6FF1A22F8022D0E3D014D0F0195FFC000008C52\ -22A063C6180000218BFF3D01102280F04F200111FFC00000AC7D22D1103D014D0F010DFFC0000021\ -D6FF32D110102280010EFFC0000021D3FF1C031A220185FFC00000FAEEF0CCC056ACF821CDFF317A\ -FF1A223A310105FFC0000021C9FF1C031A22017CFFC000002D0C91C8FF9A110871C861D851E841F8\ -3112C1200DF0000200600000001040020060FFFFFF0012C1E00C02290131FAFF21FAFF026107C961\ -C02000226300C02000C80320CC10564CFF21F5FFC02000380221F4FF20231029010C432D010163FF\ -C0000008712D0CC86112C1200DF00080FE3F8449004012C1D0C9A109B17CFC22C1110C13C51C0026\ -1202463000220111C24110B68202462B0031F5FF3022A02802A002002D011C03851A0066820A2801\ -32210105A6FF0607003C12C60500000010212032A01085180066A20F2221003811482105B3FF2241\ -10861A004C1206FDFF2D011C03C5160066B20E280138114821583185CFFF06F7FF005C1286F5FF00\ -10212032A01085140066A20D2221003811482105E1FF06EFFF0022A06146EDFF45F0FFC6EBFF0000\ -01D2FFC0000006E9FF000C022241100C1322C110C50F00220111060600000022C1100C13C50E0022\ -011132C2FA303074B6230206C8FF08B1C8A112C1300DF0000000000010404F484149007519031027\ -000000110040A8100040BC0F0040583F0040CC2E00401CE20040D83900408000004021F4FF12C1E0\ -C961C80221F2FF097129010C02D951C91101F4FFC0000001F3FFC00000AC2C22A3E801F2FFC00000\ -21EAFFC031412A233D0C01EFFFC000003D0222A00001EDFFC00000C1E4FF2D0C01E8FFC000002D01\ -32A004450400C5E7FFDD022D0C01E3FFC00000666D1F4B2131DCFF4600004B22C0200048023794F5\ -31D9FFC0200039023DF08601000001DCFFC000000871C861D85112C1200DF000000012C1F0026103\ -01EAFEC00000083112C1100DF000643B004012C1D0E98109B1C9A1D991F97129013911E2A0C001FA\ -FFC00000CD02E792F40C0DE2A0C0F2A0DB860D00000001F4FFC00000204220E71240F7921C226102\ -01EFFFC0000052A0DC482157120952A0DD571205460500004D0C3801DA234242001BDD3811379DC5\ -C6000000000C0DC2A0C001E3FFC00000C792F608B12D0DC8A1D891E881F87112C1300DF00000", "\ -entry": 1074792180, "num_params": 1, "params_start": 1074790400, "data": "FE0510\ -401A0610403B0610405A0610407A061040820610408C0610408C061040", "data_start": 10736\ -43520} -""" - -if __name__ == '__main__': - try: - main() - except FatalError as e: - print('\nA fatal error occurred: %s' % e) - sys.exit(2) diff --git a/panda/python/flash_release.py b/panda/python/flash_release.py index 961a83a0d..6e77d6a7f 100755 --- a/panda/python/flash_release.py +++ b/panda/python/flash_release.py @@ -7,7 +7,7 @@ import json import io def flash_release(path=None, st_serial=None): - from panda import Panda, PandaDFU, ESPROM, CesantaFlasher + from panda import Panda, PandaDFU from zipfile import ZipFile def status(x): @@ -23,33 +23,28 @@ def flash_release(path=None, st_serial=None): st_serial = panda_list[0] print("Using panda with serial %s" % st_serial) - if path is not None: + if path is None: print("Fetching latest firmware from github.com/commaai/panda-artifacts") r = requests.get("https://raw.githubusercontent.com/commaai/panda-artifacts/master/latest.json") url = json.loads(r.text)['url'] r = requests.get(url) print("Fetching firmware from %s" % url) - path = io.StringIO(r.content) + path = io.BytesIO(r.content) zf = ZipFile(path) zf.printdir() - version = zf.read("version") - status("0. Preparing to flash " + version) + version = zf.read("version").decode() + status("0. Preparing to flash " + str(version)) code_bootstub = zf.read("bootstub.panda.bin") code_panda = zf.read("panda.bin") - code_boot_15 = zf.read("boot_v1.5.bin") - code_boot_15 = code_boot_15[0:2] + "\x00\x30" + code_boot_15[4:] - - code_user1 = zf.read("user1.bin") - code_user2 = zf.read("user2.bin") - # enter DFU mode status("1. Entering DFU mode") panda = Panda(st_serial) - panda.enter_bootloader() + panda.reset(enter_bootstub=True) + panda.reset(enter_bootloader=True) time.sleep(1) # program bootstub @@ -64,29 +59,8 @@ def flash_release(path=None, st_serial=None): panda.flash(code=code_panda) panda.close() - # flashing ESP - if panda.is_white(): - status("4. Flashing ESP (slow!)") - - def align(x, sz=0x1000): - x + "\xFF" * ((sz - len(x)) % sz) - - esp = ESPROM(st_serial) - esp.connect() - flasher = CesantaFlasher(esp, 230400) - flasher.flash_write(0x0, align(code_boot_15), True) - flasher.flash_write(0x1000, align(code_user1), True) - flasher.flash_write(0x81000, align(code_user2), True) - flasher.flash_write(0x3FE000, "\xFF" * 0x1000) - flasher.boot_fw() - del flasher - del esp - time.sleep(1) - else: - status("4. No ESP in non-white panda") - # check for connection - status("5. Verifying version") + status("4. Verifying version") panda = Panda(st_serial) my_version = panda.get_version() print("dongle id: %s" % panda.get_serial()[0]) diff --git a/rednose/helpers/__init__.py b/rednose/helpers/__init__.py index 1588a4338..aba6b41fe 100644 --- a/rednose/helpers/__init__.py +++ b/rednose/helpers/__init__.py @@ -1,4 +1,5 @@ import os +import platform from cffi import FFI TEMPLATE_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', 'templates')) @@ -13,7 +14,8 @@ def write_code(folder, name, code, header): def load_code(folder, name): - shared_fn = os.path.join(folder, f"lib{name}.so") + shared_ext = "dylib" if platform.system() == "Darwin" else "so" + shared_fn = os.path.join(folder, f"lib{name}.{shared_ext}") header_fn = os.path.join(folder, f"{name}.h") with open(header_fn) as f: diff --git a/selfdrive/assets/offroad/circled-checkmark.png b/selfdrive/assets/offroad/circled-checkmark.png new file mode 100644 index 000000000..bc6b49585 Binary files /dev/null and b/selfdrive/assets/offroad/circled-checkmark.png differ diff --git a/selfdrive/assets/offroad/icon_app_store.png b/selfdrive/assets/offroad/icon_app_store.png new file mode 100644 index 000000000..ae0dd95ce Binary files /dev/null and b/selfdrive/assets/offroad/icon_app_store.png differ diff --git a/selfdrive/assets/offroad/icon_calibration.png b/selfdrive/assets/offroad/icon_calibration.png new file mode 100644 index 000000000..c4ee0d63d Binary files /dev/null and b/selfdrive/assets/offroad/icon_calibration.png differ diff --git a/selfdrive/assets/offroad/icon_checkmark.png b/selfdrive/assets/offroad/icon_checkmark.png new file mode 100644 index 000000000..06efdfb0c Binary files /dev/null and b/selfdrive/assets/offroad/icon_checkmark.png differ diff --git a/selfdrive/assets/offroad/icon_chevron_right.png b/selfdrive/assets/offroad/icon_chevron_right.png new file mode 100644 index 000000000..a3aaa7648 Binary files /dev/null and b/selfdrive/assets/offroad/icon_chevron_right.png differ diff --git a/selfdrive/assets/offroad/icon_connect_app.png b/selfdrive/assets/offroad/icon_connect_app.png new file mode 100644 index 000000000..cd216d3e6 Binary files /dev/null and b/selfdrive/assets/offroad/icon_connect_app.png differ diff --git a/selfdrive/assets/offroad/icon_eon.png b/selfdrive/assets/offroad/icon_eon.png new file mode 100644 index 000000000..72856c4e6 Binary files /dev/null and b/selfdrive/assets/offroad/icon_eon.png differ diff --git a/selfdrive/assets/offroad/icon_map.png b/selfdrive/assets/offroad/icon_map.png new file mode 100644 index 000000000..21dd0bacc Binary files /dev/null and b/selfdrive/assets/offroad/icon_map.png differ diff --git a/selfdrive/assets/offroad/icon_map_speed.png b/selfdrive/assets/offroad/icon_map_speed.png new file mode 100644 index 000000000..1eeab8460 Binary files /dev/null and b/selfdrive/assets/offroad/icon_map_speed.png differ diff --git a/selfdrive/assets/offroad/icon_menu.png b/selfdrive/assets/offroad/icon_menu.png new file mode 100644 index 000000000..837cf5831 Binary files /dev/null and b/selfdrive/assets/offroad/icon_menu.png differ diff --git a/selfdrive/assets/offroad/icon_metric.png b/selfdrive/assets/offroad/icon_metric.png new file mode 100644 index 000000000..eaa2438fa Binary files /dev/null and b/selfdrive/assets/offroad/icon_metric.png differ diff --git a/selfdrive/assets/offroad/icon_minus.png b/selfdrive/assets/offroad/icon_minus.png new file mode 100644 index 000000000..e5327c0d3 Binary files /dev/null and b/selfdrive/assets/offroad/icon_minus.png differ diff --git a/selfdrive/assets/offroad/icon_monitoring.png b/selfdrive/assets/offroad/icon_monitoring.png new file mode 100644 index 000000000..05f78811e Binary files /dev/null and b/selfdrive/assets/offroad/icon_monitoring.png differ diff --git a/selfdrive/assets/offroad/icon_network.png b/selfdrive/assets/offroad/icon_network.png new file mode 100644 index 000000000..3236924f4 Binary files /dev/null and b/selfdrive/assets/offroad/icon_network.png differ diff --git a/selfdrive/assets/offroad/icon_openpilot.png b/selfdrive/assets/offroad/icon_openpilot.png new file mode 100644 index 000000000..0a90a8791 Binary files /dev/null and b/selfdrive/assets/offroad/icon_openpilot.png differ diff --git a/selfdrive/assets/offroad/icon_openpilot_mirrored.png b/selfdrive/assets/offroad/icon_openpilot_mirrored.png new file mode 100644 index 000000000..23a7d5a55 Binary files /dev/null and b/selfdrive/assets/offroad/icon_openpilot_mirrored.png differ diff --git a/selfdrive/assets/offroad/icon_play_store.png b/selfdrive/assets/offroad/icon_play_store.png new file mode 100644 index 000000000..1eca9d589 Binary files /dev/null and b/selfdrive/assets/offroad/icon_play_store.png differ diff --git a/selfdrive/assets/offroad/icon_plus.png b/selfdrive/assets/offroad/icon_plus.png new file mode 100644 index 000000000..92b448b0b Binary files /dev/null and b/selfdrive/assets/offroad/icon_plus.png differ diff --git a/selfdrive/assets/offroad/icon_road.png b/selfdrive/assets/offroad/icon_road.png new file mode 100644 index 000000000..5868ed1cc Binary files /dev/null and b/selfdrive/assets/offroad/icon_road.png differ diff --git a/selfdrive/assets/offroad/icon_settings.png b/selfdrive/assets/offroad/icon_settings.png new file mode 100644 index 000000000..d0c90a620 Binary files /dev/null and b/selfdrive/assets/offroad/icon_settings.png differ diff --git a/selfdrive/assets/offroad/icon_shell.png b/selfdrive/assets/offroad/icon_shell.png new file mode 100644 index 000000000..f1d655416 Binary files /dev/null and b/selfdrive/assets/offroad/icon_shell.png differ diff --git a/selfdrive/assets/offroad/icon_speed_limit.png b/selfdrive/assets/offroad/icon_speed_limit.png new file mode 100644 index 000000000..0aa7038f9 Binary files /dev/null and b/selfdrive/assets/offroad/icon_speed_limit.png differ diff --git a/selfdrive/assets/offroad/icon_user.png b/selfdrive/assets/offroad/icon_user.png new file mode 100644 index 000000000..9b653cc4b Binary files /dev/null and b/selfdrive/assets/offroad/icon_user.png differ diff --git a/selfdrive/assets/offroad/icon_warning.png b/selfdrive/assets/offroad/icon_warning.png new file mode 100644 index 000000000..50fe82112 Binary files /dev/null and b/selfdrive/assets/offroad/icon_warning.png differ diff --git a/selfdrive/assets/offroad/illustration_arrow.png b/selfdrive/assets/offroad/illustration_arrow.png new file mode 100644 index 000000000..221908664 Binary files /dev/null and b/selfdrive/assets/offroad/illustration_arrow.png differ diff --git a/selfdrive/assets/offroad/illustration_sim_absent.png b/selfdrive/assets/offroad/illustration_sim_absent.png new file mode 100644 index 000000000..554097409 Binary files /dev/null and b/selfdrive/assets/offroad/illustration_sim_absent.png differ diff --git a/selfdrive/assets/offroad/illustration_sim_present.png b/selfdrive/assets/offroad/illustration_sim_present.png new file mode 100644 index 000000000..0856795f0 Binary files /dev/null and b/selfdrive/assets/offroad/illustration_sim_present.png differ diff --git a/selfdrive/assets/offroad/illustration_training_lane_01.png b/selfdrive/assets/offroad/illustration_training_lane_01.png new file mode 100644 index 000000000..27d9bcee3 Binary files /dev/null and b/selfdrive/assets/offroad/illustration_training_lane_01.png differ diff --git a/selfdrive/assets/offroad/illustration_training_lane_02.png b/selfdrive/assets/offroad/illustration_training_lane_02.png new file mode 100644 index 000000000..4f3e2ef44 Binary files /dev/null and b/selfdrive/assets/offroad/illustration_training_lane_02.png differ diff --git a/selfdrive/assets/offroad/illustration_training_lead_01.png b/selfdrive/assets/offroad/illustration_training_lead_01.png new file mode 100644 index 000000000..12f3f6bae Binary files /dev/null and b/selfdrive/assets/offroad/illustration_training_lead_01.png differ diff --git a/selfdrive/assets/offroad/illustration_training_lead_02.png b/selfdrive/assets/offroad/illustration_training_lead_02.png new file mode 100644 index 000000000..26c9ffe71 Binary files /dev/null and b/selfdrive/assets/offroad/illustration_training_lead_02.png differ diff --git a/selfdrive/assets/offroad/indicator_wifi_0.png b/selfdrive/assets/offroad/indicator_wifi_0.png new file mode 100644 index 000000000..9cf9762ad Binary files /dev/null and b/selfdrive/assets/offroad/indicator_wifi_0.png differ diff --git a/selfdrive/assets/offroad/indicator_wifi_100.png b/selfdrive/assets/offroad/indicator_wifi_100.png new file mode 100644 index 000000000..dc9f28fab Binary files /dev/null and b/selfdrive/assets/offroad/indicator_wifi_100.png differ diff --git a/selfdrive/assets/offroad/indicator_wifi_25.png b/selfdrive/assets/offroad/indicator_wifi_25.png new file mode 100644 index 000000000..cbf9bc89e Binary files /dev/null and b/selfdrive/assets/offroad/indicator_wifi_25.png differ diff --git a/selfdrive/assets/offroad/indicator_wifi_50.png b/selfdrive/assets/offroad/indicator_wifi_50.png new file mode 100644 index 000000000..8ee118a41 Binary files /dev/null and b/selfdrive/assets/offroad/indicator_wifi_50.png differ diff --git a/selfdrive/assets/offroad/indicator_wifi_75.png b/selfdrive/assets/offroad/indicator_wifi_75.png new file mode 100644 index 000000000..bcbebce85 Binary files /dev/null and b/selfdrive/assets/offroad/indicator_wifi_75.png differ diff --git a/selfdrive/athena/athenad.py b/selfdrive/athena/athenad.py index 72e81a58b..6f34cbed7 100755 --- a/selfdrive/athena/athenad.py +++ b/selfdrive/athena/athenad.py @@ -20,7 +20,7 @@ from websocket import ABNF, WebSocketTimeoutException, create_connection import cereal.messaging as messaging from cereal.services import service_list -from common import android +from common.hardware import HARDWARE from common.api import Api from common.basedir import PERSIST from common.params import Params @@ -132,7 +132,7 @@ def reboot(): def do_reboot(): time.sleep(2) - android.reboot() + HARDWARE.reboot() threading.Thread(target=do_reboot).start() @@ -218,21 +218,7 @@ def getSshAuthorizedKeys(): @dispatcher.add_method def getSimInfo(): - sim_state = android.getprop("gsm.sim.state").split(",") - network_type = android.getprop("gsm.network.type").split(',') - mcc_mnc = android.getprop("gsm.sim.operator.numeric") or None - - sim_id = android.parse_service_call_string(android.service_call(['iphonesubinfo', '11'])) - cell_data_state = android.parse_service_call_unpack(android.service_call(['phone', '46']), ">q") - cell_data_connected = (cell_data_state == 2) - - return { - 'sim_id': sim_id, - 'mcc_mnc': mcc_mnc, - 'network_type': network_type, - 'sim_state': sim_state, - 'data_connected': cell_data_connected - } + return HARDWARE.get_sim_info() @dispatcher.add_method diff --git a/selfdrive/boardd/SConscript b/selfdrive/boardd/SConscript index 0ccbe6bd2..94e630a64 100644 --- a/selfdrive/boardd/SConscript +++ b/selfdrive/boardd/SConscript @@ -1,6 +1,6 @@ Import('env', 'common', 'cereal', 'messaging', 'cython_dependencies') -env.Program('boardd', ['boardd.cc', 'panda.cc'], LIBS=['usb-1.0', common, cereal, messaging, 'pthread', 'zmq', 'capnp', 'kj']) +env.Program('boardd', ['boardd.cc', 'panda.cc', 'pigeon.cc'], LIBS=['usb-1.0', common, cereal, messaging, 'pthread', 'zmq', 'capnp', 'kj']) env.Library('libcan_list_to_can_capnp', ['can_list_to_can_capnp.cc']) env.Command(['boardd_api_impl.so', 'boardd_api_impl.cpp'], diff --git a/selfdrive/boardd/boardd.cc b/selfdrive/boardd/boardd.cc index a3bb2b0b4..9823ad985 100644 --- a/selfdrive/boardd/boardd.cc +++ b/selfdrive/boardd/boardd.cc @@ -28,6 +28,7 @@ #include "messaging.hpp" #include "panda.h" +#include "pigeon.h" #define MAX_IR_POWER 0.5f @@ -35,15 +36,6 @@ #define CUTOFF_IL 200 #define SATURATE_IL 1600 #define NIBBLE_TO_HEX(n) ((n) < 10 ? (n) + '0' : ((n) - 10) + 'a') -#define VOLTAGE_K 0.091 // LPF gain for 5s tau (dt/tau / (dt/tau + 1)) - -#ifdef QCOM -const uint32_t NO_IGNITION_CNT_MAX = 2 * 60 * 60 * 30; // turn off charge after 30 hrs -const float VBATT_START_CHARGING = 11.5; -const float VBATT_PAUSE_CHARGING = 11.0; -#endif - -namespace { Panda * panda = NULL; std::atomic safety_setter_thread_running(false); @@ -195,13 +187,9 @@ void usb_retry_connect() { } void can_recv(PubMaster &pm) { - uint64_t start_time = nanos_since_boot(); - // create message - capnp::MallocMessageBuilder msg; - cereal::Event::Builder event = msg.initRoot(); - event.setLogMonoTime(start_time); - + MessageBuilder msg; + auto event = msg.initEvent(); int recv = panda->can_receive(event); if (recv){ pm.send("can", msg); @@ -266,7 +254,7 @@ void can_recv_thread() { useconds_t sleep = remaining / 1000; usleep(sleep); } else { - LOGW("missed cycle"); + LOGW("missed cycles (%d) %lld", (int)-1*remaining/dt, remaining); next_frame_time = cur_time; } @@ -280,14 +268,11 @@ void can_health_thread() { uint32_t no_ignition_cnt = 0; bool ignition_last = false; - float voltage_f = 12.5; // filtered voltage // Broadcast empty health message when panda is not yet connected while (!panda){ - capnp::MallocMessageBuilder msg; - cereal::Event::Builder event = msg.initRoot(); - event.setLogMonoTime(nanos_since_boot()); - auto healthData = event.initHealth(); + MessageBuilder msg; + auto healthData = msg.initEvent().initHealth(); healthData.setHwType(cereal::HealthData::HwType::UNKNOWN); pm.send("health", msg); @@ -296,10 +281,8 @@ void can_health_thread() { // run at 2hz while (!do_exit && panda->connected) { - capnp::MallocMessageBuilder msg; - cereal::Event::Builder event = msg.initRoot(); - event.setLogMonoTime(nanos_since_boot()); - auto healthData = event.initHealth(); + MessageBuilder msg; + auto healthData = msg.initEvent().initHealth(); health_t health = panda->get_health(); @@ -307,8 +290,6 @@ void can_health_thread() { health.ignition_line = 1; } - voltage_f = VOLTAGE_K * (health.voltage / 1000.0) + (1.0 - VOLTAGE_K) * voltage_f; // LPF - // Make sure CAN buses are live: safety_setter_thread does not work if Panda CAN are silent and there is only one other CAN node if (health.safety_model == (uint8_t)(cereal::CarParams::SafetyModel::SILENT)) { panda->set_safety_model(cereal::CarParams::SafetyModel::NO_OUTPUT); @@ -322,24 +303,6 @@ void can_health_thread() { no_ignition_cnt += 1; } -#ifdef QCOM - bool cdp_mode = health.usb_power_mode == (uint8_t)(cereal::HealthData::UsbPowerMode::CDP); - bool no_ignition_exp = no_ignition_cnt > NO_IGNITION_CNT_MAX; - if ((no_ignition_exp || (voltage_f < VBATT_PAUSE_CHARGING)) && cdp_mode && !ignition) { - std::vector disable_power_down = read_db_bytes("DisablePowerDown"); - if (disable_power_down.size() != 1 || disable_power_down[0] != '1') { - LOGW("TURN OFF CHARGING!\n"); - panda->set_usb_power_mode(cereal::HealthData::UsbPowerMode::CLIENT); - LOGW("POWER DOWN DEVICE\n"); - system("service call power 17 i32 0 i32 1"); - } - } - if (!no_ignition_exp && (voltage_f > VBATT_START_CHARGING) && !cdp_mode) { - LOGW("TURN ON CHARGING!\n"); - panda->set_usb_power_mode(cereal::HealthData::UsbPowerMode::CDP); - } -#endif - #ifndef __x86_64__ bool power_save_desired = !ignition; if (health.power_save_enabled != power_save_desired){ @@ -405,7 +368,7 @@ void can_health_thread() { size_t i = 0; for (size_t f = size_t(cereal::HealthData::FaultType::RELAY_MALFUNCTION); - f <= size_t(cereal::HealthData::FaultType::INTERRUPT_RATE_KLINE_INIT); f++){ + f <= size_t(cereal::HealthData::FaultType::INTERRUPT_RATE_TIM9); f++){ if (fault_bits.test(f)) { faults.set(i, cereal::HealthData::FaultType(f)); i++; @@ -421,13 +384,16 @@ void hardware_control_thread() { LOGD("start hardware control thread"); SubMaster sm({"thermal", "frontFrame"}); - // Only control fan speed on UNO - if (panda->hw_type != cereal::HealthData::HwType::UNO) return; + // Other pandas don't have hardware to control + if (panda->hw_type != cereal::HealthData::HwType::UNO && panda->hw_type != cereal::HealthData::HwType::DOS) return; uint64_t last_front_frame_t = 0; uint16_t prev_fan_speed = 999; uint16_t ir_pwr = 0; uint16_t prev_ir_pwr = 999; +#ifdef QCOM + bool prev_charging_disabled = false; +#endif unsigned int cnt = 0; while (!do_exit && panda->connected) { @@ -435,11 +401,27 @@ void hardware_control_thread() { sm.update(1000); // TODO: what happens if EINTR is sent while in sm.update? if (sm.updated("thermal")){ + // Fan speed uint16_t fan_speed = sm["thermal"].getThermal().getFanSpeed(); if (fan_speed != prev_fan_speed || cnt % 100 == 0){ panda->set_fan_speed(fan_speed); prev_fan_speed = fan_speed; } + +#ifdef QCOM + // Charging mode + bool charging_disabled = sm["thermal"].getThermal().getChargingDisabled(); + if (charging_disabled != prev_charging_disabled){ + if (charging_disabled){ + panda->set_usb_power_mode(cereal::HealthData::UsbPowerMode::CLIENT); + LOGW("TURN OFF CHARGING!\n"); + } else { + panda->set_usb_power_mode(cereal::HealthData::UsbPowerMode::CDP); + LOGW("TURN ON CHARGING!\n"); + } + prev_charging_disabled = charging_disabled; + } +#endif } if (sm.updated("frontFrame")){ auto event = sm["frontFrame"]; @@ -468,82 +450,11 @@ void hardware_control_thread() { } } -#define pigeon_send(x) _pigeon_send(x, sizeof(x)-1) -void _pigeon_send(const char *dat, int len) { - unsigned char a[0x20+1]; - a[0] = 1; - for (int i=0; iusb_bulk_write(2, a, ll+1); - } -} - -void pigeon_set_power(int power) { - panda->usb_write(0xd9, power, 0); -} - -void pigeon_set_baud(int baud) { - panda->usb_write(0xe2, 1, 0); - panda->usb_write(0xe4, 1, baud/300); -} - -void pigeon_init() { - usleep(1000*1000); - LOGW("panda GPS start"); - - // power off pigeon - pigeon_set_power(0); - usleep(100*1000); - - // 9600 baud at init - pigeon_set_baud(9600); - - // power on pigeon - pigeon_set_power(1); - usleep(500*1000); - - // baud rate upping - pigeon_send("\x24\x50\x55\x42\x58\x2C\x34\x31\x2C\x31\x2C\x30\x30\x30\x37\x2C\x30\x30\x30\x33\x2C\x34\x36\x30\x38\x30\x30\x2C\x30\x2A\x31\x35\x0D\x0A"); - usleep(100*1000); - - // set baud rate to 460800 - pigeon_set_baud(460800); - usleep(100*1000); - - // init from ubloxd - // To generate this data, run test/ubloxd.py with the print statements enabled in the write function in panda/python/serial.py - pigeon_send("\xB5\x62\x06\x00\x14\x00\x03\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x01\x00\x00\x00\x00\x00\x1E\x7F"); - pigeon_send("\xB5\x62\x06\x3E\x00\x00\x44\xD2"); - pigeon_send("\xB5\x62\x06\x00\x14\x00\x00\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x19\x35"); - pigeon_send("\xB5\x62\x06\x00\x14\x00\x01\x00\x00\x00\xC0\x08\x00\x00\x00\x08\x07\x00\x01\x00\x01\x00\x00\x00\x00\x00\xF4\x80"); - pigeon_send("\xB5\x62\x06\x00\x14\x00\x04\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x1D\x85"); - pigeon_send("\xB5\x62\x06\x00\x00\x00\x06\x18"); - pigeon_send("\xB5\x62\x06\x00\x01\x00\x01\x08\x22"); - pigeon_send("\xB5\x62\x06\x00\x01\x00\x02\x09\x23"); - pigeon_send("\xB5\x62\x06\x00\x01\x00\x03\x0A\x24"); - pigeon_send("\xB5\x62\x06\x08\x06\x00\x64\x00\x01\x00\x00\x00\x79\x10"); - pigeon_send("\xB5\x62\x06\x24\x24\x00\x05\x00\x04\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x5A\x63"); - pigeon_send("\xB5\x62\x06\x1E\x14\x00\x00\x00\x00\x00\x01\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x3C\x37"); - pigeon_send("\xB5\x62\x06\x24\x00\x00\x2A\x84"); - pigeon_send("\xB5\x62\x06\x23\x00\x00\x29\x81"); - pigeon_send("\xB5\x62\x06\x1E\x00\x00\x24\x72"); - pigeon_send("\xB5\x62\x06\x01\x03\x00\x01\x07\x01\x13\x51"); - pigeon_send("\xB5\x62\x06\x01\x03\x00\x02\x15\x01\x22\x70"); - pigeon_send("\xB5\x62\x06\x01\x03\x00\x02\x13\x01\x20\x6C"); - pigeon_send("\xB5\x62\x06\x01\x03\x00\x0A\x09\x01\x1E\x70"); - - LOGW("panda GPS on"); -} - -static void pigeon_publish_raw(PubMaster &pm, unsigned char *dat, int alen) { +static void pigeon_publish_raw(PubMaster &pm, std::string dat) { // create message - capnp::MallocMessageBuilder msg; - cereal::Event::Builder event = msg.initRoot(); - event.setLogMonoTime(nanos_since_boot()); - auto ublox_raw = event.initUbloxRaw(alen); - memcpy(ublox_raw.begin(), dat, alen); + MessageBuilder msg; + auto ublox_raw = msg.initEvent().initUbloxRaw(dat.length()); + memcpy(ublox_raw.begin(), dat.data(), dat.length()); pm.send("ubloxRaw", msg); } @@ -555,37 +466,32 @@ void pigeon_thread() { // ubloxRaw = 8042 PubMaster pm({"ubloxRaw"}); - // run at ~100hz - unsigned char dat[0x1000]; - uint64_t cnt = 0; +#ifdef QCOM2 + Pigeon * pigeon = Pigeon::connect("/dev/ttyHS0"); +#else + Pigeon * pigeon = Pigeon::connect(panda); +#endif - pigeon_init(); + pigeon->init(); while (!do_exit && panda->connected) { - int alen = 0; - while (alen < 0xfc0) { - int len = panda->usb_read(0xe0, 1, 0, dat+alen, 0x40); - if (len <= 0) break; - - //printf("got %d\n", len); - alen += len; - } - if (alen > 0) { - if (dat[0] == (char)0x00){ + std::string recv = pigeon->receive(); + if (recv.length() > 0) { + if (recv[0] == (char)0x00){ LOGW("received invalid ublox message, resetting panda GPS"); - pigeon_init(); + pigeon->init(); } else { - pigeon_publish_raw(pm, dat, alen); + pigeon_publish_raw(pm, recv); } } - // 10ms + // 10ms - 100 Hz usleep(10*1000); - cnt++; } + + delete pigeon; } -} int main() { int err; @@ -606,6 +512,8 @@ int main() { fake_send = true; } + panda_set_power(true); + while (!do_exit){ std::vector threads; threads.push_back(std::thread(can_health_thread)); diff --git a/selfdrive/boardd/can_list_to_can_capnp.cc b/selfdrive/boardd/can_list_to_can_capnp.cc index 8ba9d3d56..2fa3f5b9b 100644 --- a/selfdrive/boardd/can_list_to_can_capnp.cc +++ b/selfdrive/boardd/can_list_to_can_capnp.cc @@ -1,10 +1,4 @@ -#include -#include -#include -#include "common/timing.h" -#include -#include "cereal/gen/cpp/log.capnp.h" -#include "cereal/gen/cpp/car.capnp.h" +#include "messaging.hpp" typedef struct { long address; @@ -16,21 +10,19 @@ typedef struct { extern "C" { void can_list_to_can_capnp_cpp(const std::vector &can_list, std::string &out, bool sendCan, bool valid) { - capnp::MallocMessageBuilder msg; - cereal::Event::Builder event = msg.initRoot(); - event.setLogMonoTime(nanos_since_boot()); - event.setValid(valid); + MessageBuilder msg; + auto event = msg.initEvent(valid); auto canData = sendCan ? event.initSendcan(can_list.size()) : event.initCan(can_list.size()); int j = 0; for (auto it = can_list.begin(); it != can_list.end(); it++, j++) { - canData[j].setAddress(it->address); - canData[j].setBusTime(it->busTime); - canData[j].setDat(kj::arrayPtr((uint8_t*)it->dat.data(), it->dat.size())); - canData[j].setSrc(it->src); + auto c = canData[j]; + c.setAddress(it->address); + c.setBusTime(it->busTime); + c.setDat(kj::arrayPtr((uint8_t*)it->dat.data(), it->dat.size())); + c.setSrc(it->src); } - auto words = capnp::messageToFlatArray(msg); - auto bytes = words.asBytes(); + auto bytes = msg.toBytes(); out.append((const char *)bytes.begin(), bytes.size()); } diff --git a/selfdrive/boardd/panda.cc b/selfdrive/boardd/panda.cc index 5820fa400..21ce0c301 100644 --- a/selfdrive/boardd/panda.cc +++ b/selfdrive/boardd/panda.cc @@ -2,10 +2,29 @@ #include #include +#include + #include "common/swaglog.h" +#include "common/gpio.h" #include "panda.h" +void panda_set_power(bool power){ +#ifdef QCOM2 + int err = 0; + err += gpio_init(GPIO_STM_RST_N, true); + err += gpio_init(GPIO_STM_BOOT0, true); + + err += gpio_set(GPIO_STM_RST_N, false); + err += gpio_set(GPIO_STM_BOOT0, false); + + usleep(100*1000); // 100 ms + + err += gpio_set(GPIO_STM_RST_N, power); + assert(err == 0); +#endif +} + Panda::Panda(){ int err; @@ -39,8 +58,10 @@ Panda::Panda(){ is_pigeon = (hw_type == cereal::HealthData::HwType::GREY_PANDA) || (hw_type == cereal::HealthData::HwType::BLACK_PANDA) || - (hw_type == cereal::HealthData::HwType::UNO); - has_rtc = (hw_type == cereal::HealthData::HwType::UNO); + (hw_type == cereal::HealthData::HwType::UNO) || + (hw_type == cereal::HealthData::HwType::DOS); + has_rtc = (hw_type == cereal::HealthData::HwType::UNO) || + (hw_type == cereal::HealthData::HwType::DOS); return; @@ -80,6 +101,10 @@ int Panda::usb_write(uint8_t bRequest, uint16_t wValue, uint16_t wIndex, unsigne int err; const uint8_t bmRequestType = LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE; + if (!connected){ + return LIBUSB_ERROR_NO_DEVICE; + } + pthread_mutex_lock(&usb_lock); do { err = libusb_control_transfer(dev_handle, bmRequestType, bRequest, wValue, wIndex, NULL, 0, timeout); @@ -109,6 +134,10 @@ int Panda::usb_bulk_write(unsigned char endpoint, unsigned char* data, int lengt int err; int transferred = 0; + if (!connected){ + return 0; + } + pthread_mutex_lock(&usb_lock); do { // Try sending can messages. If the receive buffer on the panda is full it will NAK @@ -131,6 +160,10 @@ int Panda::usb_bulk_read(unsigned char endpoint, unsigned char* data, int length int err; int transferred = 0; + if (!connected){ + return 0; + } + pthread_mutex_lock(&usb_lock); do { diff --git a/selfdrive/boardd/panda.h b/selfdrive/boardd/panda.h index 3586cda74..c3e2dc981 100644 --- a/selfdrive/boardd/panda.h +++ b/selfdrive/boardd/panda.h @@ -34,6 +34,9 @@ struct __attribute__((packed)) health_t { uint8_t power_save_enabled; }; + +void panda_set_power(bool power); + class Panda { private: libusb_context *ctx = NULL; diff --git a/selfdrive/boardd/pigeon.cc b/selfdrive/boardd/pigeon.cc new file mode 100644 index 000000000..4ec7ebf8c --- /dev/null +++ b/selfdrive/boardd/pigeon.cc @@ -0,0 +1,226 @@ +#include +#include +#include +#include +#include + +#include "common/swaglog.h" +#include "common/gpio.h" + +#include "pigeon.h" + +// Termios on macos doesn't define all baud rate constants +#ifndef B460800 +#define B460800 0010004 +#endif + +using namespace std::string_literals; + + +Pigeon * Pigeon::connect(Panda * p){ + PandaPigeon * pigeon = new PandaPigeon(); + pigeon->connect(p); + + return pigeon; +} + +Pigeon * Pigeon::connect(const char * tty){ + TTYPigeon * pigeon = new TTYPigeon(); + pigeon->connect(tty); + + return pigeon; +} + +void Pigeon::init() { + usleep(1000*1000); + LOGW("panda GPS start"); + + // power off pigeon + set_power(0); + usleep(100*1000); + + // 9600 baud at init + set_baud(9600); + + // power on pigeon + set_power(1); + usleep(500*1000); + + // baud rate upping + send("\x24\x50\x55\x42\x58\x2C\x34\x31\x2C\x31\x2C\x30\x30\x30\x37\x2C\x30\x30\x30\x33\x2C\x34\x36\x30\x38\x30\x30\x2C\x30\x2A\x31\x35\x0D\x0A"s); + usleep(100*1000); + + // set baud rate to 460800 + set_baud(460800); + usleep(100*1000); + + // init from ubloxd + // To generate this data, run test/ubloxd.py with the print statements enabled in the write function in panda/python/serial.py + send("\xB5\x62\x06\x00\x14\x00\x03\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x01\x00\x00\x00\x00\x00\x1E\x7F"s); + send("\xB5\x62\x06\x3E\x00\x00\x44\xD2"s); + send("\xB5\x62\x06\x00\x14\x00\x00\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x19\x35"s); + send("\xB5\x62\x06\x00\x14\x00\x01\x00\x00\x00\xC0\x08\x00\x00\x00\x08\x07\x00\x01\x00\x01\x00\x00\x00\x00\x00\xF4\x80"s); + send("\xB5\x62\x06\x00\x14\x00\x04\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x1D\x85"s); + send("\xB5\x62\x06\x00\x00\x00\x06\x18"s); + send("\xB5\x62\x06\x00\x01\x00\x01\x08\x22"s); + send("\xB5\x62\x06\x00\x01\x00\x02\x09\x23"s); + send("\xB5\x62\x06\x00\x01\x00\x03\x0A\x24"s); + send("\xB5\x62\x06\x08\x06\x00\x64\x00\x01\x00\x00\x00\x79\x10"s); + send("\xB5\x62\x06\x24\x24\x00\x05\x00\x04\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x5A\x63"s); + send("\xB5\x62\x06\x1E\x14\x00\x00\x00\x00\x00\x01\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x3C\x37"s); + send("\xB5\x62\x06\x24\x00\x00\x2A\x84"s); + send("\xB5\x62\x06\x23\x00\x00\x29\x81"s); + send("\xB5\x62\x06\x1E\x00\x00\x24\x72"s); + send("\xB5\x62\x06\x01\x03\x00\x01\x07\x01\x13\x51"s); + send("\xB5\x62\x06\x01\x03\x00\x02\x15\x01\x22\x70"s); + send("\xB5\x62\x06\x01\x03\x00\x02\x13\x01\x20\x6C"s); + send("\xB5\x62\x06\x01\x03\x00\x0A\x09\x01\x1E\x70"s); + + LOGW("panda GPS on"); +} + +void PandaPigeon::connect(Panda * p) { + panda = p; +} + +void PandaPigeon::set_baud(int baud) { + panda->usb_write(0xe2, 1, 0); + panda->usb_write(0xe4, 1, baud/300); +} + +void PandaPigeon::send(std::string s) { + int len = s.length(); + const char * dat = s.data(); + + unsigned char a[0x20+1]; + a[0] = 1; + for (int i=0; iusb_bulk_write(2, a, ll+1); + } +} + +std::string PandaPigeon::receive() { + std::string r; + + while (true){ + unsigned char dat[0x40]; + int len = panda->usb_read(0xe0, 1, 0, dat, sizeof(dat)); + if (len <= 0 || r.length() > 0x1000) break; + r.append((char*)dat, len); + } + + return r; +} + +void PandaPigeon::set_power(bool power) { + panda->usb_write(0xd9, power, 0); +} + +PandaPigeon::~PandaPigeon(){ +} + +void handle_tty_issue(int err, const char func[]) { + LOGE_100("tty error %d \"%s\" in %s", err, strerror(err), func); +} + +void TTYPigeon::connect(const char * tty) { + pigeon_tty_fd = open(tty, O_RDWR); + if (pigeon_tty_fd < 0){ + handle_tty_issue(errno, __func__); + assert(pigeon_tty_fd >= 0); + } + assert(tcgetattr(pigeon_tty_fd, &pigeon_tty) == 0); + + // configure tty + pigeon_tty.c_cflag &= ~PARENB; // disable parity + pigeon_tty.c_cflag &= ~CSTOPB; // single stop bit + pigeon_tty.c_cflag |= CS8; // 8 bits per byte + pigeon_tty.c_cflag &= ~CRTSCTS; // no RTS/CTS flow control + pigeon_tty.c_cflag |= CREAD | CLOCAL; // turn on READ & ignore ctrl lines + pigeon_tty.c_lflag &= ~ICANON; // disable canonical mode + pigeon_tty.c_lflag &= ~ISIG; // disable interpretation of INTR, QUIT and SUSP + pigeon_tty.c_iflag &= ~(IXON | IXOFF | IXANY); // turn off software flow ctrl + pigeon_tty.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL); // disable any special handling of received bytes + pigeon_tty.c_oflag &= ~OPOST; // prevent special interpretation of output bytes + pigeon_tty.c_oflag &= ~ONLCR; // prevent conversion of newline to carriage return/line feed + + // configure blocking behavior + pigeon_tty.c_cc[VMIN] = 0; // min amount of characters returned + pigeon_tty.c_cc[VTIME] = 0; // max blocking time in s/10 (0=inf) + + assert(tcsetattr(pigeon_tty_fd, TCSANOW, &pigeon_tty) == 0); +} + +void TTYPigeon::set_baud(int baud){ + speed_t baud_const = 0; + switch(baud){ + case 9600: + baud_const = B9600; + break; + case 460800: + baud_const = B460800; + break; + default: + assert(false); + } + + // make sure everything is tx'ed before changing baud + assert(tcdrain(pigeon_tty_fd) == 0); + + // change baud + assert(tcgetattr(pigeon_tty_fd, &pigeon_tty) == 0); + assert(cfsetspeed(&pigeon_tty, baud_const) == 0); + assert(tcsetattr(pigeon_tty_fd, TCSANOW, &pigeon_tty) == 0); + + // flush + assert(tcflush(pigeon_tty_fd, TCIOFLUSH) == 0); +} + +void TTYPigeon::send(std::string s) { + int len = s.length(); + const char * dat = s.data(); + + int err = write(pigeon_tty_fd, dat, len); + if(err < 0) { handle_tty_issue(err, __func__); } + err = tcdrain(pigeon_tty_fd); + if(err < 0) { handle_tty_issue(err, __func__); } +} + +std::string TTYPigeon::receive() { + std::string r; + + while (true){ + char dat[0x40]; + int len = read(pigeon_tty_fd, dat, sizeof(dat)); + if(len < 0) { + handle_tty_issue(len, __func__); + } else if (len == 0 || r.length() > 0x1000){ + break; + } else { + r.append(dat, len); + } + + } + return r; +} + +void TTYPigeon::set_power(bool power){ +#ifdef QCOM2 + int err = 0; + err += gpio_init(GPIO_UBLOX_RST_N, true); + err += gpio_init(GPIO_UBLOX_SAFEBOOT_N, true); + err += gpio_init(GPIO_UBLOX_PWR_EN, true); + + err += gpio_set(GPIO_UBLOX_RST_N, power); + err += gpio_set(GPIO_UBLOX_SAFEBOOT_N, power); + err += gpio_set(GPIO_UBLOX_PWR_EN, power); + assert(err == 0); +#endif +} + +TTYPigeon::~TTYPigeon(){ + close(pigeon_tty_fd); +} diff --git a/selfdrive/boardd/pigeon.h b/selfdrive/boardd/pigeon.h new file mode 100644 index 000000000..667ac7061 --- /dev/null +++ b/selfdrive/boardd/pigeon.h @@ -0,0 +1,43 @@ +#pragma once +#include +#include + + +#include "panda.h" + +class Pigeon { + public: + static Pigeon* connect(Panda * p); + static Pigeon* connect(const char * tty); + virtual ~Pigeon(){}; + + void init(); + virtual void set_baud(int baud) = 0; + virtual void send(std::string s) = 0; + virtual std::string receive() = 0; + virtual void set_power(bool power) = 0; +}; + +class PandaPigeon : public Pigeon { + Panda * panda = NULL; +public: + ~PandaPigeon(); + void connect(Panda * p); + void set_baud(int baud); + void send(std::string s); + std::string receive(); + void set_power(bool power); +}; + + +class TTYPigeon : public Pigeon { + int pigeon_tty_fd = -1; + struct termios pigeon_tty; +public: + ~TTYPigeon(); + void connect(const char* tty); + void set_baud(int baud); + void send(std::string s); + std::string receive(); + void set_power(bool power); +}; diff --git a/selfdrive/camerad/SConscript b/selfdrive/camerad/SConscript index 1b205198c..7a81b76c8 100644 --- a/selfdrive/camerad/SConscript +++ b/selfdrive/camerad/SConscript @@ -11,6 +11,10 @@ if arch == "aarch64": elif arch == "larch64": libs += [] cameras = ['cameras/camera_qcom2.c'] + # no screen + # env = env.Clone() + # env.Append(CXXFLAGS = '-DNOSCREEN') + # env.Append(CFLAGS = '-DNOSCREEN') else: if webcam: libs += ['opencv_core', 'opencv_highgui', 'opencv_imgproc', 'opencv_videoio'] @@ -20,8 +24,8 @@ else: env.Append(CFLAGS = '-DWEBCAM') env.Append(CPPPATH = '/usr/local/include/opencv4') else: - libs += [] cameras = ['cameras/camera_frame_stream.cc'] + if arch == "Darwin": del libs[libs.index('OpenCL')] env = env.Clone() diff --git a/selfdrive/camerad/cameras/camera_frame_stream.cc b/selfdrive/camerad/cameras/camera_frame_stream.cc index aecca564a..9a4263103 100644 --- a/selfdrive/camerad/cameras/camera_frame_stream.cc +++ b/selfdrive/camerad/cameras/camera_frame_stream.cc @@ -45,7 +45,7 @@ void camera_init(CameraState *s, int camera_id, unsigned int fps) { tbuffer_init2(&s->camera_tb, FRAME_BUF_COUNT, "frame", camera_release_buffer, s); } -void run_frame_stream(DualCameraState *s) { +void run_frame_stream(MultiCameraState *s) { SubMaster sm({"frame"}); CameraState *const rear_camera = &s->rear; @@ -93,7 +93,7 @@ CameraInfo cameras_supported[CAMERA_ID_MAX] = { }, }; -void cameras_init(DualCameraState *s) { +void cameras_init(MultiCameraState *s) { camera_init(&s->rear, CAMERA_ID_IMX298, 20); s->rear.transform = (mat3){{ 1.0, 0.0, 0.0, @@ -111,7 +111,7 @@ void cameras_init(DualCameraState *s) { void camera_autoexposure(CameraState *s, float grey_frac) {} -void cameras_open(DualCameraState *s, VisionBuf *camera_bufs_rear, +void cameras_open(MultiCameraState *s, VisionBuf *camera_bufs_rear, VisionBuf *camera_bufs_focus, VisionBuf *camera_bufs_stats, VisionBuf *camera_bufs_front) { assert(camera_bufs_rear); @@ -124,11 +124,11 @@ void cameras_open(DualCameraState *s, VisionBuf *camera_bufs_rear, camera_open(&s->rear, camera_bufs_rear, true); } -void cameras_close(DualCameraState *s) { +void cameras_close(MultiCameraState *s) { camera_close(&s->rear); } -void cameras_run(DualCameraState *s) { +void cameras_run(MultiCameraState *s) { set_thread_name("frame_streaming"); run_frame_stream(s); cameras_close(s); diff --git a/selfdrive/camerad/cameras/camera_frame_stream.h b/selfdrive/camerad/cameras/camera_frame_stream.h index 80ff54b5e..a29e40304 100644 --- a/selfdrive/camerad/cameras/camera_frame_stream.h +++ b/selfdrive/camerad/cameras/camera_frame_stream.h @@ -40,17 +40,17 @@ typedef struct CameraState { } CameraState; -typedef struct DualCameraState { +typedef struct MultiCameraState { int ispif_fd; CameraState rear; CameraState front; -} DualCameraState; +} MultiCameraState; -void cameras_init(DualCameraState *s); -void cameras_open(DualCameraState *s, VisionBuf *camera_bufs_rear, VisionBuf *camera_bufs_focus, VisionBuf *camera_bufs_stats, VisionBuf *camera_bufs_front); -void cameras_run(DualCameraState *s); -void cameras_close(DualCameraState *s); +void cameras_init(MultiCameraState *s); +void cameras_open(MultiCameraState *s, VisionBuf *camera_bufs_rear, VisionBuf *camera_bufs_focus, VisionBuf *camera_bufs_stats, VisionBuf *camera_bufs_front); +void cameras_run(MultiCameraState *s); +void cameras_close(MultiCameraState *s); void camera_autoexposure(CameraState *s, float grey_frac); #ifdef __cplusplus } // extern "C" diff --git a/selfdrive/camerad/cameras/camera_qcom.cc b/selfdrive/camerad/cameras/camera_qcom.cc index dfed65238..27709ef22 100644 --- a/selfdrive/camerad/cameras/camera_qcom.cc +++ b/selfdrive/camerad/cameras/camera_qcom.cc @@ -258,7 +258,7 @@ static int imx179_s5k3p8sp_apply_exposure(CameraState *s, int gain, int integ_li return err; } -void cameras_init(DualCameraState *s) { +void cameras_init(MultiCameraState *s) { char project_name[1024] = {0}; property_get("ro.boot.project_name", project_name, ""); @@ -545,10 +545,10 @@ static void imx298_ois_calibration(int ois_fd, uint8_t* eeprom) { -static void sensors_init(DualCameraState *s) { +static void sensors_init(MultiCameraState *s) { int err; - int sensorinit_fd = -1; + unique_fd sensorinit_fd; if (s->device == DEVICE_LP3) { sensorinit_fd = open("/dev/v4l-subdev11", O_RDWR | O_NONBLOCK); } else { @@ -1829,7 +1829,7 @@ static void front_start(CameraState *s) { -void cameras_open(DualCameraState *s, VisionBuf *camera_bufs_rear, VisionBuf *camera_bufs_focus, VisionBuf *camera_bufs_stats, VisionBuf *camera_bufs_front) { +void cameras_open(MultiCameraState *s, VisionBuf *camera_bufs_rear, VisionBuf *camera_bufs_focus, VisionBuf *camera_bufs_stats, VisionBuf *camera_bufs_front) { int err; struct ispif_cfg_data ispif_cfg_data; @@ -1866,13 +1866,13 @@ void cameras_open(DualCameraState *s, VisionBuf *camera_bufs_rear, VisionBuf *ca assert(camera_bufs_rear); assert(camera_bufs_front); - int msmcfg_fd = open("/dev/media0", O_RDWR | O_NONBLOCK); - assert(msmcfg_fd >= 0); + s->msmcfg_fd = open("/dev/media0", O_RDWR | O_NONBLOCK); + assert(s->msmcfg_fd >= 0); sensors_init(s); - int v4l_fd = open("/dev/video0", O_RDWR | O_NONBLOCK); - assert(v4l_fd >= 0); + s->v4l_fd = open("/dev/video0", O_RDWR | O_NONBLOCK); + assert(s->v4l_fd >= 0); if (s->device == DEVICE_LP3) { s->ispif_fd = open("/dev/v4l-subdev15", O_RDWR | O_NONBLOCK); @@ -2018,7 +2018,7 @@ static void ops_term() { static void* ops_thread(void* arg) { int err; - DualCameraState *s = (DualCameraState*)arg; + MultiCameraState *s = (MultiCameraState*)arg; set_thread_name("camera_settings"); @@ -2109,7 +2109,7 @@ static void* ops_thread(void* arg) { return NULL; } -void cameras_run(DualCameraState *s) { +void cameras_run(MultiCameraState *s) { int err; pthread_t ops_thread_handle; @@ -2221,7 +2221,7 @@ void cameras_run(DualCameraState *s) { cameras_close(s); } -void cameras_close(DualCameraState *s) { +void cameras_close(MultiCameraState *s) { camera_close(&s->rear); camera_close(&s->front); } diff --git a/selfdrive/camerad/cameras/camera_qcom.h b/selfdrive/camerad/cameras/camera_qcom.h index 812f1a596..02e139d7c 100644 --- a/selfdrive/camerad/cameras/camera_qcom.h +++ b/selfdrive/camerad/cameras/camera_qcom.h @@ -15,6 +15,7 @@ #include "common/mat.h" #include "common/visionbuf.h" #include "common/buffering.h" +#include "common/utilpp.h" #include "camera_common.h" @@ -69,13 +70,13 @@ typedef struct CameraState { uint32_t line_length_pclk; unsigned int max_gain; - int csid_fd; - int csiphy_fd; - int sensor_fd; - int isp_fd; - int eeprom_fd; + unique_fd csid_fd; + unique_fd csiphy_fd; + unique_fd sensor_fd; + unique_fd isp_fd; + unique_fd eeprom_fd; // rear only - int ois_fd, actuator_fd; + unique_fd ois_fd, actuator_fd; uint16_t infinity_dac; struct msm_vfe_axi_stream_cfg_cmd stream_cfg; @@ -123,19 +124,21 @@ typedef struct CameraState { } CameraState; -typedef struct DualCameraState { +typedef struct MultiCameraState { int device; - int ispif_fd; + unique_fd ispif_fd; + unique_fd msmcfg_fd; + unique_fd v4l_fd; CameraState rear; CameraState front; -} DualCameraState; +} MultiCameraState; -void cameras_init(DualCameraState *s); -void cameras_open(DualCameraState *s, VisionBuf *camera_bufs_rear, VisionBuf *camera_bufs_focus, VisionBuf *camera_bufs_stats, VisionBuf *camera_bufs_front); -void cameras_run(DualCameraState *s); -void cameras_close(DualCameraState *s); +void cameras_init(MultiCameraState *s); +void cameras_open(MultiCameraState *s, VisionBuf *camera_bufs_rear, VisionBuf *camera_bufs_focus, VisionBuf *camera_bufs_stats, VisionBuf *camera_bufs_front); +void cameras_run(MultiCameraState *s); +void cameras_close(MultiCameraState *s); void camera_autoexposure(CameraState *s, float grey_frac); void actuator_move(CameraState *s, uint16_t target); diff --git a/selfdrive/camerad/cameras/debayer.cl b/selfdrive/camerad/cameras/debayer.cl index c9e3716df..5188dc88c 100644 --- a/selfdrive/camerad/cameras/debayer.cl +++ b/selfdrive/camerad/cameras/debayer.cl @@ -81,28 +81,18 @@ __kernel void debayer10(__global uchar const * const in, float4 p = convert_float4(pint); -#if NEW == 1 - // now it's 0x2a - const float black_level = 42.0f; - // max on black level - p = max(0.0, p - black_level); -#else // 64 is the black level of the sensor, remove // (changed to 56 for HDR) const float black_level = 56.0f; // TODO: switch to max here? p = (p - black_level); -#endif - -#if NEW == 0 // correct vignetting (no pow function?) // see https://www.eecis.udel.edu/~jye/lab_research/09/JiUp.pdf the A (4th order) const float r = ((oy - RGB_HEIGHT/2)*(oy - RGB_HEIGHT/2) + (ox - RGB_WIDTH/2)*(ox - RGB_WIDTH/2)); const float fake_f = 700.0f; // should be 910, but this fits... const float lil_a = (1.0f + r/(fake_f*fake_f)); p = p * lil_a * lil_a; -#endif // rescale to 1.0 #if HDR @@ -125,12 +115,8 @@ __kernel void debayer10(__global uchar const * const in, float3 c1 = (float3)(p.s0, (p.s1+p.s2)/2.0f, p.s3); #endif -#if NEW - // TODO: new color correction -#else // color correction c1 = color_correct(c1); -#endif #if HDR // srgb gamma isn't right for YUV, so it's disabled for now diff --git a/selfdrive/camerad/main.cc b/selfdrive/camerad/main.cc index 658ab55ba..c28d6edf5 100644 --- a/selfdrive/camerad/main.cc +++ b/selfdrive/camerad/main.cc @@ -1,6 +1,7 @@ #include #include #include +#include #if defined(QCOM) && !defined(QCOM_REPLAY) #include "cameras/camera_qcom.h" @@ -13,6 +14,7 @@ #endif #include "common/util.h" +#include "common/params.h" #include "common/swaglog.h" #include "common/ipc.h" @@ -33,8 +35,9 @@ #include #define UI_BUF_COUNT 4 +#define DEBAYER_LOCAL_WORKSIZE 16 #define YUV_COUNT 40 -#define MAX_CLIENTS 5 +#define MAX_CLIENTS 6 extern "C" { volatile sig_atomic_t do_exit = 0; @@ -44,6 +47,10 @@ void set_do_exit(int sig) { do_exit = 1; } +/* +TODO: refactor out camera specific things from here +*/ + struct VisionState; struct VisionClientState { @@ -74,8 +81,13 @@ struct VisionState { cl_program prg_debayer_rear; cl_program prg_debayer_front; + cl_program prg_debayer_wide; cl_kernel krnl_debayer_rear; cl_kernel krnl_debayer_front; + cl_kernel krnl_debayer_wide; + int debayer_cl_localMemSize; + size_t debayer_cl_globalWorkSize[2]; + size_t debayer_cl_localWorkSize[2]; cl_program prg_rgb_laplacian; cl_kernel krnl_rgb_laplacian; @@ -88,10 +100,12 @@ struct VisionState { // processing TBuffer ui_tb; TBuffer ui_front_tb; + TBuffer ui_wide_tb; mat3 yuv_transform; TBuffer *yuv_tb; TBuffer *yuv_front_tb; + TBuffer *yuv_wide_tb; // TODO: refactor for both cameras? Pool yuv_pool; @@ -113,6 +127,15 @@ struct VisionState { int yuv_front_width, yuv_front_height; RGBToYUVState front_rgb_to_yuv_state; + Pool yuv_wide_pool; + VisionBuf yuv_wide_ion[YUV_COUNT]; + cl_mem yuv_wide_cl[YUV_COUNT]; + YUVBuf yuv_wide_bufs[YUV_COUNT]; + FrameMetadata yuv_wide_metas[YUV_COUNT]; + size_t yuv_wide_buf_size; + int yuv_wide_width, yuv_wide_height; + RGBToYUVState wide_rgb_to_yuv_state; + size_t rgb_buf_size; int rgb_width, rgb_height, rgb_stride; VisionBuf rgb_bufs[UI_BUF_COUNT]; @@ -124,11 +147,15 @@ struct VisionState { int rgb_front_width, rgb_front_height, rgb_front_stride; VisionBuf rgb_front_bufs[UI_BUF_COUNT]; cl_mem rgb_front_bufs_cl[UI_BUF_COUNT]; + bool rhd_front; - bool rhd_front_checked; int front_meteringbox_xmin, front_meteringbox_xmax; int front_meteringbox_ymin, front_meteringbox_ymax; + size_t rgb_wide_buf_size; + int rgb_wide_width, rgb_wide_height, rgb_wide_stride; + VisionBuf rgb_wide_bufs[UI_BUF_COUNT]; + cl_mem rgb_wide_bufs_cl[UI_BUF_COUNT]; cl_mem camera_bufs_cl[FRAME_BUF_COUNT]; VisionBuf camera_bufs[FRAME_BUF_COUNT]; @@ -138,7 +165,11 @@ struct VisionState { cl_mem front_camera_bufs_cl[FRAME_BUF_COUNT]; VisionBuf front_camera_bufs[FRAME_BUF_COUNT]; - DualCameraState cameras; + cl_mem wide_camera_bufs_cl[FRAME_BUF_COUNT]; + VisionBuf wide_camera_bufs[FRAME_BUF_COUNT]; + + + MultiCameraState cameras; zsock_t *terminate_pub; @@ -152,11 +183,13 @@ struct VisionState { void* frontview_thread(void *arg) { int err; VisionState *s = (VisionState*)arg; + s->rhd_front = read_db_bool("IsRHD"); set_thread_name("frontview"); + err = set_realtime_priority(51); // we subscribe to this for placement of the AE metering box // TODO: the loop is bad, ideally models shouldn't affect sensors - SubMaster sm({"driverState", "dMonitoringState"}); + SubMaster sm({"driverState"}); cl_command_queue q = clCreateCommandQueue(s->context, s->device_id, 0, &err); assert(err == 0); @@ -179,14 +212,20 @@ void* frontview_thread(void *arg) { assert(err == 0); err = clSetKernelArg(s->krnl_debayer_front, 1, sizeof(cl_mem), &s->rgb_front_bufs_cl[rgb_idx]); assert(err == 0); +#ifdef QCOM2 + err = clSetKernelArg(s->krnl_debayer_front, 2, s->debayer_cl_localMemSize, 0); + assert(err == 0); + err = clEnqueueNDRangeKernel(q, s->krnl_debayer_front, 2, NULL, + s->debayer_cl_globalWorkSize, s->debayer_cl_localWorkSize, 0, 0, &debayer_event); +#else float digital_gain = 1.0; err = clSetKernelArg(s->krnl_debayer_front, 2, sizeof(float), &digital_gain); assert(err == 0); - const size_t debayer_work_size = s->rgb_front_height; // doesn't divide evenly, is this okay? const size_t debayer_local_work_size = 128; err = clEnqueueNDRangeKernel(q, s->krnl_debayer_front, 1, NULL, &debayer_work_size, &debayer_local_work_size, 0, 0, &debayer_event); +#endif assert(err == 0); } else { assert(s->rgb_front_buf_size >= s->cameras.front.frame_size); @@ -197,16 +236,17 @@ void* frontview_thread(void *arg) { } clWaitForEvents(1, &debayer_event); clReleaseEvent(debayer_event); + tbuffer_release(&s->cameras.front.camera_tb, buf_idx); visionbuf_sync(&s->rgb_front_bufs[ui_idx], VISIONBUF_SYNC_FROM_DEVICE); sm.update(0); - // no more check after gps check - if (!s->rhd_front_checked && sm.updated("dMonitoringState")) { - auto state = sm["dMonitoringState"].getDMonitoringState(); - s->rhd_front = state.getIsRHD(); - s->rhd_front_checked = state.getRhdChecked(); + +#ifdef NOSCREEN + if (frame_data.frame_id % 4 == 2) { + sendrgb(&s->cameras, (uint8_t*) s->rgb_front_bufs[rgb_idx].addr, s->rgb_front_bufs[rgb_idx].len, 2); } +#endif if (sm.updated("driverState")) { auto state = sm["driverState"].getDriverState(); @@ -241,6 +281,7 @@ void* frontview_thread(void *arg) { int x_end; int y_start; int y_end; + int skip = 1; if (s->front_meteringbox_xmax > 0) { @@ -256,9 +297,15 @@ void* frontview_thread(void *arg) { x_start = s->rhd_front ? 0:s->rgb_front_width * 3 / 5; x_end = s->rhd_front ? s->rgb_front_width * 2 / 5:s->rgb_front_width; } - +#ifdef QCOM2 + x_start = 0.15*s->rgb_front_width; + x_end = 0.85*s->rgb_front_width; + y_start = 0.5*s->rgb_front_height; + y_end = 0.75*s->rgb_front_height; + skip = 2; +#endif uint32_t lum_binning[256] = {0,}; - for (int y = y_start; y < y_end; ++y) { + for (int y = y_start; y < y_end; y += skip) { for (int x = x_start; x < x_end; x += 2) { // every 2nd col const uint8_t *pix = &bgr_front_ptr[y * s->rgb_front_stride + x * 3]; unsigned int lum = (unsigned int)pix[0] + pix[1] + pix[2]; @@ -272,7 +319,7 @@ void* frontview_thread(void *arg) { lum_binning[std::min(lum / 3, 255u)]++; } } - const unsigned int lum_total = (y_end - y_start) * (x_end - x_start)/2; + const unsigned int lum_total = (y_end - y_start) * (x_end - x_start) / 2 / skip; unsigned int lum_cur = 0; int lum_med = 0; for (lum_med=0; lum_med<256; lum_med++) { @@ -300,11 +347,8 @@ void* frontview_thread(void *arg) { // send frame event { if (s->pm != NULL) { - capnp::MallocMessageBuilder msg; - cereal::Event::Builder event = msg.initRoot(); - event.setLogMonoTime(nanos_since_boot()); - - auto framed = event.initFrontFrame(); + MessageBuilder msg; + auto framed = msg.initEvent().initFrontFrame(); framed.setFrameId(frame_data.frame_id); framed.setEncodeId(cnt); framed.setTimestampEof(frame_data.timestamp_eof); @@ -336,6 +380,159 @@ void* frontview_thread(void *arg) { return NULL; } + +#ifdef QCOM2 +// wide +void* wideview_thread(void *arg) { + int err; + VisionState *s = (VisionState*)arg; + + set_thread_name("wideview"); + + err = set_realtime_priority(51); + LOG("setpriority returns %d", err); + + // init cl stuff + const cl_queue_properties props[] = {0}; //CL_QUEUE_PRIORITY_KHR, CL_QUEUE_PRIORITY_HIGH_KHR, 0}; + cl_command_queue q = clCreateCommandQueueWithProperties(s->context, s->device_id, props, &err); + assert(err == 0); + + // init the net + LOG("wideview start!"); + + for (int cnt = 0; !do_exit; cnt++) { + int buf_idx = tbuffer_acquire(&s->cameras.wide.camera_tb); + // int buf_idx = camera_acquire_buffer(s); + if (buf_idx < 0) { + break; + } + + double t1 = millis_since_boot(); + + FrameMetadata frame_data = s->cameras.wide.camera_bufs_metadata[buf_idx]; + uint32_t frame_id = frame_data.frame_id; + + if (frame_id == -1) { + LOGE("no frame data? wtf"); + tbuffer_release(&s->cameras.wide.camera_tb, buf_idx); + continue; + } + + int ui_idx = tbuffer_select(&s->ui_wide_tb); + int rgb_idx = ui_idx; + + cl_event debayer_event; + if (s->cameras.wide.ci.bayer) { + err = clSetKernelArg(s->krnl_debayer_wide, 0, sizeof(cl_mem), &s->wide_camera_bufs_cl[buf_idx]); + assert(err == 0); + err = clSetKernelArg(s->krnl_debayer_wide, 1, sizeof(cl_mem), &s->rgb_wide_bufs_cl[rgb_idx]); + assert(err == 0); + err = clSetKernelArg(s->krnl_debayer_wide, 2, s->debayer_cl_localMemSize, 0); + assert(err == 0); + err = clEnqueueNDRangeKernel(q, s->krnl_debayer_wide, 2, NULL, + s->debayer_cl_globalWorkSize, s->debayer_cl_localWorkSize, 0, 0, &debayer_event); + assert(err == 0); + } else { + assert(s->rgb_wide_buf_size >= s->frame_size); + assert(s->rgb_stride == s->frame_stride); + err = clEnqueueCopyBuffer(q, s->wide_camera_bufs_cl[buf_idx], s->rgb_wide_bufs_cl[rgb_idx], + 0, 0, s->rgb_wide_buf_size, 0, 0, &debayer_event); + assert(err == 0); + } + + clWaitForEvents(1, &debayer_event); + clReleaseEvent(debayer_event); + + tbuffer_release(&s->cameras.wide.camera_tb, buf_idx); + + visionbuf_sync(&s->rgb_wide_bufs[rgb_idx], VISIONBUF_SYNC_FROM_DEVICE); + +#ifdef NOSCREEN + if (frame_data.frame_id % 4 == 0) { + sendrgb(&s->cameras, (uint8_t*) s->rgb_wide_bufs[rgb_idx].addr, s->rgb_wide_bufs[rgb_idx].len, 1); + } +#endif + + double t2 = millis_since_boot(); + + double yt1 = millis_since_boot(); + + int yuv_idx = pool_select(&s->yuv_wide_pool); + + s->yuv_wide_metas[yuv_idx] = frame_data; + + uint8_t* yuv_ptr_y = s->yuv_wide_bufs[yuv_idx].y; + cl_mem yuv_wide_cl = s->yuv_wide_cl[yuv_idx]; + rgb_to_yuv_queue(&s->wide_rgb_to_yuv_state, q, s->rgb_wide_bufs_cl[rgb_idx], yuv_wide_cl); + visionbuf_sync(&s->yuv_wide_ion[yuv_idx], VISIONBUF_SYNC_FROM_DEVICE); + + double yt2 = millis_since_boot(); + + // keep another reference around till were done processing + pool_acquire(&s->yuv_wide_pool, yuv_idx); + pool_push(&s->yuv_wide_pool, yuv_idx); + + // send frame event + { + if (s->pm != NULL) { + MessageBuilder msg; + auto framed = msg.initEvent().initWideFrame(); + framed.setFrameId(frame_data.frame_id); + framed.setEncodeId(cnt); + framed.setTimestampEof(frame_data.timestamp_eof); + framed.setFrameLength(frame_data.frame_length); + framed.setIntegLines(frame_data.integ_lines); + framed.setGlobalGain(frame_data.global_gain); + framed.setLensPos(frame_data.lens_pos); + framed.setLensSag(frame_data.lens_sag); + framed.setLensErr(frame_data.lens_err); + framed.setLensTruePos(frame_data.lens_true_pos); + framed.setGainFrac(frame_data.gain_frac); + + s->pm->send("wideFrame", msg); + } + } + + tbuffer_dispatch(&s->ui_wide_tb, ui_idx); + + // auto exposure over big box + // TODO: fix this? should not use med imo + const int exposure_x = 384; + const int exposure_y = 300; + const int exposure_height = 400; + const int exposure_width = 1152; + if (cnt % 3 == 0) { + // find median box luminance for AE + uint32_t lum_binning[256] = {0,}; + for (int y=0; yyuv_wide_width) + exposure_x + x]; + lum_binning[lum]++; + } + } + const unsigned int lum_total = exposure_height * exposure_width / 4; + unsigned int lum_cur = 0; + int lum_med = 0; + for (lum_med=0; lum_med<256; lum_med++) { + // shouldn't be any values less than 16 - yuv footroom + lum_cur += lum_binning[lum_med]; + if (lum_cur >= lum_total / 2) { + break; + } + } + + camera_autoexposure(&s->cameras.wide, lum_med / 256.0); + } + + pool_release(&s->yuv_wide_pool, yuv_idx); + double t5 = millis_since_boot(); + LOGD("queued: %.2fms, yuv: %.2f, | processing: %.3fms", (t2-t1), (yt2-yt1), (t5-t1)); + } + + return NULL; +} +#endif + // processing void* processing_thread(void *arg) { int err; @@ -390,13 +587,19 @@ void* processing_thread(void *arg) { assert(err == 0); err = clSetKernelArg(s->krnl_debayer_rear, 1, sizeof(cl_mem), &s->rgb_bufs_cl[rgb_idx]); assert(err == 0); +#ifdef QCOM2 + err = clSetKernelArg(s->krnl_debayer_rear, 2, s->debayer_cl_localMemSize, 0); + assert(err == 0); + err = clEnqueueNDRangeKernel(q, s->krnl_debayer_rear, 2, NULL, + s->debayer_cl_globalWorkSize, s->debayer_cl_localWorkSize, 0, 0, &debayer_event); +#else err = clSetKernelArg(s->krnl_debayer_rear, 2, sizeof(float), &s->cameras.rear.digital_gain); assert(err == 0); - const size_t debayer_work_size = s->rgb_height; // doesn't divide evenly, is this okay? const size_t debayer_local_work_size = 128; err = clEnqueueNDRangeKernel(q, s->krnl_debayer_rear, 1, NULL, &debayer_work_size, &debayer_local_work_size, 0, 0, &debayer_event); +#endif assert(err == 0); } else { assert(s->rgb_buf_size >= s->frame_size); @@ -405,7 +608,6 @@ void* processing_thread(void *arg) { 0, 0, s->rgb_buf_size, 0, 0, &debayer_event); assert(err == 0); } - clWaitForEvents(1, &debayer_event); clReleaseEvent(debayer_event); @@ -413,6 +615,12 @@ void* processing_thread(void *arg) { visionbuf_sync(&s->rgb_bufs[rgb_idx], VISIONBUF_SYNC_FROM_DEVICE); +#ifdef NOSCREEN + if (frame_data.frame_id % 4 == 1) { + sendrgb(&s->cameras, (uint8_t*) s->rgb_bufs[rgb_idx].addr, s->rgb_bufs[rgb_idx].len, 0); + } +#endif + #if defined(QCOM) && !defined(QCOM_REPLAY) /*FILE *dump_rgb_file = fopen("/tmp/process_dump.rgb", "wb"); fwrite(s->rgb_bufs[rgb_idx].addr, s->rgb_bufs[rgb_idx].len, sizeof(uint8_t), dump_rgb_file); @@ -485,7 +693,7 @@ void* processing_thread(void *arg) { // truly stuck, needs help s->cameras.rear.self_recover -= 1; if (s->cameras.rear.self_recover < -FOCUS_RECOVER_PATIENCE) { - LOGW("rear camera bad state detected. attempting recovery from %.1f, recover state is %d", + LOGD("rear camera bad state detected. attempting recovery from %.1f, recover state is %d", lens_true_pos, s->cameras.rear.self_recover.load()); s->cameras.rear.self_recover = FOCUS_RECOVER_STEPS + ((lens_true_pos < (s->cameras.device == DEVICE_LP3? LP3_AF_DAC_M:OP3T_AF_DAC_M))?1:0); // parity determined by which end is stuck at } @@ -495,7 +703,7 @@ void* processing_thread(void *arg) { // in suboptimal position with high prob, but may still recover by itself s->cameras.rear.self_recover -= 1; if (s->cameras.rear.self_recover < -(FOCUS_RECOVER_PATIENCE*3)) { - LOGW("rear camera bad state detected. attempting recovery from %.1f, recover state is %d", lens_true_pos, s->cameras.rear.self_recover.load()); + LOGD("rear camera bad state detected. attempting recovery from %.1f, recover state is %d", lens_true_pos, s->cameras.rear.self_recover.load()); s->cameras.rear.self_recover = FOCUS_RECOVER_STEPS/2 + ((lens_true_pos < (s->cameras.device == DEVICE_LP3? LP3_AF_DAC_M:OP3T_AF_DAC_M))?1:0); } } else if (s->cameras.rear.self_recover < 0) { @@ -530,11 +738,8 @@ void* processing_thread(void *arg) { // send frame event { if (s->pm != NULL) { - capnp::MallocMessageBuilder msg; - cereal::Event::Builder event = msg.initRoot(); - event.setLogMonoTime(nanos_since_boot()); - - auto framed = event.initFrame(); + MessageBuilder msg; + auto framed = msg.initEvent().initFrame(); framed.setFrameId(frame_data.frame_id); framed.setEncodeId(cnt); framed.setTimestampEof(frame_data.timestamp_eof); @@ -618,11 +823,8 @@ void* processing_thread(void *arg) { free(row); jpeg_finish_compress(&cinfo); - capnp::MallocMessageBuilder msg; - cereal::Event::Builder event = msg.initRoot(); - event.setLogMonoTime(nanos_since_boot()); - - auto thumbnaild = event.initThumbnail(); + MessageBuilder msg; + auto thumbnaild = msg.initEvent().initThumbnail(); thumbnaild.setFrameId(frame_data.frame_id); thumbnaild.setTimestampEof(frame_data.timestamp_eof); thumbnaild.setThumbnail(kj::arrayPtr((const uint8_t*)thumbnail_buffer, thumbnail_len)); @@ -638,20 +840,29 @@ void* processing_thread(void *arg) { tbuffer_dispatch(&s->ui_tb, ui_idx); // auto exposure over big box +#ifdef QCOM2 + const int exposure_x = 384; + const int exposure_y = 300; + const int exposure_height = 400; + const int exposure_width = 1152; + const int skip = 2; +#else const int exposure_x = 290; - const int exposure_y = 282 + 40; + const int exposure_y = 322; const int exposure_height = 314; const int exposure_width = 560; + const int skip = 1; +#endif if (cnt % 3 == 0) { // find median box luminance for AE uint32_t lum_binning[256] = {0,}; - for (int y=0; yyuv_width) + exposure_x + x]; lum_binning[lum]++; } } - const unsigned int lum_total = exposure_height * exposure_width; + const unsigned int lum_total = exposure_height * exposure_width / skip / skip; unsigned int lum_cur = 0; int lum_med = 0; for (lum_med=0; lum_med<256; lum_med++) { @@ -661,8 +872,6 @@ void* processing_thread(void *arg) { break; } } - // double avg = (double)acc / (big_box_width * big_box_height) - 16; - // printf("avg %d\n", lum_med); camera_autoexposure(&s->cameras.rear, lum_med / 256.0); } @@ -769,6 +978,20 @@ void* visionserver_client_thread(void* arg) { } else { assert(false); } + } else if (stream_type == VISION_STREAM_RGB_WIDE) { + stream_bufs->width = s->rgb_wide_width; + stream_bufs->height = s->rgb_wide_height; + stream_bufs->stride = s->rgb_wide_stride; + stream_bufs->buf_len = s->rgb_wide_bufs[0].len; + rep.num_fds = UI_BUF_COUNT; + for (int i=0; irgb_wide_bufs[i].fd; + } + if (stream->tb) { + stream->tbuffer = &s->ui_wide_tb; + } else { + assert(false); + } } else if (stream_type == VISION_STREAM_YUV) { stream_bufs->width = s->yuv_width; stream_bufs->height = s->yuv_height; @@ -797,6 +1020,21 @@ void* visionserver_client_thread(void* arg) { } else { stream->queue = pool_get_queue(&s->yuv_front_pool); } + } else if (stream_type == VISION_STREAM_YUV_WIDE) { + stream_bufs->width = s->yuv_wide_width; + stream_bufs->height = s->yuv_wide_height; + stream_bufs->stride = s->yuv_wide_width; + stream_bufs->buf_len = s->yuv_wide_buf_size; + rep.num_fds = YUV_COUNT; + for (int i=0; iyuv_wide_ion[i].fd; + } + if (stream->tb) { + stream->tbuffer = s->yuv_wide_tb; + } else { + stream->queue = pool_get_queue(&s->yuv_wide_pool); + } + } else { assert(false); } @@ -849,6 +1087,9 @@ void* visionserver_client_thread(void* arg) { } else if (stream_i == VISION_STREAM_YUV_FRONT) { rep.d.stream_acq.extra.frame_id = s->yuv_front_metas[idx].frame_id; rep.d.stream_acq.extra.timestamp_eof = s->yuv_front_metas[idx].timestamp_eof; + } else if (stream_i == VISION_STREAM_YUV_WIDE) { + rep.d.stream_acq.extra.frame_id = s->yuv_wide_metas[idx].frame_id; + rep.d.stream_acq.extra.timestamp_eof = s->yuv_wide_metas[idx].timestamp_eof; } vipc_send(fd, &rep); } @@ -959,25 +1200,28 @@ cl_program build_debayer_program(VisionState *s, int frame_width, int frame_height, int frame_stride, int rgb_width, int rgb_height, int rgb_stride, int bayer_flip, int hdr) { +#ifdef QCOM2 + assert(rgb_width == frame_width); + assert(rgb_height == frame_height); +#else assert(rgb_width == frame_width/2); assert(rgb_height == frame_height/2); - - #ifdef QCOM2 - int dnew = 1; - #else - int dnew = 0; - #endif +#endif char args[4096]; snprintf(args, sizeof(args), "-cl-fast-relaxed-math -cl-denorms-are-zero " "-DFRAME_WIDTH=%d -DFRAME_HEIGHT=%d -DFRAME_STRIDE=%d " "-DRGB_WIDTH=%d -DRGB_HEIGHT=%d -DRGB_STRIDE=%d " - "-DBAYER_FLIP=%d -DHDR=%d -DNEW=%d", + "-DBAYER_FLIP=%d -DHDR=%d", frame_width, frame_height, frame_stride, rgb_width, rgb_height, rgb_stride, - bayer_flip, hdr, dnew); + bayer_flip, hdr); +#ifdef QCOM2 + return CLU_LOAD_FROM_FILE(s->context, s->device_id, "cameras/real_debayer.cl", args); +#else return CLU_LOAD_FROM_FILE(s->context, s->device_id, "cameras/debayer.cl", args); +#endif } cl_program build_conv_program(VisionState *s, @@ -1010,19 +1254,7 @@ cl_program build_pool_program(VisionState *s, void cl_init(VisionState *s) { int err; - cl_platform_id platform_id = NULL; - cl_uint num_devices; - cl_uint num_platforms; - - err = clGetPlatformIDs(1, &platform_id, &num_platforms); - assert(err == 0); - err = clGetDeviceIDs(platform_id, CL_DEVICE_TYPE_DEFAULT, 1, - &s->device_id, &num_devices); - assert(err == 0); - - cl_print_info(platform_id, s->device_id); - printf("\n"); - + s->device_id = cl_get_device_id(CL_DEVICE_TYPE_DEFAULT); s->context = clCreateContext(NULL, 1, &s->device_id, NULL, NULL, &err); assert(err == 0); } @@ -1042,7 +1274,7 @@ void init_buffers(VisionState *s) { for (int i=0; icamera_bufs[i] = visionbuf_allocate_cl(s->frame_size, s->device_id, s->context, &s->camera_bufs_cl[i]); - #ifndef QCOM2 + #ifdef QCOM // TODO: make lengths correct s->focus_bufs[i] = visionbuf_allocate(0xb80); s->stats_bufs[i] = visionbuf_allocate(0xb80); @@ -1054,11 +1286,22 @@ void init_buffers(VisionState *s) { s->device_id, s->context, &s->front_camera_bufs_cl[i]); } - +#ifdef QCOM2 + for (int i=0; iwide_camera_bufs[i] = visionbuf_allocate_cl(s->cameras.wide.frame_size, + s->device_id, s->context, + &s->wide_camera_bufs_cl[i]); + } +#endif // processing buffers if (s->cameras.rear.ci.bayer) { - s->rgb_width = s->frame_width/2; - s->rgb_height = s->frame_height/2; +#ifdef QCOM2 + s->rgb_width = s->frame_width; + s->rgb_height = s->frame_height; +#else + s->rgb_width = s->frame_width / 2; + s->rgb_height = s->frame_height / 2; +#endif } else { s->rgb_width = s->frame_width; s->rgb_height = s->frame_height; @@ -1074,15 +1317,22 @@ void init_buffers(VisionState *s) { } tbuffer_init(&s->ui_tb, UI_BUF_COUNT, "rgb"); - //assert(s->cameras.front.ci.bayer); if (s->cameras.front.ci.bayer) { - s->rgb_front_width = s->cameras.front.ci.frame_width/2; - s->rgb_front_height = s->cameras.front.ci.frame_height/2; +#ifdef QCOM2 + s->rgb_front_width = s->cameras.front.ci.frame_width; + s->rgb_front_height = s->cameras.front.ci.frame_height; +#else + s->rgb_front_width = s->cameras.front.ci.frame_width / 2; + s->rgb_front_height = s->cameras.front.ci.frame_height / 2; +#endif } else { s->rgb_front_width = s->cameras.front.ci.frame_width; s->rgb_front_height = s->cameras.front.ci.frame_height; } - +#ifdef QCOM2 + s->rgb_wide_width = s->cameras.wide.ci.frame_width; + s->rgb_wide_height = s->cameras.wide.ci.frame_height; +#endif for (int i=0; irgb_front_width, s->rgb_front_height, &s->rgb_front_bufs[i]); @@ -1093,6 +1343,17 @@ void init_buffers(VisionState *s) { } } tbuffer_init(&s->ui_front_tb, UI_BUF_COUNT, "frontrgb"); +#ifdef QCOM2 + for (int i=0; irgb_wide_width, s->rgb_wide_height, &s->rgb_wide_bufs[i]); + s->rgb_wide_bufs_cl[i] = visionbuf_to_cl(&s->rgb_wide_bufs[i], s->device_id, s->context); + if (i == 0){ + s->rgb_wide_stride = img.stride; + s->rgb_wide_buf_size = img.size; + } + } + tbuffer_init(&s->ui_wide_tb, UI_BUF_COUNT, "widergb"); +#endif // yuv back for recording and orbd pool_init(&s->yuv_pool, YUV_COUNT); @@ -1124,6 +1385,23 @@ void init_buffers(VisionState *s) { s->yuv_front_bufs[i].v = s->yuv_front_bufs[i].u + (s->yuv_front_width/2 * s->yuv_front_height/2); } + // yuv wide for recording +#ifdef QCOM2 + pool_init(&s->yuv_wide_pool, YUV_COUNT); + s->yuv_wide_tb = pool_get_tbuffer(&s->yuv_wide_pool); + + s->yuv_wide_width = s->rgb_wide_width; + s->yuv_wide_height = s->rgb_wide_height; + s->yuv_wide_buf_size = s->rgb_wide_width * s->rgb_wide_height * 3 / 2; + + for (int i=0; iyuv_wide_ion[i] = visionbuf_allocate_cl(s->yuv_wide_buf_size, s->device_id, s->context, &s->yuv_wide_cl[i]); + s->yuv_wide_bufs[i].y = (uint8_t*)s->yuv_wide_ion[i].addr; + s->yuv_wide_bufs[i].u = s->yuv_wide_bufs[i].y + (s->yuv_wide_width * s->yuv_wide_height); + s->yuv_wide_bufs[i].v = s->yuv_wide_bufs[i].u + (s->yuv_wide_width/2 * s->yuv_wide_height/2); + } +#endif + if (s->cameras.rear.ci.bayer) { // debayering does a 2x downscale s->yuv_transform = transform_scale_buffer(s->cameras.rear.transform, 0.5); @@ -1149,7 +1427,24 @@ void init_buffers(VisionState *s) { s->krnl_debayer_front = clCreateKernel(s->prg_debayer_front, "debayer10", &err); assert(err == 0); } +#ifdef QCOM2 + if (s->cameras.wide.ci.bayer) { + s->prg_debayer_wide = build_debayer_program(s, s->cameras.wide.ci.frame_width, s->cameras.wide.ci.frame_height, + s->cameras.wide.ci.frame_stride, + s->rgb_wide_width, s->rgb_wide_height, s->rgb_wide_stride, + s->cameras.wide.ci.bayer_flip, s->cameras.wide.ci.hdr); + s->krnl_debayer_wide = clCreateKernel(s->prg_debayer_wide, "debayer10", &err); + assert(err == 0); + } +#endif + s->debayer_cl_localMemSize = (DEBAYER_LOCAL_WORKSIZE + 2 * (3 / 2)) * (DEBAYER_LOCAL_WORKSIZE + 2 * (3 / 2)) * sizeof(float); + s->debayer_cl_globalWorkSize[0] = s->rgb_width; + s->debayer_cl_globalWorkSize[1] = s->rgb_height; + s->debayer_cl_localWorkSize[0] = DEBAYER_LOCAL_WORKSIZE; + s->debayer_cl_localWorkSize[1] = DEBAYER_LOCAL_WORKSIZE; + +#ifdef QCOM s->prg_rgb_laplacian = build_conv_program(s, s->rgb_width/NUM_SEGMENTS_X, s->rgb_height/NUM_SEGMENTS_Y, 3); s->krnl_rgb_laplacian = clCreateKernel(s->prg_rgb_laplacian, "rgb2gray_conv2d", &err); @@ -1169,9 +1464,13 @@ void init_buffers(VisionState *s) { s->conv_cl_localWorkSize[1] = CONV_LOCAL_WORKSIZE; for (int i=0; i<(ROI_X_MAX-ROI_X_MIN+1)*(ROI_Y_MAX-ROI_Y_MIN+1); i++) {s->lapres[i] = 16160;} +#endif rgb_to_yuv_init(&s->rgb_to_yuv_state, s->context, s->device_id, s->yuv_width, s->yuv_height, s->rgb_stride); rgb_to_yuv_init(&s->front_rgb_to_yuv_state, s->context, s->device_id, s->yuv_front_width, s->yuv_front_height, s->rgb_front_stride); +#ifdef QCOM2 + rgb_to_yuv_init(&s->wide_rgb_to_yuv_state, s->context, s->device_id, s->yuv_wide_width, s->yuv_wide_height, s->rgb_wide_stride); +#endif } void free_buffers(VisionState *s) { @@ -1179,18 +1478,28 @@ void free_buffers(VisionState *s) { for (int i=0; icamera_bufs[i]); visionbuf_free(&s->front_camera_bufs[i]); +#ifdef QCOM visionbuf_free(&s->focus_bufs[i]); visionbuf_free(&s->stats_bufs[i]); +#elif defined(QCOM2) + visionbuf_free(&s->wide_camera_bufs[i]); +#endif } for (int i=0; irgb_bufs[i]); visionbuf_free(&s->rgb_front_bufs[i]); +#ifdef QCOM2 + visionbuf_free(&s->rgb_wide_bufs[i]); +#endif } for (int i=0; iyuv_ion[i]); visionbuf_free(&s->yuv_front_ion[i]); +#ifdef QCOM2 + visionbuf_free(&s->yuv_wide_ion[i]); +#endif } clReleaseMemObject(s->rgb_conv_roi_cl); @@ -1201,7 +1510,11 @@ void free_buffers(VisionState *s) { clReleaseProgram(s->prg_debayer_front); clReleaseKernel(s->krnl_debayer_rear); clReleaseKernel(s->krnl_debayer_front); - +#ifdef QCOM2 + clReleaseProgram(s->prg_debayer_wide); + clReleaseKernel(s->krnl_debayer_wide); +#endif + clReleaseProgram(s->prg_rgb_laplacian); clReleaseKernel(s->krnl_rgb_laplacian); @@ -1223,13 +1536,18 @@ void party(VisionState *s) { processing_thread, s); assert(err == 0); -#if !defined(QCOM2) && !defined(__APPLE__) - // TODO: fix front camera on qcom2 +#ifndef __APPLE__ pthread_t frontview_thread_handle; err = pthread_create(&frontview_thread_handle, NULL, frontview_thread, s); assert(err == 0); #endif +#ifdef QCOM2 + pthread_t wideview_thread_handle; + err = pthread_create(&wideview_thread_handle, NULL, + wideview_thread, s); + assert(err == 0); +#endif // priority for cameras err = set_realtime_priority(51); @@ -1241,15 +1559,23 @@ void party(VisionState *s) { tbuffer_stop(&s->ui_front_tb); pool_stop(&s->yuv_pool); pool_stop(&s->yuv_front_pool); +#ifdef QCOM2 + tbuffer_stop(&s->ui_wide_tb); + pool_stop(&s->yuv_wide_pool); +#endif zsock_signal(s->terminate_pub, 0); -#if !defined(QCOM2) && !defined(QCOM_REPLAY) && !defined(__APPLE__) +#if (defined(QCOM) && !defined(QCOM_REPLAY)) || defined(WEBCAM) || defined(QCOM2) LOG("joining frontview_thread"); err = pthread_join(frontview_thread_handle, NULL); assert(err == 0); #endif - +#ifdef QCOM2 + LOG("joining wideview_thread"); + err = pthread_join(wideview_thread_handle, NULL); + assert(err == 0); +#endif LOG("joining visionserver_thread"); err = pthread_join(visionserver_thread_handle, NULL); assert(err == 0); @@ -1263,6 +1589,9 @@ void party(VisionState *s) { int main(int argc, char *argv[]) { set_realtime_priority(51); +#ifdef QCOM + set_core_affinity(2); +#endif zsys_handler_set(NULL); signal(SIGINT, (sighandler_t)set_do_exit); @@ -1283,11 +1612,17 @@ int main(int argc, char *argv[]) { init_buffers(s); -#if (defined(QCOM) && !defined(QCOM_REPLAY)) || defined(QCOM2) +#if defined(QCOM) && !defined(QCOM_REPLAY) s->pm = new PubMaster({"frame", "frontFrame", "thumbnail"}); +#elif defined(QCOM2) + s->pm = new PubMaster({"frame", "frontFrame", "wideFrame", "thumbnail"}); #endif +#ifndef QCOM2 cameras_open(&s->cameras, &s->camera_bufs[0], &s->focus_bufs[0], &s->stats_bufs[0], &s->front_camera_bufs[0]); +#else + cameras_open(&s->cameras, &s->camera_bufs[0], &s->focus_bufs[0], &s->stats_bufs[0], &s->front_camera_bufs[0], &s->wide_camera_bufs[0]); +#endif party(s); diff --git a/selfdrive/camerad/transforms/rgb_to_yuv_test.cc b/selfdrive/camerad/transforms/rgb_to_yuv_test.cc index c8b875105..9d68e5b9e 100644 --- a/selfdrive/camerad/transforms/rgb_to_yuv_test.cc +++ b/selfdrive/camerad/transforms/rgb_to_yuv_test.cc @@ -42,14 +42,7 @@ static inline double millis_since_boot() { void cl_init(cl_device_id &device_id, cl_context &context) { int err; - cl_platform_id platform_id = NULL; - cl_uint num_devices; - cl_uint num_platforms; - - err = clGetPlatformIDs(1, &platform_id, &num_platforms); - err = clGetDeviceIDs(platform_id, CL_DEVICE_TYPE_DEFAULT, 1, - &device_id, &num_devices); - cl_print_info(platform_id, device_id); + device_id = cl_get_device_id(CL_DEVICE_TYPE_DEFAULT); context = clCreateContext(NULL, 1, &device_id, NULL, NULL, &err); } diff --git a/selfdrive/car/honda/carcontroller.py b/selfdrive/car/honda/carcontroller.py index 0438dd76a..60f3a94e4 100644 --- a/selfdrive/car/honda/carcontroller.py +++ b/selfdrive/car/honda/carcontroller.py @@ -5,7 +5,7 @@ from selfdrive.controls.lib.drive_helpers import rate_limit from common.numpy_fast import clip, interp from selfdrive.car import create_gas_command from selfdrive.car.honda import hondacan -from selfdrive.car.honda.values import CruiseButtons, CAR, VISUAL_HUD +from selfdrive.car.honda.values import CruiseButtons, CAR, VISUAL_HUD, HONDA_BOSCH from opendbc.can.packer import CANPacker VisualAlert = car.CarControl.HUDControl.VisualAlert @@ -135,8 +135,6 @@ class CarController(): # **** process the car messages **** # steer torque is converted back to CAN reference (positive when steering right) - apply_gas = clip(actuators.gas, 0., 1.) - apply_brake = int(clip(self.brake_last * P.BRAKE_MAX, 0, P.BRAKE_MAX - 1)) apply_steer = int(interp(-actuators.steer * P.STEER_MAX, P.STEER_LOOKUP_BP, P.STEER_LOOKUP_V)) lkas_active = enabled and not CS.steer_not_allowed @@ -147,14 +145,14 @@ class CarController(): # Send steering command. idx = frame % 4 can_sends.append(hondacan.create_steering_control(self.packer, apply_steer, - lkas_active, CS.CP.carFingerprint, idx, CS.CP.isPandaBlack)) + lkas_active, CS.CP.carFingerprint, idx, CS.CP.isPandaBlack, CS.CP.openpilotLongitudinalControl)) # Send dashboard UI commands. if (frame % 10) == 0: idx = (frame//10) % 4 - can_sends.extend(hondacan.create_ui_commands(self.packer, pcm_speed, hud, CS.CP.carFingerprint, CS.is_metric, idx, CS.CP.isPandaBlack, CS.stock_hud)) + can_sends.extend(hondacan.create_ui_commands(self.packer, pcm_speed, hud, CS.CP.carFingerprint, CS.is_metric, idx, CS.CP.isPandaBlack, CS.CP.openpilotLongitudinalControl, CS.stock_hud)) - if CS.CP.radarOffCan: + if not CS.CP.openpilotLongitudinalControl: if (frame % 2) == 0: idx = frame // 2 can_sends.append(hondacan.create_bosch_supplemental_1(self.packer, CS.CP.carFingerprint, idx, CS.CP.isPandaBlack)) @@ -169,14 +167,19 @@ class CarController(): if (frame % 2) == 0: idx = frame // 2 ts = frame * DT_CTRL - pump_on, self.last_pump_ts = brake_pump_hysteresis(apply_brake, self.apply_brake_last, self.last_pump_ts, ts) - can_sends.append(hondacan.create_brake_command(self.packer, apply_brake, pump_on, - pcm_override, pcm_cancel_cmd, hud.fcw, idx, CS.CP.carFingerprint, CS.CP.isPandaBlack, CS.stock_brake)) - self.apply_brake_last = apply_brake + if CS.CP.carFingerprint in HONDA_BOSCH: + pass # TODO: implement + else: + apply_gas = clip(actuators.gas, 0., 1.) + apply_brake = int(clip(self.brake_last * P.BRAKE_MAX, 0, P.BRAKE_MAX - 1)) + pump_on, self.last_pump_ts = brake_pump_hysteresis(apply_brake, self.apply_brake_last, self.last_pump_ts, ts) + can_sends.append(hondacan.create_brake_command(self.packer, apply_brake, pump_on, + pcm_override, pcm_cancel_cmd, hud.fcw, idx, CS.CP.carFingerprint, CS.CP.isPandaBlack, CS.stock_brake)) + self.apply_brake_last = apply_brake - if CS.CP.enableGasInterceptor: - # send exactly zero if apply_gas is zero. Interceptor will send the max between read value and apply_gas. - # This prevents unexpected pedal range rescaling - can_sends.append(create_gas_command(self.packer, apply_gas, idx)) + if CS.CP.enableGasInterceptor: + # send exactly zero if apply_gas is zero. Interceptor will send the max between read value and apply_gas. + # This prevents unexpected pedal range rescaling + can_sends.append(create_gas_command(self.packer, apply_gas, idx)) return can_sends diff --git a/selfdrive/car/honda/carstate.py b/selfdrive/car/honda/carstate.py index 4aaa98aa6..77f74681b 100644 --- a/selfdrive/car/honda/carstate.py +++ b/selfdrive/car/honda/carstate.py @@ -79,7 +79,7 @@ def get_can_signals(CP): ("GEARBOX", 100), ] - if CP.radarOffCan: + if CP.carFingerprint in HONDA_BOSCH: # Civic is only bosch to use the same brake message as other hondas. if CP.carFingerprint not in (CAR.ACCORDH, CAR.CIVIC_BOSCH, CAR.CIVIC_BOSCH_DIESEL, CAR.CRV_HYBRID, CAR.INSIGHT): signals += [("BRAKE_PRESSED", "BRAKE_MODULE", 0)] @@ -90,6 +90,10 @@ def get_can_signals(CP): ("EPB_STATE", "EPB_STATUS", 0), ("CRUISE_SPEED", "ACC_HUD", 0)] checks += [("GAS_PEDAL_2", 100)] + if CP.openpilotLongitudinalControl: + signals += [("BRAKE_ERROR_1", "STANDSTILL", 1), + ("BRAKE_ERROR_2", "STANDSTILL", 1)] + checks += [("STANDSTILL", 50)] else: # Nidec signals. signals += [("BRAKE_ERROR_1", "STANDSTILL", 1), @@ -206,7 +210,7 @@ class CarState(CarStateBase): # LOW_SPEED_LOCKOUT is not worth a warning ret.steerWarning = steer_status not in ['NORMAL', 'LOW_SPEED_LOCKOUT', 'NO_TORQUE_ALERT_2'] - if self.CP.radarOffCan: + if not self.CP.openpilotLongitudinalControl: self.brake_error = 0 else: self.brake_error = cp.vl["STANDSTILL"]['BRAKE_ERROR_1'] or cp.vl["STANDSTILL"]['BRAKE_ERROR_2'] @@ -270,7 +274,7 @@ class CarState(CarStateBase): self.brake_switch = cp.vl["POWERTRAIN_DATA"]['BRAKE_SWITCH'] != 0 - if self.CP.radarOffCan: + if self.CP.carFingerprint in HONDA_BOSCH: self.cruise_mode = cp.vl["ACC_HUD"]['CRUISE_CONTROL_LABEL'] ret.cruiseState.standstill = cp.vl["ACC_HUD"]['CRUISE_SPEED'] == 252. ret.cruiseState.speedOffset = calc_cruise_offset(0, ret.vEgo) diff --git a/selfdrive/car/honda/hondacan.py b/selfdrive/car/honda/hondacan.py index 178a452ec..45aea3fc1 100644 --- a/selfdrive/car/honda/hondacan.py +++ b/selfdrive/car/honda/hondacan.py @@ -1,12 +1,26 @@ from selfdrive.config import Conversions as CV from selfdrive.car.honda.values import HONDA_BOSCH +# CAN bus layout with relay +# 0 = ACC-CAN - radar side +# 1 = F-CAN B - powertrain +# 2 = ACC-CAN - camera side +# 3 = F-CAN A - OBDII port + +# CAN bus layout with giraffe +# 0 = F-CAN B - powertrain +# 1 = ACC-CAN - camera side +# 2 = ACC-CAN - radar side def get_pt_bus(car_fingerprint, has_relay): return 1 if car_fingerprint in HONDA_BOSCH and has_relay else 0 -def get_lkas_cmd_bus(car_fingerprint, has_relay): +def get_lkas_cmd_bus(car_fingerprint, has_relay, radar_disabled=False): + if radar_disabled: + # when radar is disabled, steering commands are sent directly to powertrain bus + return get_pt_bus(car_fingerprint, has_relay) + # normally steering commands are sent to radar, which forwards them to powertrain bus return 2 if car_fingerprint in HONDA_BOSCH and not has_relay else 0 @@ -35,12 +49,47 @@ def create_brake_command(packer, apply_brake, pump_on, pcm_override, pcm_cancel_ return packer.make_can_msg("BRAKE_COMMAND", bus, values, idx) -def create_steering_control(packer, apply_steer, lkas_active, car_fingerprint, idx, has_relay): +def create_acc_commands(packer, enabled, accel, gas, idx, stopping, starting, car_fingerprint, has_relay): + commands = [] + bus = get_pt_bus(car_fingerprint, has_relay) + + control_on = 5 if enabled else 0 + # no gas = -30000 + gas_command = gas if enabled and gas > 0 else -30000 + accel_command = accel if enabled else 0 + braking = 1 if enabled and accel < 0 else 0 + standstill = 1 if enabled and stopping else 0 + standstill_release = 1 if enabled and starting else 0 + + acc_control_values = { + # setting CONTROL_ON causes car to set POWERTRAIN_DATA->ACC_STATUS = 1 + "CONTROL_ON": control_on, + "GAS_COMMAND": gas_command, # used for gas + "ACCEL_COMMAND": accel_command, # used for brakes + "BRAKE_LIGHTS": braking, + "BRAKE_REQUEST": braking, + "STANDSTILL": standstill, + "STANDSTILL_RELEASE": standstill_release, + } + commands.append(packer.make_can_msg("ACC_CONTROL", bus, acc_control_values, idx)) + + acc_control_on_values = { + "SET_TO_3": 0x03, + "CONTROL_ON": enabled, + "SET_TO_FF": 0xff, + "SET_TO_75": 0x75, + "SET_TO_30": 0x30, + } + commands.append(packer.make_can_msg("ACC_CONTROL_ON", bus, acc_control_on_values, idx)) + + return commands + +def create_steering_control(packer, apply_steer, lkas_active, car_fingerprint, idx, has_relay, radar_disabled): values = { "STEER_TORQUE": apply_steer if lkas_active else 0, "STEER_TORQUE_REQUEST": lkas_active, } - bus = get_lkas_cmd_bus(car_fingerprint, has_relay) + bus = get_lkas_cmd_bus(car_fingerprint, has_relay, radar_disabled) return packer.make_can_msg("STEERING_CONTROL", bus, values, idx) @@ -55,27 +104,40 @@ def create_bosch_supplemental_1(packer, car_fingerprint, idx, has_relay): return packer.make_can_msg("BOSCH_SUPPLEMENTAL_1", bus, values, idx) -def create_ui_commands(packer, pcm_speed, hud, car_fingerprint, is_metric, idx, has_relay, stock_hud): +def create_ui_commands(packer, pcm_speed, hud, car_fingerprint, is_metric, idx, has_relay, openpilot_longitudinal_control, stock_hud): commands = [] bus_pt = get_pt_bus(car_fingerprint, has_relay) - bus_lkas = get_lkas_cmd_bus(car_fingerprint, has_relay) + radar_disabled = car_fingerprint in HONDA_BOSCH and openpilot_longitudinal_control + bus_lkas = get_lkas_cmd_bus(car_fingerprint, has_relay, radar_disabled) - if car_fingerprint not in HONDA_BOSCH: - 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"], - } + if openpilot_longitudinal_control: + if car_fingerprint 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), + } + 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"], + } commands.append(packer.make_can_msg("ACC_HUD", bus_pt, acc_hud_values, idx)) lkas_hud_values = { @@ -87,6 +149,12 @@ def create_ui_commands(packer, pcm_speed, hud, car_fingerprint, is_metric, idx, } commands.append(packer.make_can_msg('LKAS_HUD', bus_lkas, lkas_hud_values, idx)) + if radar_disabled and car_fingerprint in HONDA_BOSCH: + radar_hud_values = { + 'SET_TO_1' : 0x01, + } + commands.append(packer.make_can_msg('RADAR_HUD', bus_pt, radar_hud_values, idx)) + return commands diff --git a/selfdrive/car/honda/interface.py b/selfdrive/car/honda/interface.py index ac8bfe3b9..d74db1b1f 100755 --- a/selfdrive/car/honda/interface.py +++ b/selfdrive/car/honda/interface.py @@ -478,7 +478,7 @@ class CarInterface(CarInterfaceBase): events = self.create_common_events(ret, pcm_enable=False) if self.CS.brake_error: events.add(EventName.brakeUnavailable) - if self.CS.brake_hold and self.CS.CP.carFingerprint not in HONDA_BOSCH: + if self.CS.brake_hold and self.CS.CP.openpilotLongitudinalControl: events.add(EventName.brakeHold) if self.CS.park_brake: events.add(EventName.parkBrake) diff --git a/selfdrive/car/honda/values.py b/selfdrive/car/honda/values.py index 713c38ae1..6a92ced81 100644 --- a/selfdrive/car/honda/values.py +++ b/selfdrive/car/honda/values.py @@ -645,10 +645,12 @@ FW_VERSIONS = { (Ecu.vsa, 0x18da28f1, None): [ b'57114-TPA-G020\x00\x00', b'57114-TPG-A020\x00\x00', + b'57114-TMB-H030\x00\x00', ], (Ecu.eps, 0x18da30f1, None): [ b'39990-TPA-G030\x00\x00', b'39990-TPG-A020\x00\x00', + b'39990-TMA-H020\x00\x00', ], (Ecu.gateway, 0x18daeff1, None): [ b'38897-TMA-H110\x00\x00', @@ -661,10 +663,12 @@ FW_VERSIONS = { (Ecu.fwdCamera, 0x18dab5f1, None): [ b'36161-TPA-E050\x00\x00', b'36161-TPG-A030\x00\x00', + b'36161-TMB-H040\x00\x00', ], (Ecu.combinationMeter, 0x18da60f1, None): [ b'78109-TPA-G520\x00\x00', b'78109-TPG-A110\x00\x00', + b'78109-TMB-H220\x00\x00', ], (Ecu.hud, 0x18da61f1, None): [ b'78209-TLA-X010\x00\x00', @@ -672,10 +676,12 @@ FW_VERSIONS = { (Ecu.fwdRadar, 0x18dab0f1, None): [ b'36802-TPA-E040\x00\x00', b'36802-TPG-A020\x00\x00', + b'36802-TMB-H040\x00\x00', ], (Ecu.srs, 0x18da53f1, None): [ b'77959-TLA-G220\x00\x00', b'77959-TLA-C320\x00\x00', + b'77959-TLA-H240\x00\x00', ], }, CAR.FIT: { @@ -754,6 +760,7 @@ FW_VERSIONS = { b'78109-THR-AB20\x00\x00', b'78109-THR-AB30\x00\x00', b'78109-THR-AB40\x00\x00', + b'78109-THR-AC20\x00\x00', b'78109-THR-AC40\x00\x00', b'78109-THR-AE20\x00\x00', b'78109-THR-AE40\x00\x00', @@ -858,6 +865,7 @@ FW_VERSIONS = { b'36161-T6Z-A020\x00\x00', b'36161-T6Z-A310\x00\x00', b'36161-T6Z-A520\x00\x00', + b'36161-TJZ-A120\x00\x00', ], (Ecu.gateway, 0x18daeff1, None): [ b'38897-T6Z-A010\x00\x00', @@ -866,6 +874,7 @@ FW_VERSIONS = { (Ecu.combinationMeter, 0x18da60f1, None): [ b'78109-T6Z-A420\x00\x00', b'78109-T6Z-A510\x00\x00', + b'78109-TJZ-A510\x00\x00', ], (Ecu.srs, 0x18da53f1, None): [ b'77959-T6Z-A020\x00\x00', @@ -874,6 +883,7 @@ FW_VERSIONS = { b'57114-T6Z-A120\x00\x00', b'57114-T6Z-A130\x00\x00', b'57114-T6Z-A520\x00\x00', + b'57114-TJZ-A520\x00\x00', ], }, CAR.INSIGHT: { @@ -891,6 +901,7 @@ FW_VERSIONS = { b'77959-TXM-A230\x00\x00', ], (Ecu.vsa, 0x18da28f1, None): [ + b'57114-TXM-A030\x00\x00', b'57114-TXM-A040\x00\x00', ], (Ecu.shiftByWire, 0x18da0bf1, None): [ @@ -900,7 +911,9 @@ FW_VERSIONS = { b'38897-TXM-A020\x00\x00', ], (Ecu.combinationMeter, 0x18da60f1, None): [ + b'78109-TXM-A010\x00\x00', b'78109-TXM-A020\x00\x00', + b'78109-TXM-A110\x00\x00', ], }, CAR.HRV: { diff --git a/selfdrive/car/hyundai/carcontroller.py b/selfdrive/car/hyundai/carcontroller.py index 787cc91b0..c47545ef2 100644 --- a/selfdrive/car/hyundai/carcontroller.py +++ b/selfdrive/car/hyundai/carcontroller.py @@ -43,11 +43,13 @@ class CarController(): self.steer_rate_limited = False self.last_resume_frame = 0 + self.p = SteerLimitParams(CP) + def update(self, enabled, CS, frame, actuators, pcm_cancel_cmd, visual_alert, left_lane, right_lane, left_lane_depart, right_lane_depart): # Steering Torque - new_steer = actuators.steer * SteerLimitParams.STEER_MAX - apply_steer = apply_std_steer_torque_limits(new_steer, self.apply_steer_last, CS.out.steeringTorque, SteerLimitParams) + new_steer = actuators.steer * self.p.STEER_MAX + 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 # disable if steer angle reach 90 deg, otherwise mdps fault in some models @@ -75,9 +77,8 @@ class CarController(): if pcm_cancel_cmd: can_sends.append(create_clu11(self.packer, frame, CS.clu11, Buttons.CANCEL)) elif CS.out.cruiseState.standstill: - # SCC won't resume anyway when the lead distace is less than 3.7m # send resume at a max freq of 5Hz - if CS.lead_distance > 3.7 and (frame - self.last_resume_frame)*DT_CTRL > 0.2: + if (frame - self.last_resume_frame)*DT_CTRL > 0.2: can_sends.append(create_clu11(self.packer, frame, CS.clu11, Buttons.RES_ACCEL)) self.last_resume_frame = frame diff --git a/selfdrive/car/hyundai/carstate.py b/selfdrive/car/hyundai/carstate.py index 61d9de05b..fd53ffc8a 100644 --- a/selfdrive/car/hyundai/carstate.py +++ b/selfdrive/car/hyundai/carstate.py @@ -1,3 +1,4 @@ +import copy from cereal import car from selfdrive.car.hyundai.values import DBC, STEER_THRESHOLD, FEATURES, EV_HYBRID from selfdrive.car.interfaces import CarStateBase @@ -124,8 +125,8 @@ class CarState(CarStateBase): ret.rightBlindspot = cp.vl["LCA11"]["CF_Lca_IndRight"] != 0 # save the entire LKAS11 and CLU11 - self.lkas11 = cp_cam.vl["LKAS11"] - self.clu11 = cp.vl["CLU11"] + self.lkas11 = copy.copy(cp_cam.vl["LKAS11"]) + self.clu11 = copy.copy(cp.vl["CLU11"]) self.park_brake = cp.vl["CGW1"]['CF_Gway_ParkBrakeSw'] self.steer_state = cp.vl["MDPS12"]['CF_Mdps_ToiActive'] # 0 NOT ACTIVE, 1 ACTIVE self.lead_distance = cp.vl["SCC11"]['ACC_ObjDist'] diff --git a/selfdrive/car/hyundai/interface.py b/selfdrive/car/hyundai/interface.py index 553d48a2b..76569d091 100644 --- a/selfdrive/car/hyundai/interface.py +++ b/selfdrive/car/hyundai/interface.py @@ -20,7 +20,7 @@ class CarInterface(CarInterfaceBase): ret.radarOffCan = True # Most Hyundai car ports are community features for now - ret.communityFeature = candidate not in [CAR.SONATA] + ret.communityFeature = candidate not in [CAR.SONATA, CAR.PALISADE] ret.steerActuatorDelay = 0.1 # Default delay ret.steerRateCost = 0.5 diff --git a/selfdrive/car/hyundai/values.py b/selfdrive/car/hyundai/values.py index 9a39cb8f3..c36ecbd96 100644 --- a/selfdrive/car/hyundai/values.py +++ b/selfdrive/car/hyundai/values.py @@ -6,12 +6,16 @@ Ecu = car.CarParams.Ecu # Steer torque limits class SteerLimitParams: - STEER_MAX = 255 # 409 is the max, 255 is stock - STEER_DELTA_UP = 3 - STEER_DELTA_DOWN = 7 - STEER_DRIVER_ALLOWANCE = 50 - STEER_DRIVER_MULTIPLIER = 2 - STEER_DRIVER_FACTOR = 1 + def __init__(self, CP): + if CP.carFingerprint in [CAR.SONATA, CAR.PALISADE]: + self.STEER_MAX = 384 + else: + self.STEER_MAX = 255 + 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 class CAR: diff --git a/selfdrive/car/mazda/carstate.py b/selfdrive/car/mazda/carstate.py index 03f3a026b..bc6c6d0eb 100644 --- a/selfdrive/car/mazda/carstate.py +++ b/selfdrive/car/mazda/carstate.py @@ -3,7 +3,7 @@ from selfdrive.config import Conversions as CV from opendbc.can.can_define import CANDefine from opendbc.can.parser import CANParser from selfdrive.car.interfaces import CarStateBase -from selfdrive.car.mazda.values import DBC, LKAS_LIMITS, CAR +from selfdrive.car.mazda.values import DBC, LKAS_LIMITS, GEN1 class CarState(CarStateBase): def __init__(self, CP): @@ -121,7 +121,7 @@ class CarState(CarStateBase): ("WHEEL_SPEEDS", 100), ] - if CP.carFingerprint == CAR.CX5: + if CP.carFingerprint in GEN1: signals += [ ("LKAS_BLOCK", "STEER_RATE", 0), ("LKAS_TRACK_STATE", "STEER_RATE", 0), @@ -165,7 +165,7 @@ class CarState(CarStateBase): signals = [] checks = [] - if CP.carFingerprint == CAR.CX5: + if CP.carFingerprint in GEN1: signals += [ # sig_name, sig_address, default ("LKAS_REQUEST", "CAM_LKAS", 0), diff --git a/selfdrive/car/mazda/interface.py b/selfdrive/car/mazda/interface.py index dfa959da3..81dff12b9 100755 --- a/selfdrive/car/mazda/interface.py +++ b/selfdrive/car/mazda/interface.py @@ -25,26 +25,36 @@ class CarInterface(CarInterfaceBase): ret.radarOffCan = True - # Mazda port is a community feature for now - ret.communityFeature = True - ret.steerActuatorDelay = 0.1 ret.steerRateCost = 1.0 ret.steerLimitTimer = 0.8 tire_stiffness_factor = 0.70 # not optimized yet - if candidate in [CAR.CX5]: + if candidate == CAR.CX5: ret.mass = 3655 * CV.LB_TO_KG + STD_CARGO_KG ret.wheelbase = 2.7 ret.steerRatio = 15.5 - ret.lateralTuning.pid.kiBP, ret.lateralTuning.pid.kpBP = [[0.], [0.]] - ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.2], [0.2]] - + ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.19], [0.019]] + ret.lateralTuning.pid.kf = 0.00006 + elif candidate == CAR.CX9: + ret.mass = 4217 * CV.LB_TO_KG + STD_CARGO_KG + ret.wheelbase = 3.1 + ret.steerRatio = 17.6 + ret.lateralTuning.pid.kiBP, ret.lateralTuning.pid.kpBP = [[0.], [0.]] + ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.19], [0.019]] + ret.lateralTuning.pid.kf = 0.00006 + elif candidate == CAR.Mazda3: + ret.mass = 2875 * CV.LB_TO_KG + STD_CARGO_KG + ret.wheelbase = 2.7 + ret.steerRatio = 14.0 + ret.lateralTuning.pid.kiBP, ret.lateralTuning.pid.kpBP = [[0.], [0.]] + ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.19], [0.019]] ret.lateralTuning.pid.kf = 0.00006 - # No steer below disable speed - ret.minSteerSpeed = LKAS_LIMITS.DISABLE_SPEED * CV.KPH_TO_MS + + # No steer below disable speed + ret.minSteerSpeed = LKAS_LIMITS.DISABLE_SPEED * CV.KPH_TO_MS ret.centerToFront = ret.wheelbase * 0.41 diff --git a/selfdrive/car/mazda/mazdacan.py b/selfdrive/car/mazda/mazdacan.py index 55b9218db..dabbd2129 100644 --- a/selfdrive/car/mazda/mazdacan.py +++ b/selfdrive/car/mazda/mazdacan.py @@ -1,4 +1,4 @@ -from selfdrive.car.mazda.values import CAR, Buttons +from selfdrive.car.mazda.values import GEN1, Buttons def create_steering_control(packer, car_fingerprint, frame, apply_steer, lkas): @@ -40,7 +40,7 @@ def create_steering_control(packer, car_fingerprint, frame, apply_steer, lkas): csum = csum % 256 - if car_fingerprint == CAR.CX5: + if car_fingerprint in GEN1: values = { "LKAS_REQUEST" : apply_steer, "CTR" : ctr, @@ -69,7 +69,7 @@ def create_button_cmd(packer, car_fingerprint, button): can = 0 res = 0 - if car_fingerprint == CAR.CX5: + if car_fingerprint in GEN1: values = { "CAN_OFF" : can, "CAN_OFF_INV" : (can + 1) % 2, diff --git a/selfdrive/car/mazda/values.py b/selfdrive/car/mazda/values.py index 5759d431a..4e194d61d 100644 --- a/selfdrive/car/mazda/values.py +++ b/selfdrive/car/mazda/values.py @@ -18,6 +18,8 @@ class SteerLimitParams: class CAR: CX5 = "Mazda CX-5 2017" + CX9 = "Mazda CX-9 2017" + Mazda3 = "Mazda3 2017" class LKAS_LIMITS: STEER_THRESHOLD = 15 @@ -41,13 +43,10 @@ FINGERPRINTS = { # CX-5 2019 GTR { 64: 8, 70: 8, 80: 8, 117: 8, 118: 8, 120: 8, 121: 8, 130: 8, 134: 8, 145: 8, 154: 8, 155: 8, 157: 8, 158: 8, 159: 8, 253: 8, 254: 8, 304: 8, 305: 8, 357: 8, 358: 8, 359: 8, 512: 8, 514: 8, 515: 8, 529: 8, 533: 8, 535: 8, 539: 8, 540: 8, 541: 8, 542: 8, 543: 8, 552: 8, 576: 8, 577: 8, 578: 8, 579: 8, 580: 8, 581: 8, 582: 8, 605: 8, 606: 8, 607: 8, 608: 8, 628: 8, 736: 8, 832: 8, 836: 8, 863: 8, 865: 8, 866: 8, 867: 8, 868: 8, 869: 8, 870: 8, 976: 8, 977: 8, 978: 8, 1034: 8, 1045: 8, 1056: 8, 1061: 8, 1067: 8, 1078: 8, 1080: 8, 1085: 8, 1086: 8, 1088: 8, 1093: 8, 1108: 8, 1114: 8, 1115: 8, 1116: 8, 1139: 8, 1143: 8, 1147: 8, 1154: 8, 1157: 8, 1160: 8, 1163: 8, 1166: 8, 1170: 8, 1171: 8, 1173: 8, 1177: 8, 1178: 8, 1179: 8, 1180: 8, 1183: 8, 1233: 8, 1236: 8, 1237: 8, 1238: 8, 1239: 8, 1241: 8, 1242: 8, 1244: 8, 1260: 8, 1264: 8, 1266: 8, 1267: 8, 1269: 8, 1270: 8, 1271: 8, 1272: 8, 1274: 8, 1277: 8, 1278: 8, 1409: 8, 1416: 8, 1425: 8, 1430: 8, 1435: 8, 1440: 8, 1446: 8, 1456: 8, 1479: 8, 1776: 8, 1792: 8, 1872: 8, 1937: 8, 1953: 8, 1968: 8, 2015: 8, 2016: 8, 2024: 8 - }, - - # Mazda 6 2017 GT - { - 64: 8, 70: 8, 80: 8, 117: 8, 118: 8, 120: 8, 121: 8, 130: 8, 134: 8, 145: 8, 154: 8, 155: 8, 157: 8, 158: 8, 159: 8, 253: 8, 304: 8, 305: 8, 357: 8, 358: 8, 359: 8, 512: 8, 514: 8, 515: 8, 529: 8, 533: 8, 535: 8, 539: 8, 540: 8, 541: 8, 542: 8, 543: 8, 552: 8, 576: 8, 577: 8, 578: 8, 579: 8, 580: 8, 581: 8, 582: 8, 605: 8, 606: 8, 607: 8, 628: 8, 832: 8, 836: 8, 863: 8, 865: 8, 866: 8, 867: 8, 868: 8, 869: 8, 870: 8, 976: 8, 977: 8, 978: 8, 1034: 8, 1045: 8, 1056: 8, 1061: 8, 1067: 8, 1070: 8, 1078: 8, 1080: 8, 1085: 8, 1086: 8, 1088: 8, 1093: 8, 1108: 8, 1114: 8, 1115: 8, 1116: 8, 1143: 8, 1147: 8, 1154: 8, 1157: 8, 1160: 8, 1163: 8, 1166: 8, 1177: 8, 1178: 8, 1179: 8, 1180: 8, 1182: 8, 1183: 8, 1233: 8, 1236: 8, 1237: 8, 1238: 8, 1239: 8, 1241: 8, 1242: 8, 1243: 8, 1244: 8, 1264: 8, 1266: 8, 1267: 8, 1269: 8, 1270: 8, 1271: 8, 1272: 8, 1274: 8, 1275: 8, 1277: 8, 1278: 8, 1409: 8, 1416: 8, 1425: 8, 1430: 8, 1435: 8, 1440: 8, 1456: 8, 1479: 8 - }, + } + ], + CAR.CX9: [ # CX-9 2017 Australia - old CAM connector { 64: 8, 70: 8, 80: 8, 117: 8, 118: 8, 120: 8, 121: 8, 130: 8, 134: 8, 138: 8, 145: 8, 154: 8, 155: 8, 157: 8, 158: 8, 159: 8, 253: 8, 304: 8, 305: 8, 357: 8, 358: 8, 359: 8, 512: 8, 514: 8, 515: 8, 522: 8, 529: 8, 533: 8, 535: 8, 539: 8, 540: 8, 541: 8, 542: 8, 543: 8, 552: 8, 576: 8, 577: 8, 578: 8, 579: 8, 580: 8, 581: 8, 582: 8, 583: 8, 605: 8, 606: 8, 628: 8, 832: 8, 976: 8, 977: 8, 978: 8, 1034: 8, 1045: 8, 1056: 8, 1061: 8, 1067: 8, 1078: 8, 1085: 8, 1086: 8, 1088: 8, 1093: 8, 1108: 8, 1114: 8, 1115: 8, 1116: 8, 1139: 8, 1143: 8, 1147: 8, 1154: 8, 1157: 8, 1160: 8, 1163: 8, 1166: 8, 1170: 8, 1177: 8, 1180: 8, 1183: 8, 1233: 8, 1236: 8, 1237: 8, 1238: 8, 1239: 8, 1241: 8, 1242: 8, 1243: 8, 1244: 8, 1247: 8, 1264: 8, 1266: 8, 1267: 8, 1269: 8, 1271: 8, 1272: 8, 1274: 8, 1277: 8, 1278: 8, 1409: 8, 1416: 8, 1425: 8, 1430: 8, 1435: 8, 1440: 8, 1446: 8, 1456: 8, 1479: 8 @@ -56,14 +55,20 @@ FINGERPRINTS = { # CX-9 2016 - old CAM connector { 64: 8, 70: 8, 80: 8, 117: 8, 118: 8, 120: 8, 121: 8, 130: 8, 134: 8, 145: 8, 154: 8, 155: 8, 157: 8, 158: 8, 159: 8, 253: 8, 304: 8, 305: 8, 357: 8, 358: 8, 359: 8, 512: 8, 514: 8, 515: 8, 529: 8, 533: 8, 535: 8, 539: 8, 540: 8, 541: 8, 542: 8, 543: 8, 552: 8, 576: 8, 577: 8, 578: 8, 579: 8, 580: 8, 581: 8, 582: 8, 583: 8, 605: 8, 606: 8, 608: 8, 628: 8, 832: 8, 836: 8, 976: 8, 977: 8, 978: 8, 1034: 8, 1045: 8, 1056: 8, 1061: 8, 1067: 8, 1078: 8, 1080: 8, 1085: 8, 1086: 8, 1088: 8, 1093: 8, 1108: 8, 1114: 8, 1115: 8, 1116: 8, 1139: 8, 1143: 8, 1147: 8, 1154: 8, 1157: 8, 1160: 8, 1163: 8, 1166: 8, 1170: 8, 1177: 8, 1178: 8, 1179: 8, 1180: 8, 1181: 8, 1183: 8, 1233: 8, 1236: 8, 1237: 8, 1238: 8, 1239: 8, 1241: 8, 1242: 8, 1244: 8, 1264: 8, 1266: 8, 1267: 8, 1269: 8, 1271: 8, 1272: 8, 1274: 8, 1277: 8, 1278: 8, 1409: 8, 1416: 8, 1425: 8, 1430: 8, 1435: 8, 1440: 8, 1446: 8, 1456: 8, 1479: 8, 1792: 8, 1872: 8, 1937: 8, 1953: 8, 1968: 8, 1988: 8, 1996: 8, 2000: 8, 2001: 8, 2004: 8, 2015: 8, 2016: 8, 2017: 8, 2024: 8, 2025: 8 - }, + } + ], + CAR.Mazda3: [ # Mazda 3 2017 { 19: 5, 80: 8, 117: 8, 118: 8, 120: 8, 121: 8, 130: 8, 134: 8, 145: 8, 154: 8, 155: 8, 157: 8, 158: 8, 159: 8, 253: 8, 304: 8, 305: 8, 357: 8, 358: 8, 359: 8, 512: 8, 514: 8, 515: 8, 529: 8, 533: 8, 535: 8, 539: 8, 540: 8, 541: 8, 542: 8, 543: 8, 552: 8, 576: 8, 577: 8, 578: 8, 579: 8, 580: 8, 581: 8, 582: 8, 605: 8, 606: 8, 607: 8, 628: 8, 832: 8, 863: 8, 865: 8, 866: 8, 867: 8, 868: 8, 869: 8, 870: 8, 976: 8, 977: 8, 978: 8, 1034: 8, 1045: 8, 1056: 8, 1061: 8, 1067: 8, 1070: 8, 1078: 8, 1086: 8, 1088: 8, 1093: 8, 1108: 8, 1114: 8, 1115: 8, 1116: 8, 1143: 8, 1147: 8, 1154: 8, 1157: 8, 1160: 8, 1163: 8, 1166: 8, 1169: 8, 1170: 8, 1173: 8, 1177: 8, 1180: 8, 1182: 8, 1183: 8, 1233: 8, 1236: 8, 1237: 8, 1238: 8, 1239: 8, 1241: 8, 1242: 8, 1243: 8, 1244: 8, 1264: 8, 1266: 8, 1267: 8, 1269: 8, 1270: 8, 1271: 8, 1272: 8, 1274: 8, 1275: 8, 1277: 8, 1278: 8, 1409: 8, 1416: 8, 1425: 8, 1430: 8, 1435: 8, 1440: 8, 1456: 8, 1479: 8, 2015: 8, 2024: 8, 2025: 8 }, - ], + # Mazda 6 2017 GT + { + 64: 8, 70: 8, 80: 8, 117: 8, 118: 8, 120: 8, 121: 8, 130: 8, 134: 8, 145: 8, 154: 8, 155: 8, 157: 8, 158: 8, 159: 8, 253: 8, 304: 8, 305: 8, 357: 8, 358: 8, 359: 8, 512: 8, 514: 8, 515: 8, 529: 8, 533: 8, 535: 8, 539: 8, 540: 8, 541: 8, 542: 8, 543: 8, 552: 8, 576: 8, 577: 8, 578: 8, 579: 8, 580: 8, 581: 8, 582: 8, 605: 8, 606: 8, 607: 8, 628: 8, 832: 8, 836: 8, 863: 8, 865: 8, 866: 8, 867: 8, 868: 8, 869: 8, 870: 8, 976: 8, 977: 8, 978: 8, 1034: 8, 1045: 8, 1056: 8, 1061: 8, 1067: 8, 1070: 8, 1078: 8, 1080: 8, 1085: 8, 1086: 8, 1088: 8, 1093: 8, 1108: 8, 1114: 8, 1115: 8, 1116: 8, 1143: 8, 1147: 8, 1154: 8, 1157: 8, 1160: 8, 1163: 8, 1166: 8, 1177: 8, 1178: 8, 1179: 8, 1180: 8, 1182: 8, 1183: 8, 1233: 8, 1236: 8, 1237: 8, 1238: 8, 1239: 8, 1241: 8, 1242: 8, 1243: 8, 1244: 8, 1264: 8, 1266: 8, 1267: 8, 1269: 8, 1270: 8, 1271: 8, 1272: 8, 1274: 8, 1275: 8, 1277: 8, 1278: 8, 1409: 8, 1416: 8, 1425: 8, 1430: 8, 1435: 8, 1440: 8, 1456: 8, 1479: 8 + } + ], } ECU_FINGERPRINT = { @@ -73,4 +78,8 @@ ECU_FINGERPRINT = { DBC = { CAR.CX5: dbc_dict('mazda_2017', None), + CAR.CX9: dbc_dict('mazda_2017', None), + CAR.Mazda3: dbc_dict('mazda_2017', None), } + +GEN1 = [ CAR.CX5, CAR.CX9, CAR.Mazda3 ] diff --git a/selfdrive/car/mock/interface.py b/selfdrive/car/mock/interface.py index d0298ec83..4b703d5e9 100755 --- a/selfdrive/car/mock/interface.py +++ b/selfdrive/car/mock/interface.py @@ -63,6 +63,7 @@ class CarInterface(CarInterfaceBase): # create message ret = car.CarState.new_message() + ret.canValid = True # speeds ret.vEgo = self.speed @@ -82,9 +83,6 @@ class CarInterface(CarInterfaceBase): curvature = self.yaw_rate / max(self.speed, 1.) ret.steeringAngle = curvature * self.CP.steerRatio * self.CP.wheelbase * CV.RAD_TO_DEG - events = [] - ret.events = events - return ret.as_reader() def apply(self, c): diff --git a/selfdrive/car/toyota/interface.py b/selfdrive/car/toyota/interface.py index e31eaee4f..e97cc23af 100755 --- a/selfdrive/car/toyota/interface.py +++ b/selfdrive/car/toyota/interface.py @@ -63,7 +63,7 @@ class CarInterface(CarInterfaceBase): elif candidate == CAR.COROLLA: stop_and_go = False - ret.safetyParam = 100 + ret.safetyParam = 88 ret.wheelbase = 2.70 ret.steerRatio = 18.27 tire_stiffness_factor = 0.444 # not optimized yet diff --git a/selfdrive/car/toyota/values.py b/selfdrive/car/toyota/values.py index da18bfba9..f1d31fb4d 100644 --- a/selfdrive/car/toyota/values.py +++ b/selfdrive/car/toyota/values.py @@ -309,6 +309,7 @@ FW_VERSIONS = { b'\x018966333P4700\x00\x00\x00\x00', b'\x018966333Q6000\x00\x00\x00\x00', b'\x018966333Q6200\x00\x00\x00\x00', + b'\x018966333W6000\x00\x00\x00\x00', ], (Ecu.dsu, 0x791, None): [ b'8821F0601200 ', @@ -316,17 +317,20 @@ FW_VERSIONS = { b'8821F0603300 ', b'8821F0607200 ', b'8821F0608000 ', + b'8821F0609100 ', ], (Ecu.esp, 0x7b0, None): [ b'F152606210\x00\x00\x00\x00\x00\x00', b'F152606230\x00\x00\x00\x00\x00\x00', b'F152606290\x00\x00\x00\x00\x00\x00', b'F152633540\x00\x00\x00\x00\x00\x00', + b'F152633A20\x00\x00\x00\x00\x00\x00', ], (Ecu.eps, 0x7a1, None): [ b'8965B33540\x00\x00\x00\x00\x00\x00', b'8965B33542\x00\x00\x00\x00\x00\x00', b'8965B33580\x00\x00\x00\x00\x00\x00', + b'8965B33621\x00\x00\x00\x00\x00\x00', ], (Ecu.fwdRadar, 0x750, 0xf): [ # Same as 0x791 b'8821F0601200 ', @@ -334,6 +338,7 @@ FW_VERSIONS = { b'8821F0603300 ', b'8821F0607200 ', b'8821F0608000 ', + b'8821F0609100 ', ], (Ecu.fwdCamera, 0x750, 0x6d): [ b'8646F0601200 ', @@ -342,6 +347,7 @@ FW_VERSIONS = { b'8646F0605000 ', b'8646F0606000 ', b'8646F0606100 ', + b'8646F0607100 ', ], }, CAR.CAMRYH: { @@ -425,6 +431,7 @@ FW_VERSIONS = { (Ecu.dsu, 0x791, None): [ b'8821F0W01000 ', b'8821FF401600 ', + b'8821FF404000 ', b'8821FF404100 ', b'8821FF405100 ', b'8821FF406000 ', @@ -436,6 +443,7 @@ FW_VERSIONS = { b'F1526F4034\x00\x00\x00\x00\x00\x00', b'F1526F4044\x00\x00\x00\x00\x00\x00', b'F1526F4073\x00\x00\x00\x00\x00\x00', + b'F1526F4121\x00\x00\x00\x00\x00\x00', b'F1526F4122\x00\x00\x00\x00\x00\x00', ], (Ecu.eps, 0x7a1, None): [ @@ -450,6 +458,7 @@ FW_VERSIONS = { (Ecu.fwdRadar, 0x750, 0xf): [ b'8821F0W01000 ', b'8821FF401600 ', + b'8821FF404000 ', b'8821FF404100 ', b'8821FF405100 ', b'8821FF406000 ', @@ -540,6 +549,7 @@ FW_VERSIONS = { b'\x018966312R3100\x00\x00\x00\x00', ], (Ecu.engine, 0x7e0, None): [ + b'\x0230ZN4000\x00\x00\x00\x00\x00\x00\x00\x00A0202000\x00\x00\x00\x00\x00\x00\x00\x00', b'\x03312N6000\x00\x00\x00\x00\x00\x00\x00\x00A0202000\x00\x00\x00\x00\x00\x00\x00\x00895231203202\x00\x00\x00\x00', b'\x03312N6000\x00\x00\x00\x00\x00\x00\x00\x00A0202000\x00\x00\x00\x00\x00\x00\x00\x00895231203302\x00\x00\x00\x00', b'\x03312N6100\x00\x00\x00\x00\x00\x00\x00\x00A0202000\x00\x00\x00\x00\x00\x00\x00\x00895231203302\x00\x00\x00\x00', @@ -548,6 +558,7 @@ FW_VERSIONS = { (Ecu.eps, 0x7a1, None): [ b'8965B12361\x00\x00\x00\x00\x00\x00', b'\x018965B12350\x00\x00\x00\x00\x00\x00', + b'\x018965B12470\x00\x00\x00\x00\x00\x00', b'\x018965B12500\x00\x00\x00\x00\x00\x00', b'\x018965B12520\x00\x00\x00\x00\x00\x00', b'\x018965B12530\x00\x00\x00\x00\x00\x00', @@ -556,6 +567,7 @@ FW_VERSIONS = { b'F152602191\x00\x00\x00\x00\x00\x00', b'\x01F152602280\x00\x00\x00\x00\x00\x00', b'\x01F152602560\x00\x00\x00\x00\x00\x00', + b'\x01F152602650\x00\x00\x00\x00\x00\x00', b'\x01F152612641\x00\x00\x00\x00\x00\x00', b'\x01F152612651\x00\x00\x00\x00\x00\x00', b'\x01F152612B10\x00\x00\x00\x00\x00\x00', @@ -573,6 +585,7 @@ FW_VERSIONS = { b'\x028646F12010D0\x00\x00\x00\x008646G26011A0\x00\x00\x00\x00', b'\x028646F1201100\x00\x00\x00\x008646G26011A0\x00\x00\x00\x00', b'\x028646F1201200\x00\x00\x00\x008646G26011A0\x00\x00\x00\x00', + b'\x028646F1201300\x00\x00\x00\x008646G2601400\x00\x00\x00\x00', b'\x028646F1202000\x00\x00\x00\x008646G2601200\x00\x00\x00\x00', b'\x028646F1202100\x00\x00\x00\x008646G2601400\x00\x00\x00\x00', ], @@ -593,6 +606,7 @@ FW_VERSIONS = { b'\x018965B12350\x00\x00\x00\x00\x00\x00', b'\x018965B12470\x00\x00\x00\x00\x00\x00', b'\x018965B12500\x00\x00\x00\x00\x00\x00', + b'\x018965B12520\x00\x00\x00\x00\x00\x00', b'\x018965B12530\x00\x00\x00\x00\x00\x00', ], (Ecu.esp, 0x7b0, None): [ @@ -604,6 +618,7 @@ FW_VERSIONS = { b'F152612840\x00\x00\x00\x00\x00\x00', b'F152612A10\x00\x00\x00\x00\x00\x00', b'F152642540\x00\x00\x00\x00\x00\x00', + b'F152612A00\x00\x00\x00\x00\x00\x00', ], (Ecu.fwdRadar, 0x750, 0xf): [ b'\x018821F3301100\x00\x00\x00\x00', @@ -683,7 +698,9 @@ FW_VERSIONS = { b'\x01F15260E051\x00\x00\x00\x00\x00\x00', ], (Ecu.engine, 0x700, None): [ + b'\x01896630E62200\x00\x00\x00\x00', b'\x01896630E64100\x00\x00\x00\x00', + b'\x01896630E64200\x00\x00\x00\x00', ], (Ecu.fwdRadar, 0x750, 0xf): [ b'\x018821F3301400\x00\x00\x00\x00', @@ -698,6 +715,7 @@ FW_VERSIONS = { ], (Ecu.esp, 0x7b0, None): [ b'\x01F15264872300\x00\x00\x00\x00', + b'\x01F15264872400\x00\x00\x00\x00', ], (Ecu.engine, 0x700, None): [ b'\x02896630E66000\x00\x00\x00\x00897CF4801001\x00\x00\x00\x00', @@ -747,6 +765,7 @@ FW_VERSIONS = { b'\x02896634784000\x00\x00\x00\x008966A4703000\x00\x00\x00\x00', b'\x028966347A5000\x00\x00\x00\x008966A4703000\x00\x00\x00\x00', b'\x028966347A8000\x00\x00\x00\x008966A4703000\x00\x00\x00\x00', + b'\x02896634765100\x00\x00\x00\x008966A4703000\x00\x00\x00\x00', b'\x03896634759100\x00\x00\x00\x008966A4703000\x00\x00\x00\x00897CF4701003\x00\x00\x00\x00', b'\x03896634759200\x00\x00\x00\x008966A4703000\x00\x00\x00\x00897CF4701003\x00\x00\x00\x00', b'\x03896634759200\x00\x00\x00\x008966A4703000\x00\x00\x00\x00897CF4701004\x00\x00\x00\x00', @@ -945,11 +964,13 @@ FW_VERSIONS = { b'\x018966342X6000\x00\x00\x00\x00', b'\x018966342W8000\x00\x00\x00\x00', b'\x028966342W4001\x00\x00\x00\x00897CF1203001\x00\x00\x00\x00', + b'\x02896634A14001\x00\x00\x00\x00897CF1203001\x00\x00\x00\x00', b'\x02896634A23001\x00\x00\x00\x00897CF1203001\x00\x00\x00\x00', ], (Ecu.esp, 0x7b0, None): [ b'F152642291\x00\x00\x00\x00\x00\x00', b'F152642330\x00\x00\x00\x00\x00\x00', + b'F152642331\x00\x00\x00\x00\x00\x00', b'F152642531\x00\x00\x00\x00\x00\x00', b'F152642532\x00\x00\x00\x00\x00\x00', b'F152642521\x00\x00\x00\x00\x00\x00', @@ -1075,9 +1096,12 @@ FW_VERSIONS = { (Ecu.engine, 0x700, None): [ b'\x01896630E37200\x00\x00\x00\x00', b'\x01896630E41000\x00\x00\x00\x00', + b'\x01896630E41100\x00\x00\x00\x00', b'\x01896630E41200\x00\x00\x00\x00', b'\x01896630E37300\x00\x00\x00\x00', + b'\x01896630E36300\x00\x00\x00\x00', b'\x018966348R8500\x00\x00\x00\x00', + b'\x018966348W1300\x00\x00\x00\x00', ], (Ecu.esp, 0x7b0, None): [ b'F152648472\x00\x00\x00\x00\x00\x00', @@ -1085,6 +1109,7 @@ FW_VERSIONS = { b'F152648492\x00\x00\x00\x00\x00\x00', b'F152648493\x00\x00\x00\x00\x00\x00', b'F152648474\x00\x00\x00\x00\x00\x00', + b'F152648630\x00\x00\x00\x00\x00\x00', ], (Ecu.dsu, 0x791, None): [ b'881514810300\x00\x00\x00\x00', @@ -1095,10 +1120,12 @@ FW_VERSIONS = { b'8965B0E011\x00\x00\x00\x00\x00\x00', b'8965B0E012\x00\x00\x00\x00\x00\x00', b'8965B48102\x00\x00\x00\x00\x00\x00', + b'8965B48112\x00\x00\x00\x00\x00\x00', ], (Ecu.fwdRadar, 0x750, 0xf): [ b'8821F4701000\x00\x00\x00\x00', b'8821F4701100\x00\x00\x00\x00', + b'8821F4701200\x00\x00\x00\x00', b'8821F4701300\x00\x00\x00\x00', ], (Ecu.fwdCamera, 0x750, 0x6d): [ @@ -1106,6 +1133,7 @@ FW_VERSIONS = { b'8646F4801200\x00\x00\x00\x00', b'8646F4802001\x00\x00\x00\x00', b'8646F4802100\x00\x00\x00\x00', + b'8646F4802200\x00\x00\x00\x00', b'8646F4809000\x00\x00\x00\x00', ], }, @@ -1154,6 +1182,7 @@ FW_VERSIONS = { ], (Ecu.esp, 0x7b0, None): [ b'\x01F15260E031\x00\x00\x00\x00\x00\x00', + b'\x01F15260E041\x00\x00\x00\x00\x00\x00', ], (Ecu.eps, 0x7a1, None): [ b'8965B48271\x00\x00\x00\x00\x00\x00', diff --git a/selfdrive/clocksd/SConscript b/selfdrive/clocksd/SConscript index 601e64bf1..d1cf13e9e 100644 --- a/selfdrive/clocksd/SConscript +++ b/selfdrive/clocksd/SConscript @@ -1,2 +1,2 @@ Import('env', 'common', 'cereal', 'messaging') -env.Program('clocksd.cc', LIBS=['diag', 'time_genoff', common, cereal, messaging, 'capnp', 'zmq', 'kj']) \ No newline at end of file +env.Program('clocksd.cc', LIBS=[common, cereal, messaging, 'capnp', 'zmq', 'kj']) diff --git a/selfdrive/clocksd/clocksd.cc b/selfdrive/clocksd/clocksd.cc index 2e17058e0..d9fa9eda3 100644 --- a/selfdrive/clocksd/clocksd.cc +++ b/selfdrive/clocksd/clocksd.cc @@ -1,12 +1,22 @@ +#include +#include + #include #include +#include #include -#include #include -#include + +#include #include "messaging.hpp" #include "common/timing.h" +// Apple doesn't have timerfd +#ifndef __APPLE__ +#include +#endif + +#ifdef QCOM namespace { int64_t arm_cntpct() { int64_t v; @@ -14,13 +24,14 @@ namespace { return v; } } +#endif int main() { setpriority(PRIO_PROCESS, 0, -13); - int err = 0; PubMaster pm({"clocks"}); +#ifndef __APPLE__ int timerfd = timerfd_create(CLOCK_BOOTTIME, 0); assert(timerfd >= 0); @@ -30,34 +41,43 @@ int main() { spec.it_value.tv_sec = 1; spec.it_value.tv_nsec = 0; - err = timerfd_settime(timerfd, 0, &spec, 0); + int err = timerfd_settime(timerfd, 0, &spec, 0); assert(err == 0); uint64_t expirations = 0; while ((err = read(timerfd, &expirations, sizeof(expirations)))) { if (err < 0) break; +#else + // Just run at 1Hz on apple + while (true){ + std::this_thread::sleep_for(std::chrono::seconds(1)); +#endif uint64_t boottime = nanos_since_boot(); uint64_t monotonic = nanos_monotonic(); uint64_t monotonic_raw = nanos_monotonic_raw(); uint64_t wall_time = nanos_since_epoch(); +#ifdef QCOM uint64_t modem_uptime_v = arm_cntpct() / 19200ULL; // 19.2 mhz clock +#endif - capnp::MallocMessageBuilder msg; - cereal::Event::Builder event = msg.initRoot(); - event.setLogMonoTime(boottime); - auto clocks = event.initClocks(); + MessageBuilder msg; + auto clocks = msg.initEvent().initClocks(); clocks.setBootTimeNanos(boottime); clocks.setMonotonicNanos(monotonic); clocks.setMonotonicRawNanos(monotonic_raw); clocks.setWallTimeNanos(wall_time); +#ifdef QCOM clocks.setModemUptimeMillis(modem_uptime_v); +#endif pm.send("clocks", msg); } +#ifndef __APPLE__ close(timerfd); +#endif return 0; -} \ No newline at end of file +} diff --git a/selfdrive/common/SConscript b/selfdrive/common/SConscript index 8ecf786ff..f70abf58c 100644 --- a/selfdrive/common/SConscript +++ b/selfdrive/common/SConscript @@ -5,7 +5,9 @@ if SHARED: else: fxn = env.Library -_common = fxn('common', ['params.cc', 'swaglog.cc', 'util.c', 'cqueue.c'], LIBS="json11") +common_libs = ['params.cc', 'swaglog.cc', 'util.c', 'cqueue.c', 'gpio.cc', 'i2c.cc'] + +_common = fxn('common', common_libs, LIBS="json11") _visionipc = fxn('visionipc', ['visionipc.c', 'ipc.c']) files = [ @@ -42,4 +44,3 @@ else: _gpucommon = fxn('gpucommon', files, CPPDEFINES=defines, LIBS=_gpu_libs) Export('_common', '_visionipc', '_gpucommon', '_gpu_libs') - diff --git a/selfdrive/common/clutil.c b/selfdrive/common/clutil.c index 9a80c9067..00d63cdca 100644 --- a/selfdrive/common/clutil.c +++ b/selfdrive/common/clutil.c @@ -36,6 +36,44 @@ void clu_init(void) { #endif } +cl_device_id cl_get_device_id(cl_device_type device_type) { + bool opencl_platform_found = false; + cl_device_id device_id = NULL; + + cl_uint num_platforms = 0; + int err = clGetPlatformIDs(0, NULL, &num_platforms); + assert(err == 0); + cl_platform_id* platform_ids = malloc(sizeof(cl_platform_id) * num_platforms); + err = clGetPlatformIDs(num_platforms, platform_ids, NULL); + assert(err == 0); + + char cBuffer[1024]; + for (size_t i = 0; i < num_platforms; i++) { + err = clGetPlatformInfo(platform_ids[i], CL_PLATFORM_NAME, sizeof(cBuffer), &cBuffer, NULL); + assert(err == 0); + printf("platform[%zu] CL_PLATFORM_NAME: %s\n", i, cBuffer); + + cl_uint num_devices; + err = clGetDeviceIDs(platform_ids[i], device_type, 0, NULL, &num_devices); + if (err != 0 || !num_devices) { + continue; + } + // Get first device + err = clGetDeviceIDs(platform_ids[i], device_type, 1, &device_id, NULL); + assert(err == 0); + cl_print_info(platform_ids[i], device_id); + opencl_platform_found = true; + break; + } + free(platform_ids); + + if (!opencl_platform_found) { + printf("No valid openCL platform found\n"); + assert(opencl_platform_found); + } + return device_id; +} + cl_program cl_create_program_from_file(cl_context ctx, const char* path) { char* src_buf = read_file(path, NULL); assert(src_buf); diff --git a/selfdrive/common/clutil.h b/selfdrive/common/clutil.h index b87961eac..abbda8c80 100644 --- a/selfdrive/common/clutil.h +++ b/selfdrive/common/clutil.h @@ -17,6 +17,7 @@ extern "C" { void clu_init(void); +cl_device_id cl_get_device_id(cl_device_type device_type); cl_program cl_create_program_from_file(cl_context ctx, const char* path); void cl_print_info(cl_platform_id platform, cl_device_id device); void cl_print_build_errors(cl_program program, cl_device_id device); diff --git a/selfdrive/common/framebuffer.cc b/selfdrive/common/framebuffer.cc index 30604deb2..30ef7ae8e 100644 --- a/selfdrive/common/framebuffer.cc +++ b/selfdrive/common/framebuffer.cc @@ -1,3 +1,4 @@ +#include "util.h" #include #include #include @@ -37,13 +38,9 @@ extern "C" void framebuffer_swap(FramebufferState *s) { } extern "C" bool set_brightness(int brightness) { - FILE *f = fopen("/sys/class/leds/lcd-backlight/brightness", "wb"); - if (f != NULL) { - fprintf(f, "%d", brightness); - fclose(f); - return true; - } - return false; + char bright[64]; + snprintf(bright, sizeof(bright), "%d", brightness); + return 0 == write_file("/sys/class/leds/lcd-backlight/brightness", bright, strlen(bright)); } extern "C" void framebuffer_set_power(FramebufferState *s, int mode) { diff --git a/selfdrive/common/gpio.cc b/selfdrive/common/gpio.cc new file mode 100644 index 000000000..8a72388d4 --- /dev/null +++ b/selfdrive/common/gpio.cc @@ -0,0 +1,29 @@ +#include "gpio.h" +#include "util.h" +#include +#include +#include + +// We assume that all pins have already been exported on boot, +// and that we have permission to write to them. + +int gpio_init(int pin_nr, bool output){ + char pin_dir_path[50]; + int pin_dir_path_len = snprintf(pin_dir_path, sizeof(pin_dir_path), + "/sys/class/gpio/gpio%d/direction", pin_nr); + if(pin_dir_path_len <= 0){ + return -1; + } + const char *value = output ? "out" : "in"; + return write_file(pin_dir_path, (void*)value, strlen(value)); +} + +int gpio_set(int pin_nr, bool high){ + char pin_val_path[50]; + int pin_val_path_len = snprintf(pin_val_path, sizeof(pin_val_path), + "/sys/class/gpio/gpio%d/value", pin_nr); + if(pin_val_path_len <= 0){ + return -1; + } + return write_file(pin_val_path, (void*)(high ? "1" : "0"), 1); +} diff --git a/selfdrive/common/gpio.h b/selfdrive/common/gpio.h new file mode 100644 index 000000000..479b4f7e0 --- /dev/null +++ b/selfdrive/common/gpio.h @@ -0,0 +1,35 @@ +#ifndef GPIO_H +#define GPIO_H + +#include + +// Pin definitions +#ifdef QCOM2 + #define GPIO_HUB_RST_N 30 + #define GPIO_UBLOX_RST_N 32 + #define GPIO_UBLOX_SAFEBOOT_N 33 + #define GPIO_UBLOX_PWR_EN 34 + #define GPIO_STM_RST_N 124 + #define GPIO_STM_BOOT0 134 +#else + #define GPIO_HUB_RST_N 0 + #define GPIO_UBLOX_RST_N 0 + #define GPIO_UBLOX_SAFEBOOT_N 0 + #define GPIO_UBLOX_PWR_EN 0 + #define GPIO_STM_RST_N 0 + #define GPIO_STM_BOOT0 0 +#endif + + +#ifdef __cplusplus +extern "C" { +#endif + +int gpio_init(int pin_nr, bool output); +int gpio_set(int pin_nr, bool high); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif diff --git a/selfdrive/common/i2c.cc b/selfdrive/common/i2c.cc new file mode 100644 index 000000000..a37b144dc --- /dev/null +++ b/selfdrive/common/i2c.cc @@ -0,0 +1,80 @@ +#include "i2c.h" + +#include +#include +#include +#include +#include +#include +#include "common/swaglog.h" + +#define UNUSED(x) (void)(x) + +#ifdef QCOM2 +// TODO: decide if we want to isntall libi2c-dev everywhere +#include + +I2CBus::I2CBus(uint8_t bus_id){ + char bus_name[20]; + snprintf(bus_name, 20, "/dev/i2c-%d", bus_id); + + i2c_fd = open(bus_name, O_RDWR); + if(i2c_fd < 0){ + throw std::runtime_error("Failed to open I2C bus"); + } +} + +I2CBus::~I2CBus(){ + if(i2c_fd >= 0){ close(i2c_fd); } +} + +int I2CBus::read_register(uint8_t device_address, uint register_address, uint8_t *buffer, uint8_t len){ + int ret = 0; + + ret = ioctl(i2c_fd, I2C_SLAVE, device_address); + if(ret < 0) { goto fail; } + + ret = i2c_smbus_read_i2c_block_data(i2c_fd, register_address, len, buffer); + if((ret < 0) || (ret != len)) { goto fail; } + +fail: + return ret; +} + +int I2CBus::set_register(uint8_t device_address, uint register_address, uint8_t data){ + int ret = 0; + + ret = ioctl(i2c_fd, I2C_SLAVE, device_address); + if(ret < 0) { goto fail; } + + ret = i2c_smbus_write_byte_data(i2c_fd, register_address, data); + if(ret < 0) { goto fail; } + +fail: + return ret; +} + +#else + +I2CBus::I2CBus(uint8_t bus_id){ + UNUSED(bus_id); + i2c_fd = -1; +} + +I2CBus::~I2CBus(){} + +int I2CBus::read_register(uint8_t device_address, uint register_address, uint8_t *buffer, uint8_t len){ + UNUSED(device_address); + UNUSED(register_address); + UNUSED(buffer); + UNUSED(len); + return -1; +} + +int I2CBus::set_register(uint8_t device_address, uint register_address, uint8_t data){ + UNUSED(device_address); + UNUSED(register_address); + UNUSED(data); + return -1; +} +#endif diff --git a/selfdrive/common/i2c.h b/selfdrive/common/i2c.h new file mode 100644 index 000000000..d5788510d --- /dev/null +++ b/selfdrive/common/i2c.h @@ -0,0 +1,16 @@ +#pragma once + +#include +#include + +class I2CBus { + private: + int i2c_fd; + + public: + I2CBus(uint8_t bus_id); + ~I2CBus(); + + int read_register(uint8_t device_address, uint register_address, uint8_t *buffer, uint8_t len); + int set_register(uint8_t device_address, uint register_address, uint8_t data); +}; diff --git a/selfdrive/common/modeldata.h b/selfdrive/common/modeldata.h index 111e20a41..77f4a1b7a 100644 --- a/selfdrive/common/modeldata.h +++ b/selfdrive/common/modeldata.h @@ -1,41 +1,7 @@ -#ifndef MODELDATA_H -#define MODELDATA_H +#pragma once #define MODEL_PATH_DISTANCE 192 #define POLYFIT_DEGREE 4 #define SPEED_PERCENTILES 10 #define DESIRE_PRED_SIZE 32 #define OTHER_META_SIZE 4 - -typedef struct PathData { - float points[MODEL_PATH_DISTANCE]; - float prob; - float std; - float stds[MODEL_PATH_DISTANCE]; - float poly[POLYFIT_DEGREE]; - float validLen; -} PathData; - -typedef struct LeadData { - float dist; - float prob; - float std; - float rel_y; - float rel_y_std; - float rel_v; - float rel_v_std; - float rel_a; - float rel_a_std; -} LeadData; - -typedef struct ModelData { - PathData path; - PathData left_lane; - PathData right_lane; - LeadData lead; - LeadData lead_future; - float meta[OTHER_META_SIZE + DESIRE_PRED_SIZE]; - float speed[SPEED_PERCENTILES]; -} ModelData; - -#endif diff --git a/selfdrive/common/params.cc b/selfdrive/common/params.cc index 4d9c214d3..d511ebf41 100644 --- a/selfdrive/common/params.cc +++ b/selfdrive/common/params.cc @@ -268,45 +268,19 @@ cleanup: } int read_db_value(const char* key, char** value, size_t* value_sz, bool persistent_param) { - int lock_fd = -1; - int result; char path[1024]; const char* params_path = persistent_param ? persistent_params_path : default_params_path; - result = snprintf(path, sizeof(path), "%s/.lock", params_path); + int result = snprintf(path, sizeof(path), "%s/d/%s", params_path, key); if (result < 0) { - goto cleanup; - } - lock_fd = open(path, 0); - - result = snprintf(path, sizeof(path), "%s/d/%s", params_path, key); - if (result < 0) { - goto cleanup; + return result; } - // Take lock. - result = flock(lock_fd, LOCK_EX); - if (result < 0) { - goto cleanup; - } - - // Read value. - // TODO(mgraczyk): If there is a lot of contention, we can release the lock - // after opening the file, before reading. *value = static_cast(read_file(path, value_sz)); if (*value == NULL) { - result = -22; - goto cleanup; + return -22; } - - result = 0; - -cleanup: - // Release lock. - if (lock_fd >= 0) { - close(lock_fd); - } - return result; + return 0; } void read_db_value_blocking(const char* key, char** value, size_t* value_sz, bool persistent_param) { @@ -330,8 +304,11 @@ int read_db_all(std::map *params, bool persistent_para int lock_fd = open(lock_path.c_str(), 0); if (lock_fd < 0) return -1; - err = flock(lock_fd, LOCK_EX); - if (err < 0) return err; + err = flock(lock_fd, LOCK_SH); + if (err < 0) { + close(lock_fd); + return err; + } std::string key_path = util::string_format("%s/d", params_path); DIR *d = opendir(key_path.c_str()); @@ -366,3 +343,8 @@ std::vector read_db_bytes(const char* param_name, bool persistent_param) { } return bytes; } + +bool read_db_bool(const char* param_name, bool persistent_param) { + std::vector bytes = read_db_bytes(param_name, persistent_param); + return bytes.size() > 0 and bytes[0] == '1'; +} diff --git a/selfdrive/common/params.h b/selfdrive/common/params.h index 49af04b4f..394c9b6ed 100644 --- a/selfdrive/common/params.h +++ b/selfdrive/common/params.h @@ -40,4 +40,5 @@ void read_db_value_blocking(const char* key, char** value, size_t* value_sz, boo #include int read_db_all(std::map *params, bool persistent_param = false); std::vector read_db_bytes(const char* param_name, bool persistent_param = false); +bool read_db_bool(const char* param_name, bool persistent_param = false); #endif diff --git a/selfdrive/common/spinner.c b/selfdrive/common/spinner.c index 99e0ed422..2054229cc 100644 --- a/selfdrive/common/spinner.c +++ b/selfdrive/common/spinner.c @@ -61,6 +61,7 @@ int spin(int argc, char** argv) { FramebufferState *fb = framebuffer_init("spinner", 0x00001000, false, &fb_w, &fb_h); assert(fb); + framebuffer_set_power(fb, HWC_POWER_MODE_NORMAL); NVGcontext *vg = nvgCreateGLES3(NVG_ANTIALIAS | NVG_STENCIL_STROKES); assert(vg); diff --git a/selfdrive/common/swaglog.cc b/selfdrive/common/swaglog.cc index 41b6358d9..9d9034754 100644 --- a/selfdrive/common/swaglog.cc +++ b/selfdrive/common/swaglog.cc @@ -93,15 +93,13 @@ void cloudlog_e(int levelnum, const char* filename, int lineno, const char* func {"created", seconds_since_epoch()} }; - char* log_s = strdup(log_j.dump().c_str()); - assert(log_s); + std::string log_s = log_j.dump(); free(msg_buf); char levelnum_c = levelnum; zmq_send(s.sock, &levelnum_c, 1, ZMQ_NOBLOCK | ZMQ_SNDMORE); - zmq_send(s.sock, log_s, strlen(log_s), ZMQ_NOBLOCK); - free(log_s); + zmq_send(s.sock, log_s.c_str(), log_s.length(), ZMQ_NOBLOCK); pthread_mutex_unlock(&s.lock); } diff --git a/selfdrive/common/touch.c b/selfdrive/common/touch.c index 2225ed215..0ab1e38c0 100644 --- a/selfdrive/common/touch.c +++ b/selfdrive/common/touch.c @@ -94,31 +94,3 @@ int touch_poll(TouchState *s, int* out_x, int* out_y, int timeout) { } return up; } - -int touch_read(TouchState *s, int* out_x, int* out_y) { - assert(out_x && out_y); - struct input_event event; - int err = read(s->fd, &event, sizeof(event)); - if (err < sizeof(event)) { - return -1; - } - bool up = false; - switch (event.type) { - case EV_ABS: - if (event.code == ABS_MT_POSITION_X) { - s->last_x = event.value; - } else if (event.code == ABS_MT_POSITION_Y) { - s->last_y = event.value; - } - up = true; - break; - default: - break; - } - if (up) { - // adjust for flippening - *out_x = s->last_y; - *out_y = 1080 - s->last_x; - } - return up; -} diff --git a/selfdrive/common/touch.h b/selfdrive/common/touch.h index c48f66b98..fa0faa271 100644 --- a/selfdrive/common/touch.h +++ b/selfdrive/common/touch.h @@ -1,5 +1,4 @@ -#ifndef TOUCH_H -#define TOUCH_H +#pragma once #ifdef __cplusplus extern "C" { @@ -12,10 +11,7 @@ typedef struct TouchState { void touch_init(TouchState *s); int touch_poll(TouchState *s, int *out_x, int *out_y, int timeout); -int touch_read(TouchState *s, int* out_x, int* out_y); #ifdef __cplusplus } #endif - -#endif diff --git a/selfdrive/common/util.c b/selfdrive/common/util.c index 3728dabcd..33930f9ec 100644 --- a/selfdrive/common/util.c +++ b/selfdrive/common/util.c @@ -3,7 +3,8 @@ #include #include #include - +#include +#include #ifdef __linux__ #include #include @@ -41,6 +42,16 @@ void* read_file(const char* path, size_t* out_len) { return buf; } +int write_file(const char* path, const void* data, size_t size) { + int fd = open(path, O_WRONLY); + if (fd == -1) { + return -1; + } + ssize_t n = write(fd, data, size); + close(fd); + return n == size ? 0 : -1; +} + void set_thread_name(const char* name) { #ifdef __linux__ // pthread_setname_np is dumb (fails instead of truncates) @@ -50,7 +61,6 @@ void set_thread_name(const char* name) { int set_realtime_priority(int level) { #ifdef __linux__ - long tid = syscall(SYS_gettid); // should match python using chrt @@ -64,8 +74,7 @@ int set_realtime_priority(int level) { } int set_core_affinity(int core) { -#ifdef QCOM - +#ifdef __linux__ long tid = syscall(SYS_gettid); cpu_set_t rt_cpu; diff --git a/selfdrive/common/util.h b/selfdrive/common/util.h index a9052146c..18d9619ce 100644 --- a/selfdrive/common/util.h +++ b/selfdrive/common/util.h @@ -41,6 +41,7 @@ extern "C" { // Returns NULL on failure, otherwise the NULL-terminated file contents. // The result must be freed by the caller. void* read_file(const char* path, size_t* out_len); +int write_file(const char* path, const void* data, size_t size); void set_thread_name(const char* name); diff --git a/selfdrive/common/utilpp.h b/selfdrive/common/utilpp.h index 6a68b5fba..e0547e0c1 100644 --- a/selfdrive/common/utilpp.h +++ b/selfdrive/common/utilpp.h @@ -1,5 +1,4 @@ -#ifndef UTILPP_H -#define UTILPP_H +#pragma once #include #include @@ -63,4 +62,16 @@ inline std::string readlink(std::string path) { } -#endif +struct unique_fd { + unique_fd(int fd = -1) : fd_(fd) {} + unique_fd& operator=(unique_fd&& uf) { + fd_ = uf.fd_; + uf.fd_ = -1; + return *this; + } + ~unique_fd() { + if (fd_ != -1) close(fd_); + } + operator int() const { return fd_; } + int fd_; +}; diff --git a/selfdrive/common/version.h b/selfdrive/common/version.h index d95231d5c..ddbf69550 100644 --- a/selfdrive/common/version.h +++ b/selfdrive/common/version.h @@ -1 +1 @@ -#define COMMA_VERSION "0.7.8-release" +#define COMMA_VERSION "0.7.9-release" diff --git a/selfdrive/common/visionbuf_cl.c b/selfdrive/common/visionbuf_cl.c index c68950baa..21f100507 100644 --- a/selfdrive/common/visionbuf_cl.c +++ b/selfdrive/common/visionbuf_cl.c @@ -110,6 +110,7 @@ void visionbuf_free(const VisionBuf* buf) { #if __OPENCL_VERSION__ >= 200 clSVMFree(buf->ctx, buf->addr); #else + clReleaseCommandQueue(buf->copy_q); munmap(buf->addr, buf->len); close(buf->fd); #endif diff --git a/selfdrive/common/visionipc.c b/selfdrive/common/visionipc.c index bae05ccc7..682049115 100644 --- a/selfdrive/common/visionipc.c +++ b/selfdrive/common/visionipc.c @@ -90,6 +90,7 @@ int visionstream_init(VisionStream *s, VisionStreamType type, bool tbuffer, Visi err = vipc_send(s->ipc_fd, &p); if (err < 0) { close(s->ipc_fd); + s->ipc_fd = -1; return -1; } @@ -97,6 +98,7 @@ int visionstream_init(VisionStream *s, VisionStreamType type, bool tbuffer, Visi err = vipc_recv(s->ipc_fd, &rp); if (err <= 0) { close(s->ipc_fd); + s->ipc_fd = -1; return -1; } assert(rp.type == VIPC_STREAM_BUFS); @@ -190,5 +192,5 @@ void visionstream_destroy(VisionStream *s) { } } if (s->bufs) free(s->bufs); - close(s->ipc_fd); + if (s->ipc_fd >= 0) close(s->ipc_fd); } diff --git a/selfdrive/common/visionipc.h b/selfdrive/common/visionipc.h index 4844a71b1..1b216f525 100644 --- a/selfdrive/common/visionipc.h +++ b/selfdrive/common/visionipc.h @@ -23,8 +23,10 @@ typedef enum VisionIPCPacketType { typedef enum VisionStreamType { VISION_STREAM_RGB_BACK, VISION_STREAM_RGB_FRONT, + VISION_STREAM_RGB_WIDE, VISION_STREAM_YUV, VISION_STREAM_YUV_FRONT, + VISION_STREAM_YUV_WIDE, VISION_STREAM_MAX, } VisionStreamType; @@ -35,6 +37,10 @@ typedef struct VisionUIInfo { int front_box_x, front_box_y; int front_box_width, front_box_height; + + int wide_box_x, wide_box_y; + int wide_box_width, wide_box_height; + } VisionUIInfo; typedef struct VisionStreamBufs { diff --git a/selfdrive/controls/controlsd.py b/selfdrive/controls/controlsd.py index 2546a598a..fe576192e 100755 --- a/selfdrive/controls/controlsd.py +++ b/selfdrive/controls/controlsd.py @@ -1,10 +1,9 @@ #!/usr/bin/env python3 import os -import gc from cereal import car, log -from common.android import ANDROID, get_sound_card_online +from common.hardware import HARDWARE from common.numpy_fast import clip -from common.realtime import sec_since_boot, set_realtime_priority, set_core_affinity, Ratekeeper, DT_CTRL +from common.realtime import sec_since_boot, config_rt_process, Priority, Ratekeeper, DT_CTRL from common.profiler import Profiler from common.params import Params, put_nonblocking import cereal.messaging as messaging @@ -21,13 +20,16 @@ from selfdrive.controls.lib.events import Events, ET from selfdrive.controls.lib.alertmanager import AlertManager from selfdrive.controls.lib.vehicle_model import VehicleModel from selfdrive.controls.lib.planner import LON_MPC_STEP -from selfdrive.locationd.calibration_helpers import Calibration +from selfdrive.locationd.calibrationd import Calibration LDW_MIN_SPEED = 31 * CV.MPH_TO_MS LANE_DEPARTURE_THRESHOLD = 0.1 STEER_ANGLE_SATURATION_TIMEOUT = 1.0 / DT_CTRL STEER_ANGLE_SATURATION_THRESHOLD = 2.5 # Degrees +SIMULATION = "SIMULATION" in os.environ +NOSENSOR = "NOSENSOR" in os.environ + ThermalStatus = log.ThermalData.ThermalStatus State = log.ControlsState.OpenpilotState HwType = log.HealthData.HwType @@ -40,9 +42,7 @@ EventName = car.CarEvent.EventName class Controls: def __init__(self, sm=None, pm=None, can_sock=None): - gc.disable() - set_realtime_priority(53) - set_core_affinity(3) + config_rt_process(3, Priority.CTRL_HIGH) # Setup sockets self.pm = pm @@ -79,11 +79,11 @@ class Controls: internet_needed or not openpilot_enabled_toggle # detect sound card presence and ensure successful init - sounds_available = not ANDROID or get_sound_card_online() + sounds_available = HARDWARE.get_sound_card_online() car_recognized = self.CP.carName != 'mock' # If stock camera is disconnected, we loaded car controls and it's not dashcam mode - controller_available = self.CP.enableCamera and self.CI.CC is not None and not passive + controller_available = self.CP.enableCamera and self.CI.CC is not None and not passive and not self.CP.dashcamOnly community_feature_disallowed = self.CP.communityFeature and not community_feature_toggle self.read_only = not car_recognized or not controller_available or \ self.CP.dashcamOnly or community_feature_disallowed @@ -94,7 +94,6 @@ class Controls: cp_bytes = self.CP.to_bytes() params.put("CarParams", cp_bytes) put_nonblocking("CarParamsCache", cp_bytes) - put_nonblocking("LongitudinalControl", "1" if self.CP.openpilotLongitudinalControl else "0") self.CC = car.CarControl.new_message() self.AM = AlertManager() @@ -139,7 +138,7 @@ class Controls: self.events.add(EventName.internetConnectivityNeeded, static=True) if community_feature_disallowed: self.events.add(EventName.communityFeatureDisallowed, static=True) - if self.read_only and not passive: + if not car_recognized: self.events.add(EventName.carUnrecognized, static=True) if hw_type == HwType.whitePanda: self.events.add(EventName.whitePandaUnsupported, static=True) @@ -192,7 +191,7 @@ class Controls: else: self.events.add(EventName.preLaneChangeRight) elif self.sm['pathPlan'].laneChangeState in [LaneChangeState.laneChangeStarting, - LaneChangeState.laneChangeFinishing]: + LaneChangeState.laneChangeFinishing]: self.events.add(EventName.laneChange) if self.can_rcv_error or (not CS.canValid and self.sm.frame > 5 / DT_CTRL): @@ -206,12 +205,13 @@ class Controls: self.events.add(EventName.commIssue) if not self.sm['pathPlan'].mpcSolutionValid: self.events.add(EventName.plannerError) - if not self.sm['liveLocationKalman'].sensorsOK and os.getenv("NOSENSOR") is None: + if not self.sm['liveLocationKalman'].sensorsOK and not NOSENSOR: if self.sm.frame > 5 / DT_CTRL: # Give locationd some time to receive all the inputs self.events.add(EventName.sensorDataInvalid) - if not self.sm['liveLocationKalman'].gpsOK and (self.distance_traveled > 1000) and os.getenv("NOSENSOR") is None: + if not self.sm['liveLocationKalman'].gpsOK and (self.distance_traveled > 1000): # Not show in first 1 km to allow for driving out of garage. This event shows after 5 minutes - self.events.add(EventName.noGps) + if not (SIMULATION or NOSENSOR): # TODO: send GPS in carla + self.events.add(EventName.noGps) if not self.sm['pathPlan'].paramsValid: self.events.add(EventName.vehicleModelInvalid) if not self.sm['liveLocationKalman'].posenetOK: @@ -229,12 +229,12 @@ class Controls: self.events.add(EventName.relayMalfunction) if self.sm['plan'].fcw: self.events.add(EventName.fcw) - if self.sm['model'].frameDropPerc > 1: - self.events.add(EventName.modeldLagging) + if self.sm['model'].frameDropPerc > 1 and (not SIMULATION): + self.events.add(EventName.modeldLagging) # Only allow engagement with brake pressed when stopped behind another stopped car if CS.brakePressed and self.sm['plan'].vTargetFuture >= STARTING_TARGET_SPEED \ - and not self.CP.radarOffCan and CS.vEgo < 0.3: + and self.CP.openpilotLongitudinalControl and CS.vEgo < 0.3: self.events.add(EventName.noTarget) def data_sample(self): diff --git a/selfdrive/controls/lib/alertmanager.py b/selfdrive/controls/lib/alertmanager.py index 870d8d4b7..6eaa86d3e 100644 --- a/selfdrive/controls/lib/alertmanager.py +++ b/selfdrive/controls/lib/alertmanager.py @@ -1,24 +1,21 @@ import os import copy import json +from typing import List, Optional from cereal import car, log from common.basedir import BASEDIR from common.params import Params from common.realtime import DT_CTRL +from selfdrive.controls.lib.events import Alert from selfdrive.swaglog import cloudlog -AlertSize = log.ControlsState.AlertSize -AlertStatus = log.ControlsState.AlertStatus -VisualAlert = car.CarControl.HUDControl.VisualAlert -AudibleAlert = car.CarControl.HUDControl.AudibleAlert - with open(os.path.join(BASEDIR, "selfdrive/controls/lib/alerts_offroad.json")) as f: OFFROAD_ALERTS = json.load(f) -def set_offroad_alert(alert, show_alert, extra_text=None): +def set_offroad_alert(alert: str, show_alert: bool, extra_text: Optional[str] = None) -> None: if show_alert: a = OFFROAD_ALERTS[alert] if extra_text is not None: @@ -29,19 +26,30 @@ def set_offroad_alert(alert, show_alert, extra_text=None): Params().delete(alert) -class AlertManager(): +class AlertManager: def __init__(self): - self.activealerts = [] + self.activealerts: List[Alert] = [] + self.clear_current_alert() - def alert_present(self): + def alert_present(self) -> bool: return len(self.activealerts) > 0 - def add_many(self, frame, alerts, enabled=True): + def clear_current_alert(self) -> None: + self.alert_type: str = "" + self.alert_text_1: str = "" + self.alert_text_2: str = "" + self.alert_status = log.ControlsState.AlertStatus.normal + self.alert_size = log.ControlsState.AlertSize.none + self.visual_alert = car.CarControl.HUDControl.VisualAlert.none + self.audible_alert = car.CarControl.HUDControl.AudibleAlert.none + self.alert_rate: float = 0. + + def add_many(self, frame: int, alerts: List[Alert], enabled: bool = True) -> None: for a in alerts: self.add(frame, a, enabled=enabled) - def add(self, frame, alert, enabled=True): + def add(self, frame: int, alert: Alert, enabled: bool = True) -> None: added_alert = copy.copy(alert) added_alert.start_time = frame * DT_CTRL @@ -54,26 +62,18 @@ class AlertManager(): # sort by priority first and then by start_time self.activealerts.sort(key=lambda k: (k.alert_priority, k.start_time), reverse=True) - def process_alerts(self, frame): + def process_alerts(self, frame: int) -> None: cur_time = frame * DT_CTRL # first get rid of all the expired alerts self.activealerts = [a for a in self.activealerts if a.start_time + max(a.duration_sound, a.duration_hud_alert, a.duration_text) > cur_time] - current_alert = self.activealerts[0] if self.alert_present() else None - # start with assuming no alerts - self.alert_type = "" - self.alert_text_1 = "" - self.alert_text_2 = "" - self.alert_status = AlertStatus.normal - self.alert_size = AlertSize.none - self.visual_alert = VisualAlert.none - self.audible_alert = AudibleAlert.none - self.alert_rate = 0. + self.clear_current_alert() - if current_alert: + current_alert = self.activealerts[0] if self.alert_present() else None + if current_alert is not None: self.alert_type = current_alert.alert_type if current_alert.start_time + current_alert.duration_sound > cur_time: diff --git a/selfdrive/controls/lib/events.py b/selfdrive/controls/lib/events.py index 205c6a172..67a6838d7 100644 --- a/selfdrive/controls/lib/events.py +++ b/selfdrive/controls/lib/events.py @@ -1,9 +1,11 @@ from functools import total_ordering +from typing import Dict, Union, Callable, Any from cereal import log, car +import cereal.messaging as messaging from common.realtime import DT_CTRL from selfdrive.config import Conversions as CV -from selfdrive.locationd.calibration_helpers import Filter +from selfdrive.locationd.calibrationd import MIN_SPEED_FILTER AlertSize = log.ControlsState.AlertSize AlertStatus = log.ControlsState.AlertStatus @@ -97,18 +99,18 @@ class Events: @total_ordering class Alert: def __init__(self, - alert_text_1, - alert_text_2, + alert_text_1: str, + alert_text_2: str, alert_status, alert_size, alert_priority, visual_alert, audible_alert, - duration_sound, - duration_hud_alert, - duration_text, - alert_rate=0., - creation_delay=0.): + duration_sound: float, + duration_hud_alert: float, + duration_text: float, + alert_rate: float = 0., + creation_delay: float = 0.): self.alert_type = "" self.alert_text_1 = alert_text_1 @@ -131,14 +133,14 @@ class Alert: tst = car.CarControl.new_message() tst.hudControl.visualAlert = self.visual_alert - def __str__(self): + def __str__(self) -> str: return self.alert_text_1 + "/" + self.alert_text_2 + " " + str(self.alert_priority) + " " + str( self.visual_alert) + " " + str(self.audible_alert) - def __gt__(self, alert2): + def __gt__(self, alert2) -> bool: return self.alert_priority > alert2.alert_priority - def __eq__(self, alert2): + def __eq__(self, alert2) -> bool: return self.alert_priority == alert2.alert_priority class NoEntryAlert(Alert): @@ -174,25 +176,25 @@ class EngagementAlert(Alert): # ********** alert callback functions ********** -def below_steer_speed_alert(CP, sm, metric): +def below_steer_speed_alert(CP: car.CarParams, sm: messaging.SubMaster, metric: bool) -> Alert: speed = int(round(CP.minSteerSpeed * (CV.MS_TO_KPH if metric else CV.MS_TO_MPH))) - unit = "kph" if metric else "mph" + unit = "km/h" if metric else "mph" return Alert( "TAKE CONTROL", "Steer Unavailable Below %d %s" % (speed, unit), AlertStatus.userPrompt, AlertSize.mid, Priority.MID, VisualAlert.steerRequired, AudibleAlert.none, 0., 0.4, .3) -def calibration_incomplete_alert(CP, sm, metric): - speed = int(Filter.MIN_SPEED * (CV.MS_TO_KPH if metric else CV.MS_TO_MPH)) - unit = "kph" if metric else "mph" +def calibration_incomplete_alert(CP: car.CarParams, sm: messaging.SubMaster, metric: bool) -> Alert: + speed = int(MIN_SPEED_FILTER * (CV.MS_TO_KPH if metric else CV.MS_TO_MPH)) + unit = "km/h" if metric else "mph" return Alert( "Calibration in Progress: %d%%" % sm['liveCalibration'].calPerc, "Drive Above %d %s" % (speed, unit), AlertStatus.normal, AlertSize.mid, Priority.LOWEST, VisualAlert.none, AudibleAlert.none, 0., 0., .2) -def no_gps_alert(CP, sm, metric): +def no_gps_alert(CP: car.CarParams, sm: messaging.SubMaster, metric: bool) -> Alert: gps_integrated = sm['health'].hwType in [log.HealthData.HwType.uno, log.HealthData.HwType.dos] return Alert( "Poor GPS reception", @@ -200,13 +202,13 @@ def no_gps_alert(CP, sm, metric): AlertStatus.normal, AlertSize.mid, Priority.LOWER, VisualAlert.none, AudibleAlert.none, 0., 0., .2, creation_delay=300.) -def wrong_car_mode_alert(CP, sm, metric): +def wrong_car_mode_alert(CP: car.CarParams, sm: messaging.SubMaster, metric: bool) -> Alert: text = "Cruise Mode Disabled" if CP.carName == "honda": text = "Main Switch Off" return NoEntryAlert(text, duration_hud_alert=0.) -EVENTS = { +EVENTS: Dict[int, Dict[str, Union[Alert, Callable[[Any, messaging.SubMaster, bool], Alert]]]] = { # ********** events with no alerts ********** # ********** events only containing alerts displayed in all states ********** @@ -227,14 +229,6 @@ EVENTS = { Priority.LOWER, VisualAlert.none, AudibleAlert.none, 0., 0., 15.), }, - EventName.startupWhitePanda: { - ET.PERMANENT: Alert( - "WARNING: White panda is deprecated", - "Upgrade to comma two or black panda", - AlertStatus.userPrompt, AlertSize.mid, - Priority.LOWER, VisualAlert.none, AudibleAlert.none, 0., 0., 15.), - }, - EventName.startupMaster: { ET.PERMANENT: Alert( "WARNING: This branch is not tested", @@ -287,7 +281,6 @@ EVENTS = { EventName.communityFeatureDisallowed: { # LOW priority to overcome Cruise Error ET.PERMANENT: Alert( - "", "Community Feature Detected", "Enable Community Features in Developer Settings", AlertStatus.normal, AlertSize.mid, @@ -341,7 +334,7 @@ EVENTS = { "openpilot will not brake while gas pressed", "", AlertStatus.normal, AlertSize.small, - Priority.LOWEST, VisualAlert.none, AudibleAlert.none, .0, .0, .1), + Priority.LOWEST, VisualAlert.none, AudibleAlert.none, .0, .0, .1, creation_delay=1.), }, EventName.vehicleModelInvalid: { @@ -413,7 +406,7 @@ EVENTS = { "CHECK DRIVER FACE VISIBILITY", "Driver Monitor Model Output Uncertain", AlertStatus.normal, AlertSize.mid, - Priority.LOW, VisualAlert.steerRequired, AudibleAlert.none, .4, 0., 1.), + Priority.LOW, VisualAlert.steerRequired, AudibleAlert.none, .4, 0., 1.5), }, EventName.manualRestart: { @@ -535,7 +528,7 @@ EVENTS = { "TAKE CONTROL", "Attempting Refocus: Camera Focus Invalid", AlertStatus.userPrompt, AlertSize.mid, - Priority.LOW, VisualAlert.steerRequired, AudibleAlert.chimeWarning1, .4, 2., 3.), + Priority.LOW, VisualAlert.steerRequired, AudibleAlert.chimeWarning1, .4, 2., 3., creation_delay=3.1), }, EventName.outOfSpace: { @@ -593,7 +586,12 @@ EVENTS = { }, EventName.calibrationInvalid: { - ET.SOFT_DISABLE: SoftDisableAlert("Calibration Invalid: Reposition Device and Recalibrate"), + ET.PERMANENT: Alert( + "Calibration Invalid", + "Reposition Device and Recalibrate", + AlertStatus.normal, AlertSize.mid, + Priority.LOWER, VisualAlert.none, AudibleAlert.none, 0., 0., .2), + ET.SOFT_DISABLE: SoftDisableAlert("Calibration Invalid: Reposition Device & Recalibrate"), ET.NO_ENTRY: NoEntryAlert("Calibration Invalid: Reposition Device & Recalibrate"), }, @@ -716,6 +714,11 @@ EVENTS = { }, EventName.reverseGear: { + ET.PERMANENT: Alert( + "Reverse\nGear", + "", + AlertStatus.normal, AlertSize.full, + Priority.LOWEST, VisualAlert.none, AudibleAlert.none, 0., 0., .2, creation_delay=0.5), ET.IMMEDIATE_DISABLE: ImmediateDisableAlert("Reverse Gear"), ET.NO_ENTRY: NoEntryAlert("Reverse Gear"), }, diff --git a/selfdrive/controls/lib/long_mpc.py b/selfdrive/controls/lib/long_mpc.py index b83354065..f9bf15cc0 100644 --- a/selfdrive/controls/lib/long_mpc.py +++ b/selfdrive/controls/lib/long_mpc.py @@ -56,7 +56,7 @@ class LongitudinalMpc(): self.cur_state[0].v_ego = v self.cur_state[0].a_ego = a - def update(self, pm, CS, lead, v_cruise_setpoint): + def update(self, pm, CS, lead): v_ego = CS.vEgo # Setup current mpc state diff --git a/selfdrive/controls/lib/longcontrol.py b/selfdrive/controls/lib/longcontrol.py index 5262f480a..f31dcb05b 100644 --- a/selfdrive/controls/lib/longcontrol.py +++ b/selfdrive/controls/lib/longcontrol.py @@ -14,9 +14,6 @@ STOPPING_BRAKE_RATE = 0.2 # brake_travel/s while trying to stop STARTING_BRAKE_RATE = 0.8 # brake_travel/s while releasing on restart BRAKE_STOPPING_TARGET = 0.5 # apply at least this amount of brake to maintain the vehicle stationary -_MAX_SPEED_ERROR_BP = [0., 30.] # speed breakpoints -_MAX_SPEED_ERROR_V = [1.5, .8] # max positive v_pid error VS actual speed; this avoids controls windup due to slow pedal resp - RATE = 100.0 @@ -86,8 +83,7 @@ class LongControl(): v_ego_pid = max(CS.vEgo, MIN_CAN_SPEED) # Without this we get jumps, CAN bus reports 0 when speed < 0.3 if self.long_control_state == LongCtrlState.off or CS.gasPressed: - self.v_pid = v_ego_pid - self.pid.reset() + self.reset(v_ego_pid) output_gb = 0. # tracking objects and driving @@ -113,15 +109,13 @@ class LongControl(): output_gb -= STOPPING_BRAKE_RATE / RATE output_gb = clip(output_gb, -brake_max, gas_max) - self.v_pid = CS.vEgo - self.pid.reset() + self.reset(CS.vEgo) # Intention is to move again, release brake fast before handing control to PID elif self.long_control_state == LongCtrlState.starting: if output_gb < -0.2: output_gb += STARTING_BRAKE_RATE / RATE - self.v_pid = CS.vEgo - self.pid.reset() + self.reset(CS.vEgo) self.last_output_gb = output_gb final_gas = clip(output_gb, 0., gas_max) diff --git a/selfdrive/controls/lib/planner.py b/selfdrive/controls/lib/planner.py index 553e5bb6f..0412df36d 100755 --- a/selfdrive/controls/lib/planner.py +++ b/selfdrive/controls/lib/planner.py @@ -15,10 +15,7 @@ from selfdrive.controls.lib.fcw import FCWChecker from selfdrive.controls.lib.long_mpc import LongitudinalMpc from selfdrive.controls.lib.drive_helpers import V_CRUISE_MAX -MAX_SPEED = 255.0 - LON_MPC_STEP = 0.2 # first step is 0.2s -MAX_SPEED_ERROR = 2.0 AWARENESS_DECEL = -0.2 # car smoothly decel at .2m/s^2 when user is distracted # lookup tables VS speed to determine min and max accels in cruise @@ -36,9 +33,6 @@ _A_CRUISE_MAX_BP = [0., 6.4, 22.5, 40.] _A_TOTAL_MAX_V = [1.7, 3.2] _A_TOTAL_MAX_BP = [20., 40.] -# 75th percentile -SPEED_PERCENTILE_IDX = 7 - def calc_cruise_accel_limits(v_ego, following): a_cruise_min = interp(v_ego, _A_CRUISE_MIN_BP, _A_CRUISE_MIN_V) @@ -162,8 +156,8 @@ class Planner(): self.mpc1.set_cur_state(self.v_acc_start, self.a_acc_start) self.mpc2.set_cur_state(self.v_acc_start, self.a_acc_start) - self.mpc1.update(pm, sm['carState'], lead_1, v_cruise_setpoint) - self.mpc2.update(pm, sm['carState'], lead_2, v_cruise_setpoint) + self.mpc1.update(pm, sm['carState'], lead_1) + self.mpc2.update(pm, sm['carState'], lead_2) self.choose_solution(v_cruise_setpoint, enabled) diff --git a/selfdrive/controls/plannerd.py b/selfdrive/controls/plannerd.py index b439681a3..44dd1ec62 100755 --- a/selfdrive/controls/plannerd.py +++ b/selfdrive/controls/plannerd.py @@ -1,9 +1,7 @@ #!/usr/bin/env python3 -import gc - from cereal import car from common.params import Params -from common.realtime import set_realtime_priority +from common.realtime import Priority, config_rt_process from selfdrive.swaglog import cloudlog from selfdrive.controls.lib.planner import Planner from selfdrive.controls.lib.vehicle_model import VehicleModel @@ -12,10 +10,8 @@ import cereal.messaging as messaging def plannerd_thread(sm=None, pm=None): - gc.disable() - # start the loop - set_realtime_priority(52) + config_rt_process(2, Priority.CTRL_LOW) cloudlog.info("plannerd is waiting for CarParams") CP = car.CarParams.from_bytes(Params().get("CarParams", block=True)) @@ -27,7 +23,8 @@ def plannerd_thread(sm=None, pm=None): VM = VehicleModel(CP) if sm is None: - sm = messaging.SubMaster(['carState', 'controlsState', 'radarState', 'model', 'liveParameters']) + sm = messaging.SubMaster(['carState', 'controlsState', 'radarState', 'model', 'liveParameters'], + poll=['radarState', 'model']) if pm is None: pm = messaging.PubMaster(['plan', 'liveLongitudinalMpc', 'pathPlan', 'liveMpc']) diff --git a/selfdrive/controls/radard.py b/selfdrive/controls/radard.py index 0ba14dcf2..c2643d76e 100755 --- a/selfdrive/controls/radard.py +++ b/selfdrive/controls/radard.py @@ -7,7 +7,7 @@ import cereal.messaging as messaging from cereal import car from common.numpy_fast import interp from common.params import Params -from common.realtime import Ratekeeper, set_realtime_priority +from common.realtime import Ratekeeper, Priority, set_realtime_priority from selfdrive.config import RADAR_TO_CAMERA from selfdrive.controls.lib.cluster.fastcluster_py import cluster_points_centroid from selfdrive.controls.lib.radar_helpers import Cluster, Track @@ -99,7 +99,7 @@ class RadarD(): self.ready = False - def update(self, frame, sm, rr, has_radar): + def update(self, frame, sm, rr, enable_lead): self.current_time = 1e-9*max([sm.logMonoTime[key] for key in sm.logMonoTime.keys()]) if sm.updated['controlsState']: @@ -166,7 +166,7 @@ class RadarD(): dat.radarState.radarErrors = list(rr.errors) dat.radarState.controlsStateMonoTime = sm.logMonoTime['controlsState'] - if has_radar: + if enable_lead: dat.radarState.leadOne = get_lead(self.v_ego, self.ready, clusters, sm['model'].lead, low_speed_override=True) dat.radarState.leadTwo = get_lead(self.v_ego, self.ready, clusters, sm['model'].leadFuture, low_speed_override=False) return dat @@ -174,7 +174,7 @@ class RadarD(): # fuses camera and radar data for best lead detection def radard_thread(sm=None, pm=None, can_sock=None): - set_realtime_priority(52) + set_realtime_priority(Priority.CTRL_LOW) # wait for stats about the car to come in from controls cloudlog.info("radard is waiting for CarParams") @@ -200,7 +200,8 @@ 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) - has_radar = not CP.radarOffCan + # 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) @@ -211,7 +212,7 @@ def radard_thread(sm=None, pm=None, can_sock=None): sm.update(0) - dat = RD.update(rk.frame, sm, rr, has_radar) + dat = RD.update(rk.frame, sm, rr, enable_lead) dat.radarState.cumLagMs = -rk.remaining*1000. pm.send('radarState', dat) diff --git a/selfdrive/crash.py b/selfdrive/crash.py index 7459f7166..22d6aa068 100644 --- a/selfdrive/crash.py +++ b/selfdrive/crash.py @@ -6,9 +6,9 @@ import capnp from selfdrive.version import version, dirty from selfdrive.swaglog import cloudlog -from common.android import ANDROID +from common.hardware import PC -if os.getenv("NOLOG") or os.getenv("NOCRASH") or not ANDROID: +if os.getenv("NOLOG") or os.getenv("NOCRASH") or PC: def capture_exception(*args, **kwargs): pass diff --git a/selfdrive/debug/cpu_usage_stat.py b/selfdrive/debug/cpu_usage_stat.py index 28cabe6c8..9b5681e89 100755 --- a/selfdrive/debug/cpu_usage_stat.py +++ b/selfdrive/debug/cpu_usage_stat.py @@ -30,8 +30,16 @@ PRINT_INTERVAL = 5 SLEEP_INTERVAL = 0.2 monitored_proc_names = [ - 'ubloxd', 'thermald', 'uploader', 'deleter', 'controlsd', 'plannerd', 'radard', 'mapd', 'loggerd', 'logmessaged', 'tombstoned', - 'logcatd', 'proclogd', 'boardd', 'pandad', './ui', 'ui', 'calibrationd', 'params_learner', 'modeld', 'dmonitoringd', 'dmonitoringmodeld', 'camerad', 'sensord', 'updated', 'gpsd', 'athena', 'locationd', 'paramsd'] + # openpilot procs + 'controlsd', 'locationd', 'loggerd','plannerd', + 'ubloxd', 'thermald', 'uploader', 'deleter', 'radard', 'logmessaged', 'tombstoned', + 'logcatd', 'proclogd', 'boardd', 'pandad', './ui', 'ui', 'calibrationd', 'params_learner', 'modeld', 'dmonitoringd', + 'dmonitoringmodeld', 'camerad', 'sensord', 'updated', 'gpsd', 'athena', 'locationd', 'paramsd', + + # android procs + 'SurfaceFlinger', 'sensors.qcom' +] + cpu_time_names = ['user', 'system', 'children_user', 'children_system'] timer = getattr(time, 'monotonic', time.time) diff --git a/selfdrive/debug/cycle_alerts.py b/selfdrive/debug/cycle_alerts.py index 63b7d758c..45bc8984c 100755 --- a/selfdrive/debug/cycle_alerts.py +++ b/selfdrive/debug/cycle_alerts.py @@ -3,56 +3,65 @@ # pylint: skip-file # type: ignore -import argparse import time import cereal.messaging as messaging -from selfdrive.controls.lib.events import EVENTS, Alert +from selfdrive.car.honda.interface import CarInterface +from selfdrive.controls.lib.events import ET, EVENTS, Events +from selfdrive.controls.lib.alertmanager import AlertManager -def now_millis(): return time.time() * 1000 -ALERTS = [a for _, et in EVENTS.items() for _, a in et.items() if isinstance(a, Alert)] +def cycle_alerts(duration=200, is_metric=False): + alerts = list(EVENTS.keys()) + print(alerts) -#from cereal import car -#ALERTS = [a for a in ALERTS if a.audible_alert == car.CarControl.HUDControl.AudibleAlert.chimeWarningRepeat] - -default_alerts = sorted(ALERTS, key=lambda alert: (alert.alert_size, len(alert.alert_text_2))) - -def cycle_alerts(duration_millis, alerts=None): - if alerts is None: - alerts = default_alerts + CP = CarInterface.get_params("HONDA CIVIC 2016 TOURING") + sm = messaging.SubMaster(['thermal', 'health', 'frame', 'model', 'liveCalibration', + 'dMonitoringState', 'plan', 'pathPlan', 'liveLocationKalman']) controls_state = messaging.pub_sock('controlsState') + thermal = messaging.pub_sock('thermal') idx, last_alert_millis = 0, 0 - alert = alerts[0] + + events = Events() + AM = AlertManager() + + frame = 0 + while 1: - if (now_millis() - last_alert_millis) > duration_millis: - alert = alerts[idx] + if frame % duration == 0: idx = (idx + 1) % len(alerts) - last_alert_millis = now_millis() - print('sending {}'.format(str(alert))) + events.clear() + events.add(alerts[idx]) + + + current_alert_types = [ET.PERMANENT, ET.USER_DISABLE, ET.IMMEDIATE_DISABLE, + ET.SOFT_DISABLE, ET.PRE_ENABLE, ET.NO_ENTRY, + ET.ENABLE, ET.WARNING] + a = events.create_alerts(current_alert_types, [CP, sm, is_metric]) + AM.add_many(frame, a) + AM.process_alerts(frame) dat = messaging.new_message() dat.init('controlsState') - dat.controlsState.alertType = alert.alert_type - 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.alertSound = alert.audible_alert + dat.controlsState.alertText1 = AM.alert_text_1 + dat.controlsState.alertText2 = AM.alert_text_2 + dat.controlsState.alertSize = AM.alert_size + dat.controlsState.alertStatus = AM.alert_status + dat.controlsState.alertBlinkingRate = AM.alert_rate + dat.controlsState.alertType = AM.alert_type + dat.controlsState.alertSound = AM.audible_alert controls_state.send(dat.to_bytes()) + dat = messaging.new_message() + dat.init('thermal') + dat.thermal.started = True + thermal.send(dat.to_bytes()) + + frame += 1 time.sleep(0.01) if __name__ == '__main__': - parser = argparse.ArgumentParser() - parser.add_argument('--duration', type=int, default=1000) - parser.add_argument('--alert-types', nargs='+') - args = parser.parse_args() - alerts = None - if args.alert_types: - alerts = [next(a for a in ALERTS if a.alert_type==alert_type) for alert_type in args.alert_types] - - cycle_alerts(args.duration, alerts=alerts) + cycle_alerts() diff --git a/selfdrive/debug/disable_ecu.py b/selfdrive/debug/disable_ecu.py new file mode 100644 index 000000000..9dace9a68 --- /dev/null +++ b/selfdrive/debug/disable_ecu.py @@ -0,0 +1,40 @@ +#!/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.1, 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/dump.py b/selfdrive/debug/dump.py index cfdb387b9..112ca96a9 100755 --- a/selfdrive/debug/dump.py +++ b/selfdrive/debug/dump.py @@ -61,4 +61,11 @@ if __name__ == "__main__": print("{} = {}".format(".".join(value), item)) print("") else: - print(evt) + try: + print(evt) + except UnicodeDecodeError: + w = evt.which() + s = f"( logMonoTime {evt.logMonoTime} \n {w} = " + s += str(evt.__getattr__(w)) + s += f"\n valid = {evt.valid} )" + print(s) diff --git a/selfdrive/debug/get_fingerprint.py b/selfdrive/debug/get_fingerprint.py index 9ece7d726..2695d457d 100755 --- a/selfdrive/debug/get_fingerprint.py +++ b/selfdrive/debug/get_fingerprint.py @@ -17,6 +17,9 @@ logcan = messaging.sub_sock('can') msgs = {} while True: lc = messaging.recv_sock(logcan, True) + if lc is None: + continue + for c in lc.can: # read also msgs sent by EON on CAN bus 0x80 and filter out the # addr with more than 11 bits diff --git a/selfdrive/debug/uiview.py b/selfdrive/debug/uiview.py old mode 100644 new mode 100755 index 899f932ad..e31d6cf3c --- a/selfdrive/debug/uiview.py +++ b/selfdrive/debug/uiview.py @@ -3,12 +3,12 @@ import time import cereal.messaging as messaging from selfdrive.manager import start_managed_process, kill_managed_process -services = ['controlsState', 'thermal'] # the services needed to be spoofed to start ui offroad -procs = ['camerad', 'ui'] +services = ['controlsState', 'thermal', 'radarState'] # the services needed to be spoofed to start ui offroad +procs = ['camerad', 'ui', 'modeld', 'calibrationd'] [start_managed_process(p) for p in procs] # start needed processes pm = messaging.PubMaster(services) -dat_cs, dat_thermal = [messaging.new_message(s) for s in services] +dat_cs, dat_thermal, dat_radar = [messaging.new_message(s) for s in services] dat_cs.controlsState.rearViewCam = False # ui checks for these two messages dat_thermal.thermal.started = True @@ -16,6 +16,7 @@ try: while True: pm.send('controlsState', dat_cs) pm.send('thermal', dat_thermal) + pm.send('radarState', dat_radar) time.sleep(1 / 100) # continually send, rate doesn't matter for thermal except KeyboardInterrupt: [kill_managed_process(p) for p in procs] diff --git a/selfdrive/locationd/calibration_helpers.py b/selfdrive/locationd/calibration_helpers.py deleted file mode 100644 index c7886d301..000000000 --- a/selfdrive/locationd/calibration_helpers.py +++ /dev/null @@ -1,10 +0,0 @@ -import math - -class Filter: - MIN_SPEED = 7 # m/s (~15.5mph) - MAX_YAW_RATE = math.radians(3) # per second - -class Calibration: - UNCALIBRATED = 0 - CALIBRATED = 1 - INVALID = 2 diff --git a/selfdrive/locationd/calibrationd.py b/selfdrive/locationd/calibrationd.py index a27e284a9..9cda80cfe 100755 --- a/selfdrive/locationd/calibrationd.py +++ b/selfdrive/locationd/calibrationd.py @@ -1,4 +1,10 @@ #!/usr/bin/env python3 +''' +This process finds calibration values. More info on what these calibration values +are can be found here https://github.com/commaai/openpilot/tree/master/common/transformations +While the roll calibration is a real value that can be estimated, here we assume it's zero, +and the image input into the neural network is not corrected for roll. +''' import os import copy @@ -6,177 +12,196 @@ import json import numpy as np import cereal.messaging as messaging from selfdrive.config import Conversions as CV -from selfdrive.locationd.calibration_helpers import Calibration from selfdrive.swaglog import cloudlog from common.params import Params, put_nonblocking from common.transformations.model import model_height -from common.transformations.camera import view_frame_from_device_frame, get_view_frame_from_road_frame, \ - get_calib_from_vp, vp_from_rpy, H, W, FOCAL +from common.transformations.camera import get_view_frame_from_road_frame +from common.transformations.orientation import rot_from_euler, euler_from_rot MIN_SPEED_FILTER = 15 * CV.MPH_TO_MS MAX_VEL_ANGLE_STD = np.radians(0.25) MAX_YAW_RATE_FILTER = np.radians(2) # per second -# This is all 20Hz, blocks needed for efficiency +# This is at model frequency, blocks needed for efficiency +SMOOTH_CYCLES = 400 BLOCK_SIZE = 100 -INPUTS_NEEDED = 5 # allow to update VP every so many frames +INPUTS_NEEDED = 5 # Minimum blocks needed for valid calibration INPUTS_WANTED = 50 # We want a little bit more than we need for stability -WRITE_CYCLES = 10 # write every 1000 cycles -VP_INIT = np.array([W/2., H/2.]) +MAX_ALLOWED_SPREAD = np.radians(2) +RPY_INIT = np.array([0.0,0.0,0.0]) # These values are needed to accomodate biggest modelframe -VP_VALIDITY_CORNERS = np.array([[W//2 - 63, 300], [W//2 + 63, 520]]) +PITCH_LIMITS = np.array([-0.09074112085129739, 0.14907572052989657]) +YAW_LIMITS = np.array([-0.06912048084718224, 0.06912048084718235]) DEBUG = os.getenv("DEBUG") is not None -def is_calibration_valid(vp): - return vp[0] > VP_VALIDITY_CORNERS[0, 0] and vp[0] < VP_VALIDITY_CORNERS[1, 0] and \ - vp[1] > VP_VALIDITY_CORNERS[0, 1] and vp[1] < VP_VALIDITY_CORNERS[1, 1] +class Calibration: + UNCALIBRATED = 0 + CALIBRATED = 1 + INVALID = 2 -def sanity_clip(vp): - if np.isnan(vp).any(): - vp = VP_INIT - return np.array([np.clip(vp[0], VP_VALIDITY_CORNERS[0, 0] - 5, VP_VALIDITY_CORNERS[1, 0] + 5), - np.clip(vp[1], VP_VALIDITY_CORNERS[0, 1] - 5, VP_VALIDITY_CORNERS[1, 1] + 5)]) +def is_calibration_valid(rpy): + return (PITCH_LIMITS[0] < rpy[1] < PITCH_LIMITS[1]) and (YAW_LIMITS[0] < rpy[2] < YAW_LIMITS[1]) -def intrinsics_from_vp(vp): - return np.array([ - [FOCAL, 0., vp[0]], - [ 0., FOCAL, vp[1]], - [ 0., 0., 1.]]) +def sanity_clip(rpy): + if np.isnan(rpy).any(): + rpy = RPY_INIT + return np.array([rpy[0], + np.clip(rpy[1], PITCH_LIMITS[0] - .005, PITCH_LIMITS[1] + .005), + np.clip(rpy[2], YAW_LIMITS[0] - .005, YAW_LIMITS[1] + .005)]) class Calibrator(): def __init__(self, param_put=False): self.param_put = param_put - self.vp = copy.copy(VP_INIT) - self.vps = np.zeros((INPUTS_WANTED, 2)) - self.idx = 0 - self.block_idx = 0 - self.valid_blocks = 0 - self.cal_status = Calibration.UNCALIBRATED - self.just_calibrated = False - self.v_ego = 0 - # Read calibration - if param_put: - calibration_params = Params().get("CalibrationParams") - else: - calibration_params = None - if calibration_params: + # Read saved calibration + calibration_params = Params().get("CalibrationParams") + + rpy_init = RPY_INIT + valid_blocks = 0 + + if param_put and calibration_params: try: calibration_params = json.loads(calibration_params) - self.vp = vp_from_rpy(calibration_params["calib_radians"]) - if not np.isfinite(self.vp).all(): - self.vp = copy.copy(VP_INIT) - self.vps = np.tile(self.vp, (INPUTS_WANTED, 1)) - self.valid_blocks = calibration_params['valid_blocks'] - if not np.isfinite(self.valid_blocks) or self.valid_blocks < 0: - self.valid_blocks = 0 - self.update_status() + rpy_init = calibration_params["calib_radians"] + valid_blocks = calibration_params['valid_blocks'] except Exception: cloudlog.exception("CalibrationParams file found but error encountered") + self.reset(rpy_init, valid_blocks) + self.update_status() + + def reset(self, rpy_init=RPY_INIT, valid_blocks=0, smooth_from=None): + if not np.isfinite(rpy_init).all(): + self.rpy = copy.copy(RPY_INIT) + else: + self.rpy = rpy_init + if not np.isfinite(valid_blocks) or valid_blocks < 0: + self.valid_blocks = 0 + else: + self.valid_blocks = valid_blocks + self.rpys = np.tile(self.rpy, (INPUTS_WANTED, 1)) + + self.idx = 0 + self.block_idx = 0 + self.v_ego = 0 + + if smooth_from is None: + self.old_rpy = RPY_INIT + self.old_rpy_weight = 0.0 + else: + self.old_rpy = smooth_from + self.old_rpy_weight = 1.0 def update_status(self): - start_status = self.cal_status + if self.valid_blocks > 0: + max_rpy_calib = np.array(np.max(self.rpys[:self.valid_blocks], axis=0)) + min_rpy_calib = np.array(np.min(self.rpys[:self.valid_blocks], axis=0)) + self.calib_spread = np.abs(max_rpy_calib - min_rpy_calib) + else: + self.calib_spread = np.zeros(3) + if self.valid_blocks < INPUTS_NEEDED: self.cal_status = Calibration.UNCALIBRATED + elif is_calibration_valid(self.rpy): + self.cal_status = Calibration.CALIBRATED else: - self.cal_status = Calibration.CALIBRATED if is_calibration_valid(self.vp) else Calibration.INVALID - end_status = self.cal_status + self.cal_status = Calibration.INVALID - self.just_calibrated = False - if start_status == Calibration.UNCALIBRATED and end_status != Calibration.UNCALIBRATED: - self.just_calibrated = True + # If spread is too high, assume mounting was changed and reset to last block. + # Make the transition smooth. Abrupt transistion are not good foor feedback loop through supercombo model. + if max(self.calib_spread) > MAX_ALLOWED_SPREAD and self.cal_status == Calibration.CALIBRATED: + self.reset(self.rpys[self.block_idx - 1], valid_blocks=INPUTS_NEEDED, smooth_from=self.rpy) + + write_this_cycle = (self.idx == 0) and (self.block_idx % (INPUTS_WANTED//5) == 5) + if self.param_put and write_this_cycle: + # TODO: this should use the liveCalibration struct from cereal + cal_params = {"calib_radians": list(self.rpy), + "valid_blocks": int(self.valid_blocks)} + put_nonblocking("CalibrationParams", json.dumps(cal_params).encode('utf8')) def handle_v_ego(self, v_ego): self.v_ego = v_ego + def get_smooth_rpy(self): + if self.old_rpy_weight > 0: + return self.old_rpy_weight * self.old_rpy + (1.0 - self.old_rpy_weight) * self.rpy + else: + return self.rpy + def handle_cam_odom(self, trans, rot, trans_std, rot_std): + self.old_rpy_weight = min(0.0, self.old_rpy_weight - 1/SMOOTH_CYCLES) + straight_and_fast = ((self.v_ego > MIN_SPEED_FILTER) and (trans[0] > MIN_SPEED_FILTER) and (abs(rot[2]) < MAX_YAW_RATE_FILTER)) certain_if_calib = ((np.arctan2(trans_std[1], trans[0]) < MAX_VEL_ANGLE_STD) or (self.valid_blocks < INPUTS_NEEDED)) if straight_and_fast and certain_if_calib: - # intrinsics are not eon intrinsics, since this is calibrated frame - intrinsics = intrinsics_from_vp(self.vp) - new_vp = intrinsics.dot(view_frame_from_device_frame.dot(trans)) - new_vp = new_vp[:2]/new_vp[2] - new_vp = sanity_clip(new_vp) + observed_rpy = np.array([0, + -np.arctan2(trans[2], trans[0]), + np.arctan2(trans[1], trans[0])]) + new_rpy = euler_from_rot(rot_from_euler(self.get_smooth_rpy()).dot(rot_from_euler(observed_rpy))) + new_rpy = sanity_clip(new_rpy) - self.vps[self.block_idx] = (self.idx*self.vps[self.block_idx] + (BLOCK_SIZE - self.idx) * new_vp) / float(BLOCK_SIZE) + self.rpys[self.block_idx] = (self.idx*self.rpys[self.block_idx] + (BLOCK_SIZE - self.idx) * new_rpy) / float(BLOCK_SIZE) self.idx = (self.idx + 1) % BLOCK_SIZE if self.idx == 0: self.block_idx += 1 self.valid_blocks = max(self.block_idx, self.valid_blocks) self.block_idx = self.block_idx % INPUTS_WANTED if self.valid_blocks > 0: - self.vp = np.mean(self.vps[:self.valid_blocks], axis=0) + self.rpy = np.mean(self.rpys[:self.valid_blocks], axis=0) + + self.update_status() - if self.param_put and ((self.idx == 0 and self.block_idx == 0) or self.just_calibrated): - calib = get_calib_from_vp(self.vp) - cal_params = {"calib_radians": list(calib), - "valid_blocks": self.valid_blocks} - put_nonblocking("CalibrationParams", json.dumps(cal_params).encode('utf8')) - return new_vp + return new_rpy else: return None def send_data(self, pm): - calib = get_calib_from_vp(self.vp) - if self.valid_blocks > 0: - max_vp_calib = np.array(get_calib_from_vp(np.max(self.vps[:self.valid_blocks], axis=0))) - min_vp_calib = np.array(get_calib_from_vp(np.min(self.vps[:self.valid_blocks], axis=0))) - calib_spread = np.abs(max_vp_calib - min_vp_calib) - else: - calib_spread = np.zeros(3) - extrinsic_matrix = get_view_frame_from_road_frame(0, calib[1], calib[2], model_height) + smooth_rpy = self.get_smooth_rpy() + extrinsic_matrix = get_view_frame_from_road_frame(0, smooth_rpy[1], smooth_rpy[2], model_height) cal_send = messaging.new_message('liveCalibration') cal_send.liveCalibration.validBlocks = self.valid_blocks cal_send.liveCalibration.calStatus = self.cal_status cal_send.liveCalibration.calPerc = min(100 * (self.valid_blocks * BLOCK_SIZE + self.idx) // (INPUTS_NEEDED * BLOCK_SIZE), 100) cal_send.liveCalibration.extrinsicMatrix = [float(x) for x in extrinsic_matrix.flatten()] - cal_send.liveCalibration.rpyCalib = [float(x) for x in calib] - cal_send.liveCalibration.rpyCalibSpread = [float(x) for x in calib_spread] + cal_send.liveCalibration.rpyCalib = [float(x) for x in smooth_rpy] + cal_send.liveCalibration.rpyCalibSpread = [float(x) for x in self.calib_spread] pm.send('liveCalibration', cal_send) def calibrationd_thread(sm=None, pm=None): if sm is None: - sm = messaging.SubMaster(['cameraOdometry', 'carState']) + sm = messaging.SubMaster(['cameraOdometry', 'carState'], poll=['cameraOdometry']) if pm is None: pm = messaging.PubMaster(['liveCalibration']) calibrator = Calibrator(param_put=True) - send_counter = 0 while 1: - sm.update() - - # if no inputs still publish calibration - if not sm.updated['carState'] and not sm.updated['cameraOdometry']: - calibrator.send_data(pm) - continue - - if sm.updated['carState']: - calibrator.handle_v_ego(sm['carState'].vEgo) - if send_counter % 25 == 0: - calibrator.send_data(pm) - send_counter += 1 + timeout = 0 if sm.frame == -1 else 100 + sm.update(timeout) if sm.updated['cameraOdometry']: - new_vp = calibrator.handle_cam_odom(sm['cameraOdometry'].trans, - sm['cameraOdometry'].rot, - sm['cameraOdometry'].transStd, - sm['cameraOdometry'].rotStd) + calibrator.handle_v_ego(sm['carState'].vEgo) + new_rpy = calibrator.handle_cam_odom(sm['cameraOdometry'].trans, + sm['cameraOdometry'].rot, + sm['cameraOdometry'].transStd, + sm['cameraOdometry'].rotStd) - if DEBUG and new_vp is not None: - print('got new vp', new_vp) + if DEBUG and new_rpy is not None: + print('got new rpy', new_rpy) + + # 4Hz driven by cameraOdometry + if sm.frame % 5 == 0: + calibrator.send_data(pm) def main(sm=None, pm=None): diff --git a/selfdrive/locationd/paramsd.py b/selfdrive/locationd/paramsd.py index ad820b4ea..1d1d35331 100755 --- a/selfdrive/locationd/paramsd.py +++ b/selfdrive/locationd/paramsd.py @@ -13,8 +13,6 @@ from selfdrive.swaglog import cloudlog KalmanStatus = log.LiveLocationKalman.Status -CARSTATE_DECIMATION = 5 - class ParamsLearner: def __init__(self, CP, steer_ratio, stiffness_factor, angle_offset): @@ -32,7 +30,6 @@ class ParamsLearner: self.speed = 0 self.steering_pressed = False self.steering_angle = 0 - self.carstate_counter = 0 self.valid = True @@ -51,18 +48,16 @@ class ParamsLearner: self.kf.predict_and_observe(t, ObservationKind.ANGLE_OFFSET_FAST, np.array([[[0]]])) elif which == 'carState': - self.carstate_counter += 1 - if self.carstate_counter % CARSTATE_DECIMATION == 0: - self.steering_angle = msg.steeringAngle - self.steering_pressed = msg.steeringPressed - self.speed = msg.vEgo + self.steering_angle = msg.steeringAngle + self.steering_pressed = msg.steeringPressed + self.speed = msg.vEgo - in_linear_region = abs(self.steering_angle) < 45 or not self.steering_pressed - self.active = self.speed > 5 and in_linear_region + in_linear_region = abs(self.steering_angle) < 45 or not self.steering_pressed + self.active = self.speed > 5 and in_linear_region - if self.active: - self.kf.predict_and_observe(t, ObservationKind.STEER_ANGLE, np.array([[[math.radians(msg.steeringAngle)]]])) - self.kf.predict_and_observe(t, ObservationKind.ROAD_FRAME_X_SPEED, np.array([[[self.speed]]])) + if self.active: + self.kf.predict_and_observe(t, ObservationKind.STEER_ANGLE, np.array([[[math.radians(msg.steeringAngle)]]])) + self.kf.predict_and_observe(t, ObservationKind.ROAD_FRAME_X_SPEED, np.array([[[self.speed]]])) if not self.active: # Reset time when stopped so uncertainty doesn't grow @@ -72,7 +67,7 @@ class ParamsLearner: def main(sm=None, pm=None): if sm is None: - sm = messaging.SubMaster(['liveLocationKalman', 'carState']) + sm = messaging.SubMaster(['liveLocationKalman', 'carState'], poll=['liveLocationKalman']) if pm is None: pm = messaging.PubMaster(['liveParameters']) @@ -111,12 +106,11 @@ def main(sm=None, pm=None): sm.update() for which, updated in sm.updated.items(): - if not updated: - continue - t = sm.logMonoTime[which] * 1e-9 - learner.handle_log(t, which, sm[which]) + if updated: + t = sm.logMonoTime[which] * 1e-9 + learner.handle_log(t, which, sm[which]) - if sm.updated['carState'] and (learner.carstate_counter % CARSTATE_DECIMATION == 0): + if sm.updated['liveLocationKalman']: msg = messaging.new_message('liveParameters') msg.logMonoTime = sm.logMonoTime['carState'] @@ -135,7 +129,7 @@ def main(sm=None, pm=None): min_sr <= msg.liveParameters.steerRatio <= max_sr, )) - if learner.carstate_counter % 6000 == 0: # once a minute + if sm.frame % 1200 == 0: # once a minute params = { 'carFingerprint': CP.carFingerprint, 'steerRatio': msg.liveParameters.steerRatio, diff --git a/selfdrive/locationd/ublox_msg.cc b/selfdrive/locationd/ublox_msg.cc index 0bc8c0e6f..ce4e4dcac 100644 --- a/selfdrive/locationd/ublox_msg.cc +++ b/selfdrive/locationd/ublox_msg.cc @@ -193,10 +193,8 @@ inline bool UbloxMsgParser::valid_so_far() { kj::Array UbloxMsgParser::gen_solution() { nav_pvt_msg *msg = (nav_pvt_msg *)&msg_parse_buf[UBLOX_HEADER_SIZE]; - capnp::MallocMessageBuilder msg_builder; - cereal::Event::Builder event = msg_builder.initRoot(); - event.setLogMonoTime(nanos_since_boot()); - auto gpsLoc = event.initGpsLocationExternal(); + MessageBuilder msg_builder; + auto gpsLoc = msg_builder.initEvent().initGpsLocationExternal(); gpsLoc.setSource(cereal::GpsLocationData::SensorSource::UBLOX); gpsLoc.setFlags(msg->flags); gpsLoc.setLatitude(msg->lat * 1e-07); @@ -236,11 +234,8 @@ kj::Array UbloxMsgParser::gen_raw() { return kj::Array(); } rxm_raw_msg_extra *measurements = (rxm_raw_msg_extra *)&msg_parse_buf[UBLOX_HEADER_SIZE + sizeof(rxm_raw_msg)]; - capnp::MallocMessageBuilder msg_builder; - cereal::Event::Builder event = msg_builder.initRoot(); - event.setLogMonoTime(nanos_since_boot()); - auto gnss = event.initUbloxGnss(); - auto mr = gnss.initMeasurementReport(); + MessageBuilder msg_builder; + auto mr = msg_builder.initEvent().initUbloxGnss().initMeasurementReport(); mr.setRcvTow(msg->rcvTow); mr.setGpsWeek(msg->week); mr.setLeapSeconds(msg->leapS); @@ -295,11 +290,8 @@ kj::Array UbloxMsgParser::gen_nav_data() { nav_frame_buffer[msg->gnssId][msg->svid][subframeId] = words; if(nav_frame_buffer[msg->gnssId][msg->svid].size() == 5) { EphemerisData ephem_data(msg->svid, nav_frame_buffer[msg->gnssId][msg->svid]); - capnp::MallocMessageBuilder msg_builder; - cereal::Event::Builder event = msg_builder.initRoot(); - event.setLogMonoTime(nanos_since_boot()); - auto gnss = event.initUbloxGnss(); - auto eph = gnss.initEphemeris(); + MessageBuilder msg_builder; + auto eph = msg_builder.initEvent().initUbloxGnss().initEphemeris(); eph.setSvId(ephem_data.svId); eph.setToc(ephem_data.toc); eph.setGpsWeek(ephem_data.gpsWeek); @@ -343,11 +335,8 @@ kj::Array UbloxMsgParser::gen_nav_data() { kj::Array UbloxMsgParser::gen_mon_hw() { mon_hw_msg *msg = (mon_hw_msg *)&msg_parse_buf[UBLOX_HEADER_SIZE]; - capnp::MallocMessageBuilder msg_builder; - cereal::Event::Builder event = msg_builder.initRoot(); - event.setLogMonoTime(nanos_since_boot()); - auto gnss = event.initUbloxGnss(); - auto hwStatus = gnss.initHwStatus(); + MessageBuilder msg_builder; + auto hwStatus = msg_builder.initEvent().initUbloxGnss().initHwStatus(); hwStatus.setNoisePerMS(msg->noisePerMS); hwStatus.setAgcCnt(msg->agcCnt); hwStatus.setAStatus((cereal::UbloxGnss::HwStatus::AntennaSupervisorState) msg->aStatus); diff --git a/selfdrive/locationd/ubloxd_test.cc b/selfdrive/locationd/ubloxd_test.cc index 8653cedea..d1118e4d2 100644 --- a/selfdrive/locationd/ubloxd_test.cc +++ b/selfdrive/locationd/ubloxd_test.cc @@ -47,15 +47,11 @@ Message * poll_ubloxraw_msg(Poller * poller) { size_t consuming = min(len - consumed, 128); if(consumed < len) { // create message - capnp::MallocMessageBuilder msg_builder; - cereal::Event::Builder event = msg_builder.initRoot(); - event.setLogMonoTime(nanos_since_boot()); - - auto ublox_raw = event.initUbloxRaw(consuming); + MessageBuilder msg_builder; + auto ublox_raw = msg_builder.initEvent().initUbloxRaw(consuming); memcpy(ublox_raw.begin(), (void *)(data + consumed), consuming); - auto words = capnp::messageToFlatArray(msg_builder); - auto bytes = words.asBytes(); + auto bytes = msg_builder.toBytes(); Message * msg = new ZMQMessage(); msg->init((char*)bytes.begin(), bytes.size()); diff --git a/selfdrive/logcatd/SConscript b/selfdrive/logcatd/SConscript index 67ad67300..7f87cbadd 100644 --- a/selfdrive/logcatd/SConscript +++ b/selfdrive/logcatd/SConscript @@ -1,2 +1,6 @@ -Import('env', 'cereal', 'messaging') -env.Program('logcatd.cc', LIBS=[cereal, messaging, 'cutils', 'zmq', 'czmq', 'capnp', 'kj']) +Import('env', 'cereal', 'messaging', 'arch') + +if arch == "aarch64": + env.Program('logcatd', 'logcatd_android.cc', LIBS=[cereal, messaging, 'cutils', 'zmq', 'czmq', 'capnp', 'kj']) +else: + env.Program('logcatd', 'logcatd_systemd.cc', LIBS=[cereal, messaging, 'zmq', 'capnp', 'kj', 'systemd', 'json11']) diff --git a/selfdrive/logcatd/logcatd.cc b/selfdrive/logcatd/logcatd_android.cc similarity index 88% rename from selfdrive/logcatd/logcatd.cc rename to selfdrive/logcatd/logcatd_android.cc index 181e29df1..9a1753b1d 100644 --- a/selfdrive/logcatd/logcatd.cc +++ b/selfdrive/logcatd/logcatd_android.cc @@ -40,10 +40,8 @@ int main() { continue; } - capnp::MallocMessageBuilder msg; - cereal::Event::Builder event = msg.initRoot(); - event.setLogMonoTime(nanos_since_boot()); - auto androidEntry = event.initAndroidLog(); + MessageBuilder msg; + auto androidEntry = msg.initEvent().initAndroidLog(); androidEntry.setId(log_msg.id()); androidEntry.setTs(entry.tv_sec * 1000000000ULL + entry.tv_nsec); androidEntry.setPriority(entry.priority); diff --git a/selfdrive/logcatd/logcatd_systemd.cc b/selfdrive/logcatd/logcatd_systemd.cc new file mode 100644 index 000000000..adc187914 --- /dev/null +++ b/selfdrive/logcatd/logcatd_systemd.cc @@ -0,0 +1,71 @@ +#include +#include +#include +#include + +#include "json11.hpp" +#include + +#include "common/timing.h" +#include "messaging.hpp" + +int main(int argc, char *argv[]) { + PubMaster pm({"androidLog"}); + + sd_journal *journal; + assert(sd_journal_open(&journal, 0) >= 0); + assert(sd_journal_get_fd(journal) >= 0); // needed so sd_journal_wait() works properly if files rotate + assert(sd_journal_seek_tail(journal) >= 0); + + int r; + while (true) { + r = sd_journal_next(journal); + assert(r >= 0); + + // Wait for new message if we didn't receive anything + if (r == 0){ + do { + r = sd_journal_wait(journal, (uint64_t)-1); + assert(r >= 0); + } while (r == SD_JOURNAL_NOP); + + r = sd_journal_next(journal); + assert(r >= 0); + + if (r == 0) continue; // Try again if we still didn't get anything + } + + uint64_t timestamp = 0; + r = sd_journal_get_realtime_usec(journal, ×tamp); + assert(r >= 0); + + const void *data; + size_t length; + std::map kv; + + SD_JOURNAL_FOREACH_DATA(journal, data, length){ + std::string str((char*)data, length); + + // Split "KEY=VALUE"" on "=" and put in map + std::size_t found = str.find("="); + if (found != std::string::npos){ + kv[str.substr(0, found)] = str.substr(found + 1, std::string::npos); + } + } + + MessageBuilder msg; + + // Build message + auto androidEntry = msg.initEvent().initAndroidLog(); + androidEntry.setTs(timestamp); + androidEntry.setMessage(json11::Json(kv).dump()); + if (kv.count("_PID")) androidEntry.setPid(std::atoi(kv["_PID"].c_str())); + if (kv.count("PRIORITY")) androidEntry.setPriority(std::atoi(kv["PRIORITY"].c_str())); + if (kv.count("SYSLOG_IDENTIFIER")) androidEntry.setTag(kv["SYSLOG_IDENTIFIER"]); + + pm.send("androidLog", msg); + } + + sd_journal_close(journal); + return 0; +} diff --git a/selfdrive/loggerd/config.py b/selfdrive/loggerd/config.py index 0ee6fa671..f859e8cb4 100644 --- a/selfdrive/loggerd/config.py +++ b/selfdrive/loggerd/config.py @@ -6,24 +6,26 @@ if os.environ.get('LOGGERD_ROOT', False): else: ROOT = '/data/media/0/realdata/' + +CAMERA_FPS = 20 SEGMENT_LENGTH = 60 def get_available_percent(default=None): - try: - statvfs = os.statvfs(ROOT) - available_percent = 100.0 * statvfs.f_bavail / statvfs.f_blocks - except OSError: - available_percent = default + try: + statvfs = os.statvfs(ROOT) + available_percent = 100.0 * statvfs.f_bavail / statvfs.f_blocks + except OSError: + available_percent = default - return available_percent + return available_percent def get_available_bytes(default=None): - try: - statvfs = os.statvfs(ROOT) - available_bytes = statvfs.f_bavail * statvfs.f_frsize - except OSError: - available_bytes = default + try: + statvfs = os.statvfs(ROOT) + available_bytes = statvfs.f_bavail * statvfs.f_frsize + except OSError: + available_bytes = default - return available_bytes + return available_bytes diff --git a/selfdrive/loggerd/encoder.c b/selfdrive/loggerd/encoder.c index 297f65cf8..4939a4853 100644 --- a/selfdrive/loggerd/encoder.c +++ b/selfdrive/loggerd/encoder.c @@ -474,17 +474,16 @@ int encoder_encode_frame(EncoderState *s, // this sometimes freezes... put it outside the encoder lock so we can still trigger rotates... // THIS IS A REALLY BAD IDEA, but apparently the race has to happen 30 times to trigger this - pthread_mutex_unlock(&s->lock); + //pthread_mutex_unlock(&s->lock); OMX_BUFFERHEADERTYPE* in_buf = queue_pop(&s->free_in); - pthread_mutex_lock(&s->lock); - - if (s->rotating) { - encoder_close(s); - encoder_open(s, s->next_path); - s->segment = s->next_segment; - s->rotating = false; - } + //pthread_mutex_lock(&s->lock); + // if (s->rotating) { + // encoder_close(s); + // encoder_open(s, s->next_path); + // s->segment = s->next_segment; + // s->rotating = false; + // } int ret = s->counter; uint8_t *in_buf_ptr = in_buf->pBuffer; @@ -638,6 +637,7 @@ void encoder_close(EncoderState *s) { if (s->remuxing) { av_write_trailer(s->ofmt_ctx); + avcodec_free_context(&s->codec_ctx); avio_closep(&s->ofmt_ctx->pb); avformat_free_context(s->ofmt_ctx); } else { diff --git a/selfdrive/loggerd/logger.cc b/selfdrive/loggerd/logger.cc index e0d0a7365..0cec20f68 100644 --- a/selfdrive/loggerd/logger.cc +++ b/selfdrive/loggerd/logger.cc @@ -12,22 +12,17 @@ #include #include +#include "messaging.hpp" #include "common/swaglog.h" #include "logger.h" -#include -#include "cereal/gen/cpp/log.capnp.h" - static void log_sentinel(LoggerState *s, cereal::Sentinel::SentinelType type) { - capnp::MallocMessageBuilder msg; - auto event = msg.initRoot(); - event.setLogMonoTime(nanos_since_boot()); - auto sen = event.initSentinel(); + MessageBuilder msg; + auto sen = msg.initEvent().initSentinel(); sen.setType(type); - auto words = capnp::messageToFlatArray(msg); - auto bytes = words.asBytes(); + auto bytes = msg.toBytes(); logger_log(s, bytes.begin(), bytes.size(), true); } diff --git a/selfdrive/loggerd/loggerd.cc b/selfdrive/loggerd/loggerd.cc index 9cc3043a7..c7487677a 100644 --- a/selfdrive/loggerd/loggerd.cc +++ b/selfdrive/loggerd/loggerd.cc @@ -10,6 +10,7 @@ #include #include #include +#include #include #include @@ -51,10 +52,21 @@ #include "cereal/gen/cpp/log.capnp.h" +#define CAM_IDX_FCAM 0 +#define CAM_IDX_DCAM 1 +#define CAM_IDX_ECAM 2 + #define CAMERA_FPS 20 #define SEGMENT_LENGTH 60 + +#define MAIN_BITRATE 5000000 +#ifndef QCOM2 +#define DCAM_BITRATE 2500000 +#else +#define DCAM_BITRATE MAIN_BITRATE +#endif + #define LOG_ROOT "/data/media/0/realdata" -#define ENABLE_LIDAR 0 #define RAW_CLIP_LENGTH 100 // 5 seconds at 20fps #define RAW_CLIP_FREQUENCY (randrange(61, 8*60)) // once every ~4 minutes @@ -84,21 +96,34 @@ struct LoggerdState { uint32_t last_frame_id; uint32_t rotate_last_frame_id; int rotate_segment; + int rotate_seq_id; + + pthread_mutex_t rotate_lock; + int num_encoder; + int should_close; + int finish_close; }; LoggerdState s; #ifndef DISABLE_ENCODER -void encoder_thread(bool is_streaming, bool raw_clips, bool front) { +void encoder_thread(bool is_streaming, bool raw_clips, int cam_idx) { int err; - if (front) { - std::vector value = read_db_bytes("RecordFront"); - if (value.size() == 0 || value[0] != '1') return; + // 0:f, 1:d, 2:e + if (cam_idx == CAM_IDX_DCAM) { + // TODO: add this back + #ifndef QCOM2 + if (!read_db_bool("RecordFront")) return; LOGW("recording front camera"); - + #endif set_thread_name("FrontCameraEncoder"); - } else { + } else if (cam_idx == CAM_IDX_FCAM) { set_thread_name("RearCameraEncoder"); + } else if (cam_idx == CAM_IDX_ECAM) { + set_thread_name("WideCameraEncoder"); + } else { + LOGE("unexpected camera index provided"); + assert(false); } VisionStream stream; @@ -108,20 +133,27 @@ void encoder_thread(bool is_streaming, bool raw_clips, bool front) { EncoderState encoder_alt; bool has_encoder_alt = false; + pthread_mutex_lock(&s.rotate_lock); + int my_idx = s.num_encoder; + s.num_encoder += 1; + pthread_mutex_unlock(&s.rotate_lock); + int encoder_segment = -1; int cnt = 0; - PubSocket *idx_sock = PubSocket::create(s.ctx, front ? "frontEncodeIdx" : "encodeIdx"); + PubSocket *idx_sock = PubSocket::create(s.ctx, cam_idx == CAM_IDX_DCAM ? "frontEncodeIdx" : (cam_idx == CAM_IDX_ECAM ? "wideEncodeIdx" : "encodeIdx")); assert(idx_sock != NULL); LoggerHandle *lh = NULL; while (!do_exit) { VisionStreamBufs buf_info; - if (front) { + if (cam_idx == CAM_IDX_DCAM) { err = visionstream_init(&stream, VISION_STREAM_YUV_FRONT, false, &buf_info); - } else { + } else if (cam_idx == CAM_IDX_FCAM) { err = visionstream_init(&stream, VISION_STREAM_YUV, false, &buf_info); + } else if (cam_idx == CAM_IDX_ECAM) { + err = visionstream_init(&stream, VISION_STREAM_YUV_WIDE, false, &buf_info); } if (err != 0) { LOGD("visionstream connect fail"); @@ -131,11 +163,11 @@ void encoder_thread(bool is_streaming, bool raw_clips, bool front) { if (!encoder_inited) { LOGD("encoder init %dx%d", buf_info.width, buf_info.height); - encoder_init(&encoder, front ? "dcamera.hevc" : "fcamera.hevc", buf_info.width, buf_info.height, CAMERA_FPS, front ? 2500000 : 5000000, true, false); + encoder_init(&encoder, cam_idx == CAM_IDX_DCAM ? "dcamera.hevc" : (cam_idx == CAM_IDX_ECAM ? "ecamera.hevc" : "fcamera.hevc"), buf_info.width, buf_info.height, CAMERA_FPS, cam_idx == CAM_IDX_DCAM ? DCAM_BITRATE:MAIN_BITRATE, true, false); #ifndef QCOM2 // TODO: fix qcamera on tici - if (!front) { + if (cam_idx == CAM_IDX_FCAM) { encoder_init(&encoder_alt, "qcamera.ts", 480, 360, CAMERA_FPS, 128000, false, true); has_encoder_alt = true; } @@ -176,19 +208,20 @@ void encoder_thread(bool is_streaming, bool raw_clips, bool front) { uint8_t *v = u + (buf_info.width/2)*(buf_info.height/2); { + // all the rotation stuff bool should_rotate = false; std::unique_lock lk(s.lock); - if (!front) { + if (cam_idx == CAM_IDX_FCAM) { // TODO: should wait for three cameras on tici? // wait if log camera is older on back camera while ( extra.frame_id > s.last_frame_id //if the log camera is older, wait for it to catch up. && (extra.frame_id-s.last_frame_id) < 8 // but if its too old then there probably was a discontinuity (visiond restarted) && !do_exit) { s.cv.wait(lk); } - should_rotate = extra.frame_id > s.rotate_last_frame_id && encoder_segment < s.rotate_segment; + should_rotate = extra.frame_id > s.rotate_last_frame_id && encoder_segment < s.rotate_segment && s.rotate_seq_id == my_idx; } else { // front camera is best effort - should_rotate = encoder_segment < s.rotate_segment; + should_rotate = encoder_segment < s.rotate_segment && s.rotate_seq_id == my_idx; } if (do_exit) break; @@ -197,6 +230,8 @@ void encoder_thread(bool is_streaming, bool raw_clips, bool front) { LOG("rotate encoder to %s", s.segment_path); encoder_rotate(&encoder, s.segment_path, s.rotate_segment); + s.rotate_seq_id = (my_idx + 1) % s.num_encoder; + if (has_encoder_alt) { encoder_rotate(&encoder_alt, s.segment_path, s.rotate_segment); } @@ -211,8 +246,43 @@ void encoder_thread(bool is_streaming, bool raw_clips, bool front) { } lh = logger_get_handle(&s.logger); } - } + if (encoder.rotating) { + pthread_mutex_lock(&s.rotate_lock); + s.should_close += 1; + pthread_mutex_unlock(&s.rotate_lock); + + while(s.should_close > 0 && s.should_close < s.num_encoder) { + // printf("%d waiting for others to reach close, %d/%d \n", my_idx, s.should_close, s.num_encoder); + s.cv.wait(lk); + } + + pthread_mutex_lock(&s.rotate_lock); + if (s.should_close == s.num_encoder) { + s.should_close = 1 - s.num_encoder; + } else { + s.should_close += 1; + } + encoder_close(&encoder); + encoder_open(&encoder, encoder.next_path); + encoder.segment = encoder.next_segment; + encoder.rotating = false; + if (has_encoder_alt) { + encoder_close(&encoder_alt); + encoder_open(&encoder_alt, encoder_alt.next_path); + encoder_alt.segment = encoder_alt.next_segment; + encoder_alt.rotating = false; + } + s.finish_close += 1; + pthread_mutex_unlock(&s.rotate_lock); + + while(s.finish_close > 0 && s.finish_close < s.num_encoder) { + // printf("%d waiting for others to actually close, %d/%d \n", my_idx, s.finish_close, s.num_encoder); + s.cv.wait(lk); + } + s.finish_close = 0; + } + } { // encode hevc int out_segment = -1; @@ -220,7 +290,6 @@ void encoder_thread(bool is_streaming, bool raw_clips, bool front) { y, u, v, buf_info.width, buf_info.height, &out_segment, &extra); - if (has_encoder_alt) { int out_segment_alt = -1; encoder_encode_frame(&encoder_alt, @@ -230,18 +299,20 @@ void encoder_thread(bool is_streaming, bool raw_clips, bool front) { } // publish encode index - capnp::MallocMessageBuilder msg; - cereal::Event::Builder event = msg.initRoot(); - event.setLogMonoTime(nanos_since_boot()); - auto eidx = event.initEncodeIdx(); + MessageBuilder msg; + auto eidx = msg.initEvent().initEncodeIdx(); eidx.setFrameId(extra.frame_id); - eidx.setType(front ? cereal::EncodeIndex::Type::FRONT : cereal::EncodeIndex::Type::FULL_H_E_V_C); +#ifdef QCOM2 + eidx.setType(cereal::EncodeIndex::Type::FULL_H_E_V_C); +#else + eidx.setType(cam_idx == CAM_IDX_DCAM ? cereal::EncodeIndex::Type::FRONT : cereal::EncodeIndex::Type::FULL_H_E_V_C); +#endif eidx.setEncodeId(cnt); eidx.setSegmentNum(out_segment); eidx.setSegmentId(out_id); - auto words = capnp::messageToFlatArray(msg); - auto bytes = words.asBytes(); + auto bytes = msg.toBytes(); + if (idx_sock->send((char*)bytes.begin(), bytes.size()) < 0) { printf("err sending encodeIdx pkt: %s\n", strerror(errno)); } @@ -262,18 +333,15 @@ void encoder_thread(bool is_streaming, bool raw_clips, bool front) { } // publish encode index - capnp::MallocMessageBuilder msg; - cereal::Event::Builder event = msg.initRoot(); - event.setLogMonoTime(nanos_since_boot()); - auto eidx = event.initEncodeIdx(); + MessageBuilder msg; + auto eidx = msg.initEvent().initEncodeIdx(); eidx.setFrameId(extra.frame_id); eidx.setType(cereal::EncodeIndex::Type::FULL_LOSSLESS_CLIP); eidx.setEncodeId(cnt); eidx.setSegmentNum(out_segment); eidx.setSegmentId(out_id); - auto words = capnp::messageToFlatArray(msg); - auto bytes = words.asBytes(); + auto bytes = msg.toBytes(); if (lh) { lh_log(lh, bytes.begin(), bytes.size(), false); } @@ -323,78 +391,6 @@ void encoder_thread(bool is_streaming, bool raw_clips, bool front) { } #endif -#if ENABLE_LIDAR - -#include -#include -#include -#include - -#define VELODYNE_DATA_PORT 2368 -#define VELODYNE_TELEMETRY_PORT 8308 - -#define MAX_LIDAR_PACKET 2048 - -int lidar_thread() { - // increase kernel max buffer size - system("sysctl -w net.core.rmem_max=26214400"); - set_thread_name("lidar"); - - int sock; - if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { - perror("cannot create socket"); - return -1; - } - - int a = 26214400; - if (setsockopt(sock, SOL_SOCKET, SO_RCVBUF, &a, sizeof(int)) == -1) { - perror("cannot set socket opts"); - return -1; - } - - struct sockaddr_in addr; - memset(&addr, 0, sizeof(struct sockaddr_in)); - addr.sin_family = AF_INET; - addr.sin_port = htons(VELODYNE_DATA_PORT); - inet_aton("192.168.5.11", &(addr.sin_addr)); - - if (bind(sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) { - perror("cannot bind LIDAR socket"); - return -1; - } - - capnp::byte buf[MAX_LIDAR_PACKET]; - - while (!do_exit) { - // receive message - struct sockaddr from; - socklen_t fromlen = sizeof(from); - int cnt = recvfrom(sock, (void *)buf, MAX_LIDAR_PACKET, 0, &from, &fromlen); - if (cnt <= 0) { - printf("bug in lidar recieve!\n"); - continue; - } - - // create message for log - capnp::MallocMessageBuilder msg; - auto event = msg.initRoot(); - event.setLogMonoTime(nanos_since_boot()); - auto lidar_pts = event.initLidarPts(); - - // copy in the buffer - // TODO: can we remove this copy? does it matter? - kj::ArrayPtr bufferPtr = kj::arrayPtr(buf, cnt); - lidar_pts.setPkt(bufferPtr); - - // log it - auto words = capnp::messageToFlatArray(msg); - auto bytes = words.asBytes(); - logger_log(&s.logger, bytes.begin(), bytes.size()); - } - return 0; -} -#endif - } void append_property(const char* key, const char* value, void *cookie) { @@ -405,10 +401,8 @@ void append_property(const char* key, const char* value, void *cookie) { } kj::Array gen_init_data() { - capnp::MallocMessageBuilder msg; - auto event = msg.initRoot(); - event.setLogMonoTime(nanos_since_boot()); - auto init = event.initInitData(); + MessageBuilder msg; + auto init = msg.initEvent().initInitData(); init.setDeviceType(cereal::InitData::DeviceType::NEO); init.setVersion(capnp::Text::Reader(COMMA_VERSION)); @@ -466,8 +460,7 @@ kj::Array gen_init_data() { init.setGitRemote(capnp::Text::Reader(git_remote.data(), git_remote.size())); } - std::vector passive = read_db_bytes("Passive"); - init.setPassive(passive.size() > 0 && passive[0] == '1'); + init.setPassive(read_db_bool("Passive")); { // log params std::map params; @@ -510,11 +503,8 @@ static void bootlog() { LOGW("bootlog to %s", s.segment_path); { - capnp::MallocMessageBuilder msg; - auto event = msg.initRoot(); - event.setLogMonoTime(nanos_since_boot()); - - auto boot = event.initBoot(); + MessageBuilder msg; + auto boot = msg.initEvent().initBoot(); boot.setWallTimeNanos(nanos_since_epoch()); @@ -524,8 +514,7 @@ static void bootlog() { std::string lastPmsg = util::read_file("/sys/fs/pstore/pmsg-ramoops-0"); boot.setLastPmsg(capnp::Data::Reader((const kj::byte*)lastPmsg.data(), lastPmsg.size())); - auto words = capnp::messageToFlatArray(msg); - auto bytes = words.asBytes(); + auto bytes = msg.toBytes(); logger_log(&s.logger, bytes.begin(), bytes.size(), false); } @@ -540,6 +529,11 @@ int main(int argc, char** argv) { return 0; } + int segment_length = SEGMENT_LENGTH; + if (getenv("LOGGERD_TEST")) { + segment_length = atoi(getenv("LOGGERD_SEGMENT_LENGTH")); + } + setpriority(PRIO_PROCESS, 0, -12); clear_locks(); @@ -602,24 +596,29 @@ int main(int argc, char** argv) { double start_ts = seconds_since_boot(); double last_rotate_ts = start_ts; - + s.rotate_seq_id = 0; + s.should_close = 0; + s.finish_close = 0; + s.num_encoder = 0; + pthread_mutex_init(&s.rotate_lock, NULL); #ifndef DISABLE_ENCODER // rear camera - std::thread encoder_thread_handle(encoder_thread, is_streaming, false, false); + std::thread encoder_thread_handle(encoder_thread, is_streaming, false, CAM_IDX_FCAM); // front camera - std::thread front_encoder_thread_handle(encoder_thread, false, false, true); -#endif + std::thread front_encoder_thread_handle(encoder_thread, false, false, CAM_IDX_DCAM); -#if ENABLE_LIDAR - std::thread lidar_thread_handle(lidar_thread); + #ifdef QCOM2 + // wide camera + std::thread wide_encoder_thread_handle(encoder_thread, false, false, CAM_IDX_ECAM); + #endif #endif uint64_t msg_count = 0; uint64_t bytes_count = 0; while (!do_exit) { - for (auto sock : poller->poll(100 * 1000)){ + for (auto sock : poller->poll(100 * 1000)) { while (true) { Message * msg = sock->receive(true); if (msg == NULL){ @@ -659,10 +658,10 @@ int main(int argc, char** argv) { } double ts = seconds_since_boot(); - if (ts - last_rotate_ts > SEGMENT_LENGTH) { + if (ts - last_rotate_ts > segment_length) { // rotate the log - last_rotate_ts += SEGMENT_LENGTH; + last_rotate_ts += segment_length; std::lock_guard guard(s.lock); s.rotate_last_frame_id = s.last_frame_id; @@ -684,16 +683,14 @@ int main(int argc, char** argv) { #ifndef DISABLE_ENCODER + #ifdef QCOM2 + wide_encoder_thread_handle.join(); + #endif front_encoder_thread_handle.join(); encoder_thread_handle.join(); LOGW("encoder joined"); #endif -#if ENABLE_LIDAR - lidar_thread_handle.join(); - LOGW("lidar joined"); -#endif - logger_close(&s.logger); for (auto s : socks){ diff --git a/selfdrive/loggerd/uploader.py b/selfdrive/loggerd/uploader.py index 34bfc7895..08c1f323e 100644 --- a/selfdrive/loggerd/uploader.py +++ b/selfdrive/loggerd/uploader.py @@ -1,29 +1,32 @@ #!/usr/bin/env python3 -import os -import re -import time -import json -import random import ctypes import inspect -import requests -import traceback -import threading +import json +import os +import random +import re import subprocess +import threading +import time +import traceback -from selfdrive.swaglog import cloudlog -from selfdrive.loggerd.config import ROOT +import requests -from common import android -from common.params import Params +from cereal import log +from common.hardware import HARDWARE from common.api import Api -from common.xattr import getxattr, setxattr +from common.params import Params +from selfdrive.loggerd.xattr_cache import getxattr, setxattr +from selfdrive.loggerd.config import ROOT +from selfdrive.swaglog import cloudlog +NetworkType = log.ThermalData.NetworkType UPLOAD_ATTR_NAME = 'user.upload' UPLOAD_ATTR_VALUE = b'1' fake_upload = os.getenv("FAKEUPLOAD") is not None + def raise_on_thread(t, exctype): '''Raises an exception in the threads with id tid''' for ctid, tobj in threading._active.items(): @@ -69,27 +72,15 @@ def clear_locks(root): cloudlog.exception("clear_locks failed") def is_on_wifi(): - # ConnectivityManager.getActiveNetworkInfo() - try: - # TODO: figure out why the android service call sometimes dies with SIGUSR2 (signal from MSGQ) - result = android.parse_service_call_string(android.service_call(["connectivity", "2"])) - if result is None: - return True - return 'WIFI' in result - except Exception: - cloudlog.exception("is_on_wifi failed") - return False + return HARDWARE.get_network_type() == NetworkType.wifi def is_on_hotspot(): try: result = subprocess.check_output(["ifconfig", "wlan0"], stderr=subprocess.STDOUT, encoding='utf8') result = re.findall(r"inet addr:((\d+\.){3}\d+)", result)[0][0] - - is_android = result.startswith('192.168.43.') - is_ios = result.startswith('172.20.10.') - is_entune = result.startswith('10.0.2.') - - return (is_android or is_ios or is_entune) + return (result.startswith('192.168.43.') or # android + result.startswith('172.20.10.') or # ios + result.startswith('10.0.2.')) # toyota entune except Exception: return False @@ -105,7 +96,7 @@ class Uploader(): self.last_exc = None self.immediate_priority = {"qlog.bz2": 0, "qcamera.ts": 1} - self.high_priority = {"rlog.bz2": 0, "fcamera.hevc": 1, "dcamera.hevc": 2} + self.high_priority = {"rlog.bz2": 0, "fcamera.hevc": 1, "dcamera.hevc": 2, "ecamera.hevc": 3} def get_upload_sort(self, name): if name in self.immediate_priority: @@ -137,11 +128,11 @@ class Uploader(): is_uploaded = True # deleter could have deleted if is_uploaded: continue - yield (name, key, fn) def next_file_to_upload(self, with_raw): upload_files = list(self.gen_upload_files()) + # try to upload qlog files first for name, key, fn in upload_files: if name in self.immediate_priority: @@ -246,17 +237,19 @@ def uploader_fn(exit_event): uploader = Uploader(dongle_id, ROOT) backoff = 0.1 - while True: + counter = 0 + should_upload = False + while not exit_event.is_set(): offroad = params.get("IsOffroad") == b'1' allow_raw_upload = (params.get("IsUploadRawEnabled") != b"0") and offroad - on_hotspot = is_on_hotspot() - on_wifi = is_on_wifi() - should_upload = on_wifi and not on_hotspot - - if exit_event.is_set(): - return + check_network = (counter % 12 == 0 if offroad else True) + if check_network: + on_hotspot = is_on_hotspot() + on_wifi = is_on_wifi() + should_upload = on_wifi and not on_hotspot d = uploader.next_file_to_upload(with_raw=allow_raw_upload and should_upload) + counter += 1 if d is None: # Nothing to upload time.sleep(60 if offroad else 5) continue @@ -277,5 +270,6 @@ def uploader_fn(exit_event): def main(): uploader_fn(threading.Event()) + if __name__ == "__main__": main() diff --git a/selfdrive/loggerd/xattr_cache.py b/selfdrive/loggerd/xattr_cache.py new file mode 100644 index 000000000..aa97f0c77 --- /dev/null +++ b/selfdrive/loggerd/xattr_cache.py @@ -0,0 +1,13 @@ +from common.xattr import getxattr as getattr1 +from common.xattr import setxattr as setattr1 + +cached_attributes = {} +def getxattr(path, attr_name): + if (path, attr_name) not in cached_attributes: + response = getattr1(path, attr_name) + cached_attributes[(path, attr_name)] = response + return cached_attributes[(path, attr_name)] + +def setxattr(path, attr_name, attr_value): + cached_attributes.pop((path, attr_name), None) + return setattr1(path, attr_name, attr_value) diff --git a/selfdrive/manager.py b/selfdrive/manager.py index e9ef36a2f..898297d64 100755 --- a/selfdrive/manager.py +++ b/selfdrive/manager.py @@ -14,7 +14,7 @@ from selfdrive.swaglog import cloudlog, add_logentries_handler from common.basedir import BASEDIR, PARAMS -from common.android import ANDROID +from common.hardware import HARDWARE, ANDROID, PC WEBCAM = os.getenv("WEBCAM") is not None sys.path.append(os.path.join(BASEDIR, "pyextra")) os.environ['BASEDIR'] = BASEDIR @@ -156,7 +156,6 @@ from selfdrive.registration import register from selfdrive.version import version, dirty from selfdrive.loggerd.config import ROOT from selfdrive.launcher import launcher -from common import android from common.apk import update_apks, pm_apply_packages, start_offroad ThermalStatus = cereal.log.ThermalData.ThermalStatus @@ -189,6 +188,7 @@ managed_processes = { "updated": "selfdrive.updated", "dmonitoringmodeld": ("selfdrive/modeld", ["./dmonitoringmodeld"]), "modeld": ("selfdrive/modeld", ["./modeld"]), + "rtshield": "selfdrive.rtshield", } daemon_processes = { @@ -216,14 +216,18 @@ persistent_processes = [ 'logmessaged', 'ui', 'uploader', + 'deleter', ] -if ANDROID: +if not PC: persistent_processes += [ 'logcatd', 'tombstoned', + ] + +if ANDROID: + persistent_processes += [ 'updated', - 'deleter', ] car_started_processes = [ @@ -231,14 +235,12 @@ car_started_processes = [ 'plannerd', 'loggerd', 'radard', - 'dmonitoringd', 'calibrationd', 'paramsd', 'camerad', - 'modeld', 'proclogd', - 'ubloxd', 'locationd', + 'clocksd', ] driver_view_processes = [ @@ -249,17 +251,28 @@ driver_view_processes = [ if WEBCAM: car_started_processes += [ + 'dmonitoringd', + 'dmonitoringmodeld', + ] + +if not PC: + car_started_processes += [ + 'ubloxd', + 'sensord', + 'dmonitoringd', 'dmonitoringmodeld', ] if ANDROID: car_started_processes += [ - 'sensord', - 'clocksd', 'gpsd', - 'dmonitoringmodeld', + 'rtshield', ] +# starting dmonitoringmodeld when modeld is initializing can sometimes \ +# result in a weird snpe state where dmon constantly uses more cpu than normal. +car_started_processes += ['modeld'] + def register_managed_process(name, desc, car_started=False): global managed_processes, car_started_processes, persistent_processes print("registering %s" % name) @@ -365,6 +378,7 @@ def kill_managed_process(name): join_process(running[name], 15) if running[name].exitcode is None: cloudlog.critical("unkillable process %s failed to die!" % name) + # TODO: Use method from HARDWARE if ANDROID: cloudlog.critical("FORCE REBOOTING PHONE!") os.system("date >> /sdcard/unkillable_reboot") @@ -536,7 +550,7 @@ def uninstall(): with open('/cache/recovery/command', 'w') as f: f.write('--wipe_data\n') # IPowerManager.reboot(confirm=false, reason="recovery", wait=true) - android.reboot(reason="recovery") + HARDWARE.reboot(reason="recovery") def main(): os.environ['PARAMS_PATH'] = PARAMS @@ -561,11 +575,6 @@ def main(): ("HasCompletedSetup", "0"), ("IsUploadRawEnabled", "1"), ("IsLdwEnabled", "1"), - ("IsGeofenceEnabled", "-1"), - ("SpeedLimitOffset", "0"), - ("LongitudinalControl", "0"), - ("LimitSetSpeed", "0"), - ("LimitSetSpeedNeural", "0"), ("LastUpdateTime", datetime.datetime.utcnow().isoformat().encode('utf8')), ("OpenpilotEnabledToggle", "1"), ("LaneChangeEnabled", "1"), @@ -616,8 +625,7 @@ if __name__ == "__main__": cloudlog.exception("Manager failed to start") # Show last 3 lines of traceback - error = traceback.format_exc(3) - + error = traceback.format_exc(-3) error = "Manager failed to start\n \n" + error with TextWindow(error) as t: t.wait_for_exit() diff --git a/selfdrive/modeld/SConscript b/selfdrive/modeld/SConscript index 6f9f6608e..18f279bf7 100644 --- a/selfdrive/modeld/SConscript +++ b/selfdrive/modeld/SConscript @@ -1,7 +1,7 @@ Import('env', 'arch', 'cereal', 'messaging', 'common', 'gpucommon', 'visionipc') lenv = env.Clone() -libs = [cereal, messaging, common, 'OpenCL', 'SNPE', 'capnp', 'zmq', 'kj', 'yuv', gpucommon, visionipc] +libs = [cereal, messaging, common, 'OpenCL', 'SNPE', 'symphony-cpu', 'capnp', 'zmq', 'kj', 'yuv', gpucommon, visionipc] TEST_THNEED = False @@ -19,9 +19,9 @@ if arch == "aarch64": lenv['CFLAGS'].append("-DUSE_THNEED") lenv['CXXFLAGS'].append("-DUSE_THNEED") elif arch == "larch64": - libs += ['gsl', 'CB', 'symphony-cpu', 'pthread'] + libs += ['gsl', 'CB', 'pthread'] else: - libs += ['symphony-cpu', 'pthread'] + libs += ['pthread'] # for tensorflow support common_src += ['runners/tfmodel.cc'] diff --git a/selfdrive/modeld/dmonitoringmodeld b/selfdrive/modeld/dmonitoringmodeld index 51b520d74..e68c13103 100755 --- a/selfdrive/modeld/dmonitoringmodeld +++ b/selfdrive/modeld/dmonitoringmodeld @@ -1,5 +1,16 @@ #!/bin/sh -export LD_LIBRARY_PATH="/data/pythonpath/phonelibs/snpe/aarch64/:$HOME/openpilot/phonelibs/snpe/larch64:$HOME/openpilot/phonelibs/snpe/x86_64-linux-clang:$LD_LIBRARY_PATH" -export ADSP_LIBRARY_PATH="/data/pythonpath/phonelibs/snpe/aarch64/" -exec ./_dmonitoringmodeld +if [ -d /system ]; then + if [ -f /TICI ]; then # QCOM2 + export LD_LIBRARY_PATH="/usr/lib/aarch64-linux-gnu:/data/pythonpath/phonelibs/snpe/larch64:$LD_LIBRARY_PATH" + export ADSP_LIBRARY_PATH="/data/pythonpath/phonelibs/snpe/larch64/" + else # QCOM + export LD_LIBRARY_PATH="/data/pythonpath/phonelibs/snpe/aarch64/:$LD_LIBRARY_PATH" + export ADSP_LIBRARY_PATH="/data/pythonpath/phonelibs/snpe/aarch64/" + fi +else + # PC + export LD_LIBRARY_PATH="$HOME/openpilot/phonelibs/snpe/x86_64-linux-clang:/openpilot/phonelibs/snpe/x86_64:$HOME/openpilot/phonelibs/snpe/x86_64:$LD_LIBRARY_PATH" +fi + +exec ./_dmonitoringmodeld diff --git a/selfdrive/modeld/dmonitoringmodeld.cc b/selfdrive/modeld/dmonitoringmodeld.cc index 28776f94e..4eeb292ef 100644 --- a/selfdrive/modeld/dmonitoringmodeld.cc +++ b/selfdrive/modeld/dmonitoringmodeld.cc @@ -25,11 +25,13 @@ int main(int argc, char **argv) { int err; set_realtime_priority(51); +#ifdef QCOM2 + set_core_affinity(5); +#endif + signal(SIGINT, (sighandler_t)set_do_exit); signal(SIGTERM, (sighandler_t)set_do_exit); - // messaging - SubMaster sm({"dMonitoringState"}); PubMaster pm({"driverState"}); // init the models @@ -49,7 +51,6 @@ int main(int argc, char **argv) { LOGW("connected with buffer size: %d", buf_info.buf_len); double last = 0; - int chk_counter = 0; while (!do_exit) { VIPCBuf *buf; VIPCBufExtra extra; @@ -58,23 +59,9 @@ int main(int argc, char **argv) { printf("visionstream get failed\n"); break; } - //printf("frame_id: %d %dx%d\n", extra.frame_id, buf_info.width, buf_info.height); - if (!dmonitoringmodel.is_rhd_checked) { - if (chk_counter >= RHD_CHECK_INTERVAL) { - if (sm.update(0) > 0) { - auto state = sm["dMonitoringState"].getDMonitoringState(); - dmonitoringmodel.is_rhd = state.getIsRHD(); - dmonitoringmodel.is_rhd_checked = state.getRhdChecked(); - } - chk_counter = 0; - } - chk_counter += 1; - } double t1 = millis_since_boot(); - DMonitoringResult res = dmonitoring_eval_frame(&dmonitoringmodel, buf->addr, buf_info.width, buf_info.height); - double t2 = millis_since_boot(); // send dm packet @@ -82,6 +69,11 @@ int main(int argc, char **argv) { LOGD("dmonitoring process: %.2fms, from last %.2fms", t2-t1, t1-last); last = t1; +#ifdef QCOM2 + // this makes it run at about 2.7Hz on tici CPU to deal with modeld lags + // TODO: DSP needs to be freed (again) + usleep(250000); +#endif } visionstream_destroy(&stream); } diff --git a/selfdrive/modeld/modeld b/selfdrive/modeld/modeld index 5369034df..668685957 100755 --- a/selfdrive/modeld/modeld +++ b/selfdrive/modeld/modeld @@ -1,4 +1,12 @@ #!/bin/sh -export LD_LIBRARY_PATH="/data/pythonpath/phonelibs/snpe/aarch64/:$HOME/openpilot/phonelibs/snpe/larch64:$HOME/openpilot/phonelibs/snpe/x86_64-linux-clang:$LD_LIBRARY_PATH" +if [ -d /system ]; then + if [ -f /TICI ]; then # QCOM2 + export LD_LIBRARY_PATH="/usr/lib/aarch64-linux-gnu:/data/pythonpath/phonelibs/snpe/larch64:$LD_LIBRARY_PATH" + else # QCOM + export LD_LIBRARY_PATH="/data/pythonpath/phonelibs/snpe/aarch64/:$LD_LIBRARY_PATH" + fi +else + # PC + export LD_LIBRARY_PATH="$HOME/openpilot/phonelibs/snpe/x86_64-linux-clang:/openpilot/phonelibs/snpe/x86_64:$HOME/openpilot/phonelibs/snpe/x86_64:$LD_LIBRARY_PATH" +fi exec ./_modeld - diff --git a/selfdrive/modeld/modeld.cc b/selfdrive/modeld/modeld.cc index 33dafc86c..0f1c22c34 100644 --- a/selfdrive/modeld/modeld.cc +++ b/selfdrive/modeld/modeld.cc @@ -6,6 +6,7 @@ #include "common/visionbuf.h" #include "common/visionipc.h" #include "common/swaglog.h" +#include "common/clutil.h" #include "models/driving.h" #include "messaging.hpp" @@ -36,11 +37,26 @@ void* live_thread(void *arg) { -1.09890110e-03, 0.00000000e+00, 2.81318681e-01, -1.84808520e-20, 9.00738606e-04,-4.28751576e-02; +#ifndef QCOM2 Eigen::Matrix eon_intrinsics; eon_intrinsics << 910.0, 0.0, 582.0, 0.0, 910.0, 437.0, 0.0, 0.0, 1.0; +#else + Eigen::Matrix eon_intrinsics; + eon_intrinsics << + 2648.0, 0.0, 1928.0/2, + 0.0, 2648.0, 1208.0/2, + 0.0, 0.0, 1.0; +#endif + + // debayering does a 2x downscale + mat3 yuv_transform = transform_scale_buffer((mat3){{ + 1.0, 0.0, 0.0, + 0.0, 1.0, 0.0, + 0.0, 0.0, 1.0, + }}, 0.5); while (!do_exit) { if (sm.update(10) > 0){ @@ -58,12 +74,13 @@ void* live_thread(void *arg) { camera_frame_from_ground.col(2) = camera_frame_from_road_frame.col(3); auto warp_matrix = camera_frame_from_ground * ground_from_medmodel_frame; - - pthread_mutex_lock(&transform_lock); + mat3 transform = {}; for (int i=0; i<3*3; i++) { - cur_transform.v[i] = warp_matrix(i / 3, i % 3); + transform.v[i] = warp_matrix(i / 3, i % 3); } - + mat3 model_transform = matmul3(yuv_transform, transform); + pthread_mutex_lock(&transform_lock); + cur_transform = model_transform; run_model = true; pthread_mutex_unlock(&transform_lock); } @@ -75,9 +92,18 @@ int main(int argc, char **argv) { int err; set_realtime_priority(51); +#ifdef QCOM + set_core_affinity(2); +#elif QCOM2 + // CPU usage is much lower when pinned to a single big core + set_core_affinity(4); +#endif + signal(SIGINT, (sighandler_t)set_do_exit); signal(SIGTERM, (sighandler_t)set_do_exit); + pthread_mutex_init(&transform_lock, NULL); + // start calibration thread pthread_t live_thread_handle; err = pthread_create(&live_thread_handle, NULL, live_thread, NULL); @@ -87,78 +113,25 @@ int main(int argc, char **argv) { PubMaster pm({"model", "cameraOdometry"}); SubMaster sm({"pathPlan", "frame"}); -#ifdef QCOM +#if defined(QCOM) || defined(QCOM2) cl_device_type device_type = CL_DEVICE_TYPE_DEFAULT; #else cl_device_type device_type = CL_DEVICE_TYPE_CPU; #endif // cl init - cl_device_id device_id; - cl_context context; - cl_command_queue q; - { - cl_uint num_platforms; - err = clGetPlatformIDs(0, NULL, &num_platforms); - assert(err == 0); + cl_device_id device_id = cl_get_device_id(device_type); + cl_context context = clCreateContext(NULL, 1, &device_id, NULL, NULL, &err); + assert(err == 0); - cl_platform_id * platform_ids = new cl_platform_id[num_platforms]; - err = clGetPlatformIDs(num_platforms, platform_ids, NULL); - assert(err == 0); - - LOGD("got %d opencl platform(s)", num_platforms); - - char cBuffer[1024]; - bool opencl_platform_found = false; - - for (size_t i = 0; i < num_platforms; i++){ - err = clGetPlatformInfo(platform_ids[i], CL_PLATFORM_NAME, sizeof(cBuffer), &cBuffer, NULL); - assert(err == 0); - LOGD("platform[%zu] CL_PLATFORM_NAME: %s", i, cBuffer); - - cl_uint num_devices; - err = clGetDeviceIDs(platform_ids[i], device_type, 0, NULL, &num_devices); - if (err != 0|| !num_devices){ - continue; - } - - // Get first device - err = clGetDeviceIDs(platform_ids[i], device_type, 1, &device_id, NULL); - assert(err == 0); - - context = clCreateContext(NULL, 1, &device_id, NULL, NULL, &err); - assert(err == 0); - - q = clCreateCommandQueue(context, device_id, 0, &err); - assert(err == 0); - - opencl_platform_found = true; - break; - } - - delete[] platform_ids; - - if (!opencl_platform_found){ - LOGE("No valid openCL platform found"); - assert(opencl_platform_found); - } - - - LOGD("opencl init complete"); - } + cl_command_queue q = clCreateCommandQueue(context, device_id, 0, &err); + assert(err == 0); // init the models ModelState model; model_init(&model, device_id, context, true); LOGW("models loaded, modeld starting"); - // debayering does a 2x downscale - mat3 yuv_transform = transform_scale_buffer((mat3){{ - 1.0, 0.0, 0.0, - 0.0, 1.0, 0.0, - 0.0, 0.0, 1.0, - }}, 0.5); - // loop VisionStream stream; while (!do_exit) { @@ -194,7 +167,7 @@ int main(int argc, char **argv) { } pthread_mutex_lock(&transform_lock); - mat3 transform = cur_transform; + mat3 model_transform = cur_transform; const bool run_model_this_iter = run_model; pthread_mutex_unlock(&transform_lock); @@ -211,8 +184,6 @@ int main(int argc, char **argv) { vec_desire[desire] = 1.0; } - mat3 model_transform = matmul3(yuv_transform, transform); - mt1 = millis_since_boot(); // TODO: don't make copies! @@ -249,5 +220,6 @@ int main(int argc, char **argv) { clReleaseCommandQueue(q); clReleaseContext(context); + pthread_mutex_destroy(&transform_lock); return 0; } diff --git a/selfdrive/modeld/models/commonmodel.c b/selfdrive/modeld/models/commonmodel.c index eebf4761a..0bdc61bbe 100644 --- a/selfdrive/modeld/models/commonmodel.c +++ b/selfdrive/modeld/models/commonmodel.c @@ -7,25 +7,23 @@ void frame_init(ModelFrame* frame, int width, int height, cl_device_id device_id, cl_context context) { int err; - frame->device_id = device_id; - frame->context = context; transform_init(&frame->transform, context, device_id); frame->transformed_width = width; frame->transformed_height = height; - frame->transformed_y_cl = clCreateBuffer(frame->context, CL_MEM_READ_WRITE, + frame->transformed_y_cl = clCreateBuffer(context, CL_MEM_READ_WRITE, (size_t)frame->transformed_width*frame->transformed_height, NULL, &err); assert(err == 0); - frame->transformed_u_cl = clCreateBuffer(frame->context, CL_MEM_READ_WRITE, + frame->transformed_u_cl = clCreateBuffer(context, CL_MEM_READ_WRITE, (size_t)(frame->transformed_width/2)*(frame->transformed_height/2), NULL, &err); assert(err == 0); - frame->transformed_v_cl = clCreateBuffer(frame->context, CL_MEM_READ_WRITE, + frame->transformed_v_cl = clCreateBuffer(context, CL_MEM_READ_WRITE, (size_t)(frame->transformed_width/2)*(frame->transformed_height/2), NULL, &err); assert(err == 0); frame->net_input_size = ((width*height*3)/2)*sizeof(float); - frame->net_input = clCreateBuffer(frame->context, CL_MEM_READ_WRITE, + frame->net_input = clCreateBuffer(context, CL_MEM_READ_WRITE, frame->net_input_size, (void*)NULL, &err); assert(err == 0); diff --git a/selfdrive/modeld/models/commonmodel.h b/selfdrive/modeld/models/commonmodel.h index bb5236648..7aed3d799 100644 --- a/selfdrive/modeld/models/commonmodel.h +++ b/selfdrive/modeld/models/commonmodel.h @@ -20,10 +20,6 @@ float softplus(float input); float sigmoid(float input); typedef struct ModelFrame { - cl_device_id device_id; - cl_context context; - - // input Transform transform; int transformed_width, transformed_height; cl_mem transformed_y_cl, transformed_u_cl, transformed_v_cl; diff --git a/selfdrive/modeld/models/dmonitoring.cc b/selfdrive/modeld/models/dmonitoring.cc index 4f4203a38..14f45dd90 100644 --- a/selfdrive/modeld/models/dmonitoring.cc +++ b/selfdrive/modeld/models/dmonitoring.cc @@ -2,12 +2,13 @@ #include "dmonitoring.h" #include "common/mat.h" #include "common/timing.h" +#include "common/params.h" #include #define MODEL_WIDTH 320 #define MODEL_HEIGHT 640 -#define FULL_W 852 +#define FULL_W 852 // should get these numbers from camerad #if defined(QCOM) || defined(QCOM2) #define input_lambda(x) (x - 128.f) * 0.0078125f @@ -21,9 +22,13 @@ void dmonitoring_init(DMonitoringModelState* s) { #else const char* model_path = "../../models/dmonitoring_model.dlc"; #endif - s->m = new DefaultRunModel(model_path, (float*)&s->output, OUTPUT_SIZE, USE_DSP_RUNTIME); - s->is_rhd = false; - s->is_rhd_checked = false; +#ifdef QCOM2 + int runtime = USE_CPU_RUNTIME; +#else + int runtime = USE_DSP_RUNTIME; +#endif + s->m = new DefaultRunModel(model_path, (float*)&s->output, OUTPUT_SIZE, runtime); + s->is_rhd = read_db_bool("IsRHD"); } template @@ -35,14 +40,30 @@ static inline T *get_buffer(std::vector &buf, const size_t size) { } DMonitoringResult dmonitoring_eval_frame(DMonitoringModelState* s, void* stream_buf, int width, int height) { - uint8_t *raw_buf = (uint8_t*) stream_buf; uint8_t *raw_y_buf = raw_buf; uint8_t *raw_u_buf = raw_y_buf + (width * height); uint8_t *raw_v_buf = raw_u_buf + ((width/2) * (height/2)); - int cropped_width = height/2; - int cropped_height = height; +#ifndef QCOM2 + const int cropped_width = height/2; + const int cropped_height = height; + const int global_x_offset = 0; + const int global_y_offset = 0; + const int crop_x_offset = width - cropped_width; + const int crop_y_offset = 0; +#else + const int full_width_tici = 1928; + const int full_height_tici = 1208; + const int adapt_width_tici = 808; + + const int cropped_height = adapt_width_tici / 1.33; + const int cropped_width = cropped_height / 2; + const int global_x_offset = full_width_tici / 2 - adapt_width_tici / 2; + const int global_y_offset = full_height_tici / 2 - cropped_height / 2; + const int crop_x_offset = adapt_width_tici - cropped_width; + const int crop_y_offset = 0; +#endif int resized_width = MODEL_WIDTH; int resized_height = MODEL_HEIGHT; @@ -52,22 +73,21 @@ DMonitoringResult dmonitoring_eval_frame(DMonitoringModelState* s, void* stream_ uint8_t *cropped_v_buf = cropped_u_buf + ((cropped_width/2) * (cropped_height/2)); if (!s->is_rhd) { - for (int r = 0; r < height/2; r++) { - memcpy(cropped_y_buf + 2*r*cropped_width, raw_y_buf + 2*r*width + (width - cropped_width), cropped_width); - memcpy(cropped_y_buf + (2*r+1)*cropped_width, raw_y_buf + (2*r+1)*width + (width - cropped_width), cropped_width); - memcpy(cropped_u_buf + r*cropped_width/2, raw_u_buf + r*width/2 + ((width/2) - (cropped_width/2)), cropped_width/2); - memcpy(cropped_v_buf + r*cropped_width/2, raw_v_buf + r*width/2 + ((width/2) - (cropped_width/2)), cropped_width/2); + for (int r = 0; r < cropped_height/2; r++) { + memcpy(cropped_y_buf + 2*r*cropped_width, raw_y_buf + (2*r + global_y_offset + crop_y_offset)*width + global_x_offset + crop_x_offset, cropped_width); + memcpy(cropped_y_buf + (2*r+1)*cropped_width, raw_y_buf + (2*r + global_y_offset + crop_y_offset + 1)*width + global_x_offset + crop_x_offset, cropped_width); + memcpy(cropped_u_buf + r*cropped_width/2, raw_u_buf + (r + (global_y_offset + crop_y_offset)/2)*width/2 + (global_x_offset + crop_x_offset)/2, cropped_width/2); + memcpy(cropped_v_buf + r*cropped_width/2, raw_v_buf + (r + (global_y_offset + crop_y_offset)/2)*width/2 + (global_x_offset + crop_x_offset)/2, cropped_width/2); } } else { - // not tested uint8_t *premirror_cropped_y_buf = get_buffer(s->premirror_cropped_buf, cropped_width*cropped_height*3/2); uint8_t *premirror_cropped_u_buf = premirror_cropped_y_buf + (cropped_width * cropped_height); uint8_t *premirror_cropped_v_buf = premirror_cropped_u_buf + ((cropped_width/2) * (cropped_height/2)); - for (int r = 0; r < height/2; r++) { - memcpy(premirror_cropped_y_buf + 2*r*cropped_width, raw_y_buf + 2*r*width, cropped_width); - memcpy(premirror_cropped_y_buf + (2*r+1)*cropped_width, raw_y_buf + (2*r+1)*width, cropped_width); - memcpy(premirror_cropped_u_buf + r*cropped_width/2, raw_u_buf + r*width/2, cropped_width/2); - memcpy(premirror_cropped_v_buf + r*cropped_width/2, raw_v_buf + r*width/2, cropped_width/2); + for (int r = 0; r < cropped_height/2; r++) { + memcpy(premirror_cropped_y_buf + (2*r)*cropped_width, raw_y_buf + (2*r + global_y_offset + crop_y_offset)*width + global_x_offset, cropped_width); + memcpy(premirror_cropped_y_buf + (2*r+1)*cropped_width, raw_y_buf + (2*r + global_y_offset + crop_y_offset + 1)*width + global_x_offset, cropped_width); + memcpy(premirror_cropped_u_buf + r*cropped_width/2, raw_u_buf + (r + (global_y_offset + crop_y_offset)/2)*width/2 + global_x_offset/2, cropped_width/2); + memcpy(premirror_cropped_v_buf + r*cropped_width/2, raw_v_buf + (r + (global_y_offset + crop_y_offset)/2)*width/2 + global_x_offset/2, cropped_width/2); } libyuv::I420Mirror(premirror_cropped_y_buf, cropped_width, premirror_cropped_u_buf, cropped_width/2, @@ -103,9 +123,9 @@ DMonitoringResult dmonitoring_eval_frame(DMonitoringModelState* s, void* stream_ // Y_ul net_input_buf[(c*MODEL_HEIGHT/2) + r] = input_lambda(resized_buf[(2*r*resized_width) + (2*c)]); // Y_ur - net_input_buf[(c*MODEL_HEIGHT/2) + r + ((MODEL_WIDTH/2)*(MODEL_HEIGHT/2))] = input_lambda(resized_buf[(2*r*resized_width) + (2*c+1)]); + net_input_buf[(c*MODEL_HEIGHT/2) + r + (2*(MODEL_WIDTH/2)*(MODEL_HEIGHT/2))] = input_lambda(resized_buf[(2*r*resized_width) + (2*c+1)]); // Y_dl - net_input_buf[(c*MODEL_HEIGHT/2) + r + (2*(MODEL_WIDTH/2)*(MODEL_HEIGHT/2))] = input_lambda(resized_buf[(2*r*resized_width+1) + (2*c)]); + net_input_buf[(c*MODEL_HEIGHT/2) + r + ((MODEL_WIDTH/2)*(MODEL_HEIGHT/2))] = input_lambda(resized_buf[(2*r*resized_width+1) + (2*c)]); // Y_dr net_input_buf[(c*MODEL_HEIGHT/2) + r + (3*(MODEL_WIDTH/2)*(MODEL_HEIGHT/2))] = input_lambda(resized_buf[(2*r*resized_width+1) + (2*c+1)]); // U @@ -147,11 +167,8 @@ DMonitoringResult dmonitoring_eval_frame(DMonitoringModelState* s, void* stream_ void dmonitoring_publish(PubMaster &pm, uint32_t frame_id, const DMonitoringResult &res){ // make msg - capnp::MallocMessageBuilder msg; - cereal::Event::Builder event = msg.initRoot(); - event.setLogMonoTime(nanos_since_boot()); - - auto framed = event.initDriverState(); + MessageBuilder msg; + auto framed = msg.initEvent().initDriverState(); framed.setFrameId(frame_id); kj::ArrayPtr face_orientation(&res.face_orientation[0], ARRAYSIZE(res.face_orientation)); diff --git a/selfdrive/modeld/models/dmonitoring.h b/selfdrive/modeld/models/dmonitoring.h index 439b9c005..b1b6819d5 100644 --- a/selfdrive/modeld/models/dmonitoring.h +++ b/selfdrive/modeld/models/dmonitoring.h @@ -10,7 +10,6 @@ extern "C" { #endif #define OUTPUT_SIZE 34 -#define RHD_CHECK_INTERVAL 10 typedef struct DMonitoringResult { float face_orientation[3]; @@ -28,7 +27,6 @@ typedef struct DMonitoringResult { typedef struct DMonitoringModelState { RunModel *m; bool is_rhd; - bool is_rhd_checked; float output[OUTPUT_SIZE]; std::vector resized_buf; std::vector cropped_buf; diff --git a/selfdrive/modeld/models/driving.cc b/selfdrive/modeld/models/driving.cc index f58e7b196..14c661616 100644 --- a/selfdrive/modeld/models/driving.cc +++ b/selfdrive/modeld/models/driving.cc @@ -54,14 +54,11 @@ void model_init(ModelState* s, cl_device_id device_id, cl_context context, int t s->traffic_convention = std::make_unique(TRAFFIC_CONVENTION_LEN); s->m->addTrafficConvention(s->traffic_convention.get(), TRAFFIC_CONVENTION_LEN); - std::vector result = read_db_bytes("IsRHD"); - if (result.size() > 0) { - bool is_rhd = result[0] == '1'; - if (is_rhd) { - s->traffic_convention[1] = 1.0; - } else { - s->traffic_convention[0] = 1.0; - } + bool is_rhd = read_db_bool("IsRHD"); + if (is_rhd) { + s->traffic_convention[1] = 1.0; + } else { + s->traffic_convention[0] = 1.0; } #endif @@ -162,8 +159,8 @@ void fill_path(cereal::ModelData::PathData::Builder path, const float * data, bo float prob; float valid_len; - // clamp to 5 and 192 - valid_len = fmin(192, fmax(5, data[MODEL_PATH_DISTANCE*2])); + // clamp to 5 and MODEL_PATH_DISTANCE + valid_len = fmin(MODEL_PATH_DISTANCE, fmax(5, data[MODEL_PATH_DISTANCE*2])); for (int i=0; i(); - event.setLogMonoTime(nanos_since_boot()); - uint32_t frame_age = (frame_id > vipc_frame_id) ? (frame_id - vipc_frame_id) : 0; - auto framed = event.initModel(); + MessageBuilder msg; + auto framed = msg.initEvent(frame_drop < MAX_FRAME_DROP).initModel(); framed.setFrameId(vipc_frame_id); framed.setFrameAge(frame_age); framed.setFrameDropPerc(frame_drop * 100); framed.setTimestampEof(timestamp_eof); - auto lpath = framed.initPath(); - fill_path(lpath, net_outputs.path, false, 0); - auto left_lane = framed.initLeftLane(); - fill_path(left_lane, net_outputs.left_lane, true, 1.8); - auto right_lane = framed.initRightLane(); - fill_path(right_lane, net_outputs.right_lane, true, -1.8); - auto longi = framed.initLongitudinal(); - fill_longi(longi, net_outputs.long_x, net_outputs.long_v, net_outputs.long_a); - + fill_path(framed.initPath(), net_outputs.path, false, 0); + fill_path(framed.initLeftLane(), net_outputs.left_lane, true, 1.8); + fill_path(framed.initRightLane(), net_outputs.right_lane, true, -1.8); + fill_longi(framed.initLongitudinal(), net_outputs.long_x, net_outputs.long_v, net_outputs.long_a); // Find the distribution that corresponds to the current lead int mdn_max_idx = 0; @@ -268,8 +256,7 @@ void model_publish(PubMaster &pm, uint32_t vipc_frame_id, uint32_t frame_id, mdn_max_idx = i; } } - auto lead = framed.initLead(); - fill_lead(lead, net_outputs.lead, mdn_max_idx, t_offset); + fill_lead(framed.initLead(), net_outputs.lead, mdn_max_idx, t_offset); // Find the distribution that corresponds to the lead in 2s mdn_max_idx = 0; t_offset = 1; @@ -278,23 +265,14 @@ void model_publish(PubMaster &pm, uint32_t vipc_frame_id, uint32_t frame_id, mdn_max_idx = i; } } - auto lead_future = framed.initLeadFuture(); - fill_lead(lead_future, net_outputs.lead, mdn_max_idx, t_offset); - - - auto meta = framed.initMeta(); - fill_meta(meta, net_outputs.meta); - event.setValid(frame_drop < MAX_FRAME_DROP); + fill_lead(framed.initLeadFuture(), net_outputs.lead, mdn_max_idx, t_offset); + fill_meta(framed.initMeta(), net_outputs.meta); pm.send("model", msg); } void posenet_publish(PubMaster &pm, uint32_t vipc_frame_id, uint32_t frame_id, uint32_t vipc_dropped_frames, float frame_drop, const ModelDataRaw &net_outputs, uint64_t timestamp_eof) { - capnp::MallocMessageBuilder msg; - cereal::Event::Builder event = msg.initRoot(); - event.setLogMonoTime(nanos_since_boot()); - float trans_arr[3]; float trans_std_arr[3]; float rot_arr[3]; @@ -308,7 +286,8 @@ void posenet_publish(PubMaster &pm, uint32_t vipc_frame_id, uint32_t frame_id, rot_std_arr[i] = M_PI * (softplus(net_outputs.pose[9 + i]) + 1e-6) / 180.0; } - auto posenetd = event.initCameraOdometry(); + MessageBuilder msg; + auto posenetd = msg.initEvent(vipc_dropped_frames < 1).initCameraOdometry(); kj::ArrayPtr trans_vs(&trans_arr[0], 3); posenetd.setTrans(trans_vs); kj::ArrayPtr rot_vs(&rot_arr[0], 3); @@ -318,11 +297,8 @@ void posenet_publish(PubMaster &pm, uint32_t vipc_frame_id, uint32_t frame_id, kj::ArrayPtr rot_std_vs(&rot_std_arr[0], 3); posenetd.setRotStd(rot_std_vs); - posenetd.setTimestampEof(timestamp_eof); posenetd.setFrameId(vipc_frame_id); - event.setValid(vipc_dropped_frames < 1); - pm.send("cameraOdometry", msg); } diff --git a/selfdrive/modeld/models/driving.h b/selfdrive/modeld/models/driving.h index 2bf4d6150..52e7401a3 100644 --- a/selfdrive/modeld/models/driving.h +++ b/selfdrive/modeld/models/driving.h @@ -9,6 +9,7 @@ #include "common/mat.h" #include "common/util.h" +#include "common/modeldata.h" #include "commonmodel.h" #include "runners/run.h" @@ -22,13 +23,8 @@ #define MODEL_FRAME_SIZE MODEL_WIDTH * MODEL_HEIGHT * 3 / 2 #define MODEL_NAME "supercombo_dlc" -#define MODEL_PATH_DISTANCE 192 -#define POLYFIT_DEGREE 4 -#define SPEED_PERCENTILES 10 #define DESIRE_LEN 8 #define TRAFFIC_CONVENTION_LEN 2 -#define DESIRE_PRED_SIZE 32 -#define OTHER_META_SIZE 4 #define LEAD_MDN_N 5 // probs for 5 groups #define MDN_VALS 4 // output xyva for each lead group #define SELECTION 3 //output 3 group (lead now, in 2s and 6s) diff --git a/selfdrive/modeld/runners/snpemodel.cc b/selfdrive/modeld/runners/snpemodel.cc index 5a92d94bf..38da13453 100644 --- a/selfdrive/modeld/runners/snpemodel.cc +++ b/selfdrive/modeld/runners/snpemodel.cc @@ -13,7 +13,7 @@ void PrintErrorStringAndExit() { SNPEModel::SNPEModel(const char *path, float *loutput, size_t loutput_size, int runtime) { output = loutput; output_size = loutput_size; -#ifdef QCOM +#if defined(QCOM) || defined(QCOM2) if (runtime==USE_GPU_RUNTIME) { Runtime = zdl::DlSystem::Runtime_t::GPU; } else if (runtime==USE_DSP_RUNTIME) { @@ -35,7 +35,7 @@ SNPEModel::SNPEModel(const char *path, float *loutput, size_t loutput_size, int // create model runner zdl::SNPE::SNPEBuilder snpeBuilder(container.get()); while (!snpe) { -#ifdef QCOM +#if defined(QCOM) || defined(QCOM2) snpe = snpeBuilder.setOutputLayers({}) .setRuntimeProcessor(Runtime) .setUseUserSuppliedBuffers(true) diff --git a/selfdrive/modeld/runners/snpemodel.h b/selfdrive/modeld/runners/snpemodel.h index f7f217cdc..90c26664f 100644 --- a/selfdrive/modeld/runners/snpemodel.h +++ b/selfdrive/modeld/runners/snpemodel.h @@ -38,7 +38,7 @@ private: Thneed *thneed = NULL; #endif -#ifdef QCOM +#if defined(QCOM) || defined(QCOM2) zdl::DlSystem::Runtime_t Runtime; #endif diff --git a/selfdrive/monitoring/dmonitoringd.py b/selfdrive/monitoring/dmonitoringd.py index 66a1f2d87..c8d6d1587 100755 --- a/selfdrive/monitoring/dmonitoringd.py +++ b/selfdrive/monitoring/dmonitoringd.py @@ -1,31 +1,23 @@ #!/usr/bin/env python3 -import gc from cereal import car -from common.realtime import set_realtime_priority from common.params import Params import cereal.messaging as messaging from selfdrive.controls.lib.events import Events from selfdrive.monitoring.driver_monitor import DriverStatus, MAX_TERMINAL_ALERTS, MAX_TERMINAL_DURATION -from selfdrive.locationd.calibration_helpers import Calibration +from selfdrive.locationd.calibrationd import Calibration def dmonitoringd_thread(sm=None, pm=None): - gc.disable() - set_realtime_priority(53) - - # Pub/Sub Sockets if pm is None: pm = messaging.PubMaster(['dMonitoringState']) if sm is None: - sm = messaging.SubMaster(['driverState', 'liveCalibration', 'carState', 'model']) - - params = Params() + sm = messaging.SubMaster(['driverState', 'liveCalibration', 'carState', 'model'], poll=['driverState']) driver_status = DriverStatus() - is_rhd = params.get("IsRHD") - driver_status.is_rhd_region = is_rhd == b"1" - driver_status.is_rhd_region_checked = is_rhd is not None + driver_status.is_rhd_region = Params().get("IsRHD") == b"1" + + offroad = Params().get("IsOffroad") == b"1" sm['liveCalibration'].calStatus = Calibration.INVALID sm['liveCalibration'].rpyCalib = [0, 0, 0] @@ -39,12 +31,14 @@ def dmonitoringd_thread(sm=None, pm=None): v_cruise_last = 0 driver_engaged = False - offroad = params.get("IsOffroad") == b"1" # 10Hz <- dmonitoringmodeld while True: sm.update() + if not sm.updated['driverState']: + continue + # Get interaction if sm.updated['carState']: v_cruise = sm['carState'].cruiseState.speed @@ -56,43 +50,40 @@ def dmonitoringd_thread(sm=None, pm=None): driver_status.update(Events(), True, sm['carState'].cruiseState.enabled, sm['carState'].standstill) v_cruise_last = v_cruise - # Get model meta if sm.updated['model']: driver_status.set_policy(sm['model']) # Get data from dmonitoringmodeld - if sm.updated['driverState']: - events = Events() - driver_status.get_pose(sm['driverState'], sm['liveCalibration'].rpyCalib, sm['carState'].vEgo, sm['carState'].cruiseState.enabled) + events = Events() + driver_status.get_pose(sm['driverState'], sm['liveCalibration'].rpyCalib, sm['carState'].vEgo, sm['carState'].cruiseState.enabled) - # Block engaging after max number of distrations - if driver_status.terminal_alert_cnt >= MAX_TERMINAL_ALERTS or driver_status.terminal_time >= MAX_TERMINAL_DURATION: - events.add(car.CarEvent.EventName.tooDistracted) + # Block engaging after max number of distrations + if driver_status.terminal_alert_cnt >= MAX_TERMINAL_ALERTS or driver_status.terminal_time >= MAX_TERMINAL_DURATION: + events.add(car.CarEvent.EventName.tooDistracted) - # Update events from driver state - driver_status.update(events, driver_engaged, sm['carState'].cruiseState.enabled, sm['carState'].standstill) + # Update events from driver state + driver_status.update(events, driver_engaged, sm['carState'].cruiseState.enabled, sm['carState'].standstill) - # build dMonitoringState packet - dat = messaging.new_message('dMonitoringState') - dat.dMonitoringState = { - "events": events.to_msg(), - "faceDetected": driver_status.face_detected, - "isDistracted": driver_status.driver_distracted, - "awarenessStatus": driver_status.awareness, - "isRHD": driver_status.is_rhd_region, - "rhdChecked": driver_status.is_rhd_region_checked, - "posePitchOffset": driver_status.pose.pitch_offseter.filtered_stat.mean(), - "posePitchValidCount": driver_status.pose.pitch_offseter.filtered_stat.n, - "poseYawOffset": driver_status.pose.yaw_offseter.filtered_stat.mean(), - "poseYawValidCount": driver_status.pose.yaw_offseter.filtered_stat.n, - "stepChange": driver_status.step_change, - "awarenessActive": driver_status.awareness_active, - "awarenessPassive": driver_status.awareness_passive, - "isLowStd": driver_status.pose.low_std, - "hiStdCount": driver_status.hi_stds, - "isPreview": offroad, - } - pm.send('dMonitoringState', dat) + # build dMonitoringState packet + dat = messaging.new_message('dMonitoringState') + dat.dMonitoringState = { + "events": events.to_msg(), + "faceDetected": driver_status.face_detected, + "isDistracted": driver_status.driver_distracted, + "awarenessStatus": driver_status.awareness, + "isRHD": driver_status.is_rhd_region, + "posePitchOffset": driver_status.pose.pitch_offseter.filtered_stat.mean(), + "posePitchValidCount": driver_status.pose.pitch_offseter.filtered_stat.n, + "poseYawOffset": driver_status.pose.yaw_offseter.filtered_stat.mean(), + "poseYawValidCount": driver_status.pose.yaw_offseter.filtered_stat.n, + "stepChange": driver_status.step_change, + "awarenessActive": driver_status.awareness_active, + "awarenessPassive": driver_status.awareness_passive, + "isLowStd": driver_status.pose.low_std, + "hiStdCount": driver_status.hi_stds, + "isPreview": offroad, + } + pm.send('dMonitoringState', dat) def main(sm=None, pm=None): dmonitoringd_thread(sm, pm) diff --git a/selfdrive/monitoring/driver_monitor.py b/selfdrive/monitoring/driver_monitor.py index 76487e24e..1122e1b7a 100644 --- a/selfdrive/monitoring/driver_monitor.py +++ b/selfdrive/monitoring/driver_monitor.py @@ -15,8 +15,8 @@ EventName = car.CarEvent.EventName # ****************************************************************************************** _AWARENESS_TIME = 70. # one minute limit without user touching steering wheels make the car enter a terminal status -_AWARENESS_PRE_TIME_TILL_TERMINAL = 15. # a first alert is issued 25s before expiration -_AWARENESS_PROMPT_TIME_TILL_TERMINAL = 6. # a second alert is issued 15s before start decelerating the car +_AWARENESS_PRE_TIME_TILL_TERMINAL = 15. # a first alert is issued 15s before expiration +_AWARENESS_PROMPT_TIME_TILL_TERMINAL = 6. # a second alert is issued 6s before start decelerating the car _DISTRACTED_TIME = 11. _DISTRACTED_PRE_TIME_TILL_TERMINAL = 8. _DISTRACTED_PROMPT_TIME_TILL_TERMINAL = 6. @@ -24,10 +24,10 @@ _DISTRACTED_PROMPT_TIME_TILL_TERMINAL = 6. _FACE_THRESHOLD = 0.6 _EYE_THRESHOLD = 0.6 _SG_THRESHOLD = 0.5 -_BLINK_THRESHOLD = 0.5 # 0.225 +_BLINK_THRESHOLD = 0.5 _BLINK_THRESHOLD_SLACK = 0.65 _BLINK_THRESHOLD_STRICT = 0.5 -_PITCH_WEIGHT = 1.35 # 1.5 # pitch matters a lot more +_PITCH_WEIGHT = 1.35 # pitch matters a lot more _POSESTD_THRESHOLD = 0.14 _METRIC_THRESHOLD = 0.4 _METRIC_THRESHOLD_SLACK = 0.55 @@ -116,6 +116,7 @@ class DriverStatus(): self.step_change = 0. self.active_monitoring_mode = True self.hi_stds = 0 + self.hi_std_alert_enabled = True self.threshold_prompt = _DISTRACTED_PROMPT_TIME_TILL_TERMINAL / _DISTRACTED_TIME self.is_rhd_region = False @@ -212,7 +213,7 @@ class DriverStatus(): self._set_timers(self.face_detected and not is_model_uncertain) if self.face_detected and not self.pose.low_std: if not is_model_uncertain: - self.step_change *= max(0, (model_std_max-0.5)*(model_std_max-2)) + self.step_change *= min(1.0, max(0.6, 1.6*(model_std_max-0.5)*(model_std_max-2))) self.hi_stds += 1 elif self.face_detected and self.pose.low_std: self.hi_stds = 0 @@ -228,8 +229,9 @@ class DriverStatus(): driver_attentive = self.driver_distraction_filter.x < 0.37 awareness_prev = self.awareness - if self.face_detected and self.hi_stds * DT_DMON > _HI_STD_TIMEOUT: + if self.face_detected and self.hi_stds * DT_DMON > _HI_STD_TIMEOUT and self.hi_std_alert_enabled: events.add(EventName.driverMonitorLowAcc) + self.hi_std_alert_enabled = False # only showed once until orange prompt resets it if (driver_attentive and self.face_detected and self.pose.low_std and self.awareness > 0): # only restore awareness when paying attention and alert is not red @@ -255,6 +257,7 @@ class DriverStatus(): elif self.awareness <= self.threshold_prompt: # prompt orange alert alert = EventName.promptDriverDistracted if self.active_monitoring_mode else EventName.promptDriverUnresponsive + self.hi_std_alert_enabled = True elif self.awareness <= self.threshold_pre: # pre green alert alert = EventName.preDriverDistracted if self.active_monitoring_mode else EventName.preDriverUnresponsive diff --git a/selfdrive/pandad.py b/selfdrive/pandad.py index 64fde8668..a3ddaf484 100755 --- a/selfdrive/pandad.py +++ b/selfdrive/pandad.py @@ -3,8 +3,25 @@ import os import time +from common.hardware import TICI +from common.gpio import GPIO_HUB_RST_N, GPIO_STM_BOOT0, GPIO_STM_RST_N, gpio_init, gpio_set +from panda import BASEDIR, Panda, PandaDFU, build_st from selfdrive.swaglog import cloudlog -from panda import Panda, PandaDFU, BASEDIR, build_st + + +def set_panda_power(power=True): + if not TICI: + return + + gpio_init(GPIO_STM_RST_N, True) + gpio_init(GPIO_STM_BOOT0, True) + + gpio_set(GPIO_STM_RST_N, False) + gpio_set(GPIO_HUB_RST_N, True) + + time.sleep(0.1) + + gpio_set(GPIO_STM_RST_N, power) def get_firmware_fn(): @@ -88,10 +105,12 @@ def update_panda(): def main(): + set_panda_power() update_panda() os.chdir("boardd") os.execvp("./boardd", ["./boardd"]) + if __name__ == "__main__": main() diff --git a/selfdrive/proclogd/proclogd.cc b/selfdrive/proclogd/proclogd.cc index c8e5f65a5..24b42c5bf 100644 --- a/selfdrive/proclogd/proclogd.cc +++ b/selfdrive/proclogd/proclogd.cc @@ -38,11 +38,8 @@ int main() { while (1) { - capnp::MallocMessageBuilder msg; - cereal::Event::Builder event = msg.initRoot(); - event.setLogMonoTime(nanos_since_boot()); - auto procLog = event.initProcLog(); - + MessageBuilder msg; + auto procLog = msg.initEvent().initProcLog(); auto orphanage = msg.getOrphanage(); // stat diff --git a/selfdrive/registration.py b/selfdrive/registration.py index 6e1596f35..de2231a45 100644 --- a/selfdrive/registration.py +++ b/selfdrive/registration.py @@ -4,12 +4,13 @@ import json from datetime import datetime, timedelta from selfdrive.swaglog import cloudlog from selfdrive.version import version, terms_version, training_version, get_git_commit, get_git_branch, get_git_remote -from common.android import get_imei, get_serial, get_subscriber_info +from common.hardware import HARDWARE from common.api import api_get from common.params import Params from common.file_helpers import mkdirs_exists_ok from common.basedir import PERSIST + def register(): params = Params() params.put("Version", version) @@ -19,7 +20,7 @@ def register(): params.put("GitCommit", get_git_commit(default="")) params.put("GitBranch", get_git_branch(default="")) params.put("GitRemote", get_git_remote(default="")) - params.put("SubscriberInfo", get_subscriber_info()) + params.put("SubscriberInfo", HARDWARE.get_subscriber_info()) # create a key for auth # your private key is kept on your device persist partition and never sent to our servers @@ -50,7 +51,7 @@ def register(): try: cloudlog.info("getting pilotauth") resp = api_get("v2/pilotauth/", method='POST', timeout=15, - imei=get_imei(0), imei2=get_imei(1), serial=get_serial(), public_key=public_key, register_token=register_token) + imei=HARDWARE.get_imei(0), imei2=HARDWARE.get_imei(1), serial=HARDWARE.get_serial(), public_key=public_key, register_token=register_token) dongleauth = json.loads(resp.text) dongle_id = dongleauth["dongle_id"] diff --git a/selfdrive/rtshield.py b/selfdrive/rtshield.py new file mode 100644 index 000000000..e76022501 --- /dev/null +++ b/selfdrive/rtshield.py @@ -0,0 +1,16 @@ +#!/usr/bin/env python3 +import time +from common.realtime import set_core_affinity, set_realtime_priority + + +# RT shield - ensure CPU 3 always remains available for RT processes +# runs as SCHED_FIFO with minimum priority to ensure kthreads don't +# get scheduled onto CPU 3, but it's always preemptible by realtime +# openpilot processes + +def main(): + set_core_affinity(3) + set_realtime_priority(1) + + while True: + time.sleep(0.000001) diff --git a/selfdrive/sensord/SConscript b/selfdrive/sensord/SConscript index b578ac951..b5c8b78f9 100644 --- a/selfdrive/sensord/SConscript +++ b/selfdrive/sensord/SConscript @@ -1,5 +1,17 @@ -Import('env', 'common', 'cereal', 'messaging') -env.Program('_sensord', 'sensors.cc', LIBS=['hardware', common, cereal, messaging, 'capnp', 'zmq', 'kj']) -lenv = env.Clone() -lenv['LIBPATH'] += ['/system/vendor/lib64'] -lenv.Program('_gpsd', ['gpsd.cc'], LIBS=['hardware', common, 'diag', 'time_genoff', cereal, messaging, 'capnp', 'zmq', 'kj']) +Import('env', 'arch', 'common', 'cereal', 'messaging') +if arch == "aarch64": + env.Program('_sensord', 'sensors_qcom.cc', LIBS=['hardware', common, cereal, messaging, 'capnp', 'zmq', 'kj']) + lenv = env.Clone() + lenv['LIBPATH'] += ['/system/vendor/lib64'] + lenv.Program('_gpsd', ['gpsd.cc'], LIBS=['hardware', common, 'diag', 'time_genoff', cereal, messaging, 'capnp', 'zmq', 'kj']) +else: + sensors = [ + 'sensors/file_sensor.cc', + 'sensors/i2c_sensor.cc', + 'sensors/light_sensor.cc', + 'sensors/bmx055_accel.cc', + 'sensors/bmx055_gyro.cc', + 'sensors/bmx055_magn.cc', + 'sensors/bmx055_temp.cc', + ] + env.Program('_sensord', ['sensors_qcom2.cc'] + sensors, LIBS=[common, cereal, messaging, 'capnp', 'zmq', 'kj']) diff --git a/selfdrive/sensord/gpsd.cc b/selfdrive/sensord/gpsd.cc index 4bf71faf6..1978a239c 100644 --- a/selfdrive/sensord/gpsd.cc +++ b/selfdrive/sensord/gpsd.cc @@ -35,14 +35,10 @@ void set_do_exit(int sig) { void nmea_callback(GpsUtcTime timestamp, const char* nmea, int length) { - uint64_t log_time = nanos_since_boot(); uint64_t log_time_wall = nanos_since_epoch(); - capnp::MallocMessageBuilder msg; - cereal::Event::Builder event = msg.initRoot(); - event.setLogMonoTime(log_time); - - auto nmeaData = event.initGpsNMEA(); + MessageBuilder msg; + auto nmeaData = msg.initEvent().initGpsNMEA(); nmeaData.setTimestamp(timestamp); nmeaData.setLocalWallTime(log_time_wall); nmeaData.setNmea(nmea); @@ -52,13 +48,9 @@ void nmea_callback(GpsUtcTime timestamp, const char* nmea, int length) { void location_callback(GpsLocation* location) { //printf("got location callback\n"); - uint64_t log_time = nanos_since_boot(); - capnp::MallocMessageBuilder msg; - cereal::Event::Builder event = msg.initRoot(); - event.setLogMonoTime(log_time); - - auto locationData = event.initGpsLocation(); + MessageBuilder msg; + auto locationData = msg.initEvent().initGpsLocation(); locationData.setFlags(location->flags); locationData.setLatitude(location->latitude); locationData.setLongitude(location->longitude); diff --git a/selfdrive/sensord/sensors/bmx055_accel.cc b/selfdrive/sensord/sensors/bmx055_accel.cc new file mode 100644 index 000000000..5eea0bcdf --- /dev/null +++ b/selfdrive/sensord/sensors/bmx055_accel.cc @@ -0,0 +1,71 @@ +#include +#include "common/swaglog.h" +#include "common/timing.h" + +#include "bmx055_accel.hpp" + + +BMX055_Accel::BMX055_Accel(I2CBus *bus) : I2CSensor(bus) {} + +int BMX055_Accel::init(){ + int ret = 0; + uint8_t buffer[1]; + + ret = read_register(BMX055_ACCEL_I2C_REG_ID, buffer, 1); + if(ret < 0){ + LOGE("Reading chip ID failed: %d", ret); + goto fail; + } + + if(buffer[0] != BMX055_ACCEL_CHIP_ID){ + LOGE("Chip ID wrong. Got: %d, Expected %d", buffer[0], BMX055_ACCEL_CHIP_ID); + ret = -1; + goto fail; + } + + // High bandwidth + // ret = set_register(BMX055_ACCEL_I2C_REG_HBW, BMX055_ACCEL_HBW_ENABLE); + // if (ret < 0){ + // goto fail; + // } + + // Low bandwidth + ret = set_register(BMX055_ACCEL_I2C_REG_HBW, BMX055_ACCEL_HBW_DISABLE); + if (ret < 0){ + goto fail; + } + ret = set_register(BMX055_ACCEL_I2C_REG_BW, BMX055_ACCEL_BW_125HZ); + if (ret < 0){ + goto fail; + } + +fail: + return ret; +} + +void BMX055_Accel::get_event(cereal::SensorEventData::Builder &event){ + uint64_t start_time = nanos_since_boot(); + uint8_t buffer[6]; + int len = read_register(BMX055_ACCEL_I2C_REG_FIFO, buffer, sizeof(buffer)); + assert(len == 6); + + // 12 bit = +-2g + float scale = 9.81 * 2.0f / (1 << 11); + float x = -read_12_bit(buffer[0], buffer[1]) * scale; + float y = -read_12_bit(buffer[2], buffer[3]) * scale; + float z = read_12_bit(buffer[4], buffer[5]) * scale; + + event.setSource(cereal::SensorEventData::SensorSource::BMX055); + event.setVersion(1); + event.setSensor(SENSOR_ACCELEROMETER); + event.setType(SENSOR_TYPE_ACCELEROMETER); + event.setTimestamp(start_time); + + float xyz[] = {x, y, z}; + kj::ArrayPtr vs(&xyz[0], 3); + + auto svec = event.initAcceleration(); + svec.setV(vs); + svec.setStatus(true); + +} diff --git a/selfdrive/sensord/sensors/bmx055_accel.hpp b/selfdrive/sensord/sensors/bmx055_accel.hpp new file mode 100644 index 000000000..4e613af7c --- /dev/null +++ b/selfdrive/sensord/sensors/bmx055_accel.hpp @@ -0,0 +1,36 @@ +#pragma once + +#include "sensors/i2c_sensor.hpp" + +// Address of the chip on the bus +#define BMX055_ACCEL_I2C_ADDR 0x18 + +// Registers of the chip +#define BMX055_ACCEL_I2C_REG_ID 0x00 +#define BMX055_ACCEL_I2C_REG_TEMP 0x08 +#define BMX055_ACCEL_I2C_REG_BW 0x10 +#define BMX055_ACCEL_I2C_REG_HBW 0x13 +#define BMX055_ACCEL_I2C_REG_FIFO 0x3F + +// Constants +#define BMX055_ACCEL_CHIP_ID 0xFA + +#define BMX055_ACCEL_HBW_ENABLE 0b10000000 +#define BMX055_ACCEL_HBW_DISABLE 0b00000000 + +#define BMX055_ACCEL_BW_7_81HZ 0b01000 +#define BMX055_ACCEL_BW_15_63HZ 0b01001 +#define BMX055_ACCEL_BW_31_25HZ 0b01010 +#define BMX055_ACCEL_BW_62_5HZ 0b01011 +#define BMX055_ACCEL_BW_125HZ 0b01100 +#define BMX055_ACCEL_BW_250HZ 0b01101 +#define BMX055_ACCEL_BW_500HZ 0b01110 +#define BMX055_ACCEL_BW_1000HZ 0b01111 + +class BMX055_Accel : public I2CSensor { + uint8_t get_device_address() {return BMX055_ACCEL_I2C_ADDR;} +public: + BMX055_Accel(I2CBus *bus); + int init(); + void get_event(cereal::SensorEventData::Builder &event); +}; diff --git a/selfdrive/sensord/sensors/bmx055_gyro.cc b/selfdrive/sensord/sensors/bmx055_gyro.cc new file mode 100644 index 000000000..61e3d9e40 --- /dev/null +++ b/selfdrive/sensord/sensors/bmx055_gyro.cc @@ -0,0 +1,81 @@ +#include +#include +#include "common/swaglog.h" + +#include "bmx055_gyro.hpp" + +#define DEG2RAD(x) ((x) * M_PI / 180.0) + + +BMX055_Gyro::BMX055_Gyro(I2CBus *bus) : I2CSensor(bus) {} + +int BMX055_Gyro::init(){ + int ret = 0; + uint8_t buffer[1]; + + ret =read_register(BMX055_GYRO_I2C_REG_ID, buffer, 1); + if(ret < 0){ + LOGE("Reading chip ID failed: %d", ret); + goto fail; + } + + if(buffer[0] != BMX055_GYRO_CHIP_ID){ + LOGE("Chip ID wrong. Got: %d, Expected %d", buffer[0], BMX055_GYRO_CHIP_ID); + ret = -1; + goto fail; + } + + // High bandwidth + // ret = set_register(BMX055_GYRO_I2C_REG_HBW, BMX055_GYRO_HBW_ENABLE); + // if (ret < 0){ + // goto fail; + // } + + // Low bandwidth + ret = set_register(BMX055_GYRO_I2C_REG_HBW, BMX055_GYRO_HBW_DISABLE); + if (ret < 0){ + goto fail; + } + + // 116 Hz filter + ret = set_register(BMX055_GYRO_I2C_REG_BW, BMX055_GYRO_BW_116HZ); + if (ret < 0){ + goto fail; + } + + // +- 125 deg/s range + ret = set_register(BMX055_GYRO_I2C_REG_RANGE, BMX055_GYRO_RANGE_125); + if (ret < 0){ + goto fail; + } + +fail: + return ret; +} + +void BMX055_Gyro::get_event(cereal::SensorEventData::Builder &event){ + uint64_t start_time = nanos_since_boot(); + uint8_t buffer[6]; + int len = read_register(BMX055_GYRO_I2C_REG_FIFO, buffer, sizeof(buffer)); + assert(len == 6); + + // 16 bit = +- 125 deg/s + float scale = 125.0f / (1 << 15); + float x = -DEG2RAD(read_16_bit(buffer[0], buffer[1]) * scale); + float y = -DEG2RAD(read_16_bit(buffer[2], buffer[3]) * scale); + float z = DEG2RAD(read_16_bit(buffer[4], buffer[5]) * scale); + + event.setSource(cereal::SensorEventData::SensorSource::BMX055); + event.setVersion(1); + event.setSensor(SENSOR_GYRO_UNCALIBRATED); + event.setType(SENSOR_TYPE_GYROSCOPE_UNCALIBRATED); + event.setTimestamp(start_time); + + float xyz[] = {x, y, z}; + kj::ArrayPtr vs(&xyz[0], 3); + + auto svec = event.initGyroUncalibrated(); + svec.setV(vs); + svec.setStatus(true); + +} diff --git a/selfdrive/sensord/sensors/bmx055_gyro.hpp b/selfdrive/sensord/sensors/bmx055_gyro.hpp new file mode 100644 index 000000000..407ee1608 --- /dev/null +++ b/selfdrive/sensord/sensors/bmx055_gyro.hpp @@ -0,0 +1,36 @@ +#pragma once + +#include "sensors/i2c_sensor.hpp" + +// Address of the chip on the bus +#define BMX055_GYRO_I2C_ADDR 0x68 + +// Registers of the chip +#define BMX055_GYRO_I2C_REG_ID 0x00 +#define BMX055_GYRO_I2C_REG_RANGE 0x0F +#define BMX055_GYRO_I2C_REG_BW 0x10 +#define BMX055_GYRO_I2C_REG_HBW 0x13 +#define BMX055_GYRO_I2C_REG_FIFO 0x3F + +// Constants +#define BMX055_GYRO_CHIP_ID 0x0F + +#define BMX055_GYRO_HBW_ENABLE 0b10000000 +#define BMX055_GYRO_HBW_DISABLE 0b00000000 + +#define BMX055_GYRO_RANGE_2000 0b000 +#define BMX055_GYRO_RANGE_1000 0b001 +#define BMX055_GYRO_RANGE_500 0b010 +#define BMX055_GYRO_RANGE_250 0b011 +#define BMX055_GYRO_RANGE_125 0b100 + +#define BMX055_GYRO_BW_116HZ 0b0010 + + +class BMX055_Gyro : public I2CSensor { + uint8_t get_device_address() {return BMX055_GYRO_I2C_ADDR;} +public: + BMX055_Gyro(I2CBus *bus); + int init(); + void get_event(cereal::SensorEventData::Builder &event); +}; diff --git a/selfdrive/sensord/sensors/bmx055_magn.cc b/selfdrive/sensord/sensors/bmx055_magn.cc new file mode 100644 index 000000000..1c23ceb6a --- /dev/null +++ b/selfdrive/sensord/sensors/bmx055_magn.cc @@ -0,0 +1,109 @@ +#include +#include +#include + +#include "common/swaglog.h" + +#include "bmx055_magn.hpp" + +int16_t parse_xy(uint8_t lsb, uint8_t msb){ + // 13 bit + uint16_t combined = (uint16_t(msb) << 5) | uint16_t(lsb >> 3); + return int16_t(combined << 3) / (1 << 3); +} + +int16_t parse_z(uint8_t lsb, uint8_t msb){ + // 15 bit + uint16_t combined = (uint16_t(msb) << 7) | uint16_t(lsb >> 1); + return int16_t(combined << 1) / (1 << 1); +} + +uint16_t parse_rhall(uint8_t lsb, uint8_t msb){ + // 14 bit + return (uint16_t(msb) << 6) | uint16_t(lsb >> 2); +} + +BMX055_Magn::BMX055_Magn(I2CBus *bus) : I2CSensor(bus) {} + +int BMX055_Magn::init(){ + int ret; + uint8_t buffer[1]; + + // suspend -> sleep + ret = set_register(BMX055_MAGN_I2C_REG_PWR_0, 0x01); + if(ret < 0){ + LOGE("Enabling power failed: %d", ret); + goto fail; + } + usleep(5 * 1000); // wait until the chip is powered on + + // read chip ID + ret = read_register(BMX055_MAGN_I2C_REG_ID, buffer, 1); + if(ret < 0){ + LOGE("Reading chip ID failed: %d", ret); + goto fail; + } + + if(buffer[0] != BMX055_MAGN_CHIP_ID){ + LOGE("Chip ID wrong. Got: %d, Expected %d", buffer[0], BMX055_MAGN_CHIP_ID); + return -1; + } + + // TODO: perform self-test + + // 9 REPXY and 15 REPZ for 100 Hz + // 3 REPXY and 3 REPZ for > 300 Hz + ret = set_register(BMX055_MAGN_I2C_REG_REPXY, (3 - 1) / 2); + if (ret < 0){ + goto fail; + } + + ret = set_register(BMX055_MAGN_I2C_REG_REPZ, 3 - 1); + if (ret < 0){ + goto fail; + } + + return 0; + + fail: + return ret; +} + + +void BMX055_Magn::get_event(cereal::SensorEventData::Builder &event){ + uint64_t start_time = nanos_since_boot(); + uint8_t buffer[8]; + + int len = read_register(BMX055_MAGN_I2C_REG_DATAX_LSB, buffer, sizeof(buffer)); + assert(len == sizeof(buffer)); + + bool ready = buffer[6] & 0x1; + if (ready){ + float x = parse_xy(buffer[0], buffer[1]); + float y = parse_xy(buffer[2], buffer[3]); + float z = parse_z(buffer[4], buffer[5]); + //uint16_t rhall = parse_rhall(buffer[5], buffer[6]); + + // TODO: convert to micro tesla: + // https://github.com/BoschSensortec/BMM150-Sensor-API/blob/master/bmm150.c#L1614 + + event.setSource(cereal::SensorEventData::SensorSource::BMX055); + event.setVersion(1); + event.setSensor(SENSOR_MAGNETOMETER_UNCALIBRATED); + event.setType(SENSOR_TYPE_MAGNETIC_FIELD_UNCALIBRATED); + event.setTimestamp(start_time); + + float xyz[] = {x, y, z}; + kj::ArrayPtr vs(&xyz[0], 3); + + auto svec = event.initMagneticUncalibrated(); + svec.setV(vs); + svec.setStatus(true); + } + + // The BMX055 Magnetometer has no FIFO mode. Self running mode only goes + // up to 30 Hz. Therefore we put in forced mode, and request measurements + // at a 100 Hz. When reading the registers we have to check the ready bit + // To verify the measurement was comleted this cycle. + set_register(BMX055_MAGN_I2C_REG_MAG, BMX055_MAGN_FORCED); +} diff --git a/selfdrive/sensord/sensors/bmx055_magn.hpp b/selfdrive/sensord/sensors/bmx055_magn.hpp new file mode 100644 index 000000000..ba4942d72 --- /dev/null +++ b/selfdrive/sensord/sensors/bmx055_magn.hpp @@ -0,0 +1,27 @@ +#pragma once + +#include "sensors/i2c_sensor.hpp" + +// Address of the chip on the bus +#define BMX055_MAGN_I2C_ADDR 0x10 + +// Registers of the chip +#define BMX055_MAGN_I2C_REG_ID 0x40 +#define BMX055_MAGN_I2C_REG_PWR_0 0x4B +#define BMX055_MAGN_I2C_REG_MAG 0x4C +#define BMX055_MAGN_I2C_REG_DATAX_LSB 0x42 +#define BMX055_MAGN_I2C_REG_RHALL_LSB 0x48 +#define BMX055_MAGN_I2C_REG_REPXY 0x51 +#define BMX055_MAGN_I2C_REG_REPZ 0x52 + +// Constants +#define BMX055_MAGN_CHIP_ID 0x32 +#define BMX055_MAGN_FORCED (0b01 << 1) + +class BMX055_Magn : public I2CSensor{ + uint8_t get_device_address() {return BMX055_MAGN_I2C_ADDR;} +public: + BMX055_Magn(I2CBus *bus); + int init(); + void get_event(cereal::SensorEventData::Builder &event); +}; diff --git a/selfdrive/sensord/sensors/bmx055_temp.cc b/selfdrive/sensord/sensors/bmx055_temp.cc new file mode 100644 index 000000000..86f7d4f0c --- /dev/null +++ b/selfdrive/sensord/sensors/bmx055_temp.cc @@ -0,0 +1,44 @@ +#include +#include "common/swaglog.h" +#include "common/timing.h" + +#include "bmx055_temp.hpp" +#include "bmx055_accel.hpp" + + +BMX055_Temp::BMX055_Temp(I2CBus *bus) : I2CSensor(bus) {} + +int BMX055_Temp::init(){ + int ret = 0; + uint8_t buffer[1]; + + ret = read_register(BMX055_ACCEL_I2C_REG_ID, buffer, 1); + if(ret < 0){ + LOGE("Reading chip ID failed: %d", ret); + goto fail; + } + + if(buffer[0] != BMX055_ACCEL_CHIP_ID){ + LOGE("Chip ID wrong. Got: %d, Expected %d", buffer[0], BMX055_ACCEL_CHIP_ID); + ret = -1; + goto fail; + } + +fail: + return ret; +} + +void BMX055_Temp::get_event(cereal::SensorEventData::Builder &event){ + uint64_t start_time = nanos_since_boot(); + uint8_t buffer[1]; + int len = read_register(BMX055_ACCEL_I2C_REG_TEMP, buffer, sizeof(buffer)); + assert(len == sizeof(buffer)); + + float temp = 23.0f + int8_t(buffer[0]) / 2.0f; + + event.setSource(cereal::SensorEventData::SensorSource::BMX055); + event.setVersion(1); + event.setType(SENSOR_TYPE_AMBIENT_TEMPERATURE); + event.setTimestamp(start_time); + event.setTemperature(temp); +} diff --git a/selfdrive/sensord/sensors/bmx055_temp.hpp b/selfdrive/sensord/sensors/bmx055_temp.hpp new file mode 100644 index 000000000..8b7119a60 --- /dev/null +++ b/selfdrive/sensord/sensors/bmx055_temp.hpp @@ -0,0 +1,13 @@ +#pragma once + +#include "sensors/i2c_sensor.hpp" +#include "sensors/bmx055_accel.hpp" + + +class BMX055_Temp : public I2CSensor { + uint8_t get_device_address() {return BMX055_ACCEL_I2C_ADDR;} +public: + BMX055_Temp(I2CBus *bus); + int init(); + void get_event(cereal::SensorEventData::Builder &event); +}; diff --git a/selfdrive/sensord/sensors/constants.hpp b/selfdrive/sensord/sensors/constants.hpp new file mode 100644 index 000000000..c216f838a --- /dev/null +++ b/selfdrive/sensord/sensors/constants.hpp @@ -0,0 +1,18 @@ +#pragma once + + +#define SENSOR_ACCELEROMETER 1 +#define SENSOR_MAGNETOMETER 2 +#define SENSOR_MAGNETOMETER_UNCALIBRATED 3 +#define SENSOR_GYRO 4 +#define SENSOR_GYRO_UNCALIBRATED 5 +#define SENSOR_LIGHT 7 + +#define SENSOR_TYPE_ACCELEROMETER 1 +#define SENSOR_TYPE_GEOMAGNETIC_FIELD 2 +#define SENSOR_TYPE_GYROSCOPE 4 +#define SENSOR_TYPE_LIGHT 5 +#define SENSOR_TYPE_AMBIENT_TEMPERATURE 13 +#define SENSOR_TYPE_MAGNETIC_FIELD_UNCALIBRATED 14 +#define SENSOR_TYPE_MAGNETIC_FIELD SENSOR_TYPE_GEOMAGNETIC_FIELD +#define SENSOR_TYPE_GYROSCOPE_UNCALIBRATED 16 diff --git a/selfdrive/sensord/sensors/file_sensor.cc b/selfdrive/sensord/sensors/file_sensor.cc new file mode 100644 index 000000000..6d03ef1b1 --- /dev/null +++ b/selfdrive/sensord/sensors/file_sensor.cc @@ -0,0 +1,15 @@ +#include +#include + +#include "file_sensor.hpp" + +FileSensor::FileSensor(std::string filename) : file(filename) { +} + +int FileSensor::init() { + return file.is_open() ? 0 : 1; +} + +FileSensor::~FileSensor(){ + file.close(); +} diff --git a/selfdrive/sensord/sensors/file_sensor.hpp b/selfdrive/sensord/sensors/file_sensor.hpp new file mode 100644 index 000000000..25a6f203c --- /dev/null +++ b/selfdrive/sensord/sensors/file_sensor.hpp @@ -0,0 +1,19 @@ +#pragma once + +#include +#include + +#include "cereal/gen/cpp/log.capnp.h" +#include "sensors/sensor.hpp" + + +class FileSensor : public Sensor { +protected: + std::ifstream file; + +public: + FileSensor(std::string filename); + ~FileSensor(); + int init(); + virtual void get_event(cereal::SensorEventData::Builder &event) = 0; +}; diff --git a/selfdrive/sensord/sensors/i2c_sensor.cc b/selfdrive/sensord/sensors/i2c_sensor.cc new file mode 100644 index 000000000..e3000c8b0 --- /dev/null +++ b/selfdrive/sensord/sensors/i2c_sensor.cc @@ -0,0 +1,24 @@ +#include +#include "i2c_sensor.hpp" + +int16_t read_12_bit(uint8_t lsb, uint8_t msb){ + uint16_t combined = (uint16_t(msb) << 8) | uint16_t(lsb & 0xF0); + return int16_t(combined) / (1 << 4); +} + +int16_t read_16_bit(uint8_t lsb, uint8_t msb){ + uint16_t combined = (uint16_t(msb) << 8) | uint16_t(lsb); + return int16_t(combined); +} + + +I2CSensor::I2CSensor(I2CBus *bus) : bus(bus){ +} + +int I2CSensor::read_register(uint register_address, uint8_t *buffer, uint8_t len){ + return bus->read_register(get_device_address(), register_address, buffer, len); +} + +int I2CSensor::set_register(uint register_address, uint8_t data){ + return bus->set_register(get_device_address(), register_address, data); +} diff --git a/selfdrive/sensord/sensors/i2c_sensor.hpp b/selfdrive/sensord/sensors/i2c_sensor.hpp new file mode 100644 index 000000000..39ef79cf8 --- /dev/null +++ b/selfdrive/sensord/sensors/i2c_sensor.hpp @@ -0,0 +1,24 @@ +#pragma once + +#include +#include "cereal/gen/cpp/log.capnp.h" +#include "common/i2c.h" +#include "sensors/sensor.hpp" +#include "sensors/constants.hpp" + +int16_t read_12_bit(uint8_t lsb, uint8_t msb); +int16_t read_16_bit(uint8_t lsb, uint8_t msb); + + +class I2CSensor : public Sensor { +private: + I2CBus *bus; + virtual uint8_t get_device_address() = 0; + +public: + I2CSensor(I2CBus *bus); + int read_register(uint register_address, uint8_t *buffer, uint8_t len); + int set_register(uint register_address, uint8_t data); + virtual int init() = 0; + virtual void get_event(cereal::SensorEventData::Builder &event) = 0; +}; diff --git a/selfdrive/sensord/sensors/light_sensor.cc b/selfdrive/sensord/sensors/light_sensor.cc new file mode 100644 index 000000000..eb3eb05f1 --- /dev/null +++ b/selfdrive/sensord/sensors/light_sensor.cc @@ -0,0 +1,23 @@ +#include +#include + +#include "common/timing.h" + +#include "light_sensor.hpp" +#include "constants.hpp" + +void LightSensor::get_event(cereal::SensorEventData::Builder &event){ + uint64_t start_time = nanos_since_boot(); + file.clear(); + file.seekg(0); + + int value; + file >> value; + + event.setSource(cereal::SensorEventData::SensorSource::RPR0521); + event.setVersion(1); + event.setSensor(SENSOR_LIGHT); + event.setType(SENSOR_TYPE_LIGHT); + event.setTimestamp(start_time); + event.setLight(value); +} diff --git a/selfdrive/sensord/sensors/light_sensor.hpp b/selfdrive/sensord/sensors/light_sensor.hpp new file mode 100644 index 000000000..7c98cb29c --- /dev/null +++ b/selfdrive/sensord/sensors/light_sensor.hpp @@ -0,0 +1,8 @@ +#pragma once +#include "file_sensor.hpp" + +class LightSensor : public FileSensor { +public: + LightSensor(std::string filename) : FileSensor(filename){}; + void get_event(cereal::SensorEventData::Builder &event); +}; diff --git a/selfdrive/sensord/sensors/sensor.hpp b/selfdrive/sensord/sensors/sensor.hpp new file mode 100644 index 000000000..91f417908 --- /dev/null +++ b/selfdrive/sensord/sensors/sensor.hpp @@ -0,0 +1,9 @@ +#pragma once + +#include "cereal/gen/cpp/log.capnp.h" + +class Sensor { +public: + virtual int init() = 0; + virtual void get_event(cereal::SensorEventData::Builder &event) = 0; +}; diff --git a/selfdrive/sensord/sensors.cc b/selfdrive/sensord/sensors_qcom.cc similarity index 67% rename from selfdrive/sensord/sensors.cc rename to selfdrive/sensord/sensors_qcom.cc index b42482d85..4119d8ede 100644 --- a/selfdrive/sensord/sensors.cc +++ b/selfdrive/sensord/sensors_qcom.cc @@ -10,7 +10,8 @@ #include #include -#include +#include +#include #include #include @@ -20,15 +21,14 @@ #include "common/timing.h" #include "common/swaglog.h" +// ACCELEROMETER_UNCALIBRATED is only in Android O +// https://developer.android.com/reference/android/hardware/Sensor.html#STRING_TYPE_ACCELEROMETER_UNCALIBRATED + #define SENSOR_ACCELEROMETER 1 #define SENSOR_MAGNETOMETER 2 #define SENSOR_GYRO 4 - -// ACCELEROMETER_UNCALIBRATED is only in Android O -// https://developer.android.com/reference/android/hardware/Sensor.html#STRING_TYPE_ACCELEROMETER_UNCALIBRATED #define SENSOR_MAGNETOMETER_UNCALIBRATED 3 #define SENSOR_GYRO_UNCALIBRATED 5 - #define SENSOR_PROXIMITY 6 #define SENSOR_LIGHT 7 @@ -70,30 +70,33 @@ void sensor_loop() { LOGD("sensor %4d: %4d %60s %d-%ld us", i, list[i].handle, list[i].name, list[i].minDelay, list[i].maxDelay); } - device->activate(device, SENSOR_MAGNETOMETER_UNCALIBRATED, 0); - device->activate(device, SENSOR_GYRO_UNCALIBRATED, 0); - device->activate(device, SENSOR_ACCELEROMETER, 0); - device->activate(device, SENSOR_MAGNETOMETER, 0); - device->activate(device, SENSOR_GYRO, 0); - device->activate(device, SENSOR_PROXIMITY, 0); - device->activate(device, SENSOR_LIGHT, 0); + std::set sensor_types = { + SENSOR_TYPE_ACCELEROMETER, + SENSOR_TYPE_MAGNETIC_FIELD_UNCALIBRATED, + SENSOR_TYPE_MAGNETIC_FIELD, + SENSOR_TYPE_GYROSCOPE_UNCALIBRATED, + SENSOR_TYPE_GYROSCOPE, + SENSOR_TYPE_PROXIMITY, + SENSOR_TYPE_LIGHT, + }; - device->activate(device, SENSOR_MAGNETOMETER_UNCALIBRATED, 1); - device->activate(device, SENSOR_GYRO_UNCALIBRATED, 1); - device->activate(device, SENSOR_ACCELEROMETER, 1); - device->activate(device, SENSOR_MAGNETOMETER, 1); - device->activate(device, SENSOR_GYRO, 1); - device->activate(device, SENSOR_PROXIMITY, 1); - device->activate(device, SENSOR_LIGHT, 1); + std::map sensors = { + {SENSOR_GYRO_UNCALIBRATED, ms2ns(10)}, + {SENSOR_MAGNETOMETER_UNCALIBRATED, ms2ns(100)}, + {SENSOR_ACCELEROMETER, ms2ns(10)}, + {SENSOR_GYRO, ms2ns(10)}, + {SENSOR_MAGNETOMETER, ms2ns(100)}, + {SENSOR_PROXIMITY, ms2ns(100)}, + {SENSOR_LIGHT, ms2ns(100)} + }; - device->setDelay(device, SENSOR_GYRO_UNCALIBRATED, ms2ns(10)); - device->setDelay(device, SENSOR_MAGNETOMETER_UNCALIBRATED, ms2ns(100)); - device->setDelay(device, SENSOR_ACCELEROMETER, ms2ns(10)); - device->setDelay(device, SENSOR_GYRO, ms2ns(10)); - device->setDelay(device, SENSOR_MAGNETOMETER, ms2ns(100)); - device->setDelay(device, SENSOR_PROXIMITY, ms2ns(100)); - device->setDelay(device, SENSOR_LIGHT, ms2ns(100)); + for (auto &s : sensors) { + device->activate(device, s.first, 0); + device->activate(device, s.first, 1); + device->setDelay(device, s.first, s.second); + } + // TODO: why is this 16? static const size_t numEvents = 16; sensors_event_t buffer[numEvents]; @@ -107,49 +110,24 @@ void sensor_loop() { int log_events = 0; for (int i=0; i < n; i++) { - switch (buffer[i].type) { - case SENSOR_TYPE_ACCELEROMETER: - case SENSOR_TYPE_MAGNETIC_FIELD_UNCALIBRATED: - case SENSOR_TYPE_MAGNETIC_FIELD: - case SENSOR_TYPE_GYROSCOPE_UNCALIBRATED: - case SENSOR_TYPE_GYROSCOPE: - case SENSOR_TYPE_PROXIMITY: - case SENSOR_TYPE_LIGHT: + if (sensor_types.find(buffer[i].type) != sensor_types.end()) { log_events++; - break; - default: - continue; } } - uint64_t log_time = nanos_since_boot(); - - capnp::MallocMessageBuilder msg; - cereal::Event::Builder event = msg.initRoot(); - event.setLogMonoTime(log_time); - - auto sensor_events = event.initSensorEvents(log_events); + MessageBuilder msg; + auto sensor_events = msg.initEvent().initSensorEvents(log_events); int log_i = 0; for (int i = 0; i < n; i++) { const sensors_event_t& data = buffer[i]; - switch (data.type) { - case SENSOR_TYPE_ACCELEROMETER: - case SENSOR_TYPE_MAGNETIC_FIELD_UNCALIBRATED: - case SENSOR_TYPE_MAGNETIC_FIELD: - case SENSOR_TYPE_GYROSCOPE_UNCALIBRATED: - case SENSOR_TYPE_GYROSCOPE: - case SENSOR_TYPE_PROXIMITY: - case SENSOR_TYPE_LIGHT: - break; - default: + if (sensor_types.find(data.type) == sensor_types.end()) { continue; } auto log_event = sensor_events[log_i]; - log_event.setSource(cereal::SensorEventData::SensorSource::ANDROID); log_event.setVersion(data.version); log_event.setSensor(data.sensor); diff --git a/selfdrive/sensord/sensors_qcom2.cc b/selfdrive/sensord/sensors_qcom2.cc new file mode 100644 index 000000000..d9da48605 --- /dev/null +++ b/selfdrive/sensord/sensors_qcom2.cc @@ -0,0 +1,90 @@ +#include +#include +#include +#include +#include + +#include "messaging.hpp" +#include "common/i2c.h" +#include "common/timing.h" +#include "common/swaglog.h" + +#include "sensors/sensor.hpp" +#include "sensors/constants.hpp" +#include "sensors/bmx055_accel.hpp" +#include "sensors/bmx055_gyro.hpp" +#include "sensors/bmx055_magn.hpp" +#include "sensors/bmx055_temp.hpp" +#include "sensors/light_sensor.hpp" + +volatile sig_atomic_t do_exit = 0; + +#define I2C_BUS_IMU 1 + + +void set_do_exit(int sig) { + do_exit = 1; +} + +int sensor_loop() { + I2CBus *i2c_bus_imu; + + try { + i2c_bus_imu = new I2CBus(I2C_BUS_IMU); + } catch (std::exception &e) { + LOGE("I2CBus init failed"); + return -1; + } + + BMX055_Accel accel(i2c_bus_imu); + BMX055_Gyro gyro(i2c_bus_imu); + BMX055_Magn magn(i2c_bus_imu); + BMX055_Temp temp(i2c_bus_imu); + LightSensor light("/sys/class/i2c-adapter/i2c-2/2-0038/iio:device1/in_intensity_both_raw"); + + // Sensor init + std::vector sensors; + sensors.push_back(&accel); + sensors.push_back(&gyro); + sensors.push_back(&magn); + sensors.push_back(&temp); + sensors.push_back(&light); + + + for (Sensor * sensor : sensors){ + int err = sensor->init(); + if (err < 0){ + LOGE("Error initializing sensors"); + return -1; + } + } + + PubMaster pm({"sensorEvents"}); + + while (!do_exit){ + std::chrono::steady_clock::time_point begin = std::chrono::steady_clock::now(); + + const int num_events = sensors.size(); + MessageBuilder msg; + auto sensor_events = msg.initEvent().initSensorEvents(num_events); + + for (int i = 0; i < num_events; i++){ + auto event = sensor_events[i]; + sensors[i]->get_event(event); + } + + pm.send("sensorEvents", msg); + + std::chrono::steady_clock::time_point end = std::chrono::steady_clock::now(); + std::this_thread::sleep_for(std::chrono::milliseconds(10) - (end - begin)); + } + return 0; +} + +int main(int argc, char *argv[]) { + setpriority(PRIO_PROCESS, 0, -13); + signal(SIGINT, set_do_exit); + signal(SIGTERM, set_do_exit); + + return sensor_loop(); +} diff --git a/selfdrive/test/helpers.py b/selfdrive/test/helpers.py index 1e53ef424..6ee6caf08 100644 --- a/selfdrive/test/helpers.py +++ b/selfdrive/test/helpers.py @@ -1,8 +1,9 @@ +import time import subprocess from functools import wraps from nose.tools import nottest -from common.android import ANDROID +from common.hardware import PC from common.apk import update_apks, start_offroad, pm_apply_packages, android_packages from common.params import Params from selfdrive.version import training_version, terms_version @@ -18,27 +19,31 @@ def set_params_enabled(): params.put("CompletedTrainingVersion", training_version) def phone_only(x): - if ANDROID: - return x - else: + if PC: return nottest(x) + else: + return x -def with_processes(processes): +def with_processes(processes, init_time=0): def wrapper(func): @wraps(func) - def wrap(): + def wrap(*args, **kwargs): # start and assert started - [start_managed_process(p) for p in processes] + for n, p in enumerate(processes): + start_managed_process(p) + if n < len(processes)-1: + time.sleep(init_time) assert all(get_running()[name].exitcode is None for name in processes) # call the function try: - func() + func(*args, **kwargs) # assert processes are still started assert all(get_running()[name].exitcode is None for name in processes) finally: # kill and assert all stopped - [kill_managed_process(p) for p in processes] + for p in processes: + kill_managed_process(p) assert len(get_running()) == 0 return wrap return wrapper @@ -64,4 +69,3 @@ def with_apks(): assert apk_is_not_running, package return wrap return wrapper - diff --git a/selfdrive/test/setup_device_ci.sh b/selfdrive/test/setup_device_ci.sh index e8228ce7d..f1801e72d 100755 --- a/selfdrive/test/setup_device_ci.sh +++ b/selfdrive/test/setup_device_ci.sh @@ -15,7 +15,7 @@ fi # TODO: never clear qcom_replay cache # clear scons cache dirs that haven't been written to in one day -cd /tmp && find -name 'scons_cache_*' -type d -maxdepth 1 -mtime 1 -exec rm -rf '{}' \; +cd /tmp && find -name 'scons_cache_*' -type d -maxdepth 1 -mtime +1 -exec rm -rf '{}' \; # set up environment cd $SOURCE_DIR diff --git a/selfdrive/test/test_cpu_usage.py b/selfdrive/test/test_cpu_usage.py index 2f260ec8a..0e06e0ab4 100755 --- a/selfdrive/test/test_cpu_usage.py +++ b/selfdrive/test/test_cpu_usage.py @@ -15,22 +15,22 @@ def cputime_total(ct): def print_cpu_usage(first_proc, last_proc): procs = [ - ("selfdrive.controls.controlsd", 66.15), - ("selfdrive.locationd.locationd", 34.38), + ("selfdrive.controls.controlsd", 45.0), ("./loggerd", 33.90), - ("selfdrive.controls.plannerd", 19.77), - ("./_modeld", 12.74), - ("selfdrive.locationd.paramsd", 11.53), + ("selfdrive.locationd.locationd", 29.5), + ("selfdrive.controls.plannerd", 11.84), + ("selfdrive.locationd.paramsd", 10.5), + ("./_modeld", 7.12), ("selfdrive.controls.radard", 9.54), - ("./_ui", 9.54), ("./camerad", 7.07), - ("selfdrive.locationd.calibrationd", 6.81), ("./_sensord", 6.17), - ("selfdrive.monitoring.dmonitoringd", 5.48), + ("./_ui", 5.82), ("./boardd", 3.63), ("./_dmonitoringmodeld", 2.67), ("selfdrive.logmessaged", 2.71), ("selfdrive.thermald.thermald", 2.41), + ("selfdrive.locationd.calibrationd", 2.0), + ("selfdrive.monitoring.dmonitoringd", 1.90), ("./proclogd", 1.54), ("./_gpsd", 0.09), ("./clocksd", 0.02), @@ -51,7 +51,7 @@ def print_cpu_usage(first_proc, last_proc): if cpu_usage > max(normal_cpu_usage * 1.1, normal_cpu_usage + 5.0): result += f"Warning {proc_name} using more CPU than normal\n" r = False - elif cpu_usage < min(normal_cpu_usage * 0.3, max(normal_cpu_usage - 1.0, 0.0)): + elif cpu_usage < min(normal_cpu_usage * 0.65, max(normal_cpu_usage - 1.0, 0.0)): result += f"Warning {proc_name} using less CPU than normal\n" r = False result += f"{proc_name.ljust(35)} {cpu_usage:.2f}%\n" @@ -71,12 +71,15 @@ def test_cpu_usage(): try: proc_sock = messaging.sub_sock('procLog', conflate=True, timeout=2000) - # wait until everything's started and get first sample + # wait until everything's started start_time = time.monotonic() - while time.monotonic() - start_time < 120: + while time.monotonic() - start_time < 210: if Params().get("CarParams") is not None: break time.sleep(2) + + # take first sample + time.sleep(5) first_proc = messaging.recv_sock(proc_sock, wait=True) if first_proc is None: raise Exception("\n\nTEST FAILED: progLog recv timed out\n\n") diff --git a/selfdrive/thermald/power_monitoring.py b/selfdrive/thermald/power_monitoring.py index 6bf26ee0e..76b9ad3b2 100644 --- a/selfdrive/thermald/power_monitoring.py +++ b/selfdrive/thermald/power_monitoring.py @@ -1,4 +1,3 @@ -import datetime import random import threading import time @@ -6,10 +5,20 @@ from statistics import mean from cereal import log from common.realtime import sec_since_boot +from common.params import Params, put_nonblocking +from common.hardware import TICI from selfdrive.swaglog import cloudlog PANDA_OUTPUT_VOLTAGE = 5.28 +CAR_VOLTAGE_LOW_PASS_K = 0.091 # LPF gain for 5s tau (dt/tau / (dt/tau + 1)) +# A C2 uses about 1W while idling, and 30h seens like a good shutoff for most cars +# While driving, a battery charges completely in about 30-60 minutes +CAR_BATTERY_CAPACITY_uWh = 30e6 +CAR_CHARGING_RATE_W = 45 + +VBATT_PAUSE_CHARGING = 11.0 +MAX_TIME_OFFROAD_S = 30*3600 # Parameters def get_battery_capacity(): @@ -36,7 +45,7 @@ def get_usb_present(): def get_battery_charging(): # This does correspond with actually charging - return _read_param("/sys/class/power_supply/battery/charge_type", lambda x: x.strip() != "N/A", False) + return _read_param("/sys/class/power_supply/battery/charge_type", lambda x: x.strip() != "N/A", True) def set_battery_charging(on): @@ -60,91 +69,120 @@ def panda_current_to_actual_current(panda_current): class PowerMonitoring: def __init__(self): + self.params = Params() self.last_measurement_time = None # Used for integration delta + self.last_save_time = 0 # Used for saving current value in a param self.power_used_uWh = 0 # Integrated power usage in uWh since going into offroad self.next_pulsed_measurement_time = None + self.car_voltage_mV = 12e3 # Low-passed version of health voltage self.integration_lock = threading.Lock() + car_battery_capacity_uWh = self.params.get("CarBatteryCapacity") + if car_battery_capacity_uWh is None: + car_battery_capacity_uWh = 0 + + # Reset capacity if it's low + self.car_battery_capacity_uWh = max((CAR_BATTERY_CAPACITY_uWh / 10), int(car_battery_capacity_uWh)) + + # Calculation tick def calculate(self, health): try: now = sec_since_boot() - # Check that time is valid - if datetime.datetime.fromtimestamp(now).year < 2019: - return - - # Only integrate when there is no ignition # If health is None, we're probably not in a car, so we don't care - if health is None or (health.health.ignitionLine or health.health.ignitionCan) or \ - health.health.hwType == log.HealthData.HwType.unknown: + if health is None or health.health.hwType == log.HealthData.HwType.unknown: with self.integration_lock: self.last_measurement_time = None self.next_pulsed_measurement_time = None self.power_used_uWh = 0 return + # Low-pass battery voltage + self.car_voltage_mV = ((health.health.voltage * CAR_VOLTAGE_LOW_PASS_K) + (self.car_voltage_mV * (1 - CAR_VOLTAGE_LOW_PASS_K))) + + # Cap the car battery power and save it in a param every 10-ish seconds + self.car_battery_capacity_uWh = max(self.car_battery_capacity_uWh, 0) + self.car_battery_capacity_uWh = min(self.car_battery_capacity_uWh, CAR_BATTERY_CAPACITY_uWh) + if now - self.last_save_time >= 10: + put_nonblocking("CarBatteryCapacity", str(int(self.car_battery_capacity_uWh))) + self.last_save_time = now + # First measurement, set integration time with self.integration_lock: if self.last_measurement_time is None: self.last_measurement_time = now return - is_uno = health.health.hwType == log.HealthData.HwType.uno - # Get current power draw somehow - current_power = 0 - if get_battery_status() == 'Discharging': - # If the battery is discharging, we can use this measurement - # On C2: this is low by about 10-15%, probably mostly due to UNO draw not being factored in - current_power = ((get_battery_voltage() / 1000000) * (get_battery_current() / 1000000)) - elif (health.health.hwType in [log.HealthData.HwType.whitePanda, log.HealthData.HwType.greyPanda]) and (health.health.current > 1): - # If white/grey panda, use the integrated current measurements if the measurement is not 0 - # If the measurement is 0, the current is 400mA or greater, and out of the measurement range of the panda - # This seems to be accurate to about 5% - current_power = (PANDA_OUTPUT_VOLTAGE * panda_current_to_actual_current(health.health.current)) - elif (self.next_pulsed_measurement_time is not None) and (self.next_pulsed_measurement_time <= now): - # TODO: Figure out why this is off by a factor of 3/4??? - FUDGE_FACTOR = 1.33 - - # Turn off charging for about 10 sec in a thread that does not get killed on SIGINT, and perform measurement here to avoid blocking thermal - def perform_pulse_measurement(now): - try: - set_battery_charging(False) - time.sleep(5) - - # Measure for a few sec to get a good average - voltages = [] - currents = [] - for _ in range(6): - voltages.append(get_battery_voltage()) - currents.append(get_battery_current()) - time.sleep(1) - current_power = ((mean(voltages) / 1000000) * (mean(currents) / 1000000)) - - self._perform_integration(now, current_power * FUDGE_FACTOR) - - # Enable charging again - set_battery_charging(True) - except Exception: - cloudlog.exception("Pulsed power measurement failed") - - # Start pulsed measurement and return - threading.Thread(target=perform_pulse_measurement, args=(now,)).start() - self.next_pulsed_measurement_time = None - return - - elif self.next_pulsed_measurement_time is None and not is_uno: - # On a charging EON with black panda, or drawing more than 400mA out of a white/grey one - # Only way to get the power draw is to turn off charging for a few sec and check what the discharging rate is - # We shouldn't do this very often, so make sure it has been some long-ish random time interval - self.next_pulsed_measurement_time = now + random.randint(120, 180) - return + if (health.health.ignitionLine or health.health.ignitionCan): + # If there is ignition, we integrate the charging rate of the car + with self.integration_lock: + self.power_used_uWh = 0 + integration_time_h = (now - self.last_measurement_time) / 3600 + if integration_time_h < 0: + raise ValueError(f"Negative integration time: {integration_time_h}h") + self.car_battery_capacity_uWh += (CAR_CHARGING_RATE_W * 1e6 * integration_time_h) + self.last_measurement_time = now else: - # Do nothing - return + # No ignition, we integrate the offroad power used by the device + is_uno = health.health.hwType == log.HealthData.HwType.uno + # Get current power draw somehow + current_power = 0 + if TICI: + with open("/sys/class/hwmon/hwmon1/power1_input") as f: + current_power = int(f.read()) / 1e6 + elif get_battery_status() == 'Discharging': + # If the battery is discharging, we can use this measurement + # On C2: this is low by about 10-15%, probably mostly due to UNO draw not being factored in + current_power = ((get_battery_voltage() / 1000000) * (get_battery_current() / 1000000)) + elif (health.health.hwType in [log.HealthData.HwType.whitePanda, log.HealthData.HwType.greyPanda]) and (health.health.current > 1): + # If white/grey panda, use the integrated current measurements if the measurement is not 0 + # If the measurement is 0, the current is 400mA or greater, and out of the measurement range of the panda + # This seems to be accurate to about 5% + current_power = (PANDA_OUTPUT_VOLTAGE * panda_current_to_actual_current(health.health.current)) + elif (self.next_pulsed_measurement_time is not None) and (self.next_pulsed_measurement_time <= now): + # TODO: Figure out why this is off by a factor of 3/4??? + FUDGE_FACTOR = 1.33 - # Do the integration - self._perform_integration(now, current_power) + # Turn off charging for about 10 sec in a thread that does not get killed on SIGINT, and perform measurement here to avoid blocking thermal + def perform_pulse_measurement(now): + try: + set_battery_charging(False) + time.sleep(5) + + # Measure for a few sec to get a good average + voltages = [] + currents = [] + for _ in range(6): + voltages.append(get_battery_voltage()) + currents.append(get_battery_current()) + time.sleep(1) + current_power = ((mean(voltages) / 1000000) * (mean(currents) / 1000000)) + + self._perform_integration(now, current_power * FUDGE_FACTOR) + + # Enable charging again + set_battery_charging(True) + except Exception: + cloudlog.exception("Pulsed power measurement failed") + + # Start pulsed measurement and return + threading.Thread(target=perform_pulse_measurement, args=(now,)).start() + self.next_pulsed_measurement_time = None + return + + elif self.next_pulsed_measurement_time is None and not is_uno: + # On a charging EON with black panda, or drawing more than 400mA out of a white/grey one + # Only way to get the power draw is to turn off charging for a few sec and check what the discharging rate is + # We shouldn't do this very often, so make sure it has been some long-ish random time interval + self.next_pulsed_measurement_time = now + random.randint(120, 180) + return + else: + # Do nothing + return + + # Do the integration + self._perform_integration(now, current_power) except Exception: cloudlog.exception("Power monitoring calculation failed") @@ -157,6 +195,7 @@ class PowerMonitoring: if power_used < 0: raise ValueError(f"Negative power used! Integration time: {integration_time_h} h Current Power: {power_used} uWh") self.power_used_uWh += power_used + self.car_battery_capacity_uWh -= power_used self.last_measurement_time = t except Exception: cloudlog.exception("Integration failed") @@ -164,3 +203,36 @@ class PowerMonitoring: # Get the power usage def get_power_used(self): return int(self.power_used_uWh) + + def get_car_battery_capacity(self): + return int(self.car_battery_capacity_uWh) + + # See if we need to disable charging + def should_disable_charging(self, health, offroad_timestamp): + if health is None or offroad_timestamp is None: + return False + + now = sec_since_boot() + disable_charging = False + disable_charging |= (now - offroad_timestamp) > MAX_TIME_OFFROAD_S + disable_charging |= (self.car_voltage_mV < (VBATT_PAUSE_CHARGING * 1e3)) + disable_charging |= (self.car_battery_capacity_uWh <= 0) + disable_charging &= (not health.health.ignitionLine and not health.health.ignitionCan) + disable_charging &= (self.params.get("DisablePowerDown") != b"1") + return disable_charging + + # See if we need to shutdown + def should_shutdown(self, health, offroad_timestamp, started_seen, LEON): + if health is None or offroad_timestamp is None: + return False + + now = sec_since_boot() + panda_charging = (health.health.usbPowerMode != log.HealthData.UsbPowerMode.client) + BATT_PERC_OFF = 10 if LEON else 3 + + should_shutdown = False + # Wait until we have shut down charging before powering down + should_shutdown |= (not panda_charging and self.should_disable_charging(health, offroad_timestamp)) + should_shutdown |= ((get_battery_capacity() < BATT_PERC_OFF) and (not get_battery_charging()) and ((now - offroad_timestamp) > 60)) + should_shutdown &= started_seen + return should_shutdown diff --git a/selfdrive/thermald/thermald.py b/selfdrive/thermald/thermald.py index 093b832da..10b8a4547 100755 --- a/selfdrive/thermald/thermald.py +++ b/selfdrive/thermald/thermald.py @@ -1,22 +1,32 @@ #!/usr/bin/env python3 -import os import datetime +import os +import time +from collections import namedtuple + import psutil from smbus2 import SMBus -from cereal import log -from common.android import ANDROID, get_network_type, get_network_strength -from common.params import Params, put_nonblocking -from common.realtime import sec_since_boot, DT_TRML -from common.numpy_fast import clip, interp -from common.filter_simple import FirstOrderFilter -from selfdrive.version import terms_version, training_version, get_git_branch -from selfdrive.swaglog import cloudlog + import cereal.messaging as messaging +from cereal import log +from common.filter_simple import FirstOrderFilter +from common.hardware import EON, HARDWARE, TICI +from common.numpy_fast import clip, interp +from common.params import Params, put_nonblocking +from common.realtime import DT_TRML, sec_since_boot from selfdrive.controls.lib.alertmanager import set_offroad_alert from selfdrive.loggerd.config import get_available_percent from selfdrive.pandad import get_expected_signature -from selfdrive.thermald.power_monitoring import PowerMonitoring, get_battery_capacity, get_battery_status, \ - get_battery_current, get_battery_voltage, get_usb_present +from selfdrive.swaglog import cloudlog +from selfdrive.thermald.power_monitoring import (PowerMonitoring, + get_battery_capacity, + get_battery_current, + get_battery_status, + get_battery_voltage, + get_usb_present) +from selfdrive.version import get_git_branch, terms_version, training_version + +ThermalConfig = namedtuple('ThermalConfig', ['cpu', 'gpu', 'mem', 'bat', 'ambient']) FW_SIGNATURE = get_expected_signature() @@ -33,31 +43,34 @@ LEON = False last_eon_fan_val = None -def read_tz(x, clip=True): - if not ANDROID: - # we don't monitor thermal on PC +def get_thermal_config(): + # (tz, scale) + if EON: + return ThermalConfig(cpu=((5, 7, 10, 12), 10), gpu=((16,), 10), mem=(2, 10), bat=(29, 1000), ambient=(25, 1)) + elif TICI: + return ThermalConfig(cpu=((1, 2, 3, 4, 5, 6, 7, 8), 1000), gpu=((48,49), 1000), mem=(15, 1000), bat=(None, 1), ambient=(70, 1000)) + else: + return ThermalConfig(cpu=((None,), 1), gpu=((None,), 1), mem=(None, 1), bat=(None, 1), ambient=(None, 1)) + + +def read_tz(x): + if x is None: return 0 + try: with open("/sys/devices/virtual/thermal/thermal_zone%d/temp" % x) as f: - ret = int(f.read()) - if clip: - ret = max(0, ret) + return int(f.read()) except FileNotFoundError: return 0 - return ret - -def read_thermal(): +def read_thermal(thermal_config): dat = messaging.new_message('thermal') - dat.thermal.cpu0 = read_tz(5) - dat.thermal.cpu1 = read_tz(7) - dat.thermal.cpu2 = read_tz(10) - dat.thermal.cpu3 = read_tz(12) - dat.thermal.mem = read_tz(2) - dat.thermal.gpu = read_tz(16) - dat.thermal.bat = read_tz(29) - dat.thermal.pa0 = read_tz(25) + dat.thermal.cpu = [read_tz(z) / thermal_config.cpu[1] for z in thermal_config.cpu[0]] + dat.thermal.gpu = [read_tz(z) / thermal_config.gpu[1] for z in thermal_config.gpu[0]] + dat.thermal.mem = read_tz(thermal_config.mem[0]) / thermal_config.mem[1] + dat.thermal.ambient = read_tz(thermal_config.ambient[0]) / thermal_config.ambient[1] + dat.thermal.bat = read_tz(thermal_config.bat[0]) / thermal_config.bat[1] return dat @@ -111,7 +124,7 @@ _TEMP_THRS_L = [42.5, 57.5, 72.5, 10000] # fan speed options _FAN_SPEEDS = [0, 16384, 32768, 65535] # max fan speed only allowed if battery is hot -_BAT_TEMP_THERSHOLD = 45. +_BAT_TEMP_THRESHOLD = 45. def handle_fan_eon(max_cpu_temp, bat_temp, fan_speed, ignition): @@ -125,7 +138,7 @@ def handle_fan_eon(max_cpu_temp, bat_temp, fan_speed, ignition): # update speed if using the low thresholds results in fan speed decrement fan_speed = new_speed_l - if bat_temp < _BAT_TEMP_THERSHOLD: + if bat_temp < _BAT_TEMP_THRESHOLD: # no max fan speed unless battery is hot fan_speed = min(fan_speed, _FAN_SPEEDS[-2]) @@ -144,9 +157,6 @@ def handle_fan_uno(max_cpu_temp, bat_temp, fan_speed, ignition): def thermald_thread(): - # prevent LEECO from undervoltage - BATT_PERC_OFF = 10 if LEON else 3 - health_timeout = int(1000 * 2.5 * DT_TRML) # 2.5x the expected health frequency # now loop @@ -185,11 +195,13 @@ def thermald_thread(): pm = PowerMonitoring() no_panda_cnt = 0 + thermal_config = get_thermal_config() + while 1: health = messaging.recv_sock(health_sock, wait=True) location = messaging.recv_sock(location_sock) location = location.gpsLocation if location else None - msg = read_thermal() + msg = read_thermal(thermal_config) if health is not None: usb_power = health.health.usbPowerMode != log.HealthData.UsbPowerMode.client @@ -210,7 +222,7 @@ def thermald_thread(): is_uno = health.health.hwType == log.HealthData.HwType.uno has_relay = health.health.hwType in [log.HealthData.HwType.blackPanda, log.HealthData.HwType.uno, log.HealthData.HwType.dos] - if is_uno or not ANDROID: + if (not EON) or is_uno: cloudlog.info("Setting up UNO fan handler") handle_fan = handle_fan_uno else: @@ -228,8 +240,8 @@ def thermald_thread(): # get_network_type is an expensive call. update every 10s if (count % int(10. / DT_TRML)) == 0: try: - network_type = get_network_type() - network_strength = get_network_strength(network_type) + network_type = HARDWARE.get_network_type() + network_strength = HARDWARE.get_network_strength(network_type) except Exception: cloudlog.exception("Error getting network status") @@ -245,7 +257,7 @@ def thermald_thread(): msg.thermal.usbOnline = get_usb_present() # Fake battery levels on uno for frame - if is_uno: + if (not EON) or is_uno: msg.thermal.batteryPercent = 100 msg.thermal.batteryStatus = "Charging" msg.thermal.bat = 0 @@ -253,14 +265,9 @@ def thermald_thread(): current_filter.update(msg.thermal.batteryCurrent / 1e6) # TODO: add car battery voltage check - max_cpu_temp = cpu_temp_filter.update( - max(msg.thermal.cpu0, - msg.thermal.cpu1, - msg.thermal.cpu2, - msg.thermal.cpu3) / 10.0) - - max_comp_temp = max(max_cpu_temp, msg.thermal.mem / 10., msg.thermal.gpu / 10.) - bat_temp = msg.thermal.bat / 1000. + max_cpu_temp = cpu_temp_filter.update(max(msg.thermal.cpu)) + max_comp_temp = max(max_cpu_temp, msg.thermal.mem, max(msg.thermal.gpu)) + bat_temp = msg.thermal.bat if handle_fan is not None: fan_speed = handle_fan(max_cpu_temp, bat_temp, fan_speed, ignition) @@ -269,7 +276,8 @@ def thermald_thread(): # If device is offroad we want to cool down before going onroad # since going onroad increases load and can make temps go over 107 # We only do this if there is a relay that prevents the car from faulting - if max_cpu_temp > 107. or bat_temp >= 63. or (has_relay and (started_ts is None) and max_cpu_temp > 70.0): + is_offroad_for_5_min = (started_ts is None) and ((not started_seen) or (off_ts is None) or (sec_since_boot() - off_ts > 60 * 5)) + if max_cpu_temp > 107. or bat_temp >= 63. or (has_relay and is_offroad_for_5_min and max_cpu_temp > 70.0): # onroad not allowed thermal_status = ThermalStatus.danger elif max_comp_temp > 96.0 or bat_temp > 60.: @@ -402,15 +410,20 @@ def thermald_thread(): off_ts = sec_since_boot() os.system('echo powersave > /sys/class/devfreq/soc:qcom,cpubw/governor') - # shutdown if the battery gets lower than 3%, it's discharging, we aren't running for - # more than a minute but we were running - if msg.thermal.batteryPercent < BATT_PERC_OFF and msg.thermal.batteryStatus == "Discharging" and \ - started_seen and (sec_since_boot() - off_ts) > 60: - os.system('LD_LIBRARY_PATH="" svc power shutdown') - # Offroad power monitoring pm.calculate(health) msg.thermal.offroadPowerUsage = pm.get_power_used() + msg.thermal.carBatteryCapacity = max(0, pm.get_car_battery_capacity()) + + # Check if we need to disable charging (handled by boardd) + msg.thermal.chargingDisabled = pm.should_disable_charging(health, off_ts) + + # Check if we need to shut down + if pm.should_shutdown(health, off_ts, started_seen, LEON): + cloudlog.info(f"shutting device down, offroad since {off_ts}") + # TODO: add function for blocking cloudlog instead of sleep + time.sleep(10) + os.system('LD_LIBRARY_PATH="" svc power shutdown') msg.thermal.chargingError = current_filter.x > 0. and msg.thermal.batteryPercent < 90 # if current is positive, then battery is being discharged msg.thermal.started = started_ts is not None diff --git a/selfdrive/tombstoned.py b/selfdrive/tombstoned.py index 4dc969b5c..fbebddbd7 100755 --- a/selfdrive/tombstoned.py +++ b/selfdrive/tombstoned.py @@ -12,10 +12,16 @@ MAX_SIZE = 100000 * 10 # Normal size is 40-100k, allow up to 1M def get_tombstones(): - """Returns list of (filename, ctime) for all tombstones in /data/tombstones""" - DIR_DATA = "/data/tombstones/" - return [(DIR_DATA + fn, int(os.stat(DIR_DATA + fn).st_ctime)) - for fn in os.listdir(DIR_DATA) if fn.startswith("tombstone")] + """Returns list of (filename, ctime) for all tombstones in /data/tombstones + and apport crashlogs in /var/crash""" + files = [] + for folder in ["/data/tombstones/", "/var/crash/"]: + if os.path.exists(folder): + for fn in os.listdir(folder): + if fn.startswith("tombstone") or fn.endswith(".crash"): + path = os.path.join(folder, fn) + files.append((path, int(os.stat(path).st_ctime))) + return files def report_tombstone(fn, client): @@ -28,17 +34,27 @@ def report_tombstone(fn, client): contents = f.read() # Get summary for sentry title - message = " ".join(contents.split('\n')[5:7]) + if fn.endswith(".crash"): + lines = contents.split('\n') + message = lines[6] - # Cut off pid/tid, since that varies per run - name_idx = message.find('name') - if name_idx >= 0: - message = message[name_idx:] + status_idx = contents.find('ProcStatus') + if status_idx >= 0: + lines = contents[status_idx:].split('\n') + message += " " + lines[1] + else: + message = " ".join(contents.split('\n')[5:7]) + + # Cut off pid/tid, since that varies per run + name_idx = message.find('name') + if name_idx >= 0: + message = message[name_idx:] + + # Cut off fault addr + fault_idx = message.find(', fault addr') + if fault_idx >= 0: + message = message[:fault_idx] - # Cut off fault addr - fault_idx = message.find(', fault addr') - if fault_idx >= 0: - message = message[:fault_idx] cloudlog.error({'tombstone': message}) client.captureMessage( @@ -53,7 +69,6 @@ def report_tombstone(fn, client): def main(): initial_tombstones = set(get_tombstones()) - client = Client('https://d3b175702f62402c91ade04d1c547e68:b20d68c813c74f63a7cdf9c4039d8f56@sentry.io/157615', install_sys_hook=False, transport=HTTPTransport, release=version, tags={'dirty': dirty}, string_max_length=10000) diff --git a/selfdrive/ui/SConscript b/selfdrive/ui/SConscript index 8f22824fb..2207123a9 100644 --- a/selfdrive/ui/SConscript +++ b/selfdrive/ui/SConscript @@ -1,17 +1,30 @@ -Import('env', 'arch', 'common', 'messaging', 'gpucommon', 'visionipc', 'cereal') +Import('env', 'qt_env', 'arch', 'common', 'messaging', 'gpucommon', 'visionipc', 'cereal') src = ['ui.cc', 'paint.cc', 'sidebar.cc', '#phonelibs/nanovg/nanovg.c'] libs = [common, 'zmq', 'czmq', 'capnp', 'kj', 'm', cereal, messaging, gpucommon, visionipc] -if arch == "aarch64": - src += ['sound.cc'] + +if qt_env is None: libs += ['EGL', 'GLESv3', 'gnustl_shared', 'log', 'utils', 'gui', 'hardware', 'ui', 'CB', 'gsl', 'adreno_utils', 'OpenSLES', 'cutils', 'uuid', 'OpenCL'] linkflags = ['-Wl,-rpath=/system/lib64,-rpath=/system/comma/usr/lib'] -else: - src += ['linux.cc'] - libs += ['pthread', 'glfw'] - linkflags = [] -env.Program('_ui', src, - LINKFLAGS=linkflags, - LIBS=libs) + src += ["android/ui.cc", "android/sl_sound.cc"] + env.Program('_ui', src, + LINKFLAGS=linkflags, + LIBS=libs) + +else: + qt_libs = ["pthread"] + + if arch == "Darwin": + qt_env["FRAMEWORKS"] += ["QtWidgets", "QtGui", "QtCore", "QtDBus", "QtMultimedia"] + else: + qt_libs += ["Qt5Widgets", "Qt5Gui", "Qt5Core", "Qt5DBus", "Qt5Multimedia"] + + if arch == "larch64": + qt_libs += ["GLESv2", "wayland-client"] + else: + qt_libs += ["GL"] + + qt_src = ["qt/ui.cc", "qt/window.cc", "qt/settings.cc", "qt/qt_sound.cc"] + src + qt_env.Program("_ui", qt_src, LIBS=qt_libs + libs) diff --git a/selfdrive/ui/sound.cc b/selfdrive/ui/android/sl_sound.cc similarity index 79% rename from selfdrive/ui/sound.cc rename to selfdrive/ui/android/sl_sound.cc index 79a8fd6e2..95cebab9c 100644 --- a/selfdrive/ui/sound.cc +++ b/selfdrive/ui/android/sl_sound.cc @@ -1,35 +1,31 @@ - -#include "sound.hpp" #include #include #include #include "common/swaglog.h" #include "common/timing.h" +#include "android/sl_sound.hpp" + #define LogOnError(func, msg) \ if ((func) != SL_RESULT_SUCCESS) { LOGW(msg); } #define ReturnOnError(func, msg) \ if ((func) != SL_RESULT_SUCCESS) { LOGW(msg); return false; } -static std::map> sound_map { - {AudibleAlert::CHIME_DISENGAGE, {"../assets/sounds/disengaged.wav", 0}}, - {AudibleAlert::CHIME_ENGAGE, {"../assets/sounds/engaged.wav", 0}}, - {AudibleAlert::CHIME_WARNING1, {"../assets/sounds/warning_1.wav", 0}}, - {AudibleAlert::CHIME_WARNING2, {"../assets/sounds/warning_2.wav", 0}}, - {AudibleAlert::CHIME_WARNING2_REPEAT, {"../assets/sounds/warning_2.wav", 3}}, - {AudibleAlert::CHIME_WARNING_REPEAT, {"../assets/sounds/warning_repeat.wav", 3}}, - {AudibleAlert::CHIME_ERROR, {"../assets/sounds/error.wav", 0}}, - {AudibleAlert::CHIME_PROMPT, {"../assets/sounds/error.wav", 0}}}; - -struct Sound::Player { +struct SLSound::Player { SLObjectItf player; SLPlayItf playItf; // slplay_callback runs on a background thread,use atomic to ensure thread safe. std::atomic repeat; }; -bool Sound::init(int volume) { +SLSound::SLSound() { + if (!init()){ + throw std::runtime_error("Failed to initialize sound"); + } +} + +bool SLSound::init() { SLEngineOption engineOptions[] = {{SL_ENGINEOPTION_THREADSAFE, SL_BOOLEAN_TRUE}}; const SLInterfaceID ids[1] = {SL_IID_VOLUME}; const SLboolean req[1] = {SL_BOOLEAN_FALSE}; @@ -54,15 +50,13 @@ bool Sound::init(int volume) { ReturnOnError((*player)->GetInterface(player, SL_IID_PLAY, &playItf), "Failed to get player interface"); ReturnOnError((*playItf)->SetPlayState(playItf, SL_PLAYSTATE_PAUSED), "Failed to initialize playstate to SL_PLAYSTATE_PAUSED"); - player_[kv.first] = new Sound::Player{player, playItf}; + player_[kv.first] = new SLSound::Player{player, playItf}; } - - setVolume(volume); return true; } void SLAPIENTRY slplay_callback(SLPlayItf playItf, void *context, SLuint32 event) { - Sound::Player *s = reinterpret_cast(context); + SLSound::Player *s = reinterpret_cast(context); if (event == SL_PLAYEVENT_HEADATEND && s->repeat > 1) { --s->repeat; (*playItf)->SetPlayState(playItf, SL_PLAYSTATE_STOPPED); @@ -71,7 +65,7 @@ void SLAPIENTRY slplay_callback(SLPlayItf playItf, void *context, SLuint32 event } } -bool Sound::play(AudibleAlert alert) { +bool SLSound::play(AudibleAlert alert) { if (currentSound_ != AudibleAlert::NONE) { stop(); } @@ -93,7 +87,7 @@ bool Sound::play(AudibleAlert alert) { return true; } -void Sound::stop() { +void SLSound::stop() { if (currentSound_ != AudibleAlert::NONE) { auto player = player_.at(currentSound_); player->repeat = 0; @@ -102,9 +96,9 @@ void Sound::stop() { } } -void Sound::setVolume(int volume) { +void SLSound::setVolume(int volume) { if (last_volume_ == volume) return; - + double current_time = nanos_since_boot(); if ((current_time - last_set_volume_time_) > (5 * (1e+9))) { // 5s timeout on updating the volume char volume_change_cmd[64]; @@ -115,11 +109,15 @@ void Sound::setVolume(int volume) { } } -Sound::~Sound() { +SLSound::~SLSound() { for (auto &kv : player_) { (*(kv.second->player))->Destroy(kv.second->player); delete kv.second; } - if (outputMix_) (*outputMix_)->Destroy(outputMix_); - if (engine_) (*engine_)->Destroy(engine_); + if (outputMix_) { + (*outputMix_)->Destroy(outputMix_); + } + if (engine_) { + (*engine_)->Destroy(engine_); + } } diff --git a/selfdrive/ui/android/sl_sound.hpp b/selfdrive/ui/android/sl_sound.hpp new file mode 100644 index 000000000..7925277b9 --- /dev/null +++ b/selfdrive/ui/android/sl_sound.hpp @@ -0,0 +1,26 @@ +#pragma once +#include +#include + +#include "sound.hpp" + + +class SLSound : public Sound { +public: + SLSound(); + ~SLSound(); + bool play(AudibleAlert alert); + void stop(); + void setVolume(int volume); + +private: + bool init(); + SLObjectItf engine_ = nullptr; + SLObjectItf outputMix_ = nullptr; + int last_volume_ = 0; + double last_set_volume_time_ = 0.; + AudibleAlert currentSound_ = AudibleAlert::NONE; + struct Player; + std::map player_; + friend void SLAPIENTRY slplay_callback(SLPlayItf playItf, void *context, SLuint32 event); +}; diff --git a/selfdrive/ui/android/ui.cc b/selfdrive/ui/android/ui.cc new file mode 100644 index 000000000..e64ab6cde --- /dev/null +++ b/selfdrive/ui/android/ui.cc @@ -0,0 +1,265 @@ +#include +#include +#include +#include + +#include "common/util.h" +#include "common/utilpp.h" +#include "common/params.h" +#include "common/touch.h" +#include "common/swaglog.h" + +#include "ui.hpp" +#include "paint.hpp" +#include "android/sl_sound.hpp" + +// Includes for light sensor +#include +#include +#include + +volatile sig_atomic_t do_exit = 0; +static void set_do_exit(int sig) { + do_exit = 1; +} + + +static void* light_sensor_thread(void *args) { + set_thread_name("light_sensor"); + + int err; + UIState *s = (UIState*)args; + s->light_sensor = 0.0; + + struct sensors_poll_device_t* device; + struct sensors_module_t* module; + + hw_get_module(SENSORS_HARDWARE_MODULE_ID, (hw_module_t const**)&module); + sensors_open(&module->common, &device); + + // need to do this + struct sensor_t const* list; + module->get_sensors_list(module, &list); + + int SENSOR_LIGHT = 7; + + err = device->activate(device, SENSOR_LIGHT, 0); + if (err != 0) goto fail; + err = device->activate(device, SENSOR_LIGHT, 1); + if (err != 0) goto fail; + + device->setDelay(device, SENSOR_LIGHT, ms2ns(100)); + + while (!do_exit) { + static const size_t numEvents = 1; + sensors_event_t buffer[numEvents]; + + int n = device->poll(device, buffer, numEvents); + if (n < 0) { + LOG_100("light_sensor_poll failed: %d", n); + } + if (n > 0) { + s->light_sensor = buffer[0].light; + } + } + sensors_close(device); + return NULL; + +fail: + LOGE("LIGHT SENSOR IS MISSING"); + s->light_sensor = 255; + + return NULL; +} + +static void ui_set_brightness(UIState *s, int brightness) { + static int last_brightness = -1; + if (last_brightness != brightness && (s->awake || brightness == 0)) { + if (set_brightness(brightness)) { + last_brightness = brightness; + } + } +} + +int event_processing_enabled = -1; +static void enable_event_processing(bool yes) { + if (event_processing_enabled != 1 && yes) { + system("service call window 18 i32 1"); // enable event processing + event_processing_enabled = 1; + } else if (event_processing_enabled != 0 && !yes) { + system("service call window 18 i32 0"); // disable event processing + event_processing_enabled = 0; + } +} + +static void set_awake(UIState *s, bool awake) { + if (awake) { + // 30 second timeout + s->awake_timeout = 30*UI_FREQ; + } + if (s->awake != awake) { + s->awake = awake; + + // TODO: replace command_awake and command_sleep with direct calls to android + if (awake) { + LOGW("awake normal"); + framebuffer_set_power(s->fb, HWC_POWER_MODE_NORMAL); + enable_event_processing(true); + } else { + LOGW("awake off"); + ui_set_brightness(s, 0); + framebuffer_set_power(s->fb, HWC_POWER_MODE_OFF); + enable_event_processing(false); + } + } +} + +static void handle_vision_touch(UIState *s, int touch_x, int touch_y) { + if (s->started && (touch_x >= s->scene.viz_rect.x - bdr_s) + && (s->active_app != cereal::UiLayoutState::App::SETTINGS)) { + if (!s->scene.frontview) { + s->scene.uilayout_sidebarcollapsed = !s->scene.uilayout_sidebarcollapsed; + } else { + write_db_value("IsDriverViewEnabled", "0", 1); + } + } +} + +static void handle_sidebar_touch(UIState *s, int touch_x, int touch_y) { + if (!s->scene.uilayout_sidebarcollapsed && touch_x <= sbr_w) { + if (settings_btn.ptInRect(touch_x, touch_y)) { + s->active_app = cereal::UiLayoutState::App::SETTINGS; + } else if (home_btn.ptInRect(touch_x, touch_y)) { + if (s->started) { + s->active_app = cereal::UiLayoutState::App::NONE; + s->scene.uilayout_sidebarcollapsed = true; + } else { + s->active_app = cereal::UiLayoutState::App::HOME; + } + } + } +} + +static void update_offroad_layout_state(UIState *s, PubMaster *pm) { + static int timeout = 0; + static bool prev_collapsed = false; + static cereal::UiLayoutState::App prev_app = cereal::UiLayoutState::App::NONE; + if (timeout > 0) { + timeout--; + } + if (prev_collapsed != s->scene.uilayout_sidebarcollapsed || prev_app != s->active_app || timeout == 0) { + MessageBuilder msg; + auto layout = msg.initEvent().initUiLayoutState(); + layout.setActiveApp(s->active_app); + layout.setSidebarCollapsed(s->scene.uilayout_sidebarcollapsed); + pm->send("offroadLayout", msg); + LOGD("setting active app to %d with sidebar %d", (int)s->active_app, s->scene.uilayout_sidebarcollapsed); + prev_collapsed = s->scene.uilayout_sidebarcollapsed; + prev_app = s->active_app; + timeout = 2 * UI_FREQ; + } +} + +int main(int argc, char* argv[]) { + int err; + setpriority(PRIO_PROCESS, 0, -14); + + signal(SIGINT, (sighandler_t)set_do_exit); + SLSound sound; + + UIState uistate = {}; + UIState *s = &uistate; + ui_init(s); + s->sound = &sound; + + set_awake(s, true); + enable_event_processing(true); + + PubMaster *pm = new PubMaster({"offroadLayout"}); + pthread_t light_sensor_thread_handle; + err = pthread_create(&light_sensor_thread_handle, NULL, + light_sensor_thread, s); + assert(err == 0); + + TouchState touch = {0}; + touch_init(&touch); + + // light sensor scaling and volume params + const bool LEON = util::read_file("/proc/cmdline").find("letv") != std::string::npos; + + float brightness_b = 0, brightness_m = 0; + int result = read_param(&brightness_b, "BRIGHTNESS_B", true); + result += read_param(&brightness_m, "BRIGHTNESS_M", true); + if(result != 0) { + brightness_b = LEON ? 10.0 : 5.0; + brightness_m = LEON ? 2.6 : 1.3; + write_param_float(brightness_b, "BRIGHTNESS_B", true); + write_param_float(brightness_m, "BRIGHTNESS_M", true); + } + float smooth_brightness = brightness_b; + + const int MIN_VOLUME = LEON ? 12 : 9; + const int MAX_VOLUME = LEON ? 15 : 12; + s->sound->setVolume(MIN_VOLUME); + + while (!do_exit) { + if (!s->started || !s->vision_connected) { + // Delay a while to avoid 9% cpu usage while car is not started and user is keeping touching on the screen. + usleep(30 * 1000); + } + double u1 = millis_since_boot(); + + ui_update(s); + + // poll for touch events + int touch_x = -1, touch_y = -1; + int touched = touch_poll(&touch, &touch_x, &touch_y, 0); + if (touched == 1) { + set_awake(s, true); + handle_sidebar_touch(s, touch_x, touch_y); + handle_vision_touch(s, touch_x, touch_y); + } + + // manage wakefulness + if (s->started || s->ignition) { + set_awake(s, true); + } + + if (s->awake_timeout > 0) { + s->awake_timeout--; + } else { + set_awake(s, false); + } + + // Don't waste resources on drawing in case screen is off + if (!s->awake) { + continue; + } + + // up one notch every 5 m/s + s->sound->setVolume(fmin(MAX_VOLUME, MIN_VOLUME + s->scene.controls_state.getVEgo() / 5)); + + // set brightness + float clipped_brightness = fmin(512, (s->light_sensor*brightness_m) + brightness_b); + smooth_brightness = fmin(255, clipped_brightness * 0.01 + smooth_brightness * 0.99); + ui_set_brightness(s, (int)smooth_brightness); + + update_offroad_layout_state(s, pm); + + ui_draw(s); + double u2 = millis_since_boot(); + if (!s->scene.frontview && (u2-u1 > 66)) { + // warn on sub 15fps + LOGW("slow frame(%llu) time: %.2f", (s->sm)->frame, u2-u1); + } + framebuffer_swap(s->fb); + } + + set_awake(s, true); + + err = pthread_join(light_sensor_thread_handle, NULL); + assert(err == 0); + delete s->sm; + delete pm; + return 0; +} diff --git a/selfdrive/ui/linux.cc b/selfdrive/ui/linux.cc deleted file mode 100644 index 5b370cc29..000000000 --- a/selfdrive/ui/linux.cc +++ /dev/null @@ -1,107 +0,0 @@ -#include -#include -#include -#include -#include - -#include "ui.hpp" - -#ifndef __APPLE__ -#define GLFW_INCLUDE_ES2 -#else -#define GLFW_INCLUDE_GLCOREARB -#endif - -#define GLFW_INCLUDE_GLEXT -#include - -typedef struct FramebufferState FramebufferState; -typedef struct TouchState TouchState; - -extern "C" { - -FramebufferState* framebuffer_init( - const char* name, int32_t layer, int alpha, - int *out_w, int *out_h) { - glfwInit(); - -#ifndef __APPLE__ - glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API); - glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); - glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0); -#else - glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); - glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); - glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); - glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2); -#endif - glfwWindowHint(GLFW_RESIZABLE, 0); - GLFWwindow* window; - window = glfwCreateWindow(1920, 1080, "ui", NULL, NULL); - if (!window) { - printf("glfwCreateWindow failed\n"); - } - - glfwMakeContextCurrent(window); - glfwSwapInterval(1); - - // clear screen - glClearColor(0.2f, 0.2f, 0.2f, 1.0f); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - framebuffer_swap((FramebufferState*)window); - - if (out_w) *out_w = 1920; - if (out_h) *out_h = 1080; - - return (FramebufferState*)window; -} - -void framebuffer_set_power(FramebufferState *s, int mode) { -} - -void framebuffer_swap(FramebufferState *s) { - glfwSwapBuffers((GLFWwindow*)s); - glfwPollEvents(); -} - -bool set_brightness(int brightness) { return true; } - -void touch_init(TouchState *s) { - printf("touch_init\n"); -} - -int touch_poll(TouchState *s, int* out_x, int* out_y, int timeout) { - return -1; -} - -int touch_read(TouchState *s, int* out_x, int* out_y) { - return -1; -} - -} - -#include "sound.hpp" - -bool Sound::init(int volume) { return true; } -bool Sound::play(AudibleAlert alert) { printf("play sound: %d\n", (int)alert); return true; } -void Sound::stop() {} -void Sound::setVolume(int volume) {} -Sound::~Sound() {} - -#include "common/visionimg.h" -#include - -GLuint visionimg_to_gl(const VisionImg *img, EGLImageKHR *pkhr, void **pph) { - unsigned int texture; - glGenTextures(1, &texture); - glBindTexture(GL_TEXTURE_2D, texture); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, img->width, img->height, 0, GL_RGB, GL_UNSIGNED_BYTE, *pph); - glGenerateMipmap(GL_TEXTURE_2D); - *pkhr = (EGLImageKHR)1; // not NULL - return texture; -} - -void visionimg_destroy_gl(EGLImageKHR khr, void *ph) { - // empty -} - diff --git a/selfdrive/ui/paint.cc b/selfdrive/ui/paint.cc index 8802526cb..881074d8f 100644 --- a/selfdrive/ui/paint.cc +++ b/selfdrive/ui/paint.cc @@ -2,10 +2,10 @@ #include #include #include +#include #include "common/util.h" #define NANOVG_GLES3_IMPLEMENTATION - #include "nanovg_gl.h" #include "nanovg_gl_utils.h" @@ -13,15 +13,27 @@ extern "C"{ #include "common/glutil.h" } +#include "paint.hpp" +#include "sidebar.hpp" + // TODO: this is also hardcoded in common/transformations/camera.py +// TODO: choose based on frame input size +#ifdef QCOM2 +const mat3 intrinsic_matrix = (mat3){{ + 2648.0, 0.0, 1928.0/2, + 0.0, 2648.0, 1208.0/2, + 0.0, 0.0, 1.0 +}}; +#else const mat3 intrinsic_matrix = (mat3){{ 910., 0., 582., 0., 910., 437., 0., 0., 1. }}; +#endif const uint8_t alert_colors[][4] = { - [STATUS_STOPPED] = {0x07, 0x23, 0x39, 0xf1}, + [STATUS_OFFROAD] = {0x07, 0x23, 0x39, 0xf1}, [STATUS_DISENGAGED] = {0x17, 0x33, 0x49, 0xc8}, [STATUS_ENGAGED] = {0x17, 0x86, 0x44, 0xf1}, [STATUS_WARNING] = {0xDA, 0x6F, 0x25, 0xf1}, @@ -142,7 +154,7 @@ static void ui_draw_lane_line(UIState *s, const model_path_vertices_data *pvd, N static void update_track_data(UIState *s, bool is_mpc, track_vertices_data *pvd) { const UIScene *scene = &s->scene; - const PathData path = scene->model.path; + const float *points = scene->path_points; const float *mpc_x_coords = &scene->mpc_x[0]; const float *mpc_y_coords = &scene->mpc_y[0]; @@ -150,6 +162,7 @@ static void update_track_data(UIState *s, bool is_mpc, track_vertices_data *pvd) float lead_d = scene->lead_data[0].getDRel()*2.; float path_height = is_mpc?(lead_d>5.)?fmin(lead_d, 25.)-fmin(lead_d*0.35, 10.):20. :(lead_d>0.)?fmin(lead_d, 50.)-fmin(lead_d*0.35, 10.):49.; + path_height = fmin(path_height, scene->model.getPath().getValidLen()); pvd->cnt = 0; // left side up for (int i=0; i<=path_height; i++) { @@ -160,7 +173,7 @@ static void update_track_data(UIState *s, bool is_mpc, track_vertices_data *pvd) py = mpc_y_coords[i] - off; } else { px = lerp(i+1.0, i, i/100.0); - py = path.points[i] - off; + py = points[i] - off; } vec4 p_car_space = (vec4){{px, py, 0., 1.}}; @@ -182,11 +195,14 @@ static void update_track_data(UIState *s, bool is_mpc, track_vertices_data *pvd) py = mpc_y_coords[i] + off; } else { px = lerp(i+1.0, i, i/100.0); - py = path.points[i] + off; + py = points[i] + off; } vec4 p_car_space = (vec4){{px, py, 0., 1.}}; vec3 p_full_frame = car_space_to_full_frame(s, p_car_space); + if (p_full_frame.v[0] < 0. || p_full_frame.v[1] < 0.) { + continue; + } pvd->v[pvd->cnt].x = p_full_frame.v[0]; pvd->v[pvd->cnt].y = p_full_frame.v[1]; pvd->cnt += 1; @@ -204,7 +220,6 @@ static void update_all_track_data(UIState *s) { } } - static void ui_draw_track(UIState *s, bool is_mpc, track_vertices_data *pvd) { if (pvd->cnt == 0) return; @@ -218,12 +233,12 @@ static void ui_draw_track(UIState *s, bool is_mpc, track_vertices_data *pvd) { NVGpaint track_bg; if (is_mpc) { // Draw colored MPC track - const uint8_t *clr = bg_colors[s->status]; - track_bg = nvgLinearGradient(s->vg, vwp_w, vwp_h, vwp_w, vwp_h*.4, - nvgRGBA(clr[0], clr[1], clr[2], 255), nvgRGBA(clr[0], clr[1], clr[2], 255/2)); + const Color clr = bg_colors[s->status]; + track_bg = nvgLinearGradient(s->vg, s->fb_w, s->fb_h, s->fb_w, s->fb_h*.4, + nvgRGBA(clr.r, clr.g, clr.b, 255), nvgRGBA(clr.r, clr.g, clr.b, 255/2)); } else { // Draw white vision track - track_bg = nvgLinearGradient(s->vg, vwp_w, vwp_h, vwp_w, vwp_h*.4, + track_bg = nvgLinearGradient(s->vg, s->fb_w, s->fb_h, s->fb_w, s->fb_h*.4, COLOR_WHITE, COLOR_WHITE_ALPHA(0)); } nvgFillPaint(s->vg, track_bg); @@ -231,30 +246,23 @@ static void ui_draw_track(UIState *s, bool is_mpc, track_vertices_data *pvd) { } static void draw_frame(UIState *s) { - const UIScene *scene = &s->scene; - + mat4 *out_mat; if (s->scene.frontview) { glBindVertexArray(s->frame_vao[1]); - } else { - glBindVertexArray(s->frame_vao[0]); - } - - mat4 *out_mat; - if (s->scene.frontview || s->scene.fullview) { out_mat = &s->front_frame_mat; } else { + glBindVertexArray(s->frame_vao[0]); out_mat = &s->rear_frame_mat; } glActiveTexture(GL_TEXTURE0); - if (s->scene.frontview && s->cur_vision_front_idx >= 0) { - glBindTexture(GL_TEXTURE_2D, s->frame_front_texs[s->cur_vision_front_idx]); - } else if (!scene->frontview && s->cur_vision_idx >= 0) { - glBindTexture(GL_TEXTURE_2D, s->frame_texs[s->cur_vision_idx]); - #ifndef QCOM - // TODO: a better way to do this? - //printf("%d\n", ((int*)s->priv_hnds[s->cur_vision_idx])[0]); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 1164, 874, 0, GL_RGB, GL_UNSIGNED_BYTE, s->priv_hnds[s->cur_vision_idx]); - #endif + + if (s->stream.last_idx >= 0) { + glBindTexture(GL_TEXTURE_2D, s->frame_texs[s->stream.last_idx]); +#ifndef QCOM + // this is handled in ion on QCOM + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, s->stream.bufs_info.width, s->stream.bufs_info.height, + 0, GL_RGB, GL_UNSIGNED_BYTE, s->priv_hnds[s->stream.last_idx]); +#endif } glUseProgram(s->frame_program); @@ -269,9 +277,9 @@ static void draw_frame(UIState *s) { } static inline bool valid_frame_pt(UIState *s, float x, float y) { - return x >= 0 && x <= s->rgb_width && y >= 0 && y <= s->rgb_height; - + return x >= 0 && x <= s->stream.bufs_info.width && y >= 0 && y <= s->stream.bufs_info.height; } + static void update_lane_line_data(UIState *s, const float *points, float off, model_path_vertices_data *pvd, float valid_len) { pvd->cnt = 0; int rcount = fmin(MODEL_PATH_MAX_VERTICES_CNT / 2, valid_len); @@ -286,7 +294,7 @@ static void update_lane_line_data(UIState *s, const float *points, float off, mo pvd->v[pvd->cnt].y = p_full_frame.v[1]; pvd->cnt += 1; } - for (int i = rcount; i > 0; i--) { + for (int i = rcount - 1; i > 0; i--) { float px = (float)i; float py = points[i] + off; const vec4 p_car_space = (vec4){{px, py, 0., 1.}}; @@ -299,42 +307,35 @@ static void update_lane_line_data(UIState *s, const float *points, float off, mo } } -static void update_all_lane_lines_data(UIState *s, const PathData &path, model_path_vertices_data *pstart) { - update_lane_line_data(s, path.points, 0.025*path.prob, pstart, path.validLen); - float var = fmin(path.std, 0.7); - update_lane_line_data(s, path.points, -var, pstart + 1, path.validLen); - update_lane_line_data(s, path.points, var, pstart + 2, path.validLen); +static void update_all_lane_lines_data(UIState *s, const cereal::ModelData::PathData::Reader &path, const float *points, model_path_vertices_data *pstart) { + update_lane_line_data(s, points, 0.025*path.getProb(), pstart, path.getValidLen()); + update_lane_line_data(s, points, fmin(path.getStd(), 0.7), pstart + 1, path.getValidLen()); } -static void ui_draw_lane(UIState *s, const PathData *path, model_path_vertices_data *pstart, NVGcolor color) { +static void ui_draw_lane(UIState *s, model_path_vertices_data *pstart, NVGcolor color) { ui_draw_lane_line(s, pstart, color); color.a /= 25; ui_draw_lane_line(s, pstart + 1, color); - ui_draw_lane_line(s, pstart + 2, color); } static void ui_draw_vision_lanes(UIState *s) { const UIScene *scene = &s->scene; model_path_vertices_data *pvd = &s->model_path_vertices[0]; if(s->sm->updated("model")) { - update_all_lane_lines_data(s, scene->model.left_lane, pvd); - update_all_lane_lines_data(s, scene->model.right_lane, pvd + MODEL_LANE_PATH_CNT); + update_all_lane_lines_data(s, scene->model.getLeftLane(), scene->left_lane_points, pvd); + update_all_lane_lines_data(s, scene->model.getRightLane(), scene->right_lane_points, pvd + MODEL_LANE_PATH_CNT); } + // Draw left lane edge - ui_draw_lane( - s, &scene->model.left_lane, - pvd, - nvgRGBAf(1.0, 1.0, 1.0, scene->model.left_lane.prob)); + ui_draw_lane(s, pvd, nvgRGBAf(1.0, 1.0, 1.0, scene->model.getLeftLane().getProb())); // Draw right lane edge - ui_draw_lane( - s, &scene->model.right_lane, - pvd + MODEL_LANE_PATH_CNT, - nvgRGBAf(1.0, 1.0, 1.0, scene->model.right_lane.prob)); + ui_draw_lane(s, pvd + MODEL_LANE_PATH_CNT, nvgRGBAf(1.0, 1.0, 1.0, scene->model.getRightLane().getProb())); if(s->sm->updated("radarState")) { update_all_track_data(s); } + // Draw vision path ui_draw_track(s, false, &s->track_vertices[0]); if (scene->controls_state.getEnabled()) { @@ -346,24 +347,21 @@ static void ui_draw_vision_lanes(UIState *s) { // Draw all world space objects. static void ui_draw_world(UIState *s) { const UIScene *scene = &s->scene; - if (!scene->world_objects_visible) { - return; - } - - const int inner_height = viz_w*9/16; - const int ui_viz_rx = scene->ui_viz_rx; - const int ui_viz_rw = scene->ui_viz_rw; - const int ui_viz_ro = scene->ui_viz_ro; - + const Rect &viz_rect = scene->viz_rect; + const int viz_w = s->fb_w - bdr_s * 2; + const int inner_height = float(viz_w) * s->fb_h / s->fb_w; nvgSave(s->vg); - nvgScissor(s->vg, ui_viz_rx, box_y, ui_viz_rw, box_h); + nvgScissor(s->vg, viz_rect.x, viz_rect.y, viz_rect.w, viz_rect.h); - nvgTranslate(s->vg, ui_viz_rx+ui_viz_ro, box_y + (box_h-inner_height)/2.0); + nvgTranslate(s->vg, viz_rect.x+scene->ui_viz_ro, viz_rect.y + (viz_rect.h-inner_height)/2.0); nvgScale(s->vg, (float)viz_w / s->fb_w, (float)inner_height / s->fb_h); - nvgTranslate(s->vg, 240.0f, 0.0); - nvgTranslate(s->vg, -1440.0f / 2, -1080.0f / 2); + + float w = 1440.0f; // Why 1440? + nvgTranslate(s->vg, (s->fb_w - w) / 2.0f, 0.0); + + nvgTranslate(s->vg, -w / 2, -1080.0f / 2); nvgScale(s->vg, 2.0, 2.0); - nvgScale(s->vg, 1440.0f / s->rgb_width, 1080.0f / s->rgb_height); + nvgScale(s->vg, w / s->stream.bufs_info.width, 1080.0f / s->stream.bufs_info.height); // Draw lane edges and vision/mpc tracks ui_draw_vision_lanes(s); @@ -381,51 +379,28 @@ static void ui_draw_world(UIState *s) { } static void ui_draw_vision_maxspeed(UIState *s) { - /*if (!s->longitudinal_control){ - return; - }*/ char maxspeed_str[32]; float maxspeed = s->scene.controls_state.getVCruise(); int maxspeed_calc = maxspeed * 0.6225 + 0.5; - float speedlimit = s->scene.speedlimit; - int speedlim_calc = speedlimit * 2.2369363 + 0.5; - int speed_lim_off = s->speed_lim_off * 2.2369363 + 0.5; if (s->is_metric) { maxspeed_calc = maxspeed + 0.5; - speedlim_calc = speedlimit * 3.6 + 0.5; - speed_lim_off = s->speed_lim_off * 3.6 + 0.5; } bool is_cruise_set = (maxspeed != 0 && maxspeed != SET_SPEED_NA); - bool is_speedlim_valid = s->scene.speedlimit_valid; - bool is_set_over_limit = is_speedlim_valid && s->scene.controls_state.getEnabled() && - is_cruise_set && maxspeed_calc > (speedlim_calc + speed_lim_off); int viz_maxspeed_w = 184; int viz_maxspeed_h = 202; - int viz_maxspeed_x = (s->scene.ui_viz_rx + (bdr_s*2)); - int viz_maxspeed_y = (box_y + (bdr_s*1.5)); + int viz_maxspeed_x = s->scene.viz_rect.x + (bdr_s*2); + int viz_maxspeed_y = s->scene.viz_rect.y + (bdr_s*1.5); int viz_maxspeed_xo = 180; -#ifdef SHOW_SPEEDLIMIT - viz_maxspeed_w += viz_maxspeed_xo; - viz_maxspeed_x += viz_maxspeed_w - (viz_maxspeed_xo * 2); -#else viz_maxspeed_xo = 0; -#endif // Draw Background - ui_draw_rect(s->vg, viz_maxspeed_x, viz_maxspeed_y, viz_maxspeed_w, viz_maxspeed_h, - is_set_over_limit ? nvgRGBA(218, 111, 37, 180) : COLOR_BLACK_ALPHA(100), 30); + ui_draw_rect(s->vg, viz_maxspeed_x, viz_maxspeed_y, viz_maxspeed_w, viz_maxspeed_h, COLOR_BLACK_ALPHA(100), 30); // Draw Border NVGcolor color = COLOR_WHITE_ALPHA(100); - if (is_set_over_limit) { - color = COLOR_OCHRE; - } else if (is_speedlim_valid) { - color = s->is_ego_over_limit ? COLOR_WHITE_ALPHA(20) : COLOR_WHITE; - } - ui_draw_rect(s->vg, viz_maxspeed_x, viz_maxspeed_y, viz_maxspeed_w, viz_maxspeed_h, color, 20, 10); nvgTextAlign(s->vg, NVG_ALIGN_CENTER | NVG_ALIGN_BASELINE); @@ -440,89 +415,30 @@ static void ui_draw_vision_maxspeed(UIState *s) { } } -#ifdef SHOW_SPEEDLIMIT -static void ui_draw_vision_speedlimit(UIState *s) { - char speedlim_str[32]; - float speedlimit = s->scene.speedlimit; - int speedlim_calc = speedlimit * 2.2369363 + 0.5; - if (s->is_metric) { - speedlim_calc = speedlimit * 3.6 + 0.5; - } - - bool is_speedlim_valid = s->scene.speedlimit_valid; - float hysteresis_offset = 0.5; - if (s->is_ego_over_limit) { - hysteresis_offset = 0.0; - } - s->is_ego_over_limit = is_speedlim_valid && s->scene.controls_state.getVEgo() > (speedlimit + s->speed_lim_off + hysteresis_offset); - - int viz_speedlim_w = 180; - int viz_speedlim_h = 202; - int viz_speedlim_x = (s->scene.ui_viz_rx + (bdr_s*2)); - int viz_speedlim_y = (box_y + (bdr_s*1.5)); - if (!is_speedlim_valid) { - viz_speedlim_w -= 5; - viz_speedlim_h -= 10; - viz_speedlim_x += 9; - viz_speedlim_y += 5; - } - // Draw Background - NVGcolor color = COLOR_WHITE_ALPHA(100); - if (is_speedlim_valid && s->is_ego_over_limit) { - color = nvgRGBA(218, 111, 37, 180); - } else if (is_speedlim_valid) { - color = COLOR_WHITE; - } - ui_draw_rect(s->vg, viz_speedlim_x, viz_speedlim_y, viz_speedlim_w, viz_speedlim_h, color, is_speedlim_valid ? 30 : 15); - - // Draw Border - if (is_speedlim_valid) { - ui_draw_rect(s->vg, viz_speedlim_x, viz_speedlim_y, viz_speedlim_w, viz_speedlim_h, - s->is_ego_over_limit ? COLOR_OCHRE : COLOR_WHITE, 20, 10); - } - const float text_x = viz_speedlim_x + viz_speedlim_w / 2; - const float text_y = viz_speedlim_y + (is_speedlim_valid ? 50 : 45); - // Draw "Speed Limit" Text - nvgTextAlign(s->vg, NVG_ALIGN_CENTER | NVG_ALIGN_BASELINE); - color = is_speedlim_valid && s->is_ego_over_limit ? COLOR_WHITE : COLOR_BLACK; - ui_draw_text(s->vg, text_x + (is_speedlim_valid ? 6 : 0), text_y, "SMART", 50, color, s->font_sans_semibold); - ui_draw_text(s->vg, text_x + (is_speedlim_valid ? 6 : 0), text_y + 40, "SPEED", 50, color, s->font_sans_semibold); - - // Draw Speed Text - color = s->is_ego_over_limit ? COLOR_WHITE : COLOR_BLACK; - if (is_speedlim_valid) { - snprintf(speedlim_str, sizeof(speedlim_str), "%d", speedlim_calc); - ui_draw_text(s->vg, text_x, viz_speedlim_y + (is_speedlim_valid ? 170 : 165), speedlim_str, 48*2.5, color, s->font_sans_bold); - } else { - ui_draw_text(s->vg, text_x, viz_speedlim_y + (is_speedlim_valid ? 170 : 165), "N/A", 42*2.5, color, s->font_sans_semibold); - } -} -#endif - static void ui_draw_vision_speed(UIState *s) { - const UIScene *scene = &s->scene; + const Rect &viz_rect = s->scene.viz_rect; float v_ego = s->scene.controls_state.getVEgo(); float speed = v_ego * 2.2369363 + 0.5; if (s->is_metric){ speed = v_ego * 3.6 + 0.5; } const int viz_speed_w = 280; - const int viz_speed_x = scene->ui_viz_rx+((scene->ui_viz_rw/2)-(viz_speed_w/2)); + const int viz_speed_x = viz_rect.centerX() - viz_speed_w/2; char speed_str[32]; nvgBeginPath(s->vg); - nvgRect(s->vg, viz_speed_x, box_y, viz_speed_w, header_h); + nvgRect(s->vg, viz_speed_x, viz_rect.y, viz_speed_w, header_h); nvgTextAlign(s->vg, NVG_ALIGN_CENTER | NVG_ALIGN_BASELINE); snprintf(speed_str, sizeof(speed_str), "%d", (int)speed); - ui_draw_text(s->vg, viz_speed_x + viz_speed_w / 2, 240, speed_str, 96*2.5, COLOR_WHITE, s->font_sans_bold); - ui_draw_text(s->vg, viz_speed_x + viz_speed_w / 2, 320, s->is_metric?"kph":"mph", 36*2.5, COLOR_WHITE_ALPHA(200), s->font_sans_regular); + ui_draw_text(s->vg, viz_rect.centerX(), 240, speed_str, 96*2.5, COLOR_WHITE, s->font_sans_bold); + ui_draw_text(s->vg, viz_rect.centerX(), 320, s->is_metric?"km/h":"mph", 36*2.5, COLOR_WHITE_ALPHA(200), s->font_sans_regular); } static void ui_draw_vision_event(UIState *s) { const int viz_event_w = 220; - const int viz_event_x = ((s->scene.ui_viz_rx + s->scene.ui_viz_rw) - (viz_event_w + (bdr_s*2))); - const int viz_event_y = (box_y + (bdr_s*1.5)); + const int viz_event_x = s->scene.viz_rect.right() - (viz_event_w + bdr_s*2); + const int viz_event_y = s->scene.viz_rect.y + (bdr_s*1.5); if (s->scene.controls_state.getDecelForModel() && s->scene.controls_state.getEnabled()) { // draw winding road sign const int img_turn_size = 160*1.5; @@ -547,28 +463,23 @@ static void ui_draw_vision_event(UIState *s) { } } -#ifdef SHOW_SPEEDLIMIT -static void ui_draw_vision_map(UIState *s) { - const int map_size = 96; - const int map_x = (s->scene.ui_viz_rx + (map_size * 3) + (bdr_s * 3)); - const int map_y = (footer_y + ((footer_h - map_size) / 2)); - ui_draw_circle_image(s->vg, map_x, map_y, map_size, s->img_map, s->scene.map_valid); -} -#endif - static void ui_draw_vision_face(UIState *s) { const int face_size = 96; - const int face_x = (s->scene.ui_viz_rx + face_size + (bdr_s * 2)); - const int face_y = (footer_y + ((footer_h - face_size) / 2)); + const int face_x = (s->scene.viz_rect.x + face_size + (bdr_s * 2)); + const int face_y = (s->scene.viz_rect.bottom() - footer_h + ((footer_h - face_size) / 2)); ui_draw_circle_image(s->vg, face_x, face_y, face_size, s->img_face, s->scene.dmonitoring_state.getFaceDetected()); } static void ui_draw_driver_view(UIState *s) { const UIScene *scene = &s->scene; s->scene.uilayout_sidebarcollapsed = true; - const int frame_x = scene->ui_viz_rx; - const int frame_w = scene->ui_viz_rw; - const int valid_frame_w = 4 * box_h / 3; + const Rect &viz_rect = s->scene.viz_rect; + const int ff_xoffset = 32; + const int frame_x = viz_rect.x; + const int frame_w = viz_rect.w; + const int valid_frame_w = 4 * viz_rect.h / 3; + const int box_y = viz_rect.y; + const int box_h = viz_rect.h; const int valid_frame_x = frame_x + (frame_w - valid_frame_w) / 2 + ff_xoffset; // blackout @@ -613,34 +524,21 @@ static void ui_draw_driver_view(UIState *s) { } static void ui_draw_vision_header(UIState *s) { - const UIScene *scene = &s->scene; - int ui_viz_rx = scene->ui_viz_rx; - int ui_viz_rw = scene->ui_viz_rw; - - NVGpaint gradient = nvgLinearGradient(s->vg, ui_viz_rx, - (box_y+(header_h-(header_h/2.5))), - ui_viz_rx, box_y+header_h, + const Rect &viz_rect = s->scene.viz_rect; + NVGpaint gradient = nvgLinearGradient(s->vg, viz_rect.x, + viz_rect.y+(header_h-(header_h/2.5)), + viz_rect.x, viz_rect.y+header_h, nvgRGBAf(0,0,0,0.45), nvgRGBAf(0,0,0,0)); - ui_draw_rect(s->vg, ui_viz_rx, box_y, ui_viz_rw, header_h, gradient); + + ui_draw_rect(s->vg, viz_rect.x, viz_rect.y, viz_rect.w, header_h, gradient); ui_draw_vision_maxspeed(s); - -#ifdef SHOW_SPEEDLIMIT - ui_draw_vision_speedlimit(s); -#endif ui_draw_vision_speed(s); ui_draw_vision_event(s); } static void ui_draw_vision_footer(UIState *s) { - nvgBeginPath(s->vg); - nvgRect(s->vg, s->scene.ui_viz_rx, footer_y, s->scene.ui_viz_rw, footer_h); - ui_draw_vision_face(s); - -#ifdef SHOW_SPEEDLIMIT - // ui_draw_vision_map(s); -#endif } void ui_draw_vision_alert(UIState *s, cereal::ControlsState::AlertSize va_size, int va_color, @@ -649,20 +547,18 @@ void ui_draw_vision_alert(UIState *s, cereal::ControlsState::AlertSize va_size, {cereal::ControlsState::AlertSize::NONE, 0}, {cereal::ControlsState::AlertSize::SMALL, 241}, {cereal::ControlsState::AlertSize::MID, 390}, - {cereal::ControlsState::AlertSize::FULL, vwp_h}}; + {cereal::ControlsState::AlertSize::FULL, s->fb_h}}; const UIScene *scene = &s->scene; - const bool hasSidebar = !scene->uilayout_sidebarcollapsed; - const bool mapEnabled = scene->uilayout_mapenabled; bool longAlert1 = strlen(va_text1) > 15; const uint8_t *color = alert_colors[va_color]; int alr_s = alert_size_map[va_size]; - const int alr_x = scene->ui_viz_rx-(mapEnabled?(hasSidebar?nav_w:(nav_ww)):0)-bdr_s; - const int alr_w = scene->ui_viz_rw+(mapEnabled?(hasSidebar?nav_w:(nav_ww)):0)+(bdr_s*2); + const int alr_x = scene->viz_rect.x - bdr_s; + const int alr_w = scene->viz_rect.w + (bdr_s*2); const int alr_h = alr_s+(va_size==cereal::ControlsState::AlertSize::NONE?0:bdr_s); - const int alr_y = vwp_h-alr_h; + const int alr_y = s->fb_h-alr_h; ui_draw_rect(s->vg, alr_x, alr_y, alr_w, alr_h, nvgRGBA(color[0],color[1],color[2],(color[3]*s->alert_blinking_alpha))); @@ -692,21 +588,20 @@ void ui_draw_vision_alert(UIState *s, cereal::ControlsState::AlertSize va_size, static void ui_draw_vision(UIState *s) { const UIScene *scene = &s->scene; - + const Rect &viz_rect = scene->viz_rect; // Draw video frames glEnable(GL_SCISSOR_TEST); - glViewport(scene->ui_viz_rx+scene->ui_viz_ro, s->fb_h-(box_y+box_h), viz_w, box_h); - glScissor(scene->ui_viz_rx, s->fb_h-(box_y+box_h), scene->ui_viz_rw, box_h); + glViewport(viz_rect.x+scene->ui_viz_ro, viz_rect.y, s->fb_w - bdr_s*2, viz_rect.h); + glScissor(viz_rect.x, viz_rect.y, viz_rect.w, viz_rect.h); draw_frame(s); glDisable(GL_SCISSOR_TEST); glViewport(0, 0, s->fb_w, s->fb_h); // Draw augmented elements - if (!scene->frontview && !scene->fullview) { + if (!scene->frontview && scene->world_objects_visible) { ui_draw_world(s); } - // Set Speed, Current Speed, Status/Events if (!scene->frontview) { ui_draw_vision_header(s); @@ -715,31 +610,36 @@ static void ui_draw_vision(UIState *s) { } if (scene->alert_size != cereal::ControlsState::AlertSize::NONE) { - // Controls Alerts ui_draw_vision_alert(s, scene->alert_size, s->status, - scene->alert_text1.c_str(), scene->alert_text2.c_str()); - } else { - if (!scene->frontview){ui_draw_vision_footer(s);} + scene->alert_text1.c_str(), scene->alert_text2.c_str()); + } else if (!scene->frontview) { + ui_draw_vision_footer(s); } } static void ui_draw_background(UIState *s) { - int bg_status = s->status; - assert(bg_status < ARRAYSIZE(bg_colors)); - const uint8_t *color = bg_colors[bg_status]; - - glClearColor(color[0]/256.0, color[1]/256.0, color[2]/256.0, 1.0); + const Color color = bg_colors[s->status]; + glClearColor(color.r/256.0, color.g/256.0, color.b/256.0, 1.0); glClear(GL_STENCIL_BUFFER_BIT | GL_COLOR_BUFFER_BIT); } void ui_draw(UIState *s) { + s->scene.viz_rect = Rect{bdr_s * 3, bdr_s, s->fb_w - 4 * bdr_s, s->fb_h - 2 * bdr_s}; + s->scene.ui_viz_ro = 0; + if (!s->scene.uilayout_sidebarcollapsed) { + s->scene.viz_rect.x = sbr_w + bdr_s; + s->scene.viz_rect.w = s->fb_w - s->scene.viz_rect.x - bdr_s; + s->scene.ui_viz_ro = -(sbr_w - 6 * bdr_s); + } + ui_draw_background(s); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glViewport(0, 0, s->fb_w, s->fb_h); nvgBeginFrame(s->vg, s->fb_w, s->fb_h, 1.0f); ui_draw_sidebar(s); - if (s->started && s->active_app == cereal::UiLayoutState::App::NONE && s->status != STATUS_STOPPED && s->vision_seen) { + if (s->started && s->active_app == cereal::UiLayoutState::App::NONE && + s->status != STATUS_OFFROAD && s->vision_connected) { ui_draw_vision(s); } nvgEndFrame(s->vg); @@ -774,9 +674,12 @@ void ui_draw_rect(NVGcontext *vg, float x, float y, float w, float h, NVGpaint & nvgFill(vg); } -#ifdef NANOVG_GL3_IMPLEMENTATION static const char frame_vertex_shader[] = +#ifdef NANOVG_GL3_IMPLEMENTATION "#version 150 core\n" +#else + "#version 300 es\n" +#endif "in vec4 aPosition;\n" "in vec4 aTexCoord;\n" "uniform mat4 uTransform;\n" @@ -787,7 +690,11 @@ static const char frame_vertex_shader[] = "}\n"; static const char frame_fragment_shader[] = +#ifdef NANOVG_GL3_IMPLEMENTATION "#version 150 core\n" +#else + "#version 300 es\n" +#endif "precision mediump float;\n" "uniform sampler2D uTexture;\n" "in vec4 vTexCoord;\n" @@ -795,25 +702,6 @@ static const char frame_fragment_shader[] = "void main() {\n" " colorOut = texture(uTexture, vTexCoord.xy);\n" "}\n"; -#else -static const char frame_vertex_shader[] = - "attribute vec4 aPosition;\n" - "attribute vec4 aTexCoord;\n" - "uniform mat4 uTransform;\n" - "varying vec4 vTexCoord;\n" - "void main() {\n" - " gl_Position = uTransform * aPosition;\n" - " vTexCoord = aTexCoord;\n" - "}\n"; - -static const char frame_fragment_shader[] = - "precision mediump float;\n" - "uniform sampler2D uTexture;\n" - "varying vec4 vTexCoord;\n" - "void main() {\n" - " gl_FragColor = texture2D(uTexture, vTexCoord.xy);\n" - "}\n"; -#endif static const mat4 device_transform = {{ 1.0, 0.0, 0.0, 0.0, @@ -822,14 +710,6 @@ static const mat4 device_transform = {{ 0.0, 0.0, 0.0, 1.0, }}; -// frame from 4/3 to box size with a 2x zoom -static const mat4 frame_transform = {{ - 2*(4./3.)/((float)viz_w/box_h), 0.0, 0.0, 0.0, - 0.0, 2.0, 0.0, 0.0, - 0.0, 0.0, 1.0, 0.0, - 0.0, 0.0, 0.0, 1.0, -}}; - // frame from 4/3 to 16/9 display static const mat4 full_to_wide_frame_transform = {{ .75, 0.0, 0.0, 0.0, @@ -862,8 +742,6 @@ void ui_nvg_init(UIState *s) { assert(s->img_turn != 0); s->img_face = nvgCreateImage(s->vg, "../assets/img_driver_face.png", 1); assert(s->img_face != 0); - s->img_map = nvgCreateImage(s->vg, "../assets/img_map.png", 1); - assert(s->img_map != 0); s->img_button_settings = nvgCreateImage(s->vg, "../assets/images/button_settings.png", 1); assert(s->img_button_settings != 0); s->img_button_home = nvgCreateImage(s->vg, "../assets/images/button_home.png", 1); @@ -918,7 +796,7 @@ void ui_nvg_init(UIState *s) { { 1.0, -1.0, x1, y1}, //br }; - glGenVertexArrays(1,&s->frame_vao[i]); + glGenVertexArrays(1, &s->frame_vao[i]); glBindVertexArray(s->frame_vao[i]); glGenBuffers(1, &s->frame_vbo[i]); glBindBuffer(GL_ARRAY_BUFFER, s->frame_vbo[i]); @@ -936,13 +814,19 @@ void ui_nvg_init(UIState *s) { glBindVertexArray(0); } + // frame from 4/3 to box size with a 2x zoom + const mat4 frame_transform = {{ + (float)(2*(4./3.)/((float)(s->fb_w-(bdr_s*2))/(s->fb_h-(bdr_s*2)))), 0.0, 0.0, 0.0, + 0.0, 2.0, 0.0, 0.0, + 0.0, 0.0, 1.0, 0.0, + 0.0, 0.0, 0.0, 1.0, + }}; + s->front_frame_mat = matmul(device_transform, full_to_wide_frame_transform); s->rear_frame_mat = matmul(device_transform, frame_transform); - for(int i = 0;i < UI_BUF_COUNT; i++) { + for(int i = 0; i < UI_BUF_COUNT; i++) { s->khr[i] = 0; s->priv_hnds[i] = NULL; - s->khr_front[i] = 0; - s->priv_hnds_front[i] = NULL; } } diff --git a/selfdrive/ui/paint.hpp b/selfdrive/ui/paint.hpp new file mode 100644 index 000000000..e71a2a43e --- /dev/null +++ b/selfdrive/ui/paint.hpp @@ -0,0 +1,11 @@ +#pragma once +#include "ui.hpp" + + +void ui_draw_vision_alert(UIState *s, cereal::ControlsState::AlertSize va_size, int va_color, + const char* va_text1, const char* va_text2); +void ui_draw(UIState *s); +void ui_draw_image(NVGcontext *vg, float x, float y, float w, float h, int image, float alpha); +void ui_draw_rect(NVGcontext *vg, float x, float y, float w, float h, NVGcolor color, float r = 0, int width = 0); +void ui_draw_rect(NVGcontext *vg, float x, float y, float w, float h, NVGpaint &paint, float r = 0); +void ui_nvg_init(UIState *s); diff --git a/selfdrive/ui/qt/qt_sound.cc b/selfdrive/ui/qt/qt_sound.cc new file mode 100644 index 000000000..76842c67e --- /dev/null +++ b/selfdrive/ui/qt/qt_sound.cc @@ -0,0 +1,30 @@ +#include +#include "qt/qt_sound.hpp" + +QtSound::QtSound() { + for (auto &kv : sound_map) { + auto path = QUrl::fromLocalFile(kv.second.first); + sounds[kv.first].setSource(path); + } +} + +bool QtSound::play(AudibleAlert alert) { + sounds[alert].setLoopCount(sound_map[alert].second); + sounds[alert].setVolume(0.9); + sounds[alert].play(); + return true; +} + +void QtSound::stop() { + for (auto &kv : sounds) { + kv.second.stop(); + } +} + +void QtSound::setVolume(int volume) { + // TODO: implement this +} + +QtSound::~QtSound() { + +} diff --git a/selfdrive/ui/qt/qt_sound.hpp b/selfdrive/ui/qt/qt_sound.hpp new file mode 100644 index 000000000..c2aab2de4 --- /dev/null +++ b/selfdrive/ui/qt/qt_sound.hpp @@ -0,0 +1,16 @@ +#pragma once + +#include +#include "sound.hpp" + +class QtSound : public Sound { +public: + QtSound(); + ~QtSound(); + bool play(AudibleAlert alert); + void stop(); + void setVolume(int volume); + +private: + std::map sounds; +}; diff --git a/selfdrive/ui/qt/settings.cc b/selfdrive/ui/qt/settings.cc new file mode 100644 index 000000000..612fe2af3 --- /dev/null +++ b/selfdrive/ui/qt/settings.cc @@ -0,0 +1,140 @@ +#include +#include +#include +#include + +#include "qt/settings.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "common/params.h" + +ParamsToggle::ParamsToggle(QString param, QString title, QString description, QString icon_path, QWidget *parent): QFrame(parent) , param(param) { + QHBoxLayout *hlayout = new QHBoxLayout; + QVBoxLayout *vlayout = new QVBoxLayout; + + hlayout->addSpacing(25); + if (icon_path.length()){ + QPixmap pix(icon_path); + QLabel *icon = new QLabel(); + icon->setPixmap(pix.scaledToWidth(100, Qt::SmoothTransformation)); + icon->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed)); + hlayout->addWidget(icon); + } else{ + hlayout->addSpacing(100); + } + hlayout->addSpacing(25); + + checkbox = new QCheckBox(title); + QLabel *label = new QLabel(description); + label->setWordWrap(true); + + vlayout->addWidget(checkbox); + vlayout->addWidget(label); + hlayout->addLayout(vlayout); + + setLayout(hlayout); + + checkbox->setChecked(read_db_bool(param.toStdString().c_str())); + + setStyleSheet(R"( + QCheckBox { font-size: 70px } + QLabel { font-size: 40px } + * { + background-color: #114265; + } + )"); + + QObject::connect(checkbox, SIGNAL(stateChanged(int)), this, SLOT(checkboxClicked(int))); +} + +void ParamsToggle::checkboxClicked(int state){ + char value = state ? '1': '0'; + write_db_value(param.toStdString().c_str(), &value, 1); +} + +SettingsWindow::SettingsWindow(QWidget *parent) : QWidget(parent) { + QWidget *container = new QWidget(this); + + QVBoxLayout *settings_list = new QVBoxLayout(); + settings_list->addWidget(new ParamsToggle("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.", + "../assets/offroad/icon_openpilot.png" + )); + settings_list->addWidget(new ParamsToggle("LaneChangeEnabled", + "Enable Lane Change Assist", + "Perform assisted lane changes with openpilot by checking your surroundings for safety, activating the turn signal and gently nudging the steering wheel towards your desired lane. openpilot is not capable of checking if a lane change is safe. You must continuously observe your surroundings to use this feature.", + "../assets/offroad/icon_road.png" + )); + settings_list->addWidget(new ParamsToggle("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 31mph (50kph).", + "../assets/offroad/icon_warning.png" + )); + settings_list->addWidget(new ParamsToggle("RecordFront", + "Record and Upload Driver Camera", + "Upload data from the driver facing camera and help improve the driver monitoring algorithm.", + "../assets/offroad/icon_network.png" + )); + settings_list->addWidget(new ParamsToggle("IsRHD", + "Enable Right-Hand Drive", + "Allow openpilot to obey left-hand traffic conventions and perform driver monitoring on right driver seat.", + "../assets/offroad/icon_openpilot_mirrored.png" + )); + settings_list->addWidget(new ParamsToggle("IsMetric", + "Use Metric System", + "Display speed in km/h instead of mp/h.", + "../assets/offroad/icon_metric.png" + )); + settings_list->addWidget(new ParamsToggle("CommunityFeaturesToggle", + "Enable Community Features", + "Use features from the open source community that are not maintained or supported by comma.ai and have not been confirmed to meet the standard safety model. These features include community supported cars and community supported hardware. Be extra cautious when using these features", + "../assets/offroad/icon_shell.png" + )); + + settings_list->setSpacing(25); + + container->setLayout(settings_list); + container->setFixedWidth(1650); + + QScrollArea *scrollArea = new QScrollArea; + scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + scrollArea->setWidget(container); + + QScrollerProperties sp; + sp.setScrollMetric(QScrollerProperties::DecelerationFactor, 2.0); + + QScroller* qs = QScroller::scroller(scrollArea); + qs->setScrollerProperties(sp); + + QHBoxLayout *main_layout = new QHBoxLayout; + main_layout->addSpacing(50); + main_layout->addWidget(scrollArea); + + QPushButton * button = new QPushButton("Close"); + main_layout->addWidget(button); + main_layout->addSpacing(20); + + setLayout(main_layout); + + QScroller::grabGesture(scrollArea, QScroller::LeftMouseButtonGesture); + QObject::connect(button, SIGNAL(clicked()), this, SIGNAL(closeSettings())); + + setStyleSheet(R"( + QPushButton { font-size: 40px } + * { + color: white; + background-color: #072339; + } + )"); +} diff --git a/selfdrive/ui/qt/settings.hpp b/selfdrive/ui/qt/settings.hpp new file mode 100644 index 000000000..e65e75bc7 --- /dev/null +++ b/selfdrive/ui/qt/settings.hpp @@ -0,0 +1,28 @@ +#pragma once + +#include +#include +#include +#include + +class ParamsToggle : public QFrame { + Q_OBJECT + +private: + QCheckBox *checkbox; + QString param; +public: + explicit ParamsToggle(QString param, QString title, QString description, QString icon, QWidget *parent = 0); +public slots: + void checkboxClicked(int state); +}; + + + +class SettingsWindow : public QWidget { + Q_OBJECT +public: + explicit SettingsWindow(QWidget *parent = 0); +signals: + void closeSettings(); +}; diff --git a/selfdrive/ui/qt/ui.cc b/selfdrive/ui/qt/ui.cc new file mode 100644 index 000000000..aece1742a --- /dev/null +++ b/selfdrive/ui/qt/ui.cc @@ -0,0 +1,37 @@ +#include + +#ifdef QCOM2 +#include +#include +#include +#endif + +#include "window.hpp" + +int main(int argc, char *argv[]) { + QSurfaceFormat fmt; +#ifdef __APPLE__ + fmt.setVersion(3, 2); + fmt.setProfile(QSurfaceFormat::OpenGLContextProfile::CoreProfile); + fmt.setRenderableType(QSurfaceFormat::OpenGL); +#else + fmt.setRenderableType(QSurfaceFormat::OpenGLES); +#endif + QSurfaceFormat::setDefaultFormat(fmt); + + QApplication a(argc, argv); + + MainWindow w; + w.setFixedSize(vwp_w, vwp_h); + w.show(); + +#ifdef QCOM2 + QPlatformNativeInterface *native = QGuiApplication::platformNativeInterface(); + wl_surface *s = reinterpret_cast(native->nativeResourceForWindow("surface", w.windowHandle())); + wl_surface_set_buffer_transform(s, WL_OUTPUT_TRANSFORM_270); + wl_surface_commit(s); + w.showFullScreen(); +#endif + + return a.exec(); +} diff --git a/selfdrive/ui/qt/wifi.cc b/selfdrive/ui/qt/wifi.cc new file mode 100644 index 000000000..1d66c2c86 --- /dev/null +++ b/selfdrive/ui/qt/wifi.cc @@ -0,0 +1,69 @@ +#include +#include + +typedef QMap > Connection; +Q_DECLARE_METATYPE(Connection) + +void wifi_stuff(){ + qDBusRegisterMetaType(); + + QString nm_path = "/org/freedesktop/NetworkManager"; + QString nm_settings_path = "/org/freedesktop/NetworkManager/Settings"; + + QString nm_iface = "org.freedesktop.NetworkManager"; + QString props_iface = "org.freedesktop.DBus.Properties"; + QString nm_settings_iface = "org.freedesktop.NetworkManager.Settings"; + + QString nm_service = "org.freedesktop.NetworkManager"; + QString device_service = "org.freedesktop.NetworkManager.Device"; + + QDBusConnection bus = QDBusConnection::systemBus(); + + // Get devices + QDBusInterface nm(nm_service, nm_path, nm_iface, bus); + QDBusMessage response = nm.call("GetDevices"); + QVariant first = response.arguments().at(0); + + const QDBusArgument &args = first.value(); + args.beginArray(); + while (!args.atEnd()) { + QDBusObjectPath path; + args >> path; + + // Get device type + QDBusInterface device_props(nm_service, path.path(), props_iface, bus); + QDBusMessage response = device_props.call("Get", device_service, "DeviceType"); + QVariant first = response.arguments().at(0); + QDBusVariant dbvFirst = first.value(); + QVariant vFirst = dbvFirst.variant(); + uint device_type = vFirst.value(); + qDebug() << path.path() << device_type; + } + args.endArray(); + + + // Add connection + Connection connection; + connection["connection"]["type"] = "802-11-wireless"; + connection["connection"]["uuid"] = QUuid::createUuid().toString().remove('{').remove('}'); + connection["connection"]["id"] = "Connection 1"; + + connection["802-11-wireless"]["ssid"] = QByteArray(""); + connection["802-11-wireless"]["mode"] = "infrastructure"; + + connection["802-11-wireless-security"]["key-mgmt"] = "wpa-psk"; + connection["802-11-wireless-security"]["auth-alg"] = "open"; + connection["802-11-wireless-security"]["psk"] = ""; + + connection["ipv4"]["method"] = "auto"; + connection["ipv6"]["method"] = "ignore"; + + + QDBusInterface nm_settings(nm_service, nm_settings_path, nm_settings_iface, bus); + QDBusReply result = nm_settings.call("AddConnection", QVariant::fromValue(connection)); + if (!result.isValid()) { + qDebug() << result.error().name() << result.error().message(); + } else { + qDebug() << result.value().path(); + } +} diff --git a/selfdrive/ui/qt/window.cc b/selfdrive/ui/qt/window.cc new file mode 100644 index 000000000..60a8e28cd --- /dev/null +++ b/selfdrive/ui/qt/window.cc @@ -0,0 +1,159 @@ +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "window.hpp" +#include "settings.hpp" + +#include "paint.hpp" +#include "common/util.h" + +volatile sig_atomic_t do_exit = 0; + +MainWindow::MainWindow(QWidget *parent) : QWidget(parent) { + main_layout = new QStackedLayout; + +#ifdef QCOM2 + set_core_affinity(7); +#endif + + GLWindow * glWindow = new GLWindow(this); + main_layout->addWidget(glWindow); + + SettingsWindow * settingsWindow = new SettingsWindow(this); + main_layout->addWidget(settingsWindow); + + + main_layout->setMargin(0); + setLayout(main_layout); + QObject::connect(glWindow, SIGNAL(openSettings()), this, SLOT(openSettings())); + QObject::connect(settingsWindow, SIGNAL(closeSettings()), this, SLOT(closeSettings())); + + setStyleSheet(R"( + * { + color: white; + background-color: #072339; + } + )"); +} + +void MainWindow::openSettings() { + main_layout->setCurrentIndex(1); +} + +void MainWindow::closeSettings() { + main_layout->setCurrentIndex(0); +} + + +GLWindow::GLWindow(QWidget *parent) : QOpenGLWidget(parent) { + timer = new QTimer(this); + QObject::connect(timer, SIGNAL(timeout()), this, SLOT(timerUpdate())); + + int result = read_param(&brightness_b, "BRIGHTNESS_B", true); + result += read_param(&brightness_m, "BRIGHTNESS_M", true); + if(result != 0) { + brightness_b = 0.0; + brightness_m = 5.0; + } + smooth_brightness = 512; +} + +GLWindow::~GLWindow() { + makeCurrent(); + doneCurrent(); +} + +void GLWindow::initializeGL() { + initializeOpenGLFunctions(); + std::cout << "OpenGL version: " << glGetString(GL_VERSION) << std::endl; + std::cout << "OpenGL vendor: " << glGetString(GL_VENDOR) << std::endl; + std::cout << "OpenGL renderer: " << glGetString(GL_RENDERER) << std::endl; + std::cout << "OpenGL language version: " << glGetString(GL_SHADING_LANGUAGE_VERSION) << std::endl; + + ui_state = new UIState(); + ui_state->sound = &sound; + ui_state->fb_w = vwp_w; + ui_state->fb_h = vwp_h; + ui_init(ui_state); + + timer->start(50); +} + +void GLWindow::timerUpdate(){ + // Update brightness + float clipped_brightness = std::min(1023.0f, (ui_state->light_sensor*brightness_m) + brightness_b); + smooth_brightness = clipped_brightness * 0.01f + smooth_brightness * 0.99f; + + std::ofstream brightness_control("/sys/class/backlight/panel0-backlight/brightness"); + if (brightness_control.is_open()){ + brightness_control << int(smooth_brightness) << "\n"; + brightness_control.close(); + } + + ui_update(ui_state); + +#ifdef QCOM2 + if (ui_state->started != onroad){ + onroad = ui_state->started; + timer->setInterval(onroad ? 50 : 1000); + + int brightness = onroad ? 1023 : 0; + std::ofstream brightness_control("/sys/class/backlight/panel0-backlight/brightness"); + if (brightness_control.is_open()){ + brightness_control << int(brightness) << "\n"; + brightness_control.close(); + } + } +#endif + + update(); +} + +void GLWindow::resizeGL(int w, int h) { + std::cout << "resize " << w << "x" << h << std::endl; +} + +void GLWindow::paintGL() { + ui_draw(ui_state); +} + +void GLWindow::mousePressEvent(QMouseEvent *e) { + // Settings button click + if (!ui_state->scene.uilayout_sidebarcollapsed && settings_btn.ptInRect(e->x(), e->y())) { + emit openSettings(); + } + + // Vision click + if (ui_state->started && (e->x() >= ui_state->scene.viz_rect.x - bdr_s)){ + ui_state->scene.uilayout_sidebarcollapsed = !ui_state->scene.uilayout_sidebarcollapsed; + } +} + + +GLuint visionimg_to_gl(const VisionImg *img, EGLImageKHR *pkhr, void **pph) { + unsigned int texture; + glGenTextures(1, &texture); + glBindTexture(GL_TEXTURE_2D, texture); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, img->width, img->height, 0, GL_RGB, GL_UNSIGNED_BYTE, *pph); + glGenerateMipmap(GL_TEXTURE_2D); + *pkhr = (EGLImageKHR)1; // not NULL + return texture; +} + +void visionimg_destroy_gl(EGLImageKHR khr, void *ph) { + // empty +} + +FramebufferState* framebuffer_init(const char* name, int32_t layer, int alpha, + int *out_w, int *out_h) { + return (FramebufferState*)1; // not null +} diff --git a/selfdrive/ui/qt/window.hpp b/selfdrive/ui/qt/window.hpp new file mode 100644 index 000000000..cc525a6e0 --- /dev/null +++ b/selfdrive/ui/qt/window.hpp @@ -0,0 +1,68 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +#include "qt/qt_sound.hpp" +#include "ui/ui.hpp" + +class MainWindow : public QWidget +{ + Q_OBJECT + +public: + explicit MainWindow(QWidget *parent = 0); + +private: + QStackedLayout *main_layout; + +public slots: + void openSettings(); + void closeSettings(); + +}; + +#ifdef QCOM2 +const int vwp_w = 2160; +#else +const int vwp_w = 1920; +#endif +const int vwp_h = 1080; +class GLWindow : public QOpenGLWidget, protected QOpenGLFunctions +{ + Q_OBJECT + +public: + using QOpenGLWidget::QOpenGLWidget; + explicit GLWindow(QWidget *parent = 0); + ~GLWindow(); + +protected: + void mousePressEvent(QMouseEvent *e) override; + void initializeGL() override; + void resizeGL(int w, int h) override; + void paintGL() override; + + +private: + QTimer * timer; + UIState * ui_state; + QtSound sound; + + bool onroad = true; + QLabel * label = NULL; + float brightness_b = 0; + float brightness_m = 0; + float smooth_brightness = 0; + +public slots: + void timerUpdate(); + +signals: + void openSettings(); +}; diff --git a/selfdrive/ui/sidebar.cc b/selfdrive/ui/sidebar.cc index e8eb894cc..679d347cb 100644 --- a/selfdrive/ui/sidebar.cc +++ b/selfdrive/ui/sidebar.cc @@ -2,21 +2,23 @@ #include #include #include -#include "ui.hpp" + +#include "paint.hpp" +#include "sidebar.hpp" static void ui_draw_sidebar_background(UIState *s) { int sbr_x = !s->scene.uilayout_sidebarcollapsed ? 0 : -(sbr_w) + bdr_s * 2; - ui_draw_rect(s->vg, sbr_x, 0, sbr_w, vwp_h, COLOR_BLACK_ALPHA(85)); + ui_draw_rect(s->vg, sbr_x, 0, sbr_w, s->fb_h, COLOR_BLACK_ALPHA(85)); } static void ui_draw_sidebar_settings_button(UIState *s) { const float alpha = s->active_app == cereal::UiLayoutState::App::SETTINGS ? 1.0f : 0.65f; - ui_draw_image(s->vg, settings_btn_x, settings_btn_y, settings_btn_w, settings_btn_h, s->img_button_settings, alpha); + ui_draw_image(s->vg, settings_btn.x, settings_btn.y, settings_btn.w, settings_btn.h, s->img_button_settings, alpha); } static void ui_draw_sidebar_home_button(UIState *s) { const float alpha = s->active_app == cereal::UiLayoutState::App::HOME ? 1.0f : 0.65f;; - ui_draw_image(s->vg, home_btn_x, home_btn_y, home_btn_w, home_btn_h, s->img_button_home, alpha); + ui_draw_image(s->vg, home_btn.x, home_btn.y, home_btn.w, home_btn.h, s->img_button_home, alpha); } static void ui_draw_sidebar_network_strength(UIState *s) { @@ -118,57 +120,43 @@ static void ui_draw_sidebar_temp_metric(UIState *s) { {cereal::ThermalData::ThermalStatus::YELLOW, 1}, {cereal::ThermalData::ThermalStatus::RED, 2}, {cereal::ThermalData::ThermalStatus::DANGER, 3}}; - char temp_label_str[32]; - char temp_value_str[32]; - char temp_value_unit[32]; - const int temp_y_offset = 0; - snprintf(temp_value_str, sizeof(temp_value_str), "%d", s->scene.thermal.getPa0()); - snprintf(temp_value_unit, sizeof(temp_value_unit), "%s", "°C"); - snprintf(temp_label_str, sizeof(temp_label_str), "%s", "TEMP"); - strcat(temp_value_str, temp_value_unit); - - ui_draw_sidebar_metric(s, temp_label_str, temp_value_str, temp_severity_map[s->scene.thermal.getThermalStatus()], temp_y_offset, NULL); + std::string temp_val = std::to_string((int)s->scene.thermal.getAmbient()) + "°C"; + ui_draw_sidebar_metric(s, "TEMP", temp_val.c_str(), temp_severity_map[s->scene.thermal.getThermalStatus()], 0, NULL); } static void ui_draw_sidebar_panda_metric(UIState *s) { - int panda_severity = 2; - char panda_message_str[32]; const int panda_y_offset = 32 + 148; + int panda_severity = 0; + std::string panda_message = "VEHICLE\nONLINE"; if (s->scene.hwType == cereal::HealthData::HwType::UNKNOWN) { panda_severity = 2; - snprintf(panda_message_str, sizeof(panda_message_str), "%s", "NO\nVEHICLE"); - } else { - if (s->started){ - if (s->scene.satelliteCount < 6) { - panda_severity = 1; - snprintf(panda_message_str, sizeof(panda_message_str), "%s", "VEHICLE\nNO GPS"); - } else if (s->scene.satelliteCount >= 6) { - panda_severity = 0; - snprintf(panda_message_str, sizeof(panda_message_str), "%s", "VEHICLE\nGOOD GPS"); - } + panda_message = "NO\nVEHICLE"; + } else if (s->started) { + if (s->scene.satelliteCount < 6) { + panda_severity = 1; + panda_message = "VEHICLE\nNO GPS"; } else { panda_severity = 0; - snprintf(panda_message_str, sizeof(panda_message_str), "%s", "VEHICLE\nONLINE"); + panda_message = "VEHICLE\nGOOD GPS"; } } - - ui_draw_sidebar_metric(s, NULL, NULL, panda_severity, panda_y_offset, panda_message_str); + ui_draw_sidebar_metric(s, NULL, NULL, panda_severity, panda_y_offset, panda_message.c_str()); } static void ui_draw_sidebar_connectivity(UIState *s) { - if (s->scene.athenaStatus == NET_DISCONNECTED) { - ui_draw_sidebar_metric(s, NULL, NULL, 1, 180+158, "CONNECT\nOFFLINE"); - } else if (s->scene.athenaStatus == NET_CONNECTED) { - ui_draw_sidebar_metric(s, NULL, NULL, 0, 180+158, "CONNECT\nONLINE"); - } else { - ui_draw_sidebar_metric(s, NULL, NULL, 2, 180+158, "CONNECT\nERROR"); - } + static std::map> connectivity_map = { + {NET_ERROR, {"CONNECT\nERROR", 2}}, + {NET_CONNECTED, {"CONNECT\nONLINE", 0}}, + {NET_DISCONNECTED, {"CONNECT\nOFFLINE", 1}}, + }; + auto net_params = connectivity_map[s->scene.athenaStatus]; + ui_draw_sidebar_metric(s, NULL, NULL, net_params.second, 180+158, net_params.first); } void ui_draw_sidebar(UIState *s) { ui_draw_sidebar_background(s); - if (s->scene.uilayout_sidebarcollapsed){ + if (s->scene.uilayout_sidebarcollapsed) { return; } ui_draw_sidebar_settings_button(s); diff --git a/selfdrive/ui/sidebar.hpp b/selfdrive/ui/sidebar.hpp new file mode 100644 index 000000000..f273e16f8 --- /dev/null +++ b/selfdrive/ui/sidebar.hpp @@ -0,0 +1,4 @@ +#pragma once +#include "ui.hpp" + +void ui_draw_sidebar(UIState *s); diff --git a/selfdrive/ui/sound.hpp b/selfdrive/ui/sound.hpp index d565f846f..931578306 100644 --- a/selfdrive/ui/sound.hpp +++ b/selfdrive/ui/sound.hpp @@ -2,31 +2,23 @@ #include #include "cereal/gen/cpp/log.capnp.h" -#if defined(QCOM) -#include -#include -#endif - typedef cereal::CarControl::HUDControl::AudibleAlert AudibleAlert; -class Sound { - public: - Sound() = default; - bool init(int volume); - bool play(AudibleAlert alert); - void stop(); - void setVolume(int volume); - ~Sound(); - -#if defined(QCOM) - private: - SLObjectItf engine_ = nullptr; - SLObjectItf outputMix_ = nullptr; - int last_volume_ = 0; - double last_set_volume_time_ = 0.; - AudibleAlert currentSound_ = AudibleAlert::NONE; - struct Player; - std::map player_; - friend void SLAPIENTRY slplay_callback(SLPlayItf playItf, void *context, SLuint32 event); -#endif +static std::map> sound_map { + // AudibleAlert, (file path, loop count) + {AudibleAlert::CHIME_DISENGAGE, {"../assets/sounds/disengaged.wav", 0}}, + {AudibleAlert::CHIME_ENGAGE, {"../assets/sounds/engaged.wav", 0}}, + {AudibleAlert::CHIME_WARNING1, {"../assets/sounds/warning_1.wav", 0}}, + {AudibleAlert::CHIME_WARNING2, {"../assets/sounds/warning_2.wav", 0}}, + {AudibleAlert::CHIME_WARNING2_REPEAT, {"../assets/sounds/warning_2.wav", 3}}, + {AudibleAlert::CHIME_WARNING_REPEAT, {"../assets/sounds/warning_repeat.wav", 3}}, + {AudibleAlert::CHIME_ERROR, {"../assets/sounds/error.wav", 0}}, + {AudibleAlert::CHIME_PROMPT, {"../assets/sounds/error.wav", 0}} +}; + +class Sound { +public: + virtual bool play(AudibleAlert alert) = 0; + virtual void stop() = 0; + virtual void setVolume(int volume) = 0; }; diff --git a/selfdrive/ui/spinner/Makefile b/selfdrive/ui/spinner/Makefile index 93871bb00..8d80f69a8 100644 --- a/selfdrive/ui/spinner/Makefile +++ b/selfdrive/ui/spinner/Makefile @@ -20,6 +20,7 @@ FRAMEBUFFER_LIBS = -lutils -lgui -lEGL OBJS = spinner.o \ ../../common/framebuffer.o \ + ../../common/util.o \ $(PHONELIBS)/nanovg/nanovg.o \ ../../common/spinner.o \ opensans_semibold.o \ diff --git a/selfdrive/ui/spinner/spinner b/selfdrive/ui/spinner/spinner index c8f8e68ec..2acc7e785 100755 Binary files a/selfdrive/ui/spinner/spinner and b/selfdrive/ui/spinner/spinner differ diff --git a/selfdrive/ui/text/Makefile b/selfdrive/ui/text/Makefile index 7a011edb3..fbde1077d 100644 --- a/selfdrive/ui/text/Makefile +++ b/selfdrive/ui/text/Makefile @@ -20,6 +20,7 @@ FRAMEBUFFER_LIBS = -lutils -lgui -lEGL OBJS = text.o \ ../../common/framebuffer.o \ + ../../common/util.o \ ../../../selfdrive/common/touch.o \ $(PHONELIBS)/nanovg/nanovg.o \ opensans_regular.o \ diff --git a/selfdrive/ui/ui.cc b/selfdrive/ui/ui.cc index b8edfc4e2..8ca5d0170 100644 --- a/selfdrive/ui/ui.cc +++ b/selfdrive/ui/ui.cc @@ -1,282 +1,137 @@ #include #include #include +#include #include #include +#include #include -#include -#include -#include -#include + #include "common/util.h" -#include "common/timing.h" #include "common/swaglog.h" -#include "common/touch.h" #include "common/visionimg.h" -#include "common/params.h" #include "common/utilpp.h" #include "ui.hpp" +#include "paint.hpp" -static void ui_set_brightness(UIState *s, int brightness) { - static int last_brightness = -1; - if (last_brightness != brightness && (s->awake || brightness == 0)) { - if (set_brightness(brightness)) { - last_brightness = brightness; - } - } -} +extern volatile sig_atomic_t do_exit; -int event_processing_enabled = -1; -static void enable_event_processing(bool yes) { - if (event_processing_enabled != 1 && yes) { - system("service call window 18 i32 1"); // enable event processing - event_processing_enabled = 1; - } else if (event_processing_enabled != 0 && !yes) { - system("service call window 18 i32 0"); // disable event processing - event_processing_enabled = 0; - } -} - -static void set_awake(UIState *s, bool awake) { -#ifdef QCOM - if (awake) { - // 30 second timeout - s->awake_timeout = 30*UI_FREQ; - } - if (s->awake != awake) { - s->awake = awake; - - // TODO: replace command_awake and command_sleep with direct calls to android - if (awake) { - LOGW("awake normal"); - framebuffer_set_power(s->fb, HWC_POWER_MODE_NORMAL); - enable_event_processing(true); - } else { - LOGW("awake off"); - ui_set_brightness(s, 0); - framebuffer_set_power(s->fb, HWC_POWER_MODE_OFF); - enable_event_processing(false); - } - } -#else - // computer UI doesn't sleep - s->awake = true; -#endif -} - -static void update_offroad_layout_state(UIState *s) { -#ifdef QCOM - static int timeout = 0; - static bool prev_collapsed = false; - static cereal::UiLayoutState::App prev_app = cereal::UiLayoutState::App::NONE; - if (timeout > 0) { - timeout--; - } - if (prev_collapsed != s->scene.uilayout_sidebarcollapsed || prev_app != s->active_app || timeout == 0) { - capnp::MallocMessageBuilder msg; - auto event = msg.initRoot(); - event.setLogMonoTime(nanos_since_boot()); - auto layout = event.initUiLayoutState(); - layout.setActiveApp(s->active_app); - layout.setSidebarCollapsed(s->scene.uilayout_sidebarcollapsed); - s->pm->send("offroadLayout", msg); - LOGD("setting active app to %d with sidebar %d", (int)s->active_app, s->scene.uilayout_sidebarcollapsed); - prev_collapsed = s->scene.uilayout_sidebarcollapsed; - prev_app = s->active_app; - timeout = 2 * UI_FREQ; - } -#endif -} - -static void handle_sidebar_touch(UIState *s, int touch_x, int touch_y) { - if (!s->scene.uilayout_sidebarcollapsed && touch_x <= sbr_w) { - if (touch_x >= settings_btn_x && touch_x < (settings_btn_x + settings_btn_w) - && touch_y >= settings_btn_y && touch_y < (settings_btn_y + settings_btn_h)) { - s->active_app = cereal::UiLayoutState::App::SETTINGS; - } - else if (touch_x >= home_btn_x && touch_x < (home_btn_x + home_btn_w) - && touch_y >= home_btn_y && touch_y < (home_btn_y + home_btn_h)) { - if (s->started) { - s->active_app = cereal::UiLayoutState::App::NONE; - s->scene.uilayout_sidebarcollapsed = true; - } else { - s->active_app = cereal::UiLayoutState::App::HOME; - } - } - } -} - -static void handle_vision_touch(UIState *s, int touch_x, int touch_y) { - if (s->started && (touch_x >= s->scene.ui_viz_rx - bdr_s) - && (s->active_app != cereal::UiLayoutState::App::SETTINGS)) { - if (!s->scene.frontview) { - s->scene.uilayout_sidebarcollapsed = !s->scene.uilayout_sidebarcollapsed; - } else { - write_db_value("IsDriverViewEnabled", "0", 1); - } - } -} - -volatile sig_atomic_t do_exit = 0; -static void set_do_exit(int sig) { - do_exit = 1; -} - -template -static int read_param(T* param, const char *param_name, bool persistent_param = false){ - T param_orig = *param; - char *value; - size_t sz; - - int result = read_db_value(param_name, &value, &sz, persistent_param); - if (result == 0){ - std::string s = std::string(value, sz); // value is not null terminated - free(value); - - // Parse result - std::istringstream iss(s); - iss >> *param; - - // Restore original value if parsing failed - if (iss.fail()) { - *param = param_orig; - result = -1; - } - } - return result; -} - -template -static int read_param_timeout(T* param, const char* param_name, int* timeout, bool persistent_param = false) { - int result = -1; - if (*timeout > 0){ - (*timeout)--; - } else { - *timeout = 2 * UI_FREQ; // 0.5Hz - result = read_param(param, param_name, persistent_param); - } - return result; -} - -static int write_param_float(float param, const char* param_name, bool persistent_param = false) { +int write_param_float(float param, const char* param_name, bool persistent_param) { char s[16]; int size = snprintf(s, sizeof(s), "%f", param); - return write_db_value(param_name, s, MIN(size, sizeof(s)), persistent_param); + return write_db_value(param_name, s, size < sizeof(s) ? size : sizeof(s), persistent_param); } -static void ui_init(UIState *s) { - - pthread_mutex_init(&s->lock, NULL); +void ui_init(UIState *s) { s->sm = new SubMaster({"model", "controlsState", "uiLayoutState", "liveCalibration", "radarState", "thermal", - "health", "ubloxGnss", "driverState", "dMonitoringState" -#ifdef SHOW_SPEEDLIMIT - , "liveMapData" -#endif - }); - s->pm = new PubMaster({"offroadLayout"}); + "health", "carParams", "ubloxGnss", "driverState", "dMonitoringState", "sensorEvents"}); - s->ipc_fd = -1; - s->scene.satelliteCount = -1; s->started = false; - s->vision_seen = false; + s->status = STATUS_OFFROAD; + s->scene.satelliteCount = -1; + read_param(&s->is_metric, "IsMetric"); - // init display s->fb = framebuffer_init("ui", 0, true, &s->fb_w, &s->fb_h); assert(s->fb); - set_awake(s, true); - ui_nvg_init(s); } -static void ui_init_vision(UIState *s, const VisionStreamBufs back_bufs, - int num_back_fds, const int *back_fds, - const VisionStreamBufs front_bufs, int num_front_fds, - const int *front_fds) { - assert(num_back_fds == UI_BUF_COUNT); - assert(num_front_fds == UI_BUF_COUNT); +static void ui_init_vision(UIState *s) { + // Invisible until we receive a calibration message. + s->scene.world_objects_visible = false; - vipc_bufs_load(s->bufs, &back_bufs, num_back_fds, back_fds); - vipc_bufs_load(s->front_bufs, &front_bufs, num_front_fds, front_fds); + for (int i = 0; i < UI_BUF_COUNT; i++) { + if (s->khr[i] != 0) { + visionimg_destroy_gl(s->khr[i], s->priv_hnds[i]); + glDeleteTextures(1, &s->frame_texs[i]); + } - s->cur_vision_idx = -1; - s->cur_vision_front_idx = -1; + VisionImg img = { + .fd = s->stream.bufs[i].fd, + .format = VISIONIMG_FORMAT_RGB24, + .width = s->stream.bufs_info.width, + .height = s->stream.bufs_info.height, + .stride = s->stream.bufs_info.stride, + .bpp = 3, + .size = s->stream.bufs_info.buf_len, + }; +#ifndef QCOM + s->priv_hnds[i] = s->stream.bufs[i].addr; +#endif + s->frame_texs[i] = visionimg_to_gl(&img, &s->khr[i], &s->priv_hnds[i]); - s->scene.frontview = getenv("FRONTVIEW") != NULL; - s->scene.fullview = getenv("FULLVIEW") != NULL; - s->scene.world_objects_visible = false; // Invisible until we receive a calibration message. + glBindTexture(GL_TEXTURE_2D, s->frame_texs[i]); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - s->rgb_width = back_bufs.width; - s->rgb_height = back_bufs.height; - s->rgb_stride = back_bufs.stride; - s->rgb_buf_len = back_bufs.buf_len; - - s->rgb_front_width = front_bufs.width; - s->rgb_front_height = front_bufs.height; - s->rgb_front_stride = front_bufs.stride; - s->rgb_front_buf_len = front_bufs.buf_len; - - read_param(&s->speed_lim_off, "SpeedLimitOffset"); - read_param(&s->is_metric, "IsMetric"); - read_param(&s->longitudinal_control, "LongitudinalControl"); - read_param(&s->limit_set_speed, "LimitSetSpeed"); - - // Set offsets so params don't get read at the same time - s->longitudinal_control_timeout = UI_FREQ / 3; - s->is_metric_timeout = UI_FREQ / 2; - s->limit_set_speed_timeout = UI_FREQ; + // BGR + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_R, GL_BLUE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_G, GL_GREEN); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_B, GL_RED); + } + assert(glGetError() == GL_NO_ERROR); } -static void read_path(PathData& p, const cereal::ModelData::PathData::Reader &pathp) { - p = {}; +void ui_update_vision(UIState *s) { - p.prob = pathp.getProb(); - p.std = pathp.getStd(); - - auto polyp = pathp.getPoly(); - for (int i = 0; i < POLYFIT_DEGREE; i++) { - p.poly[i] = polyp[i]; + if (!s->vision_connected && s->started) { + const VisionStreamType type = s->scene.frontview ? VISION_STREAM_RGB_FRONT : VISION_STREAM_RGB_BACK; + int err = visionstream_init(&s->stream, type, true, nullptr); + if (err == 0) { + ui_init_vision(s); + s->vision_connected = true; + } } - // Compute points locations - for (int i = 0; i < MODEL_PATH_DISTANCE; i++) { - p.points[i] = p.poly[0] * (i*i*i) + p.poly[1] * (i*i)+ p.poly[2] * i + p.poly[3]; + if (s->vision_connected) { + if (!s->started) goto destroy; + + // poll for a new frame + struct pollfd fds[1] = {{ + .fd = s->stream.ipc_fd, + .events = POLLOUT, + }}; + int ret = poll(fds, 1, 100); + if (ret > 0) { + if (!visionstream_get(&s->stream, nullptr)) goto destroy; + } } - p.validLen = pathp.getValidLen(); + return; + +destroy: + visionstream_destroy(&s->stream); + s->vision_connected = false; } -static void read_model(ModelData &d, const cereal::ModelData::Reader &model) { - d = {}; - read_path(d.path, model.getPath()); - read_path(d.left_lane, model.getLeftLane()); - read_path(d.right_lane, model.getRightLane()); - auto leadd = model.getLead(); - d.lead = (LeadData){ - .dist = leadd.getDist(), .prob = leadd.getProb(), .std = leadd.getStd(), - }; -} - -static void update_status(UIState *s, int status) { - if (s->status != status) { - s->status = status; +static inline void fill_path_points(const cereal::ModelData::PathData::Reader &path, float *points) { + const capnp::List::Reader &poly = path.getPoly(); + for (int i = 0; i < path.getValidLen(); i++) { + points[i] = poly[0] * (i * i * i) + poly[1] * (i * i) + poly[2] * i + poly[3]; } } -void handle_message(UIState *s, SubMaster &sm) { +void update_sockets(UIState *s) { + UIScene &scene = s->scene; + SubMaster &sm = *(s->sm); + + if (sm.update(0) == 0){ + return; + } + if (s->started && sm.updated("controlsState")) { auto event = sm["controlsState"]; scene.controls_state = event.getControlsState(); - s->controls_timeout = 1 * UI_FREQ; - s->controls_seen = true; + // TODO: the alert stuff shouldn't be handled here auto alert_sound = scene.controls_state.getAlertSound(); if (scene.alert_type.compare(scene.controls_state.getAlertType()) != 0) { if (alert_sound == AudibleAlert::NONE) { - s->sound.stop(); + s->sound->stop(); } else { - s->sound.play(alert_sound); + s->sound->play(alert_sound); } } scene.alert_text1 = scene.controls_state.getAlertText1(); @@ -285,11 +140,11 @@ void handle_message(UIState *s, SubMaster &sm) { scene.alert_type = scene.controls_state.getAlertType(); auto alertStatus = scene.controls_state.getAlertStatus(); if (alertStatus == cereal::ControlsState::AlertStatus::USER_PROMPT) { - update_status(s, STATUS_WARNING); + s->status = STATUS_WARNING; } else if (alertStatus == cereal::ControlsState::AlertStatus::CRITICAL) { - update_status(s, STATUS_ALERT); + s->status = STATUS_ALERT; } else{ - update_status(s, scene.controls_state.getEnabled() ? STATUS_ENGAGED : STATUS_DISENGAGED); + s->status = scene.controls_state.getEnabled() ? STATUS_ENGAGED : STATUS_DISENGAGED; } float alert_blinkingrate = scene.controls_state.getAlertBlinkingRate(); @@ -323,28 +178,16 @@ void handle_message(UIState *s, SubMaster &sm) { } } if (sm.updated("model")) { - read_model(scene.model, sm["model"].getModel()); + scene.model = sm["model"].getModel(); + fill_path_points(scene.model.getPath(), scene.path_points); + fill_path_points(scene.model.getLeftLane(), scene.left_lane_points); + fill_path_points(scene.model.getRightLane(), scene.right_lane_points); } - // else if (which == cereal::Event::LIVE_MPC) { - // auto data = event.getLiveMpc(); - // auto x_list = data.getX(); - // auto y_list = data.getY(); - // for (int i = 0; i < 50; i++){ - // scene.mpc_x[i] = x_list[i]; - // scene.mpc_y[i] = y_list[i]; - // } - // s->livempc_or_radarstate_changed = true; - // } if (sm.updated("uiLayoutState")) { auto data = sm["uiLayoutState"].getUiLayoutState(); s->active_app = data.getActiveApp(); scene.uilayout_sidebarcollapsed = data.getSidebarCollapsed(); } -#ifdef SHOW_SPEEDLIMIT - if (sm.updated("liveMapData")) { - scene.map_valid = sm["liveMapData"].getLiveMapData().getMapValid(); - } -#endif if (sm.updated("thermal")) { scene.thermal = sm["thermal"].getThermal(); } @@ -355,8 +198,14 @@ void handle_message(UIState *s, SubMaster &sm) { } } if (sm.updated("health")) { - scene.hwType = sm["health"].getHealth().getHwType(); - s->hardware_timeout = 5*UI_FREQ; // 5 seconds + auto health = sm["health"].getHealth(); + scene.hwType = health.getHwType(); + s->ignition = health.getIgnitionLine() || health.getIgnitionCan(); + } else if ((s->sm->frame - s->sm->rcv_frame("health")) > 5*UI_FREQ) { + scene.hwType = cereal::HealthData::HwType::UNKNOWN; + } + if (sm.updated("carParams")) { + s->longitudinal_control = sm["carParams"].getCarParams().getOpenpilotLongitudinalControl(); } if (sm.updated("driverState")) { scene.driver_state = sm["driverState"].getDriverState(); @@ -365,526 +214,76 @@ void handle_message(UIState *s, SubMaster &sm) { scene.dmonitoring_state = sm["dMonitoringState"].getDMonitoringState(); scene.is_rhd = scene.dmonitoring_state.getIsRHD(); scene.frontview = scene.dmonitoring_state.getIsPreview(); - } - - // timeout on frontview - if ((sm.frame - sm.rcv_frame("dMonitoringState")) > 1*UI_FREQ) { + } else if ((sm.frame - sm.rcv_frame("dMonitoringState")) > UI_FREQ/2) { scene.frontview = false; } +#ifdef QCOM2 // TODO: use this for QCOM too + if (sm.updated("sensorEvents")) { + for (auto sensor : sm["sensorEvents"].getSensorEvents()) { + if (sensor.which() == cereal::SensorEventData::LIGHT) { + s->light_sensor = sensor.getLight(); + } + } + } +#endif + s->started = scene.thermal.getStarted() || scene.frontview; +} + +void ui_update(UIState *s) { + + update_sockets(s); + ui_update_vision(s); + // Handle onroad/offroad transition - if (!s->started) { - if (s->status != STATUS_STOPPED) { - update_status(s, STATUS_STOPPED); - s->vision_seen = false; - s->controls_seen = false; - s->active_app = cereal::UiLayoutState::App::HOME; + if (!s->started && s->status != STATUS_OFFROAD) { + s->status = STATUS_OFFROAD; + s->active_app = cereal::UiLayoutState::App::HOME; + s->scene.uilayout_sidebarcollapsed = false; + } else if (s->started && s->status == STATUS_OFFROAD) { + s->status = STATUS_DISENGAGED; + s->started_frame = s->sm->frame; - #ifndef QCOM - // disconnect from visionipc on PC - close(s->ipc_fd); - s->ipc_fd = -1; - #endif - } - } else if (s->status == STATUS_STOPPED) { - update_status(s, STATUS_DISENGAGED); s->active_app = cereal::UiLayoutState::App::NONE; - } -} - -static void check_messages(UIState *s) { - if (s->sm->update(0) > 0){ - handle_message(s, *(s->sm)); - } -} - -static void ui_update(UIState *s) { - int err; - - if (s->vision_connect_firstrun) { - // cant run this in connector thread because opengl. - // do this here for now in lieu of a run_on_main_thread event - - for (int i=0; ikhr[i] != 0) { - visionimg_destroy_gl(s->khr[i], s->priv_hnds[i]); - glDeleteTextures(1, &s->frame_texs[i]); - } - - VisionImg img = { - .fd = s->bufs[i].fd, - .format = VISIONIMG_FORMAT_RGB24, - .width = s->rgb_width, - .height = s->rgb_height, - .stride = s->rgb_stride, - .bpp = 3, - .size = s->rgb_buf_len, - }; - #ifndef QCOM - s->priv_hnds[i] = s->bufs[i].addr; - #endif - s->frame_texs[i] = visionimg_to_gl(&img, &s->khr[i], &s->priv_hnds[i]); - - glBindTexture(GL_TEXTURE_2D, s->frame_texs[i]); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - - // BGR - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_R, GL_BLUE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_G, GL_GREEN); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_B, GL_RED); - } - - for (int i=0; ikhr_front[i] != 0) { - visionimg_destroy_gl(s->khr_front[i], s->priv_hnds_front[i]); - glDeleteTextures(1, &s->frame_front_texs[i]); - } - - VisionImg img = { - .fd = s->front_bufs[i].fd, - .format = VISIONIMG_FORMAT_RGB24, - .width = s->rgb_front_width, - .height = s->rgb_front_height, - .stride = s->rgb_front_stride, - .bpp = 3, - .size = s->rgb_front_buf_len, - }; - #ifndef QCOM - s->priv_hnds_front[i] = s->bufs[i].addr; - #endif - s->frame_front_texs[i] = visionimg_to_gl(&img, &s->khr_front[i], &s->priv_hnds_front[i]); - - glBindTexture(GL_TEXTURE_2D, s->frame_front_texs[i]); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - - // BGR - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_R, GL_BLUE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_G, GL_GREEN); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_B, GL_RED); - } - - assert(glGetError() == GL_NO_ERROR); - s->scene.uilayout_sidebarcollapsed = true; - s->scene.ui_viz_rx = (box_x-sbr_w+bdr_s*2); - s->scene.ui_viz_rw = (box_w+sbr_w-(bdr_s*2)); - s->scene.ui_viz_ro = 0; - - s->vision_connect_firstrun = false; - - s->alert_blinking_alpha = 1.0; s->alert_blinked = false; + s->alert_blinking_alpha = 1.0; + s->scene.alert_size = cereal::ControlsState::AlertSize::NONE; } - zmq_pollitem_t polls[1] = {{0}}; - // Take an rgb image from visiond if there is one - assert(s->ipc_fd >= 0); - while(true) { - if (s->ipc_fd < 0) { - // TODO: rethink this, for now it should only trigger on PC - LOGW("vision disconnected by other thread"); - s->vision_connected = false; - return; - } - polls[0].fd = s->ipc_fd; - polls[0].events = ZMQ_POLLIN; - #ifdef UI_60FPS - // uses more CPU in both UI and surfaceflinger - // 16% / 21% - int ret = zmq_poll(polls, 1, 1); - #else - // 9% / 13% = a 14% savings - int ret = zmq_poll(polls, 1, 1000); - #endif - if (ret < 0) { - if (errno == EINTR || errno == EAGAIN) continue; - - LOGE("poll failed (%d - %d)", ret, errno); - close(s->ipc_fd); - s->ipc_fd = -1; - s->vision_connected = false; - return; - } else if (ret == 0) { - break; - } - // vision ipc event - VisionPacket rp; - err = vipc_recv(s->ipc_fd, &rp); - if (err <= 0) { - LOGW("vision disconnected"); - close(s->ipc_fd); - s->ipc_fd = -1; - s->vision_connected = false; - return; - } - if (rp.type == VIPC_STREAM_ACQUIRE) { - bool front = rp.d.stream_acq.type == VISION_STREAM_RGB_FRONT; - int idx = rp.d.stream_acq.idx; - - int release_idx; - if (front) { - release_idx = s->cur_vision_front_idx; - } else { - release_idx = s->cur_vision_idx; - } - if (release_idx >= 0) { - VisionPacket rep = { - .type = VIPC_STREAM_RELEASE, - .d = { .stream_rel = { - .type = rp.d.stream_acq.type, - .idx = release_idx, - }}, - }; - vipc_send(s->ipc_fd, &rep); - } - - if (front) { - assert(idx < UI_BUF_COUNT); - s->cur_vision_front_idx = idx; - } else { - assert(idx < UI_BUF_COUNT); - s->cur_vision_idx = idx; - // printf("v %d\n", ((uint8_t*)s->bufs[idx].addr)[0]); - } - } else { - assert(false); - } - break; - } -} - -static int vision_subscribe(int fd, VisionPacket *rp, VisionStreamType type) { - int err; - LOGW("vision_subscribe type:%d", type); - - VisionPacket p1 = { - .type = VIPC_STREAM_SUBSCRIBE, - .d = { .stream_sub = { .type = type, .tbuffer = true, }, }, - }; - err = vipc_send(fd, &p1); - if (err < 0) { - close(fd); - return 0; - } - - do { - err = vipc_recv(fd, rp); - if (err <= 0) { - close(fd); - return 0; - } - - // release what we aren't ready for yet - if (rp->type == VIPC_STREAM_ACQUIRE) { - VisionPacket rep = { - .type = VIPC_STREAM_RELEASE, - .d = { .stream_rel = { - .type = rp->d.stream_acq.type, - .idx = rp->d.stream_acq.idx, - }}, - }; - vipc_send(fd, &rep); - } - } while (rp->type != VIPC_STREAM_BUFS || rp->d.stream_bufs.type != type); - - return 1; -} - -static void* vision_connect_thread(void *args) { - set_thread_name("vision_connect"); - - UIState *s = (UIState*)args; - while (!do_exit) { - usleep(100000); - pthread_mutex_lock(&s->lock); - bool connected = s->vision_connected; - pthread_mutex_unlock(&s->lock); - if (connected) continue; - - int fd = vipc_connect(); - if (fd < 0) continue; - - VisionPacket back_rp, front_rp; - if (!vision_subscribe(fd, &back_rp, VISION_STREAM_RGB_BACK)) continue; - if (!vision_subscribe(fd, &front_rp, VISION_STREAM_RGB_FRONT)) continue; - - pthread_mutex_lock(&s->lock); - assert(!s->vision_connected); - s->ipc_fd = fd; - - ui_init_vision(s, - back_rp.d.stream_bufs, back_rp.num_fds, back_rp.fds, - front_rp.d.stream_bufs, front_rp.num_fds, front_rp.fds); - - s->vision_connected = true; - s->vision_seen = true; - s->vision_connect_firstrun = true; - - // Drain sockets - s->sm->drain(); - - pthread_mutex_unlock(&s->lock); - } - return NULL; -} - -#ifdef QCOM - -#include -#include -#include - -static void* light_sensor_thread(void *args) { - int err; - set_thread_name("light_sensor"); - - UIState *s = (UIState*)args; - s->light_sensor = 0.0; - - struct sensors_poll_device_t* device; - struct sensors_module_t* module; - - hw_get_module(SENSORS_HARDWARE_MODULE_ID, (hw_module_t const**)&module); - sensors_open(&module->common, &device); - - // need to do this - struct sensor_t const* list; - module->get_sensors_list(module, &list); - - int SENSOR_LIGHT = 7; - - err = device->activate(device, SENSOR_LIGHT, 0); - if (err != 0) goto fail; - err = device->activate(device, SENSOR_LIGHT, 1); - if (err != 0) goto fail; - - device->setDelay(device, SENSOR_LIGHT, ms2ns(100)); - - while (!do_exit) { - static const size_t numEvents = 1; - sensors_event_t buffer[numEvents]; - - int n = device->poll(device, buffer, numEvents); - if (n < 0) { - LOG_100("light_sensor_poll failed: %d", n); - } - if (n > 0) { - s->light_sensor = buffer[0].light; - } - } - sensors_close(device); - return NULL; - -fail: - LOGE("LIGHT SENSOR IS MISSING"); - s->light_sensor = 255; - return NULL; -} - -#endif - -int main(int argc, char* argv[]) { - int err; - setpriority(PRIO_PROCESS, 0, -14); - - zsys_handler_set(NULL); - signal(SIGINT, (sighandler_t)set_do_exit); - - UIState uistate = {}; - UIState *s = &uistate; - ui_init(s); - - enable_event_processing(true); - - pthread_t connect_thread_handle; - err = pthread_create(&connect_thread_handle, NULL, - vision_connect_thread, s); - assert(err == 0); - -#ifdef QCOM - pthread_t light_sensor_thread_handle; - err = pthread_create(&light_sensor_thread_handle, NULL, - light_sensor_thread, s); - assert(err == 0); -#endif - - TouchState touch = {0}; - touch_init(&touch); - s->touch_fd = touch.fd; - - // light sensor scaling params - const bool LEON = util::read_file("/proc/cmdline").find("letv") != std::string::npos; - - float brightness_b, brightness_m; - int result = read_param(&brightness_b, "BRIGHTNESS_B", true); - result += read_param(&brightness_m, "BRIGHTNESS_M", true); - - if(result != 0){ - brightness_b = LEON ? 10.0 : 5.0; - brightness_m = LEON ? 2.6 : 1.3; - write_param_float(brightness_b, "BRIGHTNESS_B", true); - write_param_float(brightness_m, "BRIGHTNESS_M", true); - } - - float smooth_brightness = brightness_b; - - const int MIN_VOLUME = LEON ? 12 : 9; - const int MAX_VOLUME = LEON ? 15 : 12; - assert(s->sound.init(MIN_VOLUME)); - - int draws = 0; - - while (!do_exit) { - bool should_swap = false; - if (!s->started) { - // Delay a while to avoid 9% cpu usage while car is not started and user is keeping touching on the screen. - // Don't hold the lock while sleeping, so that vision_connect_thread have chances to get the lock. - usleep(30 * 1000); - } - pthread_mutex_lock(&s->lock); - double u1 = millis_since_boot(); - - // light sensor is only exposed on EONs - float clipped_brightness = (s->light_sensor*brightness_m) + brightness_b; - if (clipped_brightness > 512) clipped_brightness = 512; - smooth_brightness = clipped_brightness * 0.01 + smooth_brightness * 0.99; - if (smooth_brightness > 255) smooth_brightness = 255; - ui_set_brightness(s, (int)smooth_brightness); - - // resize vision for collapsing sidebar - const bool hasSidebar = !s->scene.uilayout_sidebarcollapsed; - s->scene.ui_viz_rx = hasSidebar ? box_x : (box_x - sbr_w + (bdr_s * 2)); - s->scene.ui_viz_rw = hasSidebar ? box_w : (box_w + sbr_w - (bdr_s * 2)); - s->scene.ui_viz_ro = hasSidebar ? -(sbr_w - 6 * bdr_s) : 0; - - // poll for touch events - int touch_x = -1, touch_y = -1; - int touched = touch_poll(&touch, &touch_x, &touch_y, 0); - if (touched == 1) { - set_awake(s, true); - handle_sidebar_touch(s, touch_x, touch_y); - handle_vision_touch(s, touch_x, touch_y); - } - - if (!s->started) { - // always process events offroad - check_messages(s); - - if (s->started) { - s->controls_timeout = 5 * UI_FREQ; - } - } else { - set_awake(s, true); - // Car started, fetch a new rgb image from ipc - if (s->vision_connected){ - ui_update(s); - } - - check_messages(s); - - // Visiond process is just stopped, force a redraw to make screen blank again. - if (!s->started) { - s->scene.uilayout_sidebarcollapsed = false; - ui_draw(s); - glFinish(); - should_swap = true; - } - } - - // manage wakefulness - if (s->awake_timeout > 0) { - s->awake_timeout--; - } else { - set_awake(s, false); - } - - // manage hardware disconnect - if (s->hardware_timeout > 0) { - s->hardware_timeout--; - } else { - s->scene.hwType = cereal::HealthData::HwType::UNKNOWN; - } - - // Don't waste resources on drawing in case screen is off - if (s->awake) { - ui_draw(s); - glFinish(); - should_swap = true; - } - - s->sound.setVolume(fmin(MAX_VOLUME, MIN_VOLUME + s->scene.controls_state.getVEgo() / 5)); // up one notch every 5 m/s - - if (s->controls_timeout > 0) { - s->controls_timeout--; - } else if (s->started && !s->scene.frontview) { - if (!s->controls_seen) { - // car is started, but controlsState hasn't been seen at all - s->scene.alert_text1 = "openpilot Unavailable"; - s->scene.alert_text2 = "Waiting for controls to start"; - s->scene.alert_size = cereal::ControlsState::AlertSize::MID; - } else { - // car is started, but controls is lagging or died + // Handle controls timeout + if (s->started && !s->scene.frontview && ((s->sm)->frame - s->started_frame) > 5*UI_FREQ) { + if ((s->sm)->rcv_frame("controlsState") < s->started_frame) { + // car is started, but controlsState hasn't been seen at all + s->scene.alert_text1 = "openpilot Unavailable"; + s->scene.alert_text2 = "Waiting for controls to start"; + s->scene.alert_size = cereal::ControlsState::AlertSize::MID; + } else if (((s->sm)->frame - (s->sm)->rcv_frame("controlsState")) > 5*UI_FREQ) { + // car is started, but controls is lagging or died + if (s->scene.alert_text2 != "Controls Unresponsive") { + s->sound->play(AudibleAlert::CHIME_WARNING_REPEAT); LOGE("Controls unresponsive"); - - if (s->scene.alert_text2 != "Controls Unresponsive") { - s->sound.play(AudibleAlert::CHIME_WARNING_REPEAT); - } - - s->scene.alert_text1 = "TAKE CONTROL IMMEDIATELY"; - s->scene.alert_text2 = "Controls Unresponsive"; - s->scene.alert_size = cereal::ControlsState::AlertSize::FULL; - update_status(s, STATUS_ALERT); } - ui_draw_vision_alert(s, s->scene.alert_size, s->status, s->scene.alert_text1.c_str(), s->scene.alert_text2.c_str()); - } - read_param_timeout(&s->is_metric, "IsMetric", &s->is_metric_timeout); - read_param_timeout(&s->longitudinal_control, "LongitudinalControl", &s->longitudinal_control_timeout); - read_param_timeout(&s->limit_set_speed, "LimitSetSpeed", &s->limit_set_speed_timeout); - read_param_timeout(&s->speed_lim_off, "SpeedLimitOffset", &s->limit_set_speed_timeout); - int param_read = read_param_timeout(&s->last_athena_ping, "LastAthenaPingTime", &s->last_athena_ping_timeout); - if (param_read != -1) { // Param was updated this loop - if (param_read != 0) { // Failed to read param - s->scene.athenaStatus = NET_DISCONNECTED; - } else if (nanos_since_boot() - s->last_athena_ping < 70e9) { - s->scene.athenaStatus = NET_CONNECTED; - } else { - s->scene.athenaStatus = NET_ERROR; - } - } - update_offroad_layout_state(s); - - pthread_mutex_unlock(&s->lock); - - // the bg thread needs to be scheduled, so the main thread needs time without the lock - // safe to do this outside the lock? - if (should_swap) { - double u2 = millis_since_boot(); - if (u2-u1 > 66) { - // warn on sub 15fps - LOGW("slow frame(%d) time: %.2f", draws, u2-u1); - } - draws++; - framebuffer_swap(s->fb); + s->scene.alert_text1 = "TAKE CONTROL IMMEDIATELY"; + s->scene.alert_text2 = "Controls Unresponsive"; + s->scene.alert_size = cereal::ControlsState::AlertSize::FULL; + s->status = STATUS_ALERT; } } - set_awake(s, true); - - // wake up bg thread to exit - pthread_mutex_lock(&s->lock); - pthread_mutex_unlock(&s->lock); - -#ifdef QCOM - // join light_sensor_thread? -#endif - - err = pthread_join(connect_thread_handle, NULL); - assert(err == 0); - delete s->sm; - delete s->pm; - return 0; + // Read params + if ((s->sm)->frame % (5*UI_FREQ) == 0) { + read_param(&s->is_metric, "IsMetric"); + } else if ((s->sm)->frame % (6*UI_FREQ) == 0) { + int param_read = read_param(&s->last_athena_ping, "LastAthenaPingTime"); + if (param_read != 0) { // Failed to read param + s->scene.athenaStatus = NET_DISCONNECTED; + } else if (nanos_since_boot() - s->last_athena_ping < 70e9) { + s->scene.athenaStatus = NET_CONNECTED; + } else { + s->scene.athenaStatus = NET_ERROR; + } + } } diff --git a/selfdrive/ui/ui.hpp b/selfdrive/ui/ui.hpp index bf64ef27a..1255e54b7 100644 --- a/selfdrive/ui/ui.hpp +++ b/selfdrive/ui/ui.hpp @@ -11,8 +11,12 @@ #define NANOVG_GLES3_IMPLEMENTATION #define nvgCreate nvgCreateGLES3 #endif + #include -#include +#include +#include +#include + #include "nanovg.h" #include "common/mat.h" @@ -20,18 +24,9 @@ #include "common/visionimg.h" #include "common/framebuffer.h" #include "common/modeldata.h" +#include "common/params.h" #include "sound.hpp" -#define STATUS_STOPPED 0 -#define STATUS_DISENGAGED 1 -#define STATUS_ENGAGED 2 -#define STATUS_WARNING 3 -#define STATUS_ALERT 4 - -#define NET_CONNECTED 0 -#define NET_DISCONNECTED 1 -#define NET_ERROR 2 - #define COLOR_BLACK nvgRGBA(0, 0, 0, 255) #define COLOR_BLACK_ALPHA(x) nvgRGBA(0, 0, 0, x) #define COLOR_WHITE nvgRGBA(255, 255, 255, 255) @@ -40,76 +35,72 @@ #define COLOR_RED nvgRGBA(201, 34, 49, 255) #define COLOR_OCHRE nvgRGBA(218, 111, 37, 255) -#ifndef QCOM - #define UI_60FPS -#endif - #define UI_BUF_COUNT 4 -//#define SHOW_SPEEDLIMIT 1 -//#define DEBUG_TURN -const int vwp_w = 1920; -const int vwp_h = 1080; -const int nav_w = 640; -const int nav_ww= 760; +typedef struct Rect { + int x, y, w, h; + int centerX() const { return x + w / 2; } + int right() const { return x + w; } + int bottom() const { return y + h; } + bool ptInRect(int px, int py) const { + return px >= x && px < (x + w) && py >= y && py < (y + h); + } +} Rect; + const int sbr_w = 300; const int bdr_s = 30; -const int box_x = sbr_w+bdr_s; -const int box_y = bdr_s; -const int box_w = vwp_w-sbr_w-(bdr_s*2); -const int box_h = vwp_h-(bdr_s*2); -const int viz_w = vwp_w-(bdr_s*2); -const int ff_xoffset = 32; const int header_h = 420; const int footer_h = 280; -const int footer_y = vwp_h-bdr_s-footer_h; -const int settings_btn_h = 117; -const int settings_btn_w = 200; -const int settings_btn_x = 50; -const int settings_btn_y = 35; -const int home_btn_h = 180; -const int home_btn_w = 180; -const int home_btn_x = 60; -const int home_btn_y = vwp_h - home_btn_h - 40; +const Rect settings_btn = {50, 35, 200, 117}; +const Rect home_btn = {60, 1080 - 180 - 40, 180, 180}; -const int UI_FREQ = 30; // Hz +const int UI_FREQ = 20; // Hz const int MODEL_PATH_MAX_VERTICES_CNT = 98; -const int MODEL_LANE_PATH_CNT = 3; +const int MODEL_LANE_PATH_CNT = 2; const int TRACK_POINTS_MAX_CNT = 50 * 2; const int SET_SPEED_NA = 255; -const uint8_t bg_colors[][4] = { - [STATUS_STOPPED] = {0x07, 0x23, 0x39, 0xff}, - [STATUS_DISENGAGED] = {0x17, 0x33, 0x49, 0xff}, - [STATUS_ENGAGED] = {0x17, 0x86, 0x44, 0xff}, - [STATUS_WARNING] = {0xDA, 0x6F, 0x25, 0xff}, - [STATUS_ALERT] = {0xC9, 0x22, 0x31, 0xff}, +typedef struct Color { + uint8_t r, g, b; +} Color; + +typedef enum NetStatus { + NET_CONNECTED, + NET_DISCONNECTED, + NET_ERROR, +} NetStatus; + +typedef enum UIStatus { + STATUS_OFFROAD, + STATUS_DISENGAGED, + STATUS_ENGAGED, + STATUS_WARNING, + STATUS_ALERT, +} UIStatus; + +static std::map bg_colors = { + {STATUS_OFFROAD, {0x07, 0x23, 0x39}}, + {STATUS_DISENGAGED, {0x17, 0x33, 0x49}}, + {STATUS_ENGAGED, {0x17, 0x86, 0x44}}, + {STATUS_WARNING, {0xDA, 0x6F, 0x25}}, + {STATUS_ALERT, {0xC9, 0x22, 0x31}}, }; typedef struct UIScene { - int frontview; - int fullview; - - ModelData model; float mpc_x[50]; float mpc_y[50]; - bool world_objects_visible; mat4 extrinsic_matrix; // Last row is 0 so we can use mat4. - - float speedlimit; - bool speedlimit_valid; + bool world_objects_visible; bool is_rhd; - bool map_valid; + bool frontview; bool uilayout_sidebarcollapsed; - bool uilayout_mapenabled; // responsive layout - int ui_viz_rx; - int ui_viz_rw; + Rect viz_rect; int ui_viz_ro; std::string alert_text1; @@ -119,18 +110,22 @@ typedef struct UIScene { cereal::HealthData::HwType hwType; int satelliteCount; - uint8_t athenaStatus; + NetStatus athenaStatus; cereal::ThermalData::Reader thermal; cereal::RadarState::LeadData::Reader lead_data[2]; cereal::ControlsState::Reader controls_state; cereal::DriverState::Reader driver_state; cereal::DMonitoringState::Reader dmonitoring_state; + cereal::ModelData::Reader model; + float left_lane_points[MODEL_PATH_DISTANCE]; + float path_points[MODEL_PATH_DISTANCE]; + float right_lane_points[MODEL_PATH_DISTANCE]; } UIScene; typedef struct { float x, y; -}vertex_data; +} vertex_data; typedef struct { vertex_data v[MODEL_PATH_MAX_VERTICES_CNT]; @@ -144,8 +139,6 @@ typedef struct { typedef struct UIState { - pthread_mutex_t lock; - // framebuffer FramebufferState *fb; int fb_w, fb_h; @@ -160,93 +153,77 @@ typedef struct UIState { int img_wheel; int img_turn; int img_face; - int img_map; int img_button_settings; int img_button_home; int img_battery; int img_battery_charging; int img_network[6]; - // sockets SubMaster *sm; - PubMaster *pm; + Sound *sound; + UIStatus status; + UIScene scene; cereal::UiLayoutState::App active_app; // vision state bool vision_connected; - bool vision_connect_firstrun; - int ipc_fd; - - VIPCBuf bufs[UI_BUF_COUNT]; - VIPCBuf front_bufs[UI_BUF_COUNT]; - int cur_vision_idx; - int cur_vision_front_idx; + VisionStream stream; + // graphics GLuint frame_program; GLuint frame_texs[UI_BUF_COUNT]; EGLImageKHR khr[UI_BUF_COUNT]; void *priv_hnds[UI_BUF_COUNT]; - GLuint frame_front_texs[UI_BUF_COUNT]; - EGLImageKHR khr_front[UI_BUF_COUNT]; - void *priv_hnds_front[UI_BUF_COUNT]; GLint frame_pos_loc, frame_texcoord_loc; GLint frame_texture_loc, frame_transform_loc; - - int rgb_width, rgb_height, rgb_stride; - size_t rgb_buf_len; - - int rgb_front_width, rgb_front_height, rgb_front_stride; - size_t rgb_front_buf_len; - - UIScene scene; - bool awake; - - // timeouts - int awake_timeout; - int controls_timeout; - int speed_lim_off_timeout; - int is_metric_timeout; - int longitudinal_control_timeout; - int limit_set_speed_timeout; - int hardware_timeout; - int last_athena_ping_timeout; - - bool controls_seen; - - uint64_t last_athena_ping; - int status; - bool is_metric; - bool longitudinal_control; - bool limit_set_speed; - float speed_lim_off; - bool is_ego_over_limit; - float alert_blinking_alpha; - bool alert_blinked; - bool started; - bool vision_seen; - - std::atomic light_sensor; - - int touch_fd; - GLuint frame_vao[2], frame_vbo[2], frame_ibo[2]; mat4 rear_frame_mat, front_frame_mat; - model_path_vertices_data model_path_vertices[MODEL_LANE_PATH_CNT * 2]; + // device state + bool awake; + int awake_timeout; + std::atomic light_sensor; + + bool started; + bool ignition; + bool is_metric; + bool longitudinal_control; + uint64_t last_athena_ping; + uint64_t started_frame; + + bool alert_blinked; + float alert_blinking_alpha; track_vertices_data track_vertices[2]; - - Sound sound; + model_path_vertices_data model_path_vertices[MODEL_LANE_PATH_CNT * 2]; } UIState; -// API -void ui_draw_vision_alert(UIState *s, cereal::ControlsState::AlertSize va_size, int va_color, - const char* va_text1, const char* va_text2); -void ui_draw(UIState *s); -void ui_draw_sidebar(UIState *s); -void ui_draw_image(NVGcontext *vg, float x, float y, float w, float h, int image, float alpha); -void ui_draw_rect(NVGcontext *vg, float x, float y, float w, float h, NVGcolor color, float r = 0, int width = 0); -void ui_draw_rect(NVGcontext *vg, float x, float y, float w, float h, NVGpaint &paint, float r = 0); -void ui_nvg_init(UIState *s); +void ui_init(UIState *s); +void ui_update(UIState *s); + +int write_param_float(float param, const char* param_name, bool persistent_param = false); +template +int read_param(T* param, const char *param_name, bool persistent_param = false){ + T param_orig = *param; + char *value; + size_t sz; + + int result = read_db_value(param_name, &value, &sz, persistent_param); + if (result == 0){ + std::string s = std::string(value, sz); // value is not null terminated + free(value); + + // Parse result + std::istringstream iss(s); + iss >> *param; + + // Restore original value if parsing failed + if (iss.fail()) { + *param = param_orig; + result = -1; + } + } + return result; +} diff --git a/selfdrive/updated.py b/selfdrive/updated.py index 205906806..1788816d3 100755 --- a/selfdrive/updated.py +++ b/selfdrive/updated.py @@ -31,15 +31,15 @@ import signal import fcntl import time import threading -from cffi import FFI from pathlib import Path +from typing import List, Tuple, Optional +from common.hardware import ANDROID from common.basedir import BASEDIR from common.params import Params from selfdrive.swaglog import cloudlog from selfdrive.controls.lib.alertmanager import set_offroad_alert -TEST_IP = os.getenv("UPDATER_TEST_IP", "8.8.8.8") LOCK_FILE = os.getenv("UPDATER_LOCK_FILE", "/tmp/safe_staging_overlay.lock") STAGING_ROOT = os.getenv("UPDATER_STAGING_ROOT", "/data/safe_staging") @@ -52,14 +52,6 @@ OVERLAY_MERGED = os.path.join(STAGING_ROOT, "merged") FINALIZED = os.path.join(STAGING_ROOT, "finalized") -# Workaround for lack of os.link in the NEOS/termux python -ffi = FFI() -ffi.cdef("int link(const char *oldpath, const char *newpath);") -libc = ffi.dlopen(None) -def link(src, dest): - return libc.link(src.encode(), dest.encode()) - - class WaitTimeHelper: def __init__(self, proc): self.proc = proc @@ -69,7 +61,7 @@ class WaitTimeHelper: signal.signal(signal.SIGINT, self.graceful_shutdown) signal.signal(signal.SIGHUP, self.update_now) - def graceful_shutdown(self, signum, frame): + def graceful_shutdown(self, signum: int, frame) -> None: # umount -f doesn't appear effective in avoiding "device busy" on NEOS, # so don't actually die until the next convenient opportunity in main(). cloudlog.info("caught SIGINT/SIGTERM, dismounting overlay at next opportunity") @@ -82,35 +74,42 @@ class WaitTimeHelper: self.shutdown = True self.ready_event.set() - def update_now(self, signum, frame): + def update_now(self, signum: int, frame) -> None: cloudlog.info("caught SIGHUP, running update check immediately") self.ready_event.set() - def sleep(self, t): + def sleep(self, t: float) -> None: self.ready_event.wait(timeout=t) -def run(cmd, cwd=None, low_priority=False): +def run(cmd: List[str], cwd: Optional[str] = None, low_priority: bool = False): if low_priority: cmd = ["nice", "-n", "19"] + cmd return subprocess.check_output(cmd, cwd=cwd, stderr=subprocess.STDOUT, encoding='utf8') -def set_consistent_flag(consistent): - os.system("sync") +def set_consistent_flag(consistent: bool) -> None: + os.sync() consistent_file = Path(os.path.join(FINALIZED, ".overlay_consistent")) if consistent: consistent_file.touch() elif not consistent and consistent_file.exists(): consistent_file.unlink() - os.system("sync") + os.sync() -def set_update_available_params(new_version): +def set_params(new_version: bool, failed_count: int, exception: Optional[str]) -> None: params = Params() - t = datetime.datetime.utcnow().isoformat() - params.put("LastUpdateTime", t.encode('utf8')) + params.put("UpdateFailedCount", str(failed_count)) + if failed_count == 0: + t = datetime.datetime.utcnow().isoformat() + params.put("LastUpdateTime", t.encode('utf8')) + + if exception is None: + params.delete("LastUpdateException") + else: + params.put("LastUpdateException", exception) if new_version: try: @@ -123,13 +122,7 @@ def set_update_available_params(new_version): params.put("UpdateAvailable", "1") -def dismount_ovfs(): - if os.path.ismount(OVERLAY_MERGED): - cloudlog.error("unmounting existing overlay") - run(["umount", "-l", OVERLAY_MERGED]) - - -def setup_git_options(cwd): +def setup_git_options(cwd: str) -> None: # We sync FS object atimes (which NEOS doesn't use) and mtimes, but ctimes # are outside user control. Make sure Git is set up to ignore system ctimes, # because they change when we make hard links during finalize. Otherwise, @@ -143,66 +136,133 @@ def setup_git_options(cwd): ("core.checkStat", "minimal"), ] for option, value in git_cfg: - try: - ret = run(["git", "config", "--get", option], cwd) - config_ok = ret.strip() == value - except subprocess.CalledProcessError: - config_ok = False - - if not config_ok: - cloudlog.info(f"Setting git '{option}' to '{value}'") - run(["git", "config", option, value], cwd) + run(["git", "config", option, value], cwd) -def init_ovfs(): +def dismount_overlay() -> None: + if os.path.ismount(OVERLAY_MERGED): + cloudlog.info("unmounting existing overlay") + run(["umount", "-l", OVERLAY_MERGED]) + + +def init_overlay() -> None: + + overlay_init_file = Path(os.path.join(BASEDIR, ".overlay_init")) + + # Re-create the overlay if BASEDIR/.git has changed since we created the overlay + if overlay_init_file.is_file(): + git_dir_path = os.path.join(BASEDIR, ".git") + new_files = run(["find", git_dir_path, "-newer", str(overlay_init_file)]) + if not len(new_files.splitlines()): + # A valid overlay already exists + return + else: + cloudlog.info(".git directory changed, recreating overlay") + cloudlog.info("preparing new safe staging area") + Params().put("UpdateAvailable", "0") - set_consistent_flag(False) - - dismount_ovfs() + dismount_overlay() if os.path.isdir(STAGING_ROOT): shutil.rmtree(STAGING_ROOT) - for dirname in [STAGING_ROOT, OVERLAY_UPPER, OVERLAY_METADATA, OVERLAY_MERGED, FINALIZED]: + for dirname in [STAGING_ROOT, OVERLAY_UPPER, OVERLAY_METADATA, OVERLAY_MERGED]: os.mkdir(dirname, 0o755) - if not os.lstat(BASEDIR).st_dev == os.lstat(OVERLAY_MERGED).st_dev: + if os.lstat(BASEDIR).st_dev != os.lstat(OVERLAY_MERGED).st_dev: raise RuntimeError("base and overlay merge directories are on different filesystems; not valid for overlay FS!") - # Remove consistent flag from current BASEDIR so it's not copied over - if os.path.isfile(os.path.join(BASEDIR, ".overlay_consistent")): - os.remove(os.path.join(BASEDIR, ".overlay_consistent")) - # Leave a timestamped canary in BASEDIR to check at startup. The device clock # should be correct by the time we get here. If the init file disappears, or # critical mtimes in BASEDIR are newer than .overlay_init, continue.sh can # assume that BASEDIR has used for local development or otherwise modified, # and skips the update activation attempt. - Path(os.path.join(BASEDIR, ".overlay_init")).touch() + consistent_file = Path(os.path.join(BASEDIR, ".overlay_consistent")) + if consistent_file.is_file(): + consistent_file.unlink() + overlay_init_file.touch() - os.system("sync") + os.sync() overlay_opts = f"lowerdir={BASEDIR},upperdir={OVERLAY_UPPER},workdir={OVERLAY_METADATA}" run(["mount", "-t", "overlay", "-o", overlay_opts, "none", OVERLAY_MERGED]) -def finalize_from_ovfs(): +def finalize_update() -> None: """Take the current OverlayFS merged view and finalize a copy outside of OverlayFS, ready to be swapped-in at BASEDIR. Copy using shutil.copytree""" # Remove the update ready flag and any old updates cloudlog.info("creating finalized version of the overlay") set_consistent_flag(False) - shutil.rmtree(FINALIZED) # Copy the merged overlay view and set the update ready flag + if os.path.exists(FINALIZED): + shutil.rmtree(FINALIZED) shutil.copytree(OVERLAY_MERGED, FINALIZED, symlinks=True) + + # Log git repo corruption + fsck = run(["git", "fsck", "--no-progress"], FINALIZED).rstrip() + if len(fsck): + cloudlog.error(f"found git corruption, git fsck:\n{fsck}") + set_consistent_flag(True) cloudlog.info("done finalizing overlay") -def attempt_update(wait_helper): - cloudlog.info("attempting git update inside staging overlay") +def handle_neos_update(wait_helper: WaitTimeHelper) -> None: + with open(NEOS_VERSION, "r") as f: + cur_neos = f.read().strip() + + updated_neos = run(["bash", "-c", r"unset REQUIRED_NEOS_VERSION && source launch_env.sh && \ + echo -n $REQUIRED_NEOS_VERSION"], OVERLAY_MERGED).strip() + + cloudlog.info(f"NEOS version check: {cur_neos} vs {updated_neos}") + if cur_neos == updated_neos: + return + + cloudlog.info(f"Beginning background download for NEOS {updated_neos}") + set_offroad_alert("Offroad_NeosUpdate", True) + + updater_path = os.path.join(OVERLAY_MERGED, "installer/updater/updater") + update_manifest = f"file://{OVERLAY_MERGED}/installer/updater/update.json" + + neos_downloaded = False + start_time = time.monotonic() + # Try to download for one day + while not neos_downloaded and not wait_helper.shutdown and \ + (time.monotonic() - start_time < 60*60*24): + wait_helper.ready_event.clear() + try: + run([updater_path, "bgcache", update_manifest], OVERLAY_MERGED, low_priority=True) + neos_downloaded = True + except subprocess.CalledProcessError: + cloudlog.info("NEOS background download failed, retrying") + wait_helper.sleep(120) + + # If the download failed, we'll show the alert again when we retry + set_offroad_alert("Offroad_NeosUpdate", False) + if not neos_downloaded: + raise Exception("Failed to download NEOS update") + cloudlog.info(f"NEOS background download successful, took {time.monotonic() - start_time} seconds") + + +def check_git_fetch_result(fetch_txt): + err_msg = "Failed to add the host to the list of known hosts (/data/data/com.termux/files/home/.ssh/known_hosts).\n" + return len(fetch_txt) > 0 and (fetch_txt != err_msg) + + +def check_for_update() -> Tuple[bool, bool]: + setup_git_options(OVERLAY_MERGED) + try: + git_fetch_output = run(["git", "fetch", "--dry-run"], OVERLAY_MERGED, low_priority=True) + return True, check_git_fetch_result(git_fetch_output) + except subprocess.CalledProcessError: + return False, False + + +def fetch_update(wait_helper: WaitTimeHelper) -> bool: + cloudlog.info("attempting git fetch inside staging overlay") setup_git_options(OVERLAY_MERGED) @@ -212,9 +272,7 @@ def attempt_update(wait_helper): cur_hash = run(["git", "rev-parse", "HEAD"], OVERLAY_MERGED).rstrip() upstream_hash = run(["git", "rev-parse", "@{u}"], OVERLAY_MERGED).rstrip() new_version = cur_hash != upstream_hash - - err_msg = "Failed to add the host to the list of known hosts (/data/data/com.termux/files/home/.ssh/known_hosts).\n" - git_fetch_result = len(git_fetch_output) > 0 and (git_fetch_output != err_msg) + git_fetch_result = check_git_fetch_result(git_fetch_output) cloudlog.info("comparing %s to %s" % (cur_hash, upstream_hash)) if new_version or git_fetch_result: @@ -230,48 +288,15 @@ def attempt_update(wait_helper): ] cloudlog.info("git reset success: %s", '\n'.join(r)) - # Download the accompanying NEOS version if it doesn't match the current version - with open(NEOS_VERSION, "r") as f: - cur_neos = f.read().strip() - - updated_neos = run(["bash", "-c", r"unset REQUIRED_NEOS_VERSION && source launch_env.sh && \ - echo -n $REQUIRED_NEOS_VERSION"], OVERLAY_MERGED).strip() - - cloudlog.info(f"NEOS version check: {cur_neos} vs {updated_neos}") - if cur_neos != updated_neos: - cloudlog.info(f"Beginning background download for NEOS {updated_neos}") - - set_offroad_alert("Offroad_NeosUpdate", True) - updater_path = os.path.join(OVERLAY_MERGED, "installer/updater/updater") - update_manifest = f"file://{OVERLAY_MERGED}/installer/updater/update.json" - - neos_downloaded = False - start_time = time.monotonic() - # Try to download for one day - while (time.monotonic() - start_time < 60*60*24) and not wait_helper.shutdown: - wait_helper.ready_event.clear() - try: - run([updater_path, "bgcache", update_manifest], OVERLAY_MERGED, low_priority=True) - neos_downloaded = True - break - except subprocess.CalledProcessError: - cloudlog.info("NEOS background download failed, retrying") - wait_helper.sleep(120) - - # If the download failed, we'll show the alert again when we retry - set_offroad_alert("Offroad_NeosUpdate", False) - if not neos_downloaded: - raise Exception("Failed to download NEOS update") - - cloudlog.info(f"NEOS background download successful, took {time.monotonic() - start_time} seconds") + if ANDROID: + handle_neos_update(wait_helper) # Create the finalized, ready-to-swap update - finalize_from_ovfs() + finalize_update() cloudlog.info("openpilot update successful!") else: cloudlog.info("nothing new from git at this time") - set_update_available_params(new_version) return new_version @@ -281,7 +306,7 @@ def main(): if params.get("DisableUpdates") == b"1": raise RuntimeError("updates are disabled by the DisableUpdates param") - if os.geteuid() != 0: + if ANDROID and os.geteuid() != 0: raise RuntimeError("updated must be launched as root!") # Set low io priority @@ -292,52 +317,56 @@ def main(): ov_lock_fd = open(LOCK_FILE, 'w') try: fcntl.flock(ov_lock_fd, fcntl.LOCK_EX | fcntl.LOCK_NB) - except IOError: - raise RuntimeError("couldn't get overlay lock; is another updated running?") + except IOError as e: + raise RuntimeError("couldn't get overlay lock; is another updated running?") from e # Wait for IsOffroad to be set before our first update attempt wait_helper = WaitTimeHelper(proc) wait_helper.sleep(30) + overlay_init = Path(os.path.join(BASEDIR, ".overlay_init")) + if overlay_init.exists(): + overlay_init.unlink() + + first_run = True + last_fetch_time = 0 update_failed_count = 0 - update_available = False - overlay_initialized = False + + # Run the update loop + # * every 1m, do a lightweight internet/update check + # * every 10m, do a full git fetch while not wait_helper.shutdown: + update_now = wait_helper.ready_event.is_set() wait_helper.ready_event.clear() - # Check for internet every 30s + # Don't run updater while onroad or if the time's wrong time_wrong = datetime.datetime.utcnow().year < 2019 - ping_failed = os.system(f"ping -W 4 -c 1 {TEST_IP}") != 0 - if ping_failed or time_wrong: + is_onroad = params.get("IsOffroad") != b"1" + if is_onroad or time_wrong: wait_helper.sleep(30) + cloudlog.info("not running updater, not offroad") continue # Attempt an update exception = None + new_version = False update_failed_count += 1 try: - # Re-create the overlay if BASEDIR/.git has changed since we created the overlay - if overlay_initialized: - overlay_init_fn = os.path.join(BASEDIR, ".overlay_init") - git_dir_path = os.path.join(BASEDIR, ".git") - new_files = run(["find", git_dir_path, "-newer", overlay_init_fn]) + init_overlay() - if len(new_files.splitlines()): - cloudlog.info(".git directory changed, recreating overlay") - overlay_initialized = False - - if not overlay_initialized: - init_ovfs() - overlay_initialized = True - - if params.get("IsOffroad") == b"1": - update_available = attempt_update(wait_helper) or update_available + internet_ok, update_available = check_for_update() + if internet_ok and not update_available: update_failed_count = 0 - if not update_available and os.path.isdir(NEOSUPDATE_DIR): - shutil.rmtree(NEOSUPDATE_DIR) - else: - cloudlog.info("not running updater, openpilot running") + # Fetch updates at most every 10 minutes + if internet_ok and (update_now or time.monotonic() - last_fetch_time > 60*10): + new_version = fetch_update(wait_helper) + update_failed_count = 0 + last_fetch_time = time.monotonic() + + if first_run and not new_version and os.path.isdir(NEOSUPDATE_DIR): + shutil.rmtree(NEOSUPDATE_DIR) + first_run = False except subprocess.CalledProcessError as e: cloudlog.event( "update process failed", @@ -345,21 +374,15 @@ def main(): output=e.output, returncode=e.returncode ) - exception = e - overlay_initialized = False - except Exception: + exception = f"command failed: {e.cmd}\n{e.output}" + except Exception as e: cloudlog.exception("uncaught updated exception, shouldn't happen") + exception = str(e) - params.put("UpdateFailedCount", str(update_failed_count)) - if exception is None: - params.delete("LastUpdateException") - else: - params.put("LastUpdateException", f"command failed: {exception.cmd}\n{exception.output}") + set_params(new_version, update_failed_count, exception) + wait_helper.sleep(60) - # Wait 10 minutes between update attempts - wait_helper.sleep(60*10) - - dismount_ovfs() + dismount_overlay() if __name__ == "__main__": main() diff --git a/selfdrive/version.py b/selfdrive/version.py index 4fec0b4cc..ef975f894 100644 --- a/selfdrive/version.py +++ b/selfdrive/version.py @@ -1,34 +1,36 @@ #!/usr/bin/env python3 import os import subprocess +from typing import List, Optional + from common.basedir import BASEDIR from selfdrive.swaglog import cloudlog -def run_cmd(cmd): +def run_cmd(cmd: List[str]) -> str: return subprocess.check_output(cmd, encoding='utf8').strip() -def run_cmd_default(cmd, default=None): +def run_cmd_default(cmd: List[str], default: Optional[str] = None) -> Optional[str]: try: return run_cmd(cmd) except subprocess.CalledProcessError: return default -def get_git_commit(branch="HEAD", default=None): +def get_git_commit(branch: str = "HEAD", default: Optional[str] = None) -> Optional[str]: return run_cmd_default(["git", "rev-parse", branch], default=default) -def get_git_branch(default=None): +def get_git_branch(default: Optional[str] = None) -> Optional[str]: return run_cmd_default(["git", "rev-parse", "--abbrev-ref", "HEAD"], default=default) -def get_git_full_branchname(default=None): +def get_git_full_branchname(default: Optional[str] = None) -> Optional[str]: return run_cmd_default(["git", "rev-parse", "--abbrev-ref", "--symbolic-full-name", "@{u}"], default=default) -def get_git_remote(default=None): +def get_git_remote(default: Optional[str] = None) -> Optional[str]: try: local_branch = run_cmd(["git", "name-rev", "--name-only", "HEAD"]) tracking_remote = run_cmd(["git", "config", "branch." + local_branch + ".remote"]) @@ -42,12 +44,12 @@ with open(os.path.join(os.path.dirname(os.path.abspath(__file__)), "common", "ve prebuilt = os.path.exists(os.path.join(BASEDIR, 'prebuilt')) -training_version = b"0.2.0" -terms_version = b"2" +training_version: bytes = b"0.2.0" +terms_version: bytes = b"2" -dirty = True -comma_remote = False -tested_branch = False +dirty: bool = True +comma_remote: bool = False +tested_branch: bool = False origin = get_git_remote() branch = get_git_full_branchname()