Compare commits

..

182 Commits

Author SHA1 Message Date
Willem Melching 59bd58c940 Remove unused path offset learner 2020-02-06 13:48:56 -08:00
Willem Melching 105b95ffd7 Cancel lane change on blinker off 2020-02-06 13:48:20 -08:00
Willem Melching fdabd57e7e Add extra engine FW for Corolla LE 2020-02-06 11:22:38 -08:00
Willem Melching 719e0572e1 Fix fw_versions.py --scan 2020-02-06 10:58:18 -08:00
Adeeb 0ad5715255 use services.h in loggerd (#1056)
* loggerd: use services.h

* don't need yaml-cpp anymore

* forgot that
2020-02-06 10:56:01 -08:00
Adeeb 30dffb486a test car models: fix random hanging (#1055)
* debug print

* unlogger doesn't need fcamera.hevc

* only need the rlog

* tiemout for log downloading

* try again after timeout
2020-02-06 10:53:59 -08:00
Harald Schafer 0728e57578 bump fix 2020-02-05 13:55:21 -08:00
Harald Schafer c2d5deeccc bump laika 2020-02-05 13:41:21 -08:00
robbederks d0c1f9a864 Added hw status to ublox test scripts and ubloxGnss packet (#1054)
* Added hw status to ublox test scripts and ubloxGnss packet

* Added extra config to boardd

* Implemented new ublox message handler in ubloxd

* Fixed debug text

* Added some explanation and cereal bump
2020-02-05 11:12:03 -08:00
Harald Schafer 920572442b remove lambda 2020-02-04 19:55:17 -08:00
George Hotz c50c718293 Cleanup simulator and add usage instructions (#1050)
* cleanup simulator files

* minor updates

* update readme

* keras runner builds

* hmm, still doesn't work

* keras runner works

* should work with python3 keras mod

* touchups
2020-02-04 19:46:57 -08:00
Harald Schafer 6b1506740e comments 2020-02-04 16:48:45 -08:00
Harald Schafer f50e016c12 better comments 2020-02-04 14:07:35 -08:00
Harald Schafer bc725b98c9 reverse geocoder 2020-02-04 13:44:39 -08:00
George Hotz 9c5e035838 add zmq to rpath on x64 2020-02-03 18:14:45 -08:00
Harald Schafer 6d5cde3412 add tf model 2020-02-03 18:10:26 -08:00
Willem Melching 07264699b7 bump opendbc: add Lexus NX300H 2020-02-03 16:42:54 -08:00
Willem Melching 37a73d5c94 specify commaai in submodules url (#1049) 2020-02-03 16:33:49 -08:00
Andy Haden 79c149f42d Update APKs 2020-02-03 16:14:03 -08:00
Andy Haden 47f4396d7d Replace 'EON' in offroad alerts 2020-02-03 16:09:08 -08:00
Andy Haden e8959e6b1a get_network_type: Sort, correct cell network lookup and fix for pc 2020-02-03 15:52:06 -08:00
Willem Melching c770f54103 Fix dirty files reporting 2020-02-03 15:12:59 -08:00
Willem Melching a98000849e Removed old signed firmware during release build 2020-02-03 15:01:36 -08:00
Willem Melching e32e754a74 Cleanup files_common for phone release 2020-02-03 14:41:43 -08:00
Willem Melching 118198605f Use full name for docker caching 2020-02-03 14:12:35 -08:00
Willem Melching 0fb9971728 Only add the file list to the release 2020-02-03 14:11:23 -08:00
Willem Melching 2fe75cb7d0 Noqa on release build script 2020-02-03 13:37:57 -08:00
George Hotz 6797910ab0 fix releases.md typo 2020-02-03 13:35:31 -08:00
Willem Melching 5ea95c3d87 Use dockerhub 2020-02-03 13:33:21 -08:00
Harald Schafer fe250d6a9e make it converge within 1min 2020-02-03 13:24:33 -08:00
Willem Melching 58f4f5aac4 use base docker container when building 2020-02-03 13:10:38 -08:00
Willem Melching 0e1fc45bc3 Update release files 2020-02-03 12:52:20 -08:00
Willem Melching 64feca5692 Fix bias state number in paramsd too 2020-02-03 12:47:04 -08:00
Harald Schafer 1db7b43870 fix 2020-02-03 12:45:12 -08:00
Andy Haden 9b1f2d5c64 manager: verify daemon process cmdline 2020-02-03 12:27:13 -08:00
Willem Melching a53577ed35 Remove unused states from locationd yawrate kalman filer 2020-02-03 12:23:41 -08:00
Willem Melching 77cb0b1464 Add pipfile to release files so CI can run 2020-02-03 12:16:38 -08:00
Willem Melching f609507afd Update release notes 2020-02-03 12:16:06 -08:00
Willem Melching 98f5d30455 Handle get_network_type exception 2020-02-03 11:29:05 -08:00
HaraldSchafer f610e596c4 Multipath supercombo (#1036)
* exclude stuff outside of validity window

* 94dd2da7-23ae-4628-9d12-37f58b379110/10

* fbe443fd-1d65-4b4d-8e3a-3817b58bacd0/50

* sanity clip
2020-02-03 11:25:22 -08:00
Willem Melching 7c94b36171 Add some more firmware fingerprints 2020-02-03 11:24:25 -08:00
illumiN8i 39c9c85562 2020 Lexus RX 350 (#1046)
* Lexus RX 2020

* Update test_car_models.py
2020-02-03 10:12:13 -08:00
illumiN8i 831cb68a64 Added Central Europe Prime (#1047)
Co-authored-by: ErichMoraga <33645296+ErichMoraga@users.noreply.github.com>
2020-02-03 09:18:04 -08:00
Drew Hintz 5aff2b6885 support for Chrysler Pacifica 2020 Hybrid (#1021)
* support for Chrysler Pacifica 2020 Hybrid
Thanks to Benson in Discord.
Example segment: 8190c7275a24557b|2020-01-29--08-33-58--7

* Add relevant route to selfdrive/test/test_car_models.py

* fix segment test name
2020-02-03 09:17:45 -08:00
George Hotz 811b3b7a9a Fix ui on mac (#1044)
* remove line_shader dead code

* fix glfwCreateWindow

* don't assert on ipc socket failure

* window now appears on mac
2020-02-02 22:19:26 -08:00
George Hotz c42e2ecc50 manager runs on Mac, and other openpilot for PC fixes (#1037)
* use the openpilot/persist directory on PC

* manager runs on mac

* sim runs w/o carla

* fix params location in test

* that rmtree can fail and it's okay

* refactor params clear functionality

* set PARAMS_PATH
2020-02-02 12:15:02 -08:00
cfranhonda 0470b25071 FP 2019 Civic Hatchback EX (#1040)
* add 2019 Civic Hatchback EX

@dvburke4 For support with Black Panda comma power. Fp taken from [1]

* missing ] oops
2020-02-02 11:23:27 -08:00
George Hotz f72f78f2b9 Support scons build on Mac (#1034)
* fix clock and add Darwin sconstruct

* it builds, this changes should be simplifications too

* fix boardd build

* that's the real type of EGLClientBuffer

* remove extra lines

* ui needs opencl on phone
2020-02-01 23:36:50 -08:00
Willem Melching df6fa7acbd move fw query debug script 2020-02-01 18:53:43 -08:00
Adeeb d4c3c7f6f8 test_car_models: fail if a model has no routes (#1029) 2020-02-01 18:41:25 -08:00
illumiN8i a7efc8a897 2020 Toyota Prius Prime LE (#1032) 2020-02-01 18:41:00 -08:00
Willem Melching 822b32656b Add fingerprint test to ci 2020-02-01 18:40:04 -08:00
Willem Melching bfa2d030d2 Add batch of firmware versions 2020-02-01 13:06:05 -08:00
robbederks 3f92bc2061 Bumped panda (#1027) 2020-02-01 12:16:50 -08:00
Andrew Valish 770903520d Add network_type to thermald (#1030)
* add network_type to thermal log

* move get_network_type to android library
2020-02-01 12:11:10 -08:00
Arne Schwarck 75fdebb1ee Add German RAV4H_TSS2 Lounge FP (#1031)
2f210c724fd9f73b|2020-01-31--14-12-53--0
2020-02-01 10:45:41 -08:00
illumiN8i c9d49b8e4a Sort cars (#1026) 2020-01-31 08:30:32 -08:00
ZwX1616 6322a275d6 dmonitoringd (#1016) 2020-01-30 19:12:44 -08:00
Willem Melching b7aeb5d64d Cache FW query (#1025)
* split fw query and matching

* Read cached firmware versions

* add tests

* this works
2020-01-30 17:57:20 -08:00
Comma Device 58262bac9d use proper timeout on frontFrame to turn off IR leds 2020-01-30 15:52:27 -08:00
Willem Melching a75891f099 remove old README 2020-01-30 14:55:38 -08:00
Willem Melching a053244a5a log fingerprint source 2020-01-30 14:39:26 -08:00
Willem Melching 356f353489 FW fingerprint for Honda & Toyota (#961)
* add script to process logs

* Skip rav4 ESP

* Improve gathering script

* Update firmware versions for honda and toyota

* more firmware versions

* If FW query returns 1 candidate, use it

* Add FW versions

* Fix COROLLA_TSS2 two enigine addresses

* uncomment rav4h tss2

* add progress bar to test script

* Batch with more ecu versions
2020-01-30 14:32:26 -08:00
Willem Melching c9501cc164 Also show top 10 procs by RAM usage 2020-01-30 14:17:09 -08:00
Willem Melching 4dac1128f1 Only ignition_last is global 2020-01-30 13:53:19 -08:00
Willem Melching 3a65206c8d Disable IR leds when no ignition detected 2020-01-30 13:42:40 -08:00
Willem Melching 4d5957432e Add dockerfile to release files 2020-01-30 13:36:15 -08:00
illumiN8i 3ebb0bc46f Lexus RX 2016-2017 (#1001)
fix dbc name

Add Lexus RX to test_car_models.py

correct route in Lexus RX test

Update test_car_models.py
2020-01-30 13:32:30 -08:00
Willem Melching a2fe62ce77 Fix source brnach in master-ci push 2020-01-30 13:31:14 -08:00
Willem Melching a97ebc28b5 Fix ci routes sync script target path 2020-01-30 13:21:14 -08:00
Willem Melching ada141e5e3 Fix target branch on master release build 2020-01-30 13:21:04 -08:00
Willem Melching 0319861700 Jenkins pipeline to create master-ci (#1019)
* Added Jenkinsfile

* Added Jenkinsfile

* Added Jenkinsfile

* change order

* sudo

* whoami?

* Added Jenkinsfile

* install git

* Untested build scripts

* Add lockable resource

* Fix syntax

* Only one stage

* fix target dir

* Use deploy key

* noqa on test_openpilot

* Fix version.h path

* Cleanup release files

* Add linter scripts to release

* Update jenkinsfile

* Fix path

* this should work

* Use python3 docker container

* Run in correct directory

* Setup /data/pythonpath

Co-authored-by: commaci-public <60409688+commaci-public@users.noreply.github.com>
2020-01-30 13:06:45 -08:00
Drew Hintz f81a43381f add submodule commands for master (#1024) 2020-01-30 10:45:36 -08:00
Adeeb 44e97ead7b process replay: fix output after timeout added (#1020)
* process replay: fix output after timeout added

* better error
2020-01-29 09:44:14 -08:00
Comma Device 8507e683ae remove empty line in launch script 2020-01-28 17:12:51 -08:00
Comma Device 05cbd3d58c test commit from phone 2020-01-28 17:10:38 -08:00
Willem Melching 1de0b9c233 Fix version.py in case of no branch 2020-01-28 16:18:26 -08:00
Willem Melching b552a627e1 cleanup version.py 2020-01-28 15:55:24 -08:00
illumiN8i 277b187a29 Longer fingerprint for 2019 Camry XSE (#1002)
Adds additonal values to 2019 Camry XSE fingerprint to support discord user phantomarrowwolf's car
2020-01-28 10:55:18 -08:00
Andy Haden 6762447ae7 logging: imports for stack info 2020-01-28 08:35:25 -08:00
Andy Haden 41e4ad5d1d logging: Vendor findCaller for correct stack frame info in logs 2020-01-27 21:10:47 -08:00
Andy Haden e400aa0205 Remove unused logging_es 2020-01-27 19:59:52 -08:00
Willem Melching 393c4987c8 Fix junk data in initParams 2020-01-27 17:08:57 -08:00
Willem Melching 47492e77eb Subaru continuous blinker signals for assisted lane change (#1000) 2020-01-27 16:39:41 -08:00
Willem Melching fe9ccb27b1 Remember lane change direction when blinker turns off 2020-01-27 16:19:22 -08:00
Willem Melching 25f799bb8e Fix linter 2020-01-27 13:50:59 -08:00
Willem Melching 413a432b13 Speed up longitudinal tests and add retry 2020-01-27 12:35:43 -08:00
Willem Melching 2571453a54 Add script to nicely print logMessages 2020-01-27 11:47:41 -08:00
illumiN8i 0c06f15ea9 Fingerprint 2018 Highlander Limited Platinum (#1008)
from discord user Ryan R
2020-01-27 11:40:59 -08:00
Nelson Chen d658767236 Add Corolla Hatchback 2020 to README (#1006)
The Comma Two worked "out of the box" (*after OP install*, but you all know what I mean) for my Corolla Hatchback 2020 SE w/ BSM.
2020-01-25 18:46:35 -08:00
Adeeb 361be2630f process replay: add timeout to prevent hanging when tested process crashes (#1007) 2020-01-25 18:45:51 -08:00
illumiN8i 9a05283566 Restore Lexus ES 2019 to README.md (#1009)
non-hybrid
2020-01-25 18:45:23 -08:00
Harald Schafer a06bb77f6b use alpha 2 codes 2020-01-24 13:24:13 -08:00
Arne Schwarck 3c6ba22931 Add Country South Africa to list of RHD countries (#999) 2020-01-24 13:20:11 -08:00
robbederks 339e6986cf Bump panda (#997) 2020-01-23 18:51:11 -08:00
Willem Melching 0c1d70ac92 Changing alert text changed the test ref 2020-01-23 17:13:09 -08:00
illumiN8i 21af1bad2b RAV4 Hybrid TSS2 Support (#962)
* merge wocsor RAV4_HYBRID_TSS2 branch

From wocsor's PR #740

cleanup fingerprint

2020 rav4 hybrid confirmed working

* 2019 RAV4 Hybrid Limited

Adds additional messages for Limited trim. XSE and XLE still contained within this longer Limited fingerprint.

* Support Swiss 2019 RAV4 Hybrid XLE

Adds 913:8 value from discord user RDuke

* Separated RAV4 ICE and Hybrid

* Add route to test_car_models.py

* Revert "Toyota Corolla Hatchback Hybrid 2019 Excite (Israel)"

This reverts commit f1d6f68625.
2020-01-23 16:48:01 -08:00
Willem Melching b9b90267c5 Limit comma two fan speed to 30% when car is not running 2020-01-23 16:41:29 -08:00
Willem Melching ba4bf07db2 EON -> Device 2020-01-23 16:38:10 -08:00
Willem Melching 09a1691caf add default EON private key 2020-01-23 14:42:06 -08:00
Willem Melching 09283f4d6a Fix CI sync script 2020-01-23 13:06:05 -08:00
Willem Melching e2c784740a CI: remove header hacks when checking out submodules (#995) 2020-01-23 12:40:40 -08:00
Harald Schafer 9ff1a54c8d bump laika 2020-01-23 11:47:45 -08:00
Willem Melching 61c104b392 update test reference after calibrationd changes 2020-01-23 11:40:34 -08:00
Willem Melching fb8efbed2d always upload artifacts 2020-01-23 11:35:07 -08:00
ga 7f6c9ebaf7 [Memory leak]uniq_ptr for tmsg (#985)
* uniq_ptr for tmsg

* Added header
2020-01-23 11:25:54 -08:00
mosh-een 53933ebf5b Update gps_helpers.py (#994) 2020-01-23 11:24:42 -08:00
Harald Schafer 48b4a57980 needs to be array 2020-01-23 11:08:05 -08:00
Willem Melching 50e859e6b1 Add timeout to github actions and remove old azure-pipelines file 2020-01-23 11:02:37 -08:00
Harald Schafer ba6fd511b6 sanity clip every vp 2020-01-22 13:09:03 -08:00
Harald Schafer c32ebcb2c6 simplify and better 2020-01-21 16:37:22 -08:00
Harald Schafer bbb37309fc need lock too 2020-01-21 14:42:07 -08:00
Harald Schafer df6b16c7f6 tf 2.1 has issues 2020-01-21 14:28:49 -08:00
Willem Melching ec6e83ca2d Fix locationd_yawrate A matrix 2020-01-21 13:52:45 -08:00
ZwX1616 cf70368f67 replace numpy funs and shorten green prompt (#989) 2020-01-21 13:40:26 -08:00
Adeeb 28af44d199 GitHub Actions for CI (#986)
* basic github actions config

* submodules

* pwd

* docker save

* unit tests

* add rest of tests

* fix unit tests

* artifacts

* container name

* does this work?

* no --rm when we want artifacts

* clean up

* fix artifact paths

* name

* rerun travis

* remove travis config
2020-01-21 12:26:10 -08:00
Arne Schwarck 855abbd99e Subsock no longer exsists (#987)
* Subsock no longer exsists

use the new SubSocket as SubSock has been removed

* fix poller syntax

update to the new syntax and remove unused messaging library
2020-01-21 11:25:12 -08:00
Adeeb f13c5d74aa disable LDW when calibration is incomplete (#984) 2020-01-20 21:43:38 -08:00
Willem Melching 6f703eaf4e Installer: disable SSH and safer continue.sh write 2020-01-20 15:16:24 -08:00
illumiN8i 689d49f3f8 add 2017 Lexus RX 450h (#981)
fingerprint from discord user elihaddad
2020-01-20 13:38:40 -08:00
Adeeb 87f9e14e9c speed up car model test (#977) 2020-01-20 12:56:14 -08:00
Adeeb 7129dc8c6a add missing unit tests to travis config (#979) 2020-01-20 12:46:38 -08:00
Willem Melching 20745fe1d7 add bug report template 2020-01-20 12:15:55 -08:00
Willem Melching f6835e9490 Add cloudlog for is_on_wifi fail 2020-01-20 11:25:16 -08:00
Harald Schafer deef19ff19 bump adding camodom to qlog 2020-01-20 10:59:17 -08:00
Willem Melching 6adbe24d4c catch CalledProcessError in uploader wifi check 2020-01-20 10:57:08 -08:00
andyh2 79122ae662 apk lib: Grant offroad access to TelephonyManager 2020-01-19 18:05:31 -05:00
andyh2 8d7bb87e7e offroad apk @ openpilot-apks#530fcb19d 2020-01-19 16:39:57 -05:00
illumiN8i 5560ef0521 Add 2019XSECAMRY fingerprint (#971)
From discord user 2019XSECAMRY

add comment
2020-01-19 10:43:44 -08:00
Willem Melching 53d9975947 installer: add reset to make sure latest is checked out if branch doesnt change 2020-01-19 10:01:11 -08:00
Willem Melching d9e054477b Panda signature needs to be bytes 2020-01-19 09:40:37 -08:00
cfranhonda a93375882a add 2017 Civic Hatchback LX fp (#966) 2020-01-19 00:24:50 -08:00
Adeeb dafdb79db2 Clean up ECU redundancy in selfdrive/car/* (#963)
* clean up ecu redundancy in selfdrive/car

* clean up gear parsing
2020-01-18 23:35:10 -08:00
Comma Device 0fa10e4dd7 wait for NTP time sync before starting install 2020-01-19 05:25:15 +00:00
Bar Harel f1d6f68625 Toyota Corolla Hatchback Hybrid 2019 Excite (Israel) 2020-01-18 15:28:12 -08:00
Willem Melching 757c29e1ae bump panda 2020-01-18 13:28:12 -08:00
Comma Device 1989df2fac change install location in internal launch script 2020-01-18 20:54:46 +00:00
Willem Melching 65a40149d8 startup alert, handle case where param returns none 2020-01-17 21:30:14 -08:00
George Hotz adb5fe1fa9 Merge pull request #964 from commaai/branch-alert
Add orange alert if you are not running a devel or release branch
2020-01-17 17:52:14 -08:00
George Hotz c7bc8ee00f python 3 issue 2020-01-17 17:50:43 -08:00
George Hotz dcd0807d7e whitelist, don't blacklist 2020-01-17 17:46:17 -08:00
George Hotz 013166a34e add alert 2020-01-17 17:46:17 -08:00
George Hotz 6ceffe68d0 base SNPE path off home dir, still not really generic 2020-01-17 17:45:38 -08:00
George Hotz c26d893a77 allow non android to be identified differently 2020-01-17 17:38:31 -08:00
George Hotz a3dde4e7fe ui was gitignored 2020-01-17 17:34:23 -08:00
Harald Schafer e4472d0d46 deprecated 2020-01-17 16:24:40 -08:00
Willem Melching f3c1992b10 Merge pull request #960 from ZwX1616/patch-1
not to change awareness to npfloat64
2020-01-17 14:20:14 -08:00
ZwX1616 12f72fb8e2 Update test_monitoring.py 2020-01-17 14:15:08 -08:00
ZwX1616 2f19eed023 Update test_monitoring.py 2020-01-17 14:02:59 -08:00
Willem Melching dfded79673 Tools is now in openpilot, and use prebuilt capnp 2020-01-17 13:13:02 -08:00
ZwX1616 b013a00095 not to change awareness to np 2020-01-17 13:12:18 -08:00
Willem Melching 7f813d23ce cleanup and make linting part of ci work 2020-01-17 13:03:44 -08:00
George Hotz 59b3d06417 tools is local now 2020-01-17 12:51:19 -08:00
George Hotz b0260dadba selfdrive/controls 2020-01-17 12:48:30 -08:00
George Hotz fcf8efb826 selfdrive/locationd 2020-01-17 11:39:56 -08:00
Willem Melching 5f2a5e5bad use relative urls in submodules 2020-01-17 11:27:29 -08:00
George Hotz f467642a1c selfdrive/debug 2020-01-17 11:23:21 -08:00
George Hotz da079d47d7 logcatd, loggerd, mapd, modeld, proclogd 2020-01-17 11:20:17 -08:00
George Hotz 5c9afcc785 selfdrive/sensord 2020-01-17 11:17:58 -08:00
George Hotz c0bfbc12c7 selfdrive/test 2020-01-17 11:16:14 -08:00
George Hotz aeb2fff068 selfdrive/ui 2020-01-17 11:05:23 -08:00
George Hotz 2f9379a139 selfdrive/*.py 2020-01-17 11:03:42 -08:00
George Hotz 368a956b96 selfdrive/common 2020-01-17 11:01:02 -08:00
George Hotz 71ead9adea selfdrive/car 2020-01-17 10:58:43 -08:00
George Hotz 978e0eb986 selfdrive/clocksd 2020-01-17 10:57:08 -08:00
George Hotz 341c0da987 selfdrive/athenad 2020-01-17 10:54:24 -08:00
George Hotz 84560ccd55 selfdrive/camerad 2020-01-17 10:52:42 -08:00
George Hotz ef93a715e1 selfdrive/boardd 2020-01-17 10:51:01 -08:00
George Hotz 41d99c3b70 selfdrive/assets 2020-01-17 10:47:43 -08:00
George Hotz d4d57ed7fe bring over scripts 2020-01-17 10:41:41 -08:00
George Hotz d2a564b9c7 bring over installer 2020-01-17 10:39:50 -08:00
George Hotz da6863f427 bring over phonelibs minus frida-gum and qsml 2020-01-17 10:37:11 -08:00
George Hotz 6abffe0ede external folder 2020-01-17 10:33:21 -08:00
George Hotz 23c2d02682 latest models 2020-01-17 10:30:34 -08:00
George Hotz e8d888c45b common folder 2020-01-17 10:28:44 -08:00
George Hotz c4e5ce685e latest built apks 2020-01-17 10:26:31 -08:00
George Hotz c8a04d25c9 root directory hidden files 2020-01-17 10:25:50 -08:00
George Hotz 012535a84e root directory non hidden files 2020-01-17 10:22:00 -08:00
George Hotz 3fe9bbe665 merge in pyextra 2020-01-17 10:08:05 -08:00
George Hotz 29ac3da7b8 merge in tools 2020-01-17 10:07:22 -08:00
George Hotz 80d6953862 submodules 2020-01-17 10:05:09 -08:00
George Hotz 6c33a5c1f3 root commit 2020-01-17 10:02:52 -08:00
2958 changed files with 707905 additions and 193916 deletions
+3
View File
@@ -0,0 +1,3 @@
((c++-mode (flycheck-gcc-language-standard . "c++11")
(flycheck-clang-language-standard . "c++11")
))
+24
View File
@@ -0,0 +1,24 @@
.git
.DS_Store
notebooks
phone
massivemap
neos
installer
chffr/app2
chffr/backend/env
selfdrive/nav
selfdrive/baseui
chffr/lib/vidindex/vidindex
chffr/lib/index_log/index_log
selfdrive/test/simulator2
**/cache_data
xx/chffr/lib/index_log/index_log
xx/chffr/lib/vidindex/vidindex
xx/plus
xx/community
xx/projects
!xx/projects/eon_testing_master
!xx/projects/map3d
xx/ops
xx/junk
+11
View File
@@ -0,0 +1,11 @@
root = true
[*]
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
[{*.py, *.pyx, *pxd}]
charset = utf-8
indent_style = space
indent_size = 2
+27
View File
@@ -0,0 +1,27 @@
*.keras filter=lfs diff=lfs merge=lfs -text
*.dlc filter=lfs diff=lfs merge=lfs -text
*.pb filter=lfs diff=lfs merge=lfs -text
*.bin filter=lfs diff=lfs merge=lfs -text
*.apk filter=lfs diff=lfs merge=lfs -text
*.ipynb filter=nbstripout -diff
external/ffmpeg/bin/ffmpeg_cuda filter=lfs diff=lfs merge=lfs -text
models/segnet.keras filter=lfs diff=lfs merge=lfs -text
external/zmq/lib/libzmq.a filter=lfs diff=lfs merge=lfs -text
external/zmq/lib/libczmq.a filter=lfs diff=lfs merge=lfs -text
phonelibs/acado/x64/lib/libacado_toolkit.a filter=lfs diff=lfs merge=lfs -text
phonelibs/acado/x64/lib/libacado_toolkit_s.so.1.2.2beta filter=lfs diff=lfs merge=lfs -text
phonelibs/acado/x64/lib/libacado_casadi.a filter=lfs diff=lfs merge=lfs -text
phonelibs/acado/x64/lib/libacado_csparse.a filter=lfs diff=lfs merge=lfs -text
phonelibs/acado/x64/lib/libacado_qpoases.a filter=lfs diff=lfs merge=lfs -text
phonelibs/acado/aarch64/lib/libacado_toolkit.a filter=lfs diff=lfs merge=lfs -text
phonelibs/acado/aarch64/lib/libacado_toolkit_s.so.1.2.2beta filter=lfs diff=lfs merge=lfs -text
phonelibs/acado/aarch64/lib/libacado_casadi.a filter=lfs diff=lfs merge=lfs -text
phonelibs/acado/aarch64/lib/libacado_csparse.a filter=lfs diff=lfs merge=lfs -text
phonelibs/acado/aarch64/lib/libacado_qpoases.a filter=lfs diff=lfs merge=lfs -text
phonelibs/fastcv/aarch64/libfastcvopt.so filter=lfs diff=lfs merge=lfs -text
phonelibs/fastcv/aarch64/libfastcvadsp_stub.so filter=lfs diff=lfs merge=lfs -text
external/mac/MP4Box filter=lfs diff=lfs merge=lfs -text
models/segnet2.keras filter=lfs diff=lfs merge=lfs -text
external/opencl/*.deb filter=lfs diff=lfs merge=lfs -text
phonelibs/zmq/aarch64-linux/lib/libzmq.a filter=lfs diff=lfs merge=lfs -text
external/azcopy/azcopy filter=lfs diff=lfs merge=lfs -text
+1 -1
View File
@@ -2,7 +2,7 @@
name: Bug report
about: Create a report to help us improve openpilot
title: ''
labels: 'bug'
labels: ''
assignees: ''
---
-21
View File
@@ -1,21 +0,0 @@
Choose one of the templates below:
# Fingerprint
This pull requests adds a fingerprint for <Make - Model - Year - Trim>.
This is an explorer link to a drive with the stock system enabled: ...
# Car support
This pull requests adds support for <Make - Model - Year - Trim>.
This is an explorer link to a drive with the stock system enabled: ...
This is an explorer link to a drive with openpilot system enabled: ...
# Feature
This pull requests adds feature X
## Description
Explain what the feature does
## Testing
Explain how the feature was tested. Either by the added unit tests, or what tests were performed while driving.
+125
View File
@@ -0,0 +1,125 @@
name: Openpilot Tests
on: [push, pull_request]
env:
RUN: docker run --shm-size 1G --rm tmppilot /bin/sh -c
LOAD: docker load -i tmppilot.tar.gz/tmppilot.tar.gz
UNIT_TEST: cd /tmp/openpilot && python -m unittest discover
jobs:
build:
name: build
runs-on: ubuntu-16.04
steps:
- uses: actions/checkout@v2
- name: Checkout submodules
run: |
git submodule update --init
- run: |
docker pull $(grep -ioP '(?<=^from)\s+\S+' Dockerfile.openpilot) || true
docker pull docker.io/commaai/openpilot:latest || true
docker build --cache-from docker.io/commaai/openpilot:latest -t tmppilot -f Dockerfile.openpilot .
docker save tmppilot:latest | gzip > tmppilot.tar.gz
- uses: actions/upload-artifact@v1
with:
name: tmppilot.tar.gz
path: tmppilot.tar.gz
linter:
name: linter
runs-on: ubuntu-16.04
needs: build
steps:
- uses: actions/download-artifact@v1
with:
name: tmppilot.tar.gz
- name: Load image
run: $LOAD
- name: flake8
run: $RUN "cd /tmp/openpilot/ && ./flake8_openpilot.sh"
- name: pylint
run: $RUN "cd /tmp/openpilot/ && ./pylint_openpilot.sh"
unit_tests:
name: unit tests
runs-on: ubuntu-16.04
needs: build
steps:
- uses: actions/download-artifact@v1
with:
name: tmppilot.tar.gz
- name: Load image
run: $LOAD
- name: Run unit tests
run: |
$RUN cd /tmp/openpilot/selfdrive/test/test_fingerprints.py
$RUN "$UNIT_TEST common"
$RUN "$UNIT_TEST opendbc/can"
$RUN "$UNIT_TEST selfdrive/boardd"
$RUN "$UNIT_TEST selfdrive/controls"
$RUN "$UNIT_TEST selfdrive/loggerd"
$RUN "$UNIT_TEST selfdrive/car"
$RUN "$UNIT_TEST selfdrive/locationd"
$RUN "$UNIT_TEST selfdrive/athena"
process_replay:
name: process replay
runs-on: ubuntu-16.04
needs: build
timeout-minutes: 30
steps:
- uses: actions/download-artifact@v1
with:
name: tmppilot.tar.gz
- name: Load image
run: $LOAD
- name: Run replay
run: |
CONTAINER_NAME="tmppilot_${GITHUB_SHA}"
docker run --shm-size 1G --name ${CONTAINER_NAME} tmppilot /bin/sh -c "cd /tmp/openpilot/selfdrive/test/process_replay && CI=1 ./test_processes.py"
docker cp $CONTAINER_NAME:/tmp/openpilot/selfdrive/test/process_replay/diff.txt diff.txt
docker rm $CONTAINER_NAME
- uses: actions/upload-artifact@v1
if: always()
with:
name: process_replay_diff.txt
path: diff.txt
test_longitudinal:
name: longitudinal
runs-on: ubuntu-16.04
needs: build
timeout-minutes: 30
steps:
- uses: actions/download-artifact@v1
with:
name: tmppilot.tar.gz
- name: Load image
run: $LOAD
- name: Test longitudinal
run: |
CONTAINER_NAME="tmppilot_${GITHUB_SHA}"
docker run --shm-size 1G --name ${CONTAINER_NAME} tmppilot /bin/sh -c "cd /tmp/openpilot/selfdrive/test/longitudinal_maneuvers && OPTEST=1 ./test_longitudinal.py"
mkdir out
docker cp $CONTAINER_NAME:/tmp/openpilot/selfdrive/test/longitudinal_maneuvers/out/longitudinal/ out/
docker rm $CONTAINER_NAME
- uses: actions/upload-artifact@v1
if: always()
with:
name: longitudinal
path: out
test_car_models:
name: test car models
runs-on: ubuntu-16.04
needs: build
timeout-minutes: 30
steps:
- uses: actions/download-artifact@v1
with:
name: tmppilot.tar.gz
- name: Load image
run: $LOAD
- name: Test car models
run: $RUN "mkdir -p /data/params && cd /tmp/openpilot/selfdrive/test/ && ./test_car_models.py"
+6 -1
View File
@@ -3,11 +3,14 @@ venv/
.tags
.ipynb_checkpoints
.idea
.overlay_init
.overlay_consistent
.sconsign.dblite
.vscode
model2.png
a.out
*.dylib
*.DSYM
*.d
*.pyc
@@ -27,6 +30,7 @@ a.out
config.json
clcache
persist
board/obj/
selfdrive/boardd/boardd
selfdrive/logcatd/logcatd
@@ -40,7 +44,7 @@ selfdrive/sensord/_gpsd
selfdrive/sensord/_sensord
selfdrive/camerad/camerad
selfdrive/modeld/_modeld
selfdrive/modeld/_monitoringd
selfdrive/modeld/_dmonitoringmodeld
/src/
one
@@ -51,4 +55,5 @@ panda_jungle
.coverage*
htmlcov
pandaextra
+16
View File
@@ -0,0 +1,16 @@
[submodule "panda"]
path = panda
url = ../../commaai/panda.git
[submodule "opendbc"]
path = opendbc
url = ../../commaai/opendbc.git
[submodule "laika_repo"]
path = laika_repo
url = ../../commaai/laika.git
[submodule "apks"]
path = apks
url = ../../commaai/openpilot-apks.git
[submodule "cereal"]
path = cereal
url = ../../commaai/cereal.git
-7
View File
@@ -1,7 +0,0 @@
sudo: required
services:
- docker
script:
- ./run_docker_tests.sh
+9
View File
@@ -29,3 +29,12 @@ Code is automatically check for style by travis as part of the automated tests.
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)
## Pull Requests
Pull requests should be against the master branch. Before running master on in-car hardware, you'll need to run
```
git submodule init
git submodule update
```
in order to pull down the submodules, such as `panda` and `opendbc`.
+12 -6
View File
@@ -17,12 +17,14 @@ RUN apt-get update && apt-get install -y \
libffi-dev \
libglew-dev \
libgles2-mesa-dev \
libglfw3-dev \
libglib2.0-0 \
liblzma-dev \
libmysqlclient-dev \
libomp-dev \
libopencv-dev \
libssl-dev \
libsqlite3-dev \
libtool \
libusb-1.0-0-dev \
libzmq5-dev \
@@ -61,13 +63,15 @@ RUN cd /tmp && pipenv install --system --deploy
# Install subset of dev dependencies needed for CI
RUN pip install matplotlib==3.1.1 dictdiffer==0.8.0 fastcluster==1.1.25 aenum==2.2.1 scipy==1.3.1 lru-dict==1.1.6 tenacity==5.1.1 azure-common==1.1.23 azure-nspkg==3.0.2 azure-storage-blob==2.1.0 azure-storage-common==2.1.0 azure-storage-nspkg==3.1.0 pycurl==7.43.0.3
COPY phonelibs/install_capnp.sh /tmp/install_capnp.sh
RUN /tmp/install_capnp.sh
RUN git clone --branch v0.7 https://github.com/commaai/openpilot-tools.git /tmp/openpilot/tools
ENV PATH="/tmp/openpilot/external/bin:${PATH}"
ENV PYTHONPATH /tmp/openpilot:${PYTHONPATH}
COPY ./.pylintrc /tmp/openpilot/.pylintrc
RUN mkdir -p /tmp/openpilot
COPY ./flake8_openpilot.sh /tmp/openpilot/
COPY ./pylint_openpilot.sh /tmp/openpilot/
COPY ./.pylintrc /tmp/openpilot/
COPY ./release /tmp/openpilot/release
COPY ./common /tmp/openpilot/common
COPY ./cereal /tmp/openpilot/cereal
COPY ./opendbc /tmp/openpilot/opendbc
@@ -75,6 +79,8 @@ COPY ./selfdrive /tmp/openpilot/selfdrive
COPY ./phonelibs /tmp/openpilot/phonelibs
COPY ./pyextra /tmp/openpilot/pyextra
COPY ./panda /tmp/openpilot/panda
COPY ./external /tmp/openpilot/external
COPY ./tools /tmp/openpilot/tools
COPY SConstruct /tmp/openpilot/SConstruct
Vendored
+23
View File
@@ -0,0 +1,23 @@
pipeline {
agent {
docker {
image 'python:3.7.3'
args '--user=root'
}
}
stages {
stage('EON Build/Test') {
steps {
lock(resource: "", label: 'eon', inversePrecedence: true, variable: 'eon_name', quantity: 1){
timeout(time: 30, unit: 'MINUTES') {
dir(path: 'release') {
sh 'pip install paramiko'
sh 'python remote_build.py'
}
}
}
}
}
}
}
-9
View File
@@ -1,9 +0,0 @@
code_dir := $(shell pwd)
# TODO: Add a global build system
.PHONY: all
all:
cd selfdrive && PYTHONPATH=$(code_dir) PREPAREONLY=1 ./manager.py
+6 -25
View File
@@ -8,71 +8,54 @@ opencv-python= "==3.4.2.17"
PyQt5 = "*"
ipython = "*"
networkx = "==2.3"
azure-common = "==1.1.23"
azure-core = "==1.1.1"
azure-common = "==1.1.24"
azure-nspkg = "==3.0.2"
azure-storage-blob = "==2.1.0"
azure-storage-common = "==2.1.0"
azure-storage-nspkg = "==3.1.0"
bincopy = "*"
bleach = "*"
boto = "*"
"boto3" = "*"
celery = "*"
control = "*"
datadog = "*"
decorator = "*"
dlib = "*"
dominate = "*"
elasticsearch = "*"
fasteners = "*"
future = "*"
futures = "*"
gevent = "*"
pycocotools = {git = "https://github.com/cocodataset/cocoapi.git",subdirectory = "PythonAPI"}
gunicorn = "*"
"h5py" = "*"
hexdump = "*"
"html5lib" = "*"
imageio = "*"
intervaltree = "*"
ipykernel = "*"
joblib = "*"
json-logging-py = "*"
jupyter = "*"
libarchive = "*"
lru-dict = "*"
lxml = "*"
"mpld3" = "*"
msgpack-python = "*"
nbstripout = "*"
nose-parameterized = "*"
numpy = "*"
osmium = "*"
pbr = "*"
percache = "*"
pprofile = "*"
psutil = "*"
pycurl = "*"
git-pylint-commit-hook = "*"
pymongo = "*"
"pynmea2" = "*"
pypolyline = "*"
pysendfile = "*"
python-logstash = "*"
pyvcd = "*"
redis = "*"
redlock = "*"
"s2sphere" = "*"
scikit-image = "*"
"subprocess32" = "*"
supervisor = "*"
tenacity = "*"
tensorflow-gpu = ""
utm = "*"
"v4l2" = "*"
tensorflow-gpu = "==2.0"
PyJWT = "==1.4.1"
PyMySQL = "==0.9.2"
Theano = "*"
Werkzeug = "*"
"backports.lzma" = "*"
Flask-Cors = "*"
@@ -84,7 +67,6 @@ PyNaCl = "*"
reverse_geocoder = "*"
Shapely = "*"
SQLAlchemy = "*"
uWSGI = "*"
scipy = "*"
fastcluster = "*"
backports-abc = "*"
@@ -92,15 +74,15 @@ pygame = "*"
simplejson = "*"
python-logstash-async = "*"
seaborn = "*"
tensorflow-estimator = "*"
pyproj = "*"
mock = "*"
blinker = "*"
gast = "==0.2.2"
matplotlib = "*"
dictdiffer = "*"
aenum = "*"
coverage = "*"
azure-cli-core = "*"
paramiko = "*"
aiohttp = "*"
[packages]
overpy = {git = "https://github.com/commaai/python-overpy.git",ref = "f86529af402d4642e1faeb146671c40284007323"}
@@ -144,6 +126,5 @@ pillow = "*"
scons = "*"
cysignals = "*"
[requires]
python_version = "3.7.3"
Generated
+814 -805
View File
File diff suppressed because it is too large Load Diff
+15 -9
View File
@@ -1,4 +1,4 @@
[![](https://i.imgur.com/UetIFyH.jpg)](#)
[![](https://i.imgur.com/UelUjKAh.png)](#)
Table of Contents
=======================
@@ -57,7 +57,7 @@ openpilot should preserve all other vehicle's stock features, including, but are
Supported Hardware
------
At the moment, openpilot supports the [EON DevKit](https://comma.ai/shop/products/eon-dashcam-devkit). A [car harness](https://comma.ai/shop/products/car-harness) is recommended to connect the EON to the car. In the future, we'd like to support other platforms as well.
At the moment, openpilot supports the [EON DevKit](https://comma.ai/shop/products/eon-dashcam-devkit) and the [comma two](https://comma.ai/shop/products/comma-two-devkit). A [car harness](https://comma.ai/shop/products/car-harness) is recommended to connect the EON or comma two to the car. In the future, we'd like to support other platforms as well, like gaming PCs.
Supported Cars
------
@@ -68,12 +68,12 @@ Supported Cars
| Acura | RDX 2016-18 | AcuraWatch Plus | openpilot | 25mph<sup>5</sup> | 12mph |
| Chrysler | Pacifica 2017-18 | Adaptive Cruise | Stock | 0mph | 9mph |
| Chrysler | Pacifica Hybrid 2017-18 | Adaptive Cruise | Stock | 0mph | 9mph |
| Chrysler | Pacifica Hybrid 2019 | Adaptive Cruise | Stock | 0mph | 39mph |
| Chrysler | Pacifica Hybrid 2019-20 | Adaptive Cruise | Stock | 0mph | 39mph |
| Honda | Accord 2018-19 | All | Stock | 0mph | 3mph |
| Honda | Accord Hybrid 2018-19 | 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 | Honda Sensing | Stock | 0mph | 2mph |
| Honda | Civic Hatchback 2017-19 | Honda Sensing | Stock | 0mph | 12mph |
| Honda | CR-V 2015-16 | Touring | openpilot | 25mph<sup>5</sup> | 12mph |
| Honda | CR-V 2017-19 | Honda Sensing | Stock | 0mph | 12mph |
| Honda | CR-V Hybrid 2017-2019 | Honda Sensing | Stock | 0mph | 12mph |
@@ -83,19 +83,22 @@ Supported Cars
| Honda | Pilot 2016-18 | Honda Sensing | openpilot | 25mph<sup>5</sup> | 12mph |
| Honda | Pilot 2019 | All | openpilot | 25mph<sup>5</sup> | 12mph |
| Honda | Ridgeline 2017-19 | Honda Sensing | openpilot | 25mph<sup>5</sup> | 12mph |
| Hyundai | Santa Fe 2019<sup>1</sup> | All | Stock | 0mph | 0mph |
| Hyundai | Elantra 2017-19<sup>1</sup> | SCC + LKAS | Stock | 19mph | 34mph |
| Hyundai | Genesis 2018<sup>1</sup> | All | Stock | 19mph | 34mph |
| Hyundai | Santa Fe 2019<sup>1</sup> | All | Stock | 0mph | 0mph |
| Jeep | Grand Cherokee 2016-18 | Adaptive Cruise | Stock | 0mph | 9mph |
| Jeep | Grand Cherokee 2019 | Adaptive Cruise | Stock | 0mph | 39mph |
| Kia | Optima 2019<sup>1</sup> | SCC + LKAS | Stock | 0mph | 0mph |
| Kia | Sorento 2018<sup>1</sup> | All | Stock | 0mph | 0mph |
| Kia | Stinger 2018<sup>1</sup> | SCC + LKAS | Stock | 0mph | 0mph |
| Lexus | CT Hybrid 2017-18 | All | Stock<sup>4</sup>| 0mph | 0mph |
| Lexus | ES 2019 | All | openpilot | 0mph | 0mph |
| Lexus | ES Hybrid 2019 | All | openpilot | 0mph | 0mph |
| Lexus | RX Hybrid 2016-19 | All | Stock<sup>4</sup>| 0mph | 0mph |
| Lexus | IS 2017-2019 | All | Stock | 22mph | 0mph |
| Lexus | IS Hybrid 2017 | All | Stock | 0mph | 0mph |
| Lexus | RX 2016-17 | All | Stock<sup>4</sup>| 0mph | 0mph |
| Lexus | RX 2020 | All | openpilot | 0mph | 0mph |
| Lexus | RX Hybrid 2016-19 | All | Stock<sup>4</sup>| 0mph | 0mph |
| Subaru | Crosstrek 2018-19 | EyeSight | Stock | 0mph | 0mph |
| Subaru | Impreza 2019-20 | EyeSight | Stock | 0mph | 0mph |
| Toyota | Avalon 2016 | TSS-P | Stock<sup>4</sup>| 20mph<sup>5</sup> | 0mph |
@@ -106,7 +109,7 @@ Supported Cars
| Toyota | C-HR Hybrid 2017-19 | All | Stock | 0mph | 0mph |
| Toyota | Corolla 2017-19 | All | Stock<sup>4</sup>| 20mph<sup>5</sup> | 0mph |
| Toyota | Corolla 2020 | All | openpilot | 0mph | 0mph |
| Toyota | Corolla Hatchback 2019 | All | openpilot | 0mph | 0mph |
| Toyota | Corolla Hatchback 2019-20 | All | openpilot | 0mph | 0mph |
| Toyota | Corolla Hybrid 2020 | All | openpilot | 0mph | 0mph |
| Toyota | Highlander 2017-19 | All | Stock<sup>4</sup>| 0mph | 0mph |
| Toyota | Highlander Hybrid 2017-19 | All | Stock<sup>4</sup>| 0mph | 0mph |
@@ -118,6 +121,7 @@ Supported Cars
| Toyota | Rav4 2019 | All | openpilot | 0mph | 0mph |
| Toyota | Rav4 Hybrid 2016 | TSS-P | Stock<sup>4</sup>| 0mph | 0mph |
| Toyota | Rav4 Hybrid 2017-18 | All | Stock<sup>4</sup>| 0mph | 0mph |
| Toyota | Rav4 Hybrid 2019-20 | All | openpilot | 0mph | 0mph |
| Toyota | Sienna 2018 | All | Stock<sup>4</sup>| 0mph | 0mph |
| Volkswagen| Golf 2016-19<sup>3</sup> | Driver Assistance | Stock | 0mph | 0mph |
@@ -131,9 +135,9 @@ Community Maintained Cars and Features
| Make | Model (US Market Reference) | Supported Package | ACC | No ACC accel below | No ALC below |
| ----------| ------------------------------| ------------------| -----------------| -------------------| -------------|
| Buick | Regal 2018<sup>6</sup> | Adaptive Cruise | openpilot | 0mph | 7mph |
| Cadillac | ATS 2018<sup>6</sup> | Adaptive Cruise | openpilot | 0mph | 7mph |
| Chevrolet | Malibu 2017<sup>6</sup> | Adaptive Cruise | openpilot | 0mph | 7mph |
| Chevrolet | Volt 2017-18<sup>6</sup> | Adaptive Cruise | openpilot | 0mph | 7mph |
| Cadillac | ATS 2018<sup>6</sup> | Adaptive Cruise | openpilot | 0mph | 7mph |
| GMC | Acadia Denali 2018<sup>6</sup>| Adaptive Cruise | openpilot | 0mph | 7mph |
| Holden | Astra 2017<sup>6</sup> | Adaptive Cruise | openpilot | 0mph | 7mph |
@@ -159,6 +163,8 @@ Limitations of openpilot ALC and LDW
openpilot ALC and openpilot LDW do not automatically drive the vehicle or reduce the amount of attention that must be paid to operate your vehicle. The driver must always keep control of the steering wheel and be ready to correct the openpilot ALC action at all times.
While changing lanes, openpilot is not capable of looking next to you or checking your blind spot. Only nudge the wheel to initiate a lane change after you have confirmed it's safe to do so.
Many factors can impact the performance of openpilot ALC and openpilot LDW, causing them to be unable to function as intended. These include, but are not limited to:
* Poor visibility (heavy rain, snow, fog, etc.) or weather conditions that may interfere with sensor operation.
@@ -240,7 +246,7 @@ Safety and Testing
Testing on PC
------
Check out [openpilot-tools](https://github.com/commaai/openpilot-tools): 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
------
+18
View File
@@ -1,3 +1,21 @@
Version 0.7.2 (2020-02-07)
========================
* ECU firmware version based fingerprinting for Honda & Toyota
* New driving model: improved path prediction during turns and lane changes and better lead speed tracking
* Improve driver monitoring under extreme lighting and add low accuracy alert
* Support for 2019 Rav4 Hybrid thanks to illumiN8i!
* Support for 2016, 2017 and 2020 Lexus RX thanks to illumiN8i!
* Support for 2020 Chrysler Pacifica Hybrid thanks to adhintz!
Version 0.7.1 (2020-01-20)
========================
* comma two support!
* Lane Change Assist above 45 mph!
* Replace zmq with custom messaging library, msgq!
* Supercombo model: calibration and driving models are combined for better lead estimate
* More robust updater thanks to jyoung8607! Requires NEOS update
* Improve low speed ACC tuning
Version 0.7 (2019-12-13)
========================
* Move to SCons build system!
+39 -19
View File
@@ -1,6 +1,7 @@
import os
import subprocess
import sys
import platform
AddOption('--test',
action='store_true',
@@ -11,6 +12,8 @@ AddOption('--asan',
help='turn on ASAN')
arch = subprocess.check_output(["uname", "-m"], encoding='utf8').rstrip()
if platform.system() == "Darwin":
arch = "Darwin"
if arch == "aarch64":
lenv = {
@@ -29,7 +32,6 @@ if arch == "aarch64":
"/data/data/com.termux/files/usr/lib",
"/system/vendor/lib64",
"/system/comma/usr/lib",
"#phonelibs/yaml-cpp/lib",
"#phonelibs/nanovg",
"#phonelibs/libyuv/lib",
]
@@ -46,22 +48,37 @@ else:
"#phonelibs/capnp-cpp/include",
"#phonelibs/capnp-c/include",
"#phonelibs/zmq/x64/include",
]
libpath = [
"#phonelibs/capnp-cpp/x64/lib",
"#phonelibs/capnp-c/x64/lib",
"#phonelibs/yaml-cpp/x64/lib",
"#phonelibs/snpe/x86_64-linux-clang",
"#phonelibs/zmq/x64/lib",
"#phonelibs/libyuv/x64/lib",
"#external/zmq/lib",
"#cereal",
"#selfdrive/common",
"/usr/lib",
"/usr/local/lib",
"#external/tensorflow/include",
]
if arch == "Darwin":
libpath = [
"#phonelibs/capnp-cpp/mac/lib",
"#phonelibs/capnp-c/mac/lib",
"#phonelibs/libyuv/mac/lib",
"#cereal",
"#selfdrive/common",
"/usr/local/lib",
"/System/Library/Frameworks/OpenGL.framework/Libraries",
]
else:
libpath = [
"#phonelibs/capnp-cpp/x64/lib",
"#phonelibs/capnp-c/x64/lib",
"#phonelibs/snpe/x86_64-linux-clang",
"#phonelibs/zmq/x64/lib",
"#phonelibs/libyuv/x64/lib",
"#external/zmq/lib",
"#external/tensorflow/lib",
"#cereal",
"#selfdrive/common",
"/usr/lib",
"/usr/local/lib",
]
rpath = ["phonelibs/capnp-cpp/x64/lib",
"phonelibs/zmq/x64/lib",
"external/tensorflow/lib",
"cereal",
"selfdrive/common"]
@@ -95,7 +112,6 @@ env = Environment(
"#selfdrive",
"#phonelibs/bzip2",
"#phonelibs/libyuv/include",
"#phonelibs/yaml-cpp/include",
"#phonelibs/openmax/include",
"#phonelibs/json/src",
"#phonelibs/json11",
@@ -192,8 +208,10 @@ SConscript(['common/SConscript'])
SConscript(['common/kalman/SConscript'])
SConscript(['phonelibs/SConscript'])
SConscript(['selfdrive/modeld/SConscript'])
SConscript(['selfdrive/camerad/SConscript'])
if arch != "Darwin":
SConscript(['selfdrive/camerad/SConscript'])
SConscript(['selfdrive/modeld/SConscript'])
SConscript(['selfdrive/controls/lib/cluster/SConscript'])
SConscript(['selfdrive/controls/lib/lateral_mpc/SConscript'])
SConscript(['selfdrive/controls/lib/longitudinal_mpc/SConscript'])
@@ -201,11 +219,13 @@ SConscript(['selfdrive/controls/lib/longitudinal_mpc/SConscript'])
SConscript(['selfdrive/boardd/SConscript'])
SConscript(['selfdrive/proclogd/SConscript'])
SConscript(['selfdrive/ui/SConscript'])
SConscript(['selfdrive/loggerd/SConscript'])
if arch == "aarch64":
SConscript(['selfdrive/logcatd/SConscript'])
SConscript(['selfdrive/ui/SConscript'])
SConscript(['selfdrive/sensord/SConscript'])
SConscript(['selfdrive/loggerd/SConscript'])
SConscript(['selfdrive/clocksd/SConscript'])
SConscript(['selfdrive/locationd/SConscript'])
+3
View File
@@ -0,0 +1,3 @@
Place apks here that you want to be installed with the name <app>.apk
These won't be committed into one, but for now baseui will ship with openpilot.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Submodule
+1
Submodule apks added at d4f4f07a15
Submodule
+1
Submodule cereal added at ab32956aaf
-1
View File
@@ -1 +0,0 @@
.sconsign.dblite
-14
View File
@@ -1,14 +0,0 @@
gen
node_modules
package-lock.json
*.pyc
__pycache__
.*.swp
.*.swo
libcereal*.a
libmessaging.*
libmessaging_shared.*
services.h
.sconsign.dblite
libcereal_shared.so
-19
View File
@@ -1,19 +0,0 @@
from ubuntu:16.04
RUN apt-get update && apt-get install -y libzmq3-dev clang wget git autoconf libtool curl make build-essential libssl-dev zlib1g-dev libbz2-dev libreadline-dev libsqlite3-dev llvm libncurses5-dev libncursesw5-dev xz-utils tk-dev libffi-dev liblzma-dev python-openssl
RUN curl -L https://github.com/pyenv/pyenv-installer/raw/master/bin/pyenv-installer | bash
ENV PATH="/root/.pyenv/bin:/root/.pyenv/shims:${PATH}"
RUN pyenv install 3.7.3
RUN pyenv global 3.7.3
RUN pyenv rehash
RUN pip3 install pyyaml==5.1.2 Cython==0.29.14 scons==3.1.1 pycapnp==0.6.4
WORKDIR /project/cereal
COPY install_capnp.sh .
RUN ./install_capnp.sh
ENV PYTHONPATH=/project
COPY . .
RUN scons -c && scons -j$(nproc)
-68
View File
@@ -1,68 +0,0 @@
Import('env', 'arch', 'zmq')
gen_dir = Dir('gen')
messaging_dir = Dir('messaging')
# TODO: remove src-prefix and cereal from command string. can we set working directory?
env.Command(["gen/c/include/c++.capnp.h", "gen/c/include/java.capnp.h"], [], "mkdir -p " + gen_dir.path + "/c/include && touch $TARGETS")
env.Command(
['gen/c/car.capnp.c', 'gen/c/log.capnp.c', 'gen/c/car.capnp.h', 'gen/c/log.capnp.h'],
['car.capnp', 'log.capnp'],
'capnpc $SOURCES --src-prefix=cereal -o c:' + gen_dir.path + '/c/')
env.Command(
['gen/cpp/car.capnp.c++', 'gen/cpp/log.capnp.c++', 'gen/cpp/car.capnp.h', 'gen/cpp/log.capnp.h'],
['car.capnp', 'log.capnp'],
'capnpc $SOURCES --src-prefix=cereal -o c++:' + gen_dir.path + '/cpp/')
import shutil
if shutil.which('capnpc-java'):
env.Command(
['gen/java/Car.java', 'gen/java/Log.java'],
['car.capnp', 'log.capnp'],
'capnpc $SOURCES --src-prefix=cereal -o java:' + gen_dir.path + '/java/')
# TODO: remove non shared cereal and messaging
cereal_objects = env.SharedObject([
'gen/c/car.capnp.c',
'gen/c/log.capnp.c',
'gen/cpp/car.capnp.c++',
'gen/cpp/log.capnp.c++',
])
env.Library('cereal', cereal_objects)
env.SharedLibrary('cereal_shared', cereal_objects)
cereal_dir = Dir('.')
services_h = env.Command(
['services.h'],
['service_list.yaml', 'services.py'],
'python3 ' + cereal_dir.path + '/services.py > $TARGET')
messaging_objects = env.SharedObject([
'messaging/messaging.cc',
'messaging/impl_zmq.cc',
'messaging/impl_msgq.cc',
'messaging/msgq.cc',
])
messaging_lib = env.Library('messaging', messaging_objects)
Depends('messaging/impl_zmq.cc', services_h)
# note, this rebuilds the deps shared, zmq is statically linked to make APK happy
# TODO: get APK to load system zmq to remove the static link
shared_lib_shared_lib = [zmq, 'm', 'stdc++'] + ["gnustl_shared"] if arch == "aarch64" else []
env.SharedLibrary('messaging_shared', messaging_objects, LIBS=shared_lib_shared_lib)
env.Program('messaging/bridge', ['messaging/bridge.cc'], LIBS=[messaging_lib, 'zmq'])
Depends('messaging/bridge.cc', services_h)
# different target?
#env.Program('messaging/demo', ['messaging/demo.cc'], LIBS=[messaging_lib, 'zmq'])
env.Command(['messaging/messaging_pyx.so'],
[messaging_lib, 'messaging/messaging_pyx_setup.py', 'messaging/messaging_pyx.pyx', 'messaging/messaging.pxd'],
"cd " + messaging_dir.path + " && python3 messaging_pyx_setup.py build_ext --inplace")
if GetOption('test'):
env.Program('messaging/test_runner', ['messaging/test_runner.cc', 'messaging/msgq_tests.cc'], LIBS=[messaging_lib])
-49
View File
@@ -1,49 +0,0 @@
import os
import subprocess
zmq = 'zmq'
arch = subprocess.check_output(["uname", "-m"], encoding='utf8').rstrip()
cereal_dir = Dir('.')
cpppath = [
cereal_dir,
'/usr/lib/include',
]
AddOption('--test',
action='store_true',
help='build test files')
AddOption('--asan',
action='store_true',
help='turn on ASAN')
ccflags_asan = ["-fsanitize=address", "-fno-omit-frame-pointer"] if GetOption('asan') else []
ldflags_asan = ["-fsanitize=address"] if GetOption('asan') else []
env = Environment(
ENV=os.environ,
CC='clang',
CXX='clang++',
CCFLAGS=[
"-g",
"-fPIC",
"-O2",
"-Werror=implicit-function-declaration",
"-Werror=incompatible-pointer-types",
"-Werror=int-conversion",
"-Werror=return-type",
"-Werror=format-extra-args",
] + ccflags_asan,
LDFLAGS=ldflags_asan,
LINKFLAGS=ldflags_asan,
CFLAGS="-std=gnu11",
CXXFLAGS="-std=c++14",
CPPPATH=cpppath,
)
Export('env', 'zmq', 'arch')
SConscript(['SConscript'])
-8
View File
@@ -1,8 +0,0 @@
import os
import capnp
CEREAL_PATH = os.path.dirname(os.path.abspath(__file__))
capnp.remove_import_hook()
log = capnp.load(os.path.join(CEREAL_PATH, "log.capnp"))
car = capnp.load(os.path.join(CEREAL_PATH, "car.capnp"))
-14
View File
@@ -1,14 +0,0 @@
pr: none
pool:
vmImage: 'ubuntu-16.04'
steps:
- script: |
set -e
docker build -t cereal .
docker run cereal bash -c "scons --test --asan -j$(nproc) && messaging/test_runner"
docker run cereal bash -c "ZMQ=1 python -m unittest discover ."
docker run cereal bash -c "MSGQ=1 python -m unittest discover ."
displayName: 'Run Tests'
-456
View File
@@ -1,456 +0,0 @@
using Cxx = import "./include/c++.capnp";
$Cxx.namespace("cereal");
using Java = import "./include/java.capnp";
$Java.package("ai.comma.openpilot.cereal");
$Java.outerClassname("Car");
@0x8e2af1e708af8b8d;
# ******* events causing controls state machine transition *******
struct CarEvent @0x9b1657f34caf3ad3 {
name @0 :EventName;
enable @1 :Bool;
noEntry @2 :Bool;
warning @3 :Bool;
userDisable @4 :Bool;
softDisable @5 :Bool;
immediateDisable @6 :Bool;
preEnable @7 :Bool;
permanent @8 :Bool;
enum EventName @0xbaa8c5d505f727de {
# TODO: copy from error list
canError @0;
steerUnavailable @1;
brakeUnavailable @2;
gasUnavailable @3;
wrongGear @4;
doorOpen @5;
seatbeltNotLatched @6;
espDisabled @7;
wrongCarMode @8;
steerTempUnavailable @9;
reverseGear @10;
buttonCancel @11;
buttonEnable @12;
pedalPressed @13;
cruiseDisabled @14;
radarCanError @15;
dataNeeded @16;
speedTooLow @17;
outOfSpace @18;
overheat @19;
calibrationIncomplete @20;
calibrationInvalid @21;
controlsMismatch @22;
pcmEnable @23;
pcmDisable @24;
noTarget @25;
radarFault @26;
modelCommIssueDEPRECATED @27;
brakeHold @28;
parkBrake @29;
manualRestart @30;
lowSpeedLockout @31;
plannerError @32;
ipasOverride @33;
debugAlert @34;
steerTempUnavailableMute @35;
resumeRequired @36;
preDriverDistracted @37;
promptDriverDistracted @38;
driverDistracted @39;
geofence @40;
driverMonitorOn @41;
driverMonitorOff @42;
preDriverUnresponsive @43;
promptDriverUnresponsive @44;
driverUnresponsive @45;
belowSteerSpeed @46;
calibrationProgress @47;
lowBattery @48;
invalidGiraffeHonda @49;
vehicleModelInvalid @50;
controlsFailed @51;
sensorDataInvalid @52;
commIssue @53;
tooDistracted @54;
posenetInvalid @55;
soundsUnavailable @56;
preLaneChangeLeft @57;
preLaneChangeRight @58;
laneChange @59;
invalidGiraffeToyota @60;
internetConnectivityNeeded @61;
communityFeatureDisallowed @62;
lowMemory @63;
stockAeb @64;
ldw @65;
}
}
# ******* main car state @ 100hz *******
# all speeds in m/s
struct CarState {
errorsDEPRECATED @0 :List(CarEvent.EventName);
events @13 :List(CarEvent);
# car speed
vEgo @1 :Float32; # best estimate of speed
aEgo @16 :Float32; # best estimate of acceleration
vEgoRaw @17 :Float32; # unfiltered speed from CAN sensors
yawRate @22 :Float32; # best estimate of yaw rate
standstill @18 :Bool;
wheelSpeeds @2 :WheelSpeeds;
# gas pedal, 0.0-1.0
gas @3 :Float32; # this is user + computer
gasPressed @4 :Bool; # this is user pedal only
# brake pedal, 0.0-1.0
brake @5 :Float32; # this is user pedal only
brakePressed @6 :Bool; # this is user pedal only
brakeLights @19 :Bool;
# steering wheel
steeringAngle @7 :Float32; # deg
steeringRate @15 :Float32; # deg/s
steeringTorque @8 :Float32; # TODO: standardize units
steeringTorqueEps @27 :Float32; # TODO: standardize units
steeringPressed @9 :Bool; # if the user is using the steering wheel
steeringRateLimited @29 :Bool; # if the torque is limited by the rate limiter
stockAeb @30 :Bool;
stockFcw @31 :Bool;
# cruise state
cruiseState @10 :CruiseState;
# gear
gearShifter @14 :GearShifter;
# button presses
buttonEvents @11 :List(ButtonEvent);
leftBlinker @20 :Bool;
rightBlinker @21 :Bool;
genericToggle @23 :Bool;
# lock info
doorOpen @24 :Bool;
seatbeltUnlatched @25 :Bool;
canValid @26 :Bool;
# clutch (manual transmission only)
clutchPressed @28 :Bool;
# which packets this state came from
canMonoTimes @12: List(UInt64);
struct WheelSpeeds {
# optional wheel speeds
fl @0 :Float32;
fr @1 :Float32;
rl @2 :Float32;
rr @3 :Float32;
}
struct CruiseState {
enabled @0 :Bool;
speed @1 :Float32;
available @2 :Bool;
speedOffset @3 :Float32;
standstill @4 :Bool;
}
enum GearShifter {
unknown @0;
park @1;
drive @2;
neutral @3;
reverse @4;
sport @5;
low @6;
brake @7;
eco @8;
manumatic @9;
}
# send on change
struct ButtonEvent {
pressed @0 :Bool;
type @1 :Type;
enum Type {
unknown @0;
leftBlinker @1;
rightBlinker @2;
accelCruise @3;
decelCruise @4;
cancel @5;
altButton1 @6;
altButton2 @7;
altButton3 @8;
setCruise @9;
resumeCruise @10;
gapAdjustCruise @11;
}
}
}
# ******* radar state @ 20hz *******
struct RadarData @0x888ad6581cf0aacb {
errors @0 :List(Error);
points @1 :List(RadarPoint);
# which packets this state came from
canMonoTimes @2 :List(UInt64);
enum Error {
canError @0;
fault @1;
wrongConfig @2;
}
# similar to LiveTracks
# is one timestamp valid for all? I think so
struct RadarPoint {
trackId @0 :UInt64; # no trackId reuse
# these 3 are the minimum required
dRel @1 :Float32; # m from the front bumper of the car
yRel @2 :Float32; # m
vRel @3 :Float32; # m/s
# these are optional and valid if they are not NaN
aRel @4 :Float32; # m/s^2
yvRel @5 :Float32; # m/s
# some radars flag measurements VS estimates
measured @6 :Bool;
}
}
# ******* car controls @ 100hz *******
struct CarControl {
# must be true for any actuator commands to work
enabled @0 :Bool;
active @7 :Bool;
gasDEPRECATED @1 :Float32;
brakeDEPRECATED @2 :Float32;
steeringTorqueDEPRECATED @3 :Float32;
actuators @6 :Actuators;
cruiseControl @4 :CruiseControl;
hudControl @5 :HUDControl;
struct Actuators {
# range from 0.0 - 1.0
gas @0: Float32;
brake @1: Float32;
# range from -1.0 - 1.0
steer @2: Float32;
steerAngle @3: Float32;
}
struct CruiseControl {
cancel @0: Bool;
override @1: Bool;
speedOverride @2: Float32;
accelOverride @3: Float32;
}
struct HUDControl {
speedVisible @0: Bool;
setSpeed @1: Float32;
lanesVisible @2: Bool;
leadVisible @3: Bool;
visualAlert @4: VisualAlert;
audibleAlert @5: AudibleAlert;
rightLaneVisible @6: Bool;
leftLaneVisible @7: Bool;
rightLaneDepart @8: Bool;
leftLaneDepart @9: Bool;
enum VisualAlert {
# these are the choices from the Honda
# map as good as you can for your car
none @0;
fcw @1;
steerRequired @2;
brakePressed @3;
wrongGear @4;
seatbeltUnbuckled @5;
speedTooHigh @6;
ldw @7;
}
enum AudibleAlert {
# these are the choices from the Honda
# map as good as you can for your car
none @0;
chimeEngage @1;
chimeDisengage @2;
chimeError @3;
chimeWarning1 @4;
chimeWarning2 @5;
chimeWarningRepeat @6;
chimePrompt @7;
}
}
}
# ****** car param ******
struct CarParams {
carName @0 :Text;
carFingerprint @1 :Text;
enableGasInterceptor @2 :Bool;
enableCruise @3 :Bool;
enableCamera @4 :Bool;
enableDsu @5 :Bool; # driving support unit
enableApgs @6 :Bool; # advanced parking guidance system
minEnableSpeed @7 :Float32;
minSteerSpeed @8 :Float32;
safetyModel @9 :SafetyModel;
safetyModelPassive @42 :SafetyModel = silent;
safetyParam @10 :Int16;
steerMaxBP @11 :List(Float32);
steerMaxV @12 :List(Float32);
gasMaxBP @13 :List(Float32);
gasMaxV @14 :List(Float32);
brakeMaxBP @15 :List(Float32);
brakeMaxV @16 :List(Float32);
# things about the car in the manual
mass @17 :Float32; # [kg] running weight
wheelbase @18 :Float32; # [m] distance from rear to front axle
centerToFront @19 :Float32; # [m] GC distance to front axle
steerRatio @20 :Float32; # [] ratio between front wheels and steering wheel angles
steerRatioRear @21 :Float32; # [] rear steering ratio wrt front steering (usually 0)
# things we can derive
rotationalInertia @22 :Float32; # [kg*m2] body rotational inertia
tireStiffnessFront @23 :Float32; # [N/rad] front tire coeff of stiff
tireStiffnessRear @24 :Float32; # [N/rad] rear tire coeff of stiff
longitudinalTuning @25 :LongitudinalPIDTuning;
lateralTuning :union {
pid @26 :LateralPIDTuning;
indi @27 :LateralINDITuning;
lqr @40 :LateralLQRTuning;
}
steerLimitAlert @28 :Bool;
steerLimitTimer @47 :Float32; # time before steerLimitAlert is issued
vEgoStopping @29 :Float32; # Speed at which the car goes into stopping state
directAccelControl @30 :Bool; # Does the car have direct accel control or just gas/brake
stoppingControl @31 :Bool; # Does the car allows full control even at lows speeds when stopping
startAccel @32 :Float32; # Required acceleraton to overcome creep braking
steerRateCost @33 :Float32; # Lateral MPC cost on steering rate
steerControlType @34 :SteerControlType;
radarOffCan @35 :Bool; # True when radar objects aren't visible on CAN
steerActuatorDelay @36 :Float32; # Steering wheel actuator delay in seconds
openpilotLongitudinalControl @37 :Bool; # is openpilot doing the longitudinal control?
carVin @38 :Text; # VIN number queried during fingerprinting
isPandaBlack @39: Bool;
dashcamOnly @41: Bool;
transmissionType @43 :TransmissionType;
carFw @44 :List(CarFw);
radarTimeStep @45: Float32 = 0.05; # time delta between radar updates, 20Hz is very standard
communityFeature @46: Bool; # true if a community maintained feature is detected
struct LateralPIDTuning {
kpBP @0 :List(Float32);
kpV @1 :List(Float32);
kiBP @2 :List(Float32);
kiV @3 :List(Float32);
kf @4 :Float32;
}
struct LongitudinalPIDTuning {
kpBP @0 :List(Float32);
kpV @1 :List(Float32);
kiBP @2 :List(Float32);
kiV @3 :List(Float32);
deadzoneBP @4 :List(Float32);
deadzoneV @5 :List(Float32);
}
struct LateralINDITuning {
outerLoopGain @0 :Float32;
innerLoopGain @1 :Float32;
timeConstant @2 :Float32;
actuatorEffectiveness @3 :Float32;
}
struct LateralLQRTuning {
scale @0 :Float32;
ki @1 :Float32;
dcGain @2 :Float32;
# State space system
a @3 :List(Float32);
b @4 :List(Float32);
c @5 :List(Float32);
k @6 :List(Float32); # LQR gain
l @7 :List(Float32); # Kalman gain
}
enum SafetyModel {
silent @0;
honda @1;
toyota @2;
elm327 @3;
gm @4;
hondaBosch @5;
ford @6;
cadillac @7;
hyundai @8;
chrysler @9;
tesla @10;
subaru @11;
gmPassive @12;
mazda @13;
nissan @14;
volkswagen @15;
toyotaIpas @16;
allOutput @17;
gmAscm @18;
noOutput @19; # like silent but with silent CAN TXs
}
enum SteerControlType {
torque @0;
angle @1;
}
enum TransmissionType {
unknown @0;
automatic @1;
manual @2;
}
struct CarFw {
ecu @0 :Ecu;
fwVersion @1 :Text;
}
enum Ecu {
eps @0;
esp @1;
fwdRadar @2;
fwdCamera @3;
}
}
-26
View File
@@ -1,26 +0,0 @@
#!/bin/bash
rm -r gen/ts
rm -r gen/js
mkdir gen/ts
mkdir gen/js
echo "Installing needed npm modules"
npm i capnpc-ts capnp-ts
capnpc -o node_modules/.bin/capnpc-ts:gen/ts log.capnp car.capnp
capnpc -o node_modules/.bin/capnpc-ts:gen/ts car.capnp
cat log.capnp | egrep '\([a-zA-Z]*\.[^\s]+\.[^s]+\)' | sed 's/^.*([a-zA-Z]*\.\([a-zA-Z.]*\)).*/\1/' | while read line
do
TOKEN=`echo $line | sed 's/\./_/g'`
ROOT=`echo $line | sed 's/\..*$//g'`
cat gen/ts/log.capnp.ts | grep '^import.*'${TOKEN}
if [[ "$?" == "1" ]]
then
sed -i 's/^\(import {.*\)'${ROOT}'\(,*\) \(.*\)$/\1'${ROOT}', '${TOKEN}'\2 \3/' ./gen/ts/log.capnp.ts
fi
done
tsc ./gen/ts/* --lib es2015 --outDir ./gen/js
-28
View File
@@ -1,28 +0,0 @@
# Copyright (c) 2013-2015 Sandstorm Development Group, Inc. and contributors
# Licensed under the MIT License:
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
@0xc5f1af96651f70ea;
annotation package @0x9ee4c8f803b3b596 (file) : Text;
# Name of the package, such as "org.example.foo", in which the generated code will reside.
annotation outerClassname @0x9b066bb4881f7cd3 (file) : Text;
# Name of the outer class that will wrap the generated code.
-40
View File
@@ -1,40 +0,0 @@
set -e
echo "Installing capnp"
cd /tmp
VERSION=0.6.1
wget https://capnproto.org/capnproto-c++-${VERSION}.tar.gz
tar xvf capnproto-c++-${VERSION}.tar.gz
cd capnproto-c++-${VERSION}
CXXFLAGS="-fPIC" ./configure
make -j$(nproc)
make install
# manually build binaries statically
g++ -std=gnu++11 -I./src -I./src -DKJ_HEADER_WARNINGS -DCAPNP_HEADER_WARNINGS -DCAPNP_INCLUDE_DIR=\"/usr/local/include\" -pthread -O2 -DNDEBUG -pthread -pthread -o .libs/capnp src/capnp/compiler/module-loader.o src/capnp/compiler/capnp.o ./.libs/libcapnpc.a ./.libs/libcapnp.a ./.libs/libkj.a -lpthread -pthread
g++ -std=gnu++11 -I./src -I./src -DKJ_HEADER_WARNINGS -DCAPNP_HEADER_WARNINGS -DCAPNP_INCLUDE_DIR=\"/usr/local/include\" -pthread -O2 -DNDEBUG -pthread -pthread -o .libs/capnpc-c++ src/capnp/compiler/capnpc-c++.o ./.libs/libcapnp.a ./.libs/libkj.a -lpthread -pthread
g++ -std=gnu++11 -I./src -I./src -DKJ_HEADER_WARNINGS -DCAPNP_HEADER_WARNINGS -DCAPNP_INCLUDE_DIR=\"/usr/local/include\" -pthread -O2 -DNDEBUG -pthread -pthread -o .libs/capnpc-capnp src/capnp/compiler/capnpc-capnp.o ./.libs/libcapnp.a ./.libs/libkj.a -lpthread -pthread
cp .libs/capnp /usr/local/bin/
cp .libs/capnpc-c++ /usr/local/bin/
cp .libs/capnpc-capnp /usr/local/bin/
cp .libs/*.a /usr/local/lib
cd /tmp
echo "Installing c-capnp"
git clone https://github.com/commaai/c-capnproto.git
cd c-capnproto
git submodule update --init --recursive
autoreconf -f -i -s
CXXFLAGS="-fPIC" ./configure
make -j$(nproc)
make install
# manually build binaries statically
gcc -fPIC -o .libs/capnpc-c compiler/capnpc-c.o compiler/schema.capnp.o compiler/str.o ./.libs/libcapnp_c.a
cp .libs/capnpc-c /usr/local/bin/
cp .libs/*.a /usr/local/lib
-1893
View File
File diff suppressed because it is too large Load Diff
-53
View File
@@ -1,53 +0,0 @@
using Cxx = import "./include/c++.capnp";
$Cxx.namespace("cereal");
using Java = import "./include/java.capnp";
$Java.package("ai.comma.openpilot.cereal");
$Java.outerClassname("Map");
@0xa086df597ef5d7a0;
# Geometry
struct Point {
x @0: Float64;
y @1: Float64;
z @2: Float64;
}
struct PolyLine {
points @0: List(Point);
}
# Map features
struct Lane {
id @0 :Text;
leftBoundary @1 :LaneBoundary;
rightBoundary @2 :LaneBoundary;
leftAdjacentId @3 :Text;
rightAdjacentId @4 :Text;
inboundIds @5 :List(Text);
outboundIds @6 :List(Text);
struct LaneBoundary {
polyLine @0 :PolyLine;
startHeading @1 :Float32; # WRT north
}
}
# Map tiles
struct TileSummary {
version @0 :Text;
updatedAt @1 :UInt64; # Millis since epoch
level @2 :UInt8;
x @3 :UInt16;
y @4 :UInt16;
}
struct MapTile {
summary @0 :TileSummary;
lanes @1 :List(Lane);
}
-10
View File
@@ -1,10 +0,0 @@
demo
bridge
test_runner
*.o
*.os
*.d
*.a
*.so
messaging_pyx.cpp
build/
-219
View File
@@ -1,219 +0,0 @@
# must be build with scons
from .messaging_pyx import Context, Poller, SubSocket, PubSocket # pylint: disable=no-name-in-module, import-error
from .messaging_pyx import MultiplePublishersError, MessagingError # pylint: disable=no-name-in-module, import-error
assert MultiplePublishersError
assert MessagingError
from cereal import log
from cereal.services import service_list
# sec_since_boot is faster, but allow to run standalone too
try:
from common.realtime import sec_since_boot
except ImportError:
import time
sec_since_boot = time.time
print("Warning, using python time.time() instead of faster sec_since_boot")
context = Context()
def new_message():
dat = log.Event.new_message()
dat.logMonoTime = int(sec_since_boot() * 1e9)
dat.valid = True
return dat
def pub_sock(endpoint):
sock = PubSocket()
sock.connect(context, endpoint)
return sock
def sub_sock(endpoint, poller=None, addr="127.0.0.1", conflate=False, timeout=None):
sock = SubSocket()
addr = addr.encode('utf8')
sock.connect(context, endpoint, addr, conflate)
if timeout is not None:
sock.setTimeout(timeout)
if poller is not None:
poller.registerSocket(sock)
return sock
def drain_sock_raw(sock, wait_for_one=False):
"""Receive all message currently available on the queue"""
ret = []
while 1:
if wait_for_one and len(ret) == 0:
dat = sock.receive()
else:
dat = sock.receive(non_blocking=True)
if dat is None:
break
ret.append(dat)
return ret
def drain_sock(sock, wait_for_one=False):
"""Receive all message currently available on the queue"""
ret = []
while 1:
if wait_for_one and len(ret) == 0:
dat = sock.receive()
else:
dat = sock.receive(non_blocking=True)
if dat is None: # Timeout hit
break
dat = log.Event.from_bytes(dat)
ret.append(dat)
return ret
# TODO: print when we drop packets?
def recv_sock(sock, wait=False):
"""Same as drain sock, but only returns latest message. Consider using conflate instead."""
dat = None
while 1:
if wait and dat is None:
rcv = sock.receive()
else:
rcv = sock.receive(non_blocking=True)
if rcv is None: # Timeout hit
break
dat = rcv
if dat is not None:
dat = log.Event.from_bytes(dat)
return dat
def recv_one(sock):
dat = sock.receive()
if dat is not None:
dat = log.Event.from_bytes(dat)
return dat
def recv_one_or_none(sock):
dat = sock.receive(non_blocking=True)
if dat is not None:
dat = log.Event.from_bytes(dat)
return dat
def recv_one_retry(sock):
"""Keep receiving until we get a message"""
while True:
dat = sock.receive()
if dat is not None:
return log.Event.from_bytes(dat)
def get_one_can(logcan):
while True:
can = recv_one_retry(logcan)
if len(can.can) > 0:
return can
class SubMaster():
def __init__(self, services, ignore_alive=None, addr="127.0.0.1"):
self.poller = Poller()
self.frame = -1
self.updated = {s : False for s in services}
self.rcv_time = {s : 0. for s in services}
self.rcv_frame = {s : 0 for s in services}
self.alive = {s : False for s in services}
self.sock = {}
self.freq = {}
self.data = {}
self.logMonoTime = {}
self.valid = {}
if ignore_alive is not None:
self.ignore_alive = ignore_alive
else:
self.ignore_alive = []
for s in services:
if addr is not None:
self.sock[s] = sub_sock(s, poller=self.poller, addr=addr, conflate=True)
self.freq[s] = service_list[s].frequency
data = new_message()
if s in ['can', 'sensorEvents', 'liveTracks', 'sendCan',
'ethernetData', 'cellInfo', 'wifiScan',
'trafficEvents', 'orbObservation', 'carEvents']:
data.init(s, 0)
else:
data.init(s)
self.data[s] = getattr(data, s)
self.logMonoTime[s] = 0
self.valid[s] = data.valid
def __getitem__(self, s):
return self.data[s]
def update(self, timeout=1000):
msgs = []
for sock in self.poller.poll(timeout):
msgs.append(recv_one_or_none(sock))
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
self.frame += 1
self.updated = dict.fromkeys(self.updated, False)
for msg in msgs:
if msg is None:
continue
s = msg.which()
self.updated[s] = True
self.rcv_time[s] = cur_time
self.rcv_frame[s] = self.frame
self.data[s] = getattr(msg, s)
self.logMonoTime[s] = msg.logMonoTime
self.valid[s] = msg.valid
for s in self.data:
# arbitrary small number to avoid float comparison. If freq is 0, we can skip the check
if self.freq[s] > 1e-5:
# alive if delay is within 10x the expected frequency
self.alive[s] = (cur_time - self.rcv_time[s]) < (10. / self.freq[s])
else:
self.alive[s] = True
def all_alive(self, service_list=None):
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):
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):
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):
self.sock = {}
for s in services:
self.sock[s] = pub_sock(s)
def send(self, s, dat):
# accept either bytes or capnp builder
if not isinstance(dat, bytes):
dat = dat.to_bytes()
self.sock[s].send(dat)
-62
View File
@@ -1,62 +0,0 @@
#include <iostream>
#include <string>
#include <cassert>
#include <csignal>
#include <map>
#include "services.h"
#include "impl_msgq.hpp"
#include "impl_zmq.hpp"
void sigpipe_handler(int sig) {
assert(sig == SIGPIPE);
std::cout << "SIGPIPE received" << std::endl;
}
static std::vector<std::string> get_services() {
std::vector<std::string> name_list;
for (const auto& it : services) {
std::string name = it.name;
if (name == "plusFrame" || name == "uiLayoutState") continue;
name_list.push_back(name);
}
return name_list;
}
int main(void){
signal(SIGPIPE, (sighandler_t)sigpipe_handler);
auto endpoints = get_services();
std::map<SubSocket*, PubSocket*> sub2pub;
Context *zmq_context = new ZMQContext();
Context *msgq_context = new MSGQContext();
Poller *poller = new MSGQPoller();
for (auto endpoint: endpoints){
SubSocket * msgq_sock = new MSGQSubSocket();
msgq_sock->connect(msgq_context, endpoint, "127.0.0.1", false);
poller->registerSocket(msgq_sock);
PubSocket * zmq_sock = new ZMQPubSocket();
zmq_sock->connect(zmq_context, endpoint);
sub2pub[msgq_sock] = zmq_sock;
}
while (true){
for (auto sub_sock : poller->poll(100)){
Message * msg = sub_sock->receive();
if (msg == NULL) continue;
sub2pub[sub_sock]->sendMessage(msg);
delete msg;
}
}
return 0;
}
File diff suppressed because it is too large Load Diff
-50
View File
@@ -1,50 +0,0 @@
#include <iostream>
#include <cstddef>
#include <chrono>
#include <thread>
#include <cassert>
#include "messaging.hpp"
#include "impl_zmq.hpp"
#define MSGS 1e5
int main() {
Context * c = Context::create();
SubSocket * sub_sock = SubSocket::create(c, "controlsState");
PubSocket * pub_sock = PubSocket::create(c, "controlsState");
char data[8];
Poller * poller = Poller::create({sub_sock});
auto start = std::chrono::steady_clock::now();
for (uint64_t i = 0; i < MSGS; i++){
*(uint64_t*)data = i;
pub_sock->send(data, 8);
auto r = poller->poll(100);
for (auto p : r){
Message * m = p->receive();
uint64_t ii = *(uint64_t*)m->getData();
assert(i == ii);
delete m;
}
}
auto end = std::chrono::steady_clock::now();
double elapsed = std::chrono::duration_cast<std::chrono::nanoseconds>(end - start).count() / 1e9;
double throughput = ((double) MSGS / (double) elapsed);
std::cout << throughput << " msg/s" << std::endl;
delete poller;
delete sub_sock;
delete pub_sock;
delete c;
return 0;
}
-30
View File
@@ -1,30 +0,0 @@
import time
from messaging_pyx import Context, Poller, SubSocket, PubSocket # pylint: disable=no-name-in-module, import-error
MSGS = 1e5
if __name__ == "__main__":
c = Context()
sub_sock = SubSocket()
pub_sock = PubSocket()
sub_sock.connect(c, "controlsState")
pub_sock.connect(c, "controlsState")
poller = Poller()
poller.registerSocket(sub_sock)
t = time.time()
for i in range(int(MSGS)):
bts = i.to_bytes(4, 'little')
pub_sock.send(bts)
for s in poller.poll(100):
dat = s.receive()
ii = int.from_bytes(dat, 'little')
assert(i == ii)
dt = time.time() - t
print("%.1f msg/s" % (MSGS / dt))
-190
View File
@@ -1,190 +0,0 @@
#include <cassert>
#include <cstring>
#include <iostream>
#include <cstdlib>
#include <csignal>
#include <cerrno>
#include "impl_msgq.hpp"
volatile sig_atomic_t msgq_do_exit = 0;
void sig_handler(int signal) {
assert(signal == SIGINT || signal == SIGTERM);
msgq_do_exit = 1;
}
MSGQContext::MSGQContext() {
}
MSGQContext::~MSGQContext() {
}
void MSGQMessage::init(size_t sz) {
size = sz;
data = new char[size];
}
void MSGQMessage::init(char * d, size_t sz) {
size = sz;
data = new char[size];
memcpy(data, d, size);
}
void MSGQMessage::takeOwnership(char * d, size_t sz) {
size = sz;
data = d;
}
void MSGQMessage::close() {
if (size > 0){
delete[] data;
}
size = 0;
}
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);
if (r != 0){
return r;
}
msgq_init_subscriber(q);
if (conflate){
q->read_conflate = true;
}
timeout = -1;
return 0;
}
Message * MSGQSubSocket::receive(bool non_blocking){
msgq_do_exit = 0;
void (*prev_handler_sigint)(int);
void (*prev_handler_sigterm)(int);
if (!non_blocking){
prev_handler_sigint = std::signal(SIGINT, sig_handler);
prev_handler_sigterm = std::signal(SIGTERM, sig_handler);
}
msgq_msg_t msg;
MSGQMessage *r = NULL;
r = NULL;
int rc = msgq_msg_recv(&msg, q);
// Hack to implement blocking read with a poller. Don't use this
while (!non_blocking && rc == 0 && msgq_do_exit == 0){
msgq_pollitem_t items[1];
items[0].q = q;
int t = (timeout != -1) ? timeout : 100;
int n = msgq_poll(items, 1, t);
rc = msgq_msg_recv(&msg, q);
// The poll indicated a message was ready, but the receive failed. Try again
if (n == 1 && rc == 0){
continue;
}
if (timeout != -1){
break;
}
}
if (rc > 0){
r = new MSGQMessage;
r->takeOwnership(msg.data, msg.size);
}
errno = msgq_do_exit ? EINTR : 0;
if (!non_blocking){
std::signal(SIGINT, prev_handler_sigint);
std::signal(SIGTERM, prev_handler_sigterm);
}
return (Message*)r;
}
void MSGQSubSocket::setTimeout(int t){
timeout = t;
}
MSGQSubSocket::~MSGQSubSocket(){
if (q != NULL){
msgq_close_queue(q);
delete q;
}
}
int MSGQPubSocket::connect(Context *context, std::string endpoint){
assert(context);
q = new msgq_queue_t;
msgq_new_queue(q, endpoint.c_str(), DEFAULT_SEGMENT_SIZE);
msgq_init_publisher(q);
return 0;
}
int MSGQPubSocket::sendMessage(Message *message){
msgq_msg_t msg;
msg.data = message->getData();
msg.size = message->getSize();
return msgq_msg_send(&msg, q);
}
int MSGQPubSocket::send(char *data, size_t size){
msgq_msg_t msg;
msg.data = data;
msg.size = size;
return msgq_msg_send(&msg, q);
}
MSGQPubSocket::~MSGQPubSocket(){
if (q != NULL){
msgq_close_queue(q);
delete q;
}
}
void MSGQPoller::registerSocket(SubSocket * socket){
assert(num_polls + 1 < MAX_POLLERS);
polls[num_polls].q = (msgq_queue_t*)socket->getRawSocket();
sockets.push_back(socket);
num_polls++;
}
std::vector<SubSocket*> MSGQPoller::poll(int timeout){
std::vector<SubSocket*> r;
msgq_poll(polls, num_polls, timeout);
for (size_t i = 0; i < num_polls; i++){
if (polls[i].revents){
r.push_back(sockets[i]);
}
}
return r;
}
-64
View File
@@ -1,64 +0,0 @@
#pragma once
#include "messaging.hpp"
#include "msgq.hpp"
#include <zmq.h>
#include <string>
#define MAX_POLLERS 128
class MSGQContext : public Context {
private:
void * context = NULL;
public:
MSGQContext();
void * getRawContext() {return context;}
~MSGQContext();
};
class MSGQMessage : public Message {
private:
char * data;
size_t size;
public:
void init(size_t size);
void init(char *data, size_t size);
void takeOwnership(char *data, size_t size);
size_t getSize(){return size;}
char * getData(){return data;}
void close();
~MSGQMessage();
};
class MSGQSubSocket : public SubSocket {
private:
msgq_queue_t * q = NULL;
int timeout;
public:
int connect(Context *context, std::string endpoint, std::string address, bool conflate=false);
void setTimeout(int timeout);
void * getRawSocket() {return (void*)q;}
Message *receive(bool non_blocking=false);
~MSGQSubSocket();
};
class MSGQPubSocket : public PubSocket {
private:
msgq_queue_t * q = NULL;
public:
int connect(Context *context, std::string endpoint);
int sendMessage(Message *message);
int send(char *data, size_t size);
~MSGQPubSocket();
};
class MSGQPoller : public Poller {
private:
std::vector<SubSocket*> sockets;
msgq_pollitem_t polls[MAX_POLLERS];
size_t num_polls = 0;
public:
void registerSocket(SubSocket *socket);
std::vector<SubSocket*> poll(int timeout);
~MSGQPoller(){};
};
-155
View File
@@ -1,155 +0,0 @@
#include <cassert>
#include <cstring>
#include <iostream>
#include <cstdlib>
#include <cerrno>
#include <zmq.h>
#include "services.h"
#include "impl_zmq.hpp"
static int get_port(std::string endpoint) {
int port = -1;
for (const auto& it : services) {
std::string name = it.name;
if (name == endpoint) {
port = it.port;
break;
}
}
assert(port >= 0);
return port;
}
ZMQContext::ZMQContext() {
context = zmq_ctx_new();
}
ZMQContext::~ZMQContext() {
zmq_ctx_term(context);
}
void ZMQMessage::init(size_t sz) {
size = sz;
data = new char[size];
}
void ZMQMessage::init(char * d, size_t sz) {
size = sz;
data = new char[size];
memcpy(data, d, size);
}
void ZMQMessage::close() {
if (size > 0){
delete[] data;
}
size = 0;
}
ZMQMessage::~ZMQMessage() {
this->close();
}
int ZMQSubSocket::connect(Context *context, std::string endpoint, std::string address, bool conflate){
sock = zmq_socket(context->getRawContext(), ZMQ_SUB);
if (sock == NULL){
return -1;
}
zmq_setsockopt(sock, ZMQ_SUBSCRIBE, "", 0);
if (conflate){
int arg = 1;
zmq_setsockopt(sock, ZMQ_CONFLATE, &arg, sizeof(int));
}
int reconnect_ivl = 500;
zmq_setsockopt(sock, ZMQ_RECONNECT_IVL_MAX, &reconnect_ivl, sizeof(reconnect_ivl));
full_endpoint = "tcp://" + address + ":";
full_endpoint += std::to_string(get_port(endpoint));
return zmq_connect(sock, full_endpoint.c_str());
}
Message * ZMQSubSocket::receive(bool non_blocking){
zmq_msg_t msg;
assert(zmq_msg_init(&msg) == 0);
int flags = non_blocking ? ZMQ_DONTWAIT : 0;
int rc = zmq_msg_recv(&msg, sock, flags);
Message *r = NULL;
if (rc >= 0){
// Make a copy to ensure the data is aligned
r = new ZMQMessage;
r->init((char*)zmq_msg_data(&msg), zmq_msg_size(&msg));
}
zmq_msg_close(&msg);
return r;
}
void ZMQSubSocket::setTimeout(int timeout){
zmq_setsockopt(sock, ZMQ_RCVTIMEO, &timeout, sizeof(int));
}
ZMQSubSocket::~ZMQSubSocket(){
zmq_close(sock);
}
int ZMQPubSocket::connect(Context *context, std::string endpoint){
sock = zmq_socket(context->getRawContext(), ZMQ_PUB);
if (sock == NULL){
return -1;
}
full_endpoint = "tcp://*:";
full_endpoint += std::to_string(get_port(endpoint));
return zmq_bind(sock, full_endpoint.c_str());
}
int ZMQPubSocket::sendMessage(Message *message){
return zmq_send(sock, message->getData(), message->getSize(), ZMQ_DONTWAIT);
}
int ZMQPubSocket::send(char *data, size_t size){
return zmq_send(sock, data, size, ZMQ_DONTWAIT);
}
ZMQPubSocket::~ZMQPubSocket(){
zmq_close(sock);
}
void ZMQPoller::registerSocket(SubSocket * socket){
assert(num_polls + 1 < MAX_POLLERS);
polls[num_polls].socket = socket->getRawSocket();
polls[num_polls].events = ZMQ_POLLIN;
sockets.push_back(socket);
num_polls++;
}
std::vector<SubSocket*> ZMQPoller::poll(int timeout){
std::vector<SubSocket*> r;
int rc = zmq_poll(polls, num_polls, timeout);
if (rc < 0){
return r;
}
for (size_t i = 0; i < num_polls; i++){
if (polls[i].revents){
r.push_back(sockets[i]);
}
}
return r;
}
-63
View File
@@ -1,63 +0,0 @@
#pragma once
#include "messaging.hpp"
#include <zmq.h>
#include <string>
#define MAX_POLLERS 128
class ZMQContext : public Context {
private:
void * context = NULL;
public:
ZMQContext();
void * getRawContext() {return context;}
~ZMQContext();
};
class ZMQMessage : public Message {
private:
char * data;
size_t size;
public:
void init(size_t size);
void init(char *data, size_t size);
size_t getSize(){return size;}
char * getData(){return data;}
void close();
~ZMQMessage();
};
class ZMQSubSocket : public SubSocket {
private:
void * sock;
std::string full_endpoint;
public:
int connect(Context *context, std::string endpoint, std::string address, bool conflate=false);
void setTimeout(int timeout);
void * getRawSocket() {return sock;}
Message *receive(bool non_blocking=false);
~ZMQSubSocket();
};
class ZMQPubSocket : public PubSocket {
private:
void * sock;
std::string full_endpoint;
public:
int connect(Context *context, std::string endpoint);
int sendMessage(Message *message);
int send(char *data, size_t size);
~ZMQPubSocket();
};
class ZMQPoller : public Poller {
private:
std::vector<SubSocket*> sockets;
zmq_pollitem_t polls[MAX_POLLERS];
size_t num_polls = 0;
public:
void registerSocket(SubSocket *socket);
std::vector<SubSocket*> poll(int timeout);
~ZMQPoller(){};
};
-117
View File
@@ -1,117 +0,0 @@
#include "messaging.hpp"
#include "impl_zmq.hpp"
#include "impl_msgq.hpp"
Context * Context::create(){
Context * c;
if (std::getenv("MSGQ")){
c = new MSGQContext();
} else {
c = new ZMQContext();
}
return c;
}
SubSocket * SubSocket::create(){
SubSocket * s;
if (std::getenv("MSGQ")){
s = new MSGQSubSocket();
} else {
s = new ZMQSubSocket();
}
return s;
}
SubSocket * SubSocket::create(Context * context, std::string endpoint){
SubSocket *s = SubSocket::create();
int r = s->connect(context, endpoint, "127.0.0.1");
if (r == 0) {
return s;
} else {
delete s;
return NULL;
}
}
SubSocket * SubSocket::create(Context * context, std::string endpoint, std::string address){
SubSocket *s = SubSocket::create();
int r = s->connect(context, endpoint, address);
if (r == 0) {
return s;
} else {
delete s;
return NULL;
}
}
SubSocket * SubSocket::create(Context * context, std::string endpoint, std::string address, bool conflate){
SubSocket *s = SubSocket::create();
int r = s->connect(context, endpoint, address, conflate);
if (r == 0) {
return s;
} else {
delete s;
return NULL;
}
}
PubSocket * PubSocket::create(){
PubSocket * s;
if (std::getenv("MSGQ")){
s = new MSGQPubSocket();
} else {
s = new ZMQPubSocket();
}
return s;
}
PubSocket * PubSocket::create(Context * context, std::string endpoint){
PubSocket *s = PubSocket::create();
int r = s->connect(context, endpoint);
if (r == 0) {
return s;
} else {
delete s;
return NULL;
}
}
Poller * Poller::create(){
Poller * p;
if (std::getenv("MSGQ")){
p = new MSGQPoller();
} else {
p = new ZMQPoller();
}
return p;
}
Poller * Poller::create(std::vector<SubSocket*> sockets){
Poller * p = Poller::create();
for (auto s : sockets){
p->registerSocket(s);
}
return p;
}
extern "C" Context * messaging_context_create() {
return Context::create();
}
extern "C" SubSocket * messaging_subsocket_create(Context* context, const char* endpoint) {
return SubSocket::create(context, std::string(endpoint));
}
extern "C" PubSocket * messaging_pubsocket_create(Context* context, const char* endpoint) {
return PubSocket::create(context, std::string(endpoint));
}
extern "C" Poller * messaging_poller_create(SubSocket** sockets, int size) {
std::vector<SubSocket*> socketsVec(sockets, sockets + size);
return Poller::create(socketsVec);
}
-56
View File
@@ -1,56 +0,0 @@
#pragma once
#include <cstddef>
#include <vector>
#include <string>
#define MSG_MULTIPLE_PUBLISHERS 100
class Context {
public:
virtual void * getRawContext() = 0;
static Context * create();
virtual ~Context(){};
};
class Message {
public:
virtual void init(size_t size) = 0;
virtual void init(char * data, size_t size) = 0;
virtual void close() = 0;
virtual size_t getSize() = 0;
virtual char * getData() = 0;
virtual ~Message(){};
};
class SubSocket {
public:
virtual int connect(Context *context, std::string endpoint, std::string address, bool conflate=false) = 0;
virtual void setTimeout(int timeout) = 0;
virtual Message *receive(bool non_blocking=false) = 0;
virtual void * getRawSocket() = 0;
static SubSocket * create();
static SubSocket * create(Context * context, std::string endpoint);
static SubSocket * create(Context * context, std::string endpoint, std::string address);
static SubSocket * create(Context * context, std::string endpoint, std::string address, bool conflate);
virtual ~SubSocket(){};
};
class PubSocket {
public:
virtual int connect(Context *context, std::string endpoint) = 0;
virtual int sendMessage(Message *message) = 0;
virtual int send(char *data, size_t size) = 0;
static PubSocket * create();
static PubSocket * create(Context * context, std::string endpoint);
virtual ~PubSocket(){};
};
class Poller {
public:
virtual void registerSocket(SubSocket *socket) = 0;
virtual std::vector<SubSocket*> poll(int timeout) = 0;
static Poller * create();
static Poller * create(std::vector<SubSocket*> sockets);
virtual ~Poller(){};
};
-39
View File
@@ -1,39 +0,0 @@
# distutils: language = c++
#cython: language_level=3
from libcpp.string cimport string
from libcpp.vector cimport vector
from libcpp cimport bool
cdef extern from "messaging.hpp":
cdef cppclass Context:
@staticmethod
Context * create()
cdef cppclass Message:
void init(size_t)
void init(char *, size_t)
void close()
size_t getSize()
char *getData()
cdef cppclass SubSocket:
@staticmethod
SubSocket * create()
int connect(Context *, string, string, bool)
Message * receive(bool)
void setTimeout(int)
cdef cppclass PubSocket:
@staticmethod
PubSocket * create()
int connect(Context *, string)
int sendMessage(Message *)
int send(char *, size_t)
cdef cppclass Poller:
@staticmethod
Poller * create()
void registerSocket(SubSocket *)
vector[SubSocket*] poll(int) nogil
-151
View File
@@ -1,151 +0,0 @@
# distutils: language = c++
# cython: c_string_encoding=ascii, language_level=3
import sys
from libcpp.string cimport string
from libcpp cimport bool
from libc cimport errno
from messaging cimport Context as cppContext
from messaging cimport SubSocket as cppSubSocket
from messaging cimport PubSocket as cppPubSocket
from messaging cimport Poller as cppPoller
from messaging cimport Message as cppMessage
class MessagingError(Exception):
pass
class MultiplePublishersError(MessagingError):
pass
cdef class Context:
cdef cppContext * context
def __cinit__(self):
self.context = cppContext.create()
def term(self):
del self.context
self.context = NULL
def __dealloc__(self):
pass
# Deleting the context will hang if sockets are still active
# TODO: Figure out a way to make sure the context is closed last
# del self.context
cdef class Poller:
cdef cppPoller * poller
cdef list sub_sockets
def __cinit__(self):
self.sub_sockets = []
self.poller = cppPoller.create()
def __dealloc__(self):
del self.poller
def registerSocket(self, SubSocket socket):
self.sub_sockets.append(socket)
self.poller.registerSocket(socket.socket)
def poll(self, timeout):
sockets = []
cdef int t = timeout
with nogil:
result = self.poller.poll(t)
for s in result:
socket = SubSocket()
socket.setPtr(s)
sockets.append(socket)
return sockets
cdef class SubSocket:
cdef cppSubSocket * socket
cdef bool is_owner
def __cinit__(self):
self.socket = cppSubSocket.create()
self.is_owner = True
if self.socket == NULL:
raise MessagingError
def __dealloc__(self):
if self.is_owner:
del self.socket
cdef setPtr(self, cppSubSocket * ptr):
if self.is_owner:
del self.socket
self.is_owner = False
self.socket = ptr
def connect(self, Context context, string endpoint, string address=b"127.0.0.1", bool conflate=False):
r = self.socket.connect(context.context, endpoint, address, conflate)
if r != 0:
if errno.errno == errno.EADDRINUSE:
raise MultiplePublishersError
else:
raise MessagingError
def setTimeout(self, int timeout):
self.socket.setTimeout(timeout)
def receive(self, bool non_blocking=False):
msg = self.socket.receive(non_blocking)
if msg == NULL:
# If a blocking read returns no message check errno if SIGINT was caught in the C++ code
if errno.errno == errno.EINTR:
print("SIGINT received, exiting")
sys.exit(1)
return None
else:
sz = msg.getSize()
m = msg.getData()[:sz]
del msg
return m
cdef class PubSocket:
cdef cppPubSocket * socket
def __cinit__(self):
self.socket = cppPubSocket.create()
if self.socket == NULL:
raise MessagingError
def __dealloc__(self):
del self.socket
def connect(self, Context context, string endpoint):
r = self.socket.connect(context.context, endpoint)
if r != 0:
if errno.errno == errno.EADDRINUSE:
raise MultiplePublishersError
else:
raise MessagingError
def send(self, string data):
length = len(data)
r = self.socket.send(<char*>data.c_str(), length)
if r != length:
if errno.errno == errno.EADDRINUSE:
raise MultiplePublishersError
else:
raise MessagingError
-56
View File
@@ -1,56 +0,0 @@
import os
import subprocess
import sysconfig
from distutils.core import Extension, setup # pylint: disable=import-error,no-name-in-module
from Cython.Build import cythonize
from Cython.Distutils import build_ext
def get_ext_filename_without_platform_suffix(filename):
name, ext = os.path.splitext(filename)
ext_suffix = sysconfig.get_config_var('EXT_SUFFIX')
if ext_suffix == ext:
return filename
ext_suffix = ext_suffix.replace(ext, '')
idx = name.find(ext_suffix)
if idx == -1:
return filename
else:
return name[:idx] + ext
class BuildExtWithoutPlatformSuffix(build_ext):
def get_ext_filename(self, ext_name):
filename = super().get_ext_filename(ext_name)
return get_ext_filename_without_platform_suffix(filename)
sourcefiles = ['messaging_pyx.pyx']
extra_compile_args = ["-std=c++11"]
libraries = ['zmq']
ARCH = subprocess.check_output(["uname", "-m"], encoding='utf8').rstrip() # pylint: disable=unexpected-keyword-arg
if ARCH == "aarch64":
extra_compile_args += ["-Wno-deprecated-register"]
libraries += ['gnustl_shared']
setup(name='CAN parser',
cmdclass={'build_ext': BuildExtWithoutPlatformSuffix},
ext_modules=cythonize(
Extension(
"messaging_pyx",
language="c++",
sources=sourcefiles,
extra_compile_args=extra_compile_args,
libraries=libraries,
extra_objects=[
os.path.join(os.path.dirname(os.path.realpath(__file__)), '../', 'libmessaging.a'),
]
)
),
nthreads=4,
)
-441
View File
@@ -1,441 +0,0 @@
#include <iostream>
#include <cassert>
#include <cerrno>
#include <cmath>
#include <cstring>
#include <cstdint>
#include <chrono>
#include <algorithm>
#include <cstdlib>
#include <csignal>
#include <random>
#include <poll.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/syscall.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include "msgq.hpp"
void sigusr1_handler(int signal) {
assert(signal == SIGUSR1);
}
uint64_t msgq_get_uid(void){
std::random_device rd("/dev/urandom");
std::uniform_int_distribution<uint64_t> distribution(0,std::numeric_limits<uint32_t>::max());
uint64_t uid = distribution(rd) << 32 | syscall(SYS_gettid);
return uid;
}
int msgq_msg_init_size(msgq_msg_t * msg, size_t size){
msg->size = size;
msg->data = new(std::nothrow) char[size];
return (msg->data == NULL) ? -1 : 0;
}
int msgq_msg_init_data(msgq_msg_t * msg, char * data, size_t size) {
int r = msgq_msg_init_size(msg, size);
if (r == 0)
memcpy(msg->data, data, size);
return r;
}
int msgq_msg_close(msgq_msg_t * msg){
if (msg->size > 0)
delete[] msg->data;
msg->size = 0;
return 0;
}
void msgq_reset_reader(msgq_queue_t * q){
int id = q->reader_id;
q->read_valids[id]->store(true);
q->read_pointers[id]->store(*q->write_pointer);
}
void msgq_wait_for_subscriber(msgq_queue_t *q){
while (*q->num_readers == 0){
;
}
return;
}
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
std::signal(SIGUSR1, sigusr1_handler);
const char * prefix = "/dev/shm/";
char * full_path = new char[strlen(path) + strlen(prefix) + 1];
strcpy(full_path, prefix);
strcat(full_path, path);
auto fd = open(full_path, O_RDWR | O_CREAT, 0777);
delete[] full_path;
if (fd < 0)
return -1;
int rc = ftruncate(fd, size + sizeof(msgq_header_t));
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)
return -1;
q->mmap_p = mem;
msgq_header_t *header = (msgq_header_t *)mem;
// Setup pointers to header segment
q->num_readers = reinterpret_cast<std::atomic<uint64_t>*>(&header->num_readers);
q->write_pointer = reinterpret_cast<std::atomic<uint64_t>*>(&header->write_pointer);
q->write_uid = reinterpret_cast<std::atomic<uint64_t>*>(&header->write_uid);
for (size_t i = 0; i < NUM_READERS; i++){
q->read_pointers[i] = reinterpret_cast<std::atomic<uint64_t>*>(&header->read_pointers[i]);
q->read_valids[i] = reinterpret_cast<std::atomic<uint64_t>*>(&header->read_valids[i]);
q->read_uids[i] = reinterpret_cast<std::atomic<uint64_t>*>(&header->read_uids[i]);
}
q->data = mem + sizeof(msgq_header_t);
q->size = size;
q->reader_id = -1;
q->endpoint = path;
q->read_conflate = false;
return 0;
}
void msgq_close_queue(msgq_queue_t *q){
if (q->mmap_p != NULL){
munmap(q->mmap_p, q->size + sizeof(msgq_header_t));
}
}
void msgq_init_publisher(msgq_queue_t * q) {
std::cout << "Starting publisher" << std::endl;
uint64_t uid = msgq_get_uid();
*q->write_uid = uid;
*q->num_readers = 0;
for (size_t i = 0; i < NUM_READERS; i++){
*q->read_valids[i] = false;
*q->read_uids[i] = 0;
}
q->write_uid_local = uid;
}
void msgq_init_subscriber(msgq_queue_t * q) {
assert(q != NULL);
assert(q->num_readers != NULL);
uint64_t uid = msgq_get_uid();
// Get reader id
while (true){
uint64_t cur_num_readers = *q->num_readers;
uint64_t new_num_readers = cur_num_readers + 1;
// No more slots available. Reset all subscribers to kick out inactive ones
if (new_num_readers > NUM_READERS){
std::cout << "Warning, evicting all subscribers!" << std::endl;
*q->num_readers = 0;
for (size_t i = 0; i < NUM_READERS; i++){
*q->read_valids[i] = false;
uint64_t old_uid = *q->read_uids[i];
*q->read_uids[i] = 0;
// Wake up reader in case they are in a poll
syscall(SYS_tkill, old_uid & 0xFFFFFFFF, SIGUSR1);
}
continue;
}
// Use atomic compare and swap to handle race condition
// where two subscribers start at the same time
if (std::atomic_compare_exchange_strong(q->num_readers,
&cur_num_readers,
new_num_readers)){
q->reader_id = cur_num_readers;
q->read_uid_local = uid;
// We start with read_valid = false,
// on the first read the read pointer will be synchronized with the write pointer
*q->read_valids[cur_num_readers] = false;
*q->read_pointers[cur_num_readers] = 0;
*q->read_uids[cur_num_readers] = uid;
break;
}
}
std::cout << "New subscriber id: " << q->reader_id << " uid: " << q->read_uid_local << " " << q->endpoint << std::endl;
msgq_reset_reader(q);
}
int msgq_msg_send(msgq_msg_t * msg, msgq_queue_t *q){
// Die if we are no longer the active publisher
if (q->write_uid_local != *q->write_uid){
std::cout << "Killing old publisher: " << q->endpoint << std::endl;
errno = EADDRINUSE;
return -1;
}
uint64_t total_msg_size = ALIGN(msg->size + sizeof(int64_t));
// We need to fit at least three messages in the queue,
// then we can always safely access the last message
assert(3 * total_msg_size <= q->size);
uint64_t num_readers = *q->num_readers;
uint32_t write_cycles, write_pointer;
UNPACK64(write_cycles, write_pointer, *q->write_pointer);
char *p = q->data + write_pointer; // add base offset
// Check remaining space
// Always leave space for a wraparound tag for the next message, including alignment
int64_t remaining_space = q->size - write_pointer - total_msg_size - sizeof(int64_t);
if (remaining_space <= 0){
// Write -1 size tag indicating wraparound
*(int64_t*)p = -1;
// Invalidate all readers that are beyond the write pointer
// TODO: should we handle the case where a new reader shows up while this is running?
for (uint64_t i = 0; i < num_readers; i++){
uint64_t read_pointer = *q->read_pointers[i];
uint64_t read_cycles = read_pointer >> 32;
read_pointer &= 0xFFFFFFFF;
if ((read_pointer > write_pointer) && (read_cycles != write_cycles)) {
*q->read_valids[i] = false;
}
}
// Update global and local copies of write pointer and write_cycles
write_pointer = 0;
write_cycles = write_cycles + 1;
PACK64(*q->write_pointer, write_cycles, write_pointer);
// Set actual pointer to the beginning of the data segment
p = q->data;
}
// Invalidate readers that are in the area that will be written
uint64_t start = write_pointer;
uint64_t end = ALIGN(start + sizeof(int64_t) + msg->size);
for (uint64_t i = 0; i < num_readers; i++){
uint32_t read_cycles, read_pointer;
UNPACK64(read_cycles, read_pointer, *q->read_pointers[i]);
if ((read_pointer >= start) && (read_pointer < end) && (read_cycles != write_cycles)) {
*q->read_valids[i] = false;
}
}
// Write size tag
std::atomic<int64_t> *size_p = reinterpret_cast<std::atomic<int64_t>*>(p);
*size_p = msg->size;
// Copy data
memcpy(p + sizeof(int64_t), msg->data, msg->size);
__sync_synchronize();
// Update write pointer
uint32_t new_ptr = ALIGN(write_pointer + msg->size + sizeof(int64_t));
PACK64(*q->write_pointer, write_cycles, new_ptr);
// Notify readers
for (uint64_t i = 0; i < num_readers; i++){
uint64_t reader_uid = *q->read_uids[i];
syscall(SYS_tkill, reader_uid & 0xFFFFFFFF, SIGUSR1);
}
return msg->size;
}
int msgq_msg_ready(msgq_queue_t * q){
start:
int id = q->reader_id;
assert(id >= 0); // Make sure subscriber is initialized
if (q->read_uid_local != *q->read_uids[id]){
std::cout << q->endpoint << ": Reader was evicted, reconnecting" << std::endl;
msgq_init_subscriber(q);
goto start;
}
// Check valid
if (!*q->read_valids[id]){
msgq_reset_reader(q);
goto start;
}
uint32_t read_cycles, read_pointer;
UNPACK64(read_cycles, read_pointer, *q->read_pointers[id]);
uint32_t write_cycles, write_pointer;
UNPACK64(write_cycles, write_pointer, *q->write_pointer);
// Check if new message is available
return (read_pointer != write_pointer);
}
int msgq_msg_recv(msgq_msg_t * msg, msgq_queue_t * q){
start:
int id = q->reader_id;
assert(id >= 0); // Make sure subscriber is initialized
if (q->read_uid_local != *q->read_uids[id]){
std::cout << q->endpoint << ": Reader was evicted, reconnecting" << std::endl;
msgq_init_subscriber(q);
goto start;
}
// Check valid
if (!*q->read_valids[id]){
msgq_reset_reader(q);
goto start;
}
uint32_t read_cycles, read_pointer;
UNPACK64(read_cycles, read_pointer, *q->read_pointers[id]);
uint32_t write_cycles, write_pointer;
UNPACK64(write_cycles, write_pointer, *q->write_pointer);
char * p = q->data + read_pointer;
// Check if new message is available
if (read_pointer == write_pointer) {
msg->size = 0;
return 0;
}
// Read potential message size
std::atomic<int64_t> *size_p = reinterpret_cast<std::atomic<int64_t>*>(p);
std::int64_t size = *size_p;
// Check if the size that was read is valid
if (!*q->read_valids[id]){
msgq_reset_reader(q);
goto start;
}
// If size is -1 the buffer was full, and we need to wrap around
if (size == -1){
read_cycles++;
PACK64(*q->read_pointers[id], read_cycles, 0);
goto start;
}
// crashing is better than passing garbage data to the consumer
// the size will have weird value if it was overwritten by data accidentally
assert((uint64_t)size < q->size);
assert(size > 0);
uint32_t new_read_pointer = ALIGN(read_pointer + sizeof(std::int64_t) + size);
// If conflate is true, check if this is the latest message, else start over
if (q->read_conflate){
if (new_read_pointer != write_pointer){
// Update read pointer
PACK64(*q->read_pointers[id], read_cycles, new_read_pointer);
goto start;
}
}
// Copy message
if (msgq_msg_init_size(msg, size) < 0)
return -1;
__sync_synchronize();
memcpy(msg->data, p + sizeof(int64_t), size);
__sync_synchronize();
// Update read pointer
PACK64(*q->read_pointers[id], read_cycles, new_read_pointer);
// Check if the actual data that was copied is valid
if (!*q->read_valids[id]){
msgq_msg_close(msg);
msgq_reset_reader(q);
goto start;
}
return msg->size;
}
int msgq_poll(msgq_pollitem_t * items, size_t nitems, int timeout){
assert(timeout >= 0);
int num = 0;
// Check if messages ready
for (size_t i = 0; i < nitems; i++) {
items[i].revents = msgq_msg_ready(items[i].q);
if (items[i].revents) num++;
}
int ms = (timeout == -1) ? 100 : timeout;
struct timespec ts;
ts.tv_sec = ms / 1000;
ts.tv_nsec = (ms % 1000) * 1000 * 1000;
while (num == 0) {
int ret;
ret = nanosleep(&ts, &ts);
// Check if messages ready
for (size_t i = 0; i < nitems; i++) {
if (items[i].revents == 0 && msgq_msg_ready(items[i].q)){
num += 1;
items[i].revents = 1;
}
}
// exit if we had a timeout and the sleep finished
if (timeout != -1 && ret == 0){
break;
}
}
return num;
}
-66
View File
@@ -1,66 +0,0 @@
#pragma once
#include <cstdint>
#include <cstring>
#include <string>
#include <atomic>
#define DEFAULT_SEGMENT_SIZE (10 * 1024 * 1024)
#define NUM_READERS 8
#define ALIGN(n) ((n + (8 - 1)) & -8)
#define UNPACK64(higher, lower, input) do {uint64_t tmp = input; higher = tmp >> 32; lower = tmp & 0xFFFFFFFF;} while (0)
#define PACK64(output, higher, lower) output = ((uint64_t)higher << 32 ) | ((uint64_t)lower & 0xFFFFFFFF)
struct msgq_header_t {
uint64_t num_readers;
uint64_t write_pointer;
uint64_t write_uid;
uint64_t read_pointers[NUM_READERS];
uint64_t read_valids[NUM_READERS];
uint64_t read_uids[NUM_READERS];
};
struct msgq_queue_t {
std::atomic<uint64_t> *num_readers;
std::atomic<uint64_t> *write_pointer;
std::atomic<uint64_t> *write_uid;
std::atomic<uint64_t> *read_pointers[NUM_READERS];
std::atomic<uint64_t> *read_valids[NUM_READERS];
std::atomic<uint64_t> *read_uids[NUM_READERS];
char * mmap_p;
char * data;
size_t size;
int reader_id;
uint64_t read_uid_local;
uint64_t write_uid_local;
bool read_conflate;
std::string endpoint;
};
struct msgq_msg_t {
size_t size;
char * data;
};
struct msgq_pollitem_t {
msgq_queue_t *q;
int revents;
};
void msgq_wait_for_subscriber(msgq_queue_t *q);
void msgq_reset_reader(msgq_queue_t *q);
int msgq_msg_init_size(msgq_msg_t *msg, size_t size);
int msgq_msg_init_data(msgq_msg_t *msg, char * data, size_t size);
int msgq_msg_close(msgq_msg_t *msg);
int msgq_new_queue(msgq_queue_t * q, const char * path, size_t size);
void msgq_close_queue(msgq_queue_t *q);
void msgq_init_publisher(msgq_queue_t * q);
void msgq_init_subscriber(msgq_queue_t * q);
int msgq_msg_send(msgq_msg_t *msg, msgq_queue_t *q);
int msgq_msg_recv(msgq_msg_t *msg, msgq_queue_t *q);
int msgq_msg_ready(msgq_queue_t * q);
int msgq_poll(msgq_pollitem_t * items, size_t nitems, int timeout);
-56
View File
@@ -1,56 +0,0 @@
# MSGQ: A lock free single producer multi consumer message queue
[![Build Status](https://dev.azure.com/commaai/default/_apis/build/status/commaai.msgq?branchName=master)](https://dev.azure.com/commaai/default/_build/latest?definitionId=21&branchName=master)
## What is MSGQ?
MSGQ is a system to pass messages from a single producer to multiple consumers. All the consumers need to be able to receive all the messages. It is designed to be a high performance replacement for ZMQ-like SUB/PUB patterns. It uses a ring buffer in shared memory to efficiently read and write data. Each read requires a copy. Writing can be done without a copy, as long as the size of the data is known in advance.
## Storage
The storage for the queue consists of an area of metadata, and the actual buffer. The metadata contains:
1. A counter to the number of readers that are active
2. A pointer to the head of the queue for writing. From now on referred to as *write pointer*
3. A cycle counter for the writer. This counter is incremented when the writer wraps around
4. N pointers, pointing to the current read position for all the readers. From now on referred to as *read pointer*
5. N counters, counting the number of cycles for all the readers
6. N booleans, indicating validity for all the readers. From now on referred to as *validity flag*
The counter and the pointer are both 32 bit values, packed into 64 bit so they can be read and written atomically.
The data buffer is a ring buffer. All messages are prefixed by an 8 byte size field, followed by the data. A size of -1 indicates a wrap-around, and means the next message is stored at the beginning of the buffer.
## Writing
Writing involves the following steps:
1. Check if the area that is to be written overlaps with any of the read pointers, mark those readers as invalid by clearing the validity flag.
2. Write the message
3. Increase the write pointer by the size of the message
In case there is not enough space at the end of the buffer, a special empty message with a prefix of -1 is written. The cycle counter is incremented by one. In this case step 1 will check there are no read pointers pointing to the remainder of the buffer. Then another write cycle will start with the actual message.
There always needs to be 8 bytes of empty space at the end of the buffer. By doing this there is always space to write the -1.
## Reset reader
When the reader is lagging too much behind the read pointer becomes invalid and no longer points to the beginning of a valid message. To reset a reader to the current write pointer, the following steps are performed:
1. Set valid flag
2. Set read cycle counter to that of the writer
3. Set read pointer to write pointer
## Reading
Reading involves the following steps:
1. Read the size field at the current read pointer
2. Read the validity flag
3. Copy the data out of the buffer
4. Increase the read pointer by the size of the message
5. Check the validity flag again
Before starting the copy, the valid flag is checked. This is to prevent a race condition where the size prefix was invalid, and the read could read outside of the buffer. Make sure that step 1 and 2 are not reordered by your compiler or CPU.
If a writer overwrites the data while it's being copied out, the data will be invalid. Therefore the validity flag is also checked after reading it. The order of step 4 and 5 does not matter.
If at steps 2 or 5 the validity flag is not set, the reader is reset. Any data that was already read is discarded. After the reader is reset, the reading starts from the beginning.
If a message with size -1 is encountered, step 3 and 4 are replaced by increasing the cycle counter and setting the read pointer to the beginning of the buffer. After that another read is performed.
-395
View File
@@ -1,395 +0,0 @@
#include "catch2/catch.hpp"
#include "msgq.hpp"
TEST_CASE("ALIGN"){
REQUIRE(ALIGN(0) == 0);
REQUIRE(ALIGN(1) == 8);
REQUIRE(ALIGN(7) == 8);
REQUIRE(ALIGN(8) == 8);
REQUIRE(ALIGN(99999) == 100000);
}
TEST_CASE("msgq_msg_init_size"){
const size_t msg_size = 30;
msgq_msg_t msg;
msgq_msg_init_size(&msg, msg_size);
REQUIRE(msg.size == msg_size);
msgq_msg_close(&msg);
}
TEST_CASE("msgq_msg_init_data"){
const size_t msg_size = 30;
char * data = new char[msg_size];
for (size_t i = 0; i < msg_size; i++){
data[i] = i;
}
msgq_msg_t msg;
msgq_msg_init_data(&msg, data, msg_size);
REQUIRE(msg.size == msg_size);
REQUIRE(memcmp(msg.data, data, msg_size) == 0);
delete[] data;
msgq_msg_close(&msg);
}
TEST_CASE("msgq_init_subscriber"){
remove("/dev/shm/test_queue");
msgq_queue_t q;
msgq_new_queue(&q, "test_queue", 1024);
REQUIRE(*q.num_readers == 0);
q.reader_id = 1;
*q.read_valids[0] = false;
*q.read_pointers[0] = ((uint64_t)1 << 32);
*q.write_pointer = 255;
msgq_init_subscriber(&q);
REQUIRE(q.read_conflate == false);
REQUIRE(*q.read_valids[0] == true);
REQUIRE((*q.read_pointers[0] >> 32) == 0);
REQUIRE((*q.read_pointers[0] & 0xFFFFFFFF) == 255);
}
TEST_CASE("msgq_msg_send first message"){
remove("/dev/shm/test_queue");
msgq_queue_t q;
msgq_new_queue(&q, "test_queue", 1024);
msgq_init_publisher(&q);
REQUIRE(*q.write_pointer == 0);
size_t msg_size = 128;
SECTION("Aligned message size"){
}
SECTION("Unaligned message size"){
msg_size--;
}
char * data = new char[msg_size];
for (size_t i = 0; i < msg_size; i++){
data[i] = i;
}
msgq_msg_t msg;
msgq_msg_init_data(&msg, data, msg_size);
msgq_msg_send(&msg, &q);
REQUIRE(*(int64_t*)q.data == msg_size); // Check size tag
REQUIRE(*q.write_pointer == 128 + sizeof(int64_t));
REQUIRE(memcmp(q.data + sizeof(int64_t), data, msg_size) == 0);
delete[] data;
msgq_msg_close(&msg);
}
TEST_CASE("msgq_msg_send test wraparound"){
remove("/dev/shm/test_queue");
msgq_queue_t q;
msgq_new_queue(&q, "test_queue", 1024);
msgq_init_publisher(&q);
REQUIRE((*q.write_pointer & 0xFFFFFFFF) == 0);
REQUIRE((*q.write_pointer >> 32) == 0);
const size_t msg_size = 120;
msgq_msg_t msg;
msgq_msg_init_size(&msg, msg_size);
for (int i = 0; i < 8; i++) {
msgq_msg_send(&msg, &q);
}
// Check 8th message was written at the beginning
REQUIRE((*q.write_pointer & 0xFFFFFFFF) == msg_size + sizeof(int64_t));
// Check cycle count
REQUIRE((*q.write_pointer >> 32) == 1);
// Check wraparound tag
char * tag_location = q.data;
tag_location += 7 * (msg_size + sizeof(int64_t));
REQUIRE(*(int64_t*)tag_location == -1);
msgq_msg_close(&msg);
}
TEST_CASE("msgq_msg_recv test wraparound"){
remove("/dev/shm/test_queue");
msgq_queue_t q_pub, q_sub;
msgq_new_queue(&q_pub, "test_queue", 1024);
msgq_new_queue(&q_sub, "test_queue", 1024);
msgq_init_publisher(&q_pub);
msgq_init_subscriber(&q_sub);
REQUIRE((*q_pub.write_pointer >> 32) == 0);
REQUIRE((*q_sub.read_pointers[0] >> 32) == 0);
const size_t msg_size = 120;
msgq_msg_t msg1;
msgq_msg_init_size(&msg1, msg_size);
SECTION("Check cycle counter after reset") {
for (int i = 0; i < 8; i++) {
msgq_msg_send(&msg1, &q_pub);
}
msgq_msg_t msg2;
msgq_msg_recv(&msg2, &q_sub);
REQUIRE(msg2.size == 0); // Reader had to reset
msgq_msg_close(&msg2);
}
SECTION("Check cycle counter while keeping up with writer") {
for (int i = 0; i < 8; i++) {
msgq_msg_send(&msg1, &q_pub);
msgq_msg_t msg2;
msgq_msg_recv(&msg2, &q_sub);
REQUIRE(msg2.size > 0);
msgq_msg_close(&msg2);
}
}
REQUIRE((*q_sub.read_pointers[0] >> 32) == 1);
msgq_msg_close(&msg1);
}
TEST_CASE("msgq_msg_send test invalidation"){
remove("/dev/shm/test_queue");
msgq_queue_t q_pub, q_sub;
msgq_new_queue(&q_pub, "test_queue", 1024);
msgq_new_queue(&q_sub, "test_queue", 1024);
msgq_init_publisher(&q_pub);
msgq_init_subscriber(&q_sub);
*q_sub.write_pointer = (uint64_t)1 << 32;
REQUIRE(*q_sub.read_valids[0] == true);
SECTION("read pointer in tag"){
*q_sub.read_pointers[0] = 0;
}
SECTION("read pointer in data section"){
*q_sub.read_pointers[0] = 64;
}
SECTION("read pointer in wraparound section"){
*q_pub.write_pointer = ((uint64_t)1 << 32) | 1000; // Writer is one cycle ahead
*q_sub.read_pointers[0] = 1020;
}
msgq_msg_t msg;
msgq_msg_init_size(&msg, 128);
msgq_msg_send(&msg, &q_pub);
REQUIRE(*q_sub.read_valids[0] == false);
msgq_msg_close(&msg);
}
TEST_CASE("msgq_init_subscriber init 2 subscribers"){
remove("/dev/shm/test_queue");
msgq_queue_t q1, q2;
msgq_new_queue(&q1, "test_queue", 1024);
msgq_new_queue(&q2, "test_queue", 1024);
*q1.num_readers = 0;
REQUIRE(*q1.num_readers == 0);
REQUIRE(*q2.num_readers == 0);
msgq_init_subscriber(&q1);
REQUIRE(*q1.num_readers == 1);
REQUIRE(*q2.num_readers == 1);
REQUIRE(q1.reader_id == 0);
msgq_init_subscriber(&q2);
REQUIRE(*q1.num_readers == 2);
REQUIRE(*q2.num_readers == 2);
REQUIRE(q2.reader_id == 1);
}
TEST_CASE("Write 1 msg, read 1 msg", "[integration]"){
remove("/dev/shm/test_queue");
const size_t msg_size = 128;
msgq_queue_t writer, reader;
msgq_new_queue(&writer, "test_queue", 1024);
msgq_new_queue(&reader, "test_queue", 1024);
msgq_init_publisher(&writer);
msgq_init_subscriber(&reader);
// Build 128 byte message
msgq_msg_t outgoing_msg;
msgq_msg_init_size(&outgoing_msg, msg_size);
for (size_t i = 0; i < msg_size; i++){
outgoing_msg.data[i] = i;
}
REQUIRE(msgq_msg_send(&outgoing_msg, &writer) == msg_size);
msgq_msg_t incoming_msg1;
REQUIRE(msgq_msg_recv(&incoming_msg1, &reader) == msg_size);
REQUIRE(memcmp(incoming_msg1.data, outgoing_msg.data, msg_size) == 0);
// Verify that there are no more messages
msgq_msg_t incoming_msg2;
REQUIRE(msgq_msg_recv(&incoming_msg2, &reader) == 0);
msgq_msg_close(&outgoing_msg);
msgq_msg_close(&incoming_msg1);
msgq_msg_close(&incoming_msg2);
}
TEST_CASE("Write 2 msg, read 2 msg - conflate = false", "[integration]"){
remove("/dev/shm/test_queue");
const size_t msg_size = 128;
msgq_queue_t writer, reader;
msgq_new_queue(&writer, "test_queue", 1024);
msgq_new_queue(&reader, "test_queue", 1024);
msgq_init_publisher(&writer);
msgq_init_subscriber(&reader);
// Build 128 byte message
msgq_msg_t outgoing_msg;
msgq_msg_init_size(&outgoing_msg, msg_size);
for (size_t i = 0; i < msg_size; i++){
outgoing_msg.data[i] = i;
}
REQUIRE(msgq_msg_send(&outgoing_msg, &writer) == msg_size);
REQUIRE(msgq_msg_send(&outgoing_msg, &writer) == msg_size);
msgq_msg_t incoming_msg1;
REQUIRE(msgq_msg_recv(&incoming_msg1, &reader) == msg_size);
REQUIRE(memcmp(incoming_msg1.data, outgoing_msg.data, msg_size) == 0);
msgq_msg_t incoming_msg2;
REQUIRE(msgq_msg_recv(&incoming_msg2, &reader) == msg_size);
REQUIRE(memcmp(incoming_msg2.data, outgoing_msg.data, msg_size) == 0);
msgq_msg_close(&outgoing_msg);
msgq_msg_close(&incoming_msg1);
msgq_msg_close(&incoming_msg2);
}
TEST_CASE("Write 2 msg, read 2 msg - conflate = true", "[integration]"){
remove("/dev/shm/test_queue");
const size_t msg_size = 128;
msgq_queue_t writer, reader;
msgq_new_queue(&writer, "test_queue", 1024);
msgq_new_queue(&reader, "test_queue", 1024);
msgq_init_publisher(&writer);
msgq_init_subscriber(&reader);
reader.read_conflate = true;
// Build 128 byte message
msgq_msg_t outgoing_msg;
msgq_msg_init_size(&outgoing_msg, msg_size);
for (size_t i = 0; i < msg_size; i++){
outgoing_msg.data[i] = i;
}
REQUIRE(msgq_msg_send(&outgoing_msg, &writer) == msg_size);
REQUIRE(msgq_msg_send(&outgoing_msg, &writer) == msg_size);
msgq_msg_t incoming_msg1;
REQUIRE(msgq_msg_recv(&incoming_msg1, &reader) == msg_size);
REQUIRE(memcmp(incoming_msg1.data, outgoing_msg.data, msg_size) == 0);
// Verify that there are no more messages
msgq_msg_t incoming_msg2;
REQUIRE(msgq_msg_recv(&incoming_msg2, &reader) == 0);
msgq_msg_close(&outgoing_msg);
msgq_msg_close(&incoming_msg1);
msgq_msg_close(&incoming_msg2);
}
TEST_CASE("1 publisher, 1 slow subscriber", "[integration]"){
remove("/dev/shm/test_queue");
msgq_queue_t writer, reader;
msgq_new_queue(&writer, "test_queue", 1024);
msgq_new_queue(&reader, "test_queue", 1024);
msgq_init_publisher(&writer);
msgq_init_subscriber(&reader);
int n_received = 0;
int n_skipped = 0;
for (uint64_t i = 0; i < 1e5; i++) {
msgq_msg_t outgoing_msg;
msgq_msg_init_data(&outgoing_msg, (char*)&i, sizeof(uint64_t));
msgq_msg_send(&outgoing_msg, &writer);
msgq_msg_close(&outgoing_msg);
if (i % 10 == 0){
msgq_msg_t msg1;
msgq_msg_recv(&msg1, &reader);
if (msg1.size == 0){
n_skipped++;
} else {
n_received++;
}
msgq_msg_close(&msg1);
}
}
// TODO: verify these numbers by hand
REQUIRE(n_received == 8572);
REQUIRE(n_skipped == 1428);
}
TEST_CASE("1 publisher, 2 subscribers", "[integration]"){
remove("/dev/shm/test_queue");
msgq_queue_t writer, reader1, reader2;
msgq_new_queue(&writer, "test_queue", 1024);
msgq_new_queue(&reader1, "test_queue", 1024);
msgq_new_queue(&reader2, "test_queue", 1024);
msgq_init_publisher(&writer);
msgq_init_subscriber(&reader1);
msgq_init_subscriber(&reader2);
for (uint64_t i = 0; i < 1024 * 3; i++) {
msgq_msg_t outgoing_msg;
msgq_msg_init_data(&outgoing_msg, (char*)&i, sizeof(uint64_t));
msgq_msg_send(&outgoing_msg, &writer);
msgq_msg_t msg1, msg2;
msgq_msg_recv(&msg1, &reader1);
msgq_msg_recv(&msg2, &reader2);
REQUIRE(msg1.size == sizeof(uint64_t));
REQUIRE(msg2.size == sizeof(uint64_t));
REQUIRE(*(uint64_t*)msg1.data == i);
REQUIRE(*(uint64_t*)msg2.data == i);
msgq_msg_close(&outgoing_msg);
msgq_msg_close(&msg1);
msgq_msg_close(&msg2);
}
}
-14
View File
@@ -1,14 +0,0 @@
from messaging_pyx import Context, SubSocket, PubSocket # pylint: disable=no-name-in-module, import-error
if __name__ == "__main__":
c = Context()
pub_sock = PubSocket()
pub_sock.connect(c, "controlsState")
for i in range(int(1e10)):
print(i)
sub_sock = SubSocket()
sub_sock.connect(c, "controlsState")
pub_sock.send(b'a')
print(sub_sock.receive())
-2
View File
@@ -1,2 +0,0 @@
#define CATCH_CONFIG_MAIN
#include "catch2/catch.hpp"
-142
View File
@@ -1,142 +0,0 @@
import unittest
import time
import cereal.messaging as messaging
import concurrent.futures
def poller():
context = messaging.Context()
p = messaging.Poller()
sub = messaging.SubSocket()
sub.connect(context, 'controlsState')
p.registerSocket(sub)
socks = p.poll(10000)
r = [s.receive(non_blocking=True) for s in socks]
return r
class TestPoller(unittest.TestCase):
def test_poll_once(self):
context = messaging.Context()
pub = messaging.PubSocket()
pub.connect(context, 'controlsState')
with concurrent.futures.ThreadPoolExecutor() as e:
poll = e.submit(poller)
time.sleep(0.1) # Slow joiner syndrome
# Send message
pub.send("a")
# Wait for poll result
result = poll.result()
del pub
context.term()
self.assertEqual(result, [b"a"])
def test_poll_and_create_many_subscribers(self):
context = messaging.Context()
pub = messaging.PubSocket()
pub.connect(context, 'controlsState')
with concurrent.futures.ThreadPoolExecutor() as e:
poll = e.submit(poller)
time.sleep(0.1) # Slow joiner syndrome
c = messaging.Context()
for _ in range(10):
messaging.SubSocket().connect(c, 'controlsState')
time.sleep(0.1)
# Send message
pub.send("a")
# Wait for poll result
result = poll.result()
del pub
context.term()
self.assertEqual(result, [b"a"])
def test_multiple_publishers_exception(self):
context = messaging.Context()
with self.assertRaises(messaging.MultiplePublishersError):
pub1 = messaging.PubSocket()
pub1.connect(context, 'controlsState')
pub2 = messaging.PubSocket()
pub2.connect(context, 'controlsState')
pub1.send("a")
del pub1
del pub2
context.term()
def test_multiple_messages(self):
context = messaging.Context()
pub = messaging.PubSocket()
pub.connect(context, 'controlsState')
sub = messaging.SubSocket()
sub.connect(context, 'controlsState')
time.sleep(0.1) # Slow joiner
for i in range(100):
pub.send(str(i))
msg_seen = False
i = 0
while True:
r = sub.receive(non_blocking=True)
if r is not None:
self.assertEqual(str(i), r.decode('utf8'))
msg_seen = True
i += 1
if r is None and msg_seen: # ZMQ sometimes receives nothing on the first receive
break
del pub
del sub
context.term()
def test_conflate(self):
context = messaging.Context()
pub = messaging.PubSocket()
pub.connect(context, 'controlsState')
sub = messaging.SubSocket()
sub.connect(context, 'controlsState', conflate=True)
time.sleep(0.1) # Slow joiner
pub.send('a')
pub.send('b')
self.assertEqual(b'b', sub.receive())
del pub
del sub
context.term()
if __name__ == "__main__":
unittest.main()
-163
View File
@@ -1,163 +0,0 @@
# TODO: these port numbers are hardcoded in c, fix this
# LogRotate: 8001 is a PUSH PULL socket between loggerd and visiond
# all ZMQ pub sub: port, should_log, frequency, (qlog_decimation)
# frame syncing packet
frame: [8002, true, 20., 1]
# accel, gyro, and compass
sensorEvents: [8003, true, 100., 100]
# GPS data, also global timestamp
gpsNMEA: [8004, true, 9.] # 9 msgs each sec
# CPU+MEM+GPU+BAT temps
thermal: [8005, true, 2., 1]
# List(CanData), list of can messages
can: [8006, true, 100.]
controlsState: [8007, true, 100., 100]
#liveEvent: [8008, true, 0.]
model: [8009, true, 20., 5]
features: [8010, true, 0.]
health: [8011, true, 2., 1]
radarState: [8012, true, 20.]
#liveUI: [8014, true, 0.]
encodeIdx: [8015, true, 20.]
liveTracks: [8016, true, 20.]
sendcan: [8017, true, 100.]
logMessage: [8018, true, 0.]
liveCalibration: [8019, true, 5.]
androidLog: [8020, true, 0.]
carState: [8021, true, 100., 10]
# 8022 is reserved for sshd
carControl: [8023, true, 100., 10]
plan: [8024, true, 20.]
liveLocation: [8025, true, 0.]
gpsLocation: [8026, true, 1., 1]
ethernetData: [8027, true, 0.]
navUpdate: [8028, true, 0.]
qcomGnss: [8029, true, 0.]
lidarPts: [8030, true, 0.]
procLog: [8031, true, 0.5]
gpsLocationExternal: [8032, true, 10., 1]
ubloxGnss: [8033, true, 10.]
clocks: [8034, true, 1., 1]
liveMpc: [8035, false, 20.]
liveLongitudinalMpc: [8036, false, 20.]
navStatus: [8038, true, 0.]
gpsLocationTrimble: [8039, true, 0.]
trimbleGnss: [8041, true, 0.]
ubloxRaw: [8042, true, 20.]
gpsPlannerPoints: [8043, true, 0.]
gpsPlannerPlan: [8044, true, 0.]
applanixRaw: [8046, true, 0.]
orbLocation: [8047, true, 0.]
trafficEvents: [8048, true, 0.]
liveLocationTiming: [8049, true, 0.]
orbslamCorrection: [8050, true, 0.]
liveLocationCorrected: [8051, true, 0.]
orbObservation: [8052, true, 0.]
applanixLocation: [8053, true, 0.]
liveLocationKalman: [8054, true, 0.]
uiNavigationEvent: [8055, true, 0.]
orbOdometry: [8057, true, 0.]
orbFeatures: [8058, false, 0.]
orbKeyFrame: [8059, true, 0.]
uiLayoutState: [8060, true, 0.]
frontEncodeIdx: [8061, true, 5.]
orbFeaturesSummary: [8062, true, 0.]
driverMonitoring: [8063, true, 5., 1]
liveParameters: [8064, true, 10.]
liveMapData: [8065, true, 0.]
cameraOdometry: [8066, true, 5.]
pathPlan: [8067, true, 20.]
kalmanOdometry: [8068, true, 0.]
thumbnail: [8069, true, 0.2, 1]
carEvents: [8070, true, 1., 1]
carParams: [8071, true, 0.02, 1]
frontFrame: [8072, true, 10.]
testModel: [8040, false, 0.]
testLiveLocation: [8045, false, 0.]
testJoystick: [8056, false, 0.]
# 8080 is reserved for slave testing daemon
# 8762 is reserved for logserver
# manager -- base process to manage starting and stopping of all others
# subscribes: thermal
# **** processes that communicate with the outside world ****
# thermald -- decides when to start and stop onroad
# subscribes: health, location
# publishes: thermal
# boardd -- communicates with the car
# subscribes: sendcan
# publishes: can, health, ubloxRaw
# sensord -- publishes IMU and Magnetometer
# publishes: sensorEvents
# gpsd -- publishes EON's gps
# publishes: gpsNMEA
# visiond -- talks to the cameras, runs the model, saves the videos
# publishes: frame, model, driverMonitoring, cameraOdometry, thumbnail
# **** stateful data transformers ****
# plannerd -- decides where to drive the car
# subscribes: carState, model, radarState, controlsState, liveParameters
# publishes: plan, pathPlan, liveMpc, liveLongitudinalMpc
# controlsd -- drives the car by sending CAN messages to panda
# subscribes: can, thermal, health, plan, pathPlan, driverMonitoring, liveCalibration
# publishes: carState, carControl, sendcan, controlsState, carEvents, carParams
# radard -- processes the radar and vision data
# subscribes: can, controlsState, model, liveParameters
# publishes: radarState, liveTracks
# params_learner -- learns vehicle params by observing the vehicle dynamics
# subscribes: controlsState, sensorEvents, cameraOdometry
# publishes: liveParameters
# calibrationd -- reads posenet and applies a temporal filter on the frame region to look at
# subscribes: cameraOdometry
# publishes: liveCalibration
# ubloxd -- read raw ublox data and converts them in readable format
# subscribes: ubloxRaw
# publishes: ubloxGnss
# **** LOGGING SERVICE ****
# loggerd
# subscribes: EVERYTHING
# **** NON VITAL SERVICES ****
# ui
# subscribes: thermal, model, controlsState, uiLayout, liveCalibration, radarState, liveMpc, plusFrame, liveMapData
# uploader
# communicates through file system with loggerd
# deleter
# communicates through file system with loggerd and uploader
# logmessaged -- central logging service, can log to cloud
# publishes: logMessage
# logcatd -- fetches logcat info from android
# publishes: androidLog
# proclogd -- fetches process information
# publishes: procLog
# tombstoned -- reports native crashes
# athenad -- on request, open a sub socket and return the value
# updated -- waits for network access and tries to update every hour
-33
View File
@@ -1,33 +0,0 @@
#!/usr/bin/env python3
import os
import yaml
class Service():
def __init__(self, port, should_log, frequency, decimation=None):
self.port = port
self.should_log = should_log
self.frequency = frequency
self.decimation = decimation
service_list_path = os.path.join(os.path.dirname(__file__), "service_list.yaml")
service_list = {}
with open(service_list_path, "r") as f:
for k, v in yaml.safe_load(f).items():
decimation = None
if len(v) == 4:
decimation = v[3]
service_list[k] = Service(v[0], v[1], v[2], decimation)
if __name__ == "__main__":
print("/* THIS IS AN AUTOGENERATED FILE, PLEASE EDIT service_list.yaml */")
print("#ifndef __SERVICES_H")
print("#define __SERVICES_H")
print("struct service { int port; bool should_log; int frequency; int decimation; char name[0x100]; };")
print("static struct service services[] = {")
for k, v in service_list.items():
print(' { .name = "%s", .port = %d, .should_log = %s, .frequency = %d, .decimation = %d },' % (k, v.port, "true" if v.should_log else "false", v.frequency, -1 if v.decimation is None else v.decimation))
print("};")
print("#endif")
-12
View File
@@ -1,12 +0,0 @@
#!/bin/bash
# Only pyflakes checks (--select=F)
flake8 --select=F $(find . -iname "*.py" | grep -vi "^\./pyextra.*" | grep -vi "^\./panda")
RESULT=$?
if [ $RESULT -eq 0 ]; then
pylint $(find . -iname "*.py" | grep -vi "^\./pyextra.*" | grep -vi "^\./panda")
RESULT=$? & 3
fi
[ $RESULT -ne 0 ] && exit 1
exit 0
+72 -15
View File
@@ -1,23 +1,40 @@
import os
import binascii
import itertools
import re
import struct
import subprocess
import random
from cereal import log
NetworkType = log.ThermalData.NetworkType
ANDROID = os.path.isfile('/EON')
def getprop(key):
if not ANDROID:
return ""
return subprocess.check_output(["getprop", key], encoding='utf8').strip()
def get_imei():
ret = getprop("oem.device.imeicache")
if ret == "":
ret = "000000000000000"
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():
return getprop("ro.serialno")
ret = getprop("ro.serialno")
if ret == "":
ret = "cccccccc"
return ret
def get_subscriber_info():
ret = parse_service_call_string(["iphonesubinfo", "7"])
ret = parse_service_call_string(service_call(["iphonesubinfo", "7"]))
if ret is None or len(ret) < 8:
return ""
return ret
@@ -35,15 +52,23 @@ def reboot(reason=None):
"i32", "1" # wait
])
def parse_service_call_unpack(call, fmt):
r = parse_service_call_bytes(call)
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(call):
r = parse_service_call_bytes(call)
def parse_service_call_string(r):
try:
r = r[8:] # Cut off length field
r = r.decode('utf_16_be')
@@ -59,11 +84,7 @@ def parse_service_call_string(call):
except Exception:
return None
def parse_service_call_bytes(call):
ret = subprocess.check_output(["service", "call", *call], encoding='utf8').strip()
if 'Parcel' not in ret:
return None
def parse_service_call_bytes(ret):
try:
r = b""
for hex_part in re.findall(r'[ (]([0-9a-f]{8})', ret):
@@ -71,3 +92,39 @@ def parse_service_call_bytes(call):
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)
+2 -2
View File
@@ -1,13 +1,13 @@
import jwt
import requests
from datetime import datetime, timedelta
from common.basedir import PERSIST
from selfdrive.version import version
class Api():
def __init__(self, dongle_id):
self.dongle_id = dongle_id
with open('/persist/comma/id_rsa') as f:
with open(PERSIST+'/comma/id_rsa') as f:
self.private_key = f.read()
def get(self, *args, **kwargs):
+1
View File
@@ -32,6 +32,7 @@ def start_frame():
def set_package_permissions():
pm_grant("ai.comma.plus.offroad", "android.permission.ACCESS_FINE_LOCATION")
pm_grant("ai.comma.plus.offroad", "android.permission.READ_PHONE_STATE")
appops_set("ai.comma.plus.offroad", "SU", "allow")
appops_set("ai.comma.plus.offroad", "WIFI_SCAN", "allow")
appops_set("ai.comma.plus.offroad", "READ_EXTERNAL_STORAGE", "allow")
+7
View File
@@ -1,4 +1,11 @@
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:
PERSIST = os.path.join(BASEDIR, "persist")
PARAMS = os.path.join(BASEDIR, "persist", "params")
+15 -9
View File
@@ -1,16 +1,22 @@
from posix.time cimport clock_gettime, timespec, CLOCK_BOOTTIME, CLOCK_MONOTONIC_RAW
from posix.time cimport clock_gettime, timespec, CLOCK_MONOTONIC_RAW, clockid_t
cdef double readclock(int clock_id):
cdef timespec ts
cdef double current
IF UNAME_SYSNAME == "Darwin":
# Darwin doesn't have a CLOCK_BOOTTIME
CLOCK_BOOTTIME = CLOCK_MONOTONIC_RAW
ELSE:
from posix.time cimport CLOCK_BOOTTIME
clock_gettime(clock_id, &ts)
current = ts.tv_sec + (ts.tv_nsec / 1000000000.)
return current
cdef double readclock(clockid_t clock_id):
cdef timespec ts
cdef double current
clock_gettime(clock_id, &ts)
current = ts.tv_sec + (ts.tv_nsec / 1000000000.)
return current
def monotonic_time():
return readclock(CLOCK_MONOTONIC_RAW)
return readclock(CLOCK_MONOTONIC_RAW)
def sec_since_boot():
return readclock(CLOCK_BOOTTIME)
return readclock(CLOCK_BOOTTIME)
+150
View File
@@ -0,0 +1,150 @@
import os
import numpy as np
import collections
from contextlib import closing
from common.file_helpers import mkdirs_exists_ok
class ColumnStoreReader():
def __init__(self, path, mmap=False, allow_pickle=False, direct_io=False):
if not (path and os.path.isdir(path)):
raise ValueError("Not a column store: {}".format(path))
self._path = os.path.realpath(path)
self._keys = os.listdir(self._path)
self._mmap = mmap
self._allow_pickle = allow_pickle
self._direct_io = direct_io
@property
def path(self):
return self._path
def close(self):
pass
def get(self, key):
try:
return self[key]
except KeyError:
return None
def keys(self):
return list(self._keys)
def iteritems(self):
for k in self:
yield (k, self[k])
def itervalues(self):
for k in self:
yield self[k]
def get_npy_path(self, key):
"""Gets a filesystem path for an npy file containing the specified array,
or none if the column store does not contain key.
"""
if key in self:
return os.path.join(self._path, key)
else:
return None
def __getitem__(self, key):
try:
path = os.path.join(self._path, key)
# TODO(mgraczyk): This implementation will need to change for zip.
if os.path.isdir(path):
return ColumnStoreReader(path)
else:
if self._mmap:
# note that direct i/o does nothing for mmap since file read/write interface is not used
ret = np.load(path, mmap_mode='r', allow_pickle=self._allow_pickle, fix_imports=False)
else:
if self._direct_io:
opener = lambda path, flags: os.open(path, os.O_RDONLY | os.O_DIRECT)
with open(path, 'rb', buffering=0, opener=opener) as f:
ret = np.load(f, allow_pickle=self._allow_pickle, fix_imports=False)
else:
ret = np.load(path, allow_pickle=self._allow_pickle, fix_imports=False)
if type(ret) == np.lib.npyio.NpzFile:
# if it's saved as compressed, it has arr_0 only in the file. deref this
return ret['arr_0']
else:
return ret
except IOError:
raise KeyError(key)
def __contains__(self, item):
try:
self[item]
return True
except KeyError:
return False
def __len__(self):
return len(self._keys)
def __bool__(self):
return bool(self._keys)
def __iter__(self):
return iter(self._keys)
def __str__(self):
return "ColumnStoreReader({})".format(str({k: "..." for k in self._keys}))
def __enter__(self): return self
def __exit__(self, type, value, traceback): self.close()
class ColumnStoreWriter():
def __init__(self, path, allow_pickle=False):
self._path = path
self._allow_pickle = allow_pickle
mkdirs_exists_ok(self._path)
def map_column(self, path, dtype, shape):
npy_path = os.path.join(self._path, path)
mkdirs_exists_ok(os.path.dirname(npy_path))
return np.lib.format.open_memmap(npy_path, mode='w+', dtype=dtype, shape=shape)
def add_column(self, path, data, dtype=None, compression=False, overwrite=False):
npy_path = os.path.join(self._path, path)
mkdirs_exists_ok(os.path.dirname(npy_path))
if overwrite:
f = open(npy_path, "wb")
else:
f = os.fdopen(os.open(npy_path, os.O_WRONLY | os.O_CREAT | os.O_EXCL), "wb")
with closing(f) as f:
data2 = np.array(data, copy=False, dtype=dtype)
if compression:
np.savez_compressed(f, data2)
else:
np.save(f, data2, allow_pickle=self._allow_pickle, fix_imports=False)
def add_group(self, group_name):
# TODO(mgraczyk): This implementation will need to change if we add zip or compression.
return ColumnStoreWriter(os.path.join(self._path, group_name))
def close(self):
pass
def __enter__(self): return self
def __exit__(self, type, value, traceback): self.close()
def _save_dict_as_column_store(values, writer):
for k, v in values.items():
if isinstance(v, collections.Mapping):
_save_dict_as_column_store(v, writer.add_group(k))
else:
writer.add_column(k, v)
def save_dict_as_column_store(values, output_path):
with ColumnStoreWriter(output_path) as writer:
_save_dict_as_column_store(values, writer)
+3
View File
@@ -0,0 +1,3 @@
# py2,3 compatiblity helpers
basestring = (str, bytes)
+6
View File
@@ -2,8 +2,14 @@ import os
import sys
import fcntl
import hashlib
import platform
from cffi import FFI
def suffix():
if platform.system() == "Darwin":
return ".dylib"
else:
return ".so"
def ffi_wrap(name, c_code, c_header, tmpdir="/tmp/ccache", cflags="", libraries=None):
if libraries is None:
+1
View File
@@ -0,0 +1 @@
+24
View File
@@ -0,0 +1,24 @@
import numpy as np
import os
import csv
path = os.path.dirname(os.path.abspath(__file__))
# locally cache cities from
# https://github.com/thampiman/reverse-geocoder
csv_file_name = path + '/rg_cities1000.csv'
# right hand drive is when the steering wheel is on the right of the car
# left hand traffic is when cars driver on the left side of the road
LHT_COUNTRIES = ['AU', 'IN', 'IE', 'JP', 'MU', 'MY', 'NZ', 'UK', 'ZA']
def get_city(lat, lon):
cities = np.array(list(csv.reader(open(csv_file_name))))[1:]
positions = cities[:,:2].astype(np.float32)
idx = np.argmin(np.linalg.norm((positions - np.array([lat, lon])), axis=1))
return cities[idx]
def is_lht(lat, lon):
city = get_city(lat, lon)
country = city[-1]
return country in LHT_COUNTRIES
File diff suppressed because it is too large Load Diff
+12
View File
@@ -0,0 +1,12 @@
class lazy_property():
"""Defines a property whose value will be computed only once and as needed.
This can only be used on instance methods.
"""
def __init__(self, func):
self._func = func
def __get__(self, obj_self, cls):
value = self._func(obj_self)
setattr(obj_self, self._func.__name__, value)
return value
+284
View File
@@ -0,0 +1,284 @@
import os
import struct
import bisect
import numpy as np
import _io
import capnp
from cereal import log as capnp_log
class RawData():
def __init__(self, f):
self.f = _io.FileIO(f, 'rb')
self.lenn = struct.unpack("I", self.f.read(4))[0]
self.count = os.path.getsize(f) / (self.lenn+4)
def read(self, i):
self.f.seek((self.lenn+4)*i + 4)
return self.f.read(self.lenn)
def yuv420_to_rgb(raw, image_dim=None, swizzled=False):
def expand(x):
x = np.repeat(x, 2, axis=0)
return np.repeat(x, 2, axis=1)
if image_dim is None:
image_dim = (raw.shape[1]*2, raw.shape[2]*2)
swizzled = True
if not swizzled:
img_data = np.array(raw, copy=False, dtype=np.uint8)
uv_len = (image_dim[0]/2)*(image_dim[1]/2)
img_data_u = expand(img_data[image_dim[0]*image_dim[1]: \
image_dim[0]*image_dim[1]+uv_len]. \
reshape(image_dim[0]/2, image_dim[1]/2))
img_data_v = expand(img_data[image_dim[0]*image_dim[1]+uv_len: \
image_dim[0]*image_dim[1]+2*uv_len]. \
reshape(image_dim[0]/2, image_dim[1]/2))
img_data_y = img_data[0:image_dim[0]*image_dim[1]].reshape(image_dim)
else:
img_data_y = np.zeros(image_dim, dtype=np.uint8)
img_data_y[0::2, 0::2] = raw[0]
img_data_y[1::2, 0::2] = raw[1]
img_data_y[0::2, 1::2] = raw[2]
img_data_y[1::2, 1::2] = raw[3]
img_data_u = expand(raw[4])
img_data_v = expand(raw[5])
yuv = np.stack((img_data_y, img_data_u, img_data_v)).swapaxes(0,2).swapaxes(0,1)
yuv = yuv.astype(np.int16)
# http://maxsharabayko.blogspot.com/2016/01/fast-yuv-to-rgb-conversion-in-python-3.html
# according to ITU-R BT.709
yuv[:,:, 0] = yuv[:,:, 0].clip(16, 235).astype(yuv.dtype) - 16
yuv[:,:,1:] = yuv[:,:,1:].clip(16, 240).astype(yuv.dtype) - 128
A = np.array([[1.164, 0.000, 1.793],
[1.164, -0.213, -0.533],
[1.164, 2.112, 0.000]])
# our result
img = np.dot(yuv, A.T).clip(0, 255).astype('uint8')
return img
class YuvData():
def __init__(self, f, dim=(160,320)):
self.f = _io.FileIO(f, 'rb')
self.image_dim = dim
self.image_size = self.image_dim[0]/2 * self.image_dim[1]/2 * 6
self.count = os.path.getsize(f) / self.image_size
def read_frame(self, frame):
self.f.seek(self.image_size*frame)
raw = self.f.read(self.image_size)
return raw
def read_frames(self, range_start, range_len):
self.f.seek(self.image_size*range_start)
raw = self.f.read(self.image_size*range_len)
return raw
def read_frames_into(self, range_start, buf):
self.f.seek(self.image_size*range_start)
return self.f.readinto(buf)
def read(self, frame):
return yuv420_to_rgb(self.read_frame(frame), self.image_dim)
def close(self):
self.f.close()
def __enter__(self):
return self
def __exit__(self, type, value, traceback):
self.close()
class OneReader():
def __init__(self, base_path, goofy=False, segment_range=None):
self.base_path = base_path
route_name = os.path.basename(base_path)
self.rcamera_size = (304, 560)
if segment_range is None:
parent_path = os.path.dirname(base_path)
self.segment_nums = []
for p in os.listdir(parent_path):
if not p.startswith(route_name+"--"):
continue
self.segment_nums.append(int(p.rsplit("--", 1)[-1]))
if not self.segment_nums:
raise Exception("no route segments found")
self.segment_nums.sort()
self.segment_range = (self.segment_nums[0], self.segment_nums[-1])
else:
self.segment_range = segment_range
self.segment_nums = range(segment_range[0], segment_range[1]+1)
for i in self.segment_nums:
if not os.path.exists(base_path+"--"+str(i)):
raise Exception("missing segment in provided range")
# goofy data is broken with discontinuous logs
if goofy and (self.segment_range[0] != 0
or self.segment_nums != range(self.segment_range[0], self.segment_range[1]+1)):
raise Exception("goofy data needs all the segments for a route")
self.cur_seg = None
self.cur_seg_f = None
# index the frames
print("indexing frames {}...".format(self.segment_nums))
self.rcamera_encode_map = {} # frame_id -> (segment num, segment id, frame_time)
last_frame_id = -1
if goofy:
# goofy is goofy
frame_size = self.rcamera_size[0]*self.rcamera_size[1]*3/2
# find the encode id ranges for each segment by using the rcamera file size
segment_encode_ids = []
cur_encode_id = 0
for n in self.segment_nums:
camera_path = os.path.join(self.seg_path(n), "rcamera")
if not os.path.exists(camera_path):
# for goofy, missing camera files means a bad route
raise Exception("Missing camera file {}".format(camera_path))
camera_size = os.path.getsize(camera_path)
assert (camera_size % frame_size) == 0
num_frames = camera_size / frame_size
segment_encode_ids.append(cur_encode_id)
cur_encode_id += num_frames
last_encode_id = -1
# use the segment encode id map and frame events to build the frame index
for n in self.segment_nums:
log_path = os.path.join(self.seg_path(n), "rlog")
if os.path.exists(log_path):
with open(log_path, "rb") as f:
for evt in capnp_log.Event.read_multiple(f):
if evt.which() == 'frame':
if evt.frame.frameId < last_frame_id:
# a non-increasing frame id is bad route (eg visiond was restarted)
raise Exception("non-increasing frame id")
last_frame_id = evt.frame.frameId
seg_i = bisect.bisect_right(segment_encode_ids, evt.frame.encodeId)-1
assert seg_i >= 0
seg_num = self.segment_nums[seg_i]
seg_id = evt.frame.encodeId-segment_encode_ids[seg_i]
frame_time = evt.logMonoTime / 1.0e9
self.rcamera_encode_map[evt.frame.frameId] = (seg_num, seg_id,
frame_time)
last_encode_id = evt.frame.encodeId
if last_encode_id-cur_encode_id > 10:
# too many missing frames is a bad route (eg route from before encoder rotating worked)
raise Exception("goofy route is missing frames: {}, {}".format(
last_encode_id, cur_encode_id))
else:
# for harry data, build the index from encodeIdx events
for n in self.segment_nums:
log_path = os.path.join(self.seg_path(n), "rlog")
if os.path.exists(log_path):
with open(log_path, "rb") as f:
for evt in capnp_log.Event.read_multiple(f):
if evt.which() == 'encodeIdx' and evt.encodeIdx.type == 'bigBoxLossless':
frame_time = evt.logMonoTime / 1.0e9
self.rcamera_encode_map[evt.encodeIdx.frameId] = (
evt.encodeIdx.segmentNum, evt.encodeIdx.segmentId,
frame_time)
print("done")
# read the first event to find the start time
self.reset_to_seg(self.segment_range[0])
for evt in self.events():
if evt.which() != 'initData':
self.start_mono = evt.logMonoTime
break
self.reset_to_seg(self.segment_range[0])
def seg_path(self, num):
return self.base_path+"--"+str(num)
def reset_to_seg(self, seg):
self.cur_seg = seg
if self.cur_seg_f:
self.cur_seg_f.close()
self.cur_seg_f = None
def seek_ts(self, ts):
seek_seg = int(ts/60)
if seek_seg < self.segment_range[0] or seek_seg > self.segment_range[1]:
raise ValueError
self.reset_to_seg(seek_seg)
target_mono = self.start_mono + int(ts*1e9)
for evt in self.events():
if evt.logMonoTime >= target_mono:
break
def read_event(self):
while True:
if self.cur_seg > self.segment_range[1]:
return None
if self.cur_seg_f is None:
log_path = os.path.join(self.seg_path(self.cur_seg), "rlog")
if not os.path.exists(log_path):
print("missing log file!", log_path)
self.cur_seg += 1
continue
self.cur_seg_f = open(log_path, "rb")
try:
return capnp_log.Event.read(self.cur_seg_f)
except capnp.lib.capnp.KjException as e:
if 'EOF' in str(e): # dumb, but pycapnp does this too
self.cur_seg_f.close()
self.cur_seg_f = None
self.cur_seg += 1
else:
raise
def events(self):
while True:
r = self.read_event()
if r is None:
break
yield r
def read_frame(self, frame_id):
encode_idx = self.rcamera_encode_map.get(frame_id)
if encode_idx is None:
return None
seg_num, seg_id, _ = encode_idx
camera_path = os.path.join(self.seg_path(seg_num), "rcamera")
if not os.path.exists(camera_path):
return None
with YuvData(camera_path, self.rcamera_size) as data:
return data.read_frame(seg_id)
def close(self):
if self.cur_seg_f is not None:
self.cur_seg_f.close()
def __enter__(self):
return self
def __exit__(self, type, value, traceback):
self.close()
+92
View File
@@ -0,0 +1,92 @@
import sys
import json
# pip2 install msgpack-python
import msgpack
import zlib
import os
import logging
from Crypto.Cipher import AES
ext = ".gz"
SWAG = '\xde\xe2\x11\x15VVC\xf2\x8ep\xd7\xe4\x87\x8d,9'
def compress_json(in_file, out_file):
logging.debug("compressing %s -> %s", in_file, out_file)
errors = 0
good = []
last_can_time = 0
with open(in_file, 'r') as inf:
for ln in inf:
ln = ln.rstrip()
if not ln: continue
try:
ll = json.loads(ln)
except ValueError:
errors += 1
continue
if ll is None or ll[0] is None:
continue
if ll[0][1] == 1:
# no CAN in hex
ll[1][2] = ll[1][2].decode("hex")
# relativize the CAN timestamps
this_can_time = ll[1][1]
ll[1] = [ll[1][0], this_can_time - last_can_time, ll[1][2]]
last_can_time = this_can_time
good.append(ll)
logging.debug("compressing %s -> %s, read done", in_file, out_file)
data = msgpack.packb(good)
data_compressed = zlib.compress(data)
# zlib doesn't care about this
data_compressed += "\x00" * (16 - len(data_compressed)%16)
aes = AES.new(SWAG, AES.MODE_CBC, "\x00"*16)
data_encrypted = aes.encrypt(data_compressed)
with open(out_file, "wb") as outf:
outf.write(data_encrypted)
logging.debug("compressing %s -> %s, write done", in_file, out_file)
return errors
def decompress_json_internal(data_encrypted):
aes = AES.new(SWAG, AES.MODE_CBC, "\x00"*16)
data_compressed = aes.decrypt(data_encrypted)
data = zlib.decompress(data_compressed)
msgs = msgpack.unpackb(data)
good = []
last_can_time = 0
for ll in msgs:
if ll[0][1] == 1:
# back into hex
ll[1][2] = ll[1][2].encode("hex")
# derelativize CAN timestamps
last_can_time += ll[1][1]
ll[1] = [ll[1][0], last_can_time, ll[1][2]]
good.append(ll)
return good
def decompress_json(in_file, out_file):
logging.debug("decompressing %s -> %s", in_file, out_file)
f = open(in_file)
data_encrypted = f.read()
f.close()
good = decompress_json_internal(data_encrypted)
out = '\n'.join(map(lambda x: json.dumps(x), good)) + "\n"
logging.debug("decompressing %s -> %s, writing", in_file, out_file)
f = open(out_file, 'w')
f.write(out)
f.close()
logging.debug("decompressing %s -> %s, write finished", in_file, out_file)
if __name__ == "__main__":
for dat in sys.argv[1:]:
print(dat)
compress_json(dat, "/tmp/out"+ext)
decompress_json("/tmp/out"+ext, "/tmp/test")
os.system("diff "+dat+" /tmp/test")
+38 -22
View File
@@ -1,9 +1,11 @@
import io
import os
import sys
import copy
import json
import socket
import logging
import traceback
from threading import local
from collections import OrderedDict
from contextlib import contextmanager
@@ -78,28 +80,6 @@ class SwagLogger(logging.Logger):
self.log_local = local()
self.log_local.ctx = {}
def findCaller(self, stack_info=None):
"""
Find the stack frame of the caller so that we can note the source
file name, line number and function name.
"""
# f = currentframe()
f = sys._getframe(3)
#On some versions of IronPython, currentframe() returns None if
#IronPython isn't run with -X:Frames.
if f is not None:
f = f.f_back
rv = "(unknown file)", 0, "(unknown function)"
while hasattr(f, "f_code"):
co = f.f_code
filename = os.path.normcase(co.co_filename)
if filename in (logging._srcfile, _srcfile):
f = f.f_back
continue
rv = (co.co_filename, f.f_lineno, co.co_name)
break
return rv
def local_ctx(self):
try:
return self.log_local.ctx
@@ -140,6 +120,42 @@ class SwagLogger(logging.Logger):
else:
self.info(evt)
def findCaller(self, stack_info=False, stacklevel=1):
"""
Find the stack frame of the caller so that we can note the source
file name, line number and function name.
"""
f = sys._getframe(3)
#On some versions of IronPython, currentframe() returns None if
#IronPython isn't run with -X:Frames.
if f is not None:
f = f.f_back
orig_f = f
while f and stacklevel > 1:
f = f.f_back
stacklevel -= 1
if not f:
f = orig_f
rv = "(unknown file)", 0, "(unknown function)", None
while hasattr(f, "f_code"):
co = f.f_code
filename = os.path.normcase(co.co_filename)
if filename == _srcfile:
f = f.f_back
continue
sinfo = None
if stack_info:
sio = io.StringIO()
sio.write('Stack (most recent call last):\n')
traceback.print_stack(f, file=sio)
sinfo = sio.getvalue()
if sinfo[-1] == '\n':
sinfo = sinfo[:-1]
sio.close()
rv = (co.co_filename, f.f_lineno, co.co_name, sinfo)
break
return rv
if __name__ == "__main__":
log = SwagLogger()
+66
View File
@@ -0,0 +1,66 @@
import bisect
import numpy as np
from scipy.interpolate import interp1d
def deep_interp_0_fast(dx, x, y):
FIX = False
if len(y.shape) == 1:
y = y[:, None]
FIX = True
ret = np.zeros((dx.shape[0], y.shape[1]))
index = list(x)
for i in range(dx.shape[0]):
idx = bisect.bisect_left(index, dx[i])
if idx == x.shape[0]:
idx = x.shape[0] - 1
ret[i] = y[idx]
if FIX:
return ret[:, 0]
else:
return ret
def running_mean(x, N):
cumsum = np.cumsum(np.insert(x, [0]*(int(N/2)) + [-1]*(N-int(N/2)), [x[0]]*int(N/2) + [x[-1]]*(N-int(N/2))))
return (cumsum[N:] - cumsum[:-N]) / N
def deep_interp_np(x, xp, fp):
x = np.atleast_1d(x)
xp = np.array(xp)
if len(xp) < 2:
return np.repeat(fp, len(x), axis=0)
if min(np.diff(xp)) < 0:
raise RuntimeError('Bad x array for interpolation')
j = np.searchsorted(xp, x) - 1
j = np.clip(j, 0, len(xp)-2)
d = np.divide(x - xp[j], xp[j + 1] - xp[j], out=np.ones_like(x, dtype=np.float64), where=xp[j + 1] - xp[j] != 0)
vals_interp = (fp[j].T*(1 - d)).T + (fp[j + 1].T*d).T
if len(vals_interp) == 1:
return vals_interp[0]
else:
return vals_interp
def clipping_deep_interp(x, xp, fp):
if len(xp) < 2:
return deep_interp_np(x, xp, fp)
bad_idx = np.where(np.diff(xp) < 0)[0]
if len(bad_idx) > 0:
if bad_idx[0] ==1:
return np.zeros([] + list(fp.shape[1:]))
return deep_interp_np(x, xp[:bad_idx[0]], fp[:bad_idx[0]])
else:
return deep_interp_np(x, xp, fp)
def deep_interp(dx, x, y, kind="slinear"):
return interp1d(
x, y,
axis=0,
kind=kind,
bounds_error=False,
fill_value="extrapolate",
assume_sorted=True)(dx)
+11 -3
View File
@@ -29,7 +29,7 @@ import fcntl
import tempfile
import threading
from enum import Enum
from common.basedir import PARAMS
def mkdirs_exists_ok(path):
try:
@@ -54,6 +54,7 @@ keys = {
"AthenadPid": [TxType.PERSISTENT],
"CalibrationParams": [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],
@@ -69,9 +70,10 @@ keys = {
"IsLdwEnabled": [TxType.PERSISTENT],
"IsGeofenceEnabled": [TxType.PERSISTENT],
"IsMetric": [TxType.PERSISTENT],
"IsOffroad": [TxType.CLEAR_ON_MANAGER_START],
"IsRHD": [TxType.PERSISTENT],
"IsTakingSnapshot": [TxType.CLEAR_ON_MANAGER_START],
"IsUpdateAvailable": [TxType.PERSISTENT],
"IsUpdateAvailable": [TxType.CLEAR_ON_MANAGER_START],
"IsUploadRawEnabled": [TxType.PERSISTENT],
"LastUpdateTime": [TxType.PERSISTENT],
"LimitSetSpeed": [TxType.PERSISTENT],
@@ -80,6 +82,7 @@ keys = {
"LongitudinalControl": [TxType.PERSISTENT],
"OpenpilotEnabledToggle": [TxType.PERSISTENT],
"PandaFirmware": [TxType.CLEAR_ON_MANAGER_START, TxType.CLEAR_ON_PANDA_DISCONNECT],
"PandaFirmwareHex": [TxType.CLEAR_ON_MANAGER_START, TxType.CLEAR_ON_PANDA_DISCONNECT],
"PandaDongleId": [TxType.CLEAR_ON_MANAGER_START, TxType.CLEAR_ON_PANDA_DISCONNECT],
"Passive": [TxType.PERSISTENT],
"RecordFront": [TxType.PERSISTENT],
@@ -317,7 +320,7 @@ def write_db(params_path, key, value):
lock.release()
class Params():
def __init__(self, db='/data/params'):
def __init__(self, db=PARAMS):
self.db = db
# create the database if it doesn't exist...
@@ -325,6 +328,11 @@ class Params():
with self.transaction(write=True):
pass
def clear_all(self):
shutil.rmtree(self.db, ignore_errors=True)
with self.transaction(write=True):
pass
def transaction(self, write=False):
if write:
return DBWriter(self.db)
+163
View File
@@ -0,0 +1,163 @@
#!/usr/bin/env python3
# Copyright (C) 2016 Sixten Bergman
# License WTFPL
#
# This program is free software. It comes without any warranty, to the extent
# permitted by applicable law.
# You can redistribute it and/or modify it under the terms of the Do What The
# Fuck You Want To Public License, Version 2, as published by Sam Hocevar. See
# http://www.wtfpl.net/ for more details.
#
# note that the function peakdetect is derived from code which was released to
# public domain see: http://billauer.co.il/peakdet.html
#
from math import log
import numpy as np
__all__ = ["peakdetect"]
def _datacheck_peakdetect(x_axis, y_axis):
if x_axis is None:
x_axis = range(len(y_axis))
if len(y_axis) != len(x_axis):
raise ValueError("Input vectors y_axis and x_axis must have same length")
#needs to be a numpy array
y_axis = np.array(y_axis)
x_axis = np.array(x_axis)
return x_axis, y_axis
def _pad(fft_data, pad_len):
"""
Pads fft data to interpolate in time domain
keyword arguments:
fft_data -- the fft
pad_len -- By how many times the time resolution should be increased by
return: padded list
"""
length = len(fft_data)
n = _n(length * pad_len)
fft_data = list(fft_data)
return fft_data[:length // 2] + [0] * (2**n-length) + fft_data[length // 2:]
def _n(x):
"""
Find the smallest value for n, which fulfils 2**n >= x
keyword arguments:
x -- the value, which 2**n must surpass
return: the integer n
"""
return int(log(x)/log(2)) + 1
def peakdetect(y_axis, x_axis=None, lookahead=200, delta=0):
"""
Converted from/based on a MATLAB script at:
http://billauer.co.il/peakdet.html
function for detecting local maxima and minima in a signal.
Discovers peaks by searching for values which are surrounded by lower
or larger values for maxima and minima respectively
keyword arguments:
y_axis -- A list containing the signal over which to find peaks
x_axis -- A x-axis whose values correspond to the y_axis list and is used
in the return to specify the position of the peaks. If omitted an
index of the y_axis is used.
(default: None)
lookahead -- distance to look ahead from a peak candidate to determine if
it is the actual peak
(default: 200)
'(samples / period) / f' where '4 >= f >= 1.25' might be a good value
delta -- this specifies a minimum difference between a peak and
the following points, before a peak may be considered a peak. Useful
to hinder the function from picking up false peaks towards to end of
the signal. To work well delta should be set to delta >= RMSnoise * 5.
(default: 0)
When omitted delta function causes a 20% decrease in speed.
When used Correctly it can double the speed of the function
return: two lists [max_peaks, min_peaks] containing the positive and
negative peaks respectively. Each cell of the lists contains a tuple
of: (position, peak_value)
to get the average peak value do: np.mean(max_peaks, 0)[1] on the
results to unpack one of the lists into x, y coordinates do:
x, y = zip(*max_peaks)
"""
max_peaks = []
min_peaks = []
dump = [] # Used to pop the first hit which almost always is false
# check input data
x_axis, y_axis = _datacheck_peakdetect(x_axis, y_axis)
# store data length for later use
length = len(y_axis)
#perform some checks
if lookahead < 1:
raise ValueError("Lookahead must be '1' or above in value")
if not (np.isscalar(delta) and delta >= 0):
raise ValueError("delta must be a positive number")
#maxima and minima candidates are temporarily stored in
#mx and mn respectively
mn, mx = np.Inf, -np.Inf
#Only detect peak if there is 'lookahead' amount of points after it
for index, (x, y) in enumerate(zip(x_axis[:-lookahead],
y_axis[:-lookahead])):
if y > mx:
mx = y
mxpos = x
if y < mn:
mn = y
mnpos = x
####look for max####
if y < mx-delta and mx != np.Inf:
#Maxima peak candidate found
#look ahead in signal to ensure that this is a peak and not jitter
if y_axis[index:index+lookahead].max() < mx:
max_peaks.append([mxpos, mx])
dump.append(True)
#set algorithm to only find minima now
mx = np.Inf
mn = np.Inf
if index+lookahead >= length:
#end is within lookahead no more peaks can be found
break
continue
#else: #slows shit down this does
# mx = ahead
# mxpos = x_axis[np.where(y_axis[index:index+lookahead]==mx)]
####look for min####
if y > mn+delta and mn != -np.Inf:
#Minima peak candidate found
#look ahead in signal to ensure that this is a peak and not jitter
if y_axis[index:index+lookahead].min() > mn:
min_peaks.append([mnpos, mn])
dump.append(False)
#set algorithm to only find maxima now
mn = -np.Inf
mx = -np.Inf
if index+lookahead >= length:
#end is within lookahead no more peaks can be found
break
#else: #slows shit down this does
# mn = ahead
# mnpos = x_axis[np.where(y_axis[index:index+lookahead]==mn)]
#Remove the false hit on the first value of the y_axis
try:
if dump[0]:
max_peaks.pop(0)
else:
min_peaks.pop(0)
del dump
except IndexError:
#no peaks were found, should the function return empty lists?
pass
return [max_peaks, min_peaks]
+78
View File
@@ -0,0 +1,78 @@
import os
import numpy as np
import random
class SamplingBuffer():
def __init__(self, fn, size, write=False):
self._fn = fn
self._write = write
if self._write:
self._f = open(self._fn, "ab")
else:
self._f = open(self._fn, "rb")
self._size = size
self._refresh()
def _refresh(self):
self.cnt = os.path.getsize(self._fn) / self._size
@property
def count(self):
self._refresh()
return self.cnt
def _fetch_one(self, x):
assert self._write == False
self._f.seek(x*self._size)
return self._f.read(self._size)
def sample(self, count, indices = None):
if indices == None:
cnt = self.count
assert cnt != 0
indices = map(lambda x: random.randint(0, cnt-1), range(count))
return map(self._fetch_one, indices)
def write(self, dat):
assert self._write == True
assert (len(dat) % self._size) == 0
self._f.write(dat)
self._f.flush()
class NumpySamplingBuffer():
def __init__(self, fn, size, dtype, write=False):
self._size = size
self._dtype = dtype
self._buf = SamplingBuffer(fn, len(np.zeros(size, dtype=dtype).tobytes()), write)
@property
def count(self):
return self._buf.count
def write(self, dat):
self._buf.write(dat.tobytes())
def sample(self, count, indices = None):
return np.fromstring(''.join(self._buf.sample(count, indices)), dtype=self._dtype).reshape([count]+list(self._size))
# TODO: n IOPS needed where n is the Multi
class MultiNumpySamplingBuffer():
def __init__(self, fn, npa, write=False):
self._bufs = []
for i,n in enumerate(npa):
self._bufs.append(NumpySamplingBuffer(fn + ("_%d" % i), n[0], n[1], write))
def write(self, dat):
for b,x in zip(self._bufs, dat):
b.write(x)
@property
def count(self):
return min(map(lambda x: x.count, self._bufs))
def sample(self, count):
cnt = self.count
assert cnt != 0
indices = map(lambda x: random.randint(0, cnt-1), range(count))
return map(lambda x: x.sample(count, indices), self._bufs)
+17 -7
View File
@@ -5,21 +5,31 @@ from common.basedir import BASEDIR
class Spinner():
def __init__(self):
self.spinner_proc = subprocess.Popen(["./spinner"],
stdin=subprocess.PIPE,
cwd=os.path.join(BASEDIR, "selfdrive", "ui", "spinner"),
close_fds=True)
try:
self.spinner_proc = subprocess.Popen(["./spinner"],
stdin=subprocess.PIPE,
cwd=os.path.join(BASEDIR, "selfdrive", "ui", "spinner"),
close_fds=True)
except OSError:
self.spinner_proc = None
def __enter__(self):
return self
def update(self, spinner_text):
self.spinner_proc.stdin.write(spinner_text.encode('utf8') + b"\n")
self.spinner_proc.stdin.flush()
if self.spinner_proc is not None:
self.spinner_proc.stdin.write(spinner_text.encode('utf8') + b"\n")
try:
self.spinner_proc.stdin.flush()
except BrokenPipeError:
pass
def close(self):
if self.spinner_proc is not None:
self.spinner_proc.stdin.close()
try:
self.spinner_proc.stdin.close()
except BrokenPipeError:
pass
self.spinner_proc.terminate()
self.spinner_proc = None
+94
View File
@@ -0,0 +1,94 @@
import numpy as np
_DESC_FMT = """
{} (n={}):
MEAN={}
VAR={}
MIN={}
MAX={}
"""
class StatTracker():
def __init__(self, name):
self._name = name
self._mean = 0.
self._var = 0.
self._n = 0
self._min = -float("-inf")
self._max = -float("inf")
@property
def mean(self):
return self._mean
@property
def var(self):
return (self._n * self._var) / (self._n - 1.)
@property
def min(self):
return self._min
@property
def max(self):
return self._max
def update(self, samples):
# https://en.wikipedia.org/wiki/Algorithms_for_calculating_variance#Parallel_algorithm
data = samples.reshape(-1)
n_a = data.size
mean_a = np.mean(data)
var_a = np.var(data, ddof=0)
n_b = self._n
mean_b = self._mean
delta = mean_b - mean_a
m_a = var_a * (n_a - 1)
m_b = self._var * (n_b - 1)
m2 = m_a + m_b + delta**2 * n_a * n_b / (n_a + n_b)
self._var = m2 / (n_a + n_b)
self._mean = (n_a * mean_a + n_b * mean_b) / (n_a + n_b)
self._n = n_a + n_b
self._min = min(self._min, np.min(data))
self._max = max(self._max, np.max(data))
def __str__(self):
return _DESC_FMT.format(self._name, self._n, self._mean, self.var, self._min,
self._max)
# FIXME(mgraczyk): The variance computation does not work with 1 sample batches.
class VectorStatTracker(StatTracker):
def __init__(self, name, dim):
self._name = name
self._mean = np.zeros((dim, ))
self._var = np.zeros((dim, dim))
self._n = 0
self._min = np.full((dim, ), -float("-inf"))
self._max = np.full((dim, ), -float("inf"))
@property
def cov(self):
return self.var
def update(self, samples):
n_a = samples.shape[0]
mean_a = np.mean(samples, axis=0)
var_a = np.cov(samples, ddof=0, rowvar=False)
n_b = self._n
mean_b = self._mean
delta = mean_b - mean_a
m_a = var_a * (n_a - 1)
m_b = self._var * (n_b - 1)
m2 = m_a + m_b + delta**2 * n_a * n_b / (n_a + n_b)
self._var = m2 / (n_a + n_b)
self._mean = (n_a * mean_a + n_b * mean_b) / (n_a + n_b)
self._n = n_a + n_b
self._min = np.minimum(self._min, np.min(samples, axis=0))
self._max = np.maximum(self._max, np.max(samples, axis=0))
+6
View File
@@ -0,0 +1,6 @@
def replace_right(s, old, new, occurrence):
# replace_right('1232425', '2', ' ', 1) -> '12324 5'
# replace_right('1232425', '2', ' ', 2) -> '123 4 5'
split = s.rsplit(old, occurrence)
return new.join(split)
+28
View File
@@ -0,0 +1,28 @@
import os
import unittest
from uuid import uuid4
from common.file_helpers import atomic_write_on_fs_tmp
from common.file_helpers import atomic_write_in_dir
class TestFileHelpers(unittest.TestCase):
def run_atomic_write_func(self, atomic_write_func):
path = "/tmp/tmp{}".format(uuid4())
with atomic_write_func(path) as f:
f.write("test")
with open(path) as f:
self.assertEqual(f.read(), "test")
self.assertEqual(os.stat(path).st_mode & 0o777, 0o644)
os.remove(path)
def test_atomic_write_on_fs_tmp(self):
self.run_atomic_write_func(atomic_write_on_fs_tmp)
def test_atomic_write_in_dir(self):
self.run_atomic_write_func(atomic_write_in_dir)
if __name__ == "__main__":
unittest.main()
+27
View File
@@ -0,0 +1,27 @@
import numpy as np
import unittest
import timeit
from common.numpy_fast import interp
class InterpTest(unittest.TestCase):
def test_correctness_controls(self):
_A_CRUISE_MIN_BP = np.asarray([0., 5., 10., 20., 40.])
_A_CRUISE_MIN_V = np.asarray([-1.0, -.8, -.67, -.5, -.30])
v_ego_arr = [-1, -1e-12, 0, 4, 5, 6, 7, 10, 11, 15.2, 20, 21, 39,
39.999999, 40, 41]
expected = np.interp(v_ego_arr, _A_CRUISE_MIN_BP, _A_CRUISE_MIN_V)
actual = interp(v_ego_arr, _A_CRUISE_MIN_BP, _A_CRUISE_MIN_V)
np.testing.assert_equal(actual, expected)
for v_ego in v_ego_arr:
expected = np.interp(v_ego, _A_CRUISE_MIN_BP, _A_CRUISE_MIN_V)
actual = interp(v_ego, _A_CRUISE_MIN_BP, _A_CRUISE_MIN_V)
np.testing.assert_equal(actual, expected)
if __name__ == "__main__":
unittest.main()
+63
View File
@@ -0,0 +1,63 @@
from common.params import Params, UnknownKeyName
import threading
import time
import tempfile
import shutil
import unittest
class TestParams(unittest.TestCase):
def setUp(self):
self.tmpdir = tempfile.mkdtemp()
print("using", self.tmpdir)
self.params = Params(self.tmpdir)
def tearDown(self):
shutil.rmtree(self.tmpdir)
def test_params_put_and_get(self):
self.params.put("DongleId", "cb38263377b873ee")
assert self.params.get("DongleId") == b"cb38263377b873ee"
def test_params_non_ascii(self):
st = b"\xe1\x90\xff"
self.params.put("CarParams", st)
assert self.params.get("CarParams") == st
def test_params_get_cleared_panda_disconnect(self):
self.params.put("CarParams", "test")
self.params.put("DongleId", "cb38263377b873ee")
assert self.params.get("CarParams") == b"test"
self.params.panda_disconnect()
assert self.params.get("CarParams") is None
assert self.params.get("DongleId") is not None
def test_params_get_cleared_manager_start(self):
self.params.put("CarParams", "test")
self.params.put("DongleId", "cb38263377b873ee")
assert self.params.get("CarParams") == b"test"
self.params.manager_start()
assert self.params.get("CarParams") is None
assert self.params.get("DongleId") is not None
def test_params_two_things(self):
self.params.put("DongleId", "bob")
self.params.put("AccessToken", "knope")
assert self.params.get("DongleId") == b"bob"
assert self.params.get("AccessToken") == b"knope"
def test_params_get_block(self):
def _delayed_writer():
time.sleep(0.1)
self.params.put("CarParams", "test")
threading.Thread(target=_delayed_writer).start()
assert self.params.get("CarParams") is None
assert self.params.get("CarParams", True) == b"test"
def test_params_unknown_key_fails(self):
with self.assertRaises(UnknownKeyName):
self.params.get("swag")
if __name__ == "__main__":
unittest.main()
+61
View File
@@ -0,0 +1,61 @@
Reference Frames
------
Many reference frames are used throughout. This
folder contains all helper functions needed to
transform between them. Generally this is done
by generating a rotation matrix and multiplying.
| Name | [x, y, z] | Units | Notes |
| :-------------: |:-------------:| :-----:| :----: |
| Geodetic | [Latitude, Longitude, Altitude] | geodetic coordinates | Sometimes used as [lon, lat, alt], avoid this frame. |
| ECEF | [x, y, z] | meters | We use **ITRF14 (IGS14)**, NOT NAD83. <br> This is the global Mesh3D frame. |
| NED | [North, East, Down] | meters | Relative to earth's surface, useful for vizualizing. |
| Device | [Forward, Right, Down] | meters | This is the Mesh3D local frame. <br> Relative to camera, **not imu.** <br> ![img](http://upload.wikimedia.org/wikipedia/commons/thumb/2/2f/RPY_angles_of_airplanes.png/440px-RPY_angles_of_airplanes.png)|
| Road | [Forward, Left, Up] | meters | On the road plane aligned to the vehicle. <br> ![img](https://upload.wikimedia.org/wikipedia/commons/f/f5/RPY_angles_of_cars.png) |
| View | [Right, Down, Forward] | meters | Like device frame, but according to camera conventions. |
| Camera | [u, v, focal] | pixels | Like view frame, but 2d on the camera image.|
| Normalized Camera | [u / focal, v / focal, 1] | / | |
| Model | [u, v, focal] | pixels | The sampled rectangle of the full camera frame the model uses. |
| Normalized Model | [u / focal, v / focal, 1] | / | |
Orientation Conventations
------
Quaternions, rotation matrices and euler angles are three
equivalent representations of orientation and all three are
used throughout the code base.
For euler angles the preferred convention is [roll, pitch, yaw]
which corresponds to rotations around the [x, y, z] axes. All
euler angles should always be in radians or radians/s unless
for plotting or display purposes. For quaternions the hamilton
notations is preferred which is [q<sub>w</sub>, q<sub>x</sub>, q<sub>y</sub>, q<sub>z</sub>]. All quaternions
should always be normalized with a strictly positive q<sub>w</sub>. **These
quaternions are a unique representation of orientation whereas euler angles
or rotation matrices are not.**
To rotate from one frame into another with euler angles the
convention is to rotate around roll, then pitch and then yaw,
while rotating around the rotated axes, not the original axes.
Calibration
------
EONs are not all mounted in the exact same way. To compensate for the effects of this the vision model takes in an image that is "calibrated". This means the image is aligned so the direction of travel of the car when it is going straight and the road is flat is always in the location on the image. This calibration is defined by a pitch and yaw angle that describe the direction of travel vector in device frame.
Example
------
To transform global Mesh3D positions and orientations (positions_ecef, quats_ecef) into the local frame described by the
first position and orientation from Mesh3D one would do:
```
ecef_from_local = rot_from_quat(quats_ecef[0])
local_from_ecef = ecef_from_local.T
positions_local = np.einsum('ij,kj->ki', local_from_ecef, postions_ecef - positions_ecef[0])
rotations_global = rot_from_quat(quats_ecef)
rotations_local = np.einsum('ij,kjl->kil', local_from_ecef, rotations_global)
eulers_local = euler_from_rot(rotations_local)
```
+8 -31
View File
@@ -44,6 +44,7 @@ def get_calib_from_vp(vp):
roll_calib = 0
return roll_calib, pitch_calib, yaw_calib
# aka 'extrinsic_matrix'
# road : x->forward, y -> left, z->up
def get_view_frame_from_road_frame(roll, pitch, yaw, height):
@@ -61,6 +62,13 @@ def vp_from_ke(m):
"""
return (m[0, 0]/m[2,0], m[1,0]/m[2,0])
def vp_from_rpy(rpy):
e = get_view_frame_from_road_frame(rpy[0], rpy[1], rpy[2], 1.22)
ke = np.dot(eon_intrinsics, e)
return vp_from_ke(ke)
def roll_from_ke(m):
# note: different from calibration.h/RollAnglefromKE: i think that one's just wrong
return np.arctan2(-(m[1, 0] - m[1, 1] * m[2, 0] / m[2, 1]),
@@ -123,37 +131,6 @@ def img_from_device(pt_device):
return pt_img.reshape(input_shape)[:,:2]
#TODO please use generic img transform below
def rotate_img(img, eulers, crop=None, intrinsics=eon_intrinsics):
import cv2 # pylint: disable=import-error
size = img.shape[:2]
rot = orient.rot_from_euler(eulers)
quadrangle = np.array([[0, 0],
[size[1]-1, 0],
[0, size[0]-1],
[size[1]-1, size[0]-1]], dtype=np.float32)
quadrangle_norm = np.hstack((normalize(quadrangle, intrinsics=intrinsics), np.ones((4,1))))
warped_quadrangle_full = np.einsum('ij, kj->ki', intrinsics.dot(rot), quadrangle_norm)
warped_quadrangle = np.column_stack((warped_quadrangle_full[:,0]/warped_quadrangle_full[:,2],
warped_quadrangle_full[:,1]/warped_quadrangle_full[:,2])).astype(np.float32)
if crop:
W_border = (size[1] - crop[0])//2
H_border = (size[0] - crop[1])//2
outside_crop = (((warped_quadrangle[:,0] < W_border) |
(warped_quadrangle[:,0] >= size[1] - W_border)) &
((warped_quadrangle[:,1] < H_border) |
(warped_quadrangle[:,1] >= size[0] - H_border)))
if not outside_crop.all():
raise ValueError("warped image not contained inside crop")
else:
H_border, W_border = 0, 0
M = cv2.getPerspectiveTransform(quadrangle, warped_quadrangle)
img_warped = cv2.warpPerspective(img, M, size[::-1])
return img_warped[H_border: size[0] - H_border,
W_border: size[1] - W_border]
def get_camera_frame_from_calib_frame(camera_frame_from_road_frame):
camera_frame_from_ground = camera_frame_from_road_frame[:, (0, 1, 3)]
calib_frame_from_ground = np.dot(eon_intrinsics,
+122
View File
@@ -0,0 +1,122 @@
#!/usr/bin/env python3
import numpy as np
import unittest
import common.transformations.coordinates as coord
geodetic_positions = np.array([[37.7610403, -122.4778699, 115],
[27.4840915, -68.5867592, 2380],
[32.4916858, -113.652821, -6],
[15.1392514, 103.6976037, 24],
[24.2302229, 44.2835412, 1650]])
geodetic_positions_radians = np.array([[0.65905448, -2.13764209, 115],
[0.47968789, -1.19706477, 2380],
[0.5670869, -1.98361593, -6],
[0.26422978, 1.80986461, 24],
[0.42289717, 0.7728936, 1650]])
ecef_positions = np.array([[-2711076.55270557, -4259167.14692758, 3884579.87669935],
[ 2068042.69652729, -5273435.40316622, 2927004.89190746],
[-2160412.60461669, -4932588.89873832, 3406542.29652851],
[-1458247.92550567, 5983060.87496612, 1654984.6099885 ],
[ 4167239.10867871, 4064301.90363223, 2602234.6065749 ]])
ecef_positions_offset = np.array([[-2711004.46961115, -4259099.33540613, 3884605.16002147],
[ 2068074.30639499, -5273413.78835412, 2927012.48741131],
[-2160344.53748176, -4932586.20092211, 3406636.2962545 ],
[-1458211.98517094, 5983151.11161276, 1655077.02698447],
[ 4167271.20055269, 4064398.22619263, 2602238.95265847]])
ned_offsets = np.array([[78.722153649976391, 24.396208657446344, 60.343017506838436],
[10.699003365155221, 37.319278617604269, 4.1084100025050407],
[95.282646251726959, 61.266689955574428, -25.376506058505054],
[68.535769283630003, -56.285970011848889, -100.54840137956515],
[-33.066609321880179, 46.549821994306861, -84.062540548335591]])
ecef_init_batch = np.array([2068042.69652729, -5273435.40316622, 2927004.89190746])
ecef_positions_offset_batch = np.array([[ 2068089.41454771, -5273434.46829148, 2927074.04783672],
[ 2068103.31628647, -5273393.92275431, 2927102.08725987],
[ 2068108.49939636, -5273359.27047121, 2927045.07091581],
[ 2068075.12395611, -5273381.69432566, 2927041.08207992],
[ 2068060.72033399, -5273430.6061505, 2927094.54928305]])
ned_offsets_batch = np.array([[ 53.88103168, 43.83445935, -46.27488057],
[ 93.83378995, 71.57943024, -30.23113187],
[ 57.26725796, 89.05602684, 23.02265814],
[ 49.71775195, 49.79767572, 17.15351015],
[ 78.56272609, 18.53100158, -43.25290759]])
class TestNED(unittest.TestCase):
def test_small_distances(self):
start_geodetic = np.array([33.8042184, -117.888593, 0.0])
local_coord = coord.LocalCoord.from_geodetic(start_geodetic)
start_ned = local_coord.geodetic2ned(start_geodetic)
np.testing.assert_array_equal(start_ned, np.zeros(3,))
west_geodetic = start_geodetic + [0, -0.0005, 0]
west_ned = local_coord.geodetic2ned(west_geodetic)
self.assertLess(np.abs(west_ned[0]), 1e-3)
self.assertLess(west_ned[1], 0)
southwest_geodetic = start_geodetic + [-0.0005, -0.002, 0]
southwest_ned = local_coord.geodetic2ned(southwest_geodetic)
self.assertLess(southwest_ned[0], 0)
self.assertLess(southwest_ned[1], 0)
def test_ecef_geodetic(self):
# testing single
np.testing.assert_allclose(ecef_positions[0], coord.geodetic2ecef(geodetic_positions[0]), rtol=1e-9)
np.testing.assert_allclose(geodetic_positions[0,:2], coord.ecef2geodetic(ecef_positions[0])[:2], rtol=1e-9)
np.testing.assert_allclose(geodetic_positions[0,2], coord.ecef2geodetic(ecef_positions[0])[2], rtol=1e-9, atol=1e-4)
np.testing.assert_allclose(geodetic_positions[:,:2], coord.ecef2geodetic(ecef_positions)[:,:2], rtol=1e-9)
np.testing.assert_allclose(geodetic_positions[:,2], coord.ecef2geodetic(ecef_positions)[:,2], rtol=1e-9, atol=1e-4)
np.testing.assert_allclose(ecef_positions, coord.geodetic2ecef(geodetic_positions), rtol=1e-9)
np.testing.assert_allclose(geodetic_positions_radians[0], coord.ecef2geodetic(ecef_positions[0], radians=True), rtol=1e-5)
np.testing.assert_allclose(geodetic_positions_radians[:,:2], coord.ecef2geodetic(ecef_positions, radians=True)[:,:2], rtol=1e-7)
np.testing.assert_allclose(geodetic_positions_radians[:,2], coord.ecef2geodetic(ecef_positions, radians=True)[:,2], rtol=1e-7, atol=1e-4)
def test_ned(self):
for ecef_pos in ecef_positions:
converter = coord.LocalCoord.from_ecef(ecef_pos)
ecef_pos_moved = ecef_pos + [25, -25, 25]
ecef_pos_moved_double_converted = converter.ned2ecef(converter.ecef2ned(ecef_pos_moved))
np.testing.assert_allclose(ecef_pos_moved, ecef_pos_moved_double_converted, rtol=1e-9)
for geo_pos in geodetic_positions:
converter = coord.LocalCoord.from_geodetic(geo_pos)
geo_pos_moved = geo_pos + np.array([0, 0, 10])
geo_pos_double_converted_moved = converter.ned2geodetic(converter.geodetic2ned(geo_pos) + np.array([0,0,-10]))
np.testing.assert_allclose(geo_pos_moved[:2], geo_pos_double_converted_moved[:2], rtol=1e-9, atol=1e-6)
np.testing.assert_allclose(geo_pos_moved[2], geo_pos_double_converted_moved[2], rtol=1e-9, atol=1e-4)
def test_ned_saved_results(self):
for i, ecef_pos in enumerate(ecef_positions):
converter = coord.LocalCoord.from_ecef(ecef_pos)
np.testing.assert_allclose(converter.ned2ecef(ned_offsets[i]),
ecef_positions_offset[i],
rtol=1e-9, atol=1e-4)
np.testing.assert_allclose(converter.ecef2ned(ecef_positions_offset[i]),
ned_offsets[i],
rtol=1e-9, atol=1e-4)
def test_ned_batch(self):
converter = coord.LocalCoord.from_ecef(ecef_init_batch)
np.testing.assert_allclose(converter.ecef2ned(ecef_positions_offset_batch),
ned_offsets_batch,
rtol=1e-9, atol=1e-7)
np.testing.assert_allclose(converter.ned2ecef(ned_offsets_batch),
ecef_positions_offset_batch,
rtol=1e-9, atol=1e-7)
if __name__ == "__main__":
unittest.main()
+69
View File
@@ -0,0 +1,69 @@
#!/usr/bin/env python3
import numpy as np
import unittest
from common.transformations.orientation import euler2quat, quat2euler, euler2rot, rot2euler, \
rot2quat, quat2rot, \
ned_euler_from_ecef
eulers = np.array([[ 1.46520501, 2.78688383, 2.92780854],
[ 4.86909526, 3.60618161, 4.30648981],
[ 3.72175965, 2.68763705, 5.43895988],
[ 5.92306687, 5.69573614, 0.81100357],
[ 0.67838374, 5.02402037, 2.47106426]])
quats = np.array([[ 0.66855182, -0.71500939, 0.19539353, 0.06017818],
[ 0.43163717, 0.70013301, 0.28209145, 0.49389021],
[ 0.44121991, -0.08252646, 0.34257534, 0.82532207],
[ 0.88578382, -0.04515356, -0.32936046, 0.32383617],
[ 0.06578165, 0.61282835, 0.07126891, 0.78424163]])
ecef_positions = np.array([[-2711076.55270557, -4259167.14692758, 3884579.87669935],
[ 2068042.69652729, -5273435.40316622, 2927004.89190746],
[-2160412.60461669, -4932588.89873832, 3406542.29652851],
[-1458247.92550567, 5983060.87496612, 1654984.6099885 ],
[ 4167239.10867871, 4064301.90363223, 2602234.6065749 ]])
ned_eulers = np.array([[ 0.46806039, -0.4881889 , 1.65697808],
[-2.14525969, -0.36533066, 0.73813479],
[-1.39523364, -0.58540761, -1.77376356],
[-1.84220435, 0.61828016, -1.03310421],
[ 2.50450101, 0.36304151, 0.33136365]])
class TestOrientation(unittest.TestCase):
def test_quat_euler(self):
for i, eul in enumerate(eulers):
np.testing.assert_allclose(quats[i], euler2quat(eul), rtol=1e-7)
np.testing.assert_allclose(quats[i], euler2quat(quat2euler(quats[i])), rtol=1e-6)
for i, eul in enumerate(eulers):
np.testing.assert_allclose(quats[i], euler2quat(list(eul)), rtol=1e-7)
np.testing.assert_allclose(quats[i], euler2quat(quat2euler(list(quats[i]))), rtol=1e-6)
np.testing.assert_allclose(quats, euler2quat(eulers), rtol=1e-7)
np.testing.assert_allclose(quats, euler2quat(quat2euler(quats)), rtol=1e-6)
def test_rot_euler(self):
for eul in eulers:
np.testing.assert_allclose(euler2quat(eul), euler2quat(rot2euler(euler2rot(eul))), rtol=1e-7)
for eul in eulers:
np.testing.assert_allclose(euler2quat(eul), euler2quat(rot2euler(euler2rot(list(eul)))), rtol=1e-7)
np.testing.assert_allclose(euler2quat(eulers), euler2quat(rot2euler(euler2rot(eulers))), rtol=1e-7)
def test_rot_quat(self):
for quat in quats:
np.testing.assert_allclose(quat, rot2quat(quat2rot(quat)), rtol=1e-7)
for quat in quats:
np.testing.assert_allclose(quat, rot2quat(quat2rot(list(quat))), rtol=1e-7)
np.testing.assert_allclose(quats, rot2quat(quat2rot(quats)), rtol=1e-7)
def test_euler_ned(self):
for i in range(len(eulers)):
np.testing.assert_allclose(ned_eulers[i], ned_euler_from_ecef(ecef_positions[i], eulers[i]), rtol=1e-7)
#np.testing.assert_allclose(eulers[i], ecef_euler_from_ned(ecef_positions[i], ned_eulers[i]), rtol=1e-7)
np.testing.assert_allclose(ned_eulers, ned_euler_from_ecef(ecef_positions, eulers), rtol=1e-7)
if __name__ == "__main__":
unittest.main()
+40
View File
@@ -0,0 +1,40 @@
import sys
import pygame
from pygame.locals import *
class Window():
def __init__(self, w, h, caption="window", double=False):
self.w = w
self.h = h
pygame.init()
pygame.display.set_caption(caption)
self.double = double
if self.double:
self.screen = pygame.display.set_mode((w*2,h*2), pygame.DOUBLEBUF)
else:
self.screen = pygame.display.set_mode((w,h), pygame.DOUBLEBUF)
self.camera_surface = pygame.surface.Surface((w,h), 0, 24).convert()
def draw(self, out):
pygame.surfarray.blit_array(self.camera_surface, out.swapaxes(0,1))
if self.double:
camera_surface_2x = pygame.transform.scale2x(self.camera_surface)
self.screen.blit(camera_surface_2x, (0, 0))
else:
self.screen.blit(self.camera_surface, (0, 0))
pygame.display.flip()
def getkey(self):
while 1:
event = pygame.event.wait()
if event.type == QUIT:
pygame.quit()
sys.exit()
if event.type == KEYDOWN:
return event.key
def getclick(self):
for event in pygame.event.get():
if event.type == pygame.MOUSEBUTTONDOWN:
mx, my = pygame.mouse.get_pos()
return mx, my
Vendored Executable
+77
View File
@@ -0,0 +1,77 @@
#!/bin/bash
#
# Copyright (C) 2007 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# This script is a wrapper for smali.jar, so you can simply call "smali",
# instead of java -jar smali.jar. It is heavily based on the "dx" script
# from the Android SDK
# Set up prog to be the path of this script, including following symlinks,
# and set up progdir to be the fully-qualified pathname of its directory.
prog="$0"
while [ -h "${prog}" ]; do
newProg=`/bin/ls -ld "${prog}"`
newProg=`expr "${newProg}" : ".* -> \(.*\)$"`
if expr "x${newProg}" : 'x/' >/dev/null; then
prog="${newProg}"
else
progdir=`dirname "${prog}"`
prog="${progdir}/${newProg}"
fi
done
oldwd=`pwd`
progdir=`dirname "${prog}"`
cd "${progdir}"
progdir=`pwd`
prog="${progdir}"/`basename "${prog}"`
cd "${oldwd}"
jarfile=apktool.jar
libdir="$progdir"
if [ ! -r "$libdir/$jarfile" ]
then
echo `basename "$prog"`": can't find $jarfile"
exit 1
fi
javaOpts=""
# If you want DX to have more memory when executing, uncomment the following
# line and adjust the value accordingly. Use "java -X" for a list of options
# you can pass here.
#
javaOpts="-Xmx512M -Dfile.encoding=utf-8"
# Alternatively, this will extract any parameter "-Jxxx" from the command line
# and pass them to Java (instead of to dx). This makes it possible for you to
# add a command-line parameter such as "-JXmx256M" in your ant scripts, for
# example.
while expr "x$1" : 'x-J' >/dev/null; do
opt=`expr "$1" : '-J\(.*\)'`
javaOpts="${javaOpts} -${opt}"
shift
done
if [ "$OSTYPE" = "cygwin" ] ; then
jarpath=`cygpath -w "$libdir/$jarfile"`
else
jarpath="$libdir/$jarfile"
fi
# add current location to path for aapt
PATH=$PATH:`pwd`;
export PATH;
exec java $javaOpts -jar "$jarpath" "$@"
+1
View File
@@ -0,0 +1 @@
apktool_2.3.0.jar
Binary file not shown.
Vendored Executable
+3
View File
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:b7d08710ae4c0bbdc73184453b4d40da979fa3b0abbdb4e974980f9250730127
size 18914367
Vendored Symlink
+1
View File
@@ -0,0 +1 @@
../gpac/bin/MP4Box
Vendored Symlink
+1
View File
@@ -0,0 +1 @@
../apktool/apktool

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