diff --git a/Pipfile b/Pipfile index 614968710c9606..6b9f7e95bf0e44 100644 --- a/Pipfile +++ b/Pipfile @@ -96,6 +96,9 @@ fastcluster = "==1.1.25" backports-abc = "*" pygame = "*" simplejson = "*" +python-logstash-async = "*" +pandas = "*" +seaborn = "*" [packages] overpy = {git = "https://github.com/commaai/python-overpy.git",ref = "f86529af402d4642e1faeb146671c40284007323"} diff --git a/Pipfile.lock b/Pipfile.lock index e4623df10afb4c..30c43e28671cc0 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "72c6a6eac19abf85ca5f272d04761f8336150d5bec599423c4b16891c99cb0bf" + "sha256": "89070d7d9478ac9e6ad2c9848aaf724cb7362a1de4af9f8fb8c40be5f37c043d" }, "pipfile-spec": 6, "requires": { @@ -43,15 +43,15 @@ "sha256:9d98697f088eb1b0fa451391f91afb5e3ebde16bbdb272819fd091151fda4f1a", "sha256:f0b0e4eba956de51238e17573b7087e852dfe9854afd2e9c873f73fc0ca0a6dd" ], - "markers": "python_version < '3.2'", + "markers": "python_version == '2.7'", "version": "==1.5" }, "certifi": { "hashes": [ - "sha256:59b7658e26ca9c7339e00f8f4636cdfe59d34fa37b9b04f6f9e9926b3cece1a5", - "sha256:b26104d6835d1f5e49452a26eb2ff87fe7090b89dfcaee5ea2212697e1e1d7ae" + "sha256:046832c04d4e752f37383b628bc601a7ea7211496b4638f6514d0e5b9acc4939", + "sha256:945e3ba63a0b9f577b1395204e13c3a231f9bc0223888be653286534e5873695" ], - "version": "==2019.3.9" + "version": "==2019.6.16" }, "cffi": { "hashes": [ @@ -149,37 +149,37 @@ }, "cython": { "hashes": [ - "sha256:0afa0b121b89de619e71587e25702e2b7068d7da2164c47e6eee80c17823a62f", - "sha256:1c608ba76f7a20cc9f0c021b7fe5cb04bc1a70327ae93a9298b1bc3e0edddebe", - "sha256:26229570d6787ff3caa932fe9d802960f51a89239b990d275ae845405ce43857", - "sha256:2a9deafa437b6154cac2f25bb88e0bfd075a897c8dc847669d6f478d7e3ee6b1", - "sha256:2f28396fbce6d9d68a40edbf49a6729cf9d92a4d39ff0f501947a89188e9099f", - "sha256:3983dd7b67297db299b403b29b328d9e03e14c4c590ea90aa1ad1d7b35fb178b", - "sha256:4100a3f8e8bbe47d499cdac00e56d5fe750f739701ea52dc049b6c56f5421d97", - "sha256:51abfaa7b6c66f3f18028876713c8804e73d4c2b6ceddbcbcfa8ec62429377f0", - "sha256:61c24f4554efdb8fb1ac6c8e75dab301bcdf2b7b739ed0c2b267493bb43163c5", - "sha256:700ccf921b2fdc9b23910e95b5caae4b35767685e0812343fa7172409f1b5830", - "sha256:7b41eb2e792822a790cb2a171df49d1a9e0baaa8e81f58077b7380a273b93d5f", - "sha256:803987d3b16d55faa997bfc12e8b97f1091f145930dee229b020487aed8a1f44", - "sha256:99af5cfcd208c81998dcf44b3ca466dee7e17453cfb50e98b87947c3a86f8753", - "sha256:9faea1cca34501c7e139bc7ef8e504d532b77865c58592493e2c154a003b450f", - "sha256:a7ba4c9a174db841cfee9a0b92563862a0301d7ca543334666c7266b541f141a", - "sha256:b26071c2313d1880599c69fd831a07b32a8c961ba69d7ccbe5db1cd8d319a4ca", - "sha256:b49dc8e1116abde13a3e6a9eb8da6ab292c5a3325155fb872e39011b110b37e6", - "sha256:bd40def0fd013569887008baa6da9ca428e3d7247adeeaeada153006227bb2e7", - "sha256:bfd0db770e8bd4e044e20298dcae6dfc42561f85d17ee546dcd978c8b23066ae", - "sha256:c2fad1efae5889925c8fd7867fdd61f59480e4e0b510f9db096c912e884704f1", - "sha256:c81aea93d526ccf6bc0b842c91216ee9867cd8792f6725a00f19c8b5837e1715", - "sha256:da786e039b4ad2bce3d53d4799438cf1f5e01a0108f1b8d78ac08e6627281b1a", - "sha256:deab85a069397540987082d251e9c89e0e5b2e3e044014344ff81f60e211fc4b", - "sha256:e3f1e6224c3407beb1849bdc5ae3150929e593e4cffff6ca41c6ec2b10942c80", - "sha256:e74eb224e53aae3943d66e2d29fe42322d5753fd4c0641329bccb7efb3a46552", - "sha256:ee697c7ea65cb14915a64f36874da8ffc2123df43cf8bc952172e04a26656cd6", - "sha256:f37792b16d11606c28e428460bd6a3d14b8917b109e77cdbe4ca78b0b9a52c87", - "sha256:fd2906b54cbf879c09d875ad4e4687c58d87f5ed03496063fec1c9065569fd5d" - ], - "index": "pypi", - "version": "==0.29.10" + "sha256:04ebf16df9406d3279a2489c3327803c782d9e17637d525bfb44ecf5ec65850f", + "sha256:1486ec88d1c73dea3846a5640054018b002608e04a791ccbd2082a47bce4440a", + "sha256:20da832a5e9a8e93d1e1eb64650258956723940968eb585506531719b55b804f", + "sha256:2464688b523d7a133b52cf1343c1c595b92fc6554af1015f74b9e49951e992d4", + "sha256:27827b68a8359e9ab6bf683c68d8ee79863a0c94a577acf56aa02cc302e16f51", + "sha256:27deeeeca0fd8933af07923e809c8fed0763d150a4fdd4082932a33b8c874ed6", + "sha256:31f4da785d5e09deb852ea59795a629c5befb6040929e7880c6f63e6668246ce", + "sha256:4828cf8fa638c35139e643f30201b240c0d156b1b9967a7321ae42d721d7224c", + "sha256:48b365e32cc5639ae2c239d7bd4f8a1d920a13a7ae92113c4c938903c9400147", + "sha256:4eb71856c1d1b33083df9318fd30143470ad6f0d1b9ad2ee61a120710842d28b", + "sha256:5b06ef8422d27d8128f8f80bdefa111eadcab246fba1d668720af4f0b97b7a0e", + "sha256:71c553640e1ddaaf143e38dbc6cd1863fa3c0738fb1830a9aaffba9a51838f30", + "sha256:73e2742ee1f923c5f213183bf493901f9630e395634fce5b739a53b7dc5d64be", + "sha256:82a632bc02063eff0b8e7ff3089aa3d912d1c7499709f51c8f04f57c8832cfe6", + "sha256:977ca1ac059e4d4a4bf5fe2224986baf42b69290453eda44822606f4deae6515", + "sha256:a7e6217d0dd864a7cc4f457172766864496efd64d24d4980df1521f75f992761", + "sha256:ad0ed7dd5dff76eb3aae8c18d95b1c9f885a91a92132728051a704fb8060d08c", + "sha256:b1b8eda9e931f0ca1aadb95a890811bdf530407e48c962643b85675329d99abf", + "sha256:cec99c79205131da3ee75becea1f3f55c57bf6a1c500431de9ae7a32ac8a5cc4", + "sha256:d4bbdaa6f61ce2ef26535a7d473d6ffa6e413013c5c580af999546bf1627ae11", + "sha256:d8bdb4208975b12048bdace46e9dd8e3dda3872432f95b53789700a1330e6060", + "sha256:dce0362ff9b61f8411d1efc9e16fc528dadbd3707a557561992457f5cb446297", + "sha256:defbbbf5653629ce5cc54091ce49c6830da8d3104de53ed2169c9efcb0720f27", + "sha256:e0c53a7e2b6d82ec3c26c009c937fc88eb8c7edf000c54334261beaf56bb08f2", + "sha256:e1065bacfe5303f107896e63263537dee90920d26050f2e23c4af12c37da2db6", + "sha256:e142837c4212c0b2c71e6773cb6740828922806b4c00ee4215be3ceb558671e6", + "sha256:f4cbbab28c93ffee6ec929cf0826f0b11d2488e53a708d51142a5e62f8cd9806", + "sha256:fa8f63b6551621eea9efea4db37ae401104352f0ebaee32f7d20be88cbe589c3" + ], + "index": "pypi", + "version": "==0.29.12" }, "enum34": { "hashes": [ @@ -193,19 +193,19 @@ }, "flask": { "hashes": [ - "sha256:ad7c6d841e64296b962296c2c2dabc6543752985727af86a975072dea984b6f3", - "sha256:e7d32475d1de5facaa55e3958bc4ec66d3762076b074296aa50ef8fdc5b9df61" + "sha256:13f9f196f330c7c2c5d7a5cf91af894110ca0215ac051b5844701f2bfd934d52", + "sha256:45eb5a6fd193d6cf7e0cf5d8a5b31f83d5faae0293695626f539a823e93b13f6" ], "index": "pypi", - "version": "==1.0.3" + "version": "==1.1.1" }, "futures": { "hashes": [ - "sha256:9ec02aa7d674acb8618afb127e27fde7fc68994c0437ad759fa094a574adb265", - "sha256:ec0a6cb848cc212002b9828c3e34c675e0c9ff6741dc445cab6fdd4e1085d1f1" + "sha256:49b3f5b064b6e3afc3316421a3f25f66c137ae88f068abbf72830170033c5e16", + "sha256:7e033af76a5e35f58e56da7a91e687706faf4e7bdfb2cbc3f2cca6b9bcda9794" ], "markers": "python_version < '3.2'", - "version": "==3.2.0" + "version": "==3.3.0" }, "gunicorn": { "hashes": [ @@ -240,10 +240,10 @@ }, "isort": { "hashes": [ - "sha256:c40744b6bc5162bbb39c1257fe298b7a393861d50978b565f3ccd9cb9de0182a", - "sha256:f57abacd059dc3bd666258d1efb0377510a89777fda3e3274e3c01f7c03ae22d" + "sha256:54da7e92468955c4fceacd0c86bd0ec997b0e1ee80d97f67c35a78b719dccab1", + "sha256:6e811fcb295968434526407adb8796944f1988c5b65e8139058f2014cbe100fd" ], - "version": "==4.3.20" + "version": "==4.3.21" }, "itsdangerous": { "hashes": [ @@ -470,11 +470,11 @@ }, "pylint": { "hashes": [ - "sha256:02c2b6d268695a8b64ad61847f92e611e6afcff33fd26c3a2125370c4662905d", - "sha256:ee1e85575587c5b58ddafa25e1c1b01691ef172e139fc25585e5d3f02451da93" + "sha256:367e3d49813d349a905390ac27989eff82ab84958731c5ef0bef867452cfdc42", + "sha256:97a42df23d436c70132971d1dcb9efad2fe5c0c6add55b90161e773caf729300" ], "index": "pypi", - "version": "==1.9.4" + "version": "==1.9.5" }, "pyserial": { "hashes": [ @@ -511,34 +511,34 @@ }, "pyzmq": { "hashes": [ - "sha256:1651e52ed91f0736afd6d94ef9f3259b5534ce8beddb054f3d5ca989c4ef7c4f", - "sha256:5ccb9b3d4cd20c000a9b75689d5add8cd3bce67fcbd0f8ae1b59345247d803af", - "sha256:5e120c4cd3872e332fb35d255ad5998ebcee32ace4387b1b337416b6b90436c7", - "sha256:5e2a3707c69a7281a9957f83718815fd74698cba31f6d69f9ed359921f662221", - "sha256:63d51add9af8d0442dc90f916baf98fdc04e3b0a32afec4bfc83f8d85e72959f", - "sha256:65c5a0bdc49e20f7d6b03a661f71e2fda7a99c51270cafe71598146d09810d0d", - "sha256:66828fabe911aa545d919028441a585edb7c9c77969a5fea6722ef6e6ece38ab", - "sha256:7d79427e82d9dad6e9b47c0b3e7ae5f9d489b1601e3a36ea629bb49501a4daf3", - "sha256:824ee5d3078c4eae737ffc500fbf32f2b14e6ec89b26b435b7834febd70120cf", - "sha256:89dc0a83cccec19ff3c62c091e43e66e0183d1e6b4658c16ee4e659518131494", - "sha256:8b319805f6f7c907b101c864c3ca6cefc9db8ce0791356f180b1b644c7347e4c", - "sha256:90facfb379ab47f94b19519c1ecc8ec8d10813b69d9c163117944948bdec5d15", - "sha256:a0a178c7420021fc0730180a914a4b4b3092ce9696ceb8e72d0f60f8ce1655dd", - "sha256:a7a89591ae315baccb8072f216614b3e59aed7385aef4393a6c741783d6ee9cf", - "sha256:ba2578f0ae582452c02ed9fac2dc477b08e80ce05d2c0885becf5fff6651ccb0", - "sha256:c69b0055c55702f5b0b6b354133e8325b9a56dbc80e1be2d240bead253fb9825", - "sha256:ca434e1858fe222380221ddeb81e86f45522773344c9da63c311d17161df5e06", - "sha256:d4b8ecfc3d92f114f04d5c40f60a65e5196198b827503341521dda12d8b14939", - "sha256:d706025c47b09a54f005953ebe206f6d07a22516776faa4f509aaff681cc5468", - "sha256:d8f27e958f8a2c0c8ffd4d8855c3ce8ac3fa1e105f0491ce31729aa2b3229740", - "sha256:dbd264298f76b9060ce537008eb989317ca787c857e23cbd1b3ddf89f190a9b1", - "sha256:e926d66f0df8fdbf03ba20583af0f215e475c667fb033d45fd031c66c63e34c9", - "sha256:efc3bd48237f973a749f7312f68062f1b4ca5c2032a0673ca3ea8e46aa77187b", - "sha256:f59bc782228777cbfe04555707a9c56d269c787ed25d6d28ed9d0fbb41cb1ad2", - "sha256:f8da5322f4ff5f667a0d5a27e871b560c6637153c81e318b35cb012b2a98835c" - ], - "index": "pypi", - "version": "==18.0.1" + "sha256:00dd015159eaeb1c0731ad49310e1f5d839c9a35a15e4f3267f5052233fad99b", + "sha256:03913b6beb8e7b417b9910b0ee1fd5d62e9626d218faefbe879d70714ceab1a2", + "sha256:13f17386df81d5e6efb9a4faea341d8de22cdc82e49a326dded26e33f42a3112", + "sha256:16c6281d96885db1e15f7047ddc1a8f48ff4ea35d31ca709f4d2eb39f246d356", + "sha256:17efab4a804e31f58361631256d660214204046f9e2b962738b171b9ad674ea7", + "sha256:2b79919ddeff3d3c96aa6087c21d294c8db1c01f6bfeee73324944683685f419", + "sha256:2f832e4711657bb8d16ea1feba860f676ec5f14fb9fe3b449b5953a60e89edae", + "sha256:31a11d37ac73107363b47e14c94547dbfc6a550029c3fe0530be443199026fc2", + "sha256:33a3e928e6c3138c675e1d6702dd11f6b7050177d7aab3fc322db6e1d2274490", + "sha256:34a38195a6d3a9646cbcdaf8eb245b4d935c7a57f7e1b3af467814bc1a92467e", + "sha256:42900054f1500acef6df7428edf806abbf641bf92eb9ceded24aa863397c3bae", + "sha256:4ccc7f3c63aa9d744dadb62c49eda2d0e7de55649b80c45d7c684d70161a69af", + "sha256:5b220c37c346e6575db8c88a940c1fc234f99ce8e0068c408919bb8896c4b6d2", + "sha256:6074848da5c8b44a1ca40adf75cf65aa92bc80f635e8249aa8f37a69b2b9b6f5", + "sha256:61a4155964bd4a14ef95bf46cb1651bcf8dcbbed8c0108e9c974c1fcbb57788f", + "sha256:62b5774688326600c52f587f7a033ca6b6284bef4c8b1b5fda32480897759eac", + "sha256:65a9ffa4f9f085d696f16fd7541f34b3c357d25fe99c90e3bce2ea59c3b5b4b6", + "sha256:76a077d2c30f8adc5e919a55985a784b96aeca69b53c1ea6fd5723d3ae2e6f53", + "sha256:8e5b4c51557071d6379d6dc1f54f35e9f6a137f5e84e102efb869c8d3c13c8ff", + "sha256:917f73e07cc04f0678a96d93e7bb8b1adcccdde9ccfe202e622814f4d1d1ecfd", + "sha256:91c75d3c4c357f9643e739db9e79ab9681b2f6ae8ec5678d6ef2ea0d01532596", + "sha256:923dd91618b100bb4c92ab9ed7b65825a595b8524a094ce03c7cb2aaae7d353b", + "sha256:9849054e0355e2bc7f4668766a25517ba76095031c9ff5e39ae8949cee5bb024", + "sha256:c9d453933f0e3f44b9759189f2a18aa765f7f1a4345c727c18ebe8ad0d748d26", + "sha256:cb7514936277abce64c2f4c56883e5704d85ed04d98d2d432d1c6764003bb003" + ], + "index": "pypi", + "version": "==18.0.2" }, "raven": { "hashes": [ @@ -597,11 +597,11 @@ }, "tqdm": { "hashes": [ - "sha256:0a860bf2683fdbb4812fe539a6c22ea3f1777843ea985cb8c3807db448a0f7ab", - "sha256:e288416eecd4df19d12407d0c913cbf77aa8009d7fddb18f632aded3bdbdda6b" + "sha256:14a285392c32b6f8222ecfbcd217838f88e11630affe9006cd0e94c7eff3cb61", + "sha256:25d4c0ea02a305a688e7e9c2cdc8f862f989ef2a4701ab28ee963295f5b109ab" ], "index": "pypi", - "version": "==4.32.1" + "version": "==4.32.2" }, "urllib3": { "hashes": [ @@ -613,10 +613,10 @@ }, "utm": { "hashes": [ - "sha256:a6608a67df84418fd959a79b228b90ab55b2ae877827f9c210947104c5a75d0e" + "sha256:07e55707ed660eec1ae983bd54a406c437962618a6261b38d70592fe30f5f508" ], "index": "pypi", - "version": "==0.4.2" + "version": "==0.5.0" }, "websocket-client": { "hashes": [ @@ -628,16 +628,16 @@ }, "werkzeug": { "hashes": [ - "sha256:865856ebb55c4dcd0630cdd8f3331a1847a819dda7e8c750d3db6f2aa6c0209c", - "sha256:a0b915f0815982fb2a09161cb8f31708052d0951c3ba433ccc5e1aa276507ca6" + "sha256:87ae4e5b5366da2347eb3116c0e6c681a0e939a33b2805e2c0cbd282664932c4", + "sha256:a13b74dd3c45f758d4ebdb224be8f1ab8ef58b3c0ffc1783a8c7d9f4f50227e6" ], - "version": "==0.15.4" + "version": "==0.15.5" }, "wrapt": { "hashes": [ - "sha256:4aea003270831cceb8a90ff27c4031da6ead7ec1886023b80ce0dfe0adf61533" + "sha256:565a021fd19419476b9362b05eeaa094178de64f8361e44468f9e9d7843901e1" ], - "version": "==1.11.1" + "version": "==1.11.2" } }, "develop": { @@ -649,19 +649,19 @@ }, "adal": { "hashes": [ - "sha256:82e84fa0b442caf8131f1e87a7ebee2546f57ab16a8917a599a02b6e455cb1b0", - "sha256:b6edd095be66561382bdaa59d40b04490e93149fb3b7fa44c1fa5504eed5b8b9" + "sha256:5a7f1e037c6290c6d7609cab33a9e5e988c2fbec5c51d1c4c649ee3faff37eaf", + "sha256:fd17e5661f60634ddf96a569b95d34ccb8a98de60593d729c28bdcfe360eaad1" ], - "version": "==1.2.1" + "version": "==1.2.2" }, "aenum": { "hashes": [ - "sha256:3df9b84cce5dc9ed77c337079f97b66c44c0053eb87d6f4d46b888dc45801e38", - "sha256:7a77c205c4bc9d7fe9bd73b3193002d724aebf5909fa0d297534208953891ec8", - "sha256:a3208e4b28db3a7b232ff69b934aef2ea1bf27286d9978e1e597d46f490e4687" + "sha256:058f0cfaf911899dc21b334362047df74ce989335dd8dff8e4be1a6313b15232", + "sha256:6af970173d9b4ac0384ad7d1cfe9523eeb9a3578793e1664090c13cb59df6469", + "sha256:80f14366578d84f6bccb0670259744cb3a7f2ab504480c306238a23cdd569457" ], "index": "pypi", - "version": "==2.1.2" + "version": "==2.2.0" }, "amqp": { "hashes": [ @@ -760,7 +760,7 @@ "sha256:9d98697f088eb1b0fa451391f91afb5e3ebde16bbdb272819fd091151fda4f1a", "sha256:f0b0e4eba956de51238e17573b7087e852dfe9854afd2e9c873f73fc0ca0a6dd" ], - "markers": "python_version < '3.2'", + "markers": "python_version == '2.7'", "version": "==1.5" }, "backports.lzma": { @@ -817,18 +817,18 @@ }, "boto3": { "hashes": [ - "sha256:794a9a4b6a9e40c1ac57a377de609872d28d62afe4295c48cdc1b1c92f96ab8e", - "sha256:962b078568cc520869ea2842f307864c9abc30ad5ed160e12b2a89debf220161" + "sha256:34a8ddb7247316be6ea94c7eeee41212312d250d99bf668fcd6748629b578622", + "sha256:71f3554cc69fa20be06cf20d6c9e0d8095d7c40695b48618676c3cd9a5ba0783" ], "index": "pypi", - "version": "==1.9.168" + "version": "==1.9.189" }, "botocore": { "hashes": [ - "sha256:675f2b66af486dd02f5825601bb0c8378773999f8705c6f75450849ca41fed80", - "sha256:c3fc314c0e0aa13aa024d272d991e23d37550050abf96b3c7dea889ed1743723" + "sha256:4febbf206d1dc8b8299aa211d8e382d5bf3f22097855b9f98d5e8c401ef8192b", + "sha256:b62ab3e4e98e075fc9e8e8fd4e8f5b92ebf311a6dc9f7578650938c7bc94e592" ], - "version": "==1.12.168" + "version": "==1.12.189" }, "celery": { "hashes": [ @@ -840,10 +840,10 @@ }, "certifi": { "hashes": [ - "sha256:59b7658e26ca9c7339e00f8f4636cdfe59d34fa37b9b04f6f9e9926b3cece1a5", - "sha256:b26104d6835d1f5e49452a26eb2ff87fe7090b89dfcaee5ea2212697e1e1d7ae" + "sha256:046832c04d4e752f37383b628bc601a7ea7211496b4638f6514d0e5b9acc4939", + "sha256:945e3ba63a0b9f577b1395204e13c3a231f9bc0223888be653286534e5873695" ], - "version": "==2019.3.9" + "version": "==2019.6.16" }, "cffi": { "hashes": [ @@ -947,37 +947,37 @@ }, "cython": { "hashes": [ - "sha256:0afa0b121b89de619e71587e25702e2b7068d7da2164c47e6eee80c17823a62f", - "sha256:1c608ba76f7a20cc9f0c021b7fe5cb04bc1a70327ae93a9298b1bc3e0edddebe", - "sha256:26229570d6787ff3caa932fe9d802960f51a89239b990d275ae845405ce43857", - "sha256:2a9deafa437b6154cac2f25bb88e0bfd075a897c8dc847669d6f478d7e3ee6b1", - "sha256:2f28396fbce6d9d68a40edbf49a6729cf9d92a4d39ff0f501947a89188e9099f", - "sha256:3983dd7b67297db299b403b29b328d9e03e14c4c590ea90aa1ad1d7b35fb178b", - "sha256:4100a3f8e8bbe47d499cdac00e56d5fe750f739701ea52dc049b6c56f5421d97", - "sha256:51abfaa7b6c66f3f18028876713c8804e73d4c2b6ceddbcbcfa8ec62429377f0", - "sha256:61c24f4554efdb8fb1ac6c8e75dab301bcdf2b7b739ed0c2b267493bb43163c5", - "sha256:700ccf921b2fdc9b23910e95b5caae4b35767685e0812343fa7172409f1b5830", - "sha256:7b41eb2e792822a790cb2a171df49d1a9e0baaa8e81f58077b7380a273b93d5f", - "sha256:803987d3b16d55faa997bfc12e8b97f1091f145930dee229b020487aed8a1f44", - "sha256:99af5cfcd208c81998dcf44b3ca466dee7e17453cfb50e98b87947c3a86f8753", - "sha256:9faea1cca34501c7e139bc7ef8e504d532b77865c58592493e2c154a003b450f", - "sha256:a7ba4c9a174db841cfee9a0b92563862a0301d7ca543334666c7266b541f141a", - "sha256:b26071c2313d1880599c69fd831a07b32a8c961ba69d7ccbe5db1cd8d319a4ca", - "sha256:b49dc8e1116abde13a3e6a9eb8da6ab292c5a3325155fb872e39011b110b37e6", - "sha256:bd40def0fd013569887008baa6da9ca428e3d7247adeeaeada153006227bb2e7", - "sha256:bfd0db770e8bd4e044e20298dcae6dfc42561f85d17ee546dcd978c8b23066ae", - "sha256:c2fad1efae5889925c8fd7867fdd61f59480e4e0b510f9db096c912e884704f1", - "sha256:c81aea93d526ccf6bc0b842c91216ee9867cd8792f6725a00f19c8b5837e1715", - "sha256:da786e039b4ad2bce3d53d4799438cf1f5e01a0108f1b8d78ac08e6627281b1a", - "sha256:deab85a069397540987082d251e9c89e0e5b2e3e044014344ff81f60e211fc4b", - "sha256:e3f1e6224c3407beb1849bdc5ae3150929e593e4cffff6ca41c6ec2b10942c80", - "sha256:e74eb224e53aae3943d66e2d29fe42322d5753fd4c0641329bccb7efb3a46552", - "sha256:ee697c7ea65cb14915a64f36874da8ffc2123df43cf8bc952172e04a26656cd6", - "sha256:f37792b16d11606c28e428460bd6a3d14b8917b109e77cdbe4ca78b0b9a52c87", - "sha256:fd2906b54cbf879c09d875ad4e4687c58d87f5ed03496063fec1c9065569fd5d" - ], - "index": "pypi", - "version": "==0.29.10" + "sha256:04ebf16df9406d3279a2489c3327803c782d9e17637d525bfb44ecf5ec65850f", + "sha256:1486ec88d1c73dea3846a5640054018b002608e04a791ccbd2082a47bce4440a", + "sha256:20da832a5e9a8e93d1e1eb64650258956723940968eb585506531719b55b804f", + "sha256:2464688b523d7a133b52cf1343c1c595b92fc6554af1015f74b9e49951e992d4", + "sha256:27827b68a8359e9ab6bf683c68d8ee79863a0c94a577acf56aa02cc302e16f51", + "sha256:27deeeeca0fd8933af07923e809c8fed0763d150a4fdd4082932a33b8c874ed6", + "sha256:31f4da785d5e09deb852ea59795a629c5befb6040929e7880c6f63e6668246ce", + "sha256:4828cf8fa638c35139e643f30201b240c0d156b1b9967a7321ae42d721d7224c", + "sha256:48b365e32cc5639ae2c239d7bd4f8a1d920a13a7ae92113c4c938903c9400147", + "sha256:4eb71856c1d1b33083df9318fd30143470ad6f0d1b9ad2ee61a120710842d28b", + "sha256:5b06ef8422d27d8128f8f80bdefa111eadcab246fba1d668720af4f0b97b7a0e", + "sha256:71c553640e1ddaaf143e38dbc6cd1863fa3c0738fb1830a9aaffba9a51838f30", + "sha256:73e2742ee1f923c5f213183bf493901f9630e395634fce5b739a53b7dc5d64be", + "sha256:82a632bc02063eff0b8e7ff3089aa3d912d1c7499709f51c8f04f57c8832cfe6", + "sha256:977ca1ac059e4d4a4bf5fe2224986baf42b69290453eda44822606f4deae6515", + "sha256:a7e6217d0dd864a7cc4f457172766864496efd64d24d4980df1521f75f992761", + "sha256:ad0ed7dd5dff76eb3aae8c18d95b1c9f885a91a92132728051a704fb8060d08c", + "sha256:b1b8eda9e931f0ca1aadb95a890811bdf530407e48c962643b85675329d99abf", + "sha256:cec99c79205131da3ee75becea1f3f55c57bf6a1c500431de9ae7a32ac8a5cc4", + "sha256:d4bbdaa6f61ce2ef26535a7d473d6ffa6e413013c5c580af999546bf1627ae11", + "sha256:d8bdb4208975b12048bdace46e9dd8e3dda3872432f95b53789700a1330e6060", + "sha256:dce0362ff9b61f8411d1efc9e16fc528dadbd3707a557561992457f5cb446297", + "sha256:defbbbf5653629ce5cc54091ce49c6830da8d3104de53ed2169c9efcb0720f27", + "sha256:e0c53a7e2b6d82ec3c26c009c937fc88eb8c7edf000c54334261beaf56bb08f2", + "sha256:e1065bacfe5303f107896e63263537dee90920d26050f2e23c4af12c37da2db6", + "sha256:e142837c4212c0b2c71e6773cb6740828922806b4c00ee4215be3ceb558671e6", + "sha256:f4cbbab28c93ffee6ec929cf0826f0b11d2488e53a708d51142a5e62f8cd9806", + "sha256:fa8f63b6551621eea9efea4db37ae401104352f0ebaee32f7d20be88cbe589c3" + ], + "index": "pypi", + "version": "==0.29.12" }, "datadog": { "hashes": [ @@ -1098,11 +1098,11 @@ }, "flask": { "hashes": [ - "sha256:ad7c6d841e64296b962296c2c2dabc6543752985727af86a975072dea984b6f3", - "sha256:e7d32475d1de5facaa55e3958bc4ec66d3762076b074296aa50ef8fdc5b9df61" + "sha256:13f9f196f330c7c2c5d7a5cf91af894110ca0215ac051b5844701f2bfd934d52", + "sha256:45eb5a6fd193d6cf7e0cf5d8a5b31f83d5faae0293695626f539a823e93b13f6" ], "index": "pypi", - "version": "==1.0.3" + "version": "==1.1.1" }, "flask-cors": { "hashes": [ @@ -1145,11 +1145,11 @@ }, "futures": { "hashes": [ - "sha256:9ec02aa7d674acb8618afb127e27fde7fc68994c0437ad759fa094a574adb265", - "sha256:ec0a6cb848cc212002b9828c3e34c675e0c9ff6741dc445cab6fdd4e1085d1f1" + "sha256:49b3f5b064b6e3afc3316421a3f25f66c137ae88f068abbf72830170033c5e16", + "sha256:7e033af76a5e35f58e56da7a91e687706faf4e7bdfb2cbc3f2cca6b9bcda9794" ], "markers": "python_version < '3.2'", - "version": "==3.2.0" + "version": "==3.3.0" }, "gast": { "hashes": [ @@ -1228,40 +1228,40 @@ }, "grpcio": { "hashes": [ - "sha256:0232add03144dd3cf9b660e2718244cb8e175370dca4d3855cb4e489a7811b53", - "sha256:0f20e6dcb1b8662cdca033bb97c0a8116a5343e3ebc7f71c5fe7f89039978350", - "sha256:10b07a623d33d4966f45c85d410bc6a79c5ac6341f06c3beda6c22be12cbfe07", - "sha256:10c0476d5a52d21f402fc073745dc43b87cc8e080a1f49bbff4e1059019310fb", - "sha256:289dae0b35c59d191c524e976dd0a6f8c995d2062e72621eb866ad0f4472a635", - "sha256:2be726f16142d358a0df1e81d583d6820ee561a7856a79cca2fbe49989308be7", - "sha256:4338d2a81f5b4ca022e085040b3cfce19419a5ce44aa7e6810ac1df05365bed7", - "sha256:4c535b46f20e66bee3097583231977e721acdfcb1671d1490c99b7be8902ce18", - "sha256:557154aef70a0e979700cc9528bc8b606b668084a29a0d57dbc4b06b078a2f1c", - "sha256:5bfdd7e6647498f979dc46583723c852d97b25afe995d55aa1c76a5f9816bc1f", - "sha256:87d8943ae7aa6ca5bbad732867d7f17d2550e4966a0c15b52088e8b579422e47", - "sha256:89d8719d8de4d137678f7caa979e1b0a6fd4026f8096ceef8c2d164bbabefaf2", - "sha256:9c3f4af989ce860710ac1864dc2e867dd87e6cee51a2368df1b253596868e52f", - "sha256:9da52c3c728883aee429bb7c315049f50b2139f680cd86bb1165418e4f93a982", - "sha256:9e9736659987beab42d18525ed10d21f80a1ba8389eac03425fbfd5684e6bbf0", - "sha256:9ebcbb1a054cab362d29d3be571d43d6b9b23302d9fc4b43e5327000da1680a9", - "sha256:a93e08636623e24c939851e2e0c0140b14f524b2980c9cdc4ea52b70a871c7e0", - "sha256:ac322d86d1a079e0a118d544443ee16f320af0062c191b4754c0c6ec2fc79310", - "sha256:b1fb101459868f52df6b61e7bb13375e50badf17a160e39fe1d51ae19e53f461", - "sha256:b39aac96cceac624a23d540473835086a3ffa77c91030189988c073488434493", - "sha256:b65507bc273c6dbf539175a786a344cc0ac78d50e5584f72c6599733f8a3301f", - "sha256:be5bb6e47417e537c884a2e2ff2e1a8b2c064a998fcfdfcc67528d4e63e7ebaf", - "sha256:c92de6a28a909c4f460dc1bbbcb50d676cf0b1f40224b222761f73fdd851b522", - "sha256:c9f5962eb7fa7607b20eb0e4f59ed35829bd600fc0eacb626a6db83229a3e445", - "sha256:d00bdf9c546ed6e649f785c55b05288e8b2dbb6bf2eb74b6c579fa0d591d35bd", - "sha256:da804b1dd8293bd9d61b1e6ea989c887ba042a808a4fbdd80001cfa059aafed2", - "sha256:ead6c5aa3e807345913649c3be395aaca2bbb2d225f18b8f31f37eab225508f6", - "sha256:eb4d81550ce6f826af4ec6e8d98be347fe96291d718bf115c3f254621ae8d98d", - "sha256:ef6a18ec8fd32ec81748fe720544ea2fb2d2dc50fd6d06739d5e2eb8f0626a1c", - "sha256:fad42835656e0b6d3b7ffc900598e776722e30f43b7234a48f2576ca30f31a47", - "sha256:fb98dbfee0d963b49ae5754554028cf62e6bd695f22de16d242ba9d2f0b7339b", - "sha256:fb9cd9bb8d26dc17c2dd715a46bca3a879ec8283879b164e85863110dc6e3b2a" - ], - "version": "==1.21.1" + "sha256:03b78b4e7dcdfe3e257bb528cc93923f9cbbab6d5babf15a60d21e9a4a70b1a2", + "sha256:1ce0ccfbdfe84387dbcbf44adb4ae16ec7ae70e166ffab478993eb1ea1cba3ce", + "sha256:22e167a9406d73dd19ffe8ed6a485f17e6eac82505be8c108897f15e68badcbb", + "sha256:31d0aeca8d8ee2301c62c5c340e0889d653b1280d68f9fa203982cb6337b050e", + "sha256:44c7f99ca17ebbcc96fc54ed00b454d8313f1eac28c563098d8b901025aff941", + "sha256:5471444f53f9db6a1f1f11f5dbc173228881df8446380b6b98f90afb8fd8348e", + "sha256:561bca3b1bde6d6564306eb05848fd155136e9c3a25d2961129b1e2edba22fce", + "sha256:5bf58e1d2c2f55365c06e8cb5abe067b88ca2e5550fb62009c41df4b54505acf", + "sha256:6b7163d1e85d76b0815df63fcc310daec02b44532bb433f743142d4febcb181f", + "sha256:766d79cddad95f5f6020037fe60ea8b98578afdf0c59d5a60c106c1bdd886303", + "sha256:770b7372d5ca68308ff66d7baee53369fa5ce985f84bcb6aa1948c1f2f7b02f2", + "sha256:7ab178da777fc0f55b6aef5a755f99726e8e4b75e3903954df07b27059b54fcf", + "sha256:8078305e77c2f6649d36b24d8778096413e474d9d7892c6f92cfb589c9d71b2e", + "sha256:85600b63a386d860eeaa955e9335e18dd0d7e5477e9214825abf2c2884488369", + "sha256:857d9b939ae128be1c0c792eb885c7ff6a386b9dea899ac4b06f4d90a31f9d87", + "sha256:87a41630c90c179fa5c593400f30a467c498972c702f348d41e19dafeb1d319e", + "sha256:8805d486c6128cc0fcc8ecf16c4095d99a8693a541ef851429ab334e028a4a97", + "sha256:8d71b7a89c306a41ccc7741fc9409b14f5b86727455c2a1c0c7cfcb0f784e1f2", + "sha256:9e1b80bd65f8f160880cb4dad7f55697f6d37b2d7f251fc0c2128e811928f369", + "sha256:9e290c84a145ae2411ee0ec9913c41cd7500e2e7485fe93632434d84ef4fda67", + "sha256:9ec9f88b5bc94bd99372f27cdd53af1c92ba06717380b127733b953cfb181174", + "sha256:a0a02a8b4ba6deadf706d5f849539b3685b72b186a3c9ef5d43e8972ed60fb6f", + "sha256:a4059c59519f5940e01a071f74ae2a60ea8f6185b03d22a09d40c7959a36b16b", + "sha256:a6e028c2a6da2ebfa2365a5b32531d311fbfec0e3600fc27e901b64f0ff7e54e", + "sha256:adcdebf9f8463df4120c427cf6c9aed39258bccd03ed37b6939e7a145d64d6e0", + "sha256:bdec982610259d07156a58f80b8c3e69be7751a9208bc577b059c5193d087fad", + "sha256:cefc4d4251ffb73feb303d4b7e9d6c367cb60f2db16d259ea28b114045f965aa", + "sha256:d4145c8aa6afbac10ad27e408f7ce15992fe89ba5d0b4abca31c0c2729864c03", + "sha256:da76dc5ad719ee99de5ea28a5629ff92172cbb4a70d8a6ae3a5b7a53c7382ce1", + "sha256:dde2452c08ef8b6426ccab6b5b6de9f06d836d9937d6870e68153cbf8cb49348", + "sha256:e3d88091d2539a4868750914a6fe7b9ec50e42b913851fc1b77423b5bd918530", + "sha256:f9c67cfe6278499d7f83559dc6322a8bbb108e307817a3d7acbfea807b3603cc" + ], + "version": "==1.22.0" }, "gunicorn": { "hashes": [ @@ -1384,10 +1384,10 @@ }, "ipywidgets": { "hashes": [ - "sha256:0f2b5cde9f272cb49d52f3f0889fdd1a7ae1e74f37b48dac35a83152780d2b7b", - "sha256:a3e224f430163f767047ab9a042fc55adbcab0c24bbe6cf9f306c4f89fdf0ba3" + "sha256:cb263c6974aca902d00a435711823bb4aaf6614a5f997f517e15fa84151e8fa2", + "sha256:eab6060f20f7f10d91f6efc8d33f9fd22133406980fcaee2738d836a910402f4" ], - "version": "==7.4.2" + "version": "==7.5.0" }, "isodate": { "hashes": [ @@ -1398,10 +1398,10 @@ }, "isort": { "hashes": [ - "sha256:c40744b6bc5162bbb39c1257fe298b7a393861d50978b565f3ccd9cb9de0182a", - "sha256:f57abacd059dc3bd666258d1efb0377510a89777fda3e3274e3c01f7c03ae22d" + "sha256:54da7e92468955c4fceacd0c86bd0ec997b0e1ee80d97f67c35a78b719dccab1", + "sha256:6e811fcb295968434526407adb8796944f1988c5b65e8139058f2014cbe100fd" ], - "version": "==4.3.20" + "version": "==4.3.21" }, "itsdangerous": { "hashes": [ @@ -1458,10 +1458,10 @@ }, "jupyter-client": { "hashes": [ - "sha256:b5f9cb06105c1d2d30719db5ffb3ea67da60919fb68deaefa583deccd8813551", - "sha256:c44411eb1463ed77548bc2d5ec0d744c9b81c4a542d9637c7a52824e2121b987" + "sha256:73a809a2964afa07adcc1521537fddb58c2ffbb7e84d53dc5901cf80480465b3", + "sha256:98e8af5edff5d24e4d31e73bc21043130ae9d955a91aa93fc0bc3b1d0f7b5880" ], - "version": "==5.2.4" + "version": "==5.3.1" }, "jupyter-console": { "hashes": [ @@ -1472,10 +1472,10 @@ }, "jupyter-core": { "hashes": [ - "sha256:927d713ffa616ea11972534411544589976b2493fc7e09ad946e010aa7eb9970", - "sha256:ba70754aa680300306c699790128f6fbd8c306ee5927976cbe48adacf240c0b7" + "sha256:2c6e7c1e9f2ac45b5c2ceea5730bc9008d92fe59d0725eac57b04c0edfba24f7", + "sha256:f4fa22d6cf25f34807c995f22d2923693575c70f02557bcbfbe59bd5ec8d8b84" ], - "version": "==4.4.0" + "version": "==4.5.0" }, "keras": { "hashes": [ @@ -1494,10 +1494,10 @@ }, "keras-maskrcnn": { "hashes": [ - "sha256:7cbadcf5d8a41e64ebd19157253f0357f2c46f289584c6f58fd38a19f7cc3509" + "sha256:9e2258390d749986fe6dc05aabdd7198ae689e60696b6a5568929379457a2e98" ], "index": "pypi", - "version": "==0.2.1" + "version": "==0.2.2" }, "keras-preprocessing": { "hashes": [ @@ -1514,10 +1514,10 @@ }, "keras-retinanet": { "hashes": [ - "sha256:257b77ca46c6846d8e9260e4eb1c77ab9b564c512bb5ebe23e72fc46b5c41899" + "sha256:6d4da9d83a9a82cb025298a88256546bc625e9c78cd4f1bbec576d11295910b6" ], "index": "pypi", - "version": "==0.5.0" + "version": "==0.5.1" }, "kiwisolver": { "hashes": [ @@ -1554,10 +1554,10 @@ }, "kombu": { "hashes": [ - "sha256:056a31cc95b10ca4eb0d4ebcba714007b1db1c6c45c8e2d139fe91933481b00b", - "sha256:af5b0f892b081f49d95c772a241fb7687f4cc3e42c94328f118dffb1f4e161e5" + "sha256:55b71d3785def3470a16217fe0780f9e6f95e61bf9ad39ef8dce0177224eab77", + "sha256:eb365ea795cd7e629ba2f1f398e0c3ba354b91ef4de225ffdf6ab45fdfc7d581" ], - "version": "==4.6.1" + "version": "==4.6.3" }, "lazy-object-proxy": { "hashes": [ @@ -1584,10 +1584,17 @@ }, "libarchive": { "hashes": [ - "sha256:37e8cca1eb85d30583cdcffc58116d83abc09be7549d5d6c9ead563c0a8d7b04" + "sha256:829dc298a08877f62335d528973bc034f7c1e8a03c16bfc1fa561e164e76a365" ], "index": "pypi", - "version": "==0.4.6" + "version": "==0.4.7" + }, + "limits": { + "hashes": [ + "sha256:9df578f4161017d79f5188609f1d65f6b639f8aad2914c3960c9252e56a0ff95", + "sha256:a017b8d9e9da6761f4574642149c337f8f540d4edfe573fb91ad2c4001a2bc76" + ], + "version": "==1.3" }, "lru-dict": { "hashes": [ @@ -1746,10 +1753,10 @@ }, "msrest": { "hashes": [ - "sha256:05538c68251eb0c81bd2010524d8ff36d4266ec0669338fbdcecfd23c733231c", - "sha256:8143093308975f815f968b0d2a1ac8e26ba217eb6d03f3f27aac616aa3a25bd0" + "sha256:2c0909570913785a4408a17286e151f3b28d39277113e5c63378572f7395c660", + "sha256:c9e9cbb0c47745f9f5c82cce60849d7c3ec9e33fc6fad9e2987b7657ad1ba479" ], - "version": "==0.6.7" + "version": "==0.6.8" }, "msrestazure": { "hashes": [ @@ -1774,11 +1781,11 @@ }, "nbstripout": { "hashes": [ - "sha256:814efbe00988445b2c3f3d1944c9f296a556e2b14a060f7b25372881c2e497d4", - "sha256:8f085e26e60e9d9c0710748510d1c763c8f63905cb16df7658b35a2936e8ca2b" + "sha256:1960caf7d1c1e281126c6c5cb98053db89eca8aaa616b58eed381e3e1508c0f4", + "sha256:d35c553f724d3fb7ec9e9602c6e55a75101064a6bbec4f8c28e8c84d6e3dd060" ], "index": "pypi", - "version": "==0.3.5" + "version": "==0.3.6" }, "networkx": { "hashes": [ @@ -1841,10 +1848,10 @@ }, "oauthlib": { "hashes": [ - "sha256:0ce32c5d989a1827e3f1148f98b9085ed2370fc939bf524c9c851d8714797298", - "sha256:3e1e14f6cde7e5475128d30e97edc3bfb4dc857cb884d8714ec161fdbb3b358e" + "sha256:40a63637707e9163eda62d0f5345120c65e001a790480b8256448543c1f78f66", + "sha256:b4d99ae8ccfb7d33ba9591b59355c64eef5241534aa3da2e4c0435346b84bc8e" ], - "version": "==3.0.1" + "version": "==3.0.2" }, "opencv-python": { "hashes": [ @@ -1905,6 +1912,32 @@ "index": "pypi", "version": "==2.15.0" }, + "pandas": { + "hashes": [ + "sha256:071e42b89b57baa17031af8c6b6bbd2e9a5c68c595bc6bf9adabd7a9ed125d3b", + "sha256:17450e25ae69e2e6b303817bdf26b2cd57f69595d8550a77c308be0cd0fd58fa", + "sha256:17916d818592c9ec891cbef2e90f98cc85e0f1e89ed0924c9b5220dc3209c846", + "sha256:2538f099ab0e9f9c9d09bbcd94b47fd889bad06dc7ae96b1ed583f1dc1a7a822", + "sha256:366f30710172cb45a6b4f43b66c220653b1ea50303fbbd94e50571637ffb9167", + "sha256:42e5ad741a0d09232efbc7fc648226ed93306551772fc8aecc6dce9f0e676794", + "sha256:4e718e7f395ba5bfe8b6f6aaf2ff1c65a09bb77a36af6394621434e7cc813204", + "sha256:4f919f409c433577a501e023943e582c57355d50a724c589e78bc1d551a535a2", + "sha256:4fe0d7e6438212e839fc5010c78b822664f1a824c0d263fd858f44131d9166e2", + "sha256:5149a6db3e74f23dc3f5a216c2c9ae2e12920aa2d4a5b77e44e5b804a5f93248", + "sha256:627594338d6dd995cfc0bacd8e654cd9e1252d2a7c959449228df6740d737eb8", + "sha256:83c702615052f2a0a7fb1dd289726e29ec87a27272d775cb77affe749cca28f8", + "sha256:8c872f7fdf3018b7891e1e3e86c55b190e6c5cee70cab771e8f246c855001296", + "sha256:90f116086063934afd51e61a802a943826d2aac572b2f7d55caaac51c13db5b5", + "sha256:a3352bacac12e1fc646213b998bce586f965c9d431773d9e91db27c7c48a1f7d", + "sha256:bcdd06007cca02d51350f96debe51331dec429ac8f93930a43eb8fb5639e3eb5", + "sha256:c1bd07ebc15285535f61ddd8c0c75d0d6293e80e1ee6d9a8d73f3f36954342d0", + "sha256:c9a4b7c55115eb278c19aa14b34fcf5920c8fe7797a09b7b053ddd6195ea89b3", + "sha256:cc8fc0c7a8d5951dc738f1c1447f71c43734244453616f32b8aa0ef6013a5dfb", + "sha256:d7b460bc316064540ce0c41c1438c416a40746fd8a4fb2999668bf18f3c4acf1" + ], + "index": "pypi", + "version": "==0.24.2" + }, "pandocfilters": { "hashes": [ "sha256:b3dd70e169bb5449e6bc6ff96aea89c5eea8c5f6ab5e207fc2f521a2cf4a0da9" @@ -1913,11 +1946,11 @@ }, "pathlib2": { "hashes": [ - "sha256:25199318e8cc3c25dcb45cbe084cc061051336d5a9ea2a12448d3d8cb748f742", - "sha256:5887121d7f7df3603bca2f710e7219f3eca0eb69e0b7cc6e0a022e155ac931a7" + "sha256:2156525d6576d21c4dcaddfa427fae887ef89a7a9de5cbfe0728b3aafa78427e", + "sha256:446014523bb9be5c28128c4d2a10ad6bb60769e78bd85658fe44a450674e0ef8" ], "markers": "python_version in '2.6 2.7 3.2 3.3'", - "version": "==2.3.3" + "version": "==2.3.4" }, "pbr": { "hashes": [ @@ -1951,34 +1984,34 @@ }, "pillow": { "hashes": [ - "sha256:15c056bfa284c30a7f265a41ac4cbbc93bdbfc0dfe0613b9cb8a8581b51a9e55", - "sha256:1a4e06ba4f74494ea0c58c24de2bb752818e9d504474ec95b0aa94f6b0a7e479", - "sha256:1c3c707c76be43c9e99cb7e3d5f1bee1c8e5be8b8a2a5eeee665efbf8ddde91a", - "sha256:1fd0b290203e3b0882d9605d807b03c0f47e3440f97824586c173eca0aadd99d", - "sha256:24114e4a6e1870c5a24b1da8f60d0ba77a0b4027907860188ea82bd3508c80eb", - "sha256:258d886a49b6b058cd7abb0ab4b2b85ce78669a857398e83e8b8e28b317b5abb", - "sha256:33c79b6dd6bc7f65079ab9ca5bebffb5f5d1141c689c9c6a7855776d1b09b7e8", - "sha256:367385fc797b2c31564c427430c7a8630db1a00bd040555dfc1d5c52e39fcd72", - "sha256:3c1884ff078fb8bf5f63d7d86921838b82ed4a7d0c027add773c2f38b3168754", - "sha256:44e5240e8f4f8861d748f2a58b3f04daadab5e22bfec896bf5434745f788f33f", - "sha256:46aa988e15f3ea72dddd81afe3839437b755fffddb5e173886f11460be909dce", - "sha256:74d90d499c9c736d52dd6d9b7221af5665b9c04f1767e35f5dd8694324bd4601", - "sha256:809c0a2ce9032cbcd7b5313f71af4bdc5c8c771cb86eb7559afd954cab82ebb5", - "sha256:85d1ef2cdafd5507c4221d201aaf62fc9276f8b0f71bd3933363e62a33abc734", - "sha256:8c3889c7681af77ecfa4431cd42a2885d093ecb811e81fbe5e203abc07e0995b", - "sha256:9218d81b9fca98d2c47d35d688a0cea0c42fd473159dfd5612dcb0483c63e40b", - "sha256:9aa4f3827992288edd37c9df345783a69ef58bd20cc02e64b36e44bcd157bbf1", - "sha256:9d80f44137a70b6f84c750d11019a3419f409c944526a95219bea0ac31f4dd91", - "sha256:b7ebd36128a2fe93991293f997e44be9286503c7530ace6a55b938b20be288d8", - "sha256:c4c78e2c71c257c136cdd43869fd3d5e34fc2162dc22e4a5406b0ebe86958239", - "sha256:c6a842537f887be1fe115d8abb5daa9bc8cc124e455ff995830cc785624a97af", - "sha256:cf0a2e040fdf5a6d95f4c286c6ef1df6b36c218b528c8a9158ec2452a804b9b8", - "sha256:cfd28aad6fc61f7a5d4ee556a997dc6e5555d9381d1390c00ecaf984d57e4232", - "sha256:dca5660e25932771460d4688ccbb515677caaf8595f3f3240ec16c117deff89a", - "sha256:de7aedc85918c2f887886442e50f52c1b93545606317956d65f342bd81cb4fc3", - "sha256:e6c0bbf8e277b74196e3140c35f9a1ae3eafd818f7f2d3a15819c49135d6c062" - ], - "version": "==6.0.0" + "sha256:0804f77cb1e9b6dbd37601cee11283bba39a8d44b9ddb053400c58e0c0d7d9de", + "sha256:0ab7c5b5d04691bcbd570658667dd1e21ca311c62dcfd315ad2255b1cd37f64f", + "sha256:0b3e6cf3ea1f8cecd625f1420b931c83ce74f00c29a0ff1ce4385f99900ac7c4", + "sha256:365c06a45712cd723ec16fa4ceb32ce46ad201eb7bbf6d3c16b063c72b61a3ed", + "sha256:38301fbc0af865baa4752ddae1bb3cbb24b3d8f221bf2850aad96b243306fa03", + "sha256:3aef1af1a91798536bbab35d70d35750bd2884f0832c88aeb2499aa2d1ed4992", + "sha256:3fe0ab49537d9330c9bba7f16a5f8b02da615b5c809cdf7124f356a0f182eccd", + "sha256:45a619d5c1915957449264c81c008934452e3fd3604e36809212300b2a4dab68", + "sha256:49f90f147883a0c3778fd29d3eb169d56416f25758d0f66775db9184debc8010", + "sha256:571b5a758baf1cb6a04233fb23d6cf1ca60b31f9f641b1700bfaab1194020555", + "sha256:5ac381e8b1259925287ccc5a87d9cf6322a2dc88ae28a97fe3e196385288413f", + "sha256:6153db744a743c0c8c91b8e3b9d40e0b13a5d31dbf8a12748c6d9bfd3ddc01ad", + "sha256:6fd63afd14a16f5d6b408f623cc2142917a1f92855f0df997e09a49f0341be8a", + "sha256:70acbcaba2a638923c2d337e0edea210505708d7859b87c2bd81e8f9902ae826", + "sha256:70b1594d56ed32d56ed21a7fbb2a5c6fd7446cdb7b21e749c9791eac3a64d9e4", + "sha256:76638865c83b1bb33bcac2a61ce4d13c17dba2204969dedb9ab60ef62bede686", + "sha256:7b2ec162c87fc496aa568258ac88631a2ce0acfe681a9af40842fc55deaedc99", + "sha256:7cee2cef07c8d76894ebefc54e4bb707dfc7f258ad155bd61d87f6cd487a70ff", + "sha256:7d16d4498f8b374fc625c4037742fbdd7f9ac383fd50b06f4df00c81ef60e829", + "sha256:b50bc1780681b127e28f0075dfb81d6135c3a293e0c1d0211133c75e2179b6c0", + "sha256:bd0582f831ad5bcad6ca001deba4568573a4675437db17c4031939156ff339fa", + "sha256:cfd40d8a4b59f7567620410f966bb1f32dc555b2b19f82a91b147fac296f645c", + "sha256:e3ae410089de680e8f84c68b755b42bc42c0ceb8c03dbea88a5099747091d38e", + "sha256:e9046e559c299b395b39ac7dbf16005308821c2f24a63cae2ab173bd6aa11616", + "sha256:ef6be704ae2bc8ad0ebc5cb850ee9139493b0fc4e81abcc240fb392a63ebc808", + "sha256:f8dc19d92896558f9c4317ee365729ead9d7bbcf2052a9a19a3ef17abbb8ac5b" + ], + "version": "==6.1.0" }, "pprofile": { "hashes": [ @@ -1996,9 +2029,9 @@ }, "prometheus-client": { "hashes": [ - "sha256:ee0c90350595e4a9f36591f291e6f9933246ea67d7cd7d1d6139a9781b14eaae" + "sha256:71cd24a2b3eb335cb800c7159f423df1bd4dcd5171b234be15e3f31ec9f622da" ], - "version": "==0.7.0" + "version": "==0.7.1" }, "prompt-toolkit": { "hashes": [ @@ -2010,26 +2043,26 @@ }, "protobuf": { "hashes": [ - "sha256:03f43eac9d5b651f976e91cf46a25b75e5779d98f0f4114b0abfed83376d75f8", - "sha256:0c94b21e6de01362f91a86b372555d22a60b59708599ca9d5032ae9fdf8e3538", - "sha256:2d2a9f30f61f4063fadd7fb68a2510a6939b43c0d6ceeec5c4704f22225da28e", - "sha256:34a0b05fca061e4abb77dd180209f68d8637115ff319f51e28a6a9382d69853a", - "sha256:358710fd0db25372edcf1150fa691f48376a134a6c69ce29f38f185eea7699e6", - "sha256:41e47198b94c27ba05a08b4a95160656105745c462af574e4bcb0807164065c0", - "sha256:8c61cc8a76e9d381c665aecc5105fa0f1878cf7db8b5cd17202603bcb386d0fc", - "sha256:a6eebc4db759e58fdac02efcd3028b811effac881d8a5bad1996e4e8ee6acb47", - "sha256:a9c12f7c98093da0a46ba76ec40ace725daa1ac4038c41e4b1466afb5c45bb01", - "sha256:cb95068492ba0859b8c9e61fa8ba206a83c64e5d0916fb4543700b2e2b214115", - "sha256:cd98476ce7bb4dcd6a7b101f5eecdc073dafea19f311e36eb8fba1a349346277", - "sha256:ce64cfbea18c535176bdaa10ba740c0fc4c6d998a3f511c17bedb0ae4b3b167c", - "sha256:dcbb59eac73fd454e8f2c5fba9e3d3320fd4707ed6a9d3ea3717924a6f0903ea", - "sha256:dd67f34458ae716029e2a71ede998e9092493b62a519236ca52e3c5202096c87", - "sha256:e3c96056eb5b7284a20e256cb0bf783c8f36ad82a4ae5434a7b7cd02384144a7", - "sha256:f612d584d7a27e2f39e7b17878430a959c1bc09a74ba09db096b468558e5e126", - "sha256:f6de8a7d6122297b81566e5bd4df37fd5d62bec14f8f90ebff8ede1c9726cd0a", - "sha256:fa529d9261682b24c2aaa683667253175c9acebe0a31105394b221090da75832" - ], - "version": "==3.8.0" + "sha256:05c36022fef3c7d3562ac22402965c0c2b9fe8421f459bb377323598996e407f", + "sha256:139b7eadcca0a861d60b523cb37d9475505e0dfb07972436b15407c2b968d87e", + "sha256:15f683006cb77fb849b1f561e509b03dd2b7dcc749086b8dd1831090d0ba4740", + "sha256:2ad566b7b7cdd8717c7af1825e19f09e8fef2787b77fcb979588944657679604", + "sha256:35cfcf97642ef62108e10a9431c77733ec7eaab8e32fe4653de20403429907cb", + "sha256:387822859ecdd012fdc25ec879f7f487da6e1d5b1ae6115e227e6be208836f71", + "sha256:4df14cbe1e7134afcfdbb9f058949e31c466de27d9b2f7fb4da9e0b67231b538", + "sha256:586c4ca37a7146d4822c700059f150ac3445ce0aef6f3ea258640838bb892dc2", + "sha256:58b11e530e954d29ab3180c48dc558a409f705bf16739fd4e0d3e07924ad7add", + "sha256:63c8c98ccb8c95f41c18fb829aeeab21c6249adee4ed75354125bdc44488f30e", + "sha256:72edcbacd0c73eef507d2ff1af99a6c27df18e66a3ff4351e401182e4de62b03", + "sha256:83dc8a561b3b954fd7002c690bb83278b8d1742a1e28abba9aaef28b0c8b437d", + "sha256:913171ecc84c2726b86574e40549a0ea619d569657c5a5ff782a3be7d81401a5", + "sha256:aabb7c741d3416671c3e6fe7c52970a226e6a8274417a97d7d795f953fadef36", + "sha256:b3452bbda12b1cbe2187d416779de07b2ab4c497d83a050e43c344778763721d", + "sha256:c5d5b8d4a9212338297fa1fa44589f69b470c0ba1d38168b432d577176b386a8", + "sha256:d86ee389c2c4fc3cebabb8ce83a8e97b6b3b5dc727b7419c1ccdc7b6e545a233", + "sha256:f2db8c754de788ab8be5e108e1e967c774c0942342b4f8aaaf14063889a6cfdc" + ], + "version": "==3.9.0" }, "psutil": { "hashes": [ @@ -2067,16 +2100,10 @@ }, "pycurl": { "hashes": [ - "sha256:0f0cdfc7a92d4f2a5c44226162434e34f7d6967d3af416a6f1448649c09a25a4", - "sha256:10510a0016c862af467c6e069e051409f15f5831552bed03f5104b395a5d7dd1", - "sha256:208dd2c89e80d32a69397ba8a5cdb3bc0dc60f961a4f2a9662e5e1624dc799d1", - "sha256:6dc6ee5e7628400083471cba8044010860fe8b22e4dee05e42150a68047d7d9d", - "sha256:794bda39ea6fe434b6e1f58ab3bea9f0e6123fb43702fecd760eed6f1547b20a", - "sha256:dae7277e7c06da00947f3cd32c095b1e65eae09f07478ada4ea9dfa57020b646", - "sha256:eccea049aef47decc380746b3ff242d95636d578c907d0eab3b00918292d6c48" + "sha256:6f08330c5cf79fa8ef68b9912b9901db7ffd34b63e225dce74db56bb21deda8e" ], "index": "pypi", - "version": "==7.43.0.2" + "version": "==7.43.0.3" }, "pygame": { "hashes": [ @@ -2126,11 +2153,18 @@ }, "pylint": { "hashes": [ - "sha256:02c2b6d268695a8b64ad61847f92e611e6afcff33fd26c3a2125370c4662905d", - "sha256:ee1e85575587c5b58ddafa25e1c1b01691ef172e139fc25585e5d3f02451da93" + "sha256:367e3d49813d349a905390ac27989eff82ab84958731c5ef0bef867452cfdc42", + "sha256:97a42df23d436c70132971d1dcb9efad2fe5c0c6add55b90161e773caf729300" ], "index": "pypi", - "version": "==1.9.4" + "version": "==1.9.5" + }, + "pylogbeat": { + "hashes": [ + "sha256:11f3b1d04424151d406d8b844a8db6299442b4af1f10d5f622a6febf1ad5c41d", + "sha256:473494a0c798a560a8312ee662b333888181cf4db18cd8f87a8d7d1548beefd9" + ], + "version": "==1.0.2" }, "pymongo": { "hashes": [ @@ -2232,9 +2266,9 @@ }, "pyrsistent": { "hashes": [ - "sha256:16692ee739d42cf5e39cef8d27649a8c1fdb7aa99887098f1460057c5eb75c3a" + "sha256:50cffebc87ca91b9d4be2dcc2e479272bcb466b5a0487b6c271f7ddea6917e14" ], - "version": "==0.15.2" + "version": "==0.15.3" }, "pysdl2": { "hashes": [ @@ -2260,10 +2294,10 @@ }, "python-engineio": { "hashes": [ - "sha256:9e4e7109d05d80ce5414f13b16f66725c2b5d574099fd43d37b024e7ea1c4354", - "sha256:d3315d3f972bd9bd32e0738d45801a912f522177ff75094762f31a8c341d1f41" + "sha256:4850c3e04b2040e4fd262d1047797473d1815b37a073807e7b80304c1c5f4848", + "sha256:89b90ee3816ed440c68ac7b6143244ae7d56a46a49295fbac8e6696482e596d1" ], - "version": "==3.8.1" + "version": "==3.8.2.post1" }, "python-logstash": { "hashes": [ @@ -2272,12 +2306,20 @@ "index": "pypi", "version": "==0.4.6" }, + "python-logstash-async": { + "hashes": [ + "sha256:994894e8b7e168e56f21e302334c08203af102c7bc760cacdb8d3d0f5aa74cea", + "sha256:ccd528a0a9c6b7aabd9944c01d628e9d6cc2149156011aafd3484c7c0abbce45" + ], + "index": "pypi", + "version": "==1.5.1" + }, "python-socketio": { "hashes": [ - "sha256:89a48591a8850c1f30d735f8e5a0294846da245a9b8940c39e1106e460c7a14e", - "sha256:c7ffeac3d81f2d8d63b3ec7ed1e2d4478cc84aa1da666c1934c19432f4b8c0f8" + "sha256:335bd0fab481d65edce4ab82c3bb5cac950afa843329ea7c38777cd56c8eba38", + "sha256:efec4844456791b7d702efefd543ed67a8e314ca45efb8f0bfca7ae18fdee60a" ], - "version": "==4.1.0" + "version": "==4.2.0" }, "python-utils": { "hashes": [ @@ -2351,34 +2393,34 @@ }, "pyzmq": { "hashes": [ - "sha256:1651e52ed91f0736afd6d94ef9f3259b5534ce8beddb054f3d5ca989c4ef7c4f", - "sha256:5ccb9b3d4cd20c000a9b75689d5add8cd3bce67fcbd0f8ae1b59345247d803af", - "sha256:5e120c4cd3872e332fb35d255ad5998ebcee32ace4387b1b337416b6b90436c7", - "sha256:5e2a3707c69a7281a9957f83718815fd74698cba31f6d69f9ed359921f662221", - "sha256:63d51add9af8d0442dc90f916baf98fdc04e3b0a32afec4bfc83f8d85e72959f", - "sha256:65c5a0bdc49e20f7d6b03a661f71e2fda7a99c51270cafe71598146d09810d0d", - "sha256:66828fabe911aa545d919028441a585edb7c9c77969a5fea6722ef6e6ece38ab", - "sha256:7d79427e82d9dad6e9b47c0b3e7ae5f9d489b1601e3a36ea629bb49501a4daf3", - "sha256:824ee5d3078c4eae737ffc500fbf32f2b14e6ec89b26b435b7834febd70120cf", - "sha256:89dc0a83cccec19ff3c62c091e43e66e0183d1e6b4658c16ee4e659518131494", - "sha256:8b319805f6f7c907b101c864c3ca6cefc9db8ce0791356f180b1b644c7347e4c", - "sha256:90facfb379ab47f94b19519c1ecc8ec8d10813b69d9c163117944948bdec5d15", - "sha256:a0a178c7420021fc0730180a914a4b4b3092ce9696ceb8e72d0f60f8ce1655dd", - "sha256:a7a89591ae315baccb8072f216614b3e59aed7385aef4393a6c741783d6ee9cf", - "sha256:ba2578f0ae582452c02ed9fac2dc477b08e80ce05d2c0885becf5fff6651ccb0", - "sha256:c69b0055c55702f5b0b6b354133e8325b9a56dbc80e1be2d240bead253fb9825", - "sha256:ca434e1858fe222380221ddeb81e86f45522773344c9da63c311d17161df5e06", - "sha256:d4b8ecfc3d92f114f04d5c40f60a65e5196198b827503341521dda12d8b14939", - "sha256:d706025c47b09a54f005953ebe206f6d07a22516776faa4f509aaff681cc5468", - "sha256:d8f27e958f8a2c0c8ffd4d8855c3ce8ac3fa1e105f0491ce31729aa2b3229740", - "sha256:dbd264298f76b9060ce537008eb989317ca787c857e23cbd1b3ddf89f190a9b1", - "sha256:e926d66f0df8fdbf03ba20583af0f215e475c667fb033d45fd031c66c63e34c9", - "sha256:efc3bd48237f973a749f7312f68062f1b4ca5c2032a0673ca3ea8e46aa77187b", - "sha256:f59bc782228777cbfe04555707a9c56d269c787ed25d6d28ed9d0fbb41cb1ad2", - "sha256:f8da5322f4ff5f667a0d5a27e871b560c6637153c81e318b35cb012b2a98835c" - ], - "index": "pypi", - "version": "==18.0.1" + "sha256:00dd015159eaeb1c0731ad49310e1f5d839c9a35a15e4f3267f5052233fad99b", + "sha256:03913b6beb8e7b417b9910b0ee1fd5d62e9626d218faefbe879d70714ceab1a2", + "sha256:13f17386df81d5e6efb9a4faea341d8de22cdc82e49a326dded26e33f42a3112", + "sha256:16c6281d96885db1e15f7047ddc1a8f48ff4ea35d31ca709f4d2eb39f246d356", + "sha256:17efab4a804e31f58361631256d660214204046f9e2b962738b171b9ad674ea7", + "sha256:2b79919ddeff3d3c96aa6087c21d294c8db1c01f6bfeee73324944683685f419", + "sha256:2f832e4711657bb8d16ea1feba860f676ec5f14fb9fe3b449b5953a60e89edae", + "sha256:31a11d37ac73107363b47e14c94547dbfc6a550029c3fe0530be443199026fc2", + "sha256:33a3e928e6c3138c675e1d6702dd11f6b7050177d7aab3fc322db6e1d2274490", + "sha256:34a38195a6d3a9646cbcdaf8eb245b4d935c7a57f7e1b3af467814bc1a92467e", + "sha256:42900054f1500acef6df7428edf806abbf641bf92eb9ceded24aa863397c3bae", + "sha256:4ccc7f3c63aa9d744dadb62c49eda2d0e7de55649b80c45d7c684d70161a69af", + "sha256:5b220c37c346e6575db8c88a940c1fc234f99ce8e0068c408919bb8896c4b6d2", + "sha256:6074848da5c8b44a1ca40adf75cf65aa92bc80f635e8249aa8f37a69b2b9b6f5", + "sha256:61a4155964bd4a14ef95bf46cb1651bcf8dcbbed8c0108e9c974c1fcbb57788f", + "sha256:62b5774688326600c52f587f7a033ca6b6284bef4c8b1b5fda32480897759eac", + "sha256:65a9ffa4f9f085d696f16fd7541f34b3c357d25fe99c90e3bce2ea59c3b5b4b6", + "sha256:76a077d2c30f8adc5e919a55985a784b96aeca69b53c1ea6fd5723d3ae2e6f53", + "sha256:8e5b4c51557071d6379d6dc1f54f35e9f6a137f5e84e102efb869c8d3c13c8ff", + "sha256:917f73e07cc04f0678a96d93e7bb8b1adcccdde9ccfe202e622814f4d1d1ecfd", + "sha256:91c75d3c4c357f9643e739db9e79ab9681b2f6ae8ec5678d6ef2ea0d01532596", + "sha256:923dd91618b100bb4c92ab9ed7b65825a595b8524a094ce03c7cb2aaae7d353b", + "sha256:9849054e0355e2bc7f4668766a25517ba76095031c9ff5e39ae8949cee5bb024", + "sha256:c9d453933f0e3f44b9759189f2a18aa765f7f1a4345c727c18ebe8ad0d748d26", + "sha256:cb7514936277abce64c2f4c56883e5704d85ed04d98d2d432d1c6764003bb003" + ], + "index": "pypi", + "version": "==18.0.2" }, "qtconsole": { "hashes": [ @@ -2523,6 +2565,14 @@ "index": "pypi", "version": "==1.2.2" }, + "seaborn": { + "hashes": [ + "sha256:42e627b24e849c2d3bbfd059e00005f6afbc4a76e4895baf44ae23fe8a4b09a5", + "sha256:76c83f794ca320fb6b23a7c6192d5e185a5fcf4758966a0c0a54baee46d41e2f" + ], + "index": "pypi", + "version": "==0.9.0" + }, "send2trash": { "hashes": [ "sha256:60001cc07d707fe247c94f74ca6ac0d3255aabcb930529690897ca2a39db28b2", @@ -2612,11 +2662,11 @@ }, "supervisor": { "hashes": [ - "sha256:a3289b9124e59aee1621d43b55cd1634468cb3212d09c5b0114a3183cc080cca", - "sha256:f768abc073e8702892718938b8a0ab98ebcb91c2afcb39bf2cb570d3eb51149e" + "sha256:43e87c7b572a94acdb586aaebb06844dae1aa02856b984c5a738032abd753fb7", + "sha256:9644990d21a1ba03b1a7ac5e9a0c0c62e12822e258f9e98f4a0b128461b3f10a" ], "index": "pypi", - "version": "==4.0.3" + "version": "==4.0.4" }, "tenacity": { "hashes": [ @@ -2635,9 +2685,9 @@ }, "tensorflow-estimator": { "hashes": [ - "sha256:7cfdaa3e83e3532f31713713feb98be7ea9f3065722be4267e49b6c301271419" + "sha256:ca073f66063407a091d610ec1b22e39ea30248710198cc6f13769320bdbe3992" ], - "version": "==1.13.0" + "version": "==1.14.0" }, "tensorflow-gpu": { "hashes": [ @@ -2713,12 +2763,12 @@ }, "typing": { "hashes": [ - "sha256:4027c5f6127a6267a435201981ba156de91ad0d1d98e9ddc2aa173453453492d", - "sha256:57dcf675a99b74d64dacf6fba08fb17cf7e3d5fdff53d4a30ea2a5e7e52543d4", - "sha256:a4c8473ce11a65999c8f59cb093e70686b6c84c98df58c1dae9b3b196089858a" + "sha256:38566c558a0a94d6531012c8e917b1b8518a41e418f7f15f00e129cc80162ad3", + "sha256:53765ec4f83a2b720214727e319607879fec4acde22c4fbb54fa2604e79e44ce", + "sha256:84698954b4e6719e912ef9a42a2431407fe3755590831699debda6fba92aac55" ], "markers": "python_version < '3.5'", - "version": "==3.6.6" + "version": "==3.7.4" }, "urllib3": { "hashes": [ @@ -2730,10 +2780,10 @@ }, "utm": { "hashes": [ - "sha256:a6608a67df84418fd959a79b228b90ab55b2ae877827f9c210947104c5a75d0e" + "sha256:07e55707ed660eec1ae983bd54a406c437962618a6261b38d70592fe30f5f508" ], "index": "pypi", - "version": "==0.4.2" + "version": "==0.5.0" }, "uwsgi": { "hashes": [ @@ -2780,10 +2830,10 @@ }, "werkzeug": { "hashes": [ - "sha256:865856ebb55c4dcd0630cdd8f3331a1847a819dda7e8c750d3db6f2aa6c0209c", - "sha256:a0b915f0815982fb2a09161cb8f31708052d0951c3ba433ccc5e1aa276507ca6" + "sha256:87ae4e5b5366da2347eb3116c0e6c681a0e939a33b2805e2c0cbd282664932c4", + "sha256:a13b74dd3c45f758d4ebdb224be8f1ab8ef58b3c0ffc1783a8c7d9f4f50227e6" ], - "version": "==0.15.4" + "version": "==0.15.5" }, "wheel": { "hashes": [ @@ -2795,16 +2845,16 @@ }, "widgetsnbextension": { "hashes": [ - "sha256:14b2c65f9940c9a7d3b70adbe713dbd38b5ec69724eebaba034d1036cf3d4740", - "sha256:fa618be8435447a017fd1bf2c7ae922d0428056cfc7449f7a8641edf76b48265" + "sha256:120f85acc3976450220b03b8933ce48678e518905cca69fc3c856ea5a0144196", + "sha256:8c9b4d73e388f2484296be18432d3cc0b8d59de243079a0db16a56c5571e1f86" ], - "version": "==3.4.2" + "version": "==3.5.0" }, "wrapt": { "hashes": [ - "sha256:4aea003270831cceb8a90ff27c4031da6ead7ec1886023b80ce0dfe0adf61533" + "sha256:565a021fd19419476b9362b05eeaa094178de64f8361e44468f9e9d7843901e1" ], - "version": "==1.11.1" + "version": "==1.11.2" } } } diff --git a/README.md b/README.md index dfbb76803083a2..f6e3fbe7971ee8 100644 --- a/README.md +++ b/README.md @@ -60,13 +60,13 @@ Supported Cars | Make | Model | Supported Package | Lateral | Longitudinal | No Accel Below | No Steer Below | Giraffe | | ---------------------| -------------------------| ---------------------| --------| ---------------| -----------------| ---------------|-------------------| -| Acura | ILX 2016-17 | AcuraWatch Plus | Yes | Yes | 25mph1| 25mph | Nidec | -| Acura | RDX 2018 | AcuraWatch Plus | Yes | Yes | 25mph1| 12mph | Nidec | +| Acura | ILX 2016-18 | AcuraWatch Plus | Yes | Yes | 25mph1| 25mph | Nidec | +| Acura | RDX 2016-18 | AcuraWatch Plus | Yes | Yes | 25mph1| 12mph | Nidec | | Buick3 | Regal 2018 | Adaptive Cruise | Yes | Yes | 0mph | 7mph | Custom7| | Chevrolet3| Malibu 2017 | Adaptive Cruise | Yes | Yes | 0mph | 7mph | Custom7| | Chevrolet3| Volt 2017-18 | Adaptive Cruise | Yes | Yes | 0mph | 7mph | Custom7| | Cadillac3 | ATS 2018 | Adaptive Cruise | Yes | Yes | 0mph | 7mph | Custom7| -| Chrysler | Pacifica 2018 | Adaptive Cruise | Yes | Stock | 0mph | 9mph | FCA | +| Chrysler | Pacifica 2017-18 | Adaptive Cruise | Yes | Stock | 0mph | 9mph | FCA | | Chrysler | Pacifica Hybrid 2017-18 | Adaptive Cruise | Yes | Stock | 0mph | 9mph | FCA | | Chrysler | Pacifica Hybrid 2019 | Adaptive Cruise | Yes | Stock | 0mph | 39mph | FCA | | GMC3 | Acadia Denali 2018 | Adaptive Cruise | Yes | Yes | 0mph | 7mph | Custom7| @@ -84,9 +84,9 @@ Supported Cars | Honda | Pilot 2019 | All | Yes | Yes | 25mph1| 12mph | Inverted Nidec | | Honda | Ridgeline 2017-19 | Honda Sensing | Yes | Yes | 25mph1| 12mph | Nidec | | Hyundai | Santa Fe 2019 | All | Yes | Stock | 0mph | 0mph | Custom6| -| Hyundai | Elantra 2017 | SCC + LKAS | Yes | Stock | 19mph | 34mph | Custom6| +| Hyundai | Elantra 2017-19 | SCC + LKAS | Yes | Stock | 19mph | 34mph | Custom6| | Hyundai | Genesis 2018 | All | Yes | Stock | 19mph | 34mph | Custom6| -| Jeep | Grand Cherokee 2017-18 | Adaptive Cruise | Yes | Stock | 0mph | 9mph | FCA | +| Jeep | Grand Cherokee 2016-18 | Adaptive Cruise | Yes | Stock | 0mph | 9mph | FCA | | Jeep | Grand Cherokee 2019 | Adaptive Cruise | Yes | Stock | 0mph | 39mph | FCA | | Kia | Optima 2019 | SCC + LKAS | Yes | Stock | 0mph | 0mph | Custom6| | Kia | Sorento 2018 | All | Yes | Stock | 0mph | 0mph | Custom6| @@ -96,8 +96,9 @@ Supported Cars | Subaru | Crosstrek 2018 | EyeSight | Yes | Stock | 0mph | 0mph | Custom4| | Subaru | Impreza 2019 | EyeSight | Yes | Stock | 0mph | 0mph | Custom4| | Toyota | Avalon 2016 | TSS-P | Yes | Yes2| 20mph1| 0mph | Toyota | -| Toyota | Camry 2018 | All | Yes | Stock | 0mph5 | 0mph | Toyota | -| Toyota | C-HR 2017-18 | All | Yes | Stock | 0mph | 0mph | Toyota | +| Toyota | Avalon 2017-18 | All | Yes | Yes2| 20mph1| 0mph | Toyota | +| Toyota | Camry 2018-19 | All | Yes | Stock | 0mph5 | 0mph | Toyota | +| Toyota | C-HR 2017-19 | All | Yes | Stock | 0mph | 0mph | Toyota | | Toyota | Corolla 2017-19 | All | Yes | Yes2| 20mph1| 0mph | Toyota | | Toyota | Corolla 2020 | All | Yes | Yes | 0mph | 0mph | Toyota | | Toyota | Corolla Hatchback 2019 | All | Yes | Yes | 0mph | 0mph | Toyota | @@ -110,6 +111,7 @@ Supported Cars | Toyota | Rav4 2017-18 | All | Yes | Yes2| 20mph1| 0mph | Toyota | | Toyota | Rav4 2019 | All | Yes | Yes | 0mph | 0mph | Toyota | | Toyota | Rav4 Hybrid 2017-18 | All | Yes | Yes2| 0mph | 0mph | Toyota | +| Toyota | Sienna 2018 | All | Yes | Yes2| 0mph | 0mph | Toyota | 1[Comma Pedal](https://community.comma.ai/wiki/index.php/Comma_Pedal) is used to provide stop-and-go capability to some of the openpilot-supported cars that don't currently support stop-and-go. Here is how to [build a Comma Pedal](https://medium.com/@jfrux/comma-pedal-building-with-macrofab-6328bea791e8). ***NOTE: The Comma Pedal is not officially supported by [comma.ai](https://comma.ai).***
2When disconnecting the Driver Support Unit (DSU), otherwise longitudinal control is stock ACC. For DSU locations, see [Toyota Wiki page](https://community.comma.ai/wiki/index.php/Toyota).
diff --git a/RELEASES.md b/RELEASES.md index f73b8daf83ebb8..07bbc6208d35e8 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,3 +1,10 @@ +Version 0.6.1 (2019-07-21) +======================== + * Remote SSH with comma prime and [ssh.comma.ai](https://ssh.comma.ai) + * Panda code Misra-c2012 compliance, tested against cppcheck coverage + * Lockout openpilot after 3 terminal alerts for driver distracted or unresponsive + * Toyota Sienna support thanks to wocsor! + Version 0.6 (2019-07-01) ======================== * New model, with double the pixels and ten times the temporal context! diff --git a/apk/ai.comma.plus.offroad.apk b/apk/ai.comma.plus.offroad.apk index 41920230b4fa09..6161bfa528b44b 100644 Binary files a/apk/ai.comma.plus.offroad.apk and b/apk/ai.comma.plus.offroad.apk differ diff --git a/common/file_helpers.py b/common/file_helpers.py index bdfe9d0ecf84b5..3300ae595afe17 100644 --- a/common/file_helpers.py +++ b/common/file_helpers.py @@ -28,7 +28,7 @@ def get_tmpdir_on_same_filesystem(path): normpath = os.path.normpath(path) parts = normpath.split("/") if len(parts) > 1: - if parts[1].startswith("raid"): + if parts[1].startswith("raid") or parts[1].startswith("datasets"): if len(parts) > 2 and parts[2] == "runner": return "/{}/runner/tmp".format(parts[1]) elif len(parts) > 2 and parts[2] == "aws": @@ -101,3 +101,18 @@ def atomic_write_in_dir(path, **kwargs): writer = AtomicWriter(path, **kwargs) return writer._open(_get_fileobject_func(writer, os.path.dirname(path))) +def atomic_write_in_dir_neos(path, contents, mode=None): + """ + Atomically writes contents to path using a temporary file in the same directory + as path. Useful on NEOS, where `os.link` (required by atomic_write_in_dir) is missing. + """ + + f = tempfile.NamedTemporaryFile(delete=False, prefix=".tmp", dir=os.path.dirname(path)) + f.write(contents) + f.flush() + if mode is not None: + os.fchmod(f.fileno(), mode) + os.fsync(f.fileno()) + f.close() + + os.rename(f.name, path) diff --git a/common/fingerprints.py b/common/fingerprints.py index ddc3bceb66ae8d..0e29e6c1e71139 100644 --- a/common/fingerprints.py +++ b/common/fingerprints.py @@ -28,10 +28,8 @@ def get_fingerprint_list(): def is_valid_for_fingerprint(msg, car_fingerprint): adr = msg.address - bus = msg.src # ignore addresses that are more than 11 bits - return (adr in car_fingerprint and car_fingerprint[adr] == len(msg.dat)) or \ - bus != 0 or adr >= 0x800 + return (adr in car_fingerprint and car_fingerprint[adr] == len(msg.dat)) or adr >= 0x800 def eliminate_incompatible_cars(msg, candidate_cars): diff --git a/common/params.py b/common/params.py index c374d5233e2b67..963debd091bf36 100755 --- a/common/params.py +++ b/common/params.py @@ -50,12 +50,14 @@ class UnknownKeyName(Exception): keys = { "AccessToken": [TxType.PERSISTENT], + "AthenadPid": [TxType.PERSISTENT], "CalibrationParams": [TxType.PERSISTENT], "CarParams": [TxType.CLEAR_ON_MANAGER_START, TxType.CLEAR_ON_PANDA_DISCONNECT], "CompletedTrainingVersion": [TxType.PERSISTENT], "ControlsParams": [TxType.PERSISTENT], "DoUninstall": [TxType.CLEAR_ON_MANAGER_START], "DongleId": [TxType.PERSISTENT], + "GithubSshKeys": [TxType.PERSISTENT], "GitBranch": [TxType.PERSISTENT], "GitCommit": [TxType.PERSISTENT], "GitRemote": [TxType.PERSISTENT], @@ -75,6 +77,7 @@ class UnknownKeyName(Exception): "ShouldDoUpdate": [TxType.CLEAR_ON_MANAGER_START], "SpeedLimitOffset": [TxType.PERSISTENT], "SubscriberInfo": [TxType.PERSISTENT], + "TermsVersion": [TxType.PERSISTENT], "TrainingVersion": [TxType.PERSISTENT], "Version": [TxType.PERSISTENT], } diff --git a/selfdrive/athena/athenad.py b/selfdrive/athena/athenad.py index 58918de81ae37c..d6d429d92b48b5 100755 --- a/selfdrive/athena/athenad.py +++ b/selfdrive/athena/athenad.py @@ -1,15 +1,22 @@ #!/usr/bin/env python2.7 import json +import jwt import os import random +import re +import select +import subprocess +import socket import time import threading import traceback import zmq import requests import six.moves.queue +from datetime import datetime, timedelta +from functools import partial from jsonrpc import JSONRPCResponseManager, dispatcher -from websocket import create_connection, WebSocketTimeoutException +from websocket import create_connection, WebSocketTimeoutException, ABNF from selfdrive.loggerd.config import ROOT import selfdrive.crash as crash @@ -21,6 +28,7 @@ ATHENA_HOST = os.getenv('ATHENA_HOST', 'wss://athena.comma.ai') HANDLER_THREADS = os.getenv('HANDLER_THREADS', 4) +LOCAL_PORT_WHITELIST = set([8022]) dispatcher["echo"] = lambda s: s payload_queue = six.moves.queue.Queue() @@ -49,6 +57,7 @@ def handle_long_poll(ws): thread.join() def jsonrpc_handler(end_event): + dispatcher["startLocalProxy"] = partial(startLocalProxy, end_event) while not end_event.is_set(): try: data = payload_queue.get(timeout=1) @@ -85,6 +94,109 @@ def uploadFileToUrl(fn, url, headers): ret = requests.put(url, data=f, headers=headers, timeout=10) return ret.status_code +def startLocalProxy(global_end_event, remote_ws_uri, local_port): + try: + cloudlog.event("athena startLocalProxy", remote_ws_uri=remote_ws_uri, local_port=local_port) + + if local_port not in LOCAL_PORT_WHITELIST: + raise Exception("Requested local port not whitelisted") + + params = Params() + dongle_id = params.get("DongleId") + private_key = open("/persist/comma/id_rsa").read() + identity_token = jwt.encode({'identity':dongle_id, 'exp': datetime.utcnow() + timedelta(hours=1)}, private_key, algorithm='RS256') + + ws = create_connection(remote_ws_uri, + cookie="jwt=" + identity_token, + enable_multithread=True) + + ssock, csock = socket.socketpair() + local_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + local_sock.connect(('127.0.0.1', local_port)) + local_sock.setblocking(0) + + proxy_end_event = threading.Event() + threads = [ + threading.Thread(target=ws_proxy_recv, args=(ws, local_sock, ssock, proxy_end_event, global_end_event)), + threading.Thread(target=ws_proxy_send, args=(ws, local_sock, csock, proxy_end_event)) + ] + + map(lambda thread: thread.start(), threads) + + return {"success": 1} + except Exception as e: + traceback.print_exc() + raise e + +@dispatcher.add_method +def getPublicKey(): + if not os.path.isfile('/persist/comma/id_rsa.pub'): + return None + + with open('/persist/comma/id_rsa.pub', 'r') as f: + return f.read() + +@dispatcher.add_method +def getSshAuthorizedKeys(): + with open('/system/comma/home/.ssh/authorized_keys', 'r') as f: + return f.read() + +@dispatcher.add_method +def getSimInfo(): + sim_state = subprocess.check_output(['getprop', 'gsm.sim.state']).strip().split(',') + network_type = subprocess.check_output(['getprop', 'gsm.network.type']).strip().split(',') + mcc_mnc = subprocess.check_output(['getprop', 'gsm.sim.operator.numeric']).strip() or None + + sim_id_aidl_out = subprocess.check_output(['service', 'call', 'iphonesubinfo', '11']) + sim_id_aidl_lines = sim_id_aidl_out.split('\n') + if len(sim_id_aidl_lines) > 3: + sim_id_lines = sim_id_aidl_lines[1:4] + sim_id_fragments = [re.search(r"'([0-9\.]+)'", line).group(1) for line in sim_id_lines] + sim_id = reduce(lambda frag1, frag2: frag1.replace('.', '') + frag2.replace('.', ''), sim_id_fragments) + else: + sim_id = None + + return { + 'sim_id': sim_id, + 'mcc_mnc': mcc_mnc, + 'network_type': network_type, + 'sim_state': sim_state + } + +def ws_proxy_recv(ws, local_sock, ssock, end_event, global_end_event): + while not (end_event.is_set() or global_end_event.is_set()): + try: + data = ws.recv() + local_sock.sendall(data) + except WebSocketTimeoutException: + pass + except Exception: + traceback.print_exc() + break + + ssock.close() + end_event.set() + +def ws_proxy_send(ws, local_sock, signal_sock, end_event): + while not end_event.is_set(): + try: + r, _, _ = select.select((local_sock, signal_sock), (), ()) + if r: + if r[0].fileno() == signal_sock.fileno(): + # got end signal from ws_proxy_recv + end_event.set() + break + data = local_sock.recv(4096) + if not data: + # local_sock is dead + end_event.set() + break + + ws.send(data, ABNF.OPCODE_BINARY) + except Exception: + traceback.print_exc() + end_event.set() + def ws_recv(ws, end_event): while not end_event.is_set(): try: @@ -138,5 +250,7 @@ def main(gctx=None): time.sleep(backoff(conn_retries)) + params.delete("AthenadPid") + if __name__ == "__main__": main() diff --git a/selfdrive/boardd/boardd.cc b/selfdrive/boardd/boardd.cc index 1a64416e9f4b4b..779ca7b279f16c 100644 --- a/selfdrive/boardd/boardd.cc +++ b/selfdrive/boardd/boardd.cc @@ -283,8 +283,9 @@ void can_health(void *s) { uint8_t started; uint8_t controls_allowed; uint8_t gas_interceptor_detected; - uint8_t started_signal_detected; - uint8_t started_alt; + uint32_t can_send_errs; + uint32_t can_fwd_errs; + uint32_t gmlan_send_errs; } health; // recv from board @@ -313,8 +314,10 @@ void can_health(void *s) { } healthData.setControlsAllowed(health.controls_allowed); healthData.setGasInterceptorDetected(health.gas_interceptor_detected); - healthData.setStartedSignalDetected(health.started_signal_detected); healthData.setIsGreyPanda(is_grey_panda); + healthData.setCanSendErrs(health.can_send_errs); + healthData.setCanFwdErrs(health.can_fwd_errs); + healthData.setGmlanSendErrs(health.gmlan_send_errs); // send to health auto words = capnp::messageToFlatArray(msg); diff --git a/selfdrive/can/parser.cc b/selfdrive/can/parser.cc index e3225181ba7004..69b30fb511438d 100644 --- a/selfdrive/can/parser.cc +++ b/selfdrive/can/parser.cc @@ -194,32 +194,36 @@ class CANParser { : bus(abus) { // connect to can on 8006 context = zmq_ctx_new(); - subscriber = zmq_socket(context, ZMQ_SUB); - zmq_setsockopt(subscriber, ZMQ_SUBSCRIBE, "", 0); - zmq_setsockopt(subscriber, ZMQ_RCVTIMEO, &timeout, sizeof(int)); - std::string tcp_addr_str; + if (tcp_addr.length() > 0) { + subscriber = zmq_socket(context, ZMQ_SUB); + zmq_setsockopt(subscriber, ZMQ_SUBSCRIBE, "", 0); + zmq_setsockopt(subscriber, ZMQ_RCVTIMEO, &timeout, sizeof(int)); - if (sendcan) { - tcp_addr_str = "tcp://" + tcp_addr + ":8017"; - } else { - tcp_addr_str = "tcp://" + tcp_addr + ":8006"; - } - const char *tcp_addr_char = tcp_addr_str.c_str(); + std::string tcp_addr_str; + + if (sendcan) { + tcp_addr_str = "tcp://" + tcp_addr + ":8017"; + } else { + tcp_addr_str = "tcp://" + tcp_addr + ":8006"; + } + const char *tcp_addr_char = tcp_addr_str.c_str(); - zmq_connect(subscriber, tcp_addr_char); + zmq_connect(subscriber, tcp_addr_char); - // drain sendcan to delete any stale messages from previous runs - zmq_msg_t msgDrain; - zmq_msg_init(&msgDrain); - int err = 0; - while(err >= 0) { - err = zmq_msg_recv(&msgDrain, subscriber, ZMQ_DONTWAIT); + // drain sendcan to delete any stale messages from previous runs + zmq_msg_t msgDrain; + zmq_msg_init(&msgDrain); + int err = 0; + while(err >= 0) { + err = zmq_msg_recv(&msgDrain, subscriber, ZMQ_DONTWAIT); + } + } else { + subscriber = NULL; } dbc = dbc_lookup(dbc_name); - assert(dbc); - + assert(dbc); for (const auto& op : options) { MessageState state = { .address = op.address, @@ -326,6 +330,21 @@ class CANParser { } } + void update_string(uint64_t sec, std::string data) { + // format for board, make copy due to alignment issues, will be freed on out of scope + auto amsg = kj::heapArray((data.length() / sizeof(capnp::word)) + 1); + memcpy(amsg.begin(), data.data(), data.length()); + + // extract the messages + capnp::FlatArrayMessageReader cmsg(amsg); + cereal::Event::Reader event = cmsg.getRoot(); + + auto cans = event.getCan(); + UpdateCans(sec, cans); + + UpdateValid(sec); + } + int update(uint64_t sec, bool wait) { int err; int result = 0; @@ -336,7 +355,7 @@ class CANParser { // multiple recv is fine bool first = wait; - while (1) { + while (subscriber != NULL) { if (first) { err = zmq_msg_recv(&msg, subscriber, 0); first = false; @@ -432,6 +451,11 @@ int can_update(void* can, uint64_t sec, bool wait) { return cp->update(sec, wait); } +void can_update_string(void *can, uint64_t sec, const char* dat, int len) { + CANParser* cp = (CANParser*)can; + cp->update_string(sec, std::string(dat, len)); +} + size_t can_query(void* can, uint64_t sec, bool *out_can_valid, size_t out_values_size, SignalValue* out_values) { CANParser* cp = (CANParser*)can; diff --git a/selfdrive/can/parser_pyx.pxd b/selfdrive/can/parser_pyx.pxd index 9d8efa318c8af3..ac619707acc77b 100644 --- a/selfdrive/can/parser_pyx.pxd +++ b/selfdrive/can/parser_pyx.pxd @@ -67,6 +67,7 @@ ctypedef void* (*can_init_with_vectors_func)(int bus, const char* dbc_name, const char* tcp_addr, int timeout) ctypedef int (*can_update_func)(void* can, uint64_t sec, bool wait); +ctypedef void (*can_update_string_func)(void* can, uint64_t sec, const char* dat, int len); ctypedef size_t (*can_query_func)(void* can, uint64_t sec, bool *out_can_valid, size_t out_values_size, SignalValue* out_values); ctypedef void (*can_query_vector_func)(void* can, uint64_t sec, bool *out_can_valid, vector[SignalValue] &values) @@ -77,6 +78,7 @@ cdef class CANParser: dbc_lookup_func dbc_lookup can_init_with_vectors_func can_init_with_vectors can_update_func can_update + can_update_string_func can_update_string can_query_vector_func can_query_vector map[string, uint32_t] msg_name_to_address map[uint32_t, string] address_to_msg_name diff --git a/selfdrive/can/parser_pyx.pyx b/selfdrive/can/parser_pyx.pyx index 65c6f5ab21bb58..c6f1f58e030dc9 100644 --- a/selfdrive/can/parser_pyx.pyx +++ b/selfdrive/can/parser_pyx.pyx @@ -8,7 +8,7 @@ import numbers cdef int CAN_INVALID_CNT = 5 cdef class CANParser: - def __init__(self, dbc_name, signals, checks=None, bus=0, sendcan=False, tcp_addr="127.0.0.1", timeout=-1): + def __init__(self, dbc_name, signals, checks=None, bus=0, sendcan=False, tcp_addr="", timeout=-1): self.test_mode_enabled = False can_dir = os.path.dirname(os.path.abspath(__file__)) libdbc_fn = os.path.join(can_dir, "libdbc.so") @@ -17,6 +17,7 @@ cdef class CANParser: self.can_init_with_vectors = dlsym(libdbc, 'can_init_with_vectors') self.dbc_lookup = dlsym(libdbc, 'dbc_lookup') self.can_update = dlsym(libdbc, 'can_update') + self.can_update_string = dlsym(libdbc, 'can_update_string') self.can_query_vector = dlsym(libdbc, 'can_query_vector') if checks is None: checks = [] @@ -99,6 +100,19 @@ cdef class CANParser: return updated_val + def update_string(self, uint64_t sec, dat): + self.can_update_string(self.can, sec, dat, len(dat)) + return self.update_vl(sec) + + def update_strings(self, uint64_t sec, strings): + updated_vals = set() + + for s in strings: + updated_val = self.update_string(sec, s) + updated_vals.update(updated_val) + + return updated_vals + def update(self, uint64_t sec, bool wait): r = (self.can_update(self.can, sec, wait) >= 0) updated_val = self.update_vl(sec) diff --git a/selfdrive/can/tests/test_parser.py b/selfdrive/can/tests/test_parser.py index bb00d042fca80c..53c95ce912be15 100755 --- a/selfdrive/can/tests/test_parser.py +++ b/selfdrive/can/tests/test_parser.py @@ -47,8 +47,9 @@ def run_route(route): CP = CarInterface.get_params(CAR.CIVIC, {}) signals, checks = get_can_signals(CP) - parser_old = CANParserOld(DBC[CP.carFingerprint]['pt'], signals, checks, 0, timeout=-1) - parser_new = CANParserNew(DBC[CP.carFingerprint]['pt'], signals, checks, 0, timeout=-1) + parser_old = CANParserOld(DBC[CP.carFingerprint]['pt'], signals, checks, 0, timeout=-1, tcp_addr="127.0.0.1") + parser_new = CANParserNew(DBC[CP.carFingerprint]['pt'], signals, checks, 0, timeout=-1, tcp_addr="127.0.0.1") + parser_string = CANParserNew(DBC[CP.carFingerprint]['pt'], signals, checks, 0, timeout=-1) if dict_keys_differ(parser_old.vl, parser_new.vl): return False @@ -61,19 +62,29 @@ def run_route(route): for msg in lr: if msg.which() == 'can': t += DT - can.send(msg.as_builder().to_bytes()) + msg_bytes = msg.as_builder().to_bytes() + can.send(msg_bytes) _, updated_old = parser_old.update(t, True) _, updated_new = parser_new.update(t, True) + updated_string = parser_string.update_string(t, msg_bytes) if updated_old != updated_new: route_ok = False print(t, "Diff in seen") + if updated_new != updated_string: + route_ok = False + print(t, "Diff in seen string") + if dicts_vals_differ(parser_old.vl, parser_new.vl): print(t, "Diff in dict") route_ok = False + if dicts_vals_differ(parser_new.vl, parser_string.vl): + print(t, "Diff in dict string") + route_ok = False + return route_ok class TestCanParser(unittest.TestCase): diff --git a/selfdrive/car/car_helpers.py b/selfdrive/car/car_helpers.py index f7e8c53757a8a0..155e443e1c7ce7 100644 --- a/selfdrive/car/car_helpers.py +++ b/selfdrive/car/car_helpers.py @@ -50,6 +50,8 @@ def _get_interface_names(): # imports from directory selfdrive/car// interfaces = load_interfaces(_get_interface_names()) +def only_toyota_left(candidate_cars): + return all(("TOYOTA" in c or "LEXUS" in c) for c in candidate_cars) # BOUNTY: every added fingerprint in selfdrive/car/*/values.py is a $100 coupon code on shop.comma.ai # **** for use live only **** @@ -59,7 +61,7 @@ def fingerprint(logcan, sendcan): elif os.getenv("SIMULATOR") is not None: return ("simulator", None, "") - finger = {} + finger = {0: {}, 2:{}} # collect on bus 0 or 2 cloudlog.warning("waiting for fingerprint...") candidate_cars = all_known_cars() can_seen_frame = None @@ -79,6 +81,7 @@ def fingerprint(logcan, sendcan): vin = "" frame = 0 + while True: a = messaging.recv_one(logcan) @@ -98,9 +101,12 @@ def fingerprint(logcan, sendcan): # ignore everything not on bus 0 and with more than 11 bits, # which are ussually sporadic and hard to include in fingerprints. - # also exclude VIN query response on 0x7e8 - if can.src == 0 and can.address < 0x800 and can.address != 0x7e8: - finger[can.address] = len(can.dat) + # also exclude VIN query response on 0x7e8. + # Include bus 2 for toyotas to disambiguate cars using camera messages + # (ideally should be done for all cars but we can't for Honda Bosch) + if (can.src == 0 or (only_toyota_left(candidate_cars) and can.src == 2)) and \ + can.address < 0x800 and can.address != 0x7e8: + finger[can.src][can.address] = len(can.dat) candidate_cars = eliminate_incompatible_cars(can, candidate_cars) if can_seen_frame is None and can_seen: @@ -110,7 +116,7 @@ def fingerprint(logcan, sendcan): # message has elapsed, exit. Toyota needs higher time_fingerprint, since DSU does not # broadcast immediately if len(candidate_cars) == 1 and can_seen_frame is not None: - time_fingerprint = 1.0 if ("TOYOTA" in candidate_cars[0] or "LEXUS" in candidate_cars[0]) else 0.1 + time_fingerprint = 1.0 if only_toyota_left(candidate_cars) else 0.1 if (frame - can_seen_frame) > (time_fingerprint * 100): break @@ -146,6 +152,6 @@ def get_car(logcan, sendcan): candidate = "mock" CarInterface, CarController = interfaces[candidate] - params = CarInterface.get_params(candidate, fingerprints, vin) + params = CarInterface.get_params(candidate, fingerprints[0], vin) return CarInterface(params, CarController), params diff --git a/selfdrive/car/chrysler/carstate.py b/selfdrive/car/chrysler/carstate.py index 1d38b8cdab7eab..24acc76a4b196a 100644 --- a/selfdrive/car/chrysler/carstate.py +++ b/selfdrive/car/chrysler/carstate.py @@ -60,7 +60,7 @@ def get_can_parser(CP): ("ACC_2", 50), ] - return CANParser(DBC[CP.carFingerprint]['pt'], signals, checks, 0, timeout=100) + return CANParser(DBC[CP.carFingerprint]['pt'], signals, checks, 0) def get_camera_parser(CP): signals = [ @@ -72,7 +72,7 @@ def get_camera_parser(CP): ] checks = [] - return CANParser(DBC[CP.carFingerprint]['pt'], signals, checks, 2, timeout=100) + return CANParser(DBC[CP.carFingerprint]['pt'], signals, checks, 2) class CarState(object): diff --git a/selfdrive/car/chrysler/interface.py b/selfdrive/car/chrysler/interface.py index 793f7320844148..65a60904f51278 100755 --- a/selfdrive/car/chrysler/interface.py +++ b/selfdrive/car/chrysler/interface.py @@ -112,18 +112,17 @@ def get_params(candidate, fingerprint, vin=""): return ret # returns a car.CarState - def update(self, c): + def update(self, c, can_strings): # ******************* do can recv ******************* - canMonoTimes = [] - can_rcv_valid, _ = self.cp.update(int(sec_since_boot() * 1e9), True) - cam_rcv_valid, _ = self.cp_cam.update(int(sec_since_boot() * 1e9), False) + self.cp.update_strings(int(sec_since_boot() * 1e9), can_strings) + self.cp_cam.update_strings(int(sec_since_boot() * 1e9), can_strings) self.CS.update(self.cp, self.cp_cam) # create message ret = car.CarState.new_message() - ret.canValid = can_rcv_valid and cam_rcv_valid and self.cp.can_valid and self.cp_cam.can_valid + ret.canValid = self.cp.can_valid and self.cp_cam.can_valid # speeds ret.vEgo = self.CS.v_ego @@ -222,7 +221,6 @@ def update(self, c): events.append(create_event('belowSteerSpeed', [ET.WARNING])) ret.events = events - ret.canMonoTimes = canMonoTimes self.gas_pressed_prev = ret.gasPressed self.brake_pressed_prev = ret.brakePressed diff --git a/selfdrive/car/chrysler/radar_interface.py b/selfdrive/car/chrysler/radar_interface.py index b4970b2223706a..43f5c6105eccf5 100755 --- a/selfdrive/car/chrysler/radar_interface.py +++ b/selfdrive/car/chrysler/radar_interface.py @@ -51,27 +51,24 @@ def __init__(self, CP): self.pts = {} self.delay = 0.0 # Delay of radar #TUNE self.rcp = _create_radar_can_parser() + self.updated_messages = set() + self.trigger_msg = LAST_MSG - def update(self): - canMonoTimes = [] + def update(self, can_strings): + tm = int(sec_since_boot() * 1e9) + vls = self.rcp.update_strings(tm, can_strings) + self.updated_messages.update(vls) - updated_messages = set() # set of message IDs (sig_addresses) we've seen - - while 1: - tm = int(sec_since_boot() * 1e9) - _, vls = self.rcp.update(tm, True) - updated_messages.update(vls) - if LAST_MSG in updated_messages: - break + if self.trigger_msg not in self.updated_messages: + return None ret = car.RadarData.new_message() errors = [] if not self.rcp.can_valid: errors.append("canError") ret.errors = errors - ret.canMonoTimes = canMonoTimes - for ii in updated_messages: # ii should be the message ID as a number + for ii in self.updated_messages: # ii should be the message ID as a number cpt = self.rcp.vl[ii] trackId = _address_to_track(ii) @@ -92,11 +89,6 @@ def update(self): # We want a list, not a dictionary. Filter out LONG_DIST==0 because that means it's not valid. ret.points = [x for x in self.pts.values() if x.dRel != 0] - return ret -if __name__ == "__main__": - RI = RadarInterface(None) - while 1: - ret = RI.update() - print(chr(27) + "[2J") # clear screen - print(ret) + self.updated_messages.clear() + return ret diff --git a/selfdrive/car/ford/carstate.py b/selfdrive/car/ford/carstate.py index dc6d824ff5a4b7..5e87a2c87812f1 100644 --- a/selfdrive/car/ford/carstate.py +++ b/selfdrive/car/ford/carstate.py @@ -29,7 +29,7 @@ def get_can_parser(CP): checks = [ ] - return CANParser(DBC[CP.carFingerprint]['pt'], signals, checks, 0, timeout=100) + return CANParser(DBC[CP.carFingerprint]['pt'], signals, checks, 0) class CarState(object): diff --git a/selfdrive/car/ford/interface.py b/selfdrive/car/ford/interface.py index 0f3bbc33cc2b84..ca0f9f830f63c9 100755 --- a/selfdrive/car/ford/interface.py +++ b/selfdrive/car/ford/interface.py @@ -105,18 +105,16 @@ def get_params(candidate, fingerprint, vin=""): return ret # returns a car.CarState - def update(self, c): + def update(self, c, can_strings): # ******************* do can recv ******************* - canMonoTimes = [] - - can_rcv_valid, _ = self.cp.update(int(sec_since_boot() * 1e9), True) + self.cp.update_strings(int(sec_since_boot() * 1e9), can_strings) self.CS.update(self.cp) # create message ret = car.CarState.new_message() - ret.canValid = can_rcv_valid and self.cp.can_valid + ret.canValid = self.cp.can_valid # speeds ret.vEgo = self.CS.v_ego @@ -167,7 +165,6 @@ def update(self, c): events.append(create_event('steerTempUnavailableMute', [ET.WARNING])) ret.events = events - ret.canMonoTimes = canMonoTimes self.gas_pressed_prev = ret.gasPressed self.brake_pressed_prev = ret.brakePressed diff --git a/selfdrive/car/ford/radar_interface.py b/selfdrive/car/ford/radar_interface.py index 08b54723d6801f..04cab9c66d7e1b 100755 --- a/selfdrive/car/ford/radar_interface.py +++ b/selfdrive/car/ford/radar_interface.py @@ -28,28 +28,25 @@ def __init__(self, CP): # Nidec self.rcp = _create_radar_can_parser() + self.trigger_msg = 0x53f + self.updated_messages = set() - def update(self): - canMonoTimes = [] + def update(self, can_strings): + tm = int(sec_since_boot() * 1e9) + vls = self.rcp.update_strings(tm, can_strings) + self.updated_messages.update(vls) - updated_messages = set() - while 1: - tm = int(sec_since_boot() * 1e9) - _, vls = self.rcp.update(tm, True) - updated_messages.update(vls) + if self.trigger_msg not in self.updated_messages: + return None - # TODO: do not hardcode last msg - if 0x53f in updated_messages: - break ret = car.RadarData.new_message() errors = [] if not self.rcp.can_valid: errors.append("canError") ret.errors = errors - ret.canMonoTimes = canMonoTimes - for ii in updated_messages: + for ii in self.updated_messages: cpt = self.rcp.vl[ii] if cpt['X_Rel'] > 0.00001: @@ -78,11 +75,5 @@ def update(self): del self.pts[ii] ret.points = self.pts.values() + self.updated_messages.clear() return ret - -if __name__ == "__main__": - RI = RadarInterface(None) - while 1: - ret = RI.update() - print(chr(27) + "[2J") - print(ret) diff --git a/selfdrive/car/gm/carstate.py b/selfdrive/car/gm/carstate.py index 174d11b46b53c2..2501598dd6cf7d 100644 --- a/selfdrive/car/gm/carstate.py +++ b/selfdrive/car/gm/carstate.py @@ -47,7 +47,7 @@ def get_powertrain_can_parser(CP, canbus): ("CruiseState", "AcceleratorPedal2", 0), ] - return CANParser(DBC[CP.carFingerprint]['pt'], signals, [], canbus.powertrain, timeout=100) + return CANParser(DBC[CP.carFingerprint]['pt'], signals, [], canbus.powertrain) class CarState(object): diff --git a/selfdrive/car/gm/interface.py b/selfdrive/car/gm/interface.py index c066a6523f2213..71b0efad29d055 100755 --- a/selfdrive/car/gm/interface.py +++ b/selfdrive/car/gm/interface.py @@ -170,15 +170,15 @@ def get_params(candidate, fingerprint, vin=""): return ret # returns a car.CarState - def update(self, c): - can_rcv_valid, _ = self.pt_cp.update(int(sec_since_boot() * 1e9), True) + def update(self, c, can_strings): + self.pt_cp.update_strings(int(sec_since_boot() * 1e9), can_strings) self.CS.update(self.pt_cp) # create message ret = car.CarState.new_message() - ret.canValid = can_rcv_valid and self.pt_cp.can_valid + ret.canValid = self.pt_cp.can_valid # speeds ret.vEgo = self.CS.v_ego diff --git a/selfdrive/car/gm/radar_interface.py b/selfdrive/car/gm/radar_interface.py index 12fb7c23427380..6788e1ce74e414 100755 --- a/selfdrive/car/gm/radar_interface.py +++ b/selfdrive/car/gm/radar_interface.py @@ -52,21 +52,23 @@ def __init__(self, CP): print "Using %d as obstacle CAN bus ID" % canbus.obstacle self.rcp = create_radar_can_parser(canbus, CP.carFingerprint) - def update(self): - updated_messages = set() - ret = car.RadarData.new_message() - while 1: + self.trigger_msg = LAST_RADAR_MSG + self.updated_messages = set() - if self.rcp is None: - time.sleep(0.05) # nothing to do - return ret + def update(self, can_strings): + if self.rcp is None: + time.sleep(0.05) # nothing to do + return car.RadarData.new_message() - tm = int(sec_since_boot() * 1e9) - _, vls = self.rcp.update(tm, True) - updated_messages.update(vls) - if LAST_RADAR_MSG in updated_messages: - break + tm = int(sec_since_boot() * 1e9) + vls = self.rcp.update_strings(tm, can_strings) + self.updated_messages.update(vls) + + if self.trigger_msg not in self.updated_messages: + return None + + ret = car.RadarData.new_message() header = self.rcp.vl[RADAR_HEADER_MSG] fault = header['FLRRSnsrBlckd'] or header['FLRRSnstvFltPrsntInt'] or \ header['FLRRYawRtPlsblityFlt'] or header['FLRRHWFltPrsntInt'] or \ @@ -83,7 +85,7 @@ def update(self): # Not all radar messages describe targets, # no need to monitor all of the self.rcp.msgs_upd - for ii in updated_messages: + for ii in self.updated_messages: if ii == RADAR_HEADER_MSG: continue @@ -112,11 +114,5 @@ def update(self): del self.pts[oldTarget] ret.points = self.pts.values() + self.updated_messages.clear() return ret - -if __name__ == "__main__": - RI = RadarInterface(None) - while 1: - ret = RI.update() - print(chr(27) + "[2J") - print(ret) diff --git a/selfdrive/car/honda/carstate.py b/selfdrive/car/honda/carstate.py index 6c0aec52a03a5f..e2bb82c73c0351 100644 --- a/selfdrive/car/honda/carstate.py +++ b/selfdrive/car/honda/carstate.py @@ -146,6 +146,7 @@ def get_can_signals(CP): # add gas interceptor reading if we are using it if CP.enableGasInterceptor: signals.append(("INTERCEPTOR_GAS", "GAS_SENSOR", 0)) + signals.append(("INTERCEPTOR_GAS2", "GAS_SENSOR", 0)) checks.append(("GAS_SENSOR", 50)) return signals, checks @@ -153,7 +154,7 @@ def get_can_signals(CP): def get_can_parser(CP): signals, checks = get_can_signals(CP) - return CANParser(DBC[CP.carFingerprint]['pt'], signals, checks, 0, timeout=100) + return CANParser(DBC[CP.carFingerprint]['pt'], signals, checks, 0) def get_cam_can_parser(CP): @@ -166,7 +167,7 @@ def get_cam_can_parser(CP): cam_bus = 1 if CP.carFingerprint in HONDA_BOSCH else 2 - return CANParser(DBC[CP.carFingerprint]['pt'], signals, checks, cam_bus, timeout=100) + return CANParser(DBC[CP.carFingerprint]['pt'], signals, checks, cam_bus) class CarState(object): def __init__(self, CP): @@ -261,7 +262,7 @@ def update(self, cp, cp_cam): # this is a hack for the interceptor. This is now only used in the simulation # TODO: Replace tests by toyota so this can go away if self.CP.enableGasInterceptor: - self.user_gas = cp.vl["GAS_SENSOR"]['INTERCEPTOR_GAS'] + self.user_gas = (cp.vl["GAS_SENSOR"]['INTERCEPTOR_GAS'] + cp.vl["GAS_SENSOR"]['INTERCEPTOR_GAS2']) / 2. self.user_gas_pressed = self.user_gas > 0 # this works because interceptor read < 0 when pedal position is 0. Once calibrated, this will change self.gear = 0 if self.CP.carFingerprint == CAR.CIVIC else cp.vl["GEARBOX"]['GEAR'] diff --git a/selfdrive/car/honda/interface.py b/selfdrive/car/honda/interface.py index 93aaf0182b7db6..7ad72cdc48c1a5 100755 --- a/selfdrive/car/honda/interface.py +++ b/selfdrive/car/honda/interface.py @@ -358,18 +358,17 @@ def get_params(candidate, fingerprint, vin=""): return ret # returns a car.CarState - def update(self, c): + def update(self, c, can_strings): # ******************* do can recv ******************* - canMonoTimes = [] - can_rcv_valid, _ = self.cp.update(int(sec_since_boot() * 1e9), True) - cam_rcv_valid, _ = self.cp_cam.update(int(sec_since_boot() * 1e9), False) + self.cp.update_strings(int(sec_since_boot() * 1e9), can_strings) + self.cp_cam.update_strings(int(sec_since_boot() * 1e9), can_strings) self.CS.update(self.cp, self.cp_cam) # create message ret = car.CarState.new_message() - ret.canValid = can_rcv_valid and cam_rcv_valid and self.cp.can_valid + ret.canValid = self.cp.can_valid # speeds ret.vEgo = self.CS.v_ego @@ -547,7 +546,6 @@ def update(self, c): events.append(create_event('buttonEnable', [ET.ENABLE])) ret.events = events - ret.canMonoTimes = canMonoTimes # update previous brake/gas pressed self.gas_pressed_prev = ret.gasPressed diff --git a/selfdrive/car/honda/radar_interface.py b/selfdrive/car/honda/radar_interface.py index 94e98cf2cb3351..f8cecd6de43d97 100755 --- a/selfdrive/car/honda/radar_interface.py +++ b/selfdrive/car/honda/radar_interface.py @@ -31,25 +31,30 @@ def __init__(self, CP): # Nidec self.rcp = _create_nidec_can_parser() + self.trigger_msg = 0x445 + self.updated_messages = set() - def update(self): - canMonoTimes = [] - - updated_messages = set() - ret = car.RadarData.new_message() - + def update(self, can_strings): # in Bosch radar and we are only steering for now, so sleep 0.05s to keep # radard at 20Hz and return no points if self.radar_off_can: time.sleep(0.05) - return ret + return car.RadarData.new_message() + + tm = int(sec_since_boot() * 1e9) + vls = self.rcp.update_strings(tm, can_strings) + self.updated_messages.update(vls) + + if self.trigger_msg not in self.updated_messages: + return None + + rr = self._update(self.updated_messages) + self.updated_messages.clear() + return rr - while 1: - tm = int(sec_since_boot() * 1e9) - _, vls = self.rcp.update(tm, True) - updated_messages.update(vls) - if 0x445 in updated_messages: - break + + def _update(self, updated_messages): + ret = car.RadarData.new_message() for ii in updated_messages: cpt = self.rcp.vl[ii] @@ -80,19 +85,7 @@ def update(self): if self.radar_wrong_config: errors.append("wrongConfig") ret.errors = errors - ret.canMonoTimes = canMonoTimes ret.points = self.pts.values() return ret - - -if __name__ == "__main__": - class CarParams: - radarOffCan = False - - RI = RadarInterface(CarParams) - while 1: - ret = RI.update() - print(chr(27) + "[2J") - print(ret) diff --git a/selfdrive/car/hyundai/carstate.py b/selfdrive/car/hyundai/carstate.py index 8c900d73f8285d..f993bd2a0f6959 100644 --- a/selfdrive/car/hyundai/carstate.py +++ b/selfdrive/car/hyundai/carstate.py @@ -93,7 +93,7 @@ def get_can_parser(CP): ("SAS11", 100) ] - return CANParser(DBC[CP.carFingerprint]['pt'], signals, checks, 0, timeout=100) + return CANParser(DBC[CP.carFingerprint]['pt'], signals, checks, 0) def get_camera_parser(CP): @@ -119,7 +119,7 @@ def get_camera_parser(CP): checks = [] - return CANParser(DBC[CP.carFingerprint]['pt'], signals, checks, 2, timeout=100) + return CANParser(DBC[CP.carFingerprint]['pt'], signals, checks, 2) class CarState(object): diff --git a/selfdrive/car/hyundai/interface.py b/selfdrive/car/hyundai/interface.py index 67ca2261ff7f17..4e1b2e6be2bcb0 100644 --- a/selfdrive/car/hyundai/interface.py +++ b/selfdrive/car/hyundai/interface.py @@ -151,17 +151,16 @@ def get_params(candidate, fingerprint, vin=""): return ret # returns a car.CarState - def update(self, c): + def update(self, c, can_strings): # ******************* do can recv ******************* - canMonoTimes = [] - can_rcv_valid, _ = self.cp.update(int(sec_since_boot() * 1e9), True) - cam_rcv_valid, _ = self.cp_cam.update(int(sec_since_boot() * 1e9), False) + self.cp.update_strings(int(sec_since_boot() * 1e9), can_strings) + self.cp_cam.update_strings(int(sec_since_boot() * 1e9), can_strings) self.CS.update(self.cp, self.cp_cam) # create message ret = car.CarState.new_message() - ret.canValid = can_rcv_valid and cam_rcv_valid and self.cp.can_valid # TODO: check cp_cam validity + ret.canValid = self.cp.can_valid # TODO: check cp_cam validity # speeds ret.vEgo = self.CS.v_ego @@ -269,7 +268,6 @@ def update(self, c): events.append(create_event('belowSteerSpeed', [ET.WARNING])) ret.events = events - ret.canMonoTimes = canMonoTimes self.gas_pressed_prev = ret.gasPressed self.brake_pressed_prev = ret.brakePressed diff --git a/selfdrive/car/hyundai/radar_interface.py b/selfdrive/car/hyundai/radar_interface.py index 75256683de3187..1d7772fd3bb626 100644 --- a/selfdrive/car/hyundai/radar_interface.py +++ b/selfdrive/car/hyundai/radar_interface.py @@ -9,16 +9,8 @@ def __init__(self, CP): self.pts = {} self.delay = 0.1 - def update(self): - + def update(self, can_strings): ret = car.RadarData.new_message() time.sleep(0.05) # radard runs on RI updates return ret - -if __name__ == "__main__": - RI = RadarInterface(None) - while 1: - ret = RI.update() - print(chr(27) + "[2J") - print(ret) diff --git a/selfdrive/car/mock/interface.py b/selfdrive/car/mock/interface.py index a18d2bf244bde0..3f47b9b00af7ea 100755 --- a/selfdrive/car/mock/interface.py +++ b/selfdrive/car/mock/interface.py @@ -80,7 +80,7 @@ def get_params(candidate, fingerprint, vin=""): return ret # returns a car.CarState - def update(self, c): + def update(self, c, can_strings): self.rk.keep_time() # get basic data from phone and gps since CAN isn't connected diff --git a/selfdrive/car/mock/radar_interface.py b/selfdrive/car/mock/radar_interface.py index 437bb0538a8dd4..8e5f7b7fcaf150 100755 --- a/selfdrive/car/mock/radar_interface.py +++ b/selfdrive/car/mock/radar_interface.py @@ -9,15 +9,7 @@ def __init__(self, CP): self.pts = {} self.delay = 0.1 - def update(self): - + def update(self, can_strings): ret = car.RadarData.new_message() time.sleep(0.05) # radard runs on RI updates return ret - -if __name__ == "__main__": - RI = RadarInterface(None) - while 1: - ret = RI.update() - print(chr(27) + "[2J") - print(ret) diff --git a/selfdrive/car/subaru/carstate.py b/selfdrive/car/subaru/carstate.py index 9a927775489335..6c5f6782662bc7 100644 --- a/selfdrive/car/subaru/carstate.py +++ b/selfdrive/car/subaru/carstate.py @@ -37,7 +37,7 @@ def get_powertrain_can_parser(CP): ("BodyInfo", 10), ] - return CANParser(DBC[CP.carFingerprint]['pt'], signals, checks, 0, timeout=100) + return CANParser(DBC[CP.carFingerprint]['pt'], signals, checks, 0) def get_camera_can_parser(CP): @@ -79,7 +79,7 @@ def get_camera_can_parser(CP): ("ES_DashStatus", 10), ] - return CANParser(DBC[CP.carFingerprint]['pt'], signals, checks, 2, timeout=100) + return CANParser(DBC[CP.carFingerprint]['pt'], signals, checks, 2) class CarState(object): diff --git a/selfdrive/car/subaru/interface.py b/selfdrive/car/subaru/interface.py index e9d9c117fc714c..ad8d1d5a48e7ab 100644 --- a/selfdrive/car/subaru/interface.py +++ b/selfdrive/car/subaru/interface.py @@ -94,16 +94,16 @@ def get_params(candidate, fingerprint, vin=""): return ret # returns a car.CarState - def update(self, c): - can_rcv_valid, _ = self.pt_cp.update(int(sec_since_boot() * 1e9), True) - cam_rcv_valid, _ = self.cam_cp.update(int(sec_since_boot() * 1e9), False) + def update(self, c, can_strings): + self.pt_cp.update_strings(int(sec_since_boot() * 1e9), can_strings) + self.cam_cp.update_strings(int(sec_since_boot() * 1e9), can_strings) self.CS.update(self.pt_cp, self.cam_cp) # create message ret = car.CarState.new_message() - ret.canValid = can_rcv_valid and cam_rcv_valid and self.pt_cp.can_valid and self.cam_cp.can_valid + ret.canValid = self.pt_cp.can_valid and self.cam_cp.can_valid # speeds ret.vEgo = self.CS.v_ego diff --git a/selfdrive/car/subaru/radar_interface.py b/selfdrive/car/subaru/radar_interface.py index 75256683de3187..0f8108771110ab 100644 --- a/selfdrive/car/subaru/radar_interface.py +++ b/selfdrive/car/subaru/radar_interface.py @@ -9,16 +9,9 @@ def __init__(self, CP): self.pts = {} self.delay = 0.1 - def update(self): + def update(self, can_strings): ret = car.RadarData.new_message() time.sleep(0.05) # radard runs on RI updates return ret - -if __name__ == "__main__": - RI = RadarInterface(None) - while 1: - ret = RI.update() - print(chr(27) + "[2J") - print(ret) diff --git a/selfdrive/car/toyota/carstate.py b/selfdrive/car/toyota/carstate.py index ee7c61d940617e..40be422db1bb13 100644 --- a/selfdrive/car/toyota/carstate.py +++ b/selfdrive/car/toyota/carstate.py @@ -1,7 +1,7 @@ import numpy as np from common.kalman.simple_kalman import KF1D -from selfdrive.can.parser import CANParser from selfdrive.can.can_define import CANDefine +from selfdrive.can.parser import CANParser from selfdrive.config import Conversions as CV from selfdrive.car.toyota.values import CAR, DBC, STEER_THRESHOLD, TSS2_CAR @@ -70,9 +70,10 @@ def get_can_parser(CP): # add gas interceptor reading if we are using it if CP.enableGasInterceptor: signals.append(("INTERCEPTOR_GAS", "GAS_SENSOR", 0)) + signals.append(("INTERCEPTOR_GAS2", "GAS_SENSOR", 0)) checks.append(("GAS_SENSOR", 50)) - return CANParser(DBC[CP.carFingerprint]['pt'], signals, checks, 0, timeout=100) + return CANParser(DBC[CP.carFingerprint]['pt'], signals, checks, 0) def get_cam_can_parser(CP): @@ -82,7 +83,7 @@ def get_cam_can_parser(CP): # use steering message to check if panda is connected to frc checks = [("STEERING_LKA", 42)] - return CANParser(DBC[CP.carFingerprint]['pt'], signals, checks, 2, timeout=100) + return CANParser(DBC[CP.carFingerprint]['pt'], signals, checks, 2) class CarState(object): @@ -118,7 +119,7 @@ def update(self, cp): self.brake_pressed = cp.vl["BRAKE_MODULE"]['BRAKE_PRESSED'] if self.CP.enableGasInterceptor: - self.pedal_gas = cp.vl["GAS_SENSOR"]['INTERCEPTOR_GAS'] + self.pedal_gas = (cp.vl["GAS_SENSOR"]['INTERCEPTOR_GAS'] + cp.vl["GAS_SENSOR"]['INTERCEPTOR_GAS2']) / 2. else: self.pedal_gas = cp.vl["GAS_PEDAL"]['GAS_PEDAL'] self.car_gas = self.pedal_gas diff --git a/selfdrive/car/toyota/interface.py b/selfdrive/car/toyota/interface.py index 29abfeb8464db6..5e5cb2c8393d17 100755 --- a/selfdrive/car/toyota/interface.py +++ b/selfdrive/car/toyota/interface.py @@ -9,7 +9,6 @@ from selfdrive.car import STD_CARGO_KG, scale_rot_inertia, scale_tire_stiffness from selfdrive.swaglog import cloudlog - class CarInterface(object): def __init__(self, CP, CarController): self.CP = CP @@ -175,6 +174,16 @@ def get_params(candidate, fingerprint, vin=""): ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.6], [0.1]] ret.lateralTuning.pid.kf = 0.00007818594 + elif candidate == CAR.SIENNA: + stop_and_go = True + ret.safetyParam = 73 + ret.wheelbase = 3.03 + ret.steerRatio = 16.0 + tire_stiffness_factor = 0.444 + ret.mass = 4590. * CV.LB_TO_KG + STD_CARGO_KG + ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.3], [0.05]] + ret.lateralTuning.pid.kf = 0.00007818594 + ret.steerRateCost = 1. ret.centerToFront = ret.wheelbase * 0.44 @@ -201,8 +210,8 @@ def get_params(candidate, fingerprint, vin=""): # steer, gas, brake limitations VS speed ret.steerMaxBP = [16. * CV.KPH_TO_MS, 45. * CV.KPH_TO_MS] # breakpoints at 1 and 40 kph ret.steerMaxV = [1., 1.] # 2/3rd torque allowed above 45 kph - ret.brakeMaxBP = [5., 20.] - ret.brakeMaxV = [1., 0.8] + ret.brakeMaxBP = [0.] + ret.brakeMaxV = [1.] ret.enableCamera = not check_ecu_msgs(fingerprint, ECU.CAM) ret.enableDsu = not check_ecu_msgs(fingerprint, ECU.DSU) @@ -236,22 +245,20 @@ def get_params(candidate, fingerprint, vin=""): return ret # returns a car.CarState - def update(self, c): + def update(self, c, can_strings): # ******************* do can recv ******************* - canMonoTimes = [] - - can_rcv_valid, _ = self.cp.update(int(sec_since_boot() * 1e9), True) + self.cp.update_strings(int(sec_since_boot() * 1e9), can_strings) # run the cam can update for 10s as we just need to know if the camera is alive if self.frame < 1000: - self.cp_cam.update(int(sec_since_boot() * 1e9), False) + self.cp_cam.update_strings(int(sec_since_boot() * 1e9), can_strings) self.CS.update(self.cp) # create message ret = car.CarState.new_message() - ret.canValid = can_rcv_valid and self.cp.can_valid + ret.canValid = self.cp.can_valid # speeds ret.vEgo = self.CS.v_ego @@ -368,7 +375,6 @@ def update(self, c): events.append(create_event('pedalPressed', [ET.PRE_ENABLE])) ret.events = events - ret.canMonoTimes = canMonoTimes self.gas_pressed_prev = ret.gasPressed self.brake_pressed_prev = ret.brakePressed diff --git a/selfdrive/car/toyota/radar_interface.py b/selfdrive/car/toyota/radar_interface.py index c160e751aa06bc..4e0a0e809b777a 100755 --- a/selfdrive/car/toyota/radar_interface.py +++ b/selfdrive/car/toyota/radar_interface.py @@ -46,32 +46,36 @@ def __init__(self, CP): self.valid_cnt = {key: 0 for key in self.RADAR_A_MSGS} self.rcp = _create_radar_can_parser(CP.carFingerprint) + self.trigger_msg = self.RADAR_B_MSGS[-1] + self.updated_messages = set() + # No radar dbc for cars without DSU which are not TSS 2.0 # TODO: make a adas dbc file for dsu-less models self.no_radar = CP.carFingerprint in NO_DSU_CAR and CP.carFingerprint not in TSS2_CAR - def update(self): - - ret = car.RadarData.new_message() - + def update(self, can_strings): if self.no_radar: time.sleep(0.05) - return ret + return car.RadarData.new_message() + + tm = int(sec_since_boot() * 1e9) + vls = self.rcp.update_strings(tm, can_strings) + self.updated_messages.update(vls) - canMonoTimes = [] - updated_messages = set() - while 1: - tm = int(sec_since_boot() * 1e9) - _, vls = self.rcp.update(tm, True) - updated_messages.update(vls) - if self.RADAR_B_MSGS[-1] in updated_messages: - break + if self.trigger_msg not in self.updated_messages: + return None + rr = self._update(self.updated_messages) + self.updated_messages.clear() + + return rr + + def _update(self, updated_messages): + ret = car.RadarData.new_message() errors = [] if not self.rcp.can_valid: errors.append("canError") ret.errors = errors - ret.canMonoTimes = canMonoTimes for ii in updated_messages: if ii in self.RADAR_A_MSGS: @@ -105,10 +109,3 @@ def update(self): ret.points = self.pts.values() return ret - -if __name__ == "__main__": - RI = RadarInterface(None) - while 1: - ret = RI.update() - print(chr(27) + "[2J") - print(ret) diff --git a/selfdrive/car/toyota/values.py b/selfdrive/car/toyota/values.py index da8b02dcd7212c..df535eab05a852 100644 --- a/selfdrive/car/toyota/values.py +++ b/selfdrive/car/toyota/values.py @@ -16,6 +16,7 @@ class CAR: RAV4_TSS2 = "TOYOTA RAV4 2019" COROLLA_TSS2 = "TOYOTA COROLLA TSS2 2019" LEXUS_ESH_TSS2 = "LEXUS ES 300H 2019" + SIENNA = "TOYOTA SIENNA XLE 2018" class ECU: @@ -42,23 +43,23 @@ class ECU: (0x4d3, ECU.CAM, (CAR.PRIUS, CAR.RAV4H, CAR.LEXUS_RXH, CAR.RAV4, CAR.COROLLA, CAR.AVALON), 0, 100, '\x1C\x00\x00\x01\x00\x00\x00\x00'), (0x128, ECU.DSU, (CAR.PRIUS, CAR.RAV4H, CAR.LEXUS_RXH, CAR.RAV4, CAR.COROLLA, CAR.AVALON), 1, 3, '\xf4\x01\x90\x83\x00\x37'), - (0x128, ECU.DSU, (CAR.HIGHLANDER, CAR.HIGHLANDERH), 1, 3, '\x03\x00\x20\x00\x00\x52'), - (0x141, ECU.DSU, (CAR.PRIUS, CAR.RAV4H, CAR.LEXUS_RXH, CAR.RAV4, CAR.COROLLA, CAR.HIGHLANDER, CAR.HIGHLANDERH, CAR.AVALON), 1, 2, '\x00\x00\x00\x46'), - (0x160, ECU.DSU, (CAR.PRIUS, CAR.RAV4H, CAR.LEXUS_RXH, CAR.RAV4, CAR.COROLLA, CAR.HIGHLANDER, CAR.HIGHLANDERH, CAR.AVALON), 1, 7, '\x00\x00\x08\x12\x01\x31\x9c\x51'), + (0x128, ECU.DSU, (CAR.HIGHLANDER, CAR.HIGHLANDERH, CAR.SIENNA), 1, 3, '\x03\x00\x20\x00\x00\x52'), + (0x141, ECU.DSU, (CAR.PRIUS, CAR.RAV4H, CAR.LEXUS_RXH, CAR.RAV4, CAR.COROLLA, CAR.HIGHLANDER, CAR.HIGHLANDERH, CAR.AVALON, CAR.SIENNA), 1, 2, '\x00\x00\x00\x46'), + (0x160, ECU.DSU, (CAR.PRIUS, CAR.RAV4H, CAR.LEXUS_RXH, CAR.RAV4, CAR.COROLLA, CAR.HIGHLANDER, CAR.HIGHLANDERH, CAR.AVALON, CAR.SIENNA), 1, 7, '\x00\x00\x08\x12\x01\x31\x9c\x51'), (0x161, ECU.DSU, (CAR.PRIUS, CAR.RAV4H, CAR.LEXUS_RXH, CAR.RAV4, CAR.COROLLA, CAR.AVALON), 1, 7, '\x00\x1e\x00\x00\x00\x80\x07'), - (0X161, ECU.DSU, (CAR.HIGHLANDERH, CAR.HIGHLANDER), 1, 7, '\x00\x1e\x00\xd4\x00\x00\x5b'), - (0x283, ECU.DSU, (CAR.PRIUS, CAR.RAV4H, CAR.LEXUS_RXH, CAR.RAV4, CAR.COROLLA, CAR.HIGHLANDER, CAR.HIGHLANDERH, CAR.AVALON), 0, 3, '\x00\x00\x00\x00\x00\x00\x8c'), + (0X161, ECU.DSU, (CAR.HIGHLANDERH, CAR.HIGHLANDER, CAR.SIENNA), 1, 7, '\x00\x1e\x00\xd4\x00\x00\x5b'), + (0x283, ECU.DSU, (CAR.PRIUS, CAR.RAV4H, CAR.LEXUS_RXH, CAR.RAV4, CAR.COROLLA, CAR.HIGHLANDER, CAR.HIGHLANDERH, CAR.AVALON, CAR.SIENNA), 0, 3, '\x00\x00\x00\x00\x00\x00\x8c'), (0x2E6, ECU.DSU, (CAR.PRIUS, CAR.RAV4H, CAR.LEXUS_RXH), 0, 3, '\xff\xf8\x00\x08\x7f\xe0\x00\x4e'), (0x2E7, ECU.DSU, (CAR.PRIUS, CAR.RAV4H, CAR.LEXUS_RXH), 0, 3, '\xa8\x9c\x31\x9c\x00\x00\x00\x02'), (0x33E, ECU.DSU, (CAR.PRIUS, CAR.RAV4H, CAR.LEXUS_RXH), 0, 20, '\x0f\xff\x26\x40\x00\x1f\x00'), - (0x344, ECU.DSU, (CAR.PRIUS, CAR.RAV4H, CAR.LEXUS_RXH, CAR.RAV4, CAR.COROLLA, CAR.HIGHLANDER, CAR.HIGHLANDERH, CAR.AVALON), 0, 5, '\x00\x00\x01\x00\x00\x00\x00\x50'), + (0x344, ECU.DSU, (CAR.PRIUS, CAR.RAV4H, CAR.LEXUS_RXH, CAR.RAV4, CAR.COROLLA, CAR.HIGHLANDER, CAR.HIGHLANDERH, CAR.AVALON, CAR.SIENNA), 0, 5, '\x00\x00\x01\x00\x00\x00\x00\x50'), (0x365, ECU.DSU, (CAR.PRIUS, CAR.LEXUS_RXH, CAR.HIGHLANDERH), 0, 20, '\x00\x00\x00\x80\x03\x00\x08'), - (0x365, ECU.DSU, (CAR.RAV4, CAR.RAV4H, CAR.COROLLA, CAR.HIGHLANDER, CAR.AVALON), 0, 20, '\x00\x00\x00\x80\xfc\x00\x08'), + (0x365, ECU.DSU, (CAR.RAV4, CAR.RAV4H, CAR.COROLLA, CAR.HIGHLANDER, CAR.AVALON, CAR.SIENNA), 0, 20, '\x00\x00\x00\x80\xfc\x00\x08'), (0x366, ECU.DSU, (CAR.PRIUS, CAR.RAV4H, CAR.LEXUS_RXH, CAR.HIGHLANDERH), 0, 20, '\x00\x00\x4d\x82\x40\x02\x00'), - (0x366, ECU.DSU, (CAR.RAV4, CAR.COROLLA, CAR.HIGHLANDER, CAR.AVALON), 0, 20, '\x00\x72\x07\xff\x09\xfe\x00'), + (0x366, ECU.DSU, (CAR.RAV4, CAR.COROLLA, CAR.HIGHLANDER, CAR.AVALON, CAR.SIENNA), 0, 20, '\x00\x72\x07\xff\x09\xfe\x00'), (0x470, ECU.DSU, (CAR.PRIUS, CAR.LEXUS_RXH), 1, 100, '\x00\x00\x02\x7a'), - (0x470, ECU.DSU, (CAR.HIGHLANDER, CAR.HIGHLANDERH, CAR.RAV4H), 1, 100, '\x00\x00\x01\x79'), - (0x4CB, ECU.DSU, (CAR.PRIUS, CAR.RAV4H, CAR.LEXUS_RXH, CAR.RAV4, CAR.COROLLA, CAR.HIGHLANDERH, CAR.HIGHLANDER, CAR.AVALON), 0, 100, '\x0c\x00\x00\x00\x00\x00\x00\x00'), + (0x470, ECU.DSU, (CAR.HIGHLANDER, CAR.HIGHLANDERH, CAR.RAV4H, CAR.SIENNA), 1, 100, '\x00\x00\x01\x79'), + (0x4CB, ECU.DSU, (CAR.PRIUS, CAR.RAV4H, CAR.LEXUS_RXH, CAR.RAV4, CAR.COROLLA, CAR.HIGHLANDERH, CAR.HIGHLANDER, CAR.AVALON, CAR.SIENNA), 0, 100, '\x0c\x00\x00\x00\x00\x00\x00\x00'), (0x292, ECU.APGS, (CAR.PRIUS), 0, 3, '\x00\x00\x00\x00\x00\x00\x00\x9e'), (0x32E, ECU.APGS, (CAR.PRIUS), 0, 20, '\x00\x00\x00\x00\x00\x00\x00\x00'), @@ -178,6 +179,9 @@ def check_ecu_msgs(fingerprint, ecu): { 36: 8, 37: 8, 166: 8, 170: 8, 180: 8, 295: 8, 296: 8, 401: 8, 426: 6, 452: 8, 466: 8, 467: 8, 550: 8, 552: 4, 560: 7, 562: 6, 581: 5, 608: 8, 610: 8, 643: 7, 658: 8, 713: 8, 728: 8, 740: 5, 742: 8, 743: 8, 744: 8, 761: 8, 764: 8, 765: 8, 800: 8, 810: 2, 812: 8, 814: 8, 818: 8, 824: 8, 829: 2, 830: 7, 835: 8, 836: 8, 863: 8, 865: 8, 869: 7, 870: 7, 871: 2, 877: 8, 881: 8, 882: 8, 885: 8, 889: 8, 896: 8, 898: 8, 900: 6, 902: 6, 905: 8, 913: 8, 918: 8, 921: 8, 933: 8, 934: 8, 935: 8, 944: 8, 945: 8, 950: 8, 951: 8, 953: 8, 955: 8, 956: 8, 971: 7, 975: 5, 987: 8, 993: 8, 998: 5, 999: 7, 1000: 8, 1001: 8, 1002: 8, 1017: 8, 1020: 8, 1041: 8, 1042: 8, 1056: 8, 1057: 8, 1059: 1, 1071: 8, 1076: 8, 1077: 8, 1082: 8, 1084: 8, 1085: 8, 1086: 8, 1114: 8, 1132: 8, 1161: 8, 1162: 8, 1163: 8, 1164: 8, 1165: 8, 1166: 8, 1167: 8, 1172: 8, 1228: 8, 1235: 8, 1264: 8, 1279: 8, 1541: 8, 1552: 8, 1553: 8, 1556: 8, 1557: 8, 1568: 8, 1570: 8, 1571: 8, 1572: 8, 1575: 8, 1592: 8, 1594: 8, 1595: 8, 1649: 8, 1696: 8, 1775: 8, 1777: 8, 1779: 8, 1786: 8, 1787: 8, 1788: 8, 1789: 8, 1904: 8, 1912: 8, 1990: 8, 1998: 8 }], + CAR.SIENNA: [{ + 36: 8, 37: 8, 114: 5, 119: 6, 120: 4, 170: 8, 180: 8, 186: 4, 426: 6, 452: 8, 464: 8, 466: 8, 467: 8, 544: 4, 545: 5, 548: 8, 550: 8, 552: 4, 562: 4, 608: 8, 610: 5, 643: 7, 705: 8, 725: 2, 740: 5, 764: 8, 800: 8, 824: 8, 835: 8, 836: 8, 849: 4, 869: 7, 870: 7, 871: 2, 888: 8, 896: 8, 900: 6, 902: 6, 905: 8, 911: 8, 916: 1, 918: 7, 921: 8, 933: 8, 944: 6, 945: 8, 951: 8, 955: 8, 956: 8, 979: 2, 992: 8, 998: 5, 999: 7, 1000: 8, 1001: 8, 1002: 8, 1008: 2, 1014: 8, 1017: 8, 1041: 8, 1042: 8, 1043: 8, 1056: 8, 1059: 1, 1076: 8, 1077: 8, 1114: 8, 1160: 8, 1161: 8, 1162: 8, 1163: 8, 1164: 8, 1165: 8, 1166: 8, 1167: 8, 1176: 8, 1177: 8, 1178: 8, 1179: 8, 1180: 8, 1181: 8, 1182: 8, 1183: 8, 1191: 8, 1192: 8, 1196: 8, 1197: 8, 1198: 8, 1199: 8, 1200: 8, 1201: 8, 1202: 8, 1203: 8, 1212: 8, 1227: 8, 1228: 8, 1235: 8, 1237: 8, 1279: 8, 1552: 8, 1553: 8, 1555: 8, 1556: 8, 1557: 8, 1561: 8, 1562: 8, 1568: 8, 1569: 8, 1570: 8, 1571: 8, 1572: 8, 1584: 8, 1589: 8, 1592: 8, 1593: 8, 1595: 8, 1656: 8, 1664: 8, 1666: 8, 1667: 8, 1728: 8, 1745: 8, 1779: 8, 1904: 8, 1912: 8, 1990: 8, 1998: 8 + }], } STEER_THRESHOLD = 100 @@ -198,8 +202,9 @@ def check_ecu_msgs(fingerprint, ecu): CAR.RAV4_TSS2: dbc_dict('toyota_nodsu_pt_generated', 'toyota_tss2_adas'), CAR.COROLLA_TSS2: dbc_dict('toyota_nodsu_pt_generated', 'toyota_tss2_adas'), CAR.LEXUS_ESH_TSS2: dbc_dict('toyota_nodsu_pt_generated', 'toyota_tss2_adas'), + CAR.SIENNA: dbc_dict('toyota_sienna_xle_2018_pt_generated', 'toyota_adas'), } NO_DSU_CAR = [CAR.CHR, CAR.CHRH, CAR.CAMRY, CAR.CAMRYH, CAR.RAV4_TSS2, CAR.COROLLA_TSS2, CAR.LEXUS_ESH_TSS2] TSS2_CAR = [CAR.RAV4_TSS2, CAR.COROLLA_TSS2, CAR.LEXUS_ESH_TSS2] -NO_STOP_TIMER_CAR = [CAR.RAV4H, CAR.HIGHLANDERH, CAR.HIGHLANDER, CAR.RAV4_TSS2, CAR.COROLLA_TSS2, CAR.LEXUS_ESH_TSS2] # no resume button press required +NO_STOP_TIMER_CAR = [CAR.RAV4H, CAR.HIGHLANDERH, CAR.HIGHLANDER, CAR.RAV4_TSS2, CAR.COROLLA_TSS2, CAR.LEXUS_ESH_TSS2, CAR.SIENNA] # no resume button press required diff --git a/selfdrive/common/version.h b/selfdrive/common/version.h index ad486deed50efd..5c21a9db872ca1 100644 --- a/selfdrive/common/version.h +++ b/selfdrive/common/version.h @@ -1 +1 @@ -#define COMMA_VERSION "0.6-release" +#define COMMA_VERSION "0.6.1-release" diff --git a/selfdrive/controls/controlsd.py b/selfdrive/controls/controlsd.py index f1c556ba12e2e0..5c5ba2f66183ba 100755 --- a/selfdrive/controls/controlsd.py +++ b/selfdrive/controls/controlsd.py @@ -22,7 +22,7 @@ from selfdrive.controls.lib.latcontrol_indi import LatControlINDI from selfdrive.controls.lib.alertmanager import AlertManager from selfdrive.controls.lib.vehicle_model import VehicleModel -from selfdrive.controls.lib.driver_monitor import DriverStatus +from selfdrive.controls.lib.driver_monitor import DriverStatus, MAX_TERMINAL_ALERTS from selfdrive.controls.lib.planner import LON_MPC_STEP from selfdrive.locationd.calibration_helpers import Calibration, Filter @@ -49,16 +49,22 @@ def events_to_bytes(events): return ret -def data_sample(CI, CC, sm, cal_status, cal_perc, overtemp, free_space, low_battery, +def data_sample(CI, CC, sm, can_sock, cal_status, cal_perc, overtemp, free_space, low_battery, driver_status, state, mismatch_counter, params): """Receive data from sockets and create events for battery, temperature and disk space""" # Update carstate from CAN and create events - CS = CI.update(CC) + can_strs = messaging.drain_sock_raw(can_sock, wait_for_one=True) + CS = CI.update(CC, can_strs) + + sm.update(0) + events = list(CS.events) enabled = isEnabled(state) - sm.update(0) + # Check for CAN timeout + if not can_strs: + events.append(create_event('canError', [ET.NO_ENTRY, ET.IMMEDIATE_DISABLE])) if sm.updated['thermal']: overtemp = sm['thermal'].thermalStatus >= ThermalStatus.red @@ -73,6 +79,7 @@ def data_sample(CI, CC, sm, cal_status, cal_perc, overtemp, free_space, low_batt if free_space: events.append(create_event('outOfSpace', [ET.NO_ENTRY])) + # Handle calibration if sm.updated['liveCalibration']: cal_status = sm['liveCalibration'].calStatus @@ -102,6 +109,9 @@ def data_sample(CI, CC, sm, cal_status, cal_perc, overtemp, free_space, low_batt if sm.updated['driverMonitoring']: driver_status.get_pose(sm['driverMonitoring'], params) + if driver_status.terminal_alert_cnt >= MAX_TERMINAL_ALERTS: + events.append(create_event("tooDistracted", [ET.NO_ENTRY])) + return CS, events, cal_status, cal_perc, overtemp, free_space, low_battery, mismatch_counter @@ -402,6 +412,7 @@ def controlsd_thread(gctx=None): params = Params() + # Pub Sockets sendcan = messaging.pub_sock(service_list['sendcan'].port) controlsstate = messaging.pub_sock(service_list['controlsState'].port) @@ -414,10 +425,15 @@ def controlsd_thread(gctx=None): passive = params.get("Passive") != "0" sm = messaging.SubMaster(['thermal', 'health', 'liveCalibration', 'driverMonitoring', 'plan', 'pathPlan']) + logcan = messaging.sub_sock(service_list['can'].port) + CI, CP = get_car(logcan, sendcan) + logcan.close() + + # TODO: Use the logcan socket from above, but that will currenly break the tests + can_sock = messaging.sub_sock(service_list['can'].port, timeout=100) CC = car.CarControl.new_message() - CI, CP = get_car(logcan, sendcan) AM = AlertManager() car_recognized = CP.carName != 'mock' @@ -469,7 +485,7 @@ def controlsd_thread(gctx=None): # Sample data and compute car events CS, events, cal_status, cal_perc, overtemp, free_space, low_battery, mismatch_counter =\ - data_sample(CI, CC, sm, cal_status, cal_perc, overtemp, free_space, low_battery, + data_sample(CI, CC, sm, can_sock, cal_status, cal_perc, overtemp, free_space, low_battery, driver_status, state, mismatch_counter, params) prof.checkpoint("Sample") diff --git a/selfdrive/controls/lib/alerts.py b/selfdrive/controls/lib/alerts.py index da7e88acbd46dd..0b0d5b1d5475b7 100644 --- a/selfdrive/controls/lib/alerts.py +++ b/selfdrive/controls/lib/alerts.py @@ -298,6 +298,13 @@ def __gt__(self, alert2): AlertStatus.normal, AlertSize.mid, Priority.LOW, VisualAlert.none, AudibleAlert.chimeError, .4, 2., 3.), + Alert( + "tooDistractedNoEntry", + "openpilot Unavailable", + "Distraction Level Too High", + AlertStatus.normal, AlertSize.mid, + Priority.LOW, VisualAlert.none, AudibleAlert.chimeError, .4, 2., 3.), + # Cancellation alerts causing soft disabling Alert( "overheat", diff --git a/selfdrive/controls/lib/driver_monitor.py b/selfdrive/controls/lib/driver_monitor.py index e813d0b176c81e..d38a115f974190 100644 --- a/selfdrive/controls/lib/driver_monitor.py +++ b/selfdrive/controls/lib/driver_monitor.py @@ -14,11 +14,12 @@ _METRIC_THRESHOLD = 0.4 _PITCH_POS_ALLOWANCE = 0.08 # rad, to not be too sensitive on positive pitch _PITCH_NATURAL_OFFSET = 0.1 # people don't seem to look straight when they drive relaxed, rather a bit up -_YAW_NATURAL_OFFSET = 0.08 # people don't seem to look straight when they drive relaxed, rather a bit to the right (center of car) +_YAW_NATURAL_OFFSET = 0.08 # people don't seem to look straight when they drive relaxed, rather a bit to the right (center of car) _STD_THRESHOLD = 0.1 # above this standard deviation consider the measurement invalid _DISTRACTED_FILTER_TS = 0.25 # 0.6Hz _VARIANCE_FILTER_TS = 20. # 0.008Hz +MAX_TERMINAL_ALERTS = 3 # not allowed to engage after 3 terminal alerts RESIZED_FOCAL = 320.0 H, W, FULL_W = 320, 160, 426 @@ -68,6 +69,7 @@ def __init__(self, monitor_on=False): self.variance_filter = FirstOrderFilter(0., _VARIANCE_FILTER_TS, DT_DMON) self.ts_last_check = 0. self.face_detected = False + self.terminal_alert_cnt = 0 self._set_timers() def _reset_filters(self): @@ -133,6 +135,7 @@ def get_pose(self, driver_monitoring, params): def update(self, events, driver_engaged, ctrl_active, standstill): driver_engaged |= (self.driver_distraction_filter.x < 0.37 and self.monitor_on) + awareness_prev = self.awareness if (driver_engaged and self.awareness > 0.) or not ctrl_active: # always reset if driver is in control (unless we are in red alert state) or op isn't active @@ -144,15 +147,18 @@ def update(self, events, driver_engaged, ctrl_active, standstill): self.awareness = max(self.awareness - self.step_change, -0.1) alert = None - if self.awareness <= 0.: + if self.awareness < 0.: # terminal red alert: disengagement required alert = 'driverDistracted' if self.monitor_on else 'driverUnresponsive' + if awareness_prev >= 0.: + self.terminal_alert_cnt += 1 elif self.awareness <= self.threshold_prompt: # prompt orange alert alert = 'promptDriverDistracted' if self.monitor_on else 'promptDriverUnresponsive' elif self.awareness <= self.threshold_pre: # pre green alert alert = 'preDriverDistracted' if self.monitor_on else 'preDriverUnresponsive' + if alert is not None: events.append(create_event(alert, [ET.WARNING])) diff --git a/selfdrive/controls/lib/fcw.py b/selfdrive/controls/lib/fcw.py index f93a72cfcce321..8180fadebae42d 100644 --- a/selfdrive/controls/lib/fcw.py +++ b/selfdrive/controls/lib/fcw.py @@ -43,8 +43,10 @@ def calc_ttc(v_ego, a_ego, x_lead, v_lead, a_lead): ttc = np.minimum(2 * x_lead / (np.sqrt(delta) + v_rel), max_ttc) return ttc - def update(self, mpc_solution, cur_time, v_ego, a_ego, x_lead, v_lead, a_lead, y_lead, vlat_lead, fcw_lead, blinkers): + def update(self, mpc_solution, cur_time, active, v_ego, a_ego, x_lead, v_lead, a_lead, y_lead, vlat_lead, fcw_lead, blinkers): mpc_solution_a = list(mpc_solution[0].a_ego) + a_target = mpc_solution_a[1] + self.last_min_a = min(mpc_solution_a) self.v_lead_max = max(self.v_lead_max, v_lead) @@ -62,8 +64,11 @@ def update(self, mpc_solution, cur_time, v_ego, a_ego, x_lead, v_lead, a_lead, y a_thr = interp(v_lead, _FCW_A_ACT_BP, _FCW_A_ACT_V) a_delta = min(mpc_solution_a[:15]) - min(0.0, a_ego) - fcw_allowed = all(c >= 10 for c in self.counters.values()) - if (self.last_min_a < -3.0 or a_delta < a_thr) and fcw_allowed and self.last_fcw_time + 5.0 < cur_time: + future_fcw_allowed = all(c >= 10 for c in self.counters.values()) + future_fcw = (self.last_min_a < -3.0 or a_delta < a_thr) and future_fcw_allowed + current_fcw = a_target < -3.0 and active + + if (future_fcw or current_fcw) and (self.last_fcw_time + 5.0 < cur_time): self.last_fcw_time = cur_time self.last_fcw_a = self.last_min_a return True diff --git a/selfdrive/controls/lib/planner.py b/selfdrive/controls/lib/planner.py index 9aaec513bee969..94cc2fc1358cad 100755 --- a/selfdrive/controls/lib/planner.py +++ b/selfdrive/controls/lib/planner.py @@ -201,7 +201,9 @@ def update(self, sm, CP, VM, PP, live_map_data): self.fcw_checker.reset_lead(cur_time) blinkers = sm['carState'].leftBlinker or sm['carState'].rightBlinker - fcw = self.fcw_checker.update(self.mpc1.mpc_solution, cur_time, v_ego, sm['carState'].aEgo, + fcw = self.fcw_checker.update(self.mpc1.mpc_solution, cur_time, + sm['controlsState'].active, + v_ego, sm['carState'].aEgo, lead_1.dRel, lead_1.vLead, lead_1.aLeadK, lead_1.yRel, lead_1.vLat, lead_1.fcw, blinkers) and not sm['carState'].brakePressed diff --git a/selfdrive/controls/radard.py b/selfdrive/controls/radard.py index b37ed8f75519b5..afb4ffd2bc6f13 100755 --- a/selfdrive/controls/radard.py +++ b/selfdrive/controls/radard.py @@ -26,6 +26,11 @@ XV, SPEEDV = 0, 1 VISION_POINT = -1 +path_x = np.arange(0.0, 140.0, 0.1) # 140 meters is max + +# Time-alignment +rate = 1. / DT_MDL # model and radar are both at 20Hz +v_len = 20 # how many speed data points to remember for t alignment with rdr data class EKFV1D(EKF): def __init__(self): @@ -43,171 +48,133 @@ def calc_transfer_fun(self, dt): tfj = tf return tf, tfj +class RadarD(object): + def __init__(self, VM, mocked): + self.VM = VM + self.mocked = mocked -## fuses camera and radar data for best lead detection -def radard_thread(gctx=None): - set_realtime_priority(2) + self.MP = ModelParser() + self.tracks = defaultdict(dict) - # wait for stats about the car to come in from controls - cloudlog.info("radard is waiting for CarParams") - CP = car.CarParams.from_bytes(Params().get("CarParams", block=True)) - mocked = CP.carName == "mock" - VM = VehicleModel(CP) - cloudlog.info("radard got CarParams") - - # import the radar from the fingerprint - cloudlog.info("radard is importing %s", CP.carName) - RadarInterface = importlib.import_module('selfdrive.car.%s.radar_interface' % CP.carName).RadarInterface - - sm = messaging.SubMaster(['model', 'controlsState', 'liveParameters']) + self.last_md_ts = 0 + self.last_controls_state_ts = 0 - # Default parameters - live_parameters = messaging.new_message() - live_parameters.init('liveParameters') - live_parameters.liveParameters.valid = True - live_parameters.liveParameters.steerRatio = CP.steerRatio - live_parameters.liveParameters.stiffnessFactor = 1.0 + self.active = 0 + self.steer_angle = 0. + self.steer_override = False - MP = ModelParser() - RI = RadarInterface(CP) + # Kalman filter stuff: + self.ekfv = EKFV1D() + self.speedSensorV = SimpleSensor(XV, 1, 2) - last_md_ts = 0 - last_controls_state_ts = 0 - - # *** publish radarState and liveTracks - radarState = messaging.pub_sock(service_list['radarState'].port) - liveTracks = messaging.pub_sock(service_list['liveTracks'].port) - - path_x = np.arange(0.0, 140.0, 0.1) # 140 meters is max - - # Time-alignment - rate = 1. / DT_MDL # model and radar are both at 20Hz - v_len = 20 # how many speed data points to remember for t alignment with rdr data - - active = 0 - steer_angle = 0. - steer_override = False - - tracks = defaultdict(dict) - - # Kalman filter stuff: - ekfv = EKFV1D() - speedSensorV = SimpleSensor(XV, 1, 2) - - # v_ego - v_ego = 0. - v_ego_hist_t = deque([0], maxlen=v_len) - v_ego_hist_v = deque([0], maxlen=v_len) - v_ego_t_aligned = 0. - - rk = Ratekeeper(rate, print_delay_threshold=None) - while 1: - rr = RI.update() + # v_ego + self.v_ego = 0. + self.v_ego_hist_t = deque([0], maxlen=v_len) + self.v_ego_hist_v = deque([0], maxlen=v_len) + self.v_ego_t_aligned = 0. + def update(self, frame, delay, sm, rr): ar_pts = {} for pt in rr.points: ar_pts[pt.trackId] = [pt.dRel + RDR_TO_LDR, pt.yRel, pt.vRel, pt.measured] - sm.update(0) - if sm.updated['liveParameters']: - VM.update_params(sm['liveParameters'].stiffnessFactor, sm['liveParameters'].steerRatio) + self.VM.update_params(sm['liveParameters'].stiffnessFactor, sm['liveParameters'].steerRatio) if sm.updated['controlsState']: - active = sm['controlsState'].active - v_ego = sm['controlsState'].vEgo - steer_angle = sm['controlsState'].angleSteers - steer_override = sm['controlsState'].steerOverride + self.active = sm['controlsState'].active + self.v_ego = sm['controlsState'].vEgo + self.steer_angle = sm['controlsState'].angleSteers + self.steer_override = sm['controlsState'].steerOverride - v_ego_hist_v.append(v_ego) - v_ego_hist_t.append(float(rk.frame)/rate) + self.v_ego_hist_v.append(self.v_ego) + self.v_ego_hist_t.append(float(frame)/rate) - last_controls_state_ts = sm.logMonoTime['controlsState'] + self.last_controls_state_ts = sm.logMonoTime['controlsState'] if sm.updated['model']: - last_md_ts = sm.logMonoTime['model'] - MP.update(v_ego, sm['model']) - + self.last_md_ts = sm.logMonoTime['model'] + self.MP.update(self.v_ego, sm['model']) # run kalman filter only if prob is high enough - if MP.lead_prob > 0.7: - reading = speedSensorV.read(MP.lead_dist, covar=np.matrix(MP.lead_var)) - ekfv.update_scalar(reading) - ekfv.predict(DT_MDL) + if self.MP.lead_prob > 0.7: + reading = self.speedSensorV.read(self.MP.lead_dist, covar=np.matrix(self.MP.lead_var)) + self.ekfv.update_scalar(reading) + self.ekfv.predict(DT_MDL) # When changing lanes the distance to the lead car can suddenly change, # which makes the Kalman filter output large relative acceleration - if mocked and abs(MP.lead_dist - ekfv.state[XV]) > 2.0: - ekfv.state[XV] = MP.lead_dist - ekfv.covar = (np.diag([MP.lead_var, ekfv.var_init])) - ekfv.state[SPEEDV] = 0. + if self.mocked and abs(self.MP.lead_dist - self.ekfv.state[XV]) > 2.0: + self.ekfv.state[XV] = self.MP.lead_dist + self.ekfv.covar = (np.diag([self.MP.lead_var, self.ekfv.var_init])) + self.ekfv.state[SPEEDV] = 0. - ar_pts[VISION_POINT] = (float(ekfv.state[XV]), np.polyval(MP.d_poly, float(ekfv.state[XV])), - float(ekfv.state[SPEEDV]), False) + ar_pts[VISION_POINT] = (float(self.ekfv.state[XV]), np.polyval(self.MP.d_poly, float(self.ekfv.state[XV])), + float(self.ekfv.state[SPEEDV]), False) else: - ekfv.state[XV] = MP.lead_dist - ekfv.covar = (np.diag([MP.lead_var, ekfv.var_init])) - ekfv.state[SPEEDV] = 0. + self.ekfv.state[XV] = self.MP.lead_dist + self.ekfv.covar = (np.diag([self.MP.lead_var, self.ekfv.var_init])) + self.ekfv.state[SPEEDV] = 0. if VISION_POINT in ar_pts: del ar_pts[VISION_POINT] # *** compute the likely path_y *** - if (active and not steer_override) or mocked: + if (self.active and not self.steer_override) or self.mocked: # use path from model (always when mocking as steering is too noisy) - path_y = np.polyval(MP.d_poly, path_x) + path_y = np.polyval(self.MP.d_poly, path_x) else: # use path from steer, set angle_offset to 0 it does not only report the physical offset - path_y = calc_lookahead_offset(v_ego, steer_angle, path_x, VM, angle_offset=live_parameters.liveParameters.angleOffsetAverage)[0] + path_y = calc_lookahead_offset(self.v_ego, self.steer_angle, path_x, self.VM, angle_offset=sm['liveParameters'].angleOffsetAverage)[0] # *** remove missing points from meta data *** - for ids in tracks.keys(): + for ids in self.tracks.keys(): if ids not in ar_pts: - tracks.pop(ids, None) + self.tracks.pop(ids, None) # *** compute the tracks *** for ids in ar_pts: # ignore standalone vision point, unless we are mocking the radar - if ids == VISION_POINT and not mocked: + if ids == VISION_POINT and not self.mocked: continue rpt = ar_pts[ids] # align v_ego by a fixed time to align it with the radar measurement - cur_time = float(rk.frame)/rate - v_ego_t_aligned = np.interp(cur_time - RI.delay, v_ego_hist_t, v_ego_hist_v) + cur_time = float(frame)/rate + self.v_ego_t_aligned = np.interp(cur_time - delay, self.v_ego_hist_t, self.v_ego_hist_v) d_path = np.sqrt(np.amin((path_x - rpt[0]) ** 2 + (path_y - rpt[1]) ** 2)) # add sign d_path *= np.sign(rpt[1] - np.interp(rpt[0], path_x, path_y)) # create the track if it doesn't exist or it's a new track - if ids not in tracks: - tracks[ids] = Track() - tracks[ids].update(rpt[0], rpt[1], rpt[2], d_path, v_ego_t_aligned, rpt[3], steer_override) + if ids not in self.tracks: + self.tracks[ids] = Track() + self.tracks[ids].update(rpt[0], rpt[1], rpt[2], d_path, self.v_ego_t_aligned, rpt[3], self.steer_override) # allow the vision model to remove the stationary flag if distance and rel speed roughly match if VISION_POINT in ar_pts: fused_id = None best_score = NO_FUSION_SCORE - for ids in tracks: - dist_to_vision = np.sqrt((0.5*(ar_pts[VISION_POINT][0] - tracks[ids].dRel)) ** 2 + (2*(ar_pts[VISION_POINT][1] - tracks[ids].yRel)) ** 2) - rel_speed_diff = abs(ar_pts[VISION_POINT][2] - tracks[ids].vRel) - tracks[ids].update_vision_score(dist_to_vision, rel_speed_diff) - if best_score > tracks[ids].vision_score: + for ids in self.tracks: + dist_to_vision = np.sqrt((0.5*(ar_pts[VISION_POINT][0] - self.tracks[ids].dRel)) ** 2 + (2*(ar_pts[VISION_POINT][1] - self.tracks[ids].yRel)) ** 2) + rel_speed_diff = abs(ar_pts[VISION_POINT][2] - self.tracks[ids].vRel) + self.tracks[ids].update_vision_score(dist_to_vision, rel_speed_diff) + if best_score > self.tracks[ids].vision_score: fused_id = ids - best_score = tracks[ids].vision_score + best_score = self.tracks[ids].vision_score if fused_id is not None: - tracks[fused_id].vision_cnt += 1 - tracks[fused_id].update_vision_fusion() + self.tracks[fused_id].vision_cnt += 1 + self.tracks[fused_id].update_vision_fusion() if DEBUG: print("NEW CYCLE") if VISION_POINT in ar_pts: print("vision", ar_pts[VISION_POINT]) - idens = list(tracks.keys()) - track_pts = np.array([tracks[iden].get_key_for_cluster() for iden in idens]) + idens = list(self.tracks.keys()) + track_pts = np.array([self.tracks[iden].get_key_for_cluster() for iden in idens]) # If we have multiple points, cluster them if len(track_pts) > 1: @@ -218,12 +185,12 @@ def radard_thread(gctx=None): cluster_i = cluster_idxs[idx] if clusters[cluster_i] is None: clusters[cluster_i] = Cluster() - clusters[cluster_i].add(tracks[idens[idx]]) + clusters[cluster_i].add(self.tracks[idens[idx]]) elif len(track_pts) == 1: # TODO: why do we need this? clusters = [Cluster()] - clusters[0].add(tracks[idens[0]]) + clusters[0].add(self.tracks[idens[0]]) else: clusters = [] @@ -232,7 +199,7 @@ def radard_thread(gctx=None): print(i) # *** extract the lead car *** lead_clusters = [c for c in clusters - if c.is_potential_lead(v_ego)] + if c.is_potential_lead(self.v_ego)] lead_clusters.sort(key=lambda x: x.dRel) lead_len = len(lead_clusters) @@ -246,10 +213,10 @@ def radard_thread(gctx=None): dat = messaging.new_message() dat.init('radarState') dat.valid = sm.all_alive_and_valid(service_list=['controlsState']) - dat.radarState.mdMonoTime = last_md_ts + dat.radarState.mdMonoTime = self.last_md_ts dat.radarState.canMonoTimes = list(rr.canMonoTimes) dat.radarState.radarErrors = list(rr.errors) - dat.radarState.controlsStateMonoTime = last_controls_state_ts + dat.radarState.controlsStateMonoTime = self.last_controls_state_ts if lead_len > 0: dat.radarState.leadOne = lead_clusters[0].toRadarState() if lead2_len > 0: @@ -259,10 +226,51 @@ def radard_thread(gctx=None): else: dat.radarState.leadOne.status = False + return dat + +## fuses camera and radar data for best lead detection +def radard_thread(gctx=None): + set_realtime_priority(2) + + # wait for stats about the car to come in from controls + cloudlog.info("radard is waiting for CarParams") + CP = car.CarParams.from_bytes(Params().get("CarParams", block=True)) + mocked = CP.carName == "mock" + VM = VehicleModel(CP) + cloudlog.info("radard got CarParams") + + # import the radar from the fingerprint + cloudlog.info("radard is importing %s", CP.carName) + RadarInterface = importlib.import_module('selfdrive.car.%s.radar_interface' % CP.carName).RadarInterface + + can_sock = messaging.sub_sock(service_list['can'].port) + sm = messaging.SubMaster(['model', 'controlsState', 'liveParameters']) + + RI = RadarInterface(CP) + + # *** publish radarState and liveTracks + radarState = messaging.pub_sock(service_list['radarState'].port) + liveTracks = messaging.pub_sock(service_list['liveTracks'].port) + + rk = Ratekeeper(rate, print_delay_threshold=None) + RD = RadarD(VM, mocked) + + while 1: + can_strings = messaging.drain_sock_raw(can_sock, wait_for_one=True) + rr = RI.update(can_strings) + + if rr is None: + continue + + sm.update(0) + + dat = RD.update(rk.frame, RI.delay, sm, rr) dat.radarState.cumLagMs = -rk.remaining*1000. + radarState.send(dat.to_bytes()) # *** publish tracks for UI debugging (keep last) *** + tracks = RD.tracks dat = messaging.new_message() dat.init('liveTracks', len(tracks)) diff --git a/selfdrive/locationd/.gitignore b/selfdrive/locationd/.gitignore index 8cdb0d23050783..6ea757462e62f8 100644 --- a/selfdrive/locationd/.gitignore +++ b/selfdrive/locationd/.gitignore @@ -1,3 +1,4 @@ ubloxd ubloxd_test -params_learner \ No newline at end of file +params_learner +paramsd \ No newline at end of file diff --git a/selfdrive/locationd/Makefile b/selfdrive/locationd/Makefile index f7652648e1bbe1..ff46847689c805 100644 --- a/selfdrive/locationd/Makefile +++ b/selfdrive/locationd/Makefile @@ -40,11 +40,11 @@ EXTRA_LIBS += -llog -luuid endif .PHONY: all -all: ubloxd params_learner +all: ubloxd paramsd include ../common/cereal.mk -LOC_OBJS = locationd_yawrate.o params_learner.o \ +LOC_OBJS = locationd_yawrate.o params_learner.o paramsd.o \ ../common/swaglog.o \ ../common/params.o \ ../common/util.o \ @@ -71,7 +71,7 @@ liblocationd.so: $(LOC_OBJS) $(ZMQ_SHARED_LIBS) \ $(EXTRA_LIBS) -params_learner: $(LOC_OBJS) +paramsd: $(LOC_OBJS) @echo "[ LINK ] $@" $(CXX) -fPIC -o '$@' $^ \ $(CEREAL_LIBS) \ @@ -115,7 +115,7 @@ ubloxd_test: ubloxd_test.o $(OBJS) .PHONY: clean clean: - rm -f ubloxd params_learner liblocationd.so ubloxd.d ubloxd.o ubloxd_test ubloxd_test.o ubloxd_test.d $(OBJS) $(LOC_OBJS) $(DEPS) + rm -f ubloxd paramsd liblocationd.so ubloxd.d ubloxd.o ubloxd_test ubloxd_test.o ubloxd_test.d $(OBJS) $(LOC_OBJS) $(DEPS) -include $(DEPS) -include $(LOC_DEPS) diff --git a/selfdrive/locationd/locationd_yawrate.cc b/selfdrive/locationd/locationd_yawrate.cc index f03f808ffdb99d..ae4b05a71a1c67 100644 --- a/selfdrive/locationd/locationd_yawrate.cc +++ b/selfdrive/locationd/locationd_yawrate.cc @@ -1,287 +1,96 @@ #include -#include #include -#include #include #include #include -#include "json11.hpp" -#include "cereal/gen/cpp/log.capnp.h" -#include "common/swaglog.h" -#include "common/messaging.h" -#include "common/params.h" -#include "common/timing.h" -#include "params_learner.h" +#include "locationd_yawrate.h" -const int num_polls = 3; - -class Localizer -{ - Eigen::Matrix2d A; - Eigen::Matrix2d I; - Eigen::Matrix2d Q; - Eigen::Matrix2d P; - Eigen::Matrix C_posenet; - Eigen::Matrix C_gyro; - - double R_gyro; - - void update_state(const Eigen::Matrix &C, const double R, double current_time, double meas) { - double dt = current_time - prev_update_time; - prev_update_time = current_time; - if (dt < 1.0e-9) { - return; - } - - // x = A * x; - // P = A * P * A.transpose() + dt * Q; - // Simplify because A is unity - P = P + dt * Q; - - double y = meas - C * x; - double S = R + C * P * C.transpose(); - Eigen::Vector2d K = P * C.transpose() * (1.0 / S); - x = x + K * y; - P = (I - K * C) * P; - } - - void handle_sensor_events(capnp::List::Reader sensor_events, double current_time) { - for (cereal::SensorEventData::Reader sensor_event : sensor_events){ - if (sensor_event.getType() == 4) { - sensor_data_time = current_time; - - double meas = -sensor_event.getGyro().getV()[0]; - update_state(C_gyro, R_gyro, current_time, meas); - } - } +void Localizer::update_state(const Eigen::Matrix &C, const double R, double current_time, double meas) { + double dt = current_time - prev_update_time; + prev_update_time = current_time; + if (dt < 1.0e-9) { + return; } - void handle_camera_odometry(cereal::CameraOdometry::Reader camera_odometry, double current_time) { - double R = 250.0 * pow(camera_odometry.getRotStd()[2], 2); - double meas = camera_odometry.getRot()[2]; - update_state(C_posenet, R, current_time, meas); - } - - void handle_controls_state(cereal::ControlsState::Reader controls_state, double current_time) { - steering_angle = controls_state.getAngleSteers() * DEGREES_TO_RADIANS; - car_speed = controls_state.getVEgo(); - controls_state_time = current_time; - } + // x = A * x; + // P = A * P * A.transpose() + dt * Q; + // Simplify because A is unity + P = P + dt * Q; + double y = meas - C * x; + double S = R + C * P * C.transpose(); + Eigen::Vector2d K = P * C.transpose() * (1.0 / S); + x = x + K * y; + P = (I - K * C) * P; +} -public: - Eigen::Vector2d x; - double steering_angle = 0; - double car_speed = 0; - double prev_update_time = -1; - double controls_state_time = -1; - double sensor_data_time = -1; - - Localizer() { - A << 1, 0, 0, 1; - I << 1, 0, 0, 1; - - Q << pow(0.1, 2.0), 0, 0, pow(0.005 / 100.0, 2.0); - P << pow(1.0, 2.0), 0, 0, pow(0.05, 2.0); - - C_posenet << 1, 0; - C_gyro << 1, 1; - x << 0, 0; - - R_gyro = pow(0.05, 2.0); - } - - cereal::Event::Which handle_log(const unsigned char* msg_dat, size_t msg_size) { - const kj::ArrayPtr view((const capnp::word*)msg_dat, msg_size); - capnp::FlatArrayMessageReader msg(view); - cereal::Event::Reader event = msg.getRoot(); - double current_time = event.getLogMonoTime() / 1.0e9; - - if (prev_update_time < 0) { - prev_update_time = current_time; - } +void Localizer::handle_sensor_events(capnp::List::Reader sensor_events, double current_time) { + for (cereal::SensorEventData::Reader sensor_event : sensor_events){ + if (sensor_event.getType() == 4) { + sensor_data_time = current_time; - auto type = event.which(); - switch(type) { - case cereal::Event::CONTROLS_STATE: - handle_controls_state(event.getControlsState(), current_time); - break; - case cereal::Event::CAMERA_ODOMETRY: - handle_camera_odometry(event.getCameraOdometry(), current_time); - break; - case cereal::Event::SENSOR_EVENTS: - handle_sensor_events(event.getSensorEvents(), current_time); - break; - default: - break; + double meas = -sensor_event.getGyro().getV()[0]; + update_state(C_gyro, R_gyro, current_time, meas); } - - return type; } -}; - - - -int main(int argc, char *argv[]) { - auto ctx = zmq_ctx_new(); - auto controls_state_sock = sub_sock(ctx, "tcp://127.0.0.1:8007"); - auto sensor_events_sock = sub_sock(ctx, "tcp://127.0.0.1:8003"); - auto camera_odometry_sock = sub_sock(ctx, "tcp://127.0.0.1:8066"); - - auto live_parameters_sock = zsock_new_pub("@tcp://*:8064"); - assert(live_parameters_sock); - auto live_parameters_sock_raw = zsock_resolve(live_parameters_sock); - - int err; - Localizer localizer; - - zmq_pollitem_t polls[num_polls] = {{0}}; - polls[0].socket = controls_state_sock; - polls[0].events = ZMQ_POLLIN; - polls[1].socket = sensor_events_sock; - polls[1].events = ZMQ_POLLIN; - polls[2].socket = camera_odometry_sock; - polls[2].events = ZMQ_POLLIN; - - // Read car params - char *value; - size_t value_sz = 0; +} - LOGW("waiting for params to set vehicle model"); - while (true) { - read_db_value(NULL, "CarParams", &value, &value_sz); - if (value_sz > 0) break; - usleep(100*1000); - } - LOGW("got %d bytes CarParams", value_sz); +void Localizer::handle_camera_odometry(cereal::CameraOdometry::Reader camera_odometry, double current_time) { + double R = 250.0 * pow(camera_odometry.getRotStd()[2], 2); + double meas = camera_odometry.getRot()[2]; + update_state(C_posenet, R, current_time, meas); +} - // make copy due to alignment issues - auto amsg = kj::heapArray((value_sz / sizeof(capnp::word)) + 1); - memcpy(amsg.begin(), value, value_sz); - free(value); +void Localizer::handle_controls_state(cereal::ControlsState::Reader controls_state, double current_time) { + steering_angle = controls_state.getAngleSteers() * DEGREES_TO_RADIANS; + car_speed = controls_state.getVEgo(); + controls_state_time = current_time; +} - capnp::FlatArrayMessageReader cmsg(amsg); - cereal::CarParams::Reader car_params = cmsg.getRoot(); - // Read params from previous run - const int result = read_db_value(NULL, "LiveParameters", &value, &value_sz); +Localizer::Localizer() { + A << 1, 0, 0, 1; + I << 1, 0, 0, 1; - std::string fingerprint = car_params.getCarFingerprint(); - std::string vin = car_params.getCarVin(); - double sR = car_params.getSteerRatio(); - double x = 1.0; - double ao = 0.0; + Q << pow(0.1, 2.0), 0, 0, pow(0.005 / 100.0, 2.0); + P << pow(1.0, 2.0), 0, 0, pow(0.05, 2.0); - if (result == 0){ - auto str = std::string(value, value_sz); - free(value); + C_posenet << 1, 0; + C_gyro << 1, 1; + x << 0, 0; - std::string err; - auto json = json11::Json::parse(str, err); - if (json.is_null() || !err.empty()) { - std::string log = "Error parsing json: " + err; - LOGW(log.c_str()); - } else { - std::string new_fingerprint = json["carFingerprint"].string_value(); - std::string new_vin = json["carVin"].string_value(); + R_gyro = pow(0.05, 2.0); +} - if (fingerprint == new_fingerprint && vin == new_vin) { - std::string log = "Parameter starting with: " + str; - LOGW(log.c_str()); +cereal::Event::Which Localizer::handle_log(const unsigned char* msg_dat, size_t msg_size) { + const kj::ArrayPtr view((const capnp::word*)msg_dat, msg_size); + capnp::FlatArrayMessageReader msg(view); + cereal::Event::Reader event = msg.getRoot(); + double current_time = event.getLogMonoTime() / 1.0e9; - sR = json["steerRatio"].number_value(); - x = json["stiffnessFactor"].number_value(); - ao = json["angleOffsetAverage"].number_value(); - } - } + if (prev_update_time < 0) { + prev_update_time = current_time; } - ParamsLearner learner(car_params, ao, x, sR, 1.0); - - // Main loop - int save_counter = 0; - while (true){ - int ret = zmq_poll(polls, num_polls, 100); - - if (ret == 0){ - continue; - } else if (ret < 0){ - break; - } - - for (int i=0; i < num_polls; i++) { - if (polls[i].revents) { - zmq_msg_t msg; - err = zmq_msg_init(&msg); - assert(err == 0); - err = zmq_msg_recv(&msg, polls[i].socket, 0); - assert(err >= 0); - // make copy due to alignment issues, will be freed on out of scope - auto amsg = kj::heapArray((zmq_msg_size(&msg) / sizeof(capnp::word)) + 1); - memcpy(amsg.begin(), zmq_msg_data(&msg), zmq_msg_size(&msg)); - - auto which = localizer.handle_log((const unsigned char*)amsg.begin(), amsg.size()); - zmq_msg_close(&msg); - - if (which == cereal::Event::CONTROLS_STATE){ - save_counter++; - - double yaw_rate = -localizer.x[0]; - bool valid = learner.update(yaw_rate, localizer.car_speed, localizer.steering_angle); - - // TODO: Fix in replay - double sensor_data_age = localizer.controls_state_time - localizer.sensor_data_time; - - double angle_offset_degrees = RADIANS_TO_DEGREES * learner.ao; - double angle_offset_average_degrees = RADIANS_TO_DEGREES * learner.slow_ao; - - // Send parameters at 10 Hz - if (save_counter % 10 == 0){ - capnp::MallocMessageBuilder msg; - cereal::Event::Builder event = msg.initRoot(); - event.setLogMonoTime(nanos_since_boot()); - auto live_params = event.initLiveParameters(); - live_params.setValid(valid); - live_params.setYawRate(localizer.x[0]); - live_params.setGyroBias(localizer.x[1]); - live_params.setSensorValid(sensor_data_age < 5.0); - live_params.setAngleOffset(angle_offset_degrees); - live_params.setAngleOffsetAverage(angle_offset_average_degrees); - live_params.setStiffnessFactor(learner.x); - live_params.setSteerRatio(learner.sR); - - auto words = capnp::messageToFlatArray(msg); - auto bytes = words.asBytes(); - zmq_send(live_parameters_sock_raw, bytes.begin(), bytes.size(), ZMQ_DONTWAIT); - } - - - // Save parameters every minute - if (save_counter % 6000 == 0) { - json11::Json json = json11::Json::object { - {"carVin", vin}, - {"carFingerprint", fingerprint}, - {"steerRatio", learner.sR}, - {"stiffnessFactor", learner.x}, - {"angleOffsetAverage", angle_offset_average_degrees}, - }; - - std::string out = json.dump(); - write_db_value(NULL, "LiveParameters", out.c_str(), out.length()); - } - } - } - } + auto type = event.which(); + switch(type) { + case cereal::Event::CONTROLS_STATE: + handle_controls_state(event.getControlsState(), current_time); + break; + case cereal::Event::CAMERA_ODOMETRY: + handle_camera_odometry(event.getCameraOdometry(), current_time); + break; + case cereal::Event::SENSOR_EVENTS: + handle_sensor_events(event.getSensorEvents(), current_time); + break; + default: + break; } - zmq_close(controls_state_sock); - zmq_close(sensor_events_sock); - zmq_close(camera_odometry_sock); - zmq_close(live_parameters_sock_raw); - return 0; + return type; } diff --git a/selfdrive/locationd/locationd_yawrate.h b/selfdrive/locationd/locationd_yawrate.h new file mode 100644 index 00000000000000..323e87de4eab10 --- /dev/null +++ b/selfdrive/locationd/locationd_yawrate.h @@ -0,0 +1,34 @@ +#pragma once + +#include +#include "cereal/gen/cpp/log.capnp.h" + +#define DEGREES_TO_RADIANS 0.017453292519943295 + +class Localizer +{ + Eigen::Matrix2d A; + Eigen::Matrix2d I; + Eigen::Matrix2d Q; + Eigen::Matrix2d P; + Eigen::Matrix C_posenet; + Eigen::Matrix C_gyro; + + double R_gyro; + + void update_state(const Eigen::Matrix &C, const double R, double current_time, double meas); + void handle_sensor_events(capnp::List::Reader sensor_events, double current_time); + void handle_camera_odometry(cereal::CameraOdometry::Reader camera_odometry, double current_time); + void handle_controls_state(cereal::ControlsState::Reader controls_state, double current_time); + +public: + Eigen::Vector2d x; + double steering_angle = 0; + double car_speed = 0; + double prev_update_time = -1; + double controls_state_time = -1; + double sensor_data_time = -1; + + Localizer(); + cereal::Event::Which handle_log(const unsigned char* msg_dat, size_t msg_size); +}; diff --git a/selfdrive/locationd/params_learner.cc b/selfdrive/locationd/params_learner.cc index a150805d863f03..912abde35704b0 100644 --- a/selfdrive/locationd/params_learner.cc +++ b/selfdrive/locationd/params_learner.cc @@ -2,6 +2,8 @@ #include #include +#include +#include #include "cereal/gen/cpp/log.capnp.h" #include "cereal/gen/cpp/car.capnp.h" #include "params_learner.h" @@ -14,14 +16,15 @@ T clip(const T& n, const T& lower, const T& upper) { } ParamsLearner::ParamsLearner(cereal::CarParams::Reader car_params, - double angle_offset, - double stiffness_factor, - double steer_ratio, - double learning_rate) : - ao(angle_offset * DEGREES_TO_RADIANS), - slow_ao(angle_offset * DEGREES_TO_RADIANS), - x(stiffness_factor), - sR(steer_ratio) { + double angle_offset, + double stiffness_factor, + double steer_ratio, + double learning_rate) : + ao(angle_offset * DEGREES_TO_RADIANS), + slow_ao(angle_offset * DEGREES_TO_RADIANS), + x(stiffness_factor), + sR(steer_ratio) { + cF0 = car_params.getTireStiffnessFront(); cR0 = car_params.getTireStiffnessRear(); @@ -73,3 +76,43 @@ bool ParamsLearner::update(double psi, double u, double sa) { valid = valid && sR < max_sr_th; return valid; } + + +extern "C" { + void *params_learner_init(size_t len, char * params, double angle_offset, double stiffness_factor, double steer_ratio, double learning_rate) { + + auto amsg = kj::heapArray((len / sizeof(capnp::word)) + 1); + memcpy(amsg.begin(), params, len); + + capnp::FlatArrayMessageReader cmsg(amsg); + cereal::CarParams::Reader car_params = cmsg.getRoot(); + + ParamsLearner * p = new ParamsLearner(car_params, angle_offset, stiffness_factor, steer_ratio, learning_rate); + return (void*)p; + } + + bool params_learner_update(void * params_learner, double psi, double u, double sa) { + ParamsLearner * p = (ParamsLearner*) params_learner; + return p->update(psi, u, sa); + } + + double params_learner_get_ao(void * params_learner){ + ParamsLearner * p = (ParamsLearner*) params_learner; + return p->ao; + } + + double params_learner_get_x(void * params_learner){ + ParamsLearner * p = (ParamsLearner*) params_learner; + return p->x; + } + + double params_learner_get_slow_ao(void * params_learner){ + ParamsLearner * p = (ParamsLearner*) params_learner; + return p->slow_ao; + } + + double params_learner_get_sR(void * params_learner){ + ParamsLearner * p = (ParamsLearner*) params_learner; + return p->sR; + } +} diff --git a/selfdrive/locationd/paramsd.cc b/selfdrive/locationd/paramsd.cc new file mode 100644 index 00000000000000..9fc3ad625d920f --- /dev/null +++ b/selfdrive/locationd/paramsd.cc @@ -0,0 +1,174 @@ +#include +#include +#include + +#include "locationd_yawrate.h" +#include "cereal/gen/cpp/log.capnp.h" + +#include "common/swaglog.h" +#include "common/messaging.h" +#include "common/params.h" +#include "common/timing.h" +#include "params_learner.h" +#include "json11.hpp" + +const int num_polls = 3; + +int main(int argc, char *argv[]) { + auto ctx = zmq_ctx_new(); + auto controls_state_sock = sub_sock(ctx, "tcp://127.0.0.1:8007"); + auto sensor_events_sock = sub_sock(ctx, "tcp://127.0.0.1:8003"); + auto camera_odometry_sock = sub_sock(ctx, "tcp://127.0.0.1:8066"); + + auto live_parameters_sock = zsock_new_pub("@tcp://*:8064"); + assert(live_parameters_sock); + auto live_parameters_sock_raw = zsock_resolve(live_parameters_sock); + + int err; + Localizer localizer; + + zmq_pollitem_t polls[num_polls] = {{0}}; + polls[0].socket = controls_state_sock; + polls[0].events = ZMQ_POLLIN; + polls[1].socket = sensor_events_sock; + polls[1].events = ZMQ_POLLIN; + polls[2].socket = camera_odometry_sock; + polls[2].events = ZMQ_POLLIN; + + // Read car params + char *value; + size_t value_sz = 0; + + LOGW("waiting for params to set vehicle model"); + while (true) { + read_db_value(NULL, "CarParams", &value, &value_sz); + if (value_sz > 0) break; + usleep(100*1000); + } + LOGW("got %d bytes CarParams", value_sz); + + // make copy due to alignment issues + auto amsg = kj::heapArray((value_sz / sizeof(capnp::word)) + 1); + memcpy(amsg.begin(), value, value_sz); + free(value); + + capnp::FlatArrayMessageReader cmsg(amsg); + cereal::CarParams::Reader car_params = cmsg.getRoot(); + + // Read params from previous run + const int result = read_db_value(NULL, "LiveParameters", &value, &value_sz); + + std::string fingerprint = car_params.getCarFingerprint(); + std::string vin = car_params.getCarVin(); + double sR = car_params.getSteerRatio(); + double x = 1.0; + double ao = 0.0; + + if (result == 0){ + auto str = std::string(value, value_sz); + free(value); + + std::string err; + auto json = json11::Json::parse(str, err); + if (json.is_null() || !err.empty()) { + std::string log = "Error parsing json: " + err; + LOGW(log.c_str()); + } else { + std::string new_fingerprint = json["carFingerprint"].string_value(); + std::string new_vin = json["carVin"].string_value(); + + if (fingerprint == new_fingerprint && vin == new_vin) { + std::string log = "Parameter starting with: " + str; + LOGW(log.c_str()); + + sR = json["steerRatio"].number_value(); + x = json["stiffnessFactor"].number_value(); + ao = json["angleOffsetAverage"].number_value(); + } + } + } + + ParamsLearner learner(car_params, ao, x, sR, 1.0); + + // Main loop + int save_counter = 0; + while (true){ + int ret = zmq_poll(polls, num_polls, 100); + + if (ret == 0){ + continue; + } else if (ret < 0){ + break; + } + + for (int i=0; i < num_polls; i++) { + if (polls[i].revents) { + zmq_msg_t msg; + err = zmq_msg_init(&msg); + assert(err == 0); + err = zmq_msg_recv(&msg, polls[i].socket, 0); + assert(err >= 0); + // make copy due to alignment issues, will be freed on out of scope + auto amsg = kj::heapArray((zmq_msg_size(&msg) / sizeof(capnp::word)) + 1); + memcpy(amsg.begin(), zmq_msg_data(&msg), zmq_msg_size(&msg)); + + auto which = localizer.handle_log((const unsigned char*)amsg.begin(), amsg.size()); + zmq_msg_close(&msg); + + if (which == cereal::Event::CONTROLS_STATE){ + save_counter++; + + double yaw_rate = -localizer.x[0]; + bool valid = learner.update(yaw_rate, localizer.car_speed, localizer.steering_angle); + + // TODO: Fix in replay + double sensor_data_age = localizer.controls_state_time - localizer.sensor_data_time; + + double angle_offset_degrees = RADIANS_TO_DEGREES * learner.ao; + double angle_offset_average_degrees = RADIANS_TO_DEGREES * learner.slow_ao; + + // Send parameters at 10 Hz + if (save_counter % 10 == 0){ + capnp::MallocMessageBuilder msg; + cereal::Event::Builder event = msg.initRoot(); + event.setLogMonoTime(nanos_since_boot()); + auto live_params = event.initLiveParameters(); + live_params.setValid(valid); + live_params.setYawRate(localizer.x[0]); + live_params.setGyroBias(localizer.x[1]); + live_params.setSensorValid(sensor_data_age < 5.0); + live_params.setAngleOffset(angle_offset_degrees); + live_params.setAngleOffsetAverage(angle_offset_average_degrees); + live_params.setStiffnessFactor(learner.x); + live_params.setSteerRatio(learner.sR); + + auto words = capnp::messageToFlatArray(msg); + auto bytes = words.asBytes(); + zmq_send(live_parameters_sock_raw, bytes.begin(), bytes.size(), ZMQ_DONTWAIT); + } + + + // Save parameters every minute + if (save_counter % 6000 == 0) { + json11::Json json = json11::Json::object { + {"carVin", vin}, + {"carFingerprint", fingerprint}, + {"steerRatio", learner.sR}, + {"stiffnessFactor", learner.x}, + {"angleOffsetAverage", angle_offset_average_degrees}, + }; + + std::string out = json.dump(); + write_db_value(NULL, "LiveParameters", out.c_str(), out.length()); + } + } + } + } + } + + zmq_close(controls_state_sock); + zmq_close(sensor_events_sock); + zmq_close(camera_odometry_sock); + zmq_close(live_parameters_sock_raw); + return 0; +} diff --git a/selfdrive/locationd/test/test_params_learner.py b/selfdrive/locationd/test/test_params_learner.py new file mode 100755 index 00000000000000..e2a6913c0cd31c --- /dev/null +++ b/selfdrive/locationd/test/test_params_learner.py @@ -0,0 +1,53 @@ +#!/usr/bin/env python + +import numpy as np +import unittest + +from selfdrive.car.honda.interface import CarInterface +from selfdrive.car.honda.values import CAR +from selfdrive.controls.lib.vehicle_model import VehicleModel +from selfdrive.locationd.liblocationd_py import liblocationd # pylint: disable=no-name-in-module, import-error + + +class TestParamsLearner(unittest.TestCase): + def setUp(self): + + self.CP = CarInterface.get_params(CAR.CIVIC, {}) + bts = self.CP.to_bytes() + + self.params_learner = liblocationd.params_learner_init(len(bts), bts, 0.0, 1.0, self.CP.steerRatio, 1.0) + + def test_convergence(self): + # Setup vehicle model with wrong parameters + VM_sim = VehicleModel(self.CP) + x_target = 0.75 + sr_target = 14 + ao_target = -1.0 + VM_sim.update_params(x_target, sr_target) + + # Run simulation + times = np.arange(0, 10*3600, 0.01) + angle_offset = np.radians(ao_target) + steering_angles = np.radians(10 * np.sin(2 * np.pi * times / 100.)) + angle_offset + speeds = 10 * np.sin(2 * np.pi * times / 1000.) + 25 + + for i, t in enumerate(times): + u = speeds[i] + sa = steering_angles[i] + psi = VM_sim.yaw_rate(sa - angle_offset, u) + liblocationd.params_learner_update(self.params_learner, psi, u, sa) + + # Verify learned parameters + sr = liblocationd.params_learner_get_sR(self.params_learner) + ao_slow = np.degrees(liblocationd.params_learner_get_slow_ao(self.params_learner)) + x = liblocationd.params_learner_get_x(self.params_learner) + self.assertAlmostEqual(x_target, x, places=1) + self.assertAlmostEqual(ao_target, ao_slow, places=1) + self.assertAlmostEqual(sr_target, sr, places=1) + + + + + +if __name__ == "__main__": + unittest.main() diff --git a/selfdrive/loggerd/uploader.py b/selfdrive/loggerd/uploader.py index 7cad71bf7da010..0bd2ff18f1d54c 100644 --- a/selfdrive/loggerd/uploader.py +++ b/selfdrive/loggerd/uploader.py @@ -168,7 +168,7 @@ def next_file_to_upload(self, with_raw): def do_upload(self, key, fn): try: - url_resp = api_get("v1.2/"+self.dongle_id+"/upload_url/", timeout=2, path=key, access_token=self.access_token) + url_resp = api_get("v1.2/"+self.dongle_id+"/upload_url/", timeout=10, path=key, access_token=self.access_token) url_resp_json = json.loads(url_resp.text) url = url_resp_json['url'] headers = url_resp_json['headers'] @@ -223,7 +223,7 @@ def upload(self, key, fn): try: os.unlink(fn) except OSError: - cloudlog.exception("delete_failed", stat=stat, exc=self.last_exc, key=key, fn=fn, sz=sz) + cloudlog.event("delete_failed", stat=stat, exc=self.last_exc, key=key, fn=fn, sz=sz) success = True else: diff --git a/selfdrive/manager.py b/selfdrive/manager.py index c3be2c094d09ed..4e52f2beb4bd4c 100755 --- a/selfdrive/manager.py +++ b/selfdrive/manager.py @@ -72,12 +72,15 @@ def unblock_stdout(): import shutil import hashlib import importlib +import re +import stat import subprocess import traceback from multiprocessing import Process from setproctitle import setproctitle #pylint: disable=no-name-in-module +from common.file_helpers import atomic_write_in_dir_neos from common.params import Params import cereal ThermalStatus = cereal.log.ThermalData.ThermalStatus @@ -109,12 +112,14 @@ def unblock_stdout(): "pandad": "selfdrive.pandad", "ui": ("selfdrive/ui", ["./start.py"]), "calibrationd": "selfdrive.locationd.calibrationd", - "params_learner": ("selfdrive/locationd", ["./params_learner"]), + "paramsd": ("selfdrive/locationd", ["./paramsd"]), "visiond": ("selfdrive/visiond", ["./visiond"]), "sensord": ("selfdrive/sensord", ["./start_sensord.py"]), "gpsd": ("selfdrive/sensord", ["./start_gpsd.py"]), "updated": "selfdrive.updated", - "athena": "selfdrive.athena.athenad", +} +daemon_processes = { + "athenad": "selfdrive.athena.athenad", } android_packages = ("ai.comma.plus.offroad", "ai.comma.plus.frame") @@ -136,7 +141,6 @@ def get_running(): 'uploader', 'ui', 'updated', - 'athena', ] car_started_processes = [ @@ -146,7 +150,7 @@ def get_running(): 'sensord', 'radard', 'calibrationd', - 'params_learner', + 'paramsd', 'visiond', 'proclogd', 'ubloxd', @@ -209,6 +213,29 @@ def start_managed_process(name): running[name] = Process(name=name, target=nativelauncher, args=(pargs, cwd)) running[name].start() +def start_daemon_process(name, params): + proc = daemon_processes[name] + pid_param = name.capitalize() + 'Pid' + pid = params.get(pid_param) + + if pid is not None: + try: + os.kill(int(pid), 0) + # process is running (kill is a poorly-named system call) + return + except OSError: + # process is dead + pass + + cloudlog.info("starting daemon %s" % name) + proc = subprocess.Popen(['python', '-m', proc], + cwd='/', + stdout=open('/dev/null', 'w'), + stderr=open('/dev/null', 'w'), + preexec_fn=os.setpgrp) + + params.put(pid_param, str(proc.pid)) + def prepare_managed_process(p): proc = managed_processes[p] if isinstance(proc, str): @@ -321,6 +348,12 @@ def manager_thread(): # save boot log subprocess.call(["./loggerd", "--bootlog"], cwd=os.path.join(BASEDIR, "selfdrive/loggerd")) + params = Params() + + # start daemon processes + for p in daemon_processes: + start_daemon_process(p, params) + # start persistent processes for p in persistent_processes: start_managed_process(p) @@ -332,7 +365,6 @@ def manager_thread(): if os.getenv("NOBOARD") is None: start_managed_process("pandad") - params = Params() logger_dead = False while 1: @@ -420,10 +452,46 @@ def update_apks(): assert success +def update_ssh(): + ssh_home_dirpath = "/system/comma/home/.ssh/" + auth_keys_path = os.path.join(ssh_home_dirpath, "authorized_keys") + auth_keys_persist_path = os.path.join(ssh_home_dirpath, "authorized_keys.persist") + auth_keys_mode = stat.S_IREAD | stat.S_IWRITE + + params = Params() + github_keys = params.get("GithubSshKeys") or '' + + old_keys = open(auth_keys_path).read() + has_persisted_keys = os.path.exists(auth_keys_persist_path) + if has_persisted_keys: + persisted_keys = open(auth_keys_persist_path).read() + else: + # add host filter + persisted_keys = re.sub(r'^(?!.+?from.+? )(ssh|ecdsa)', 'from="10.0.0.0/8,172.16.0.0/12,192.168.0.0/16" \\1', old_keys, flags=re.MULTILINE) + + new_keys = persisted_keys + '\n' + github_keys + + if has_persisted_keys and new_keys == old_keys and os.stat(auth_keys_path)[stat.ST_MODE] == auth_keys_mode: + # nothing to do - let's avoid remount + return + + try: + subprocess.check_call(["mount", "-o", "rw,remount", "/system"]) + if not has_persisted_keys: + atomic_write_in_dir_neos(auth_keys_persist_path, persisted_keys, mode=auth_keys_mode) + + atomic_write_in_dir_neos(auth_keys_path, new_keys, mode=auth_keys_mode) + finally: + try: + subprocess.check_call(["mount", "-o", "ro,remount", "/system"]) + except: + cloudlog.exception("Failed to remount as read-only") + # this can fail due to "Device busy" - reboot if so + os.system("reboot") + raise RuntimeError + def manager_update(): - if os.path.exists(os.path.join(BASEDIR, "vpn")): - cloudlog.info("installing vpn") - os.system(os.path.join(BASEDIR, "vpn", "install.sh")) + update_ssh() update_apks() def manager_prepare(): diff --git a/selfdrive/messaging.py b/selfdrive/messaging.py index 4e78f66c817bad..cad7e2e8bf79db 100644 --- a/selfdrive/messaging.py +++ b/selfdrive/messaging.py @@ -16,17 +16,34 @@ def pub_sock(port, addr="*"): sock.bind("tcp://%s:%d" % (addr, port)) return sock -def sub_sock(port, poller=None, addr="127.0.0.1", conflate=False): +def sub_sock(port, poller=None, addr="127.0.0.1", conflate=False, timeout=None): context = zmq.Context.instance() sock = context.socket(zmq.SUB) if conflate: sock.setsockopt(zmq.CONFLATE, 1) sock.connect("tcp://%s:%d" % (addr, port)) sock.setsockopt(zmq.SUBSCRIBE, b"") + + if timeout is not None: + sock.RCVTIMEO = timeout + if poller is not None: poller.register(sock, zmq.POLLIN) return sock +def drain_sock_raw(sock, wait_for_one=False): + ret = [] + while 1: + try: + if wait_for_one and len(ret) == 0: + dat = sock.recv() + else: + dat = sock.recv(zmq.NOBLOCK) + ret.append(dat) + except zmq.error.Again: + break + return ret + def drain_sock(sock, wait_for_one=False): ret = [] while 1: @@ -82,24 +99,29 @@ def __init__(self, services, addr="127.0.0.1"): self.valid = {} for s in services: # TODO: get address automatically from service_list - self.sock[s] = sub_sock(service_list[s].port, poller=self.poller, addr=addr, conflate=True) + if addr is not None: + self.sock[s] = sub_sock(service_list[s].port, poller=self.poller, addr=addr, conflate=True) self.freq[s] = service_list[s].frequency data = new_message() data.init(s) self.data[s] = getattr(data, s) - self.logMonoTime[s] = data.logMonoTime + self.logMonoTime[s] = 0 self.valid[s] = data.valid def __getitem__(self, s): return self.data[s] def update(self, timeout=-1): + msgs = [] + for sock, _ in self.poller.poll(timeout): + msgs.append(recv_one(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) - cur_time = sec_since_boot() - for sock, _ in self.poller.poll(timeout): - msg = recv_one(sock) + for msg in msgs: s = msg.which() self.updated[s] = True self.rcv_time[s] = cur_time diff --git a/selfdrive/registration.py b/selfdrive/registration.py index 9f689984999c06..f8a084bd3605e7 100644 --- a/selfdrive/registration.py +++ b/selfdrive/registration.py @@ -5,7 +5,7 @@ from datetime import datetime, timedelta from selfdrive.swaglog import cloudlog -from selfdrive.version import version, training_version, get_git_commit, get_git_branch, get_git_remote +from selfdrive.version import version, terms_version, training_version, get_git_commit, get_git_branch, get_git_remote from common.api import api_get from common.params import Params from common.file_helpers import mkdirs_exists_ok @@ -53,6 +53,7 @@ def get_subscriber_info(): def register(): params = Params() params.put("Version", version) + params.put("TermsVersion", terms_version) params.put("TrainingVersion", training_version) params.put("GitCommit", get_git_commit()) params.put("GitBranch", get_git_branch()) diff --git a/selfdrive/test/plant/plant.py b/selfdrive/test/plant/plant.py index e39bafd7921961..930f83be8720d9 100755 --- a/selfdrive/test/plant/plant.py +++ b/selfdrive/test/plant/plant.py @@ -244,6 +244,7 @@ def step(self, v_lead=0.0, cruise_buttons=None, grade=0.0, publish_model = True) 'EPB_STATE', 'BRAKE_HOLD_ACTIVE', 'INTERCEPTOR_GAS', + 'INTERCEPTOR_GAS2', 'IMPERIAL_UNIT', ]) vls = vls_tuple( @@ -276,6 +277,7 @@ def step(self, v_lead=0.0, cruise_buttons=None, grade=0.0, publish_model = True) 0, # EPB State 0, # Brake hold 0, # Interceptor feedback + 0, # Interceptor 2 feedback False ) diff --git a/selfdrive/thermald.py b/selfdrive/thermald.py index ef75cfffd982f1..9a29b96fec2800 100755 --- a/selfdrive/thermald.py +++ b/selfdrive/thermald.py @@ -2,7 +2,7 @@ import os from smbus2 import SMBus from cereal import log -from selfdrive.version import training_version +from selfdrive.version import terms_version, training_version from selfdrive.swaglog import cloudlog import selfdrive.messaging as messaging from selfdrive.services import service_list @@ -216,7 +216,7 @@ def thermald_thread(): ignition = True do_uninstall = params.get("DoUninstall") == "1" - accepted_terms = params.get("HasAcceptedTerms") == "1" + accepted_terms = params.get("HasAcceptedTerms") == terms_version completed_training = params.get("CompletedTrainingVersion") == training_version should_start = ignition diff --git a/selfdrive/version.py b/selfdrive/version.py index 4acc183502f9f4..2eb39dc9769826 100644 --- a/selfdrive/version.py +++ b/selfdrive/version.py @@ -59,6 +59,7 @@ def get_git_remote(): dirty = True training_version = "0.1.0" +terms_version = "2" if __name__ == "__main__": print("Dirty: %s" % dirty) diff --git a/selfdrive/visiond/models/driving.cc b/selfdrive/visiond/models/driving.cc index bf5933f4288ae7..28876c81b1acbe 100644 --- a/selfdrive/visiond/models/driving.cc +++ b/selfdrive/visiond/models/driving.cc @@ -135,7 +135,14 @@ void poly_fit(float *in_pts, float *in_stds, float *out) { Eigen::Matrix lhs = vander.array().colwise() / std.array(); Eigen::Matrix rhs = pts.array() / std.array(); + // Improve numerical stability + Eigen::Matrix scale = 1. / (lhs.array()*lhs.array()).sqrt().colwise().sum(); + lhs = lhs * scale.asDiagonal(); + // Solve inplace Eigen::ColPivHouseholderQR > qr(lhs); p = qr.solve(rhs); + + // Apply scale to output + p = p.transpose() * scale.asDiagonal(); }