mirror of
https://github.com/firestar5683/StarPilot.git
synced 2026-11-01 19:43:51 +08:00
Compare commits
664 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 15ee981d5b | |||
| d87428f98b | |||
| 141c3466f1 | |||
| 14adf3afa2 | |||
| 9779e34f75 | |||
| a965de3c96 | |||
| bc7d21687a | |||
| ce9e739428 | |||
| 1e0ef417c3 | |||
| bf338bf5e9 | |||
| 93e8dbb8b5 | |||
| 37192c1aba | |||
| 58bdb272a7 | |||
| baffaeee93 | |||
| 444aace15f | |||
| 5face13bc5 | |||
| b201dc1996 | |||
| ce6564c684 | |||
| 145b222f0d | |||
| 0ef3ce173a | |||
| c1112e4400 | |||
| bbc546fcf3 | |||
| bf662bb1fd | |||
| 8b274fc974 | |||
| 99b1d6a40b | |||
| 6418d4a09a | |||
| 747ff45da4 | |||
| 55c44bf34e | |||
| aed724beda | |||
| 22a4193109 | |||
| 29d9e1ce69 | |||
| 0b384ea101 | |||
| 8369e11417 | |||
| 26bccbdcc8 | |||
| db336329c5 | |||
| 7a7f343978 | |||
| 49d82d6ac1 | |||
| 0aa4867be4 | |||
| f370bf5ba6 | |||
| d6074e554d | |||
| b205dd6954 | |||
| 9e7fb4680d | |||
| 40982f18ab | |||
| 4d47e1243d | |||
| 9ff1fb48de | |||
| 51aa86da12 | |||
| 6e00ef601d | |||
| db7ea2a599 | |||
| 682dc5ddad | |||
| 3b62a21fa7 | |||
| ffe062c774 | |||
| 4fd2b0e730 | |||
| 2d659de09c | |||
| 35bd9d9fb3 | |||
| f878928731 | |||
| 771a8b7a55 | |||
| fcd5a66252 | |||
| 23bbf52578 | |||
| abfe469d95 | |||
| a39e33b9d0 | |||
| c9c66ce21a | |||
| ddee718536 | |||
| e0ab166ed3 | |||
| 7e908db510 | |||
| eb0dff8438 | |||
| d1aff96611 | |||
| 5bfae8d806 | |||
| d692982601 | |||
| cc6358d983 | |||
| a5c3340c8d | |||
| fa98a83b06 | |||
| d1ad7f3fe1 | |||
| 72427d8cc4 | |||
| 9397e18742 | |||
| 0e793367de | |||
| a3690e4034 | |||
| 29f108de10 | |||
| a091133e80 | |||
| 42e0d13389 | |||
| 98c8888cf7 | |||
| c534346d70 | |||
| 4bf377cfef | |||
| 332cb82886 | |||
| ab3557e107 | |||
| b3e67e28e2 | |||
| 6b8726ef75 | |||
| 2924e495ce | |||
| 1d7d11f5dd | |||
| a47feb0817 | |||
| dc61915529 | |||
| d25c617595 | |||
| b5807aab22 | |||
| 7827a7cdba | |||
| 21f4245444 | |||
| 69df34bf0f | |||
| 305037fc1a | |||
| 4ea182a1b1 | |||
| 7a69c82d65 | |||
| 60d3364233 | |||
| 7119634b5b | |||
| 1e1de64a1e | |||
| 67ab1c5b2e | |||
| 460a8e3189 | |||
| 354d14515f | |||
| 5c359f93ac | |||
| f60fe79999 | |||
| 21572ce67b | |||
| 8f62890de4 | |||
| adf25bcb5e | |||
| 7c25bb6950 | |||
| 8da8b6135c | |||
| 77e450db57 | |||
| 25d18afad5 | |||
| b0b7a768f0 | |||
| af301ad7b5 | |||
| ab415aa5fa | |||
| 9504037aa7 | |||
| 0440535f64 | |||
| a2ae18d1db | |||
| 8f78b61a47 | |||
| 69fb8b312f | |||
| e0d7a55eab | |||
| a4b4513149 | |||
| c025b96e8a | |||
| 683b6151ce | |||
| 261fb00e11 | |||
| df875819f5 | |||
| bc7b9b38ae | |||
| e3b21173a5 | |||
| 391eb0b74b | |||
| 7be62e781d | |||
| 02c4ade948 | |||
| 84ef9d50b7 | |||
| 812f93a5a5 | |||
| 1e1065874b | |||
| d4eb5a6eaf | |||
| 3f4695de53 | |||
| d20896ced5 | |||
| a649732568 | |||
| 3c6d8846ac | |||
| 29642139f0 | |||
| d7f0b402a8 | |||
| 67c4121f62 | |||
| b4f4b92ecf | |||
| 22023ebd58 | |||
| 97a289e6a9 | |||
| 047924cb35 | |||
| 38e7d54d5d | |||
| fe0ed57f45 | |||
| affe1c3d2c | |||
| 98d2907a35 | |||
| 2a77b02bd8 | |||
| d2a92fcbe3 | |||
| 7a2addf782 | |||
| 23dfde1ce7 | |||
| d953b6a7aa | |||
| 26dd7e27c8 | |||
| 70d17cd69b | |||
| 069e337bea | |||
| 6b62dd2308 | |||
| 4ff7da61e0 | |||
| 686d4bbdc2 | |||
| 08d86aa7d7 | |||
| cf80f7a28b | |||
| 4f4a90117b | |||
| 3a04a69cbe | |||
| 38faf7f8a4 | |||
| f2de9784f8 | |||
| f66527d0b1 | |||
| d17745fb5e | |||
| 1bc51d6b47 | |||
| 6edc36181a | |||
| a0b41e6cf9 | |||
| 47e87baa60 | |||
| 5cfc0be5cb | |||
| 41d0a8957a | |||
| 58f376002e | |||
| b0563a5968 | |||
| 61229779e4 | |||
| d2b3ed0ec7 | |||
| af4f9f1f31 | |||
| 876256a268 | |||
| 4dc51cfea6 | |||
| f274a8e3b0 | |||
| df46400d6d | |||
| da42760e55 | |||
| f6531a9d08 | |||
| c73464f67f | |||
| 4d5d709bea | |||
| d5f9caa82d | |||
| cb0ee932c4 | |||
| 6a61788682 | |||
| 9955b3c806 | |||
| ead59a5a61 | |||
| a1ec1c089e | |||
| a1f069c6d8 | |||
| 095ef5f9f6 | |||
| fd71fe698c | |||
| d5242c5b82 | |||
| 1f1893a170 | |||
| 5808958fb2 | |||
| e90c41c576 | |||
| f448d357e0 | |||
| aa1b61eb8e | |||
| 30bb73d527 | |||
| 98cd6147c3 | |||
| d22636b194 | |||
| 4808de10d6 | |||
| a440425ef8 | |||
| 61bbb52172 | |||
| cf5c4aeacb | |||
| 94053536b4 | |||
| b539ae3e2d | |||
| cd98235644 | |||
| 1ff59baea7 | |||
| c8b4633cd1 | |||
| 813d5b9982 | |||
| 53413fa019 | |||
| 63da1abe2c | |||
| be28530ee4 | |||
| 13bdfcdd95 | |||
| 9675794969 | |||
| 151a504507 | |||
| 03e764bcb3 | |||
| 16eb74250c | |||
| 4a48ef8dbc | |||
| 76ab558ca6 | |||
| 60a20537c5 | |||
| be020bdedb | |||
| 1e77f2482c | |||
| d8da18ed54 | |||
| 6abd80f116 | |||
| f4b258a082 | |||
| 8a9ed94f5f | |||
| ae44a57565 | |||
| 78e4e4ea23 | |||
| 1c6164e11c | |||
| b93f77ea0e | |||
| dba8e01e54 | |||
| 983120bfed | |||
| 27803e787b | |||
| 24e096d9f0 | |||
| 6651d50c81 | |||
| f64c4df8c8 | |||
| 1883bd6135 | |||
| 1ba6b0004d | |||
| 68b86c7ca8 | |||
| 36881b6410 | |||
| fe7e5cf5df | |||
| d8b1588937 | |||
| a2f4d6b5ad | |||
| 16d0c4a3e7 | |||
| dd34ccfe28 | |||
| 7ccae06bce | |||
| 59bd6b8837 | |||
| 53c4b90ffc | |||
| f91df07d3f | |||
| 0829b0a767 | |||
| 9a143c5ab2 | |||
| 1e8098c140 | |||
| b5a88f5700 | |||
| c0a3e48d94 | |||
| 65e1342e41 | |||
| 7ada2abca0 | |||
| 9278fad15c | |||
| 0aa41e348e | |||
| 64a6e9776c | |||
| 56b2945de4 | |||
| b686ca87d3 | |||
| 1b3b260b4d | |||
| 25d43fe15e | |||
| cbc73e55a2 | |||
| 9b31da0985 | |||
| 3f9059fea8 | |||
| 99c81d291f | |||
| 530b637d27 | |||
| 52e55eb244 | |||
| ec36bc3a39 | |||
| 98797fb24e | |||
| 127998ca8b | |||
| 1da59216b0 | |||
| 184ba93833 | |||
| a321836788 | |||
| f5044670fa | |||
| 340e0f4a4c | |||
| d8b1e99d77 | |||
| 2eebe49940 | |||
| 9dae0bfac4 | |||
| 2f92d577f9 | |||
| 684e7b9f9e | |||
| 790732bea3 | |||
| 3c25760cc9 | |||
| 7834995df4 | |||
| 0a7fed9437 | |||
| a201322160 | |||
| d64025cca4 | |||
| 58d645cd18 | |||
| 76dfb135ca | |||
| 27ef9f2236 | |||
| 185375ed64 | |||
| 7978afabe5 | |||
| e89fd1236f | |||
| bdf6585650 | |||
| 4662cfc8d7 | |||
| dd32343342 | |||
| 1efa3f0eba | |||
| ca2f30989d | |||
| 03f09105c6 | |||
| 5a9d89ed42 | |||
| f74a201edc | |||
| 342bb13bff | |||
| be5c2aef3a | |||
| 6aa1048d15 | |||
| ede869e34a | |||
| 2a0f066426 | |||
| 113d4c1b70 | |||
| 27e0adc434 | |||
| 4697568e67 | |||
| 21a24446db | |||
| 26cc816c1b | |||
| 6c1f516bb3 | |||
| 737f2ace69 | |||
| f0203614ee | |||
| 8b7dfb647f | |||
| 15fa6664c0 | |||
| 5c8aa7295d | |||
| 0207a97040 | |||
| 9a79df8a8a | |||
| fdb04d9f69 | |||
| 603f3f1a34 | |||
| 27f405bb6f | |||
| 26da755a1e | |||
| a25e2153a0 | |||
| 88246af4c7 | |||
| 03f13e614b | |||
| 23512ba932 | |||
| ee35b905d7 | |||
| 2c9d5afd75 | |||
| 860623f157 | |||
| 73c1df714d | |||
| b967da5fc1 | |||
| ea35a0043c | |||
| f364a3ee8f | |||
| d21c6591c1 | |||
| ece9cf9480 | |||
| d5f8643e7c | |||
| 9cf542d7f7 | |||
| 9eb1666a52 | |||
| b78ed77f1d | |||
| d84e64b315 | |||
| 0fded2c08b | |||
| 67e7f6dc3d | |||
| 4d2ff103d6 | |||
| 58e716ff48 | |||
| f41cb3b4ec | |||
| b5d8230c3d | |||
| 853bcda352 | |||
| 4610a6e4d4 | |||
| 067f1bfe9e | |||
| e3c934bcc1 | |||
| 210db686bb | |||
| 30ec405890 | |||
| 573a6915fc | |||
| 0002cc7c3a | |||
| dc331df1fc | |||
| 5c4ae7a2fc | |||
| 29bea62ac1 | |||
| 024870d16a | |||
| 68e56c8017 | |||
| 9d76070ab7 | |||
| 6ec0d23b7a | |||
| c9dd7fe0e6 | |||
| e32463ee96 | |||
| 53c6ca6589 | |||
| e4ed48928b | |||
| b94d598c2d | |||
| 860a48765d | |||
| efa97f23a5 | |||
| 92c596544d | |||
| 2f0b0fb15e | |||
| 3e78efacca | |||
| c10a755516 | |||
| 3b744f4fba | |||
| b4dd562c43 | |||
| 892e14aa92 | |||
| 8b7c726bea | |||
| db24b1e838 | |||
| 7db592d3b8 | |||
| b7c029c92c | |||
| fdbf1c5938 | |||
| 00429e6bbb | |||
| 2f8034b2ec | |||
| 5501541aa2 | |||
| 3d05cca678 | |||
| 195139999b | |||
| c9fa92b377 | |||
| dfa7757c40 | |||
| 8f3539a27b | |||
| d0715b6562 | |||
| e8ae37e36e | |||
| a2c76acf3b | |||
| a51a60b598 | |||
| 7ed5c6554d | |||
| 5641fc986d | |||
| c499aa549c | |||
| 0a99fe3baa | |||
| 8cc32df779 | |||
| 8291f35701 | |||
| 0f885c87a5 | |||
| a422246dc3 | |||
| 906b55a5bd | |||
| e5b2ec4f01 | |||
| 5c0935d253 | |||
| 97f1ee4a0d | |||
| 2d450bc3a8 | |||
| 96c923875b | |||
| 14b7eadf63 | |||
| 402faabd42 | |||
| de79a07745 | |||
| 4b2c137489 | |||
| b3654cbcc9 | |||
| d2b8c4f0cb | |||
| 50d9c446cf | |||
| 6eb1247426 | |||
| faea9a42c4 | |||
| 86813e6d37 | |||
| 012727ef60 | |||
| 9653f9d6a6 | |||
| 285c52eb69 | |||
| 75db5e854e | |||
| 8f6e36f426 | |||
| c4bba32347 | |||
| f0c5ca7227 | |||
| 8970cc8d70 | |||
| 54f3c2b373 | |||
| 2c4e1fd4fa | |||
| 401c4026ac | |||
| 22f0a89cf8 | |||
| b942ab58e1 | |||
| c29b311583 | |||
| ed72759a48 | |||
| 0129a8a4ff | |||
| 6839a11685 | |||
| db96b4b912 | |||
| c812915765 | |||
| b70d75d1d0 | |||
| 6a02547b42 | |||
| 33a75c3506 | |||
| 2cfdc67610 | |||
| 6f3d10a4c4 | |||
| fe9ce203ee | |||
| 589b6187a1 | |||
| 6b1efbf185 | |||
| 76494ab0be | |||
| b63d51d3ef | |||
| e07853bb5e | |||
| 866711ef2e | |||
| ef8e6fb39f | |||
| 4568504c41 | |||
| 18c6482545 | |||
| b0c83bb9a6 | |||
| ce57ce4898 | |||
| 7d9f8c55e0 | |||
| 04c26981f0 | |||
| 7f6ea03066 | |||
| de33bc4645 | |||
| 87fcd4537f | |||
| e9ad7793f0 | |||
| 4bdf3b95f3 | |||
| 7fca1ba2f4 | |||
| 504d00353d | |||
| ce67c75f1f | |||
| 95509a58cd | |||
| 548608490a | |||
| 21b3f5321a | |||
| 7953078a31 | |||
| b75d606c91 | |||
| ca3e1ce9b0 | |||
| 58744df1c5 | |||
| 1181a00fe9 | |||
| 71f547219c | |||
| 35b08e1e3d | |||
| fd2bead226 | |||
| ae5cb7a0da | |||
| e425c1cf59 | |||
| e3ab9d6460 | |||
| c210011c84 | |||
| d5b884f824 | |||
| 2751d87d01 | |||
| 8849aa02a3 | |||
| f49e9f4f09 | |||
| f0a6db351e | |||
| 53b177b3c1 | |||
| c6df34f55b | |||
| ea6c19638c | |||
| 28e3543ec4 | |||
| 752df1681a | |||
| 7865525451 | |||
| dcacbf606a | |||
| 4a5019d925 | |||
| 1b7b3b4e66 | |||
| 37285038d3 | |||
| c32fb85dbd | |||
| 91a731f2ae | |||
| bb2587664a | |||
| e41761eb1b | |||
| 9a9ff839a9 | |||
| f66eb5a0a3 | |||
| 4f7336f0e4 | |||
| e6e6ad2e1f | |||
| 94a27e351f | |||
| 28b8043c5b | |||
| 78df63a6af | |||
| a69dfe7b20 | |||
| d0c9cd28d1 | |||
| 9e1cabde36 | |||
| c7cd8b4459 | |||
| 3d628a6fe2 | |||
| 51fad4a6c6 | |||
| 1a8883fb63 | |||
| a70a821d28 | |||
| a8d110ad74 | |||
| 013351a2ee | |||
| ad3f0a348a | |||
| 9a411ebf32 | |||
| 4188b54c28 | |||
| 19010d3766 | |||
| 02968cda63 | |||
| 098e304118 | |||
| 650c45dcc5 | |||
| 93f55f3ccf | |||
| 569db3c1d2 | |||
| 26d97d3374 | |||
| 3acfa1b39b | |||
| bbc67f8506 | |||
| 8343b56870 | |||
| 008d900f81 | |||
| 32e5d6cd15 | |||
| 28c0797d30 | |||
| 7ae657880d | |||
| a701aa7292 | |||
| db67cffb4d | |||
| 433f934783 | |||
| 0dc6778548 | |||
| c86b52698b | |||
| e5e5aa7ded | |||
| 4474b9b371 | |||
| 26e966852c | |||
| 852166f5cc | |||
| 75ac92b90f | |||
| 96f8e5158e | |||
| b68f3f7781 | |||
| 36bef17224 | |||
| 715771bcff | |||
| 8e264baaa2 | |||
| 8ff147de6d | |||
| 71057c586f | |||
| fac22f4038 | |||
| 40094779d7 | |||
| 2552aa45c3 | |||
| c2972a80f9 | |||
| 791a440709 | |||
| 13145dd9a7 | |||
| e9c477dcf2 | |||
| 8eebd19884 | |||
| a54b35a4d6 | |||
| 8a6f7a5e78 | |||
| 6752959d4a | |||
| da52d065a4 | |||
| 255e45ca58 | |||
| b731b7cf12 | |||
| 5f014635e1 | |||
| ef3d8314d3 | |||
| 7ef3fd567f | |||
| b773e27ad1 | |||
| a77c0a1098 | |||
| 57b9ddf20e | |||
| c7b5fb9116 | |||
| ba97d0e838 | |||
| b8085e2c42 | |||
| c251b312d8 | |||
| 14fb17e22f | |||
| a34c87ab46 | |||
| 1ad9cc8c67 | |||
| 5627d0d7fd | |||
| 2cfdbefde8 | |||
| 7dabcdace8 | |||
| 187a70f760 | |||
| 48303589e9 | |||
| 8385b27cad | |||
| abd75aedd7 | |||
| daf54ad54d | |||
| 94fe677f91 | |||
| 3de85098e5 | |||
| 5524dc8773 | |||
| 19dd5f3e32 | |||
| 99cb610b12 | |||
| 9d3963559a | |||
| 1b8c44b506 | |||
| 6f46f988d9 | |||
| 68485aa4e4 | |||
| 1581fdc198 | |||
| 317ae0fb37 | |||
| 5bf4196aed | |||
| 38aa03e0f7 | |||
| 9a9dc3ab23 | |||
| e4aa959e2c | |||
| 2aa9a56f40 | |||
| 721ed4ec0e | |||
| 70be4ceab1 | |||
| 5cf91d0496 | |||
| 6fee0bdb2d | |||
| e40c161125 | |||
| 97eb55cc55 | |||
| 65134be0d1 | |||
| dbf71a23aa | |||
| 7fec3db1d6 | |||
| 26b573c1d0 | |||
| 5ec1e7307e | |||
| 8bc36b7f21 | |||
| 3b909eb693 | |||
| 57e39c4472 | |||
| ff7672339c | |||
| 6e824a2c22 | |||
| 32fa49e093 | |||
| 2250eac58f | |||
| 97be6b3a0e | |||
| 942655c947 | |||
| 615db3f7fd | |||
| 0bb75c5389 | |||
| 7b5ee81d2d | |||
| 7fe46f1e1d | |||
| 50c0d1c9da | |||
| 6dbf544d06 | |||
| 41e3a0f699 | |||
| c5d8aec28b | |||
| 4653a9aef0 | |||
| ab3492bb90 | |||
| ed7cbb3866 | |||
| a30626cfe3 | |||
| d2c087b3e2 | |||
| 0a747f991d | |||
| f5a1e86d85 | |||
| 693bcb0f83 | |||
| 0d0daed86e | |||
| 95a349abcc | |||
| c6ba5dc539 | |||
| 6c3afeec0f | |||
| 29c58b4588 | |||
| ecc565aa3f | |||
| db61810f98 | |||
| 48f203ad5b | |||
| 6ab4ac2dfb | |||
| 9cb3c7b6e6 | |||
| adaa4ed350 | |||
| a64b9aa9b8 | |||
| 0138eca61d | |||
| 139a40de29 | |||
| 17d9becd3c | |||
| 449b482cc3 | |||
| a7e099c946 | |||
| 610462be5a | |||
| 207d32668f | |||
| e94a30bec0 |
@@ -1,11 +0,0 @@
|
||||
root = true
|
||||
|
||||
[*]
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[*.{py,pyx,pxd}]
|
||||
charset = utf-8
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
@@ -1,43 +0,0 @@
|
||||
* text=auto eol=lf
|
||||
|
||||
# Binary resources are ordinary Git blobs.
|
||||
*.onnx -text
|
||||
openpilot/selfdrive/modeld/models/big_*_tinygrad.pkl -text
|
||||
*.svg -text
|
||||
*.png -text
|
||||
*.gif -text
|
||||
*.ttf -text
|
||||
*.otf -text
|
||||
*.woff -text
|
||||
*.woff2 -text
|
||||
*.wav -text
|
||||
openpilot/starpilot/galaxy/web/vendor/vue/vue.esm-browser.js -text
|
||||
|
||||
openpilot/selfdrive/car/tests/test_models_segs.txt -text
|
||||
openpilot/common/hardware/comma/updater -text
|
||||
|
||||
# Dependency snapshots keep their own storage attributes.
|
||||
msgq_repo/** !filter !diff !merge -text
|
||||
opendbc_repo/** !filter !diff !merge -text
|
||||
panda/** !filter !diff !merge -text
|
||||
rednose_repo/** !filter !diff !merge -text
|
||||
teleoprtc_repo/** !filter !diff !merge -text
|
||||
tinygrad_repo/** !filter !diff !merge -text
|
||||
|
||||
# Compiled model parts must retain exact binary bytes.
|
||||
*.pkl.chunk* -text
|
||||
*.pkl -text
|
||||
|
||||
# Native release artifacts must retain exact binary bytes.
|
||||
*.a -text
|
||||
*.so -text
|
||||
*.so.* -text
|
||||
openpilot/cereal/messaging/bridge -text
|
||||
openpilot/selfdrive/pandad/pandad -text
|
||||
openpilot/system/camerad/camerad -text
|
||||
openpilot/system/loggerd/bootlog -text
|
||||
openpilot/system/loggerd/encoderd -text
|
||||
openpilot/system/loggerd/loggerd -text
|
||||
openpilot/starpilot/galaxy/bin/frpc_linux_arm64 -text
|
||||
openpilot/starpilot/maps/provider/mapd -text
|
||||
openpilot/selfdrive/test/process_replay/refs/starpilot/*.zst -text
|
||||
@@ -1,47 +0,0 @@
|
||||
name: Bug report
|
||||
description: For issues with running openpilot on your comma device
|
||||
labels: ["bug"]
|
||||
body:
|
||||
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: >
|
||||
Before creating a **bug report**, please check the following:
|
||||
* If the issue likely only affects your car model or make, go back and open a **car bug report** instead.
|
||||
* If the issue is related to the driving or driver monitoring models, you should open a [discussion](https://github.com/commaai/openpilot/discussions/categories/model-feedback) instead.
|
||||
* Ensure you're running the latest openpilot release.
|
||||
* Ensure you're using officially supported hardware. Issues running on PCs have a different issue template.
|
||||
* Ensure there isn't an existing issue for your bug. If there is, leave a comment on the existing issue.
|
||||
* Ensure you're running stock openpilot. We cannot look into bug reports from forks.
|
||||
|
||||
If you're unsure whether you've hit a bug, check out the #installation-help channel in the [community Discord server](https://discord.comma.ai).
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Describe the bug
|
||||
description: Also include a description of how to reproduce the bug
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: input
|
||||
id: route
|
||||
attributes:
|
||||
label: Provide a route where the issue occurs
|
||||
description: Ensure the route is fully uploaded at https://useradmin.comma.ai. We cannot look into issues without routes, or at least a Dongle ID.
|
||||
placeholder: 77611a1fac303767|2020-05-11--16-37-07
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: input
|
||||
id: version
|
||||
attributes:
|
||||
label: openpilot version
|
||||
description: If you're not on release, provide the commit hash
|
||||
placeholder: 0.8.10
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Additional info
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: Car bug report
|
||||
url: https://github.com/commaai/opendbc/issues/new
|
||||
about: For issues with a particular car make or model
|
||||
- name: Join the Discord
|
||||
url: https://discord.comma.ai
|
||||
about: The community Discord is for both openpilot development and experience discussion
|
||||
- name: Report driving behavior feedback
|
||||
url: https://discord.com/channels/469524606043160576/1254834193066623017
|
||||
about: Feedback for the driving and driver monitoring models goes in the #driving-feedback in Discord
|
||||
- name: Community Wiki
|
||||
url: https://github.com/commaai/openpilot/wiki
|
||||
about: Check out our community wiki
|
||||
@@ -1,42 +0,0 @@
|
||||
name: PC bug report
|
||||
description: For issues with running openpilot on PC
|
||||
labels: ["PC"]
|
||||
body:
|
||||
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: >
|
||||
Before creating a **bug report**, please check the following:
|
||||
* Ensure you're running the latest openpilot release.
|
||||
* Ensure there isn't an existing issue for your bug. If there is, leave a comment on the existing issue.
|
||||
* Ensure you're running stock openpilot. We cannot look into bug reports from forks.
|
||||
|
||||
If you're unsure whether you've hit a bug, check out the #installation-help channel in the [community Discord server](https://discord.comma.ai).
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Describe the bug
|
||||
description: Also include a description of how to reproduce the bug
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: input
|
||||
id: os-version
|
||||
attributes:
|
||||
label: OS Version
|
||||
placeholder: Ubuntu 24.04
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: input
|
||||
id: version
|
||||
attributes:
|
||||
label: openpilot version or commit
|
||||
placeholder: bd36f2ec8d3559909678eff2690c10a520938367
|
||||
validations:
|
||||
required: false
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Additional info
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
CI / testing:
|
||||
- changed-files:
|
||||
- any-glob-to-all-files: "{.github/**,**/test_*,**/test/**,Jenkinsfile}"
|
||||
|
||||
car:
|
||||
- changed-files:
|
||||
- any-glob-to-all-files: '{openpilot/selfdrive/car/**,opendbc_repo}'
|
||||
|
||||
simulation:
|
||||
- changed-files:
|
||||
- any-glob-to-all-files: 'openpilot/tools/sim/**'
|
||||
|
||||
ui:
|
||||
- changed-files:
|
||||
- any-glob-to-all-files: '{openpilot/selfdrive/assets/**,openpilot/selfdrive/ui/**,openpilot/system/ui/**}'
|
||||
|
||||
tools:
|
||||
- changed-files:
|
||||
- any-glob-to-all-files: 'openpilot/tools/**'
|
||||
|
||||
multilanguage:
|
||||
- changed-files:
|
||||
- any-glob-to-all-files: 'openpilot/selfdrive/ui/translations/**'
|
||||
|
||||
autonomy:
|
||||
- changed-files:
|
||||
- any-glob-to-all-files: "{openpilot/selfdrive/modeld/models/**,openpilot/selfdrive/test/process_replay/model_replay_ref_commit}"
|
||||
@@ -1,33 +0,0 @@
|
||||
name: "PR review"
|
||||
on:
|
||||
pull_request_target:
|
||||
types: [opened, reopened, synchronize, edited]
|
||||
|
||||
jobs:
|
||||
labeler:
|
||||
name: review
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
|
||||
# Label PRs
|
||||
- uses: actions/labeler@v6
|
||||
with:
|
||||
dot: true
|
||||
configuration-path: .github/labeler.yaml
|
||||
|
||||
# Check PR target branch
|
||||
- name: check branch
|
||||
uses: Vankka/pr-target-branch-action@5da68a42bcb7b43d39104295a876a6f3f8d7908b
|
||||
if: github.repository == 'commaai/openpilot'
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
target: /^(?!master$).*/
|
||||
exclude: /commaai:.*/
|
||||
change-to: ${{ github.base_ref }}
|
||||
already-exists-action: close_this
|
||||
already-exists-comment: "Your PR should be made against the `master` branch"
|
||||
@@ -1,58 +0,0 @@
|
||||
name: diff report
|
||||
|
||||
on:
|
||||
pull_request_target:
|
||||
types: [opened, synchronize, reopened]
|
||||
|
||||
jobs:
|
||||
comment:
|
||||
name: comment
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
actions: read
|
||||
steps:
|
||||
- name: Wait for process replay
|
||||
id: wait
|
||||
continue-on-error: true
|
||||
uses: lewagon/wait-on-check-action@1d57e2c51a58d812d2765e036a028b6bdb5a6154
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
check-name: process replay
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
allowed-conclusions: success,failure
|
||||
wait-interval: 20
|
||||
- name: Get tests run ID
|
||||
if: steps.wait.outcome == 'success'
|
||||
id: tests-run
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
run_id=$(gh api \
|
||||
"repos/${{ github.repository }}/actions/workflows/tests.yaml/runs?event=pull_request&head_sha=${{ github.event.pull_request.head.sha }}&per_page=1" \
|
||||
--jq '.workflow_runs[0].id // empty')
|
||||
if [ -z "$run_id" ]; then
|
||||
echo "No tests.yaml run found for ${{ github.event.pull_request.head.sha }}" >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "run-id=$run_id" >> "$GITHUB_OUTPUT"
|
||||
- name: Download diff
|
||||
if: steps.wait.outcome == 'success'
|
||||
continue-on-error: true
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
repository: ${{ github.repository }}
|
||||
run-id: ${{ steps.tests-run.outputs.run-id }}
|
||||
name: diff_report_${{ github.event.number }}
|
||||
path: .
|
||||
- name: Comment on PR
|
||||
if: steps.wait.outcome == 'success' && hashFiles('diff_report.txt') != ''
|
||||
uses: thollander/actions-comment-pull-request@24bffb9b452ba05a4f3f77933840a6a841d1b32b
|
||||
with:
|
||||
file-path: diff_report.txt
|
||||
comment-tag: diff_report
|
||||
pr-number: ${{ github.event.number }}
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
@@ -1,67 +0,0 @@
|
||||
name: docs
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- Domathon
|
||||
- SecretGoodStarPilot
|
||||
pull_request:
|
||||
workflow_call:
|
||||
inputs:
|
||||
run_number:
|
||||
default: '1'
|
||||
required: false
|
||||
type: string
|
||||
concurrency:
|
||||
group: docs-tests-ci-run-${{ inputs.run_number }}-${{ github.event_name == 'push' && github.ref == 'refs/heads/master' && github.run_id || github.head_ref || github.ref }}-${{ github.workflow }}-${{ github.event_name }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
docs:
|
||||
name: build docs
|
||||
runs-on: ${{
|
||||
(github.repository == 'commaai/openpilot') &&
|
||||
((github.event_name != 'pull_request') ||
|
||||
(github.event.pull_request.head.repo.full_name == 'commaai/openpilot'))
|
||||
&& fromJSON('["namespace-profile-amd64-8x16"]')
|
||||
|| fromJSON('["ubuntu-24.04"]') }}
|
||||
steps:
|
||||
- uses: commaai/timeout@v1
|
||||
- uses: actions/checkout@v7
|
||||
- run: ./tools/op.sh setup
|
||||
|
||||
# Build
|
||||
- name: Build docs
|
||||
run: ./tools/op.sh docs --build
|
||||
|
||||
# Push to docs.comma.ai
|
||||
- uses: actions/checkout@v7
|
||||
if: github.ref == 'refs/heads/master' && github.repository == 'commaai/openpilot'
|
||||
with:
|
||||
path: openpilot-docs
|
||||
ssh-key: ${{ secrets.OPENPILOT_DOCS_KEY }}
|
||||
repository: commaai/openpilot-docs
|
||||
- name: Push
|
||||
if: github.ref == 'refs/heads/master' && github.repository == 'commaai/openpilot'
|
||||
run: |
|
||||
set -x
|
||||
|
||||
source tools/release/identity.sh
|
||||
|
||||
cd openpilot-docs
|
||||
git checkout --orphan tmp
|
||||
git rm -rf .
|
||||
|
||||
# copy over docs
|
||||
cp -r ../docs/_site/ docs/
|
||||
|
||||
# GitHub pages config
|
||||
touch docs/.nojekyll
|
||||
echo -n docs.comma.ai > docs/CNAME
|
||||
|
||||
git add -f .
|
||||
git commit -m "build docs"
|
||||
|
||||
# docs live in different repo to not bloat openpilot's full clone size
|
||||
git push -f origin tmp:gh-pages
|
||||
@@ -1,96 +0,0 @@
|
||||
name: jenkins scan
|
||||
|
||||
on:
|
||||
issue_comment:
|
||||
types: [created, edited]
|
||||
|
||||
jobs:
|
||||
cleanup-branches:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
steps:
|
||||
- name: Delete stale Jenkins branches
|
||||
uses: actions/github-script@v9
|
||||
with:
|
||||
script: |
|
||||
const cutoff = Date.now() - 24 * 60 * 60 * 1000;
|
||||
const prefixes = ['tmp-jenkins', '__jenkins'];
|
||||
|
||||
for await (const response of github.paginate.iterator(github.rest.repos.listBranches, {
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
per_page: 100,
|
||||
})) {
|
||||
for (const branch of response.data) {
|
||||
if (!prefixes.some(p => branch.name.startsWith(p))) continue;
|
||||
|
||||
const { data: commit } = await github.rest.repos.getCommit({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
ref: branch.commit.sha,
|
||||
});
|
||||
|
||||
const commitDate = new Date(commit.commit.committer.date).getTime();
|
||||
if (commitDate < cutoff) {
|
||||
console.log(`Deleting branch: ${branch.name} (last commit: ${commit.commit.committer.date})`);
|
||||
await github.rest.git.deleteRef({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
ref: `heads/${branch.name}`,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
scan-comments:
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ github.event.issue.pull_request }}
|
||||
permissions:
|
||||
contents: write
|
||||
issues: write
|
||||
steps:
|
||||
- name: Check for trigger phrase
|
||||
id: check_comment
|
||||
uses: actions/github-script@v9
|
||||
with:
|
||||
script: |
|
||||
const triggerPhrase = "trigger-jenkins";
|
||||
const comment = context.payload.comment.body;
|
||||
const commenter = context.payload.comment.user.login;
|
||||
|
||||
const { data: permissions } = await github.rest.repos.getCollaboratorPermissionLevel({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
username: commenter
|
||||
});
|
||||
|
||||
const hasWriteAccess = permissions.permission === 'write' || permissions.permission === 'admin';
|
||||
|
||||
return (hasWriteAccess && comment.includes(triggerPhrase));
|
||||
result-encoding: json
|
||||
|
||||
- name: Checkout repository
|
||||
if: steps.check_comment.outputs.result == 'true'
|
||||
uses: actions/checkout@v7
|
||||
with:
|
||||
ref: refs/pull/${{ github.event.issue.number }}/head
|
||||
|
||||
- name: Push to tmp-jenkins branch
|
||||
if: steps.check_comment.outputs.result == 'true'
|
||||
run: |
|
||||
git config --global user.name "github-actions[bot]"
|
||||
git config --global user.email "github-actions[bot]@users.noreply.github.com"
|
||||
git checkout -b tmp-jenkins-${{ github.event.issue.number }}
|
||||
git push -f origin tmp-jenkins-${{ github.event.issue.number }}
|
||||
|
||||
- name: Delete trigger comment
|
||||
if: steps.check_comment.outputs.result == 'true' && always()
|
||||
uses: actions/github-script@v9
|
||||
with:
|
||||
script: |
|
||||
await github.rest.issues.deleteComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
comment_id: context.payload.comment.id,
|
||||
});
|
||||
@@ -1,33 +0,0 @@
|
||||
name: release
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 9 * * *'
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
build_masterci:
|
||||
name: build master-ci
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository == 'commaai/openpilot'
|
||||
permissions:
|
||||
checks: read
|
||||
contents: write
|
||||
steps:
|
||||
- name: Wait for green check mark
|
||||
if: ${{ github.event_name == 'schedule' }}
|
||||
uses: lewagon/wait-on-check-action@1d57e2c51a58d812d2765e036a028b6bdb5a6154
|
||||
with:
|
||||
ref: master
|
||||
wait-interval: 30
|
||||
running-workflow-name: 'build master-ci'
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
check-regexp: ^((?!.*(build prebuilt|create badges).*).)*$
|
||||
- uses: actions/checkout@v4 # checkout version > v4 breaks nightly release script
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- run: ./tools/op.sh setup
|
||||
- name: Push master-ci
|
||||
run: BRANCH=__nightly tools/release/build_stripped.sh
|
||||
- name: Push chestnut nightly
|
||||
run: |
|
||||
INCLUDE_BIG_MODEL=1 BRANCH=__nightly-chestnut tools/release/build_stripped.sh
|
||||
@@ -1,121 +0,0 @@
|
||||
name: repo maintenance
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 14 * * 1" # every Monday at 2am UTC (6am PST)
|
||||
pull_request:
|
||||
types: [closed]
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
PYTHONPATH: ${{ github.workspace }}
|
||||
|
||||
jobs:
|
||||
package_updates:
|
||||
name: package_updates
|
||||
runs-on: ubuntu-latest
|
||||
if: >-
|
||||
github.repository == 'commaai/openpilot' &&
|
||||
(github.event_name == 'schedule' || github.event_name == 'workflow_dispatch')
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
- run: ./tools/op.sh setup
|
||||
- name: uv lock
|
||||
run: uv lock --upgrade
|
||||
- name: uv pip tree
|
||||
id: pip_tree
|
||||
run: |
|
||||
echo 'PIP_TREE<<EOF' >> $GITHUB_OUTPUT
|
||||
uv pip tree >> $GITHUB_OUTPUT
|
||||
echo 'EOF' >> $GITHUB_OUTPUT
|
||||
- name: venv size
|
||||
id: venv_size
|
||||
run: |
|
||||
echo 'VENV_SIZE<<EOF' >> $GITHUB_OUTPUT
|
||||
echo "Total: $(du -sh .venv | cut -f1)" >> $GITHUB_OUTPUT
|
||||
echo "" >> $GITHUB_OUTPUT
|
||||
echo "Top 10 by size:" >> $GITHUB_OUTPUT
|
||||
du -sh .venv/lib/python*/site-packages/* 2>/dev/null \
|
||||
| grep -v '\.dist-info' \
|
||||
| grep -v '__pycache__' \
|
||||
| sort -rh \
|
||||
| head -10 \
|
||||
| while IFS=$'\t' read size path; do echo "$size ${path##*/}"; done >> $GITHUB_OUTPUT
|
||||
echo 'EOF' >> $GITHUB_OUTPUT
|
||||
- name: Check tracked dependencies
|
||||
run: python3 tools/vendor/check.py
|
||||
- name: update car docs
|
||||
run: |
|
||||
python openpilot/selfdrive/car/docs.py
|
||||
git add docs/CARS.md
|
||||
- name: Create Pull Request
|
||||
uses: peter-evans/create-pull-request@5f6978faf089d4d20b00c7766989d076bb2fc7f1
|
||||
with:
|
||||
author: Vehicle Researcher <user@comma.ai>
|
||||
token: ${{ secrets.ACTIONS_CREATE_PR_PAT }}
|
||||
commit-message: Update Python packages
|
||||
title: '[bot] Update Python packages'
|
||||
branch: auto-package-updates
|
||||
base: master
|
||||
delete-branch: true
|
||||
body: |
|
||||
Automatic PR from repo-maintenance -> package_updates
|
||||
|
||||
```
|
||||
$ du -sh .venv && du -sh .venv/lib/python*/site-packages/* | sort -rh | head -10
|
||||
${{ steps.venv_size.outputs.VENV_SIZE }}
|
||||
```
|
||||
|
||||
```
|
||||
$ uv pip tree
|
||||
${{ steps.pip_tree.outputs.PIP_TREE }}
|
||||
```
|
||||
labels: bot
|
||||
|
||||
cleanup_closed_branches:
|
||||
if: github.repository == 'commaai/openpilot' && github.event_name == 'pull_request'
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: read
|
||||
steps:
|
||||
- uses: actions/github-script@v9
|
||||
with:
|
||||
script: |
|
||||
const { owner, repo } = context.repo;
|
||||
const upstream = `${owner}/${repo}`;
|
||||
|
||||
const closed = context.payload.pull_request;
|
||||
if (closed) {
|
||||
if (closed.head.repo?.full_name === upstream) {
|
||||
await github.rest.git.deleteRef({ owner, repo, ref: `heads/${closed.head.ref}` }).catch(console.log);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
for await (const response of github.paginate.iterator(github.rest.pulls.list, {
|
||||
owner,
|
||||
repo,
|
||||
state: 'closed',
|
||||
per_page: 100,
|
||||
})) {
|
||||
for (const pr of response.data) {
|
||||
if (pr.head.repo?.full_name !== upstream) continue;
|
||||
|
||||
const branch = pr.head.ref;
|
||||
try {
|
||||
await github.rest.git.deleteRef({
|
||||
owner,
|
||||
repo,
|
||||
ref: `heads/${branch}`,
|
||||
});
|
||||
console.log(`Deleted branch ${branch} (PR #${pr.number})`);
|
||||
} catch (error) {
|
||||
if (error.status === 422 || error.status === 403) {
|
||||
console.log(`Skipping branch ${branch} (PR #${pr.number}): ${error.message}`);
|
||||
continue;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
name: safety qualification
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [master, Domathon, SecretGoodStarPilot]
|
||||
pull_request:
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
host_gates:
|
||||
name: ${{ matrix.gate }}
|
||||
runs-on: ubuntu-24.04
|
||||
timeout-minutes: 100
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
gate: [coverage, opendbc-misra, panda-misra, panda-host, mutation-list]
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
- run: ./tools/op.sh setup
|
||||
- name: Run isolated source-pinned gate
|
||||
run: .venv/bin/python tools/ci/run_safety_qualification.py --gate "${{ matrix.gate }}" --output "ci-results/safety-${{ matrix.gate }}"
|
||||
- name: Save full gate evidence
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: safety-${{ matrix.gate }}
|
||||
path: ci-results/safety-${{ matrix.gate }}/
|
||||
if-no-files-found: error
|
||||
|
||||
full_mutation:
|
||||
name: full safety mutation
|
||||
runs-on: ubuntu-24.04
|
||||
timeout-minutes: 360
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
- run: ./tools/op.sh setup
|
||||
- name: Run all discovered mutants
|
||||
run: .venv/bin/python tools/ci/run_safety_qualification.py --gate mutation-full --output ci-results/safety-mutation-full
|
||||
- name: Save full mutation evidence
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: safety-mutation-full
|
||||
path: ci-results/safety-mutation-full/
|
||||
if-no-files-found: error
|
||||
@@ -1,52 +0,0 @@
|
||||
name: stale
|
||||
on:
|
||||
schedule:
|
||||
- cron: '30 1 * * *'
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
DAYS_BEFORE_PR_CLOSE: 7
|
||||
DAYS_BEFORE_PR_STALE: 24
|
||||
DAYS_BEFORE_PR_STALE_DRAFT: 30
|
||||
|
||||
jobs:
|
||||
stale:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/stale@v10
|
||||
with:
|
||||
exempt-all-milestones: true
|
||||
|
||||
# pull request config
|
||||
stale-pr-message: 'This PR has had no activity for ${{ env.DAYS_BEFORE_PR_STALE }} days. It will be automatically closed in ${{ env.DAYS_BEFORE_PR_CLOSE }} days if there is no activity.'
|
||||
close-pr-message: 'This PR has been automatically closed due to inactivity. Feel free to re-open once activity resumes.'
|
||||
stale-pr-label: stale
|
||||
delete-branch: ${{ github.event.pull_request.head.repo.full_name == 'commaai/openpilot' }} # only delete branches on the main repo
|
||||
exempt-pr-labels: "ignore stale,needs testing" # if wip or it needs testing from the community, don't mark as stale
|
||||
days-before-pr-stale: ${{ env.DAYS_BEFORE_PR_STALE }}
|
||||
days-before-pr-close: ${{ env.DAYS_BEFORE_PR_CLOSE }}
|
||||
exempt-draft-pr: false
|
||||
|
||||
# issue config
|
||||
days-before-issue-stale: -1 # ignore issues for now
|
||||
|
||||
# same as above, but give draft PRs more time
|
||||
stale_drafts:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/stale@v10
|
||||
with:
|
||||
exempt-all-milestones: true
|
||||
|
||||
# pull request config
|
||||
stale-pr-message: 'This PR has had no activity for ${{ env.DAYS_BEFORE_PR_STALE_DRAFT }} days. It will be automatically closed in ${{ env.DAYS_BEFORE_PR_CLOSE }} days if there is no activity.'
|
||||
close-pr-message: 'This PR has been automatically closed due to inactivity. Feel free to re-open once activity resumes.'
|
||||
stale-pr-label: stale
|
||||
delete-branch: ${{ github.event.pull_request.head.repo.full_name == 'commaai/openpilot' }} # only delete branches on the main repo
|
||||
exempt-pr-labels: "ignore stale,needs testing" # if wip or it needs testing from the community, don't mark as stale
|
||||
days-before-pr-stale: ${{ env.DAYS_BEFORE_PR_STALE_DRAFT }}
|
||||
days-before-pr-close: ${{ env.DAYS_BEFORE_PR_CLOSE }}
|
||||
exempt-draft-pr: true
|
||||
|
||||
# issue config
|
||||
days-before-issue-stale: -1 # ignore issues for now
|
||||
@@ -1,472 +0,0 @@
|
||||
name: tests
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- Domathon
|
||||
- SecretGoodStarPilot
|
||||
pull_request:
|
||||
workflow_dispatch:
|
||||
workflow_call:
|
||||
inputs:
|
||||
run_number:
|
||||
default: '1'
|
||||
required: false
|
||||
type: string
|
||||
|
||||
concurrency:
|
||||
group: tests-ci-run-${{ inputs.run_number }}-${{ github.event_name == 'push' && github.ref == 'refs/heads/master' && github.run_id || github.head_ref || github.ref }}-${{ github.workflow }}-${{ github.event_name }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
env:
|
||||
CI: 1
|
||||
PYTHONPATH: ${{ github.workspace }}
|
||||
|
||||
jobs:
|
||||
source_integrity:
|
||||
name: source integrity
|
||||
runs-on: ubuntu-24.04
|
||||
permissions:
|
||||
contents: read
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
- name: Validate tracked dependencies
|
||||
run: python3 tools/vendor/check.py --revision HEAD
|
||||
- run: ./tools/op.sh setup
|
||||
id: setup
|
||||
- name: Build native dependencies for packaging tests
|
||||
id: build
|
||||
run: scons -j2 openpilot/common/libparams_c.so msgq_repo/msgq/ipc_pyx.so
|
||||
- name: Test dependency tools
|
||||
if: ${{ !cancelled() && steps.build.outcome == 'success' }}
|
||||
run: python3 -m unittest discover -s tools/vendor/tests -v
|
||||
- name: Test release packaging
|
||||
if: ${{ !cancelled() && steps.build.outcome == 'success' }}
|
||||
run: python3 -m pytest -q tools/release/tests
|
||||
- name: Test CI tools
|
||||
if: ${{ !cancelled() && steps.build.outcome == 'success' }}
|
||||
run: python3 -m unittest discover -s tools/ci/tests -v
|
||||
- name: Test updater
|
||||
if: ${{ !cancelled() && steps.build.outcome == 'success' }}
|
||||
run: python3 -m unittest discover -s openpilot/system/updated/tests -v
|
||||
- name: Check setup and release shell syntax
|
||||
if: ${{ !cancelled() && steps.setup.outcome == 'success' }}
|
||||
run: |
|
||||
for script in tools/op.sh tools/release/*.sh openpilot/selfdrive/test/setup_device_ci.sh; do
|
||||
bash -n "$script"
|
||||
done
|
||||
|
||||
build_release:
|
||||
name: build release
|
||||
runs-on: ubuntu-24.04-arm
|
||||
env:
|
||||
STRIPPED_DIR: /tmp/releasepilot
|
||||
PYTHONPATH: /tmp/releasepilot
|
||||
STARPILOT_CI_BUILD_MANIFEST: /tmp/starpilot-release-build-${{ github.run_id }}-${{ github.run_attempt }}.json
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
- uses: actions/setup-go@v7
|
||||
with:
|
||||
go-version: '1.25.1'
|
||||
cache-dependency-path: mapd_repo/go.sum
|
||||
- name: Build the pinned release map provider
|
||||
env:
|
||||
GOTOOLCHAIN: local
|
||||
run: |
|
||||
sudo apt-get update && sudo apt-get install -y --no-install-recommends pkg-config zlib1g-dev
|
||||
(cd mapd_repo && go mod download)
|
||||
PYTHONPATH="$GITHUB_WORKSPACE" python3 -m openpilot.starpilot.maps.package_shadow
|
||||
- name: Build devel
|
||||
timeout-minutes: 1
|
||||
run: TARGET_DIR=$STRIPPED_DIR tools/release/build_stripped.sh
|
||||
- run: ./tools/op.sh setup
|
||||
- name: Build openpilot and run checks
|
||||
timeout-minutes: 30
|
||||
working-directory: ${{ env.STRIPPED_DIR }}
|
||||
run: python3 openpilot/system/manager/build.py
|
||||
- name: Run tests
|
||||
timeout-minutes: 1
|
||||
working-directory: ${{ env.STRIPPED_DIR }}
|
||||
run: tools/release/check-dirty.sh
|
||||
- name: Check tracked dependencies
|
||||
timeout-minutes: 3
|
||||
run: python3 tools/vendor/check.py --revision HEAD
|
||||
|
||||
developer_commands:
|
||||
name: developer commands (${{ matrix.os }})
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-24.04, macos-latest]
|
||||
runs-on: ${{ matrix.os }}
|
||||
permissions:
|
||||
contents: read
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
- name: Install developer prerequisites
|
||||
run: tools/setup_dependencies.sh
|
||||
- name: Check launchers without starting tools
|
||||
run: |
|
||||
./build --help
|
||||
./dev help
|
||||
./test --help
|
||||
./tool help
|
||||
./tools/host help
|
||||
./c3 --help
|
||||
./c4 --help
|
||||
./onroad --help
|
||||
- name: Test commands through the isolated host environment
|
||||
run: |
|
||||
./dev pytest -q tools/ci/tests/test_dev_runtime.py tools/ci/tests/test_dev_runtime_review.py tools/ci/tests/test_build_entry.py openpilot/starpilot/ui/tests/test_host_launch.py openpilot/tools/replay/tests/test_onroad_host.py openpilot/starpilot/ui/tests/test_host_window_failure.py
|
||||
- name: Build both desktop UI entrypoints without a display
|
||||
run: |
|
||||
SP_C3_COMPILE_ONLY=1 ./c3
|
||||
SP_C4_COMPILE_ONLY=1 ./c4
|
||||
|
||||
build_mac:
|
||||
name: build macOS
|
||||
runs-on: ${{ ((github.repository == 'commaai/openpilot') && ((github.event_name != 'pull_request') || (github.event.pull_request.head.repo.full_name == 'commaai/openpilot'))) && 'namespace-profile-macos-8x14' || 'macos-latest' }}
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
- name: Remove Homebrew from environment
|
||||
run: |
|
||||
FILTERED=$(echo "$PATH" | tr ':' '\n' | grep -v '/opt/homebrew' | tr '\n' ':')
|
||||
echo "PATH=${FILTERED}/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin" >> $GITHUB_ENV
|
||||
- run: ./tools/op.sh setup
|
||||
- name: Building openpilot
|
||||
run: scons
|
||||
|
||||
static_analysis:
|
||||
name: static analysis
|
||||
runs-on: ${{
|
||||
(github.repository == 'commaai/openpilot') &&
|
||||
((github.event_name != 'pull_request') ||
|
||||
(github.event.pull_request.head.repo.full_name == 'commaai/openpilot'))
|
||||
&& fromJSON('["namespace-profile-amd64-8x16"]')
|
||||
|| fromJSON('["ubuntu-24.04"]') }}
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
- run: ./tools/op.sh setup
|
||||
- name: Static analysis
|
||||
timeout-minutes: 1
|
||||
run: scripts/lint/lint.sh
|
||||
|
||||
unit_tests:
|
||||
name: unit tests
|
||||
runs-on: ${{
|
||||
(github.repository == 'commaai/openpilot') &&
|
||||
((github.event_name != 'pull_request') ||
|
||||
(github.event.pull_request.head.repo.full_name == 'commaai/openpilot'))
|
||||
&& fromJSON('["namespace-profile-amd64-8x16"]')
|
||||
|| fromJSON('["ubuntu-24.04"]') }}
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
- name: Provide JavaScript runtime for Galaxy contract tests
|
||||
uses: actions/setup-node@v7
|
||||
with:
|
||||
node-version: '24'
|
||||
package-manager-cache: false
|
||||
- name: Provide pinned map provider toolchain
|
||||
uses: actions/setup-go@v7
|
||||
with:
|
||||
go-version: '1.25.1'
|
||||
cache-dependency-path: mapd_repo/go.sum
|
||||
- name: Fetch pinned map provider dependencies
|
||||
working-directory: mapd_repo
|
||||
env:
|
||||
GOTOOLCHAIN: local
|
||||
run: go mod download
|
||||
- run: ./tools/op.sh setup
|
||||
id: setup
|
||||
- name: Build openpilot
|
||||
id: build
|
||||
run: scons
|
||||
- name: Run unit tests
|
||||
if: ${{ !cancelled() && steps.build.outcome == 'success' }}
|
||||
timeout-minutes: ${{ contains(runner.name, 'nsc') && 2 || 20 }}
|
||||
env:
|
||||
RAYLIB_BACKEND: headless
|
||||
run: tools/op.sh test --json-output unit-test-results.json
|
||||
- name: Test custom module-level pytest cases
|
||||
if: ${{ !cancelled() && steps.build.outcome == 'success' }}
|
||||
timeout-minutes: 10
|
||||
env:
|
||||
RAYLIB_BACKEND: headless
|
||||
run: python3 tools/ci/run_host_tests.py --pytest
|
||||
- name: Test GM vehicle configurations and CAN behavior
|
||||
if: ${{ !cancelled() && steps.build.outcome == 'success' }}
|
||||
timeout-minutes: 5
|
||||
run: tools/op.sh test opendbc_repo/opendbc/car/gm/tests --json-output gm-test-results.json
|
||||
- name: Test native independent-axis protocol
|
||||
if: ${{ !cancelled() && steps.build.outcome == 'success' }}
|
||||
run: |
|
||||
openpilot/selfdrive/pandad/tests/test_panda_usb
|
||||
openpilot/selfdrive/pandad/tests/test_pandad_canprotocol
|
||||
openpilot/selfdrive/pandad/tests/test_aol_protocol
|
||||
openpilot/selfdrive/pandad/tests/test_aol_wire
|
||||
- name: Test map provider to host messaging
|
||||
if: ${{ !cancelled() && steps.build.outcome == 'success' }}
|
||||
working-directory: mapd_repo
|
||||
timeout-minutes: 5
|
||||
env:
|
||||
GOTOOLCHAIN: local
|
||||
STARPILOT_SHADOW_IPC_REQUIRED: '1'
|
||||
STARPILOT_SHADOW_EXEC_REQUIRED: '1'
|
||||
STARPILOT_PYTHON: ${{ github.workspace }}/.venv/bin/python
|
||||
run: go test -mod=readonly -race -run '^Test(MsgqNativeLayout|Shadow(GoPythonIPC|ExecutableHostGpsIPC))$' -count=1 -v
|
||||
- name: Save complete unit test results
|
||||
if: ${{ always() && (hashFiles('unit-test-results.json') != '' || hashFiles('gm-test-results.json') != '') }}
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: unit-test-results
|
||||
path: |
|
||||
unit-test-results.json
|
||||
gm-test-results.json
|
||||
if-no-files-found: error
|
||||
|
||||
map_provider:
|
||||
name: map provider
|
||||
runs-on: ubuntu-24.04
|
||||
timeout-minutes: 15
|
||||
permissions:
|
||||
contents: read
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
- uses: actions/setup-go@v7
|
||||
with:
|
||||
go-version: '1.25.1'
|
||||
cache-dependency-path: mapd_repo/go.sum
|
||||
- name: Install native build dependencies
|
||||
run: sudo apt-get update && sudo apt-get install -y --no-install-recommends pkg-config zlib1g-dev
|
||||
- name: Test and build pinned map provider
|
||||
run: python3 tools/ci/run_map_tests.py --output ci-results/map-provider
|
||||
- name: Save test and build evidence
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: map-provider
|
||||
path: ci-results/map-provider/
|
||||
if-no-files-found: error
|
||||
|
||||
vehicle_contracts:
|
||||
name: vehicle contracts (${{ matrix.suite }})
|
||||
runs-on: ubuntu-24.04
|
||||
timeout-minutes: 35
|
||||
permissions:
|
||||
contents: read
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
suite: [interfaces, safety-debug, safety-release]
|
||||
env:
|
||||
FUZZ_SEED: '0'
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
- run: ./tools/op.sh setup
|
||||
- name: Build native host dependencies
|
||||
run: scons
|
||||
- name: Run explicit vehicle suite and record every result
|
||||
timeout-minutes: 20
|
||||
run: python3 tools/ci/run_vehicle_tests.py --suite "${{ matrix.suite }}" --output "ci-results/${{ matrix.suite }}"
|
||||
- name: Require current complete interface regression coverage
|
||||
if: ${{ always() && matrix.suite == 'interfaces' }}
|
||||
run: |
|
||||
python3 tools/ci/fleet_coverage.py --require interfaces \
|
||||
--interface-coverage ci-results/interfaces/coverage.json \
|
||||
--interface-results ci-results/interfaces/results.json \
|
||||
--output ci-results/interfaces/fleet-coverage.json
|
||||
- name: Save collection, outcomes, and native build evidence
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: vehicle-contracts-${{ matrix.suite }}
|
||||
path: ci-results/${{ matrix.suite }}/
|
||||
if-no-files-found: error
|
||||
|
||||
recorded_vehicles:
|
||||
name: recorded vehicles (${{ matrix.variant }})
|
||||
runs-on: ubuntu-24.04
|
||||
timeout-minutes: 30
|
||||
permissions:
|
||||
contents: read
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
variant: [debug, release]
|
||||
env:
|
||||
FUZZ_SEED: '0'
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
- name: Install host prerequisites
|
||||
run: tools/setup_dependencies.sh
|
||||
- name: Replay pinned public CAN recordings
|
||||
timeout-minutes: 20
|
||||
run: |
|
||||
./dev python tools/ci/run_recorded_vehicle_tests.py \
|
||||
--variant "${{ matrix.variant }}" \
|
||||
--cache "${{ runner.temp }}/recorded-fixtures" \
|
||||
--output "$GITHUB_WORKSPACE/ci-results/recorded-${{ matrix.variant }}" \
|
||||
--download
|
||||
- name: Save recorded results and native build evidence
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: recorded-vehicles-${{ matrix.variant }}
|
||||
path: ci-results/recorded-${{ matrix.variant }}/
|
||||
if-no-files-found: error
|
||||
|
||||
panda_build_variants:
|
||||
name: Panda compile (${{ matrix.variant }})
|
||||
runs-on: ubuntu-24.04
|
||||
timeout-minutes: 35
|
||||
permissions:
|
||||
contents: read
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
variant: [debug, release]
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
- run: ./tools/op.sh setup
|
||||
- name: Compile firmware with the checked-in development certificate
|
||||
timeout-minutes: 15
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
mkdir -p ci-results/firmware
|
||||
if [[ "${{ matrix.variant }}" == release ]]; then
|
||||
export RELEASE=1
|
||||
export CERT="$PWD/panda/board/crypto/certs/debug"
|
||||
else
|
||||
unset RELEASE
|
||||
fi
|
||||
scons --verbose panda/board/obj/version panda/board/obj/panda.bin.signed panda/board/obj/bootstub.panda.bin panda/board/obj/panda_h7.bin.signed panda/board/obj/bootstub.panda_h7.bin 2>&1 | tee ci-results/firmware/build.log
|
||||
python3 tools/ci/record_firmware.py --variant "${{ matrix.variant }}" --build-log ci-results/firmware/build.log --output ci-results/firmware/provenance.json
|
||||
- name: Save compile evidence
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: panda-compile-${{ matrix.variant }}
|
||||
path: ci-results/firmware/
|
||||
if-no-files-found: error
|
||||
|
||||
process_replay:
|
||||
name: process replay
|
||||
runs-on: ${{
|
||||
(github.repository == 'commaai/openpilot') &&
|
||||
((github.event_name != 'pull_request') ||
|
||||
(github.event.pull_request.head.repo.full_name == 'commaai/openpilot'))
|
||||
&& fromJSON('["namespace-profile-amd64-8x16"]')
|
||||
|| fromJSON('["ubuntu-24.04"]') }}
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
- run: ./tools/op.sh setup
|
||||
- name: Build openpilot
|
||||
run: scons
|
||||
- name: Run replay
|
||||
timeout-minutes: ${{ contains(runner.name, 'nsc') && 2 || 20 }}
|
||||
run: openpilot/selfdrive/test/process_replay/test_processes.py -j$(nproc)
|
||||
- name: Print diff
|
||||
id: print-diff
|
||||
if: ${{ always() && hashFiles('openpilot/selfdrive/test/process_replay/diff.txt') != '' }}
|
||||
run: cat openpilot/selfdrive/test/process_replay/diff.txt
|
||||
- name: Save process replay input coverage
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: process-replay-coverage
|
||||
path: openpilot/selfdrive/test/process_replay/coverage.json
|
||||
if-no-files-found: error
|
||||
- name: Print diff report
|
||||
if: ${{ always() && hashFiles('openpilot/selfdrive/test/process_replay/diff_report.txt') != '' }}
|
||||
run: cat openpilot/selfdrive/test/process_replay/diff_report.txt
|
||||
- uses: actions/upload-artifact@v7
|
||||
if: always()
|
||||
continue-on-error: true
|
||||
with:
|
||||
name: process_replay_diff.txt
|
||||
path: openpilot/selfdrive/test/process_replay/diff.txt
|
||||
- name: Upload diff report
|
||||
uses: actions/upload-artifact@v7
|
||||
if: always() && github.event_name == 'pull_request'
|
||||
continue-on-error: true
|
||||
with:
|
||||
name: diff_report_${{ github.event.number }}
|
||||
path: openpilot/selfdrive/test/process_replay/diff_report.txt
|
||||
- name: Checkout ci-artifacts
|
||||
if: github.repository == 'commaai/openpilot' && github.ref == 'refs/heads/master'
|
||||
uses: actions/checkout@v7
|
||||
with:
|
||||
repository: commaai/ci-artifacts
|
||||
ssh-key: ${{ secrets.CI_ARTIFACTS_DEPLOY_KEY }}
|
||||
path: ${{ github.workspace }}/ci-artifacts
|
||||
- name: Prepare refs
|
||||
if: github.repository == 'commaai/openpilot' && github.ref == 'refs/heads/master'
|
||||
working-directory: ${{ github.workspace }}/ci-artifacts
|
||||
run: |
|
||||
git config user.name "GitHub Actions Bot"
|
||||
git config user.email "<>"
|
||||
git fetch origin process-replay || true
|
||||
git checkout process-replay 2>/dev/null || git checkout --orphan process-replay
|
||||
cp ${{ github.workspace }}/openpilot/selfdrive/test/process_replay/fakedata/*.zst .
|
||||
echo "${{ github.sha }}" > ref_commit
|
||||
git add .
|
||||
git commit -m "process-replay refs for ${{ github.repository }}@${{ github.sha }}" || echo "No changes to commit"
|
||||
- name: Push refs
|
||||
if: github.repository == 'commaai/openpilot' && github.ref == 'refs/heads/master'
|
||||
uses: nick-fields/retry@ad984534de44a9489a53aefd81eb77f87c70dc60
|
||||
with:
|
||||
timeout_minutes: 2
|
||||
max_attempts: 3
|
||||
command: cd ${{ github.workspace }}/ci-artifacts && git push origin process-replay --force
|
||||
|
||||
simulator_driving:
|
||||
name: simulator driving
|
||||
runs-on: ${{
|
||||
(github.repository == 'commaai/openpilot') &&
|
||||
((github.event_name != 'pull_request') ||
|
||||
(github.event.pull_request.head.repo.full_name == 'commaai/openpilot'))
|
||||
&& fromJSON('["namespace-profile-amd64-8x16"]')
|
||||
|| fromJSON('["ubuntu-24.04"]') }}
|
||||
if: false # FIXME: Started to timeout recently
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
- run: ./tools/op.sh setup
|
||||
- name: Build openpilot
|
||||
run: scons
|
||||
- name: Driving test
|
||||
timeout-minutes: 2
|
||||
env:
|
||||
# MetaDrive renders offscreen through panda3d's EGL pipe on llvmpipe
|
||||
EGL_PLATFORM: surfaceless
|
||||
run: python openpilot/tools/sim/tests/test_metadrive_bridge.py
|
||||
|
||||
create_ui_report:
|
||||
name: Create UI Report
|
||||
runs-on: ${{
|
||||
(github.repository == 'commaai/openpilot') &&
|
||||
((github.event_name != 'pull_request') ||
|
||||
(github.event.pull_request.head.repo.full_name == 'commaai/openpilot'))
|
||||
&& fromJSON('["namespace-profile-amd64-8x16"]')
|
||||
|| fromJSON('["ubuntu-24.04"]') }}
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
- run: ./tools/op.sh setup
|
||||
- name: Build openpilot
|
||||
run: scons
|
||||
- name: Create UI Report
|
||||
env:
|
||||
RAYLIB_BACKEND: headless
|
||||
run: |
|
||||
python3 openpilot/selfdrive/ui/tests/diff/replay.py
|
||||
python3 openpilot/selfdrive/ui/tests/diff/replay.py --big
|
||||
- name: Upload UI Report
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: ui-report-${{ inputs.run_number || '1' }}-${{ github.event_name == 'push' && github.ref == 'refs/heads/master' && 'master' || github.event.number }}
|
||||
path: openpilot/selfdrive/ui/tests/diff/report
|
||||
@@ -1,174 +0,0 @@
|
||||
name: "ui preview"
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
pull_request_target:
|
||||
types: [assigned, opened, synchronize, reopened, edited]
|
||||
branches:
|
||||
- 'master'
|
||||
paths:
|
||||
- 'openpilot/selfdrive/assets/**'
|
||||
- 'openpilot/selfdrive/ui/**'
|
||||
- 'openpilot/system/ui/**'
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
UI_JOB_NAME: "Create UI Report"
|
||||
REPORT_NAME: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' && 'master' || github.event.number }}
|
||||
SHA: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' && github.sha || github.event.pull_request.head.sha }}
|
||||
BRANCH_NAME: "openpilot/pr-${{ github.event.number }}-ui-preview"
|
||||
REPORT_FILES_BRANCH_NAME: "mici-raylib-ui-reports"
|
||||
|
||||
# variant:video_prefix:master_branch
|
||||
VARIANTS: "mici:mici_ui_replay:openpilot_master_ui_mici_raylib big:tizi_ui_replay:openpilot_master_ui_big_raylib"
|
||||
|
||||
jobs:
|
||||
preview:
|
||||
if: false # tmp disable due to GH API rate limiting flakiness
|
||||
#if: github.repository == 'commaai/openpilot'
|
||||
name: preview
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 20
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
actions: read
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
|
||||
- name: Waiting for ui generation to end
|
||||
uses: lewagon/wait-on-check-action@1d57e2c51a58d812d2765e036a028b6bdb5a6154
|
||||
with:
|
||||
ref: ${{ env.SHA }}
|
||||
check-name: ${{ env.UI_JOB_NAME }}
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
allowed-conclusions: success
|
||||
wait-interval: 20
|
||||
|
||||
- name: Getting workflow run ID
|
||||
id: get_run_id
|
||||
run: |
|
||||
echo "run_id=$(curl https://api.github.com/repos/${{ github.repository }}/commits/${{ env.SHA }}/check-runs | jq -r '.check_runs[] | select(.name == "${{ env.UI_JOB_NAME }}") | .html_url | capture("(?<number>[0-9]+)") | .number')" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Getting proposed ui
|
||||
uses: dawidd6/action-download-artifact@b6e2e70617bc3265edd6dab6c906732b2f1ae151
|
||||
with:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
run_id: ${{ steps.get_run_id.outputs.run_id }}
|
||||
search_artifacts: true
|
||||
name: ui-report-1-${{ env.REPORT_NAME }}
|
||||
path: ${{ github.workspace }}/pr_ui
|
||||
|
||||
- name: Getting mici master ui
|
||||
uses: actions/checkout@v7
|
||||
with:
|
||||
repository: commaai/ci-artifacts
|
||||
ssh-key: ${{ secrets.CI_ARTIFACTS_DEPLOY_KEY }}
|
||||
path: ${{ github.workspace }}/master_mici
|
||||
ref: openpilot_master_ui_mici_raylib
|
||||
|
||||
- name: Getting big master ui
|
||||
uses: actions/checkout@v7
|
||||
with:
|
||||
repository: commaai/ci-artifacts
|
||||
ssh-key: ${{ secrets.CI_ARTIFACTS_DEPLOY_KEY }}
|
||||
path: ${{ github.workspace }}/master_big
|
||||
ref: openpilot_master_ui_big_raylib
|
||||
|
||||
- name: Saving new master ui
|
||||
if: github.ref == 'refs/heads/master' && github.event_name == 'push'
|
||||
run: |
|
||||
for variant in $VARIANTS; do
|
||||
IFS=':' read -r name video branch <<< "$variant"
|
||||
master_dir="${{ github.workspace }}/master_${name}"
|
||||
cd "$master_dir"
|
||||
git checkout --orphan=new_branch
|
||||
git rm -rf *
|
||||
git branch -D "$branch"
|
||||
git branch -m "$branch"
|
||||
git config user.name "GitHub Actions Bot"
|
||||
git config user.email "<>"
|
||||
cp "${{ github.workspace }}/pr_ui/${video}.mp4" .
|
||||
git add .
|
||||
git commit -m "${name} video for commit ${{ env.SHA }}"
|
||||
git push origin "$branch" --force
|
||||
done
|
||||
|
||||
- name: Setup FFmpeg
|
||||
uses: AnimMouse/setup-ffmpeg@178e0b4a408f06ce40d896c3cd79f50fa6f8b0c3
|
||||
|
||||
- name: Finding diffs
|
||||
if: github.event_name == 'pull_request_target'
|
||||
id: find_diff
|
||||
run: |
|
||||
export PYTHONPATH=${{ github.workspace }}
|
||||
baseurl="https://github.com/commaai/ci-artifacts/raw/refs/heads/${{ env.BRANCH_NAME }}"
|
||||
|
||||
COMMENT=""
|
||||
for variant in $VARIANTS; do
|
||||
IFS=':' read -r name video _ <<< "$variant"
|
||||
diff_name="${name}_diff"
|
||||
|
||||
mv "${{ github.workspace }}/pr_ui/${video}.mp4" "${{ github.workspace }}/pr_ui/${video}_proposed.mp4"
|
||||
cp "${{ github.workspace }}/master_${name}/${video}.mp4" "${{ github.workspace }}/pr_ui/${video}_master.mp4"
|
||||
|
||||
diff_exit_code=0
|
||||
python3 ${{ github.workspace }}/openpilot/selfdrive/ui/tests/diff/diff.py \
|
||||
"${{ github.workspace }}/pr_ui/${video}_master.mp4" \
|
||||
"${{ github.workspace }}/pr_ui/${video}_proposed.mp4" \
|
||||
"${diff_name}.html" --basedir "$baseurl" --no-open || diff_exit_code=$?
|
||||
|
||||
cp "${{ github.workspace }}/openpilot/selfdrive/ui/tests/diff/report/${diff_name}.html" "${{ github.workspace }}/pr_ui/"
|
||||
cp "${{ github.workspace }}/openpilot/selfdrive/ui/tests/diff/report/${diff_name}.mp4" "${{ github.workspace }}/pr_ui/"
|
||||
|
||||
REPORT_URL="https://commaai.github.io/ci-artifacts/${diff_name}_pr_${{ github.event.number }}.html"
|
||||
if [ $diff_exit_code -eq 0 ]; then
|
||||
COMMENT+="**${name}**: Videos are identical! [View Diff Report]($REPORT_URL)"$'\n'
|
||||
else
|
||||
COMMENT+="**${name}**: ⚠️ <strong>Videos differ!</strong> [View Diff Report]($REPORT_URL)"$'\n'
|
||||
fi
|
||||
done
|
||||
|
||||
{
|
||||
echo "COMMENT<<EOF"
|
||||
echo "$COMMENT"
|
||||
echo "EOF"
|
||||
} >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Saving proposed ui
|
||||
if: github.event_name == 'pull_request_target'
|
||||
working-directory: ${{ github.workspace }}/master_mici
|
||||
run: |
|
||||
git config user.name "GitHub Actions Bot"
|
||||
git config user.email "<>"
|
||||
git checkout --orphan=${{ env.BRANCH_NAME }}
|
||||
git rm -rf *
|
||||
mv ${{ github.workspace }}/pr_ui/* .
|
||||
git add .
|
||||
git commit -m "ui videos for PR #${{ github.event.number }}"
|
||||
git push origin ${{ env.BRANCH_NAME }} --force
|
||||
|
||||
# Append diff reports to report files branch
|
||||
git fetch origin ${{ env.REPORT_FILES_BRANCH_NAME }}
|
||||
git checkout ${{ env.REPORT_FILES_BRANCH_NAME }}
|
||||
for variant in $VARIANTS; do
|
||||
IFS=':' read -r name _ _ <<< "$variant"
|
||||
diff_name="${name}_diff"
|
||||
cp "${{ github.workspace }}/openpilot/selfdrive/ui/tests/diff/report/${diff_name}.html" "${diff_name}_pr_${{ github.event.number }}.html"
|
||||
git add "${diff_name}_pr_${{ github.event.number }}.html"
|
||||
done
|
||||
git commit -m "ui diff reports for PR #${{ github.event.number }}" || echo "No changes to commit"
|
||||
git push origin ${{ env.REPORT_FILES_BRANCH_NAME }}
|
||||
|
||||
- name: Comment on PR
|
||||
if: github.event_name == 'pull_request_target'
|
||||
uses: thollander/actions-comment-pull-request@24bffb9b452ba05a4f3f77933840a6a841d1b32b
|
||||
with:
|
||||
message: |
|
||||
<!-- _(run_id_ui_preview **${{ github.run_id }}**)_ -->
|
||||
## UI Preview
|
||||
${{ steps.find_diff.outputs.COMMENT }}
|
||||
comment-tag: run_id_ui_preview
|
||||
pr-number: ${{ github.event.number }}
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
+41
-92
@@ -1,6 +1,4 @@
|
||||
venv/
|
||||
.venv/
|
||||
.ci_cache
|
||||
.env
|
||||
.clang-format
|
||||
.DS_Store
|
||||
@@ -10,28 +8,14 @@ venv/
|
||||
.overlay_init
|
||||
.overlay_consistent
|
||||
.sconsign.dblite
|
||||
.vscode*
|
||||
model2.png
|
||||
a.out
|
||||
.hypothesis
|
||||
.cache/
|
||||
.host_runtime/
|
||||
.comma_sysroot/
|
||||
.venv-linux-arm64/
|
||||
bin/
|
||||
!openpilot/starpilot/galaxy/bin/
|
||||
|
||||
# created at launch for comma hardware PYTHONPATH (PC uses editable installs via pyproject.toml)
|
||||
/msgq
|
||||
/opendbc
|
||||
/rednose
|
||||
/teleoprtc
|
||||
/tinygrad
|
||||
/jetlink
|
||||
|
||||
*.mp4
|
||||
*.dylib
|
||||
*.DSYM
|
||||
*.d
|
||||
*.pem
|
||||
*.pyc
|
||||
*.pyo
|
||||
.*.swp
|
||||
@@ -44,94 +28,59 @@ bin/
|
||||
*.os-*
|
||||
*.so
|
||||
*.a
|
||||
st[0-9A-Za-z][0-9A-Za-z][0-9A-Za-z][0-9A-Za-z][0-9A-Za-z][0-9A-Za-z]
|
||||
*.unchunked
|
||||
*.clb
|
||||
*.class
|
||||
*.pyxbldc
|
||||
*.vcd
|
||||
*.mo
|
||||
*_pyx.cpp
|
||||
*.stats
|
||||
*.pkl
|
||||
*.pkl*
|
||||
config.json
|
||||
clcache
|
||||
compile_commands.json
|
||||
compare_runtime*.html
|
||||
openpilot/selfdrive/modeld/models/tg_input_devices.json
|
||||
|
||||
# build artifacts
|
||||
docs/_site/
|
||||
openpilot/selfdrive/pandad/pandad
|
||||
openpilot/selfdrive/pandad/tests/test_aol_protocol
|
||||
openpilot/selfdrive/pandad/tests/test_aol_wire
|
||||
openpilot/selfdrive/pandad/tests/test_panda_usb
|
||||
openpilot/cereal/services.h
|
||||
openpilot/cereal/gen
|
||||
openpilot/cereal/messaging/bridge
|
||||
openpilot/selfdrive/ui/translations/tmp
|
||||
openpilot/selfdrive/car/tests/cars_dump
|
||||
openpilot/system/camerad/camerad
|
||||
openpilot/system/camerad/test/ae_gray_test
|
||||
persist
|
||||
board/obj/
|
||||
selfdrive/boardd/boardd
|
||||
selfdrive/logcatd/logcatd
|
||||
selfdrive/mapd/default_speeds_by_region.json
|
||||
selfdrive/proclogd/proclogd
|
||||
selfdrive/ui/_ui
|
||||
selfdrive/test/longitudinal_maneuvers/out
|
||||
selfdrive/visiond/visiond
|
||||
selfdrive/loggerd/loggerd
|
||||
selfdrive/loggerd/bootlog
|
||||
selfdrive/sensord/_gpsd
|
||||
selfdrive/sensord/_sensord
|
||||
selfdrive/camerad/camerad
|
||||
selfdrive/camerad/test/ae_gray_test
|
||||
selfdrive/modeld/_modeld
|
||||
selfdrive/modeld/_dmonitoringmodeld
|
||||
/src/
|
||||
|
||||
one
|
||||
/body/
|
||||
openpilot
|
||||
notebooks
|
||||
xx
|
||||
yy
|
||||
hyperthneed
|
||||
panda_jungle
|
||||
provisioning
|
||||
|
||||
.coverage*
|
||||
coverage.xml
|
||||
htmlcov
|
||||
pandaextra
|
||||
|
||||
.mypy_cache/
|
||||
flycheck_*
|
||||
|
||||
cppcheck_report.txt
|
||||
comma*.sh
|
||||
|
||||
selfdrive/modeld/thneed/compile
|
||||
selfdrive/modeld/models/*.thneed
|
||||
|
||||
# openpilot log files
|
||||
*.bz2
|
||||
*.zst
|
||||
*.rlog
|
||||
|
||||
build/
|
||||
|
||||
!**/.gitkeep
|
||||
|
||||
|
||||
### VisualStudioCode ###
|
||||
*.vsix
|
||||
.history
|
||||
.ionide
|
||||
.vscode/*
|
||||
.history/
|
||||
!.vscode/settings.json
|
||||
!.vscode/tasks.json
|
||||
!.vscode/launch.json
|
||||
!.vscode/extensions.json
|
||||
!.vscode/*.code-snippets
|
||||
|
||||
# agents
|
||||
.claude/
|
||||
.context/
|
||||
PLAN.md
|
||||
TASK.md
|
||||
|
||||
# Disk-backed model cache reconstructed from published numbered parts.
|
||||
openpilot/selfdrive/modeld/models/*.unchunked
|
||||
|
||||
# Publish only small compiled model packages, never raw pickles or caches.
|
||||
!openpilot/selfdrive/modeld/models/dmonitoring_model_tinygrad.pkl.chunk[0-9][0-9]of[0-9][0-9]
|
||||
!openpilot/selfdrive/modeld/models/dmonitoring_model_tinygrad.pkl.chunk[0-9][0-9]of[0-9][0-9][0-9]
|
||||
!openpilot/selfdrive/modeld/models/dmonitoring_model_tinygrad.pkl.chunk[0-9][0-9]of[0-9][0-9][0-9][0-9]
|
||||
!openpilot/selfdrive/modeld/models/dmonitoring_model_tinygrad.pkl.chunk[0-9][0-9][0-9]of[0-9][0-9]
|
||||
!openpilot/selfdrive/modeld/models/dmonitoring_model_tinygrad.pkl.chunk[0-9][0-9][0-9]of[0-9][0-9][0-9]
|
||||
!openpilot/selfdrive/modeld/models/dmonitoring_model_tinygrad.pkl.chunk[0-9][0-9][0-9]of[0-9][0-9][0-9][0-9]
|
||||
!openpilot/selfdrive/modeld/models/dmonitoring_model_tinygrad.pkl.chunk[0-9][0-9][0-9][0-9]of[0-9][0-9]
|
||||
!openpilot/selfdrive/modeld/models/dmonitoring_model_tinygrad.pkl.chunk[0-9][0-9][0-9][0-9]of[0-9][0-9][0-9]
|
||||
!openpilot/selfdrive/modeld/models/dmonitoring_model_tinygrad.pkl.chunk[0-9][0-9][0-9][0-9]of[0-9][0-9][0-9][0-9]
|
||||
!openpilot/selfdrive/modeld/models/dmonitoring_model_tinygrad.pkl.chunkmanifest
|
||||
!openpilot/selfdrive/modeld/models/dmonitoring_model_tinygrad.pkl.chunksha256
|
||||
!openpilot/selfdrive/modeld/models/rdf43_driving_tinygrad.pkl.chunk[0-9][0-9]of[0-9][0-9]
|
||||
!openpilot/selfdrive/modeld/models/rdf43_driving_tinygrad.pkl.chunk[0-9][0-9]of[0-9][0-9][0-9]
|
||||
!openpilot/selfdrive/modeld/models/rdf43_driving_tinygrad.pkl.chunk[0-9][0-9]of[0-9][0-9][0-9][0-9]
|
||||
!openpilot/selfdrive/modeld/models/rdf43_driving_tinygrad.pkl.chunk[0-9][0-9][0-9]of[0-9][0-9]
|
||||
!openpilot/selfdrive/modeld/models/rdf43_driving_tinygrad.pkl.chunk[0-9][0-9][0-9]of[0-9][0-9][0-9]
|
||||
!openpilot/selfdrive/modeld/models/rdf43_driving_tinygrad.pkl.chunk[0-9][0-9][0-9]of[0-9][0-9][0-9][0-9]
|
||||
!openpilot/selfdrive/modeld/models/rdf43_driving_tinygrad.pkl.chunk[0-9][0-9][0-9][0-9]of[0-9][0-9]
|
||||
!openpilot/selfdrive/modeld/models/rdf43_driving_tinygrad.pkl.chunk[0-9][0-9][0-9][0-9]of[0-9][0-9][0-9]
|
||||
!openpilot/selfdrive/modeld/models/rdf43_driving_tinygrad.pkl.chunk[0-9][0-9][0-9][0-9]of[0-9][0-9][0-9][0-9]
|
||||
!openpilot/selfdrive/modeld/models/rdf43_driving_tinygrad.pkl.chunkmanifest
|
||||
!openpilot/selfdrive/modeld/models/rdf43_driving_tinygrad.pkl.chunksha256
|
||||
|
||||
# Jetlink editable-install metadata.
|
||||
/jetlink_repo/jetlink.egg-info/
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
3.12.13
|
||||
Vendored
-8
@@ -1,8 +0,0 @@
|
||||
{
|
||||
"recommendations": [
|
||||
"ms-python.python",
|
||||
"ms-vscode.cpptools",
|
||||
"elagil.pre-commit-helper",
|
||||
"charliermarsh.ruff",
|
||||
]
|
||||
}
|
||||
Vendored
-88
@@ -1,88 +0,0 @@
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"inputs": [
|
||||
{
|
||||
"id": "python_process",
|
||||
"type": "pickString",
|
||||
"description": "Select the process to debug",
|
||||
"options": [
|
||||
"openpilot/selfdrive/controls/controlsd.py",
|
||||
"openpilot/system/timed/timed.py",
|
||||
"openpilot/tools/sim/run_bridge.py"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "cpp_process",
|
||||
"type": "pickString",
|
||||
"description": "Select the process to debug",
|
||||
"options": [
|
||||
"openpilot/selfdrive/ui/ui"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "args",
|
||||
"description": "Arguments to pass to the process",
|
||||
"type": "promptString"
|
||||
},
|
||||
{
|
||||
"id": "replayArg",
|
||||
"type": "promptString",
|
||||
"description": "Enter route or segment to replay."
|
||||
}
|
||||
],
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Python: openpilot Process",
|
||||
"type": "debugpy",
|
||||
"request": "launch",
|
||||
"program": "${input:python_process}",
|
||||
"console": "integratedTerminal",
|
||||
"justMyCode": true,
|
||||
"args": "${input:args}"
|
||||
},
|
||||
{
|
||||
"name": "C++: openpilot Process",
|
||||
"type": "cppdbg",
|
||||
"request": "launch",
|
||||
"program": "${workspaceFolder}/${input:cpp_process}",
|
||||
"cwd": "${workspaceFolder}"
|
||||
},
|
||||
{
|
||||
"name": "Attach LLDB to Replay drive",
|
||||
"type": "lldb",
|
||||
"request": "attach",
|
||||
"pid": "${command:pickMyProcess}",
|
||||
"sourceMap": {
|
||||
".": "${workspaceFolder}/opendbc/safety"
|
||||
},
|
||||
"initCommands": [
|
||||
"script import time; time.sleep(3)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Replay drive",
|
||||
"type": "debugpy",
|
||||
"request": "launch",
|
||||
"program": "${workspaceFolder}/opendbc/safety/tests/safety_replay/replay_drive.py",
|
||||
"args": [
|
||||
"${input:replayArg}"
|
||||
],
|
||||
"console": "integratedTerminal",
|
||||
"justMyCode": false,
|
||||
"env": {
|
||||
"PYTHONPATH": "${workspaceFolder}"
|
||||
},
|
||||
"subProcess": true,
|
||||
"stopOnEntry": false
|
||||
}
|
||||
],
|
||||
"compounds": [
|
||||
{
|
||||
"name": "Replay drive + Safety LLDB",
|
||||
"configurations": [
|
||||
"Replay drive",
|
||||
"Attach LLDB to Replay drive"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
Vendored
-21
@@ -1,21 +0,0 @@
|
||||
{
|
||||
"editor.tabSize": 2,
|
||||
"editor.insertSpaces": true,
|
||||
"editor.renderWhitespace": "trailing",
|
||||
"files.trimTrailingWhitespace": true,
|
||||
"search.exclude": {
|
||||
"**/.git": true,
|
||||
"**/.venv": true,
|
||||
"**/__pycache__": true
|
||||
},
|
||||
"files.exclude": {
|
||||
"**/.git": true,
|
||||
"**/.venv": true,
|
||||
"**/__pycache__": true
|
||||
},
|
||||
"python.analysis.exclude": [
|
||||
"**/.git",
|
||||
"**/.venv",
|
||||
"**/__pycache__",
|
||||
]
|
||||
}
|
||||
-19
@@ -1,19 +0,0 @@
|
||||
# Source provenance
|
||||
|
||||
## Jetlink external inference
|
||||
|
||||
The comma-side Jetlink client and USB transport in `jetlink_repo` are by Zeph Leggett and the [Jetlink contributors](https://github.com/zoompilot/jetlink). They are vendored from revision `c17cd5c52be0ee83b9a6265b7da69d24ad6c5d02` under the MIT license, retained in [jetlink_repo/LICENSE](jetlink_repo/LICENSE). StarPilot owns the adapter to its model runner, settings and process lifecycle. Companion applications and inference servers remain distributed by Jetlink.
|
||||
|
||||
## MRR35 Hyundai angle and radar
|
||||
|
||||
The four MRR35 angle platforms (2026 Genesis GV70, second-generation electrified GV70, 2025 Genesis GV80, and Hyundai Ioniq 9) adapt Hyundai CAN FD angle-control design and the MRR35 radar DBC generator from the locally frozen StarPilot source snapshot `678af783`. That snapshot traces its Hyundai extension work to sunnypilot/opendbc `hkg-angle-steering-2025` at `cc4b08625a98e94b318cab15e45e05dad58042bd`; its historical import revision is not established. The relevant upstream contributors include Haibin (Jason) Wen, Shane Smiskol, and DevTekVE. Their authorship remains attributable to the upstream history; they do not maintain or endorse this port.
|
||||
|
||||
The copied MRR35 generator source and DBC definition are at `opendbc_repo/opendbc/dbc/generator/hyundai/hyundai_mrr35_radar.py` and `.dbc`. The host angle adapter and native safety profile are adaptations, not copies of a generic DBC or of unrelated vehicle support. Applicable upstream notices are preserved in [THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md).
|
||||
|
||||
## Ford support adapted from BluePilot
|
||||
|
||||
The Ford lateral strategies, extended CAN constructors, camera-radar filtering and native checks are adapted from StarPilot `2efe6017bfe14cee4807833e05f9a7f6ba255cdb`. Their lineage includes BluePilot's `bp-7.0` Ford work, principally by Alan Polk, with contributions from John Christman, Jacob Neulight, tonesto7, Haibin Wen and other sunnypilot and BluePilot contributors. The reference snapshot is [`e1d051d7ba270261b4455068bd68f1a58db15a4a`](https://github.com/BluePilotDev/bluepilot/commit/e1d051d7ba270261b4455068bd68f1a58db15a4a); the exact checkout used for the earlier StarPilot import was not recorded.
|
||||
|
||||
Relevant lineage includes the controller and integration work in [`db2bdff05`](https://github.com/BluePilotDev/bluepilot/commit/db2bdff05df103d71df62f45c2a3cb5211aba6e6) and [`d0aac605f`](https://github.com/BluePilotDev/bluepilot/commit/d0aac605f99d37e9da205e419f7989c1e9eaa386), extended CAN and safety work in [`8f8d6d15f`](https://github.com/BluePilotDev/bluepilot/commit/8f8d6d15f0a590f42b78de964ffb0d0af7f5d63d), and manual-turn detection in [`97867c1eb`](https://github.com/BluePilotDev/bluepilot/commit/97867c1eb57b7472f6fc3de62f0fef576e5a5497).
|
||||
|
||||
The vehicle strategy and CAN/native owners preserve that attribution while adapting to the current controller interfaces and shared safety limits. Upstream contributors do not maintain or endorse this adaptation. Applicable notices are retained in [THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md).
|
||||
Vendored
+106
-281
@@ -1,320 +1,145 @@
|
||||
def retryWithDelay(int maxRetries, int delay, Closure body) {
|
||||
for (int i = 0; i < maxRetries; i++) {
|
||||
try {
|
||||
return body()
|
||||
} catch (Exception e) {
|
||||
sleep(delay)
|
||||
}
|
||||
}
|
||||
throw Exception("Failed after ${maxRetries} retries")
|
||||
}
|
||||
|
||||
def device(String ip, String step_label, String cmd) {
|
||||
def phone(String ip, String step_label, String cmd) {
|
||||
withCredentials([file(credentialsId: 'id_rsa', variable: 'key_file')]) {
|
||||
def ssh_cmd = """
|
||||
ssh -o ControlMaster=no -o ControlPath=none -o ConnectTimeout=5 -o ServerAliveInterval=5 -o ServerAliveCountMax=12 -o BatchMode=yes -o StrictHostKeyChecking=no -i ${key_file} 'comma@${ip}' exec setpriv --pdeathsig HUP /usr/bin/bash <<'END'
|
||||
ssh -tt -o StrictHostKeyChecking=no -i ${key_file} 'comma@${ip}' /usr/bin/bash <<'END'
|
||||
|
||||
set -e
|
||||
|
||||
export TERM=xterm-256color
|
||||
|
||||
trap 'kill 0' HUP # stop this process group on SSH disconnect
|
||||
|
||||
export CI=1
|
||||
export PYTHONWARNINGS=error
|
||||
export PYTHONFAULTHANDLER=1
|
||||
export COMMA_CACHE=/data/tmp/comma_download_cache
|
||||
#export LOGPRINT=debug # this has gotten too spammy...
|
||||
export TEST_DIR=${env.TEST_DIR}
|
||||
export SOURCE_DIR=${env.SOURCE_DIR}
|
||||
export GIT_BRANCH=${env.GIT_BRANCH}
|
||||
export GIT_COMMIT=${env.GIT_COMMIT}
|
||||
export CI_ARTIFACTS_TOKEN=${env.CI_ARTIFACTS_TOKEN}
|
||||
export GITHUB_COMMENTS_TOKEN=${env.GITHUB_COMMENTS_TOKEN}
|
||||
export AZURE_TOKEN='${env.AZURE_TOKEN}'
|
||||
# only use 1 thread since most require real hardware that can't be shared
|
||||
export PYTEST_ADDOPTS="-n0 -s"
|
||||
|
||||
|
||||
export GIT_SSH_COMMAND="ssh -i /data/gitkey"
|
||||
|
||||
source ~/.bash_profile
|
||||
if [ -f /AGNOS ]; then
|
||||
if [ -f /TICI ]; then
|
||||
source /etc/profile
|
||||
|
||||
rm -rf /tmp/tmp*
|
||||
rm -rf ~/.commacache
|
||||
rm -rf /dev/shm/*
|
||||
rm -rf /dev/tmp/tmp*
|
||||
|
||||
if ! systemctl is-active --quiet systemd-resolved; then
|
||||
echo "restarting resolved"
|
||||
sudo systemctl start systemd-resolved
|
||||
sleep 3
|
||||
fi
|
||||
|
||||
# restart aux USB
|
||||
if [ -e /sys/bus/usb/drivers/hub/3-0:1.0 ]; then
|
||||
echo "restarting aux usb"
|
||||
echo "3-0:1.0" | sudo tee /sys/bus/usb/drivers/hub/unbind
|
||||
sleep 0.5
|
||||
echo "3-0:1.0" | sudo tee /sys/bus/usb/drivers/hub/bind
|
||||
fi
|
||||
fi
|
||||
if [ -f /data/openpilot/launch_env.sh ]; then
|
||||
source /data/openpilot/launch_env.sh
|
||||
fi
|
||||
|
||||
export LD_LIBRARY_PATH="\$(python -c 'import ffmpeg; print(ffmpeg.LIB_DIR)'):/usr/local/lib:\${LD_LIBRARY_PATH:-}"
|
||||
|
||||
ln -snf ${env.TEST_DIR} /data/pythonpath
|
||||
|
||||
cd ${env.TEST_DIR} || true
|
||||
time ( ${cmd} ) &
|
||||
wait \$!
|
||||
${cmd}
|
||||
exit 0
|
||||
|
||||
END"""
|
||||
|
||||
sh script: ssh_cmd, label: step_label
|
||||
}
|
||||
}
|
||||
|
||||
def deviceStage(String stageName, String deviceType, List extra_env, def steps) {
|
||||
stage(stageName) {
|
||||
if (currentBuild.result != null) {
|
||||
return
|
||||
def phone_steps(String device_type, steps) {
|
||||
lock(resource: "", label: device_type, inversePrecedence: true, variable: 'device_ip', quantity: 1) {
|
||||
timeout(time: 20, unit: 'MINUTES') {
|
||||
phone(device_ip, "git checkout", readFile("selfdrive/test/setup_device_ci.sh"),)
|
||||
steps.each { item ->
|
||||
phone(device_ip, item[0], item[1])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pipeline {
|
||||
agent none
|
||||
environment {
|
||||
TEST_DIR = "/data/openpilot"
|
||||
SOURCE_DIR = "/data/openpilot_source/"
|
||||
}
|
||||
options {
|
||||
timeout(time: 4, unit: 'HOURS')
|
||||
}
|
||||
|
||||
stages {
|
||||
stage('build release3') {
|
||||
agent { docker { image 'ghcr.io/commaai/alpine-ssh'; args '--user=root' } }
|
||||
when {
|
||||
branch 'devel-staging'
|
||||
}
|
||||
steps {
|
||||
phone_steps("tici", [
|
||||
["build release3-staging & dashcam3-staging", "PUSH=1 $SOURCE_DIR/release/build_release.sh"],
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
if (isReplay()) {
|
||||
error("REPLAYING TESTS IS NOT ALLOWED. FIX THEM INSTEAD.")
|
||||
}
|
||||
|
||||
def extra = extra_env.collect { "export ${it}" }.join('\n');
|
||||
def branch = env.BRANCH_NAME ?: 'master';
|
||||
def gitDiff = sh returnStdout: true, script: 'curl -s -H "Authorization: Bearer ${GITHUB_COMMENTS_TOKEN}" https://api.github.com/repos/commaai/openpilot/compare/master...${GIT_BRANCH} | jq .files[].filename || echo "/"', label: 'Getting changes'
|
||||
|
||||
lock(resource: "", label: deviceType, inversePrecedence: true, variable: 'device_ip', quantity: 1, resourceSelectStrategy: 'random') {
|
||||
docker.image('ghcr.io/commaai/alpine-ssh').inside('--user=root') {
|
||||
timeout(time: 35, unit: 'MINUTES') {
|
||||
retry (3) {
|
||||
def date = sh(script: 'date', returnStdout: true).trim();
|
||||
device(device_ip, "set time", "date -s '" + date + "'")
|
||||
device(device_ip, "git checkout", extra + "\n" + readFile("openpilot/selfdrive/test/setup_device_ci.sh"))
|
||||
stage('openpilot tests') {
|
||||
when {
|
||||
not {
|
||||
anyOf {
|
||||
branch 'master-ci'; branch 'devel'; branch 'devel-staging';
|
||||
branch 'release2'; branch 'release2-staging'; branch 'dashcam'; branch 'dashcam-staging';
|
||||
branch 'release3'; branch 'release3-staging'; branch 'dashcam3'; branch 'dashcam3-staging';
|
||||
branch 'testing-closet*'; branch 'hotfix-*'
|
||||
}
|
||||
steps.each { item ->
|
||||
def name = item[0]
|
||||
def cmd = item[1]
|
||||
}
|
||||
}
|
||||
|
||||
def args = item[2]
|
||||
def diffPaths = args.diffPaths ?: []
|
||||
def cmdTimeout = args.timeout ?: 9999
|
||||
stages {
|
||||
stage('On-device Tests') {
|
||||
agent { docker { image 'ghcr.io/commaai/alpine-ssh'; args '--user=root' } }
|
||||
stages {
|
||||
stage('parallel tests') {
|
||||
parallel {
|
||||
stage('build') {
|
||||
environment {
|
||||
R3_PUSH = "${env.BRANCH_NAME == 'master' ? '1' : ' '}"
|
||||
}
|
||||
steps {
|
||||
phone_steps("tici", [
|
||||
["build master-ci", "cd $SOURCE_DIR/release && TARGET_DIR=$TEST_DIR EXTRA_FILES='tools/' ./build_devel.sh"],
|
||||
["build openpilot", "cd selfdrive/manager && ./build.py"],
|
||||
["test manager", "python selfdrive/manager/test/test_manager.py"],
|
||||
["onroad tests", "cd selfdrive/test/ && ./test_onroad.py"],
|
||||
["test car interfaces", "cd selfdrive/car/tests/ && ./test_car_interfaces.py"],
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
stage('HW + Unit Tests') {
|
||||
steps {
|
||||
phone_steps("tici2", [
|
||||
["build", "cd selfdrive/manager && ./build.py"],
|
||||
["test power draw", "python selfdrive/hardware/tici/test_power_draw.py"],
|
||||
["test boardd loopback", "python selfdrive/boardd/tests/test_boardd_loopback.py"],
|
||||
["test loggerd", "python selfdrive/loggerd/tests/test_loggerd.py"],
|
||||
["test encoder", "LD_LIBRARY_PATH=/usr/local/lib python selfdrive/loggerd/tests/test_encoder.py"],
|
||||
["test sensord", "python selfdrive/sensord/test/test_sensord.py"],
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
stage('camerad') {
|
||||
steps {
|
||||
phone_steps("tici-party", [
|
||||
["build", "cd selfdrive/manager && ./build.py"],
|
||||
["test camerad", "python selfdrive/camerad/test/test_camerad.py"],
|
||||
["test exposure", "python selfdrive/camerad/test/test_exposure.py"],
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
stage('replay') {
|
||||
steps {
|
||||
phone_steps("tici3", [
|
||||
["build", "cd selfdrive/manager && ./build.py"],
|
||||
["model replay", "cd selfdrive/test/process_replay && ./model_replay.py"],
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
if (branch != "master" && !branch.contains("__jenkins_loop_") && diffPaths && !hasPathChanged(gitDiff, diffPaths)) {
|
||||
println "Skipping ${name}: no changes in ${diffPaths}."
|
||||
return
|
||||
} else {
|
||||
timeout(time: cmdTimeout, unit: 'SECONDS') {
|
||||
device(device_ip, name, cmd)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def hasPathChanged(String gitDiff, List<String> paths) {
|
||||
for (path in paths) {
|
||||
if (gitDiff.contains(path)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
def isReplay() {
|
||||
def replayClass = "org.jenkinsci.plugins.workflow.cps.replay.ReplayCause"
|
||||
return currentBuild.rawBuild.getCauses().any{ cause -> cause.toString().contains(replayClass) }
|
||||
}
|
||||
|
||||
def setupCredentials() {
|
||||
withCredentials([
|
||||
string(credentialsId: 'azure_token', variable: 'AZURE_TOKEN'),
|
||||
]) {
|
||||
env.AZURE_TOKEN = "${AZURE_TOKEN}"
|
||||
}
|
||||
|
||||
withCredentials([
|
||||
string(credentialsId: 'ci_artifacts_pat', variable: 'CI_ARTIFACTS_TOKEN'),
|
||||
]) {
|
||||
env.CI_ARTIFACTS_TOKEN = "${CI_ARTIFACTS_TOKEN}"
|
||||
}
|
||||
|
||||
withCredentials([
|
||||
string(credentialsId: 'post_comments_github_pat', variable: 'GITHUB_COMMENTS_TOKEN'),
|
||||
]) {
|
||||
env.GITHUB_COMMENTS_TOKEN = "${GITHUB_COMMENTS_TOKEN}"
|
||||
}
|
||||
}
|
||||
|
||||
def step(String name, String cmd, Map args = [:]) {
|
||||
return [name, cmd, args]
|
||||
}
|
||||
|
||||
def chestnutStage(String name = "chestnut") {
|
||||
deviceStage(name, "mici-chestnut-ci", ["UNSAFE=1", "CHESTNUT=1"], [
|
||||
step("build", "./openpilot/selfdrive/test/chestnut.sh"),
|
||||
step("model replay", "openpilot/selfdrive/test/process_replay/model_replay.py --chestnut"),
|
||||
step("onroad tests", "./openpilot/selfdrive/test/test_onroad.py TestChestnutOnroad", [timeout: 120]),
|
||||
])
|
||||
}
|
||||
|
||||
node {
|
||||
env.CI = "1"
|
||||
env.PYTHONWARNINGS = "error"
|
||||
env.TEST_DIR = "/data/openpilot"
|
||||
env.SOURCE_DIR = "/data/openpilot_source/"
|
||||
setupCredentials()
|
||||
|
||||
env.GIT_BRANCH = checkout(scm).GIT_BRANCH
|
||||
env.GIT_COMMIT = checkout(scm).GIT_COMMIT
|
||||
|
||||
if (env.JOB_BASE_NAME == 'chestnut-stresstest') {
|
||||
sh 'git fetch --no-tags origin nightly-chestnut'
|
||||
env.GIT_COMMIT = sh(script: 'git show FETCH_HEAD:git_src_commit', returnStdout: true).trim()
|
||||
if (!(env.GIT_COMMIT ==~ /[0-9a-f]{40}/)) {
|
||||
error("invalid nightly-chestnut source commit")
|
||||
}
|
||||
def results = []
|
||||
try {
|
||||
timeout(time: 12, unit: 'HOURS') {
|
||||
for (int run = 1; run <= 100; run++) {
|
||||
results << "run ${run}: INCOMPLETE"
|
||||
try {
|
||||
chestnutStage("chestnut ${run}/100")
|
||||
results[run - 1] = "run ${run}: PASS"
|
||||
} catch (hudson.AbortException e) {
|
||||
results[run - 1] = "run ${run}: FAIL"
|
||||
echo e.toString()
|
||||
post {
|
||||
always {
|
||||
cleanWs()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
} finally {
|
||||
writeFile file: 'chestnut_stress_report.txt', text: "commit: ${env.GIT_COMMIT}\ncompleted: ${results.count { !it.endsWith('INCOMPLETE') }}/100\n" + results.join('\n') + '\n'
|
||||
archiveArtifacts artifacts: 'chestnut_stress_report.txt'
|
||||
}
|
||||
if (results.any { !it.endsWith('PASS') }) {
|
||||
error("chestnut stress test failed")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
def excludeBranches = ['__nightly', '__nightly-chestnut', 'devel', 'devel-staging',
|
||||
'release-tizi', 'release-tizi-staging', 'release-mici', 'release-mici-staging', 'testing-closet*', 'hotfix-*']
|
||||
def excludeRegex = excludeBranches.join('|').replaceAll('\\*', '.*')
|
||||
|
||||
if (env.BRANCH_NAME != 'master' && !env.BRANCH_NAME.contains('__jenkins_loop_')) {
|
||||
properties([
|
||||
disableConcurrentBuilds(abortPrevious: true)
|
||||
])
|
||||
}
|
||||
|
||||
try {
|
||||
if (env.BRANCH_NAME == 'devel-staging') {
|
||||
deviceStage("build release-tizi-staging", "tizi-needs-can", [], [
|
||||
step("build release-tizi-staging", "RELEASE_BRANCH=release-tizi-staging,release-mici-staging $SOURCE_DIR/tools/release/build_release.sh"),
|
||||
])
|
||||
}
|
||||
|
||||
if (env.BRANCH_NAME == '__nightly') {
|
||||
parallel (
|
||||
'nightly': {
|
||||
deviceStage("build nightly", "tizi-needs-can", [], [
|
||||
step("build nightly", "RELEASE_BRANCH=nightly $SOURCE_DIR/tools/release/build_release.sh"),
|
||||
])
|
||||
},
|
||||
'nightly-dev': {
|
||||
deviceStage("build nightly-dev", "tizi-needs-can", [], [
|
||||
step("build nightly-dev", "PANDA_DEBUG_BUILD=1 RELEASE_BRANCH=nightly-dev $SOURCE_DIR/tools/release/build_release.sh"),
|
||||
])
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
if (env.BRANCH_NAME == '__nightly-chestnut') {
|
||||
parallel (
|
||||
'nightly-chestnut': {
|
||||
deviceStage("build nightly-chestnut", "mici-chestnut-ci", ["CHESTNUT=1"], [
|
||||
step("build nightly-chestnut", "SCONSFLAGS=-j4 INCLUDE_BIG_MODEL=1 RELEASE_BRANCH=nightly-chestnut $SOURCE_DIR/tools/release/build_release.sh TestChestnutOnroad"),
|
||||
])
|
||||
},
|
||||
'nightly-chestnut-dev': {
|
||||
deviceStage("build nightly-chestnut-dev", "mici-chestnut-ci", ["CHESTNUT=1"], [
|
||||
step("build nightly-chestnut-dev", "SCONSFLAGS=-j4 INCLUDE_BIG_MODEL=1 PANDA_DEBUG_BUILD=1 RELEASE_BRANCH=nightly-chestnut-dev $SOURCE_DIR/tools/release/build_release.sh TestChestnutOnroad"),
|
||||
])
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
if (!env.BRANCH_NAME.matches(excludeRegex)) {
|
||||
parallel (
|
||||
'onroad tests': {
|
||||
deviceStage("onroad", "tizi-needs-can", ["UNSAFE=1"], [
|
||||
step("build openpilot", "cd openpilot/system/manager && ./build.py"),
|
||||
step("check dirty", "tools/release/check-dirty.sh"),
|
||||
step("onroad tests", "./openpilot/selfdrive/test/test_onroad.py", [timeout: 60]),
|
||||
])
|
||||
},
|
||||
'HW + Unit Tests': {
|
||||
deviceStage("tizi-hardware", "tizi-common", ["UNSAFE=1"], [
|
||||
step("build", "cd openpilot/system/manager && ./build.py"),
|
||||
step("test power draw", "./openpilot/selfdrive/test/test_power_draw.py"),
|
||||
step("test encoder", "./openpilot/system/loggerd/tests/test_encoder.py", [diffPaths: ["openpilot/system/loggerd/"]]),
|
||||
step("test manager", "./openpilot/system/manager/test/test_manager.py"),
|
||||
])
|
||||
},
|
||||
'camerad OX03C10': {
|
||||
deviceStage("OX03C10", "tizi-ox03c10", ["UNSAFE=1"], [
|
||||
step("build", "cd openpilot/system/manager && ./build.py"),
|
||||
step("test pandad", "./openpilot/selfdrive/pandad/tests/test_pandad.py"),
|
||||
step("test camerad", "./openpilot/system/camerad/test/test_camerad.py", [timeout: 90]),
|
||||
])
|
||||
},
|
||||
'camerad OS04C10': {
|
||||
deviceStage("OS04C10", "tici-os04c10", ["UNSAFE=1"], [
|
||||
step("build", "cd openpilot/system/manager && ./build.py"),
|
||||
step("test pandad", "./openpilot/selfdrive/pandad/tests/test_pandad.py"),
|
||||
step("test camerad", "./openpilot/system/camerad/test/test_camerad.py", [timeout: 90]),
|
||||
])
|
||||
},
|
||||
'sensord': {
|
||||
deviceStage("LSM + MMC", "tizi-lsmc", ["UNSAFE=1"], [
|
||||
step("build", "cd openpilot/system/manager && ./build.py"),
|
||||
step("test sensord", "./openpilot/system/sensord/tests/test_sensord.py"),
|
||||
])
|
||||
},
|
||||
'replay': {
|
||||
deviceStage("model-replay", "tizi-replay", ["UNSAFE=1"], [
|
||||
step("build", "cd openpilot/system/manager && ./build.py", [diffPaths: ["openpilot/selfdrive/modeld/", "tinygrad_repo", "openpilot/selfdrive/test/process_replay/model_replay.py"]]),
|
||||
step("model replay", "openpilot/selfdrive/test/process_replay/model_replay.py", [diffPaths: ["openpilot/selfdrive/modeld/", "tinygrad_repo", "openpilot/selfdrive/test/process_replay/model_replay.py"]]),
|
||||
])
|
||||
},
|
||||
'tizi': {
|
||||
deviceStage("tizi", "tizi", ["UNSAFE=1"], [
|
||||
step("build openpilot", "cd openpilot/system/manager && ./build.py"),
|
||||
step("test pandad loopback", "./openpilot/selfdrive/pandad/tests/test_pandad_loopback.py"),
|
||||
step("test pandad spi", "./openpilot/selfdrive/pandad/tests/test_pandad_spi.py"),
|
||||
step("test amp", "./openpilot/common/hardware/comma/tests/test_amplifier.py"),
|
||||
])
|
||||
},
|
||||
'chestnut': {
|
||||
chestnutStage()
|
||||
},
|
||||
|
||||
)
|
||||
}
|
||||
} catch (Exception e) {
|
||||
currentBuild.result = 'FAILED'
|
||||
throw e
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,43 +1,142 @@
|
||||
# StarPilot
|
||||

|
||||
|
||||
StarPilot is an independent fork of [openpilot](https://github.com/commaai/openpilot), with custom driving controls, vehicle integrations, raylib interfaces for comma 3 and comma four, and the Galaxy companion interface.
|
||||
Table of Contents
|
||||
=======================
|
||||
|
||||
**Domathon is the development branch for StarPilot 7.0.** It rebuilds StarPilot on a current openpilot foundation. Feature migration and vehicle qualification are still in progress; this branch is not a completed replacement for the existing Dom release.
|
||||
* [What is openpilot?](#what-is-openpilot)
|
||||
* [Running in a car](#running-in-a-car)
|
||||
* [Running on PC](#running-on-pc)
|
||||
* [Community and Contributing](#community-and-contributing)
|
||||
* [User Data and comma Account](#user-data-and-comma-account)
|
||||
* [Safety and Testing](#safety-and-testing)
|
||||
* [Directory Structure](#directory-structure)
|
||||
* [Licensing](#licensing)
|
||||
|
||||
## Explore the project
|
||||
---
|
||||
|
||||
- [Developer commands](tools/STARPILOT_DEVELOPMENT.md): host tools, replay, and device builds.
|
||||
- [Driving interfaces](openpilot/starpilot/ui/README.md) and [Galaxy](openpilot/starpilot/galaxy/README.md): native and companion controls.
|
||||
- [Driving models](openpilot/starpilot/models/README.md): model management, artifact compatibility, and Model Laboratory status.
|
||||
- [Builds and developer tools](docs/how-to/laptop-device-build.md): local development and device builds.
|
||||
- [Dependency maintenance](tools/vendor/README.md): tracked source folders and upstream sync records.
|
||||
What is openpilot?
|
||||
------
|
||||
|
||||
## Development
|
||||
[openpilot](http://github.com/commaai/openpilot) is an open source driver assistance system. Currently, openpilot performs the functions of Adaptive Cruise Control (ACC), Automated Lane Centering (ALC), Forward Collision Warning (FCW) and Lane Departure Warning (LDW) for a growing variety of [supported car makes, models and model years](docs/CARS.md). In addition, while openpilot is engaged, a camera based Driver Monitoring (DM) feature alerts distracted and asleep drivers. See more about [the vehicle integration](docs/INTEGRATION.md) and [limitations](docs/LIMITATIONS.md).
|
||||
|
||||
Dependencies are included as ordinary source folders. No recursive submodule checkout is needed. [upstream-sync.json](upstream-sync.json) records the openpilot baseline and the source revisions used for vendored dependencies.
|
||||
<table>
|
||||
<tr>
|
||||
<td><a href="https://youtu.be/NmBfgOanCyk" title="Video By Greer Viau"><img src="https://i.imgur.com/1w8c6d2.jpg"></a></td>
|
||||
<td><a href="https://youtu.be/VHKyqZ7t8Gw" title="Video By Logan LeGrand"><img src="https://i.imgur.com/LnBucik.jpg"></a></td>
|
||||
<td><a href="https://youtu.be/VxiR4iyBruo" title="Video By Charlie Kim"><img src="https://i.imgur.com/4Qoy48c.jpg"></a></td>
|
||||
<td><a href="https://youtu.be/-IkImTe1NYE" title="Video By Aragon"><img src="https://i.imgur.com/04VNzPf.jpg"></a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="https://youtu.be/iIUICQkdwFQ" title="Video By Logan LeGrand"><img src="https://i.imgur.com/b1LHQTy.jpg"></a></td>
|
||||
<td><a href="https://youtu.be/XOsa0FsVIsg" title="Video By PinoyDrives"><img src="https://i.imgur.com/6FG0Bd8.jpg"></a></td>
|
||||
<td><a href="https://youtu.be/bCwcJ98R_Xw" title="Video By JS"><img src="https://i.imgur.com/zO18CbW.jpg"></a></td>
|
||||
<td><a href="https://youtu.be/BQ0tF3MTyyc" title="Video By Tsai-Fi"><img src="https://i.imgur.com/eZzelq3.jpg"></a></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
The familiar development entry points are retained:
|
||||
|
||||
| Command | Purpose |
|
||||
| --- | --- |
|
||||
| `./build` | Build the device software using the matching AGNOS environment |
|
||||
| `./build --panda` | Build the supported Panda firmware targets |
|
||||
| `./c3` | Open the comma 3 interface on a development host |
|
||||
| `./c4` | Open the comma four interface on a development host |
|
||||
| `./onroad` | Open the onroad development view |
|
||||
| `./dev` | Use the isolated host development environment |
|
||||
| `./test` | Run local pre-push regression checks without changing device artifacts |
|
||||
Running in a car
|
||||
------
|
||||
|
||||
See the [build guide](docs/how-to/laptop-device-build.md) for setup, prerequisites, and target-specific behavior. The [UI guide](openpilot/starpilot/ui/README.md) describes desktop previews and their limits.
|
||||
To use openpilot in a car, you need four things
|
||||
* This software. It's free and available right here.
|
||||
* One of [the 150+ supported cars](docs/CARS.md). We support Honda, Toyota, Hyundai, Nissan, Kia, Chrysler, Lexus, Acura, Audi, VW, and more. If your car is not supported, but has adaptive cruise control and lane keeping assist, it's likely able to run openpilot.
|
||||
* A supported device to run this software: a [comma three](https://comma.ai/shop/products/three), or if you like to experiment, a [Ubuntu computer with webcams](https://github.com/commaai/openpilot/tree/master/tools/webcam).
|
||||
* A way to connect to your car. With a comma three, you need only a [car harness](https://comma.ai/shop/products/car-harness). With a PC, you also need a [black panda](https://comma.ai/shop/products/panda).
|
||||
|
||||
## Testing and vehicle support
|
||||
We have detailed instructions for [how to install the device in a car](https://comma.ai/setup).
|
||||
|
||||
StarPilot retains upstream vehicle and Panda safety interfaces and adds regression coverage for its custom behavior. The [safety workflow](.github/workflows/safety.yaml) and feature tests cover distinct parts of the system. A passing interface test, model comparison, or bench run does not establish driving support for every configuration.
|
||||
Running on PC
|
||||
------
|
||||
|
||||
Support depends on the vehicle configuration and enabled feature. An existing vehicle identifier or settings page does not by itself establish driving support. Model Laboratory currently exposes management controls, but paired driving inference remains unavailable pending integration and Chestnut validation.
|
||||
All of openpilot's services can run as normal on a PC, even without special hardware or a car. To develop or experiment with openpilot you can run openpilot on recorded or simulated data.
|
||||
|
||||
## Upstream and attribution
|
||||
With openpilot's tools you can plot logs, replay drives and watch the full-res camera streams. See [the tools README](tools/README.md) for more information.
|
||||
|
||||
The current openpilot baseline is [`78dccf0b482ea1298fd1f6fa59b0554187b02ce6`](https://github.com/commaai/openpilot/commit/78dccf0b482ea1298fd1f6fa59b0554187b02ce6). Its history remains intact, followed by the StarPilot foundation and feature commits. Upstream development continues independently; the baseline changes only after an update has been integrated and checked.
|
||||
You can also run openpilot in simulation [with the CARLA simulator](tools/sim/README.md). This allows openpilot to drive around a virtual car on your Ubuntu machine. The whole setup should only take a few minutes, but does require a decent GPU.
|
||||
|
||||
See [LICENSE](LICENSE), [THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md), and [CREDITS.md](CREDITS.md) for licensing and source attribution. Component licenses remain with their source folders. StarPilot is maintained independently of comma.
|
||||
|
||||
Community and Contributing
|
||||
------
|
||||
|
||||
openpilot is developed by [comma](https://comma.ai/) and by users like you. We welcome both pull requests and issues on [GitHub](http://github.com/commaai/openpilot). Bug fixes and new car ports are encouraged. Check out [the contributing docs](docs/CONTRIBUTING.md).
|
||||
|
||||
Documentation related to openpilot development can be found on [docs.comma.ai](https://docs.comma.ai). Information about running openpilot (e.g. FAQ, fingerprinting, troubleshooting, custom forks, community hardware) should go on the [wiki](https://github.com/commaai/openpilot/wiki).
|
||||
|
||||
You can add support for your car by following guides we have written for [Brand](https://blog.comma.ai/how-to-write-a-car-port-for-openpilot/) and [Model](https://blog.comma.ai/openpilot-port-guide-for-toyota-models/) ports. Generally, a car with adaptive cruise control and lane keep assist is a good candidate. [Join our Discord](https://discord.comma.ai) to discuss car ports: most car makes have a dedicated channel.
|
||||
|
||||
Want to get paid to work on openpilot? [comma is hiring](https://comma.ai/jobs/).
|
||||
|
||||
And [follow us on Twitter](https://twitter.com/comma_ai).
|
||||
|
||||
User Data and comma Account
|
||||
------
|
||||
|
||||
By default, openpilot uploads the driving data to our servers. You can also access your data through [comma connect](https://connect.comma.ai/). We use your data to train better models and improve openpilot for everyone.
|
||||
|
||||
openpilot is open source software: the user is free to disable data collection if they wish to do so.
|
||||
|
||||
openpilot logs the road facing cameras, CAN, GPS, IMU, magnetometer, thermal sensors, crashes, and operating system logs.
|
||||
The driver facing camera is only logged if you explicitly opt-in in settings. The microphone is not recorded.
|
||||
|
||||
By using openpilot, you agree to [our Privacy Policy](https://comma.ai/privacy). You understand that use of this software or its related services will generate certain types of user data, which may be logged and stored at the sole discretion of comma. By accepting this agreement, you grant an irrevocable, perpetual, worldwide right to comma for the use of this data.
|
||||
|
||||
Safety and Testing
|
||||
----
|
||||
|
||||
* openpilot observes ISO26262 guidelines, see [SAFETY.md](docs/SAFETY.md) for more details.
|
||||
* openpilot has software in the loop [tests](.github/workflows/selfdrive_tests.yaml) that run on every commit.
|
||||
* The code enforcing the safety model lives in panda and is written in C, see [code rigor](https://github.com/commaai/panda#code-rigor) for more details.
|
||||
* panda has software in the loop [safety tests](https://github.com/commaai/panda/tree/master/tests/safety).
|
||||
* Internally, we have a hardware in the loop Jenkins test suite that builds and unit tests the various processes.
|
||||
* panda has additional hardware in the loop [tests](https://github.com/commaai/panda/blob/master/Jenkinsfile).
|
||||
* We run the latest openpilot in a testing closet containing 10 comma devices continuously replaying routes.
|
||||
|
||||
Directory Structure
|
||||
------
|
||||
.
|
||||
├── cereal # The messaging spec and libs used for all logs
|
||||
├── common # Library like functionality we've developed here
|
||||
├── docs # Documentation
|
||||
├── opendbc # Files showing how to interpret data from cars
|
||||
├── panda # Code used to communicate on CAN
|
||||
├── third_party # External libraries
|
||||
├── pyextra # Extra python packages
|
||||
└── selfdrive # Code needed to drive the car
|
||||
├── assets # Fonts, images, and sounds for UI
|
||||
├── athena # Allows communication with the app
|
||||
├── boardd # Daemon to talk to the board
|
||||
├── camerad # Driver to capture images from the camera sensors
|
||||
├── car # Car specific code to read states and control actuators
|
||||
├── common # Shared C/C++ code for the daemons
|
||||
├── controls # Planning and controls
|
||||
├── debug # Tools to help you debug and do car ports
|
||||
├── locationd # Precise localization and vehicle parameter estimation
|
||||
├── logcatd # Android logcat as a service
|
||||
├── loggerd # Logger and uploader of car data
|
||||
├── modeld # Driving and monitoring model runners
|
||||
├── proclogd # Logs information from proc
|
||||
├── sensord # IMU interface code
|
||||
├── test # Unit tests, system tests, and a car simulator
|
||||
└── ui # The UI
|
||||
|
||||
Licensing
|
||||
------
|
||||
|
||||
openpilot is released under the MIT license. Some parts of the software are released under other licenses as specified.
|
||||
|
||||
Any user of this software shall indemnify and hold harmless Comma.ai, Inc. and its directors, officers, employees, agents, stockholders, affiliates, subcontractors and customers from and against all allegations, claims, actions, suits, demands, damages, liabilities, obligations, losses, settlements, judgments, costs and expenses (including without limitation attorneys’ fees and costs) which arise out of, relate to or result from any use of this software by user.
|
||||
|
||||
**THIS IS ALPHA QUALITY SOFTWARE FOR RESEARCH PURPOSES ONLY. THIS IS NOT A PRODUCT.
|
||||
YOU ARE RESPONSIBLE FOR COMPLYING WITH LOCAL LAWS AND REGULATIONS.
|
||||
NO WARRANTY EXPRESSED OR IMPLIED.**
|
||||
|
||||
---
|
||||
|
||||
<img src="https://d1qb2nb5cznatu.cloudfront.net/startups/i/1061157-bc7e9bf3b246ece7322e6ffe653f6af8-medium_jpg.jpg?buster=1458363130" width="75"></img> <img src="https://cdn-images-1.medium.com/max/1600/1*C87EjxGeMPrkTuVRVWVg4w.png" width="225"></img>
|
||||
|
||||
[](https://github.com/commaai/openpilot/actions)
|
||||
[](https://lgtm.com/projects/g/commaai/openpilot/alerts/)
|
||||
[](https://lgtm.com/projects/g/commaai/openpilot/context:python)
|
||||
[](https://lgtm.com/projects/g/commaai/openpilot/context:cpp)
|
||||
[](https://codecov.io/gh/commaai/openpilot)
|
||||
|
||||
+8
-335
@@ -1,330 +1,3 @@
|
||||
# StarPilot 7.0 — Domathon development
|
||||
|
||||
The openpilot rebase is in progress. Domathon is not yet a completed StarPilot release, and feature or vehicle presence alone does not establish full parity with Dom.
|
||||
|
||||
See the [project overview](README.md) for the current development entry points and the [history guide](docs/DEVELOPMENT_HISTORY.md) for the upstream boundary and feature organization. Release-specific support and validation will be recorded when a release is prepared.
|
||||
|
||||
# Upstream openpilot release history
|
||||
|
||||
The entries below are retained upstream release notes. They describe openpilot releases, not completed StarPilot 7.0 milestones.
|
||||
|
||||
Version 0.11.2 (2026-08-12)
|
||||
=======================
|
||||
* New driving model
|
||||
* Big model with 880M parameters
|
||||
* Support for big models running on an external GPU
|
||||
* Live stream cameras from comma connect
|
||||
* Generate dashcam clips from comma connect
|
||||
* Remote comma body control from comma connect
|
||||
* New alert sounds
|
||||
* CUPRA Born 2021-2023 support thanks to DaHansi!
|
||||
* Volkswagen ID.4 2021-2025 support thanks to DaHansi!
|
||||
|
||||
Version 0.11.1 (2026-05-18)
|
||||
========================
|
||||
* New driver monitoring model
|
||||
* Improved image processing pipeline for driver camera
|
||||
* Improved thermal policy for comma four
|
||||
* Acura MDX 2022-24 support thanks to mvl-boston!
|
||||
* Rivian R1S and R1T 2025 support thanks to lukasloetkolben!
|
||||
|
||||
Version 0.11.0 (2026-03-17)
|
||||
========================
|
||||
* New driving model #36798
|
||||
* Fully trained using a learned simulator
|
||||
* Improved longitudinal performance in Experimental mode
|
||||
* Reduce comma four standby power usage by 77% to 52 mW
|
||||
* Kia K7 2017 support thanks to royjr!
|
||||
* Lexus LS 2018 support thanks to Hacheoy!
|
||||
|
||||
Version 0.10.3 (2025-12-17)
|
||||
========================
|
||||
* New driving model #36249
|
||||
* New temporal policy architecture
|
||||
* New on-policy training physics noise model
|
||||
* New driver monitoring model #36409
|
||||
* Trained on a new dataset, including comma four data
|
||||
* Improved inter-process communication memory efficiency
|
||||
|
||||
Version 0.10.2 (2025-11-19)
|
||||
========================
|
||||
* comma four support
|
||||
|
||||
Version 0.10.1 (2025-09-08)
|
||||
========================
|
||||
* New driving model #36276
|
||||
* World Model: removed global localization inputs
|
||||
* World Model: 2x the number of parameters
|
||||
* World Model: trained on 4x the number of segments
|
||||
* VAE Compression Model: new architecture and training objective
|
||||
* Driving Vision Model: trained on 4x the number of segments
|
||||
* New Driver Monitoring model #36198
|
||||
* Acura TLX 2021 support thanks to MVL!
|
||||
* Honda City 2023 support thanks to vanillagorillaa and drFritz!
|
||||
* Honda N-Box 2018 support thanks to miettal!
|
||||
* Honda Odyssey 2021-25 support thanks to csouers and MVL!
|
||||
* Honda Passport 2026 support thanks to vanillagorillaa and MVL!
|
||||
|
||||
Version 0.10.0 (2025-08-05)
|
||||
========================
|
||||
* New driving model
|
||||
* New training architecture
|
||||
* Described in our CVPR paper: "Learning to Drive from a World Model"
|
||||
* Longitudinal MPC replaced by E2E planning from World Model in Experimental Mode
|
||||
* Action from lateral MPC as training objective replaced by E2E planning from World Model
|
||||
* Low-speed lead car ground-truth fixes
|
||||
* Enable live-learned steering actuation delay
|
||||
* Opt-in audio recording for dashcam video
|
||||
* Acura MDX 2025 support thanks to vanillagorillaa and MVL!
|
||||
* Honda Accord 2023-25 support thanks to vanillagorillaa and MVL!
|
||||
* Honda CR-V 2023-25 support thanks to vanillagorillaa and MVL!
|
||||
* Honda Pilot 2023-25 support thanks to vanillagorillaa and MVL!
|
||||
|
||||
Version 0.9.9 (2025-05-23)
|
||||
========================
|
||||
* New driving model
|
||||
* New training architecture using parts from MLSIM
|
||||
* Steering actuation delay is now learned online
|
||||
* Ford Escape 2023-24 support thanks to incognitojam!
|
||||
* Ford Kuga 2024 support thanks to incognitojam!
|
||||
* Hyundai Nexo 2021 support thanks to sunnyhaibin!
|
||||
* Tesla Model 3 and Y support thanks to lukasloetkolben!
|
||||
* Lexus RC 2023 support thanks to nelsonjchen!
|
||||
|
||||
Version 0.9.8 (2025-02-28)
|
||||
========================
|
||||
* New driving model
|
||||
* Model now gates applying positive acceleration in Chill mode
|
||||
* New driver monitoring model
|
||||
* Reduced false positives related to passengers
|
||||
* Image processing pipeline moved to the ISP
|
||||
* More GPU time for bigger driving models
|
||||
* Power draw reduced 0.5W, which means your device runs cooler
|
||||
* Added toggle to enable driver monitoring even when openpilot is not engaged
|
||||
* Localizer rewritten to remove GPS dependency at runtime
|
||||
* Firehose Mode for maximizing your training data uploads
|
||||
* Enable openpilot longitudinal control for Ford Q3 vehicles
|
||||
* New Toyota TSS2 longitudinal tune
|
||||
* Rivian R1S and R1T support thanks to lukasloetkolben!
|
||||
* Ford F-150, F-150 Hybrid, Mach-E, and Ranger support
|
||||
|
||||
Version 0.9.7 (2024-06-13)
|
||||
========================
|
||||
* New driving model
|
||||
* Inputs the past curvature for smoother and more accurate lateral control
|
||||
* Simplified neural network architecture in the model's last layers
|
||||
* Minor fixes to desire augmentation and weight decay
|
||||
* New driver monitoring model
|
||||
* Improved end-to-end bit for phone detection
|
||||
* Adjust driving personality with the follow distance button
|
||||
* Support for hybrid variants of supported Ford models
|
||||
* Fingerprinting without the OBD-II port on all cars
|
||||
* Improved fuzzy fingerprinting for Ford and Volkswagen
|
||||
|
||||
Version 0.9.6 (2024-02-27)
|
||||
========================
|
||||
* New driving model
|
||||
* Vision model trained on more data
|
||||
* Improved driving performance
|
||||
* Directly outputs curvature for lateral control
|
||||
* New driver monitoring model
|
||||
* Trained on larger dataset
|
||||
* Model path UI
|
||||
* Shows where driving model wants to be
|
||||
* Shows what model is seeing more clearly, but more jittery
|
||||
* AGNOS 9
|
||||
* comma body streaming and controls over WebRTC
|
||||
* Improved fuzzy fingerprinting for many makes and models
|
||||
* Alpha longitudinal support for new Toyota models
|
||||
* Chevrolet Equinox 2019-22 support thanks to JasonJShuler and nworb-cire!
|
||||
* Dodge Durango 2020-21 support
|
||||
* Hyundai Staria 2023 support thanks to sunnyhaibin!
|
||||
* Kia Niro Plug-in Hybrid 2022 support thanks to sunnyhaibin!
|
||||
* Lexus LC 2024 support thanks to nelsonjchen!
|
||||
* Toyota RAV4 2023-24 support
|
||||
* Toyota RAV4 Hybrid 2023-24 support
|
||||
|
||||
Version 0.9.5 (2023-11-17)
|
||||
========================
|
||||
* New driving model
|
||||
* Improved navigate on openpilot performance using navigation instructions as an additional model input
|
||||
* Do lateral planning inside the model
|
||||
* New vision transformer architecture
|
||||
* Cadillac Escalade ESV 2019 support thanks to twilsonco!
|
||||
* Hyundai Azera 2022 support thanks to sunnyhaibin!
|
||||
* Hyundai Azera Hybrid 2020 support thanks to chanhojung and haram-KONA!
|
||||
* Hyundai Custin 2023 support thanks to sunnyhaibin and Saber422!
|
||||
* Hyundai Ioniq 6 2023 support thanks to sunnyhaibin and alamo3!
|
||||
* Hyundai Kona Electric 2023 (Korean version) support thanks to sunnyhaibin and haram-KONA!
|
||||
* Kia K8 Hybrid (with HDA II) 2023 support thanks to sunnyhaibin!
|
||||
* Kia Optima Hybrid 2019 support
|
||||
* Kia Sorento Hybrid 2023 support thanks to sunnyhaibin!
|
||||
* Lexus GS F 2016 support thanks to snyperifle!
|
||||
* Lexus IS 2023 support thanks to L3R5!
|
||||
|
||||
Version 0.9.4 (2023-07-27)
|
||||
========================
|
||||
* comma 3X support
|
||||
* Navigate on openpilot in Experimental mode
|
||||
* When navigation has a destination, openpilot will input the map information into the model, which provides useful context to help the model understand the scene
|
||||
* When navigating on openpilot, openpilot will keep left or right appropriately at forks and exits
|
||||
* When navigating on openpilot, lane change behavior is unchanged and still activated by the driver
|
||||
* When navigate on openpilot is active, the path on the map is green
|
||||
* UI updates
|
||||
* Navigation settings moved to home screen and map
|
||||
* Border color always shows engagement status. Blue means disengaged, green means engaged, and grey means engaged with human overriding
|
||||
* Alerts are shown inside the border. Black means info, orange means warning, and red means critical alert
|
||||
* Bookmarked segments are preserved on the device's storage
|
||||
* Ford Focus 2018 support
|
||||
* Kia Carnival 2023 support thanks to sunnyhaibin!
|
||||
|
||||
Version 0.9.3 (2023-06-29)
|
||||
========================
|
||||
* New driving model
|
||||
* Improved height estimation and added height tracking in liveCalibration
|
||||
* Model inputs refactor
|
||||
* New driving personality setting
|
||||
* Three settings: aggressive, standard, and relaxed
|
||||
* Standard is recommended and the default
|
||||
* In aggressive mode, lead follow distance is shorter and acceleration response is quicker
|
||||
* In relaxed mode, lead follow distance is longer
|
||||
* Improved fuzzy fingerprinting for Hyundai, Kia, and Genesis
|
||||
* Improved thermal management logic
|
||||
|
||||
Version 0.9.2 (2023-05-22)
|
||||
========================
|
||||
* New driving model
|
||||
* Reduced turn diving
|
||||
* Trained on a new dataset
|
||||
* UI updates
|
||||
* New experimental mode visualization
|
||||
* Draw MPC path instead of model-predicted path
|
||||
* AGNOS 7
|
||||
* Faster boot time
|
||||
* Fixes rare no sounds bug
|
||||
* Fixes bootsplash bug at extreme temperatures
|
||||
* Buick LaCrosse 2017-19 support thanks to koch-cf!
|
||||
* Chevrolet Trailblazer 2021-22 support thanks to TurboCE!
|
||||
* Ford Bronco Sport 2021-22 support
|
||||
* Ford Escape 2020-22 support
|
||||
* Ford Explorer 2020-22 support
|
||||
* Ford Kuga 2020-22 support
|
||||
* Ford Maverick 2022-23 support
|
||||
* Genesis GV80 2023 support thanks to JWingate80!
|
||||
* Honda HR-V 2023 support thanks to AlexandreSato and galegozi!
|
||||
* Kia Niro EV 2023 support thanks to JosselinLecocq!
|
||||
* Lexus ES 2017-18 support
|
||||
* Lincoln Aviator 2021 support
|
||||
* Škoda Fabia 2022-23 support thanks to jyoung8607!
|
||||
|
||||
|
||||
Version 0.9.1 (2023-02-28)
|
||||
========================
|
||||
* New driving model
|
||||
* 30% improved height estimation resulting in better driving performance for tall cars
|
||||
* Driver monitoring: removed timer resetting on user interaction if distracted
|
||||
* UI updates
|
||||
* Adjust alert volume using ambient noise level
|
||||
* Driver monitoring icon shows driver's head pose
|
||||
* German translation thanks to Vrabetz and CzokNorris!
|
||||
* Cadillac Escalade 2017 support thanks to rickygilleland!
|
||||
* Chevrolet Bolt EV 2022-23 support thanks to JasonJShuler!
|
||||
* Genesis GV60 2023 support thanks to sunnyhaibin!
|
||||
* Hyundai Tucson 2022-23 support
|
||||
* Kia K5 Hybrid 2020 support thanks to sunnyhaibin!
|
||||
* Kia Niro Hybrid 2023 support thanks to sunnyhaibin!
|
||||
* Kia Sorento 2022-23 support thanks to sunnyhaibin!
|
||||
* Kia Sorento Plug-in Hybrid 2022 support thanks to sunnyhaibin!
|
||||
* Toyota C-HR 2021 support thanks to eFiniLan!
|
||||
* Toyota C-HR Hybrid 2022 support thanks to Korben00!
|
||||
* Volkswagen Crafter and MAN TGE 2017-23 support thanks to jyoung8607!
|
||||
|
||||
Version 0.9.0 (2022-11-21)
|
||||
========================
|
||||
* New driving model
|
||||
* Internal feature space information content increased tenfold during training to ~700 bits, which makes the model dramatically more accurate
|
||||
* Less reliance on previous frames makes model more reactive and snappy
|
||||
* Trained in new reprojective simulator
|
||||
* Trained in 36 hours from scratch, compared to one week for previous releases
|
||||
* Training now simulates both lateral and longitudinal behavior, which allows openpilot to slow down for turns, stop at traffic lights, and more in experimental mode
|
||||
* Experimental driving mode
|
||||
* End-to-end longitudinal control
|
||||
* Stops for traffic lights and stop signs
|
||||
* Slows down for turns
|
||||
* openpilot defaults to chill mode, enable experimental mode in settings
|
||||
* Driver monitoring updates
|
||||
* New bigger model with added end-to-end distracted trigger
|
||||
* Reduced false positives during driver calibration
|
||||
* Self-tuning torque controller: learns parameters live for each car
|
||||
* Torque controller used on all Toyota, Lexus, Hyundai, Kia, and Genesis models
|
||||
* UI updates
|
||||
* Matched speeds shown on car's dash
|
||||
* Multi-language in navigation
|
||||
* Improved update experience
|
||||
* Border turns grey while overriding steering
|
||||
* Bookmark events while driving; view them in comma connect
|
||||
* New onroad visualization for experimental mode
|
||||
* tools: new and improved cabana thanks to deanlee!
|
||||
* Experimental longitudinal support for Volkswagen, CAN-FD Hyundai, and new GM models
|
||||
* Genesis GV70 2022-23 support thanks to zunichky and sunnyhaibin!
|
||||
* Hyundai Santa Cruz 2021-22 support thanks to sunnyhaibin!
|
||||
* Kia Sportage 2023 support thanks to sunnyhaibin!
|
||||
* Kia Sportage Hybrid 2023 support thanks to sunnyhaibin!
|
||||
* Kia Stinger 2022 support thanks to sunnyhaibin!
|
||||
|
||||
Version 0.8.16 (2022-08-26)
|
||||
========================
|
||||
* New driving model
|
||||
* Reduced turn cutting
|
||||
* Auto-detect right hand drive setting with driver monitoring model
|
||||
* Improved fan controller for comma three
|
||||
* New translations
|
||||
* Japanese thanks to cydia2020!
|
||||
* Brazilian Portuguese thanks to AlexandreSato!
|
||||
* Chevrolet Bolt EUV 2022-23 support thanks to JasonJShuler!
|
||||
* Chevrolet Silverado 1500 2020-21 support thanks to JasonJShuler!
|
||||
* GMC Sierra 1500 2020-21 support thanks to JasonJShuler!
|
||||
* Hyundai Ioniq 5 2022 support thanks to sunnyhaibin!
|
||||
* Hyundai Kona Electric 2022 support thanks to sunnyhaibin!
|
||||
* Hyundai Tucson Hybrid 2022 support thanks to sunnyhaibin!
|
||||
* Subaru Legacy 2020-22 support thanks to martinl!
|
||||
* Subaru Outback 2020-22 support
|
||||
|
||||
Version 0.8.15 (2022-07-20)
|
||||
========================
|
||||
* New driving model
|
||||
* Path planning uses end-to-end output instead of lane lines at all times
|
||||
* Reduced ping pong
|
||||
* Improved lane centering
|
||||
* New lateral controller based on physical wheel torque model
|
||||
* Much smoother control that's consistent across the speed range
|
||||
* Effective feedforward that uses road roll
|
||||
* Simplified tuning, all car-specific parameters can be derived from data
|
||||
* Used on select Toyota and Hyundai models at first
|
||||
* Significantly improved control on TSS-P Prius
|
||||
* New driver monitoring model
|
||||
* Bigger model, covering full interior view from driver camera
|
||||
* Works with a wider variety of mounting angles
|
||||
* 3x more unique comma three training data than previous
|
||||
* Navigation improvements
|
||||
* Speed limits shown while navigating
|
||||
* Faster position fix by using raw GPS measurements
|
||||
* UI updates
|
||||
* Multilanguage support for settings and home screen
|
||||
* New font
|
||||
* Refreshed max speed design
|
||||
* More consistent camera view perspective across cars
|
||||
* Reduced power usage: device runs cooler and fan spins less
|
||||
* AGNOS 5
|
||||
* Support VSCode remote SSH target
|
||||
* Support for delta updates to reduce data usage on future OS updates
|
||||
* Chrysler ECU firmware fingerprinting thanks to realfast!
|
||||
* Honda Civic 2022 support
|
||||
* Hyundai Tucson 2021 support thanks to bluesforte!
|
||||
* Kia EV6 2022 support
|
||||
* Lexus NX Hybrid 2020 support thanks to AlexandreSato!
|
||||
* Ram 1500 2019-21 support thanks to realfast!
|
||||
|
||||
Version 0.8.14 (2022-06-01)
|
||||
========================
|
||||
* New driving model
|
||||
@@ -347,7 +20,7 @@ Version 0.8.14 (2022-06-01)
|
||||
Version 0.8.13 (2022-02-18)
|
||||
========================
|
||||
* Improved driver monitoring
|
||||
* Re-tuned driver pose learner for relaxed driving positions
|
||||
* Retuned driver pose learner for relaxed driving positions
|
||||
* Added reliance on driving model to be more scene adaptive
|
||||
* Matched strictness between comma two and comma three
|
||||
* Improved performance in turns by compensating for the road bank angle
|
||||
@@ -493,7 +166,7 @@ Version 0.8.4 (2021-05-17)
|
||||
* Delay controls start until system is ready
|
||||
* Fuzzy car identification, enabled with Community Features toggle
|
||||
* Localizer optimized for increased precision and less CPU usage
|
||||
* Re-tuned lateral control to be more aggressive when model is confident
|
||||
* Retuned lateral control to be more aggressive when model is confident
|
||||
* Toyota Mirai 2021 support
|
||||
* Lexus NX 300 2020 support thanks to goesreallyfast!
|
||||
* Volkswagen Atlas 2018-19 support thanks to jyoung8607!
|
||||
@@ -658,7 +331,7 @@ Version 0.7 (2019-12-13)
|
||||
* Improve GM longitudinal control: proper computations for 15Hz radar
|
||||
* Move GM port, Toyota with DSU removed, comma pedal in community features; toggle switch required
|
||||
* Remove upload over cellular toggle: only upload qlog and qcamera files if not on wifi
|
||||
* Refactor Panda code towards ISO26262 and SIL2 compliance
|
||||
* Refactor Panda code towards ISO26262 and SIL2 compliancy
|
||||
* Forward stock FCW for Honda Nidec
|
||||
* Volkswagen port now standard: comma Harness intercepts stock camera
|
||||
|
||||
@@ -749,7 +422,7 @@ Version 0.5.13 (2019-05-31)
|
||||
* Reduce CPU utilization by 20% and improve stability
|
||||
* Temporarily remove mapd functionalities to improve stability
|
||||
* Add openpilot record-only mode for unsupported cars
|
||||
* Synchronize controlsd to pandad to reduce latency
|
||||
* Synchronize controlsd to boardd to reduce latency
|
||||
* Remove panda support for Subaru giraffe
|
||||
|
||||
Version 0.5.12 (2019-05-16)
|
||||
@@ -789,7 +462,7 @@ Version 0.5.10 (2019-03-19)
|
||||
* New Driver Monitoring Model
|
||||
* Support QR codes for login using comma connect
|
||||
* Refactor comma pedal FW and use CRC-8 checksum algorithm for safety. Reflashing pedal is required.
|
||||
Please see `#hw-pedal` on [discord](https://discord.comma.ai) for assistance updating comma pedal.
|
||||
Please see `#hw-pedal` on [discord](discord.comma.ai) for assistance updating comma pedal.
|
||||
* Additional speed limit rules for Germany thanks to arne182
|
||||
* Allow negative speed limit offsets
|
||||
|
||||
@@ -1085,7 +758,7 @@ Version 0.2.8 (2017-02-27)
|
||||
Version 0.2.7 (2017-02-08)
|
||||
===========================
|
||||
* Better performance and pictures at night
|
||||
* Fix ptr alignment issue in pandad
|
||||
* Fix ptr alignment issue in boardd
|
||||
* Fix brake error light, fix crash if too cold
|
||||
|
||||
Version 0.2.6 (2017-01-31)
|
||||
@@ -1117,12 +790,12 @@ Version 0.2.2 (2017-01-10)
|
||||
Version 0.2.1 (2016-12-14)
|
||||
===========================
|
||||
* Performance improvements, removal of more numpy
|
||||
* Fix pandad process priority
|
||||
* Fix boardd process priority
|
||||
* Make counter timer reset on use of steering wheel
|
||||
|
||||
Version 0.2 (2016-12-12)
|
||||
=========================
|
||||
* Car/Radar abstraction layers have shipped, see openpilot/cereal/car.capnp
|
||||
* Car/Radar abstraction layers have shipped, see cereal/car.capnp
|
||||
* controlsd has been refactored
|
||||
* Shipped plant model and testing maneuvers
|
||||
* visiond exits more gracefully now
|
||||
|
||||
+374
-360
@@ -1,401 +1,415 @@
|
||||
import os
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
import sysconfig
|
||||
import platform
|
||||
import shlex
|
||||
import importlib
|
||||
import re
|
||||
import stat
|
||||
import numpy as np
|
||||
|
||||
import SCons.Errors
|
||||
from SCons.Defaults import _stripixes
|
||||
|
||||
COMMA_HARDWARE = os.path.isfile('/AGNOS')
|
||||
|
||||
SCons.Warnings.warningAsException(True)
|
||||
|
||||
TICI = os.path.isfile('/TICI')
|
||||
Decider('MD5-timestamp')
|
||||
|
||||
SetOption('num_jobs', max(1, int(os.cpu_count()/(1 if "CI" in os.environ else 2))))
|
||||
AddOption('--test',
|
||||
action='store_true',
|
||||
help='build test files')
|
||||
|
||||
AddOption('--ccflags', action='store', type='string', default='', help='pass arbitrary flags over the command line')
|
||||
AddOption('--verbose', action='store_true', default=False, help='show full build commands')
|
||||
release = os.path.exists(File('#git_src_commit').abspath) # written by release packaging
|
||||
AddOption('--minimal',
|
||||
action='store_false',
|
||||
dest='extras',
|
||||
default=(not COMMA_HARDWARE and not release),
|
||||
help='the minimum build to run openpilot. no tests, tools, etc.')
|
||||
AddOption('--extras',
|
||||
action='store_true',
|
||||
help='build misc extras, like setup and installer files')
|
||||
|
||||
dependency_python_paths = [
|
||||
Dir("#").abspath,
|
||||
Dir("#msgq_repo").abspath,
|
||||
Dir("#opendbc_repo").abspath,
|
||||
Dir("#rednose_repo").abspath,
|
||||
Dir("#teleoprtc_repo").abspath,
|
||||
Dir("#tinygrad_repo").abspath,
|
||||
]
|
||||
for p in reversed(dependency_python_paths):
|
||||
if p not in sys.path:
|
||||
sys.path.insert(0, p)
|
||||
AddOption('--kaitai',
|
||||
action='store_true',
|
||||
help='Regenerate kaitai struct parsers')
|
||||
|
||||
if external_pythonpath := os.environ.get("PYTHONPATH"):
|
||||
dependency_python_paths += [p for p in external_pythonpath.split(os.pathsep) if p and p not in dependency_python_paths]
|
||||
AddOption('--asan',
|
||||
action='store_true',
|
||||
help='turn on ASAN')
|
||||
|
||||
# Detect platform
|
||||
arch = subprocess.check_output(["uname", "-m"], encoding='utf8').rstrip()
|
||||
AddOption('--ubsan',
|
||||
action='store_true',
|
||||
help='turn on UBSan')
|
||||
|
||||
AddOption('--clazy',
|
||||
action='store_true',
|
||||
help='build with clazy')
|
||||
|
||||
AddOption('--compile_db',
|
||||
action='store_true',
|
||||
help='build clang compilation database')
|
||||
|
||||
AddOption('--snpe',
|
||||
action='store_true',
|
||||
help='use SNPE on PC')
|
||||
|
||||
AddOption('--external-sconscript',
|
||||
action='store',
|
||||
metavar='FILE',
|
||||
dest='external_sconscript',
|
||||
help='add an external SConscript to the build')
|
||||
|
||||
AddOption('--no-thneed',
|
||||
action='store_true',
|
||||
dest='no_thneed',
|
||||
help='avoid using thneed')
|
||||
|
||||
real_arch = arch = subprocess.check_output(["uname", "-m"], encoding='utf8').rstrip()
|
||||
if platform.system() == "Darwin":
|
||||
arch = "Darwin"
|
||||
elif arch == "aarch64" and COMMA_HARDWARE:
|
||||
arch = "comma_arm64"
|
||||
assert arch in [
|
||||
"comma_arm64", # linux comma hardware (AGNOS) arm64
|
||||
"aarch64", # linux pc arm64
|
||||
"x86_64", # linux pc x64
|
||||
"Darwin", # macOS arm64 (x86 not supported)
|
||||
]
|
||||
|
||||
pkg_names = ['acados', 'capnproto', 'ffmpeg', 'json11', 'ncurses', 'zeromq', 'zstd']
|
||||
if arch != 'Darwin':
|
||||
pkg_names.append('libusb')
|
||||
pkgs = [importlib.import_module(name) for name in pkg_names]
|
||||
acados = pkgs[pkg_names.index('acados')]
|
||||
ffmpeg = pkgs[pkg_names.index('ffmpeg')]
|
||||
from tools.laptop_device_build.runtime_paths import runtime_library_path
|
||||
if arch == "aarch64" and TICI:
|
||||
arch = "larch64"
|
||||
|
||||
build_sysroot = os.environ.get("COMMA_BUILD_SYSROOT") if arch == "comma_arm64" else None
|
||||
ffmpeg_runtime_dir = runtime_library_path(ffmpeg.LIB_DIR, sys.prefix, "/usr/local/venv" if build_sysroot else None)
|
||||
# Shared package ships .so/.dylib; older device venvs still have static .a only.
|
||||
# Keep static link deps (x264/z/va/drm) when the installed package is static so
|
||||
# COMMA_HARDWARE CI works without upgrading the device venv yet.
|
||||
# TODO: drop the static fallback once device venvs have comma-deps-ffmpeg>=7.1.0.post94
|
||||
_ffmpeg_lib_names = os.listdir(ffmpeg.LIB_DIR) if os.path.isdir(ffmpeg.LIB_DIR) else []
|
||||
ffmpeg_shared = any(
|
||||
n.startswith('libavcodec.so') or (n.startswith('libavcodec') and n.endswith('.dylib'))
|
||||
for n in _ffmpeg_lib_names
|
||||
)
|
||||
ffmpeg_libs = ['avformat', 'avcodec', 'swresample', 'avutil']
|
||||
if not ffmpeg_shared:
|
||||
ffmpeg_libs += ['x264', 'z']
|
||||
if arch != "Darwin":
|
||||
ffmpeg_libs += ['va', 'va-drm', 'drm']
|
||||
acados_include_dirs = [
|
||||
acados.INCLUDE_DIR,
|
||||
os.path.join(acados.INCLUDE_DIR, "blasfeo", "include"),
|
||||
os.path.join(acados.INCLUDE_DIR, "hpipm", "include"),
|
||||
]
|
||||
USE_WEBCAM = os.getenv("USE_WEBCAM") is not None
|
||||
|
||||
lenv = {
|
||||
"PATH": os.environ['PATH'],
|
||||
"LD_LIBRARY_PATH": [Dir(f"#third_party/acados/{arch}/lib").abspath],
|
||||
"PYTHONPATH": Dir("#").abspath + ":" + Dir("#pyextra/").abspath,
|
||||
|
||||
# ***** enforce a whitelist of system libraries *****
|
||||
# this prevents silently relying on a 3rd party package,
|
||||
# e.g. apt-installed libusb. all libraries should either
|
||||
# be distributed with all Linux distros and macOS, or
|
||||
# vendored in commaai/dependencies.
|
||||
allowed_system_libs = {
|
||||
"EGL", "GLESv2", "GL",
|
||||
"dl", "drm", "gbm", "m", "pthread",
|
||||
"ACADOS_SOURCE_DIR": Dir("#third_party/acados/include/acados").abspath,
|
||||
"ACADOS_PYTHON_INTERFACE_PATH": Dir("#pyextra/acados_template").abspath,
|
||||
"TERA_PATH": Dir("#").abspath + f"/third_party/acados/{arch}/t_renderer",
|
||||
}
|
||||
|
||||
def _resolve_lib(env, name):
|
||||
for d in env.Flatten(env.get('LIBPATH', [])):
|
||||
p = Dir(str(d)).abspath
|
||||
for ext in ('.a', '.so', '.dylib'):
|
||||
f = File(os.path.join(p, f'lib{name}{ext}'))
|
||||
if f.exists() or f.has_builder():
|
||||
return name
|
||||
if name in allowed_system_libs:
|
||||
return name
|
||||
raise SCons.Errors.UserError(f"Unexpected non-vendored library '{name}'")
|
||||
rpath = lenv["LD_LIBRARY_PATH"].copy()
|
||||
|
||||
def _libflags(target, source, env, for_signature):
|
||||
libs = []
|
||||
lp = env.subst('$LIBLITERALPREFIX')
|
||||
for lib in env.Flatten(env.get('LIBS', [])):
|
||||
if isinstance(lib, str):
|
||||
if os.sep in lib or lib.startswith('#'):
|
||||
libs.append(File(lib))
|
||||
elif lib.startswith('-') or (lp and lib.startswith(lp)):
|
||||
libs.append(lib)
|
||||
else:
|
||||
libs.append(_resolve_lib(env, lib))
|
||||
else:
|
||||
libs.append(lib)
|
||||
return _stripixes(env['LIBLINKPREFIX'], libs, env['LIBLINKSUFFIX'],
|
||||
env['LIBPREFIXES'], env['LIBSUFFIXES'], env, env['LIBLITERALPREFIX'])
|
||||
if arch == "larch64":
|
||||
lenv["LD_LIBRARY_PATH"] += ['/data/data/com.termux/files/usr/lib']
|
||||
|
||||
env = Environment(
|
||||
ENV={
|
||||
"PATH": os.environ['PATH'],
|
||||
"PYTHONPATH": os.pathsep.join(dependency_python_paths),
|
||||
"ACADOS_SOURCE_DIR": acados.DIR,
|
||||
"ACADOS_PYTHON_INTERFACE_PATH": acados.TEMPLATE_DIR,
|
||||
"TERA_PATH": acados.TERA_PATH
|
||||
},
|
||||
CCFLAGS=[
|
||||
"-g",
|
||||
"-fPIC",
|
||||
"-pipe",
|
||||
"-O2",
|
||||
"-Wunused",
|
||||
"-Werror",
|
||||
"-Wshadow" if arch in ("Darwin", "comma_arm64") else "-Wshadow=local",
|
||||
"-Wno-unknown-warning-option",
|
||||
"-Wno-inconsistent-missing-override",
|
||||
"-Wno-c99-designator",
|
||||
"-Wno-reorder-init-list",
|
||||
"-Wno-vla-cxx-extension",
|
||||
],
|
||||
CFLAGS=["-std=gnu11"],
|
||||
CXXFLAGS=["-std=c++1z"],
|
||||
CPPPATH=[
|
||||
"#openpilot",
|
||||
"#msgq_repo", # #include "msgq/..."
|
||||
"#opendbc_repo", # #include "opendbc/..."
|
||||
"#rednose_repo", # #include "rednose/..."
|
||||
"#rednose_repo/rednose", # #include "logger/..." (rednose package root)
|
||||
"#openpilot/cereal/gen/cpp",
|
||||
acados_include_dirs,
|
||||
[x.INCLUDE_DIR for x in pkgs],
|
||||
"#",
|
||||
],
|
||||
LIBPATH=[
|
||||
"#openpilot/common",
|
||||
"#msgq_repo",
|
||||
"#openpilot/selfdrive/pandad",
|
||||
"#rednose_repo/rednose/helpers",
|
||||
[x.LIB_DIR for x in pkgs],
|
||||
],
|
||||
RPATH=[ffmpeg_runtime_dir] if ffmpeg_shared else [],
|
||||
CYTHONCFILESUFFIX=".cpp",
|
||||
COMPILATIONDB_USE_ABSPATH=True,
|
||||
REDNOSE_ROOT="#rednose_repo",
|
||||
tools=["default", "cython", "compilation_db", "rednose_filter"],
|
||||
toolpath=["#msgq_repo/site_scons/site_tools", "#rednose_repo/site_scons/site_tools"],
|
||||
)
|
||||
# SCons' Darwin linker tool doesn't define the variables used to expand RPATH.
|
||||
if arch == "Darwin":
|
||||
env["RPATHPREFIX"] = "-Wl,-rpath,"
|
||||
env["RPATHSUFFIX"] = ""
|
||||
env["_RPATH"] = "${_concat(RPATHPREFIX, RPATH, RPATHSUFFIX, __env__)}"
|
||||
if arch != "comma_arm64":
|
||||
env['_LIBFLAGS'] = _libflags
|
||||
cpppath = [
|
||||
"#third_party/opencl/include",
|
||||
]
|
||||
|
||||
# Arch-specific flags and paths
|
||||
if arch == "comma_arm64":
|
||||
env["CC"] = "clang"
|
||||
env["CXX"] = "clang++"
|
||||
env.Append(LIBPATH=[
|
||||
"/usr/lib/aarch64-linux-gnu",
|
||||
])
|
||||
arch_flags = ["-D__COMMA_HARDWARE__", "-mcpu=cortex-a57"]
|
||||
env.Append(CCFLAGS=arch_flags)
|
||||
env.Append(CXXFLAGS=arch_flags)
|
||||
if build_sysroot:
|
||||
if not all(os.path.isdir(os.path.join(build_sysroot, part)) for part in ("usr/include", "usr/lib/aarch64-linux-gnu", "lib/aarch64-linux-gnu")):
|
||||
raise SCons.Errors.UserError(f"Incomplete COMMA_BUILD_SYSROOT: {build_sysroot}")
|
||||
# Clang's --sysroot controls the standard headers and linker startup/libs;
|
||||
# explicit SCons paths make device libraries win over container libraries.
|
||||
env.Prepend(CPPPATH=[os.path.join(build_sysroot, part) for part in ("usr/local/include", "usr/include")])
|
||||
env.Prepend(LIBPATH=[os.path.join(build_sysroot, part) for part in ("usr/local/lib", "usr/lib/aarch64-linux-gnu", "lib/aarch64-linux-gnu")])
|
||||
env.Append(CCFLAGS=[f"--sysroot={build_sysroot}"])
|
||||
env.Append(LINKFLAGS=[f"--sysroot={build_sysroot}"])
|
||||
elif arch == "Darwin":
|
||||
env.Append(LIBPATH=[
|
||||
"/System/Library/Frameworks/OpenGL.framework/Libraries",
|
||||
])
|
||||
env.Append(CCFLAGS=["-DGL_SILENCE_DEPRECATION"])
|
||||
env.Append(CXXFLAGS=["-DGL_SILENCE_DEPRECATION"])
|
||||
libpath = [
|
||||
"/usr/local/lib",
|
||||
"/usr/lib",
|
||||
"/system/vendor/lib64",
|
||||
f"#third_party/acados/{arch}/lib",
|
||||
]
|
||||
|
||||
_extra_cc = shlex.split(GetOption('ccflags') or '')
|
||||
if _extra_cc:
|
||||
env.Append(CCFLAGS=_extra_cc)
|
||||
libpath += [
|
||||
"#third_party/snpe/larch64",
|
||||
"#third_party/libyuv/larch64/lib",
|
||||
"/usr/lib/aarch64-linux-gnu"
|
||||
]
|
||||
cpppath += [
|
||||
"#selfdrive/camerad/include",
|
||||
]
|
||||
cflags = ["-DQCOM2", "-mcpu=cortex-a57"]
|
||||
cxxflags = ["-DQCOM2", "-mcpu=cortex-a57"]
|
||||
rpath += ["/usr/local/lib"]
|
||||
else:
|
||||
cflags = []
|
||||
cxxflags = []
|
||||
cpppath = []
|
||||
|
||||
# MacOS
|
||||
if arch == "Darwin":
|
||||
brew_prefix = subprocess.check_output(['brew', '--prefix'], encoding='utf8').strip()
|
||||
yuv_dir = "mac" if real_arch != "arm64" else "mac_arm64"
|
||||
libpath = [
|
||||
f"#third_party/libyuv/{yuv_dir}/lib",
|
||||
f"{brew_prefix}/lib",
|
||||
f"{brew_prefix}/Library",
|
||||
f"{brew_prefix}/opt/openssl/lib",
|
||||
f"{brew_prefix}/Cellar",
|
||||
f"#third_party/acados/{arch}/lib",
|
||||
"/System/Library/Frameworks/OpenGL.framework/Libraries",
|
||||
]
|
||||
cflags += ["-DGL_SILENCE_DEPRECATION"]
|
||||
cxxflags += ["-DGL_SILENCE_DEPRECATION"]
|
||||
cpppath += [
|
||||
f"{brew_prefix}/include",
|
||||
f"{brew_prefix}/opt/openssl/include",
|
||||
]
|
||||
# Linux 86_64
|
||||
else:
|
||||
libpath = [
|
||||
"#third_party/acados/x86_64/lib",
|
||||
"#third_party/snpe/x86_64-linux-clang",
|
||||
"#third_party/libyuv/x64/lib",
|
||||
"#third_party/mapbox-gl-native-qt/x86_64",
|
||||
"#cereal",
|
||||
"#common",
|
||||
"/usr/lib",
|
||||
"/usr/local/lib",
|
||||
]
|
||||
|
||||
rpath += [
|
||||
Dir("#third_party/snpe/x86_64-linux-clang").abspath,
|
||||
Dir("#cereal").abspath,
|
||||
Dir("#common").abspath
|
||||
]
|
||||
|
||||
if GetOption('asan'):
|
||||
ccflags = ["-fsanitize=address", "-fno-omit-frame-pointer"]
|
||||
ldflags = ["-fsanitize=address"]
|
||||
elif GetOption('ubsan'):
|
||||
ccflags = ["-fsanitize=undefined"]
|
||||
ldflags = ["-fsanitize=undefined"]
|
||||
else:
|
||||
ccflags = []
|
||||
ldflags = []
|
||||
|
||||
# no --as-needed on mac linker
|
||||
if arch != "Darwin":
|
||||
env.Append(LINKFLAGS=["-Wl,--as-needed", "-Wl,--no-undefined"])
|
||||
ldflags += ["-Wl,--as-needed", "-Wl,--no-undefined"]
|
||||
|
||||
# Shorter build output: show brief descriptions instead of full commands.
|
||||
# Full command lines are still printed on failure by scons.
|
||||
if not GetOption('verbose'):
|
||||
for action, short in (
|
||||
("CC", "CC"),
|
||||
("CXX", "CXX"),
|
||||
("LINK", "LINK"),
|
||||
("SHCC", "CC"),
|
||||
("SHCXX", "CXX"),
|
||||
("SHLINK", "LINK"),
|
||||
("AR", "AR"),
|
||||
("RANLIB", "RANLIB"),
|
||||
("AS", "AS"),
|
||||
):
|
||||
env[f"{action}COMSTR"] = f" [{short}] $TARGET"
|
||||
# Enable swaglog include in submodules
|
||||
cflags += ['-DSWAGLOG="\\"common/swaglog.h\\""']
|
||||
cxxflags += ['-DSWAGLOG="\\"common/swaglog.h\\""']
|
||||
|
||||
# ********** Cython build environment **********
|
||||
envCython = env.Clone()
|
||||
envCython["CPPPATH"] += [sysconfig.get_paths()['include'], np.get_include()]
|
||||
envCython["CCFLAGS"] += ["-Wno-#warnings", "-Wno-cpp", "-Wno-shadow", "-Wno-deprecated-declarations"]
|
||||
envCython["CCFLAGS"].remove("-Werror")
|
||||
env = Environment(
|
||||
ENV=lenv,
|
||||
CCFLAGS=[
|
||||
"-g",
|
||||
"-fPIC",
|
||||
"-O2",
|
||||
"-Wunused",
|
||||
"-Werror",
|
||||
"-Wshadow",
|
||||
"-Wno-unknown-warning-option",
|
||||
"-Wno-deprecated-register",
|
||||
"-Wno-register",
|
||||
"-Wno-inconsistent-missing-override",
|
||||
"-Wno-c99-designator",
|
||||
"-Wno-reorder-init-list",
|
||||
"-Wno-error=unused-but-set-variable",
|
||||
] + cflags + ccflags,
|
||||
|
||||
CPPPATH=cpppath + [
|
||||
"#",
|
||||
"#third_party/acados/include",
|
||||
"#third_party/acados/include/blasfeo/include",
|
||||
"#third_party/acados/include/hpipm/include",
|
||||
"#third_party/catch2/include",
|
||||
"#third_party/libyuv/include",
|
||||
"#third_party/json11",
|
||||
"#third_party/curl/include",
|
||||
"#third_party/libgralloc/include",
|
||||
"#third_party/android_frameworks_native/include",
|
||||
"#third_party/android_hardware_libhardware/include",
|
||||
"#third_party/android_system_core/include",
|
||||
"#third_party/linux/include",
|
||||
"#third_party/snpe/include",
|
||||
"#third_party/mapbox-gl-native-qt/include",
|
||||
"#third_party/qrcode",
|
||||
"#third_party",
|
||||
"#cereal",
|
||||
"#opendbc/can",
|
||||
],
|
||||
|
||||
CC='clang',
|
||||
CXX='clang++',
|
||||
LINKFLAGS=ldflags,
|
||||
|
||||
RPATH=rpath,
|
||||
|
||||
CFLAGS=["-std=gnu11"] + cflags,
|
||||
CXXFLAGS=["-std=c++1z"] + cxxflags,
|
||||
LIBPATH=libpath + [
|
||||
"#cereal",
|
||||
"#third_party",
|
||||
"#opendbc/can",
|
||||
"#selfdrive/boardd",
|
||||
"#common",
|
||||
],
|
||||
CYTHONCFILESUFFIX=".cpp",
|
||||
COMPILATIONDB_USE_ABSPATH=True,
|
||||
tools=["default", "cython", "compilation_db"],
|
||||
)
|
||||
|
||||
envCython["LIBS"] = []
|
||||
if arch == "Darwin":
|
||||
envCython["LINKFLAGS"] = env["LINKFLAGS"] + ["-bundle", "-undefined", "dynamic_lookup"]
|
||||
else:
|
||||
envCython["LINKFLAGS"] = ["-pthread", "-shared"]
|
||||
if build_sysroot:
|
||||
envCython.Append(LINKFLAGS=[f"--sysroot={build_sysroot}"])
|
||||
env['RPATHPREFIX'] = "-rpath "
|
||||
|
||||
np_version = SCons.Script.Value(np.__version__)
|
||||
Export('envCython', 'np_version')
|
||||
|
||||
Export('env', 'arch', 'acados', 'ffmpeg_libs')
|
||||
if GetOption('compile_db'):
|
||||
env.CompilationDatabase('compile_commands.json')
|
||||
|
||||
# Setup cache dir
|
||||
cache_dir = os.environ.get('SCONS_CACHE', '/data/scons_cache' if arch == "comma_arm64" else '/tmp/scons_cache')
|
||||
cache_size_limit = 4e9 if "CI" in os.environ else 2e9
|
||||
cache_dir = '/data/scons_cache' if TICI else '/tmp/scons_cache'
|
||||
CacheDir(cache_dir)
|
||||
Clean(["."], cache_dir)
|
||||
|
||||
def prune_cache_dir(target=None, source=None, env=None):
|
||||
cache_files = []
|
||||
cache_root = os.path.normpath(cache_dir)
|
||||
for root, _, files in os.walk(cache_root):
|
||||
shard = os.path.basename(root)
|
||||
if os.path.dirname(root) != cache_root or re.fullmatch(r'[0-9A-Fa-f]{2}', shard) is None:
|
||||
continue
|
||||
for name in files:
|
||||
if re.fullmatch(r'(?:[0-9a-f]{32}|[0-9a-f]{64})', name) is None or name[:2].lower() != shard.lower():
|
||||
continue
|
||||
path = os.path.join(root, name)
|
||||
try:
|
||||
info = os.stat(path, follow_symlinks=False)
|
||||
except FileNotFoundError:
|
||||
continue
|
||||
if stat.S_ISREG(info.st_mode):
|
||||
cache_files.append((info.st_mtime, info.st_size, path))
|
||||
cache_files.sort()
|
||||
cache_size = sum(size for _, size, _ in cache_files)
|
||||
for _, size, path in cache_files:
|
||||
if cache_size < cache_size_limit:
|
||||
break
|
||||
try:
|
||||
os.unlink(path)
|
||||
except FileNotFoundError:
|
||||
pass
|
||||
cache_size -= size
|
||||
|
||||
# ********** start building stuff **********
|
||||
|
||||
# Build common module
|
||||
SConscript(['openpilot/common/SConscript'])
|
||||
Import('_common')
|
||||
common = [_common, 'json11', 'zmq']
|
||||
Export('common')
|
||||
|
||||
# Build messaging (cereal + msgq + socketmaster + their dependencies)
|
||||
# Enable swaglog include in dependency sources
|
||||
env_swaglog = env.Clone()
|
||||
env_swaglog['CXXFLAGS'].append('-DSWAGLOG="\\"common/swaglog.h\\""')
|
||||
SConscript(['msgq_repo/SConscript'], exports={'env': env_swaglog})
|
||||
|
||||
SConscript(['openpilot/cereal/SConscript'])
|
||||
|
||||
Import('socketmaster', 'msgq')
|
||||
messaging = [socketmaster, msgq, 'capnp', 'kj',]
|
||||
Export('messaging')
|
||||
|
||||
|
||||
# Build dependency sources
|
||||
SConscript(['panda/SConscript'])
|
||||
|
||||
# Build rednose library
|
||||
SConscript(['rednose_repo/rednose/SConscript'])
|
||||
|
||||
# Build system services
|
||||
SConscript([
|
||||
'openpilot/system/loggerd/SConscript',
|
||||
])
|
||||
|
||||
if arch == "comma_arm64":
|
||||
SConscript(['openpilot/system/camerad/SConscript'])
|
||||
SConscript(['openpilot/starpilot/system/android_auto/SConscript'])
|
||||
|
||||
# Build selfdrive
|
||||
SConscript([
|
||||
'openpilot/selfdrive/pandad/SConscript',
|
||||
'openpilot/selfdrive/controls/lib/longitudinal_mpc_lib/SConscript',
|
||||
'openpilot/selfdrive/locationd/SConscript',
|
||||
'openpilot/selfdrive/modeld/SConscript',
|
||||
'openpilot/selfdrive/ui/SConscript',
|
||||
])
|
||||
|
||||
# Build desktop-only tools
|
||||
if GetOption('extras') and arch != "comma_arm64":
|
||||
SConscript([
|
||||
'openpilot/tools/replay/SConscript',
|
||||
'openpilot/tools/cabana/SConscript',
|
||||
'openpilot/tools/jotpluggler/SConscript',
|
||||
])
|
||||
|
||||
|
||||
env.CompilationDatabase('compile_commands.json')
|
||||
|
||||
# progress output
|
||||
def count_scons_nodes(nodes):
|
||||
seen = set()
|
||||
stack = list(nodes)
|
||||
|
||||
while stack:
|
||||
node = stack.pop().disambiguate()
|
||||
if node in seen:
|
||||
continue
|
||||
seen.add(node)
|
||||
executor = node.get_executor()
|
||||
if executor is not None:
|
||||
stack += executor.get_all_prerequisites() + executor.get_all_children()
|
||||
|
||||
return len(seen)
|
||||
|
||||
progress_interval = 5
|
||||
progress_count = 0
|
||||
progress_total = max(1, count_scons_nodes(env.arg2nodes(BUILD_TARGETS or [Dir('.')], env.fs.Entry)))
|
||||
|
||||
node_interval = 5
|
||||
node_count = 0
|
||||
def progress_function(node):
|
||||
global progress_count
|
||||
if progress_count >= progress_total:
|
||||
return
|
||||
progress_count = min(progress_count + progress_interval, progress_total)
|
||||
progress = round(100. * progress_count / progress_total, 1)
|
||||
sys.stderr.write("\rBuilding: %5.1f%%" % progress if sys.stderr.isatty() else "progress: %.1f\n" % progress)
|
||||
if progress == 100. and sys.stderr.isatty():
|
||||
sys.stderr.write("\n")
|
||||
sys.stderr.flush()
|
||||
global node_count
|
||||
node_count += node_interval
|
||||
sys.stderr.write("progress: %d\n" % node_count)
|
||||
|
||||
Progress(progress_function, interval=progress_interval)
|
||||
AddPostAction(BUILD_TARGETS or [Dir('.')], prune_cache_dir)
|
||||
if os.environ.get('SCONS_PROGRESS'):
|
||||
Progress(progress_function, interval=node_interval)
|
||||
|
||||
# Release CI rebuilds tracked device outputs for the host architecture.
|
||||
if manifest_path := os.environ.get("STARPILOT_CI_BUILD_MANIFEST"):
|
||||
from tools.release.build_manifest import capture
|
||||
manifest_nodes = env.arg2nodes(BUILD_TARGETS or [Dir('.')], env.fs.Entry)
|
||||
manifest_seen = set()
|
||||
manifest_outputs = set()
|
||||
while manifest_nodes:
|
||||
manifest_node = manifest_nodes.pop().disambiguate()
|
||||
if manifest_node in manifest_seen:
|
||||
continue
|
||||
manifest_seen.add(manifest_node)
|
||||
manifest_executor = manifest_node.get_executor()
|
||||
if manifest_executor is not None:
|
||||
manifest_nodes += manifest_executor.get_all_prerequisites() + manifest_executor.get_all_children()
|
||||
for target in manifest_executor.get_all_targets():
|
||||
if target.is_derived() and hasattr(target, 'abspath'):
|
||||
relative = os.path.relpath(target.abspath, Dir('#').abspath)
|
||||
if not relative.startswith('../'):
|
||||
manifest_outputs.add(relative)
|
||||
capture(Dir('#').abspath, manifest_path, manifest_outputs,
|
||||
{'arch': arch, 'targets': list(BUILD_TARGETS), 'extras': GetOption('extras'),
|
||||
'ccflags': GetOption('ccflags'), 'release': release, 'arguments': dict(ARGUMENTS),
|
||||
'toolchain': {key: str(env.get(key, '')) for key in
|
||||
('CC', 'CXX', 'CCFLAGS', 'CFLAGS', 'CXXFLAGS', 'LINKFLAGS')}})
|
||||
SHARED = False
|
||||
|
||||
# TODO: this can probably be removed
|
||||
def abspath(x):
|
||||
if arch == 'aarch64':
|
||||
pth = os.path.join("/data/pythonpath", x[0].path)
|
||||
env.Depends(pth, x)
|
||||
return File(pth)
|
||||
else:
|
||||
# rpath works elsewhere
|
||||
return x[0].path.rsplit("/", 1)[1][:-3]
|
||||
|
||||
# Cython build enviroment
|
||||
py_include = sysconfig.get_paths()['include']
|
||||
envCython = env.Clone()
|
||||
envCython["CPPPATH"] += [py_include, np.get_include()]
|
||||
envCython["CCFLAGS"] += ["-Wno-#warnings", "-Wno-shadow", "-Wno-deprecated-declarations"]
|
||||
|
||||
envCython["LIBS"] = []
|
||||
if arch == "Darwin":
|
||||
envCython["LINKFLAGS"] = ["-bundle", "-undefined", "dynamic_lookup"]
|
||||
elif arch == "aarch64":
|
||||
envCython["LINKFLAGS"] = ["-shared"]
|
||||
envCython["LIBS"] = [os.path.basename(py_include)]
|
||||
else:
|
||||
envCython["LINKFLAGS"] = ["-pthread", "-shared"]
|
||||
|
||||
Export('envCython')
|
||||
|
||||
# Qt build environment
|
||||
qt_env = env.Clone()
|
||||
qt_modules = ["Widgets", "Gui", "Core", "Network", "Concurrent", "Multimedia", "Quick", "Qml", "QuickWidgets", "Location", "Positioning", "DBus"]
|
||||
|
||||
qt_libs = []
|
||||
if arch == "Darwin":
|
||||
if real_arch == "arm64":
|
||||
qt_env['QTDIR'] = "/opt/homebrew/opt/qt@5"
|
||||
else:
|
||||
qt_env['QTDIR'] = "/usr/local/opt/qt@5"
|
||||
qt_dirs = [
|
||||
os.path.join(qt_env['QTDIR'], "include"),
|
||||
]
|
||||
qt_dirs += [f"{qt_env['QTDIR']}/include/Qt{m}" for m in qt_modules]
|
||||
qt_env["LINKFLAGS"] += ["-F" + os.path.join(qt_env['QTDIR'], "lib")]
|
||||
qt_env["FRAMEWORKS"] += [f"Qt{m}" for m in qt_modules] + ["OpenGL"]
|
||||
qt_env.AppendENVPath('PATH', os.path.join(qt_env['QTDIR'], "bin"))
|
||||
else:
|
||||
qt_env['QTDIR'] = "/usr"
|
||||
qt_dirs = [
|
||||
f"/usr/include/{real_arch}-linux-gnu/qt5",
|
||||
f"/usr/include/{real_arch}-linux-gnu/qt5/QtGui/5.12.8/QtGui",
|
||||
]
|
||||
qt_dirs += [f"/usr/include/{real_arch}-linux-gnu/qt5/Qt{m}" for m in qt_modules]
|
||||
|
||||
qt_libs = [f"Qt5{m}" for m in qt_modules]
|
||||
if arch == "larch64":
|
||||
qt_libs += ["GLESv2", "wayland-client"]
|
||||
elif arch != "Darwin":
|
||||
qt_libs += ["GL"]
|
||||
|
||||
qt_env.Tool('qt')
|
||||
qt_env['CPPPATH'] += qt_dirs + ["#selfdrive/ui/qt/"]
|
||||
qt_flags = [
|
||||
"-D_REENTRANT",
|
||||
"-DQT_NO_DEBUG",
|
||||
"-DQT_WIDGETS_LIB",
|
||||
"-DQT_GUI_LIB",
|
||||
"-DQT_QUICK_LIB",
|
||||
"-DQT_QUICKWIDGETS_LIB",
|
||||
"-DQT_QML_LIB",
|
||||
"-DQT_CORE_LIB",
|
||||
"-DQT_MESSAGELOGCONTEXT",
|
||||
]
|
||||
qt_env['CXXFLAGS'] += qt_flags
|
||||
qt_env['LIBPATH'] += ['#selfdrive/ui']
|
||||
qt_env['LIBS'] = qt_libs
|
||||
|
||||
if GetOption("clazy"):
|
||||
checks = [
|
||||
"level0",
|
||||
"level1",
|
||||
"no-range-loop",
|
||||
"no-non-pod-global-static",
|
||||
]
|
||||
qt_env['CXX'] = 'clazy'
|
||||
qt_env['ENV']['CLAZY_IGNORE_DIRS'] = qt_dirs[0]
|
||||
qt_env['ENV']['CLAZY_CHECKS'] = ','.join(checks)
|
||||
|
||||
Export('env', 'qt_env', 'arch', 'real_arch', 'SHARED', 'USE_WEBCAM')
|
||||
|
||||
SConscript(['common/SConscript'])
|
||||
Import('_common', '_gpucommon')
|
||||
|
||||
if SHARED:
|
||||
common, gpucommon = abspath(common), abspath(gpucommon)
|
||||
else:
|
||||
common = [_common, 'json11']
|
||||
gpucommon = [_gpucommon]
|
||||
|
||||
Export('common', 'gpucommon')
|
||||
|
||||
# cereal and messaging are shared with the system
|
||||
SConscript(['cereal/SConscript'])
|
||||
if SHARED:
|
||||
cereal = abspath([File('cereal/libcereal_shared.so')])
|
||||
messaging = abspath([File('cereal/libmessaging_shared.so')])
|
||||
else:
|
||||
cereal = [File('#cereal/libcereal.a')]
|
||||
messaging = [File('#cereal/libmessaging.a')]
|
||||
visionipc = [File('#cereal/libvisionipc.a')]
|
||||
|
||||
Export('cereal', 'messaging', 'visionipc')
|
||||
|
||||
# Build rednose library and ekf models
|
||||
|
||||
rednose_config = {
|
||||
'generated_folder': '#selfdrive/locationd/models/generated',
|
||||
'to_build': {
|
||||
'live': ('#selfdrive/locationd/models/live_kf.py', True, ['live_kf_constants.h']),
|
||||
'car': ('#selfdrive/locationd/models/car_kf.py', True, []),
|
||||
},
|
||||
}
|
||||
|
||||
if arch != "larch64":
|
||||
rednose_config['to_build'].update({
|
||||
'gnss': ('#selfdrive/locationd/models/gnss_kf.py', True, []),
|
||||
'loc_4': ('#selfdrive/locationd/models/loc_kf.py', True, []),
|
||||
'pos_computer_4': ('#rednose/helpers/lst_sq_computer.py', False, []),
|
||||
'pos_computer_5': ('#rednose/helpers/lst_sq_computer.py', False, []),
|
||||
'feature_handler_5': ('#rednose/helpers/feature_handler.py', False, []),
|
||||
'lane': ('#xx/pipeline/lib/ekf/lane_kf.py', True, []),
|
||||
})
|
||||
|
||||
Export('rednose_config')
|
||||
SConscript(['rednose/SConscript'])
|
||||
|
||||
# Build openpilot
|
||||
|
||||
SConscript(['cereal/SConscript'])
|
||||
SConscript(['panda/board/SConscript'])
|
||||
SConscript(['opendbc/can/SConscript'])
|
||||
|
||||
SConscript(['third_party/SConscript'])
|
||||
|
||||
SConscript(['common/kalman/SConscript'])
|
||||
SConscript(['common/transformations/SConscript'])
|
||||
|
||||
SConscript(['selfdrive/camerad/SConscript'])
|
||||
SConscript(['selfdrive/modeld/SConscript'])
|
||||
|
||||
SConscript(['selfdrive/controls/lib/cluster/SConscript'])
|
||||
SConscript(['selfdrive/controls/lib/lateral_mpc_lib/SConscript'])
|
||||
SConscript(['selfdrive/controls/lib/longitudinal_mpc_lib/SConscript'])
|
||||
|
||||
SConscript(['selfdrive/boardd/SConscript'])
|
||||
SConscript(['selfdrive/proclogd/SConscript'])
|
||||
SConscript(['selfdrive/clocksd/SConscript'])
|
||||
|
||||
SConscript(['selfdrive/loggerd/SConscript'])
|
||||
|
||||
SConscript(['selfdrive/locationd/SConscript'])
|
||||
SConscript(['selfdrive/sensord/SConscript'])
|
||||
SConscript(['selfdrive/ui/SConscript'])
|
||||
|
||||
if arch != "Darwin":
|
||||
SConscript(['selfdrive/logcatd/SConscript'])
|
||||
|
||||
if GetOption('test'):
|
||||
SConscript('panda/tests/safety/SConscript')
|
||||
|
||||
external_sconscript = GetOption('external_sconscript')
|
||||
if external_sconscript:
|
||||
SConscript([external_sconscript])
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
# Security Policy
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
Suspected vulnerabilities can be reported to both `adeeb@comma.ai` and `security@comma.ai`.
|
||||
@@ -1,76 +0,0 @@
|
||||
# Third-party notices
|
||||
|
||||
## Jetlink
|
||||
|
||||
Jetlink's comma client is copyright (c) 2026 Zeph Leggett and distributed under the MIT License. Its complete license and permission notice are retained in [jetlink_repo/LICENSE](jetlink_repo/LICENSE); source and revision are recorded in [CREDITS.md](CREDITS.md#jetlink-external-inference) and `upstream-sync.json`.
|
||||
|
||||
## MRR35 Hyundai angle and radar
|
||||
|
||||
The MRR35 radar generator and related Hyundai angle integration derive from the frozen StarPilot snapshot `678af783`, with relevant sunnypilot/opendbc angle-branch ancestry at `cc4b08625a98e94b318cab15e45e05dad58042bd`. Both upstream `LICENSE` and `LICENSE.md` were published for that source. Their scope is not unambiguous, so both applicable notices and the extension-file attribution are retained here without choosing between them. See [CREDITS.md](CREDITS.md) for the narrow source map.
|
||||
|
||||
### Upstream `opendbc` `LICENSE` notice (MIT)
|
||||
|
||||
Copyright (c) 2020, Comma.ai, Inc.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
|
||||
associated documentation files (the "Software"), to deal in the Software without restriction,
|
||||
including without limitation the rights to use, copy, modify, merge, publish, distribute,
|
||||
sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or
|
||||
substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
|
||||
NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT
|
||||
OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
### Upstream extension file notice
|
||||
|
||||
> Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
|
||||
>
|
||||
> This file is part of sunnypilot and is licensed under the MIT License.
|
||||
> See the LICENSE.md file in the root directory for more details.
|
||||
|
||||
### Upstream `LICENSE.md` notice (published as “Custom MIT License”)
|
||||
|
||||
The following notice is reproduced verbatim from the reference branch:
|
||||
|
||||
> # Custom MIT License
|
||||
>
|
||||
> Copyright (c) 2024, Haibin Wen, SUNNYPILOT LLC
|
||||
>
|
||||
> Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to view and modify the Software, subject to the following conditions:
|
||||
>
|
||||
> 1. **Permission Required**: Permission Required for Commercial, For-Profit, or Closed Source Use: Use of the Software, in whole or in part, for any commercial purposes, for-profit projects, or in closed source projects requires explicit written permission from the original author(s).
|
||||
>
|
||||
> 2. **Redistribution**: Any redistribution of the Software, modified or unmodified, must retain this license notice and the following acknowledgment:
|
||||
> "This software is licensed under a custom license requiring permission for use."
|
||||
>
|
||||
> 3. **Visibility**: Any project that uses the Software must visibly mention the following acknowledgment:
|
||||
> "This project uses software from Haibin Wen and SUNNYPILOT LLC and is licensed under a custom license requiring permission for use."
|
||||
>
|
||||
> 4. **No Warranty**: THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
>
|
||||
> Contact sunnypilot Support <support@sunnypilot.ai> for permission requests.
|
||||
>
|
||||
> ---
|
||||
>
|
||||
> Haibin Wen, SUNNYPILOT LLC
|
||||
|
||||
Required upstream acknowledgments:
|
||||
|
||||
> This software is licensed under a custom license requiring permission for use.
|
||||
>
|
||||
> This project uses software from Haibin Wen and SUNNYPILOT LLC and is licensed under a custom license requiring permission for use.
|
||||
|
||||
For commercial, for-profit, or closed-source use, consult the upstream notice and obtain any
|
||||
permission it requires. The upstream repository's simultaneous publication of two differently
|
||||
scoped notices should be clarified with the relevant copyright holders before relying on one to
|
||||
the exclusion of the other.
|
||||
|
||||
## BluePilot Ford support
|
||||
|
||||
The Ford lateral strategies, CAN constructors, camera-radar filtering and native checks include work adapted from BluePilot's `bp-7.0` source, through StarPilot `2efe6017bfe14cee4807833e05f9a7f6ba255cdb`. See [CREDITS.md](CREDITS.md#ford-support-adapted-from-bluepilot) for contributors and source revisions. The upstream extension-file and `LICENSE.md` notices reproduced above also apply to the Ford extension sources; they are retained without resolving the difference between their published license descriptions.
|
||||
@@ -1,134 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
# Project build shortcuts. AGNOS builds natively; laptops use the ARM64 container.
|
||||
set -euo pipefail
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
if [[ "${1:-}" == -h || "${1:-}" == --help ]]; then
|
||||
cat <<'EOF'
|
||||
Usage: ./build [jobs] [scons options]
|
||||
./build [--params] [--cereal] [--panda] [jobs] [scons options]
|
||||
./build --mapd
|
||||
./build --check-startup <Params root or namespace> <recovery storage>
|
||||
|
||||
On AGNOS/aarch64, builds run natively. On laptops, builds use a Linux/ARM64 container.
|
||||
--params builds libparams_c.so; --cereal builds cereal outputs; --panda
|
||||
builds signed panda (C3), panda_h7 and body_h7 firmware only. Numeric jobs become -jN.
|
||||
RELEASE and CERT are environment variables, not trailing SCons arguments:
|
||||
RELEASE=1 CERT=/path/to/certificate ./build --panda 4
|
||||
CERT must name a readable certificate file. Omit RELEASE for DEBUG firmware.
|
||||
--mapd explicitly packages the pinned offline Linux/ARM64 Go provider; it
|
||||
does not fetch map regions or participate in the default build.
|
||||
Run scripts/laptop_device_build.sh doctor to check local prerequisites.
|
||||
Full laptop builds require a source-matched QCOM artifact package in
|
||||
.cache/qcom-artifact-package; device builds still compile models natively.
|
||||
--check-startup checks retained settings admission read-only using the local
|
||||
native developer runtime. Supply a matching Params namespace and recovery
|
||||
storage. A relocated copy can fail its path-bound profile marker.
|
||||
EOF
|
||||
exit 0
|
||||
fi
|
||||
if [[ "${1:-}" == --check-startup ]]; then
|
||||
if [[ $# -ne 3 ]]; then
|
||||
echo "Usage: ./build --check-startup <Params root or namespace> <recovery storage>" >&2
|
||||
exit 2
|
||||
fi
|
||||
exec "${ROOT_DIR}/dev" python "${ROOT_DIR}/tools/check_startup.py" "$2" "$3"
|
||||
fi
|
||||
for arg in "$@"; do
|
||||
case "${arg}" in
|
||||
RELEASE=*|CERT=*)
|
||||
echo "ERROR: RELEASE and CERT must be environment variables, not SCons arguments." >&2
|
||||
echo "Use: RELEASE=1 CERT=/path/to/certificate ./build --panda 4" >&2
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
original_args=("$@")
|
||||
build_helper="${ROOT_DIR}/scripts/laptop_device_build.sh"
|
||||
if [[ -f /AGNOS && "$(uname -s)" == Linux && "$(uname -m)" == aarch64 ]]; then
|
||||
build_helper="${ROOT_DIR}/scripts/native_device_build.sh"
|
||||
fi
|
||||
if [[ $# -eq 1 && "$1" == --mapd ]]; then
|
||||
exec "${ROOT_DIR}/scripts/build_mapd_provider.sh"
|
||||
fi
|
||||
if [[ $# -gt 0 ]]; then
|
||||
for arg in "$@"; do
|
||||
if [[ "$arg" == --mapd ]]; then
|
||||
echo "ERROR: --mapd is a standalone package operation; use ./build --mapd" >&2
|
||||
exit 2
|
||||
fi
|
||||
done
|
||||
fi
|
||||
shortcut_targets=()
|
||||
passthrough_args=()
|
||||
shortcut_includes_panda=0
|
||||
panda_generated_files=(panda/board/obj/gitversion.h panda/board/obj/version)
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--params|-params|-param)
|
||||
shortcut_targets+=(openpilot/common/libparams_c.so)
|
||||
;;
|
||||
--panda|-panda)
|
||||
shortcut_includes_panda=1
|
||||
shortcut_targets+=(panda/board/obj/panda.bin.signed panda/board/obj/panda_h7.bin.signed panda/board/obj/body_h7.bin.signed)
|
||||
;;
|
||||
--cereal|-cereal)
|
||||
shortcut_targets+=(openpilot/cereal/libcereal.a openpilot/cereal/libsocketmaster.a
|
||||
openpilot/cereal/messaging/bridge openpilot/cereal/services.h)
|
||||
;;
|
||||
*)
|
||||
if [[ "$1" =~ ^[0-9]+$ ]]; then
|
||||
passthrough_args+=("-j$1")
|
||||
else
|
||||
passthrough_args+=("$1")
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
if [[ "${#shortcut_targets[@]}" -gt 0 ]]; then
|
||||
scons_args=(--no-scrub "${shortcut_targets[@]}")
|
||||
if [[ ${passthrough_args[0]+present} ]]; then
|
||||
scons_args+=("${passthrough_args[@]}")
|
||||
fi
|
||||
if [[ "$shortcut_includes_panda" -eq 1 ]]; then
|
||||
exec "${build_helper}" scons "${scons_args[@]}"
|
||||
fi
|
||||
|
||||
# Panda/SConscript updates these even for unrelated targets. Restore their
|
||||
# prior bytes for the non-firmware shortcuts, including on build failure.
|
||||
tmpdir="$(mktemp -d)"
|
||||
restore_panda_metadata() {
|
||||
local path safe
|
||||
for path in "${panda_generated_files[@]}"; do
|
||||
safe="${path//\//__}"
|
||||
if [[ -e "${tmpdir}/${safe}.missing" ]]; then
|
||||
rm -f "${ROOT_DIR}/${path}"
|
||||
else
|
||||
cp -p "${tmpdir}/${safe}" "${ROOT_DIR}/${path}"
|
||||
fi
|
||||
done
|
||||
rm -rf "${tmpdir}"
|
||||
}
|
||||
trap restore_panda_metadata EXIT
|
||||
for path in "${panda_generated_files[@]}"; do
|
||||
safe="${path//\//__}"
|
||||
if [[ -e "${ROOT_DIR}/${path}" ]]; then
|
||||
cp -p "${ROOT_DIR}/${path}" "${tmpdir}/${safe}"
|
||||
else
|
||||
touch "${tmpdir}/${safe}.missing"
|
||||
fi
|
||||
done
|
||||
"${build_helper}" scons "${scons_args[@]}"
|
||||
exit $?
|
||||
fi
|
||||
|
||||
python3 "${ROOT_DIR}/tools/release/stage_mapd_provider.py" --source "${ROOT_DIR}" --require-tracked
|
||||
|
||||
if [[ "${#original_args[@]}" -gt 0 ]]; then
|
||||
exec "${build_helper}" build "${original_args[@]}"
|
||||
fi
|
||||
exec "${build_helper}" build
|
||||
@@ -1,4 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
exec "${ROOT_DIR}/scripts/host_tool_runner.sh" c3 "$@"
|
||||
@@ -1,4 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
exec "${ROOT_DIR}/scripts/host_tool_runner.sh" c4 "$@"
|
||||
@@ -0,0 +1,21 @@
|
||||
gen
|
||||
node_modules
|
||||
package-lock.json
|
||||
*.tmp
|
||||
*.pyc
|
||||
__pycache__
|
||||
.*.swp
|
||||
.*.swo
|
||||
*.os
|
||||
*.o
|
||||
*.a
|
||||
|
||||
test_runner
|
||||
|
||||
libmessaging.*
|
||||
libmessaging_shared.*
|
||||
services.h
|
||||
.sconsign.dblite
|
||||
libcereal_shared.*
|
||||
.mypy_cache/
|
||||
catch2/
|
||||
@@ -0,0 +1,74 @@
|
||||
Import('env', 'envCython', 'arch', 'common')
|
||||
|
||||
import shutil
|
||||
|
||||
cereal_dir = Dir('.')
|
||||
gen_dir = Dir('gen')
|
||||
messaging_dir = Dir('messaging')
|
||||
|
||||
# Build cereal
|
||||
|
||||
schema_files = ['log.capnp', 'car.capnp', 'legacy.capnp']
|
||||
env.Command(["gen/c/include/c++.capnp.h"], [], "mkdir -p " + gen_dir.path + "/c/include && touch $TARGETS")
|
||||
env.Command([f'gen/cpp/{s}.c++' for s in schema_files] + [f'gen/cpp/{s}.h' for s in schema_files],
|
||||
schema_files,
|
||||
f"capnpc --src-prefix={cereal_dir.path} $SOURCES -o c++:{gen_dir.path}/cpp/")
|
||||
|
||||
# TODO: remove non shared cereal and messaging
|
||||
cereal_objects = env.SharedObject([f'gen/cpp/{s}.c++' for s in schema_files])
|
||||
|
||||
env.Library('cereal', cereal_objects)
|
||||
env.SharedLibrary('cereal_shared', cereal_objects)
|
||||
|
||||
# Build messaging
|
||||
|
||||
services_h = env.Command(['services.h'], ['services.py'], 'python3 ' + cereal_dir.path + '/services.py > $TARGET')
|
||||
|
||||
messaging_objects = env.SharedObject([
|
||||
'messaging/messaging.cc',
|
||||
'messaging/impl_zmq.cc',
|
||||
'messaging/impl_msgq.cc',
|
||||
'messaging/msgq.cc',
|
||||
'messaging/socketmaster.cc',
|
||||
])
|
||||
|
||||
messaging_lib = env.Library('messaging', messaging_objects)
|
||||
Depends('messaging/impl_zmq.cc', services_h)
|
||||
|
||||
env.Program('messaging/bridge', ['messaging/bridge.cc'], LIBS=[messaging_lib, 'zmq', common])
|
||||
Depends('messaging/bridge.cc', services_h)
|
||||
|
||||
envCython.Program('messaging/messaging_pyx.so', 'messaging/messaging_pyx.pyx', LIBS=envCython["LIBS"]+[messaging_lib, "zmq", common])
|
||||
|
||||
|
||||
# Build Vision IPC
|
||||
vipc_sources = [
|
||||
'visionipc/ipc.cc',
|
||||
'visionipc/visionipc_server.cc',
|
||||
'visionipc/visionipc_client.cc',
|
||||
'visionipc/visionbuf.cc',
|
||||
]
|
||||
|
||||
if arch == "larch64":
|
||||
vipc_sources += ['visionipc/visionbuf_ion.cc']
|
||||
else:
|
||||
vipc_sources += ['visionipc/visionbuf_cl.cc']
|
||||
|
||||
vipc_objects = env.SharedObject(vipc_sources)
|
||||
vipc = env.Library('visionipc', vipc_objects)
|
||||
|
||||
|
||||
vipc_frameworks = []
|
||||
vipc_libs = envCython["LIBS"] + [vipc, messaging_lib, common, "zmq"]
|
||||
if arch == "Darwin":
|
||||
vipc_frameworks.append('OpenCL')
|
||||
else:
|
||||
vipc_libs.append('OpenCL')
|
||||
envCython.Program('visionipc/visionipc_pyx.so', 'visionipc/visionipc_pyx.pyx',
|
||||
LIBS=vipc_libs, FRAMEWORKS=vipc_frameworks)
|
||||
|
||||
if GetOption('test'):
|
||||
env.Program('messaging/test_runner', ['messaging/test_runner.cc', 'messaging/msgq_tests.cc'], LIBS=[messaging_lib, common])
|
||||
|
||||
env.Program('visionipc/test_runner', ['visionipc/test_runner.cc', 'visionipc/visionipc_tests.cc'],
|
||||
LIBS=['pthread'] + vipc_libs, FRAMEWORKS=vipc_frameworks)
|
||||
@@ -0,0 +1,9 @@
|
||||
# pylint: skip-file
|
||||
import os
|
||||
import capnp
|
||||
|
||||
CEREAL_PATH = os.path.dirname(os.path.abspath(__file__))
|
||||
capnp.remove_import_hook()
|
||||
|
||||
log = capnp.load(os.path.join(CEREAL_PATH, "log.capnp"))
|
||||
car = capnp.load(os.path.join(CEREAL_PATH, "car.capnp"))
|
||||
@@ -0,0 +1,646 @@
|
||||
using Cxx = import "./include/c++.capnp";
|
||||
$Cxx.namespace("cereal");
|
||||
|
||||
@0x8e2af1e708af8b8d;
|
||||
|
||||
# ******* events causing controls state machine transition *******
|
||||
|
||||
struct CarEvent @0x9b1657f34caf3ad3 {
|
||||
name @0 :EventName;
|
||||
|
||||
# event types
|
||||
enable @1 :Bool;
|
||||
noEntry @2 :Bool;
|
||||
warning @3 :Bool; # alerts presented only when enabled or soft disabling
|
||||
userDisable @4 :Bool;
|
||||
softDisable @5 :Bool;
|
||||
immediateDisable @6 :Bool;
|
||||
preEnable @7 :Bool;
|
||||
permanent @8 :Bool; # alerts presented regardless of openpilot state
|
||||
override @9 :Bool;
|
||||
|
||||
enum EventName @0xbaa8c5d505f727de {
|
||||
canError @0;
|
||||
steerUnavailable @1;
|
||||
brakeUnavailable @2;
|
||||
wrongGear @4;
|
||||
doorOpen @5;
|
||||
seatbeltNotLatched @6;
|
||||
espDisabled @7;
|
||||
wrongCarMode @8;
|
||||
steerTempUnavailable @9;
|
||||
reverseGear @10;
|
||||
buttonCancel @11;
|
||||
buttonEnable @12;
|
||||
pedalPressed @13; # exits active state
|
||||
pedalPressedPreEnable @73; # added during pre-enable state for either pedal
|
||||
gasPressedOverride @108; # added when user is pressing gas with no disengage on gas
|
||||
cruiseDisabled @14;
|
||||
speedTooLow @17;
|
||||
outOfSpace @18;
|
||||
overheat @19;
|
||||
calibrationIncomplete @20;
|
||||
calibrationInvalid @21;
|
||||
controlsMismatch @22;
|
||||
pcmEnable @23;
|
||||
pcmDisable @24;
|
||||
noTarget @25;
|
||||
radarFault @26;
|
||||
brakeHold @28;
|
||||
parkBrake @29;
|
||||
manualRestart @30;
|
||||
lowSpeedLockout @31;
|
||||
plannerError @32;
|
||||
joystickDebug @34;
|
||||
steerTempUnavailableSilent @35;
|
||||
resumeRequired @36;
|
||||
preDriverDistracted @37;
|
||||
promptDriverDistracted @38;
|
||||
driverDistracted @39;
|
||||
preDriverUnresponsive @43;
|
||||
promptDriverUnresponsive @44;
|
||||
driverUnresponsive @45;
|
||||
belowSteerSpeed @46;
|
||||
lowBattery @48;
|
||||
vehicleModelInvalid @50;
|
||||
accFaulted @51;
|
||||
sensorDataInvalid @52;
|
||||
commIssue @53;
|
||||
commIssueAvgFreq @109;
|
||||
tooDistracted @54;
|
||||
posenetInvalid @55;
|
||||
soundsUnavailable @56;
|
||||
preLaneChangeLeft @57;
|
||||
preLaneChangeRight @58;
|
||||
laneChange @59;
|
||||
lowMemory @63;
|
||||
stockAeb @64;
|
||||
ldw @65;
|
||||
carUnrecognized @66;
|
||||
invalidLkasSetting @69;
|
||||
speedTooHigh @70;
|
||||
laneChangeBlocked @71;
|
||||
relayMalfunction @72;
|
||||
stockFcw @74;
|
||||
startup @75;
|
||||
startupNoCar @76;
|
||||
startupNoControl @77;
|
||||
startupMaster @78;
|
||||
startupNoFw @104;
|
||||
fcw @79;
|
||||
steerSaturated @80;
|
||||
belowEngageSpeed @84;
|
||||
noGps @85;
|
||||
wrongCruiseMode @87;
|
||||
modeldLagging @89;
|
||||
deviceFalling @90;
|
||||
fanMalfunction @91;
|
||||
cameraMalfunction @92;
|
||||
cameraFrameRate @110;
|
||||
gpsMalfunction @94;
|
||||
processNotRunning @95;
|
||||
dashcamMode @96;
|
||||
controlsInitializing @98;
|
||||
usbError @99;
|
||||
roadCameraError @100;
|
||||
driverCameraError @101;
|
||||
wideRoadCameraError @102;
|
||||
localizerMalfunction @103;
|
||||
highCpuUsage @105;
|
||||
cruiseMismatch @106;
|
||||
lkasDisabled @107;
|
||||
canBusMissing @111;
|
||||
controlsdLagging @112;
|
||||
|
||||
radarCanErrorDEPRECATED @15;
|
||||
communityFeatureDisallowedDEPRECATED @62;
|
||||
radarCommIssueDEPRECATED @67;
|
||||
driverMonitorLowAccDEPRECATED @68;
|
||||
gasUnavailableDEPRECATED @3;
|
||||
dataNeededDEPRECATED @16;
|
||||
modelCommIssueDEPRECATED @27;
|
||||
ipasOverrideDEPRECATED @33;
|
||||
geofenceDEPRECATED @40;
|
||||
driverMonitorOnDEPRECATED @41;
|
||||
driverMonitorOffDEPRECATED @42;
|
||||
calibrationProgressDEPRECATED @47;
|
||||
invalidGiraffeHondaDEPRECATED @49;
|
||||
invalidGiraffeToyotaDEPRECATED @60;
|
||||
internetConnectivityNeededDEPRECATED @61;
|
||||
whitePandaUnsupportedDEPRECATED @81;
|
||||
commIssueWarningDEPRECATED @83;
|
||||
focusRecoverActiveDEPRECATED @86;
|
||||
neosUpdateRequiredDEPRECATED @88;
|
||||
modelLagWarningDEPRECATED @93;
|
||||
startupOneplusDEPRECATED @82;
|
||||
startupFuzzyFingerprintDEPRECATED @97;
|
||||
}
|
||||
}
|
||||
|
||||
# ******* main car state @ 100hz *******
|
||||
# all speeds in m/s
|
||||
|
||||
struct CarState {
|
||||
events @13 :List(CarEvent);
|
||||
|
||||
# CAN health
|
||||
canValid @26 :Bool; # invalid counter/checksums
|
||||
canTimeout @40 :Bool; # CAN bus dropped out
|
||||
|
||||
# car speed
|
||||
vEgo @1 :Float32; # best estimate of speed
|
||||
aEgo @16 :Float32; # best estimate of acceleration
|
||||
vEgoRaw @17 :Float32; # unfiltered speed from CAN sensors
|
||||
yawRate @22 :Float32; # best estimate of yaw rate
|
||||
standstill @18 :Bool;
|
||||
wheelSpeeds @2 :WheelSpeeds;
|
||||
|
||||
# gas pedal, 0.0-1.0
|
||||
gas @3 :Float32; # this is user pedal only
|
||||
gasPressed @4 :Bool; # this is user pedal only
|
||||
|
||||
# brake pedal, 0.0-1.0
|
||||
brake @5 :Float32; # this is user pedal only
|
||||
brakePressed @6 :Bool; # this is user pedal only
|
||||
parkingBrake @39 :Bool;
|
||||
brakeHoldActive @38 :Bool;
|
||||
|
||||
# steering wheel
|
||||
steeringAngleDeg @7 :Float32;
|
||||
steeringAngleOffsetDeg @37 :Float32; # Offset betweens sensors in case there multiple
|
||||
steeringRateDeg @15 :Float32;
|
||||
steeringTorque @8 :Float32; # TODO: standardize units
|
||||
steeringTorqueEps @27 :Float32; # TODO: standardize units
|
||||
steeringPressed @9 :Bool; # if the user is using the steering wheel
|
||||
steeringRateLimited @29 :Bool; # if the torque is limited by the rate limiter
|
||||
steerFaultTemporary @35 :Bool; # temporary EPS fault
|
||||
steerFaultPermanent @36 :Bool; # permanent EPS fault
|
||||
stockAeb @30 :Bool;
|
||||
stockFcw @31 :Bool;
|
||||
espDisabled @32 :Bool;
|
||||
accFaulted @42 :Bool;
|
||||
|
||||
# cruise state
|
||||
cruiseState @10 :CruiseState;
|
||||
|
||||
# gear
|
||||
gearShifter @14 :GearShifter;
|
||||
|
||||
# button presses
|
||||
buttonEvents @11 :List(ButtonEvent);
|
||||
leftBlinker @20 :Bool;
|
||||
rightBlinker @21 :Bool;
|
||||
genericToggle @23 :Bool;
|
||||
|
||||
# lock info
|
||||
doorOpen @24 :Bool;
|
||||
seatbeltUnlatched @25 :Bool;
|
||||
|
||||
# clutch (manual transmission only)
|
||||
clutchPressed @28 :Bool;
|
||||
|
||||
# which packets this state came from
|
||||
canMonoTimes @12: List(UInt64);
|
||||
|
||||
# blindspot sensors
|
||||
leftBlindspot @33 :Bool; # Is there something blocking the left lane change
|
||||
rightBlindspot @34 :Bool; # Is there something blocking the right lane change
|
||||
|
||||
fuelGauge @41 :Float32; # battery or fuel tank level from 0.0 to 1.0
|
||||
charging @43 :Bool;
|
||||
|
||||
struct WheelSpeeds {
|
||||
# optional wheel speeds
|
||||
fl @0 :Float32;
|
||||
fr @1 :Float32;
|
||||
rl @2 :Float32;
|
||||
rr @3 :Float32;
|
||||
}
|
||||
|
||||
struct CruiseState {
|
||||
enabled @0 :Bool;
|
||||
speed @1 :Float32;
|
||||
available @2 :Bool;
|
||||
speedOffset @3 :Float32;
|
||||
standstill @4 :Bool;
|
||||
nonAdaptive @5 :Bool;
|
||||
}
|
||||
|
||||
enum GearShifter {
|
||||
unknown @0;
|
||||
park @1;
|
||||
drive @2;
|
||||
neutral @3;
|
||||
reverse @4;
|
||||
sport @5;
|
||||
low @6;
|
||||
brake @7;
|
||||
eco @8;
|
||||
manumatic @9;
|
||||
}
|
||||
|
||||
# send on change
|
||||
struct ButtonEvent {
|
||||
pressed @0 :Bool;
|
||||
type @1 :Type;
|
||||
|
||||
enum Type {
|
||||
unknown @0;
|
||||
leftBlinker @1;
|
||||
rightBlinker @2;
|
||||
accelCruise @3;
|
||||
decelCruise @4;
|
||||
cancel @5;
|
||||
altButton1 @6;
|
||||
altButton2 @7;
|
||||
altButton3 @8;
|
||||
setCruise @9;
|
||||
resumeCruise @10;
|
||||
gapAdjustCruise @11;
|
||||
}
|
||||
}
|
||||
|
||||
errorsDEPRECATED @0 :List(CarEvent.EventName);
|
||||
brakeLightsDEPRECATED @19 :Bool;
|
||||
}
|
||||
|
||||
# ******* radar state @ 20hz *******
|
||||
|
||||
struct RadarData @0x888ad6581cf0aacb {
|
||||
errors @0 :List(Error);
|
||||
points @1 :List(RadarPoint);
|
||||
|
||||
# which packets this state came from
|
||||
canMonoTimes @2 :List(UInt64);
|
||||
|
||||
enum Error {
|
||||
canError @0;
|
||||
fault @1;
|
||||
wrongConfig @2;
|
||||
}
|
||||
|
||||
# similar to LiveTracks
|
||||
# is one timestamp valid for all? I think so
|
||||
struct RadarPoint {
|
||||
trackId @0 :UInt64; # no trackId reuse
|
||||
|
||||
# these 3 are the minimum required
|
||||
dRel @1 :Float32; # m from the front bumper of the car
|
||||
yRel @2 :Float32; # m
|
||||
vRel @3 :Float32; # m/s
|
||||
|
||||
# these are optional and valid if they are not NaN
|
||||
aRel @4 :Float32; # m/s^2
|
||||
yvRel @5 :Float32; # m/s
|
||||
|
||||
# some radars flag measurements VS estimates
|
||||
measured @6 :Bool;
|
||||
}
|
||||
}
|
||||
|
||||
# ******* car controls @ 100hz *******
|
||||
|
||||
struct CarControl {
|
||||
# must be true for any actuator commands to work
|
||||
enabled @0 :Bool;
|
||||
latActive @11: Bool;
|
||||
longActive @12: Bool;
|
||||
|
||||
# Actuator commands as computed by controlsd
|
||||
actuators @6 :Actuators;
|
||||
|
||||
# Any car specific rate limits or quirks applied by
|
||||
# the CarController are reflected in actuatorsOutput
|
||||
# and matches what is sent to the car
|
||||
actuatorsOutput @10 :Actuators;
|
||||
|
||||
orientationNED @13 :List(Float32);
|
||||
angularVelocity @14 :List(Float32);
|
||||
|
||||
cruiseControl @4 :CruiseControl;
|
||||
hudControl @5 :HUDControl;
|
||||
|
||||
struct Actuators {
|
||||
# range from 0.0 - 1.0
|
||||
gas @0: Float32;
|
||||
brake @1: Float32;
|
||||
# range from -1.0 - 1.0
|
||||
steer @2: Float32;
|
||||
steeringAngleDeg @3: Float32;
|
||||
|
||||
speed @6: Float32; # m/s
|
||||
accel @4: Float32; # m/s^2
|
||||
longControlState @5: LongControlState;
|
||||
|
||||
enum LongControlState @0xe40f3a917d908282{
|
||||
off @0;
|
||||
pid @1;
|
||||
stopping @2;
|
||||
|
||||
startingDEPRECATED @3;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
struct CruiseControl {
|
||||
cancel @0: Bool;
|
||||
override @1: Bool;
|
||||
speedOverride @2: Float32;
|
||||
accelOverride @3: Float32;
|
||||
}
|
||||
|
||||
struct HUDControl {
|
||||
speedVisible @0: Bool;
|
||||
setSpeed @1: Float32;
|
||||
lanesVisible @2: Bool;
|
||||
leadVisible @3: Bool;
|
||||
visualAlert @4: VisualAlert;
|
||||
audibleAlert @5: AudibleAlert;
|
||||
rightLaneVisible @6: Bool;
|
||||
leftLaneVisible @7: Bool;
|
||||
rightLaneDepart @8: Bool;
|
||||
leftLaneDepart @9: Bool;
|
||||
|
||||
enum VisualAlert {
|
||||
# these are the choices from the Honda
|
||||
# map as good as you can for your car
|
||||
none @0;
|
||||
fcw @1;
|
||||
steerRequired @2;
|
||||
brakePressed @3;
|
||||
wrongGear @4;
|
||||
seatbeltUnbuckled @5;
|
||||
speedTooHigh @6;
|
||||
ldw @7;
|
||||
}
|
||||
|
||||
enum AudibleAlert {
|
||||
none @0;
|
||||
|
||||
engage @1;
|
||||
disengage @2;
|
||||
refuse @3;
|
||||
|
||||
warningSoft @4;
|
||||
warningImmediate @5;
|
||||
|
||||
prompt @6;
|
||||
promptRepeat @7;
|
||||
promptDistracted @8;
|
||||
}
|
||||
}
|
||||
|
||||
gasDEPRECATED @1 :Float32;
|
||||
brakeDEPRECATED @2 :Float32;
|
||||
steeringTorqueDEPRECATED @3 :Float32;
|
||||
activeDEPRECATED @7 :Bool;
|
||||
rollDEPRECATED @8 :Float32;
|
||||
pitchDEPRECATED @9 :Float32;
|
||||
}
|
||||
|
||||
# ****** car param ******
|
||||
|
||||
struct CarParams {
|
||||
carName @0 :Text;
|
||||
carFingerprint @1 :Text;
|
||||
fuzzyFingerprint @55 :Bool;
|
||||
|
||||
notCar @66 :Bool; # flag for non-car robotics platforms
|
||||
|
||||
enableGasInterceptor @2 :Bool;
|
||||
pcmCruise @3 :Bool; # is openpilot's state tied to the PCM's cruise state?
|
||||
enableDsu @5 :Bool; # driving support unit
|
||||
enableApgs @6 :Bool; # advanced parking guidance system
|
||||
enableBsm @56 :Bool; # blind spot monitoring
|
||||
flags @64 :UInt32; # flags for car specific quirks
|
||||
|
||||
minEnableSpeed @7 :Float32;
|
||||
minSteerSpeed @8 :Float32;
|
||||
maxSteeringAngleDeg @54 :Float32;
|
||||
safetyConfigs @62 :List(SafetyConfig);
|
||||
alternativeExperience @65 :Int16; # panda flag for features like no disengage on gas
|
||||
|
||||
steerMaxBPDEPRECATED @11 :List(Float32);
|
||||
steerMaxVDEPRECATED @12 :List(Float32);
|
||||
gasMaxBPDEPRECATED @13 :List(Float32);
|
||||
gasMaxVDEPRECATED @14 :List(Float32);
|
||||
brakeMaxBPDEPRECATED @15 :List(Float32);
|
||||
brakeMaxVDEPRECATED @16 :List(Float32);
|
||||
|
||||
# things about the car in the manual
|
||||
mass @17 :Float32; # [kg] curb weight: all fluids no cargo
|
||||
wheelbase @18 :Float32; # [m] distance from rear axle to front axle
|
||||
centerToFront @19 :Float32; # [m] distance from center of mass to front axle
|
||||
steerRatio @20 :Float32; # [] ratio of steering wheel angle to front wheel angle
|
||||
steerRatioRear @21 :Float32; # [] ratio of steering wheel angle to rear wheel angle (usually 0)
|
||||
|
||||
# things we can derive
|
||||
rotationalInertia @22 :Float32; # [kg*m2] body rotational inertia
|
||||
tireStiffnessFront @23 :Float32; # [N/rad] front tire coeff of stiff
|
||||
tireStiffnessRear @24 :Float32; # [N/rad] rear tire coeff of stiff
|
||||
|
||||
longitudinalTuning @25 :LongitudinalPIDTuning;
|
||||
lateralParams @48 :LateralParams;
|
||||
lateralTuning :union {
|
||||
pid @26 :LateralPIDTuning;
|
||||
indi @27 :LateralINDITuning;
|
||||
lqr @40 :LateralLQRTuning;
|
||||
torque @67 :LateralTorqueTuning;
|
||||
}
|
||||
|
||||
steerLimitAlert @28 :Bool;
|
||||
steerLimitTimer @47 :Float32; # time before steerLimitAlert is issued
|
||||
|
||||
vEgoStopping @29 :Float32; # Speed at which the car goes into stopping state
|
||||
vEgoStarting @59 :Float32; # Speed at which the car goes into starting state
|
||||
directAccelControl @30 :Bool; # Does the car have direct accel control or just gas/brake
|
||||
stoppingControl @31 :Bool; # Does the car allows full control even at lows speeds when stopping
|
||||
stopAccel @60 :Float32; # Required acceleraton to keep vehicle stationary
|
||||
steerRateCost @33 :Float32; # Lateral MPC cost on steering rate
|
||||
steerControlType @34 :SteerControlType;
|
||||
radarOffCan @35 :Bool; # True when radar objects aren't visible on CAN
|
||||
stoppingDecelRate @52 :Float32; # m/s^2/s while trying to stop
|
||||
|
||||
steerActuatorDelay @36 :Float32; # Steering wheel actuator delay in seconds
|
||||
longitudinalActuatorDelayLowerBound @61 :Float32; # Gas/Brake actuator delay in seconds, lower bound
|
||||
longitudinalActuatorDelayUpperBound @58 :Float32; # Gas/Brake actuator delay in seconds, upper bound
|
||||
openpilotLongitudinalControl @37 :Bool; # is openpilot doing the longitudinal control?
|
||||
carVin @38 :Text; # VIN number queried during fingerprinting
|
||||
dashcamOnly @41: Bool;
|
||||
transmissionType @43 :TransmissionType;
|
||||
carFw @44 :List(CarFw);
|
||||
|
||||
radarTimeStep @45: Float32 = 0.05; # time delta between radar updates, 20Hz is very standard
|
||||
fingerprintSource @49: FingerprintSource;
|
||||
networkLocation @50 :NetworkLocation; # Where Panda/C2 is integrated into the car's CAN network
|
||||
|
||||
wheelSpeedFactor @63 :Float32; # Multiplier on wheels speeds to computer actual speeds
|
||||
|
||||
struct SafetyConfig {
|
||||
safetyModel @0 :SafetyModel;
|
||||
safetyParam @3 :UInt16;
|
||||
safetyParamDEPRECATED @1 :Int16;
|
||||
safetyParam2DEPRECATED @2 :UInt32;
|
||||
}
|
||||
|
||||
struct LateralParams {
|
||||
torqueBP @0 :List(Int32);
|
||||
torqueV @1 :List(Int32);
|
||||
}
|
||||
|
||||
struct LateralPIDTuning {
|
||||
kpBP @0 :List(Float32);
|
||||
kpV @1 :List(Float32);
|
||||
kiBP @2 :List(Float32);
|
||||
kiV @3 :List(Float32);
|
||||
kf @4 :Float32;
|
||||
}
|
||||
|
||||
struct LateralTorqueTuning {
|
||||
useSteeringAngle @0 :Bool;
|
||||
kp @1 :Float32;
|
||||
ki @2 :Float32;
|
||||
friction @3 :Float32;
|
||||
kf @4 :Float32;
|
||||
}
|
||||
|
||||
struct LongitudinalPIDTuning {
|
||||
kpBP @0 :List(Float32);
|
||||
kpV @1 :List(Float32);
|
||||
kiBP @2 :List(Float32);
|
||||
kiV @3 :List(Float32);
|
||||
kf @6 :Float32;
|
||||
deadzoneBP @4 :List(Float32);
|
||||
deadzoneV @5 :List(Float32);
|
||||
}
|
||||
|
||||
struct LateralINDITuning {
|
||||
outerLoopGainBP @4 :List(Float32);
|
||||
outerLoopGainV @5 :List(Float32);
|
||||
innerLoopGainBP @6 :List(Float32);
|
||||
innerLoopGainV @7 :List(Float32);
|
||||
timeConstantBP @8 :List(Float32);
|
||||
timeConstantV @9 :List(Float32);
|
||||
actuatorEffectivenessBP @10 :List(Float32);
|
||||
actuatorEffectivenessV @11 :List(Float32);
|
||||
|
||||
outerLoopGainDEPRECATED @0 :Float32;
|
||||
innerLoopGainDEPRECATED @1 :Float32;
|
||||
timeConstantDEPRECATED @2 :Float32;
|
||||
actuatorEffectivenessDEPRECATED @3 :Float32;
|
||||
}
|
||||
|
||||
struct LateralLQRTuning {
|
||||
scale @0 :Float32;
|
||||
ki @1 :Float32;
|
||||
dcGain @2 :Float32;
|
||||
|
||||
# State space system
|
||||
a @3 :List(Float32);
|
||||
b @4 :List(Float32);
|
||||
c @5 :List(Float32);
|
||||
|
||||
k @6 :List(Float32); # LQR gain
|
||||
l @7 :List(Float32); # Kalman gain
|
||||
}
|
||||
|
||||
enum SafetyModel {
|
||||
silent @0;
|
||||
hondaNidec @1;
|
||||
toyota @2;
|
||||
elm327 @3;
|
||||
gm @4;
|
||||
hondaBoschGiraffe @5;
|
||||
ford @6;
|
||||
cadillac @7;
|
||||
hyundai @8;
|
||||
chrysler @9;
|
||||
tesla @10;
|
||||
subaru @11;
|
||||
gmPassive @12;
|
||||
mazda @13;
|
||||
nissan @14;
|
||||
volkswagen @15;
|
||||
toyotaIpas @16;
|
||||
allOutput @17;
|
||||
gmAscm @18;
|
||||
noOutput @19; # like silent but without silent CAN TXs
|
||||
hondaBosch @20;
|
||||
volkswagenPq @21;
|
||||
subaruLegacy @22; # pre-Global platform
|
||||
hyundaiLegacy @23;
|
||||
hyundaiCommunity @24;
|
||||
stellantis @25;
|
||||
faw @26;
|
||||
body @27;
|
||||
hyundaiHDA2 @28;
|
||||
}
|
||||
|
||||
enum SteerControlType {
|
||||
torque @0;
|
||||
angle @1;
|
||||
}
|
||||
|
||||
enum TransmissionType {
|
||||
unknown @0;
|
||||
automatic @1; # Traditional auto, including DSG
|
||||
manual @2; # True "stick shift" only
|
||||
direct @3; # Electric vehicle or other direct drive
|
||||
cvt @4;
|
||||
}
|
||||
|
||||
struct CarFw {
|
||||
ecu @0 :Ecu;
|
||||
fwVersion @1 :Data;
|
||||
address @2: UInt32;
|
||||
subAddress @3: UInt8;
|
||||
}
|
||||
|
||||
enum Ecu {
|
||||
eps @0;
|
||||
esp @1;
|
||||
fwdRadar @2;
|
||||
fwdCamera @3;
|
||||
engine @4;
|
||||
unknown @5;
|
||||
transmission @8; # Transmission Control Module
|
||||
srs @9; # airbag
|
||||
gateway @10; # can gateway
|
||||
hud @11; # heads up display
|
||||
combinationMeter @12; # instrument cluster
|
||||
|
||||
# Toyota only
|
||||
dsu @6;
|
||||
apgs @7;
|
||||
|
||||
# Honda only
|
||||
vsa @13; # Vehicle Stability Assist
|
||||
programmedFuelInjection @14;
|
||||
electricBrakeBooster @15;
|
||||
shiftByWire @16;
|
||||
|
||||
debug @17;
|
||||
}
|
||||
|
||||
enum FingerprintSource {
|
||||
can @0;
|
||||
fw @1;
|
||||
fixed @2;
|
||||
}
|
||||
|
||||
enum NetworkLocation {
|
||||
fwdCamera @0; # Standard/default integration at LKAS camera
|
||||
gateway @1; # Integration at vehicle's CAN gateway
|
||||
}
|
||||
|
||||
enableCameraDEPRECATED @4 :Bool;
|
||||
isPandaBlackDEPRECATED @39 :Bool;
|
||||
hasStockCameraDEPRECATED @57 :Bool;
|
||||
safetyParamDEPRECATED @10 :Int16;
|
||||
safetyModelDEPRECATED @9 :SafetyModel;
|
||||
safetyModelPassiveDEPRECATED @42 :SafetyModel = silent;
|
||||
minSpeedCanDEPRECATED @51 :Float32;
|
||||
startAccelDEPRECATED @32 :Float32;
|
||||
communityFeatureDEPRECATED @46: Bool;
|
||||
startingAccelRateDEPRECATED @53 :Float32;
|
||||
}
|
||||
@@ -0,0 +1,574 @@
|
||||
using Cxx = import "./include/c++.capnp";
|
||||
$Cxx.namespace("cereal");
|
||||
|
||||
@0x80ef1ec4889c2a63;
|
||||
|
||||
# legacy.capnp: a home for deprecated structs
|
||||
|
||||
struct LogRotate @0x9811e1f38f62f2d1 {
|
||||
segmentNum @0 :Int32;
|
||||
path @1 :Text;
|
||||
}
|
||||
|
||||
struct LiveUI @0xc08240f996aefced {
|
||||
rearViewCam @0 :Bool;
|
||||
alertText1 @1 :Text;
|
||||
alertText2 @2 :Text;
|
||||
awarenessStatus @3 :Float32;
|
||||
}
|
||||
|
||||
struct UiLayoutState @0x88dcce08ad29dda0 {
|
||||
activeApp @0 :App;
|
||||
sidebarCollapsed @1 :Bool;
|
||||
mapEnabled @2 :Bool;
|
||||
mockEngaged @3 :Bool;
|
||||
|
||||
enum App @0x9917470acf94d285 {
|
||||
home @0;
|
||||
music @1;
|
||||
nav @2;
|
||||
settings @3;
|
||||
none @4;
|
||||
}
|
||||
}
|
||||
|
||||
struct OrbslamCorrection @0x8afd33dc9b35e1aa {
|
||||
correctionMonoTime @0 :UInt64;
|
||||
prePositionECEF @1 :List(Float64);
|
||||
postPositionECEF @2 :List(Float64);
|
||||
prePoseQuatECEF @3 :List(Float32);
|
||||
postPoseQuatECEF @4 :List(Float32);
|
||||
numInliers @5 :UInt32;
|
||||
}
|
||||
|
||||
struct EthernetPacket @0xa99a9d5b33cf5859 {
|
||||
pkt @0 :Data;
|
||||
ts @1 :Float32;
|
||||
}
|
||||
|
||||
struct CellInfo @0xcff7566681c277ce {
|
||||
timestamp @0 :UInt64;
|
||||
repr @1 :Text; # android toString() for now
|
||||
}
|
||||
|
||||
struct WifiScan @0xd4df5a192382ba0b {
|
||||
bssid @0 :Text;
|
||||
ssid @1 :Text;
|
||||
capabilities @2 :Text;
|
||||
frequency @3 :Int32;
|
||||
level @4 :Int32;
|
||||
timestamp @5 :Int64;
|
||||
|
||||
centerFreq0 @6 :Int32;
|
||||
centerFreq1 @7 :Int32;
|
||||
channelWidth @8 :ChannelWidth;
|
||||
operatorFriendlyName @9 :Text;
|
||||
venueName @10 :Text;
|
||||
is80211mcResponder @11 :Bool;
|
||||
passpoint @12 :Bool;
|
||||
|
||||
distanceCm @13 :Int32;
|
||||
distanceSdCm @14 :Int32;
|
||||
|
||||
enum ChannelWidth @0xcb6a279f015f6b51 {
|
||||
w20Mhz @0;
|
||||
w40Mhz @1;
|
||||
w80Mhz @2;
|
||||
w160Mhz @3;
|
||||
w80Plus80Mhz @4;
|
||||
}
|
||||
}
|
||||
|
||||
struct LiveEventData @0x94b7baa90c5c321e {
|
||||
name @0 :Text;
|
||||
value @1 :Int32;
|
||||
}
|
||||
|
||||
struct ModelData @0xb8aad62cffef28a9 {
|
||||
frameId @0 :UInt32;
|
||||
frameAge @12 :UInt32;
|
||||
frameDropPerc @13 :Float32;
|
||||
timestampEof @9 :UInt64;
|
||||
modelExecutionTime @14 :Float32;
|
||||
gpuExecutionTime @16 :Float32;
|
||||
rawPred @15 :Data;
|
||||
|
||||
path @1 :PathData;
|
||||
leftLane @2 :PathData;
|
||||
rightLane @3 :PathData;
|
||||
lead @4 :LeadData;
|
||||
freePath @6 :List(Float32);
|
||||
|
||||
settings @5 :ModelSettings;
|
||||
leadFuture @7 :LeadData;
|
||||
speed @8 :List(Float32);
|
||||
meta @10 :MetaData;
|
||||
longitudinal @11 :LongitudinalData;
|
||||
|
||||
struct PathData @0x8817eeea389e9f08 {
|
||||
points @0 :List(Float32);
|
||||
prob @1 :Float32;
|
||||
std @2 :Float32;
|
||||
stds @3 :List(Float32);
|
||||
poly @4 :List(Float32);
|
||||
validLen @5 :Float32;
|
||||
}
|
||||
|
||||
struct LeadData @0xd1c9bef96d26fa91 {
|
||||
dist @0 :Float32;
|
||||
prob @1 :Float32;
|
||||
std @2 :Float32;
|
||||
relVel @3 :Float32;
|
||||
relVelStd @4 :Float32;
|
||||
relY @5 :Float32;
|
||||
relYStd @6 :Float32;
|
||||
relA @7 :Float32;
|
||||
relAStd @8 :Float32;
|
||||
}
|
||||
|
||||
struct ModelSettings @0xa26e3710efd3e914 {
|
||||
bigBoxX @0 :UInt16;
|
||||
bigBoxY @1 :UInt16;
|
||||
bigBoxWidth @2 :UInt16;
|
||||
bigBoxHeight @3 :UInt16;
|
||||
boxProjection @4 :List(Float32);
|
||||
yuvCorrection @5 :List(Float32);
|
||||
inputTransform @6 :List(Float32);
|
||||
}
|
||||
|
||||
struct MetaData @0x9744f25fb60f2bf8 {
|
||||
engagedProb @0 :Float32;
|
||||
desirePrediction @1 :List(Float32);
|
||||
brakeDisengageProb @2 :Float32;
|
||||
gasDisengageProb @3 :Float32;
|
||||
steerOverrideProb @4 :Float32;
|
||||
desireState @5 :List(Float32);
|
||||
}
|
||||
|
||||
struct LongitudinalData @0xf98f999c6a071122 {
|
||||
distances @2 :List(Float32);
|
||||
speeds @0 :List(Float32);
|
||||
accelerations @1 :List(Float32);
|
||||
}
|
||||
}
|
||||
|
||||
struct ECEFPoint @0xc25bbbd524983447 {
|
||||
x @0 :Float64;
|
||||
y @1 :Float64;
|
||||
z @2 :Float64;
|
||||
}
|
||||
|
||||
struct ECEFPointDEPRECATED @0xe10e21168db0c7f7 {
|
||||
x @0 :Float32;
|
||||
y @1 :Float32;
|
||||
z @2 :Float32;
|
||||
}
|
||||
|
||||
struct GPSPlannerPoints @0xab54c59699f8f9f3 {
|
||||
curPosDEPRECATED @0 :ECEFPointDEPRECATED;
|
||||
pointsDEPRECATED @1 :List(ECEFPointDEPRECATED);
|
||||
curPos @6 :ECEFPoint;
|
||||
points @7 :List(ECEFPoint);
|
||||
valid @2 :Bool;
|
||||
trackName @3 :Text;
|
||||
speedLimit @4 :Float32;
|
||||
accelTarget @5 :Float32;
|
||||
}
|
||||
|
||||
struct GPSPlannerPlan @0xf5ad1d90cdc1dd6b {
|
||||
valid @0 :Bool;
|
||||
poly @1 :List(Float32);
|
||||
trackName @2 :Text;
|
||||
speed @3 :Float32;
|
||||
acceleration @4 :Float32;
|
||||
pointsDEPRECATED @5 :List(ECEFPointDEPRECATED);
|
||||
points @6 :List(ECEFPoint);
|
||||
xLookahead @7 :Float32;
|
||||
}
|
||||
|
||||
struct UiNavigationEvent @0x90c8426c3eaddd3b {
|
||||
type @0: Type;
|
||||
status @1: Status;
|
||||
distanceTo @2: Float32;
|
||||
endRoadPointDEPRECATED @3: ECEFPointDEPRECATED;
|
||||
endRoadPoint @4: ECEFPoint;
|
||||
|
||||
enum Type @0xe8db07dcf8fcea05 {
|
||||
none @0;
|
||||
laneChangeLeft @1;
|
||||
laneChangeRight @2;
|
||||
mergeLeft @3;
|
||||
mergeRight @4;
|
||||
turnLeft @5;
|
||||
turnRight @6;
|
||||
}
|
||||
|
||||
enum Status @0xb9aa88c75ef99a1f {
|
||||
none @0;
|
||||
passive @1;
|
||||
approaching @2;
|
||||
active @3;
|
||||
}
|
||||
}
|
||||
|
||||
struct LiveLocationData @0xb99b2bc7a57e8128 {
|
||||
status @0 :UInt8;
|
||||
|
||||
# 3D fix
|
||||
lat @1 :Float64;
|
||||
lon @2 :Float64;
|
||||
alt @3 :Float32; # m
|
||||
|
||||
# speed
|
||||
speed @4 :Float32; # m/s
|
||||
|
||||
# NED velocity components
|
||||
vNED @5 :List(Float32);
|
||||
|
||||
# roll, pitch, heading (x,y,z)
|
||||
roll @6 :Float32; # WRT to center of earth?
|
||||
pitch @7 :Float32; # WRT to center of earth?
|
||||
heading @8 :Float32; # WRT to north?
|
||||
|
||||
# what are these?
|
||||
wanderAngle @9 :Float32;
|
||||
trackAngle @10 :Float32;
|
||||
|
||||
# car frame -- https://upload.wikimedia.org/wikipedia/commons/f/f5/RPY_angles_of_cars.png
|
||||
|
||||
# gyro, in car frame, deg/s
|
||||
gyro @11 :List(Float32);
|
||||
|
||||
# accel, in car frame, m/s^2
|
||||
accel @12 :List(Float32);
|
||||
|
||||
accuracy @13 :Accuracy;
|
||||
|
||||
source @14 :SensorSource;
|
||||
# if we are fixing a location in the past
|
||||
fixMonoTime @15 :UInt64;
|
||||
|
||||
gpsWeek @16 :Int32;
|
||||
timeOfWeek @17 :Float64;
|
||||
|
||||
positionECEF @18 :List(Float64);
|
||||
poseQuatECEF @19 :List(Float32);
|
||||
pitchCalibration @20 :Float32;
|
||||
yawCalibration @21 :Float32;
|
||||
imuFrame @22 :List(Float32);
|
||||
|
||||
struct Accuracy @0x943dc4625473b03f {
|
||||
pNEDError @0 :List(Float32);
|
||||
vNEDError @1 :List(Float32);
|
||||
rollError @2 :Float32;
|
||||
pitchError @3 :Float32;
|
||||
headingError @4 :Float32;
|
||||
ellipsoidSemiMajorError @5 :Float32;
|
||||
ellipsoidSemiMinorError @6 :Float32;
|
||||
ellipsoidOrientationError @7 :Float32;
|
||||
}
|
||||
|
||||
enum SensorSource @0xc871d3cc252af657 {
|
||||
applanix @0;
|
||||
kalman @1;
|
||||
orbslam @2;
|
||||
timing @3;
|
||||
dummy @4;
|
||||
}
|
||||
}
|
||||
|
||||
struct OrbOdometry @0xd7700859ed1f5b76 {
|
||||
# timing first
|
||||
startMonoTime @0 :UInt64;
|
||||
endMonoTime @1 :UInt64;
|
||||
|
||||
# fundamental matrix and error
|
||||
f @2: List(Float64);
|
||||
err @3: Float64;
|
||||
|
||||
# number of inlier points
|
||||
inliers @4: Int32;
|
||||
|
||||
# for debug only
|
||||
# indexed by endMonoTime features
|
||||
# value is startMonoTime feature match
|
||||
# -1 if no match
|
||||
matches @5: List(Int16);
|
||||
}
|
||||
|
||||
struct OrbFeatures @0xcd60164a8a0159ef {
|
||||
timestampEof @0 :UInt64;
|
||||
# transposed arrays of normalized image coordinates
|
||||
# len(xs) == len(ys) == len(descriptors) * 32
|
||||
xs @1 :List(Float32);
|
||||
ys @2 :List(Float32);
|
||||
descriptors @3 :Data;
|
||||
octaves @4 :List(Int8);
|
||||
|
||||
# match index to last OrbFeatures
|
||||
# -1 if no match
|
||||
timestampLastEof @5 :UInt64;
|
||||
matches @6: List(Int16);
|
||||
}
|
||||
|
||||
struct OrbFeaturesSummary @0xd500d30c5803fa4f {
|
||||
timestampEof @0 :UInt64;
|
||||
timestampLastEof @1 :UInt64;
|
||||
|
||||
featureCount @2 :UInt16;
|
||||
matchCount @3 :UInt16;
|
||||
computeNs @4 :UInt64;
|
||||
}
|
||||
|
||||
struct OrbKeyFrame @0xc8233c0345e27e24 {
|
||||
# this is a globally unique id for the KeyFrame
|
||||
id @0: UInt64;
|
||||
|
||||
# this is the location of the KeyFrame
|
||||
pos @1: ECEFPoint;
|
||||
|
||||
# these are the features in the world
|
||||
# len(dpos) == len(descriptors) * 32
|
||||
dpos @2 :List(ECEFPoint);
|
||||
descriptors @3 :Data;
|
||||
}
|
||||
|
||||
struct KalmanOdometry @0x92e21bb7ea38793a {
|
||||
trans @0 :List(Float32); # m/s in device frame
|
||||
rot @1 :List(Float32); # rad/s in device frame
|
||||
transStd @2 :List(Float32); # std m/s in device frame
|
||||
rotStd @3 :List(Float32); # std rad/s in device frame
|
||||
}
|
||||
|
||||
struct OrbObservation @0x9b326d4e436afec7 {
|
||||
observationMonoTime @0 :UInt64;
|
||||
normalizedCoordinates @1 :List(Float32);
|
||||
locationECEF @2 :List(Float64);
|
||||
matchDistance @3: UInt32;
|
||||
}
|
||||
|
||||
struct CalibrationFeatures @0x8fdfadb254ea867a {
|
||||
frameId @0 :UInt32;
|
||||
|
||||
p0 @1 :List(Float32);
|
||||
p1 @2 :List(Float32);
|
||||
status @3 :List(Int8);
|
||||
}
|
||||
|
||||
struct NavStatus @0xbd8822120928120c {
|
||||
isNavigating @0 :Bool;
|
||||
currentAddress @1 :Address;
|
||||
|
||||
struct Address @0xce7cd672cacc7814 {
|
||||
title @0 :Text;
|
||||
lat @1 :Float64;
|
||||
lng @2 :Float64;
|
||||
house @3 :Text;
|
||||
address @4 :Text;
|
||||
street @5 :Text;
|
||||
city @6 :Text;
|
||||
state @7 :Text;
|
||||
country @8 :Text;
|
||||
}
|
||||
}
|
||||
|
||||
struct NavUpdate @0xdb98be6565516acb {
|
||||
isNavigating @0 :Bool;
|
||||
curSegment @1 :Int32;
|
||||
segments @2 :List(Segment);
|
||||
|
||||
struct LatLng @0x9eaef9187cadbb9b {
|
||||
lat @0 :Float64;
|
||||
lng @1 :Float64;
|
||||
}
|
||||
|
||||
struct Segment @0xa5b39b4fc4d7da3f {
|
||||
from @0 :LatLng;
|
||||
to @1 :LatLng;
|
||||
updateTime @2 :Int32;
|
||||
distance @3 :Int32;
|
||||
crossTime @4 :Int32;
|
||||
exitNo @5 :Int32;
|
||||
instruction @6 :Instruction;
|
||||
|
||||
parts @7 :List(LatLng);
|
||||
|
||||
enum Instruction @0xc5417a637451246f {
|
||||
turnLeft @0;
|
||||
turnRight @1;
|
||||
keepLeft @2;
|
||||
keepRight @3;
|
||||
straight @4;
|
||||
roundaboutExitNumber @5;
|
||||
roundaboutExit @6;
|
||||
roundaboutTurnLeft @7;
|
||||
unkn8 @8;
|
||||
roundaboutStraight @9;
|
||||
unkn10 @10;
|
||||
roundaboutTurnRight @11;
|
||||
unkn12 @12;
|
||||
roundaboutUturn @13;
|
||||
unkn14 @14;
|
||||
arrive @15;
|
||||
exitLeft @16;
|
||||
exitRight @17;
|
||||
unkn18 @18;
|
||||
uturn @19;
|
||||
# ...
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct TrafficEvent @0xacfa74a094e62626 {
|
||||
type @0 :Type;
|
||||
distance @1 :Float32;
|
||||
action @2 :Action;
|
||||
resuming @3 :Bool;
|
||||
|
||||
enum Type @0xd85d75253435bf4b {
|
||||
stopSign @0;
|
||||
lightRed @1;
|
||||
lightYellow @2;
|
||||
lightGreen @3;
|
||||
stopLight @4;
|
||||
}
|
||||
|
||||
enum Action @0xa6f6ce72165ccb49 {
|
||||
none @0;
|
||||
yield @1;
|
||||
stop @2;
|
||||
resumeReady @3;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
struct AndroidGnss @0xdfdf30d03fc485bd {
|
||||
union {
|
||||
measurements @0 :Measurements;
|
||||
navigationMessage @1 :NavigationMessage;
|
||||
}
|
||||
|
||||
struct Measurements @0xa20710d4f428d6cd {
|
||||
clock @0 :Clock;
|
||||
measurements @1 :List(Measurement);
|
||||
|
||||
struct Clock @0xa0e27b453a38f450 {
|
||||
timeNanos @0 :Int64;
|
||||
hardwareClockDiscontinuityCount @1 :Int32;
|
||||
|
||||
hasTimeUncertaintyNanos @2 :Bool;
|
||||
timeUncertaintyNanos @3 :Float64;
|
||||
|
||||
hasLeapSecond @4 :Bool;
|
||||
leapSecond @5 :Int32;
|
||||
|
||||
hasFullBiasNanos @6 :Bool;
|
||||
fullBiasNanos @7 :Int64;
|
||||
|
||||
hasBiasNanos @8 :Bool;
|
||||
biasNanos @9 :Float64;
|
||||
|
||||
hasBiasUncertaintyNanos @10 :Bool;
|
||||
biasUncertaintyNanos @11 :Float64;
|
||||
|
||||
hasDriftNanosPerSecond @12 :Bool;
|
||||
driftNanosPerSecond @13 :Float64;
|
||||
|
||||
hasDriftUncertaintyNanosPerSecond @14 :Bool;
|
||||
driftUncertaintyNanosPerSecond @15 :Float64;
|
||||
}
|
||||
|
||||
struct Measurement @0xd949bf717d77614d {
|
||||
svId @0 :Int32;
|
||||
constellation @1 :Constellation;
|
||||
|
||||
timeOffsetNanos @2 :Float64;
|
||||
state @3 :Int32;
|
||||
receivedSvTimeNanos @4 :Int64;
|
||||
receivedSvTimeUncertaintyNanos @5 :Int64;
|
||||
cn0DbHz @6 :Float64;
|
||||
pseudorangeRateMetersPerSecond @7 :Float64;
|
||||
pseudorangeRateUncertaintyMetersPerSecond @8 :Float64;
|
||||
accumulatedDeltaRangeState @9 :Int32;
|
||||
accumulatedDeltaRangeMeters @10 :Float64;
|
||||
accumulatedDeltaRangeUncertaintyMeters @11 :Float64;
|
||||
|
||||
hasCarrierFrequencyHz @12 :Bool;
|
||||
carrierFrequencyHz @13 :Float32;
|
||||
hasCarrierCycles @14 :Bool;
|
||||
carrierCycles @15 :Int64;
|
||||
hasCarrierPhase @16 :Bool;
|
||||
carrierPhase @17 :Float64;
|
||||
hasCarrierPhaseUncertainty @18 :Bool;
|
||||
carrierPhaseUncertainty @19 :Float64;
|
||||
hasSnrInDb @20 :Bool;
|
||||
snrInDb @21 :Float64;
|
||||
|
||||
multipathIndicator @22 :MultipathIndicator;
|
||||
|
||||
enum Constellation @0x9ef1f3ff0deb5ffb {
|
||||
unknown @0;
|
||||
gps @1;
|
||||
sbas @2;
|
||||
glonass @3;
|
||||
qzss @4;
|
||||
beidou @5;
|
||||
galileo @6;
|
||||
}
|
||||
|
||||
enum State @0xcbb9490adce12d72 {
|
||||
unknown @0;
|
||||
codeLock @1;
|
||||
bitSync @2;
|
||||
subframeSync @3;
|
||||
towDecoded @4;
|
||||
msecAmbiguous @5;
|
||||
symbolSync @6;
|
||||
gloStringSync @7;
|
||||
gloTodDecoded @8;
|
||||
bdsD2BitSync @9;
|
||||
bdsD2SubframeSync @10;
|
||||
galE1bcCodeLock @11;
|
||||
galE1c2ndCodeLock @12;
|
||||
galE1bPageSync @13;
|
||||
sbasSync @14;
|
||||
}
|
||||
|
||||
enum MultipathIndicator @0xc04e7b6231d4caa8 {
|
||||
unknown @0;
|
||||
detected @1;
|
||||
notDetected @2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct NavigationMessage @0xe2517b083095fd4e {
|
||||
type @0 :Int32;
|
||||
svId @1 :Int32;
|
||||
messageId @2 :Int32;
|
||||
submessageId @3 :Int32;
|
||||
data @4 :Data;
|
||||
status @5 :Status;
|
||||
|
||||
enum Status @0xec1ff7996b35366f {
|
||||
unknown @0;
|
||||
parityPassed @1;
|
||||
parityRebuilt @2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct LidarPts @0xe3d6685d4e9d8f7a {
|
||||
r @0 :List(UInt16); # uint16 m*500.0
|
||||
theta @1 :List(UInt16); # uint16 deg*100.0
|
||||
reflect @2 :List(UInt8); # uint8 0-255
|
||||
|
||||
# For storing out of file.
|
||||
idx @3 :UInt64;
|
||||
|
||||
# For storing in file
|
||||
pkt @4 :Data;
|
||||
}
|
||||
|
||||
|
||||
+1926
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,10 @@
|
||||
demo
|
||||
bridge
|
||||
test_runner
|
||||
*.o
|
||||
*.os
|
||||
*.d
|
||||
*.a
|
||||
*.so
|
||||
messaging_pyx.cpp
|
||||
build/
|
||||
@@ -0,0 +1,262 @@
|
||||
# must be build with scons
|
||||
from .messaging_pyx import Context, Poller, SubSocket, PubSocket # pylint: disable=no-name-in-module, import-error
|
||||
from .messaging_pyx import MultiplePublishersError, MessagingError # pylint: disable=no-name-in-module, import-error
|
||||
import os
|
||||
import capnp
|
||||
|
||||
from typing import Optional, List, Union
|
||||
from collections import deque
|
||||
|
||||
from cereal import log
|
||||
from cereal.services import service_list
|
||||
|
||||
assert MultiplePublishersError
|
||||
assert MessagingError
|
||||
|
||||
NO_TRAVERSAL_LIMIT = 2**64-1
|
||||
AVG_FREQ_HISTORY = 100
|
||||
SIMULATION = "SIMULATION" in os.environ
|
||||
|
||||
# sec_since_boot is faster, but allow to run standalone too
|
||||
try:
|
||||
from common.realtime import sec_since_boot
|
||||
except ImportError:
|
||||
import time
|
||||
sec_since_boot = time.time
|
||||
print("Warning, using python time.time() instead of faster sec_since_boot")
|
||||
|
||||
context = Context()
|
||||
|
||||
def log_from_bytes(dat: bytes) -> capnp.lib.capnp._DynamicStructReader:
|
||||
return log.Event.from_bytes(dat, traversal_limit_in_words=NO_TRAVERSAL_LIMIT)
|
||||
|
||||
def new_message(service: Optional[str] = None, size: Optional[int] = None) -> capnp.lib.capnp._DynamicStructBuilder:
|
||||
dat = log.Event.new_message()
|
||||
dat.logMonoTime = int(sec_since_boot() * 1e9)
|
||||
dat.valid = True
|
||||
if service is not None:
|
||||
if size is None:
|
||||
dat.init(service)
|
||||
else:
|
||||
dat.init(service, size)
|
||||
return dat
|
||||
|
||||
def pub_sock(endpoint: str) -> PubSocket:
|
||||
sock = PubSocket()
|
||||
sock.connect(context, endpoint)
|
||||
return sock
|
||||
|
||||
def sub_sock(endpoint: str, poller: Optional[Poller] = None, addr: str = "127.0.0.1",
|
||||
conflate: bool = False, timeout: Optional[int] = None) -> SubSocket:
|
||||
sock = SubSocket()
|
||||
sock.connect(context, endpoint, addr.encode('utf8'), conflate)
|
||||
|
||||
if timeout is not None:
|
||||
sock.setTimeout(timeout)
|
||||
|
||||
if poller is not None:
|
||||
poller.registerSocket(sock)
|
||||
return sock
|
||||
|
||||
|
||||
def drain_sock_raw(sock: SubSocket, wait_for_one: bool = False) -> List[bytes]:
|
||||
"""Receive all message currently available on the queue"""
|
||||
ret: List[bytes] = []
|
||||
while 1:
|
||||
if wait_for_one and len(ret) == 0:
|
||||
dat = sock.receive()
|
||||
else:
|
||||
dat = sock.receive(non_blocking=True)
|
||||
|
||||
if dat is None:
|
||||
break
|
||||
|
||||
ret.append(dat)
|
||||
|
||||
return ret
|
||||
|
||||
def drain_sock(sock: SubSocket, wait_for_one: bool = False) -> List[capnp.lib.capnp._DynamicStructReader]:
|
||||
"""Receive all message currently available on the queue"""
|
||||
ret: List[capnp.lib.capnp._DynamicStructReader] = []
|
||||
while 1:
|
||||
if wait_for_one and len(ret) == 0:
|
||||
dat = sock.receive()
|
||||
else:
|
||||
dat = sock.receive(non_blocking=True)
|
||||
|
||||
if dat is None: # Timeout hit
|
||||
break
|
||||
|
||||
dat = log_from_bytes(dat)
|
||||
ret.append(dat)
|
||||
|
||||
return ret
|
||||
|
||||
|
||||
# TODO: print when we drop packets?
|
||||
def recv_sock(sock: SubSocket, wait: bool = False) -> Optional[capnp.lib.capnp._DynamicStructReader]:
|
||||
"""Same as drain sock, but only returns latest message. Consider using conflate instead."""
|
||||
dat = None
|
||||
|
||||
while 1:
|
||||
if wait and dat is None:
|
||||
rcv = sock.receive()
|
||||
else:
|
||||
rcv = sock.receive(non_blocking=True)
|
||||
|
||||
if rcv is None: # Timeout hit
|
||||
break
|
||||
|
||||
dat = rcv
|
||||
|
||||
if dat is not None:
|
||||
dat = log_from_bytes(dat)
|
||||
|
||||
return dat
|
||||
|
||||
def recv_one(sock: SubSocket) -> Optional[capnp.lib.capnp._DynamicStructReader]:
|
||||
dat = sock.receive()
|
||||
if dat is not None:
|
||||
dat = log_from_bytes(dat)
|
||||
return dat
|
||||
|
||||
def recv_one_or_none(sock: SubSocket) -> Optional[capnp.lib.capnp._DynamicStructReader]:
|
||||
dat = sock.receive(non_blocking=True)
|
||||
if dat is not None:
|
||||
dat = log_from_bytes(dat)
|
||||
return dat
|
||||
|
||||
def recv_one_retry(sock: SubSocket) -> capnp.lib.capnp._DynamicStructReader:
|
||||
"""Keep receiving until we get a message"""
|
||||
while True:
|
||||
dat = sock.receive()
|
||||
if dat is not None:
|
||||
return log_from_bytes(dat)
|
||||
|
||||
class SubMaster:
|
||||
def __init__(self, services: List[str], poll: Optional[List[str]] = None,
|
||||
ignore_alive: Optional[List[str]] = None, ignore_avg_freq: Optional[List[str]] = None,
|
||||
addr: str = "127.0.0.1"):
|
||||
self.frame = -1
|
||||
self.updated = {s: False for s in services}
|
||||
self.rcv_time = {s: 0. for s in services}
|
||||
self.rcv_frame = {s: 0 for s in services}
|
||||
self.alive = {s: False for s in services}
|
||||
self.freq_ok = {s: False for s in services}
|
||||
self.recv_dts = {s: deque([0.0] * AVG_FREQ_HISTORY, maxlen=AVG_FREQ_HISTORY) for s in services}
|
||||
self.sock = {}
|
||||
self.freq = {}
|
||||
self.data = {}
|
||||
self.valid = {}
|
||||
self.logMonoTime = {}
|
||||
|
||||
self.poller = Poller()
|
||||
self.non_polled_services = [s for s in services if poll is not None and
|
||||
len(poll) and s not in poll]
|
||||
|
||||
self.ignore_average_freq = [] if ignore_avg_freq is None else ignore_avg_freq
|
||||
self.ignore_alive = [] if ignore_alive is None else ignore_alive
|
||||
|
||||
for s in services:
|
||||
if addr is not None:
|
||||
p = self.poller if s not in self.non_polled_services else None
|
||||
self.sock[s] = sub_sock(s, poller=p, addr=addr, conflate=True)
|
||||
self.freq[s] = service_list[s].frequency
|
||||
|
||||
try:
|
||||
data = new_message(s)
|
||||
except capnp.lib.capnp.KjException: # pylint: disable=c-extension-no-member
|
||||
data = new_message(s, 0) # lists
|
||||
|
||||
self.data[s] = getattr(data, s)
|
||||
self.logMonoTime[s] = 0
|
||||
self.valid[s] = data.valid
|
||||
|
||||
def __getitem__(self, s: str) -> capnp.lib.capnp._DynamicStructReader:
|
||||
return self.data[s]
|
||||
|
||||
def update(self, timeout: int = 1000) -> None:
|
||||
msgs = []
|
||||
for sock in self.poller.poll(timeout):
|
||||
msgs.append(recv_one_or_none(sock))
|
||||
|
||||
# non-blocking receive for non-polled sockets
|
||||
for s in self.non_polled_services:
|
||||
msgs.append(recv_one_or_none(self.sock[s]))
|
||||
self.update_msgs(sec_since_boot(), msgs)
|
||||
|
||||
def update_msgs(self, cur_time: float, msgs: List[capnp.lib.capnp._DynamicStructReader]) -> None:
|
||||
self.frame += 1
|
||||
self.updated = dict.fromkeys(self.updated, False)
|
||||
for msg in msgs:
|
||||
if msg is None:
|
||||
continue
|
||||
|
||||
s = msg.which()
|
||||
self.updated[s] = True
|
||||
|
||||
if self.rcv_time[s] > 1e-5 and self.freq[s] > 1e-5 and (s not in self.non_polled_services) \
|
||||
and (s not in self.ignore_average_freq):
|
||||
self.recv_dts[s].append(cur_time - self.rcv_time[s])
|
||||
|
||||
self.rcv_time[s] = cur_time
|
||||
self.rcv_frame[s] = self.frame
|
||||
self.data[s] = getattr(msg, s)
|
||||
self.logMonoTime[s] = msg.logMonoTime
|
||||
self.valid[s] = msg.valid
|
||||
|
||||
if SIMULATION:
|
||||
self.freq_ok[s] = True
|
||||
self.alive[s] = True
|
||||
|
||||
if not SIMULATION:
|
||||
for s in self.data:
|
||||
# arbitrary small number to avoid float comparison. If freq is 0, we can skip the check
|
||||
if self.freq[s] > 1e-5:
|
||||
# alive if delay is within 10x the expected frequency
|
||||
self.alive[s] = (cur_time - self.rcv_time[s]) < (10. / self.freq[s])
|
||||
|
||||
# TODO: check if update frequency is high enough to not drop messages
|
||||
# freq_ok if average frequency is higher than 90% of expected frequency
|
||||
avg_dt = sum(self.recv_dts[s]) / AVG_FREQ_HISTORY
|
||||
expected_dt = 1 / (self.freq[s] * 0.90)
|
||||
self.freq_ok[s] = (avg_dt < expected_dt)
|
||||
else:
|
||||
self.freq_ok[s] = True
|
||||
self.alive[s] = True
|
||||
|
||||
def all_alive(self, service_list=None) -> bool:
|
||||
if service_list is None: # check all
|
||||
service_list = self.alive.keys()
|
||||
return all(self.alive[s] for s in service_list if s not in self.ignore_alive)
|
||||
|
||||
def all_freq_ok(self, service_list=None) -> bool:
|
||||
if service_list is None: # check all
|
||||
service_list = self.alive.keys()
|
||||
return all(self.freq_ok[s] for s in service_list if s not in self.ignore_alive)
|
||||
|
||||
def all_valid(self, service_list=None) -> bool:
|
||||
if service_list is None: # check all
|
||||
service_list = self.valid.keys()
|
||||
return all(self.valid[s] for s in service_list)
|
||||
|
||||
def all_checks(self, service_list=None) -> bool:
|
||||
if service_list is None: # check all
|
||||
service_list = self.alive.keys()
|
||||
return self.all_alive(service_list=service_list) \
|
||||
and self.all_freq_ok(service_list=service_list) \
|
||||
and self.all_valid(service_list=service_list)
|
||||
|
||||
class PubMaster:
|
||||
def __init__(self, services: List[str]):
|
||||
self.sock = {}
|
||||
for s in services:
|
||||
self.sock[s] = pub_sock(s)
|
||||
|
||||
def send(self, s: str, dat: Union[bytes, capnp.lib.capnp._DynamicStructBuilder]) -> None:
|
||||
if not isinstance(dat, bytes):
|
||||
dat = dat.to_bytes()
|
||||
self.sock[s].send(dat)
|
||||
|
||||
def all_readers_updated(self, s: str) -> bool:
|
||||
return self.sock[s].all_readers_updated() # type: ignore
|
||||
@@ -0,0 +1,88 @@
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <csignal>
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
typedef void (*sighandler_t)(int sig);
|
||||
|
||||
#include "impl_msgq.h"
|
||||
#include "impl_zmq.h"
|
||||
#include "services.h"
|
||||
|
||||
std::atomic<bool> do_exit = false;
|
||||
static void set_do_exit(int sig) {
|
||||
do_exit = true;
|
||||
}
|
||||
|
||||
void sigpipe_handler(int sig) {
|
||||
assert(sig == SIGPIPE);
|
||||
std::cout << "SIGPIPE received" << std::endl;
|
||||
}
|
||||
|
||||
static std::vector<std::string> get_services(std::string whitelist_str, bool zmq_to_msgq) {
|
||||
std::vector<std::string> service_list;
|
||||
for (const auto& it : services) {
|
||||
std::string name = it.name;
|
||||
bool in_whitelist = whitelist_str.find(name) != std::string::npos;
|
||||
if (name == "plusFrame" || name == "uiLayoutState" || (zmq_to_msgq && !in_whitelist)) {
|
||||
continue;
|
||||
}
|
||||
service_list.push_back(name);
|
||||
}
|
||||
return service_list;
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
signal(SIGPIPE, (sighandler_t)sigpipe_handler);
|
||||
signal(SIGINT, (sighandler_t)set_do_exit);
|
||||
signal(SIGTERM, (sighandler_t)set_do_exit);
|
||||
|
||||
bool zmq_to_msgq = argc > 2;
|
||||
std::string ip = zmq_to_msgq ? argv[1] : "127.0.0.1";
|
||||
std::string whitelist_str = zmq_to_msgq ? std::string(argv[2]) : "";
|
||||
|
||||
Poller *poller;
|
||||
Context *pub_context;
|
||||
Context *sub_context;
|
||||
if (zmq_to_msgq) { // republishes zmq debugging messages as msgq
|
||||
poller = new ZMQPoller();
|
||||
pub_context = new MSGQContext();
|
||||
sub_context = new ZMQContext();
|
||||
} else {
|
||||
poller = new MSGQPoller();
|
||||
pub_context = new ZMQContext();
|
||||
sub_context = new MSGQContext();
|
||||
}
|
||||
|
||||
std::map<SubSocket*, PubSocket*> sub2pub;
|
||||
for (auto endpoint: get_services(whitelist_str, zmq_to_msgq)) {
|
||||
PubSocket * pub_sock;
|
||||
SubSocket * sub_sock;
|
||||
if (zmq_to_msgq) {
|
||||
pub_sock = new MSGQPubSocket();
|
||||
sub_sock = new ZMQSubSocket();
|
||||
} else {
|
||||
pub_sock = new ZMQPubSocket();
|
||||
sub_sock = new MSGQSubSocket();
|
||||
}
|
||||
pub_sock->connect(pub_context, endpoint);
|
||||
sub_sock->connect(sub_context, endpoint, ip, false);
|
||||
|
||||
poller->registerSocket(sub_sock);
|
||||
sub2pub[sub_sock] = pub_sock;
|
||||
}
|
||||
|
||||
while (!do_exit) {
|
||||
for (auto sub_sock : poller->poll(100)) {
|
||||
Message * msg = sub_sock->receive();
|
||||
if (msg == NULL) continue;
|
||||
int ret;
|
||||
do { ret = sub2pub[sub_sock]->sendMessage(msg); } while (ret == -1 && errno == EINTR);
|
||||
assert(ret >= 0);
|
||||
delete msg;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,230 @@
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
#include <cstdlib>
|
||||
#include <csignal>
|
||||
#include <cerrno>
|
||||
|
||||
#include "services.h"
|
||||
#include "impl_msgq.h"
|
||||
|
||||
|
||||
volatile sig_atomic_t msgq_do_exit = 0;
|
||||
|
||||
void sig_handler(int signal) {
|
||||
assert(signal == SIGINT || signal == SIGTERM);
|
||||
msgq_do_exit = 1;
|
||||
}
|
||||
|
||||
static bool service_exists(std::string path){
|
||||
for (const auto& it : services) {
|
||||
if (it.name == path) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static size_t get_size(std::string endpoint){
|
||||
size_t sz = DEFAULT_SEGMENT_SIZE;
|
||||
|
||||
if (endpoint == "roadCameraState" || endpoint == "driverCameraState" || endpoint == "wideRoadCameraState"){
|
||||
sz *= 10;
|
||||
}
|
||||
|
||||
return sz;
|
||||
}
|
||||
|
||||
|
||||
MSGQContext::MSGQContext() {
|
||||
}
|
||||
|
||||
MSGQContext::~MSGQContext() {
|
||||
}
|
||||
|
||||
void MSGQMessage::init(size_t sz) {
|
||||
size = sz;
|
||||
data = new char[size];
|
||||
}
|
||||
|
||||
void MSGQMessage::init(char * d, size_t sz) {
|
||||
size = sz;
|
||||
data = new char[size];
|
||||
memcpy(data, d, size);
|
||||
}
|
||||
|
||||
void MSGQMessage::takeOwnership(char * d, size_t sz) {
|
||||
size = sz;
|
||||
data = d;
|
||||
}
|
||||
|
||||
void MSGQMessage::close() {
|
||||
if (size > 0){
|
||||
delete[] data;
|
||||
}
|
||||
size = 0;
|
||||
}
|
||||
|
||||
MSGQMessage::~MSGQMessage() {
|
||||
this->close();
|
||||
}
|
||||
|
||||
int MSGQSubSocket::connect(Context *context, std::string endpoint, std::string address, bool conflate, bool check_endpoint){
|
||||
assert(context);
|
||||
assert(address == "127.0.0.1");
|
||||
|
||||
if (check_endpoint && !service_exists(std::string(endpoint))){
|
||||
std::cout << "Warning, " << std::string(endpoint) << " is not in service list." << std::endl;
|
||||
}
|
||||
|
||||
q = new msgq_queue_t;
|
||||
int r = msgq_new_queue(q, endpoint.c_str(), get_size(endpoint));
|
||||
if (r != 0){
|
||||
return r;
|
||||
}
|
||||
|
||||
msgq_init_subscriber(q);
|
||||
|
||||
if (conflate){
|
||||
q->read_conflate = true;
|
||||
}
|
||||
|
||||
timeout = -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
Message * MSGQSubSocket::receive(bool non_blocking){
|
||||
msgq_do_exit = 0;
|
||||
|
||||
void (*prev_handler_sigint)(int);
|
||||
void (*prev_handler_sigterm)(int);
|
||||
if (!non_blocking){
|
||||
prev_handler_sigint = std::signal(SIGINT, sig_handler);
|
||||
prev_handler_sigterm = std::signal(SIGTERM, sig_handler);
|
||||
}
|
||||
|
||||
msgq_msg_t msg;
|
||||
|
||||
MSGQMessage *r = NULL;
|
||||
|
||||
int rc = msgq_msg_recv(&msg, q);
|
||||
|
||||
// Hack to implement blocking read with a poller. Don't use this
|
||||
while (!non_blocking && rc == 0 && msgq_do_exit == 0){
|
||||
msgq_pollitem_t items[1];
|
||||
items[0].q = q;
|
||||
|
||||
int t = (timeout != -1) ? timeout : 100;
|
||||
|
||||
int n = msgq_poll(items, 1, t);
|
||||
rc = msgq_msg_recv(&msg, q);
|
||||
|
||||
// The poll indicated a message was ready, but the receive failed. Try again
|
||||
if (n == 1 && rc == 0){
|
||||
continue;
|
||||
}
|
||||
|
||||
if (timeout != -1){
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (!non_blocking){
|
||||
std::signal(SIGINT, prev_handler_sigint);
|
||||
std::signal(SIGTERM, prev_handler_sigterm);
|
||||
}
|
||||
|
||||
errno = msgq_do_exit ? EINTR : 0;
|
||||
|
||||
if (rc > 0){
|
||||
if (msgq_do_exit){
|
||||
msgq_msg_close(&msg); // Free unused message on exit
|
||||
} else {
|
||||
r = new MSGQMessage;
|
||||
r->takeOwnership(msg.data, msg.size);
|
||||
}
|
||||
}
|
||||
|
||||
return (Message*)r;
|
||||
}
|
||||
|
||||
void MSGQSubSocket::setTimeout(int t){
|
||||
timeout = t;
|
||||
}
|
||||
|
||||
MSGQSubSocket::~MSGQSubSocket(){
|
||||
if (q != NULL){
|
||||
msgq_close_queue(q);
|
||||
delete q;
|
||||
}
|
||||
}
|
||||
|
||||
int MSGQPubSocket::connect(Context *context, std::string endpoint, bool check_endpoint){
|
||||
assert(context);
|
||||
|
||||
if (check_endpoint && !service_exists(std::string(endpoint))){
|
||||
std::cout << "Warning, " << std::string(endpoint) << " is not in service list." << std::endl;
|
||||
}
|
||||
|
||||
q = new msgq_queue_t;
|
||||
int r = msgq_new_queue(q, endpoint.c_str(), get_size(endpoint));
|
||||
if (r != 0){
|
||||
return r;
|
||||
}
|
||||
|
||||
msgq_init_publisher(q);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int MSGQPubSocket::sendMessage(Message *message){
|
||||
msgq_msg_t msg;
|
||||
msg.data = message->getData();
|
||||
msg.size = message->getSize();
|
||||
|
||||
return msgq_msg_send(&msg, q);
|
||||
}
|
||||
|
||||
int MSGQPubSocket::send(char *data, size_t size){
|
||||
msgq_msg_t msg;
|
||||
msg.data = data;
|
||||
msg.size = size;
|
||||
|
||||
return msgq_msg_send(&msg, q);
|
||||
}
|
||||
|
||||
bool MSGQPubSocket::all_readers_updated() {
|
||||
return msgq_all_readers_updated(q);
|
||||
}
|
||||
|
||||
MSGQPubSocket::~MSGQPubSocket(){
|
||||
if (q != NULL){
|
||||
msgq_close_queue(q);
|
||||
delete q;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void MSGQPoller::registerSocket(SubSocket * socket){
|
||||
assert(num_polls + 1 < MAX_POLLERS);
|
||||
polls[num_polls].q = (msgq_queue_t*)socket->getRawSocket();
|
||||
|
||||
sockets.push_back(socket);
|
||||
num_polls++;
|
||||
}
|
||||
|
||||
std::vector<SubSocket*> MSGQPoller::poll(int timeout){
|
||||
std::vector<SubSocket*> r;
|
||||
|
||||
msgq_poll(polls, num_polls, timeout);
|
||||
for (size_t i = 0; i < num_polls; i++){
|
||||
if (polls[i].revents){
|
||||
r.push_back(sockets[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
#pragma once
|
||||
#include "messaging.h"
|
||||
#include "msgq.h"
|
||||
#include <zmq.h>
|
||||
#include <string>
|
||||
|
||||
#define MAX_POLLERS 128
|
||||
|
||||
class MSGQContext : public Context {
|
||||
private:
|
||||
void * context = NULL;
|
||||
public:
|
||||
MSGQContext();
|
||||
void * getRawContext() {return context;}
|
||||
~MSGQContext();
|
||||
};
|
||||
|
||||
class MSGQMessage : public Message {
|
||||
private:
|
||||
char * data;
|
||||
size_t size;
|
||||
public:
|
||||
void init(size_t size);
|
||||
void init(char *data, size_t size);
|
||||
void takeOwnership(char *data, size_t size);
|
||||
size_t getSize(){return size;}
|
||||
char * getData(){return data;}
|
||||
void close();
|
||||
~MSGQMessage();
|
||||
};
|
||||
|
||||
class MSGQSubSocket : public SubSocket {
|
||||
private:
|
||||
msgq_queue_t * q = NULL;
|
||||
int timeout;
|
||||
public:
|
||||
int connect(Context *context, std::string endpoint, std::string address, bool conflate=false, bool check_endpoint=true);
|
||||
void setTimeout(int timeout);
|
||||
void * getRawSocket() {return (void*)q;}
|
||||
Message *receive(bool non_blocking=false);
|
||||
~MSGQSubSocket();
|
||||
};
|
||||
|
||||
class MSGQPubSocket : public PubSocket {
|
||||
private:
|
||||
msgq_queue_t * q = NULL;
|
||||
public:
|
||||
int connect(Context *context, std::string endpoint, bool check_endpoint=true);
|
||||
int sendMessage(Message *message);
|
||||
int send(char *data, size_t size);
|
||||
bool all_readers_updated();
|
||||
~MSGQPubSocket();
|
||||
};
|
||||
|
||||
class MSGQPoller : public Poller {
|
||||
private:
|
||||
std::vector<SubSocket*> sockets;
|
||||
msgq_pollitem_t polls[MAX_POLLERS];
|
||||
size_t num_polls = 0;
|
||||
|
||||
public:
|
||||
void registerSocket(SubSocket *socket);
|
||||
std::vector<SubSocket*> poll(int timeout);
|
||||
~MSGQPoller(){};
|
||||
};
|
||||
@@ -0,0 +1,168 @@
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
#include <cstdlib>
|
||||
#include <cerrno>
|
||||
|
||||
#include <zmq.h>
|
||||
|
||||
#include "services.h"
|
||||
#include "impl_zmq.h"
|
||||
|
||||
static int get_port(std::string endpoint) {
|
||||
int port = -1;
|
||||
for (const auto& it : services) {
|
||||
std::string name = it.name;
|
||||
if (name == endpoint) {
|
||||
port = it.port;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
assert(port >= 0);
|
||||
return port;
|
||||
}
|
||||
|
||||
ZMQContext::ZMQContext() {
|
||||
context = zmq_ctx_new();
|
||||
}
|
||||
|
||||
ZMQContext::~ZMQContext() {
|
||||
zmq_ctx_term(context);
|
||||
}
|
||||
|
||||
void ZMQMessage::init(size_t sz) {
|
||||
size = sz;
|
||||
data = new char[size];
|
||||
}
|
||||
|
||||
void ZMQMessage::init(char * d, size_t sz) {
|
||||
size = sz;
|
||||
data = new char[size];
|
||||
memcpy(data, d, size);
|
||||
}
|
||||
|
||||
void ZMQMessage::close() {
|
||||
if (size > 0){
|
||||
delete[] data;
|
||||
}
|
||||
size = 0;
|
||||
}
|
||||
|
||||
ZMQMessage::~ZMQMessage() {
|
||||
this->close();
|
||||
}
|
||||
|
||||
|
||||
int ZMQSubSocket::connect(Context *context, std::string endpoint, std::string address, bool conflate, bool check_endpoint){
|
||||
sock = zmq_socket(context->getRawContext(), ZMQ_SUB);
|
||||
if (sock == NULL){
|
||||
return -1;
|
||||
}
|
||||
|
||||
zmq_setsockopt(sock, ZMQ_SUBSCRIBE, "", 0);
|
||||
|
||||
if (conflate){
|
||||
int arg = 1;
|
||||
zmq_setsockopt(sock, ZMQ_CONFLATE, &arg, sizeof(int));
|
||||
}
|
||||
|
||||
int reconnect_ivl = 500;
|
||||
zmq_setsockopt(sock, ZMQ_RECONNECT_IVL_MAX, &reconnect_ivl, sizeof(reconnect_ivl));
|
||||
|
||||
full_endpoint = "tcp://" + address + ":";
|
||||
if (check_endpoint){
|
||||
full_endpoint += std::to_string(get_port(endpoint));
|
||||
} else {
|
||||
full_endpoint += endpoint;
|
||||
}
|
||||
|
||||
return zmq_connect(sock, full_endpoint.c_str());
|
||||
}
|
||||
|
||||
|
||||
Message * ZMQSubSocket::receive(bool non_blocking){
|
||||
zmq_msg_t msg;
|
||||
assert(zmq_msg_init(&msg) == 0);
|
||||
|
||||
int flags = non_blocking ? ZMQ_DONTWAIT : 0;
|
||||
int rc = zmq_msg_recv(&msg, sock, flags);
|
||||
Message *r = NULL;
|
||||
|
||||
if (rc >= 0){
|
||||
// Make a copy to ensure the data is aligned
|
||||
r = new ZMQMessage;
|
||||
r->init((char*)zmq_msg_data(&msg), zmq_msg_size(&msg));
|
||||
}
|
||||
|
||||
zmq_msg_close(&msg);
|
||||
return r;
|
||||
}
|
||||
|
||||
void ZMQSubSocket::setTimeout(int timeout){
|
||||
zmq_setsockopt(sock, ZMQ_RCVTIMEO, &timeout, sizeof(int));
|
||||
}
|
||||
|
||||
ZMQSubSocket::~ZMQSubSocket(){
|
||||
zmq_close(sock);
|
||||
}
|
||||
|
||||
int ZMQPubSocket::connect(Context *context, std::string endpoint, bool check_endpoint){
|
||||
sock = zmq_socket(context->getRawContext(), ZMQ_PUB);
|
||||
if (sock == NULL){
|
||||
return -1;
|
||||
}
|
||||
|
||||
full_endpoint = "tcp://*:";
|
||||
if (check_endpoint){
|
||||
full_endpoint += std::to_string(get_port(endpoint));
|
||||
} else {
|
||||
full_endpoint += endpoint;
|
||||
}
|
||||
|
||||
return zmq_bind(sock, full_endpoint.c_str());
|
||||
}
|
||||
|
||||
int ZMQPubSocket::sendMessage(Message *message){
|
||||
return zmq_send(sock, message->getData(), message->getSize(), ZMQ_DONTWAIT);
|
||||
}
|
||||
|
||||
int ZMQPubSocket::send(char *data, size_t size){
|
||||
return zmq_send(sock, data, size, ZMQ_DONTWAIT);
|
||||
}
|
||||
|
||||
bool ZMQPubSocket::all_readers_updated() {
|
||||
assert(false); // TODO not implemented
|
||||
return false;
|
||||
}
|
||||
|
||||
ZMQPubSocket::~ZMQPubSocket(){
|
||||
zmq_close(sock);
|
||||
}
|
||||
|
||||
|
||||
void ZMQPoller::registerSocket(SubSocket * socket){
|
||||
assert(num_polls + 1 < MAX_POLLERS);
|
||||
polls[num_polls].socket = socket->getRawSocket();
|
||||
polls[num_polls].events = ZMQ_POLLIN;
|
||||
|
||||
sockets.push_back(socket);
|
||||
num_polls++;
|
||||
}
|
||||
|
||||
std::vector<SubSocket*> ZMQPoller::poll(int timeout){
|
||||
std::vector<SubSocket*> r;
|
||||
|
||||
int rc = zmq_poll(polls, num_polls, timeout);
|
||||
if (rc < 0){
|
||||
return r;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < num_polls; i++){
|
||||
if (polls[i].revents){
|
||||
r.push_back(sockets[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
#pragma once
|
||||
#include "messaging.h"
|
||||
#include <zmq.h>
|
||||
#include <string>
|
||||
|
||||
#define MAX_POLLERS 128
|
||||
|
||||
class ZMQContext : public Context {
|
||||
private:
|
||||
void * context = NULL;
|
||||
public:
|
||||
ZMQContext();
|
||||
void * getRawContext() {return context;}
|
||||
~ZMQContext();
|
||||
};
|
||||
|
||||
class ZMQMessage : public Message {
|
||||
private:
|
||||
char * data;
|
||||
size_t size;
|
||||
public:
|
||||
void init(size_t size);
|
||||
void init(char *data, size_t size);
|
||||
size_t getSize(){return size;}
|
||||
char * getData(){return data;}
|
||||
void close();
|
||||
~ZMQMessage();
|
||||
};
|
||||
|
||||
class ZMQSubSocket : public SubSocket {
|
||||
private:
|
||||
void * sock;
|
||||
std::string full_endpoint;
|
||||
public:
|
||||
int connect(Context *context, std::string endpoint, std::string address, bool conflate=false, bool check_endpoint=true);
|
||||
void setTimeout(int timeout);
|
||||
void * getRawSocket() {return sock;}
|
||||
Message *receive(bool non_blocking=false);
|
||||
~ZMQSubSocket();
|
||||
};
|
||||
|
||||
class ZMQPubSocket : public PubSocket {
|
||||
private:
|
||||
void * sock;
|
||||
std::string full_endpoint;
|
||||
public:
|
||||
int connect(Context *context, std::string endpoint, bool check_endpoint=true);
|
||||
int sendMessage(Message *message);
|
||||
int send(char *data, size_t size);
|
||||
bool all_readers_updated();
|
||||
~ZMQPubSocket();
|
||||
};
|
||||
|
||||
class ZMQPoller : public Poller {
|
||||
private:
|
||||
std::vector<SubSocket*> sockets;
|
||||
zmq_pollitem_t polls[MAX_POLLERS];
|
||||
size_t num_polls = 0;
|
||||
|
||||
public:
|
||||
void registerSocket(SubSocket *socket);
|
||||
std::vector<SubSocket*> poll(int timeout);
|
||||
~ZMQPoller(){};
|
||||
};
|
||||
@@ -0,0 +1,113 @@
|
||||
#include <cassert>
|
||||
#include <iostream>
|
||||
|
||||
#include "messaging.h"
|
||||
#include "impl_zmq.h"
|
||||
#include "impl_msgq.h"
|
||||
|
||||
#ifdef __APPLE__
|
||||
const bool MUST_USE_ZMQ = true;
|
||||
#else
|
||||
const bool MUST_USE_ZMQ = false;
|
||||
#endif
|
||||
|
||||
bool messaging_use_zmq(){
|
||||
if (std::getenv("ZMQ") || MUST_USE_ZMQ) {
|
||||
if (std::getenv("OPENPILOT_PREFIX")) {
|
||||
std::cerr << "OPENPILOT_PREFIX not supported with ZMQ backend\n";
|
||||
assert(false);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Context * Context::create(){
|
||||
Context * c;
|
||||
if (messaging_use_zmq()){
|
||||
c = new ZMQContext();
|
||||
} else {
|
||||
c = new MSGQContext();
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
SubSocket * SubSocket::create(){
|
||||
SubSocket * s;
|
||||
if (messaging_use_zmq()){
|
||||
s = new ZMQSubSocket();
|
||||
} else {
|
||||
s = new MSGQSubSocket();
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
SubSocket * SubSocket::create(Context * context, std::string endpoint, std::string address, bool conflate, bool check_endpoint){
|
||||
SubSocket *s = SubSocket::create();
|
||||
int r = s->connect(context, endpoint, address, conflate, check_endpoint);
|
||||
|
||||
if (r == 0) {
|
||||
return s;
|
||||
} else {
|
||||
delete s;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
PubSocket * PubSocket::create(){
|
||||
PubSocket * s;
|
||||
if (messaging_use_zmq()){
|
||||
s = new ZMQPubSocket();
|
||||
} else {
|
||||
s = new MSGQPubSocket();
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
PubSocket * PubSocket::create(Context * context, std::string endpoint, bool check_endpoint){
|
||||
PubSocket *s = PubSocket::create();
|
||||
int r = s->connect(context, endpoint, check_endpoint);
|
||||
|
||||
if (r == 0) {
|
||||
return s;
|
||||
} else {
|
||||
delete s;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
Poller * Poller::create(){
|
||||
Poller * p;
|
||||
if (messaging_use_zmq()){
|
||||
p = new ZMQPoller();
|
||||
} else {
|
||||
p = new MSGQPoller();
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
Poller * Poller::create(std::vector<SubSocket*> sockets){
|
||||
Poller * p = Poller::create();
|
||||
|
||||
for (auto s : sockets){
|
||||
p->registerSocket(s);
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
extern "C" Context * messaging_context_create() {
|
||||
return Context::create();
|
||||
}
|
||||
|
||||
extern "C" SubSocket * messaging_subsocket_create(Context* context, const char* endpoint) {
|
||||
return SubSocket::create(context, std::string(endpoint));
|
||||
}
|
||||
|
||||
extern "C" PubSocket * messaging_pubsocket_create(Context* context, const char* endpoint) {
|
||||
return PubSocket::create(context, std::string(endpoint));
|
||||
}
|
||||
|
||||
extern "C" Poller * messaging_poller_create(SubSocket** sockets, int size) {
|
||||
std::vector<SubSocket*> socketsVec(sockets, sockets + size);
|
||||
return Poller::create(socketsVec);
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
#pragma once
|
||||
#include <cstddef>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <capnp/serialize.h>
|
||||
#include "../gen/cpp/log.capnp.h"
|
||||
|
||||
#ifdef __APPLE__
|
||||
#define CLOCK_BOOTTIME CLOCK_MONOTONIC
|
||||
#endif
|
||||
|
||||
#define MSG_MULTIPLE_PUBLISHERS 100
|
||||
|
||||
bool messaging_use_zmq();
|
||||
|
||||
class Context {
|
||||
public:
|
||||
virtual void * getRawContext() = 0;
|
||||
static Context * create();
|
||||
virtual ~Context(){};
|
||||
};
|
||||
|
||||
class Message {
|
||||
public:
|
||||
virtual void init(size_t size) = 0;
|
||||
virtual void init(char * data, size_t size) = 0;
|
||||
virtual void close() = 0;
|
||||
virtual size_t getSize() = 0;
|
||||
virtual char * getData() = 0;
|
||||
virtual ~Message(){};
|
||||
};
|
||||
|
||||
|
||||
class SubSocket {
|
||||
public:
|
||||
virtual int connect(Context *context, std::string endpoint, std::string address, bool conflate=false, bool check_endpoint=true) = 0;
|
||||
virtual void setTimeout(int timeout) = 0;
|
||||
virtual Message *receive(bool non_blocking=false) = 0;
|
||||
virtual void * getRawSocket() = 0;
|
||||
static SubSocket * create();
|
||||
static SubSocket * create(Context * context, std::string endpoint, std::string address="127.0.0.1", bool conflate=false, bool check_endpoint=true);
|
||||
virtual ~SubSocket(){};
|
||||
};
|
||||
|
||||
class PubSocket {
|
||||
public:
|
||||
virtual int connect(Context *context, std::string endpoint, bool check_endpoint=true) = 0;
|
||||
virtual int sendMessage(Message *message) = 0;
|
||||
virtual int send(char *data, size_t size) = 0;
|
||||
virtual bool all_readers_updated() = 0;
|
||||
static PubSocket * create();
|
||||
static PubSocket * create(Context * context, std::string endpoint, bool check_endpoint=true);
|
||||
static PubSocket * create(Context * context, std::string endpoint, int port, bool check_endpoint=true);
|
||||
virtual ~PubSocket(){};
|
||||
};
|
||||
|
||||
class Poller {
|
||||
public:
|
||||
virtual void registerSocket(SubSocket *socket) = 0;
|
||||
virtual std::vector<SubSocket*> poll(int timeout) = 0;
|
||||
static Poller * create();
|
||||
static Poller * create(std::vector<SubSocket*> sockets);
|
||||
virtual ~Poller(){};
|
||||
};
|
||||
|
||||
class SubMaster {
|
||||
public:
|
||||
SubMaster(const std::vector<const char *> &service_list, const std::vector<const char *> &poll = {},
|
||||
const char *address = nullptr, const std::vector<const char *> &ignore_alive = {});
|
||||
void update(int timeout = 1000);
|
||||
void update_msgs(uint64_t current_time, const std::vector<std::pair<std::string, cereal::Event::Reader>> &messages);
|
||||
inline bool allAlive(const std::vector<const char *> &service_list = {}) { return all_(service_list, false, true); }
|
||||
inline bool allValid(const std::vector<const char *> &service_list = {}) { return all_(service_list, true, false); }
|
||||
inline bool allAliveAndValid(const std::vector<const char *> &service_list = {}) { return all_(service_list, true, true); }
|
||||
void drain();
|
||||
~SubMaster();
|
||||
|
||||
uint64_t frame = 0;
|
||||
bool updated(const char *name) const;
|
||||
bool alive(const char *name) const;
|
||||
bool valid(const char *name) const;
|
||||
uint64_t rcv_frame(const char *name) const;
|
||||
uint64_t rcv_time(const char *name) const;
|
||||
cereal::Event::Reader &operator[](const char *name) const;
|
||||
|
||||
private:
|
||||
bool all_(const std::vector<const char *> &service_list, bool valid, bool alive);
|
||||
Poller *poller_ = nullptr;
|
||||
struct SubMessage;
|
||||
std::map<SubSocket *, SubMessage *> messages_;
|
||||
std::map<std::string, SubMessage *> services_;
|
||||
};
|
||||
|
||||
class MessageBuilder : public capnp::MallocMessageBuilder {
|
||||
public:
|
||||
MessageBuilder() = default;
|
||||
|
||||
cereal::Event::Builder initEvent(bool valid = true) {
|
||||
cereal::Event::Builder event = initRoot<cereal::Event>();
|
||||
struct timespec t;
|
||||
clock_gettime(CLOCK_BOOTTIME, &t);
|
||||
uint64_t current_time = t.tv_sec * 1000000000ULL + t.tv_nsec;
|
||||
event.setLogMonoTime(current_time);
|
||||
event.setValid(valid);
|
||||
return event;
|
||||
}
|
||||
|
||||
kj::ArrayPtr<capnp::byte> toBytes() {
|
||||
heapArray_ = capnp::messageToFlatArray(*this);
|
||||
return heapArray_.asBytes();
|
||||
}
|
||||
|
||||
private:
|
||||
kj::Array<capnp::word> heapArray_;
|
||||
};
|
||||
|
||||
class PubMaster {
|
||||
public:
|
||||
PubMaster(const std::vector<const char *> &service_list);
|
||||
inline int send(const char *name, capnp::byte *data, size_t size) { return sockets_.at(name)->send((char *)data, size); }
|
||||
int send(const char *name, MessageBuilder &msg);
|
||||
~PubMaster();
|
||||
|
||||
private:
|
||||
std::map<std::string, PubSocket *> sockets_;
|
||||
};
|
||||
|
||||
class AlignedBuffer {
|
||||
public:
|
||||
kj::ArrayPtr<const capnp::word> align(const char *data, const size_t size) {
|
||||
words_size = size / sizeof(capnp::word) + 1;
|
||||
if (aligned_buf.size() < words_size) {
|
||||
aligned_buf = kj::heapArray<capnp::word>(words_size < 512 ? 512 : words_size);
|
||||
}
|
||||
memcpy(aligned_buf.begin(), data, size);
|
||||
return aligned_buf.slice(0, words_size);
|
||||
}
|
||||
inline kj::ArrayPtr<const capnp::word> align(Message *m) {
|
||||
return align(m->getData(), m->getSize());
|
||||
}
|
||||
private:
|
||||
kj::Array<capnp::word> aligned_buf;
|
||||
size_t words_size;
|
||||
};
|
||||
@@ -0,0 +1,40 @@
|
||||
# distutils: language = c++
|
||||
#cython: language_level=3
|
||||
|
||||
from libcpp.string cimport string
|
||||
from libcpp.vector cimport vector
|
||||
from libcpp cimport bool
|
||||
|
||||
|
||||
cdef extern from "messaging.h":
|
||||
cdef cppclass Context:
|
||||
@staticmethod
|
||||
Context * create()
|
||||
|
||||
cdef cppclass Message:
|
||||
void init(size_t)
|
||||
void init(char *, size_t)
|
||||
void close()
|
||||
size_t getSize()
|
||||
char *getData()
|
||||
|
||||
cdef cppclass SubSocket:
|
||||
@staticmethod
|
||||
SubSocket * create()
|
||||
int connect(Context *, string, string, bool)
|
||||
Message * receive(bool)
|
||||
void setTimeout(int)
|
||||
|
||||
cdef cppclass PubSocket:
|
||||
@staticmethod
|
||||
PubSocket * create()
|
||||
int connect(Context *, string)
|
||||
int sendMessage(Message *)
|
||||
int send(char *, size_t)
|
||||
bool all_readers_updated()
|
||||
|
||||
cdef cppclass Poller:
|
||||
@staticmethod
|
||||
Poller * create()
|
||||
void registerSocket(SubSocket *)
|
||||
vector[SubSocket*] poll(int) nogil
|
||||
@@ -0,0 +1,154 @@
|
||||
# distutils: language = c++
|
||||
# cython: c_string_encoding=ascii, language_level=3
|
||||
|
||||
import sys
|
||||
from libcpp.string cimport string
|
||||
from libcpp cimport bool
|
||||
from libc cimport errno
|
||||
|
||||
|
||||
from .messaging cimport Context as cppContext
|
||||
from .messaging cimport SubSocket as cppSubSocket
|
||||
from .messaging cimport PubSocket as cppPubSocket
|
||||
from .messaging cimport Poller as cppPoller
|
||||
from .messaging cimport Message as cppMessage
|
||||
|
||||
|
||||
class MessagingError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class MultiplePublishersError(MessagingError):
|
||||
pass
|
||||
|
||||
|
||||
cdef class Context:
|
||||
cdef cppContext * context
|
||||
|
||||
def __cinit__(self):
|
||||
self.context = cppContext.create()
|
||||
|
||||
def term(self):
|
||||
del self.context
|
||||
self.context = NULL
|
||||
|
||||
def __dealloc__(self):
|
||||
pass
|
||||
# Deleting the context will hang if sockets are still active
|
||||
# TODO: Figure out a way to make sure the context is closed last
|
||||
# del self.context
|
||||
|
||||
|
||||
cdef class Poller:
|
||||
cdef cppPoller * poller
|
||||
cdef list sub_sockets
|
||||
|
||||
def __cinit__(self):
|
||||
self.sub_sockets = []
|
||||
self.poller = cppPoller.create()
|
||||
|
||||
def __dealloc__(self):
|
||||
del self.poller
|
||||
|
||||
def registerSocket(self, SubSocket socket):
|
||||
self.sub_sockets.append(socket)
|
||||
self.poller.registerSocket(socket.socket)
|
||||
|
||||
def poll(self, timeout):
|
||||
sockets = []
|
||||
cdef int t = timeout
|
||||
|
||||
with nogil:
|
||||
result = self.poller.poll(t)
|
||||
|
||||
for s in result:
|
||||
socket = SubSocket()
|
||||
socket.setPtr(s)
|
||||
sockets.append(socket)
|
||||
|
||||
return sockets
|
||||
|
||||
cdef class SubSocket:
|
||||
cdef cppSubSocket * socket
|
||||
cdef bool is_owner
|
||||
|
||||
def __cinit__(self):
|
||||
self.socket = cppSubSocket.create()
|
||||
self.is_owner = True
|
||||
|
||||
if self.socket == NULL:
|
||||
raise MessagingError
|
||||
|
||||
def __dealloc__(self):
|
||||
if self.is_owner:
|
||||
del self.socket
|
||||
|
||||
cdef setPtr(self, cppSubSocket * ptr):
|
||||
if self.is_owner:
|
||||
del self.socket
|
||||
|
||||
self.is_owner = False
|
||||
self.socket = ptr
|
||||
|
||||
def connect(self, Context context, string endpoint, string address=b"127.0.0.1", bool conflate=False):
|
||||
r = self.socket.connect(context.context, endpoint, address, conflate)
|
||||
|
||||
if r != 0:
|
||||
if errno.errno == errno.EADDRINUSE:
|
||||
raise MultiplePublishersError
|
||||
else:
|
||||
raise MessagingError
|
||||
|
||||
def setTimeout(self, int timeout):
|
||||
self.socket.setTimeout(timeout)
|
||||
|
||||
def receive(self, bool non_blocking=False):
|
||||
msg = self.socket.receive(non_blocking)
|
||||
|
||||
if msg == NULL:
|
||||
# If a blocking read returns no message check errno if SIGINT was caught in the C++ code
|
||||
if errno.errno == errno.EINTR:
|
||||
print("SIGINT received, exiting")
|
||||
sys.exit(1)
|
||||
|
||||
return None
|
||||
else:
|
||||
sz = msg.getSize()
|
||||
m = msg.getData()[:sz]
|
||||
del msg
|
||||
|
||||
return m
|
||||
|
||||
|
||||
cdef class PubSocket:
|
||||
cdef cppPubSocket * socket
|
||||
|
||||
def __cinit__(self):
|
||||
self.socket = cppPubSocket.create()
|
||||
if self.socket == NULL:
|
||||
raise MessagingError
|
||||
|
||||
def __dealloc__(self):
|
||||
del self.socket
|
||||
|
||||
def connect(self, Context context, string endpoint):
|
||||
r = self.socket.connect(context.context, endpoint)
|
||||
|
||||
if r != 0:
|
||||
if errno.errno == errno.EADDRINUSE:
|
||||
raise MultiplePublishersError
|
||||
else:
|
||||
raise MessagingError
|
||||
|
||||
def send(self, bytes data):
|
||||
length = len(data)
|
||||
r = self.socket.send(<char*>data, length)
|
||||
|
||||
if r != length:
|
||||
if errno.errno == errno.EADDRINUSE:
|
||||
raise MultiplePublishersError
|
||||
else:
|
||||
raise MessagingError
|
||||
|
||||
def all_readers_updated(self):
|
||||
return self.socket.all_readers_updated()
|
||||
@@ -10,7 +10,6 @@
|
||||
#include <csignal>
|
||||
#include <random>
|
||||
#include <string>
|
||||
#include <limits>
|
||||
|
||||
#include <poll.h>
|
||||
#include <sys/ioctl.h>
|
||||
@@ -23,7 +22,7 @@
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "msgq/msgq.h"
|
||||
#include "msgq.h"
|
||||
|
||||
void sigusr2_handler(int signal) {
|
||||
assert(signal == SIGUSR2);
|
||||
@@ -31,7 +30,7 @@ void sigusr2_handler(int signal) {
|
||||
|
||||
uint64_t msgq_get_uid(void){
|
||||
std::random_device rd("/dev/urandom");
|
||||
std::uniform_int_distribution<uint64_t> distribution(0, std::numeric_limits<uint32_t>::max());
|
||||
std::uniform_int_distribution<uint64_t> distribution(0,std::numeric_limits<uint32_t>::max());
|
||||
|
||||
#ifdef __APPLE__
|
||||
// TODO: this doesn't work
|
||||
@@ -77,24 +76,22 @@ void msgq_reset_reader(msgq_queue_t * q){
|
||||
|
||||
void msgq_wait_for_subscriber(msgq_queue_t *q){
|
||||
while (*q->num_readers == 0){
|
||||
// wait for subscriber
|
||||
;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
int msgq_new_queue(msgq_queue_t * q, const char * path, size_t size){
|
||||
assert(size < 0xFFFFFFFF); // Buffer must be smaller than 2^32 bytes
|
||||
std::signal(SIGUSR2, sigusr2_handler);
|
||||
|
||||
#ifdef __APPLE__
|
||||
std::string base_path = "/tmp/msgq_";
|
||||
#else
|
||||
std::string base_path = "/dev/shm/msgq_";
|
||||
#endif
|
||||
std::string full_path = "/dev/shm/";
|
||||
const char* prefix = std::getenv("OPENPILOT_PREFIX");
|
||||
if (prefix) {
|
||||
base_path += std::string(prefix) + "/";
|
||||
full_path += std::string(prefix) + "/";
|
||||
}
|
||||
std::string full_path = base_path + path;
|
||||
full_path += path;
|
||||
|
||||
auto fd = open(full_path.c_str(), O_RDWR | O_CREAT, 0664);
|
||||
if (fd < 0) {
|
||||
@@ -107,21 +104,12 @@ int msgq_new_queue(msgq_queue_t * q, const char * path, size_t size){
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int mmap_flags = MAP_SHARED;
|
||||
#ifdef MAP_POPULATE
|
||||
if (std::getenv("MSGQ_PREALLOC")) {
|
||||
mmap_flags |= MAP_POPULATE;
|
||||
}
|
||||
#endif
|
||||
|
||||
char * mem = (char*)mmap(NULL, size + sizeof(msgq_header_t), PROT_READ | PROT_WRITE, mmap_flags, fd, 0);
|
||||
char * mem = (char*)mmap(NULL, size + sizeof(msgq_header_t), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
|
||||
close(fd);
|
||||
|
||||
if (mem == MAP_FAILED){
|
||||
if (mem == NULL){
|
||||
return -1;
|
||||
}
|
||||
|
||||
q->mmap_p = mem;
|
||||
|
||||
msgq_header_t *header = (msgq_header_t *)mem;
|
||||
@@ -170,11 +158,8 @@ void msgq_init_publisher(msgq_queue_t * q) {
|
||||
}
|
||||
|
||||
static void thread_signal(uint32_t tid) {
|
||||
#ifdef __APPLE__
|
||||
// macOS doesn't have tkill, rely on polling instead
|
||||
(void)tid;
|
||||
#elif !defined(SYS_tkill)
|
||||
// fallback for systems without tkill
|
||||
#ifndef SYS_tkill
|
||||
// TODO: this won't work for multithreaded programs
|
||||
kill(tid, SIGUSR2);
|
||||
#else
|
||||
syscall(SYS_tkill, tid, SIGUSR2);
|
||||
@@ -335,11 +320,9 @@ int msgq_msg_ready(msgq_queue_t * q){
|
||||
|
||||
uint32_t read_cycles, read_pointer;
|
||||
UNPACK64(read_cycles, read_pointer, *q->read_pointers[id]);
|
||||
UNUSED(read_cycles);
|
||||
|
||||
uint32_t write_cycles, write_pointer;
|
||||
UNPACK64(write_cycles, write_pointer, *q->write_pointer);
|
||||
UNUSED(write_cycles);
|
||||
|
||||
// Check if new message is available
|
||||
return (read_pointer != write_pointer);
|
||||
@@ -367,7 +350,6 @@ int msgq_msg_recv(msgq_msg_t * msg, msgq_queue_t * q){
|
||||
|
||||
uint32_t write_cycles, write_pointer;
|
||||
UNPACK64(write_cycles, write_pointer, *q->write_pointer);
|
||||
UNUSED(write_cycles);
|
||||
|
||||
char * p = q->data + read_pointer;
|
||||
|
||||
@@ -444,18 +426,9 @@ int msgq_poll(msgq_pollitem_t * items, size_t nitems, int timeout){
|
||||
}
|
||||
|
||||
int ms = (timeout == -1) ? 100 : timeout;
|
||||
|
||||
#ifdef __APPLE__
|
||||
// On macOS, signals can't interrupt nanosleep, so poll more frequently
|
||||
int poll_ms = std::min(ms, 10);
|
||||
int remaining_ms = ms;
|
||||
#else
|
||||
int poll_ms = ms;
|
||||
#endif
|
||||
|
||||
struct timespec ts;
|
||||
ts.tv_sec = poll_ms / 1000;
|
||||
ts.tv_nsec = (poll_ms % 1000) * 1000 * 1000;
|
||||
ts.tv_sec = ms / 1000;
|
||||
ts.tv_nsec = (ms % 1000) * 1000 * 1000;
|
||||
|
||||
|
||||
while (num == 0) {
|
||||
@@ -471,23 +444,10 @@ int msgq_poll(msgq_pollitem_t * items, size_t nitems, int timeout){
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef __APPLE__
|
||||
// exit if we had a timeout and we've exhausted it
|
||||
if (timeout != -1 && ret == 0){
|
||||
remaining_ms -= poll_ms;
|
||||
if (remaining_ms <= 0){
|
||||
break;
|
||||
}
|
||||
poll_ms = std::min(remaining_ms, 10);
|
||||
ts.tv_sec = poll_ms / 1000;
|
||||
ts.tv_nsec = (poll_ms % 1000) * 1000 * 1000;
|
||||
}
|
||||
#else
|
||||
// exit if we had a timeout and the sleep finished
|
||||
if (timeout != -1 && ret == 0){
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
return num;
|
||||
@@ -1,17 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include <atomic>
|
||||
|
||||
#define DEFAULT_SEGMENT_SIZE (1 * 1024 * 1024)
|
||||
#define NUM_READERS 64
|
||||
#define DEFAULT_SEGMENT_SIZE (10 * 1024 * 1024)
|
||||
#define NUM_READERS 10
|
||||
#define ALIGN(n) ((n + (8 - 1)) & -8)
|
||||
|
||||
#define UNUSED(x) (void)x
|
||||
#define UNPACK64(higher, lower, input) do {uint64_t tmp = input; higher = tmp >> 32; lower = tmp & 0xFFFFFFFF;} while (0)
|
||||
#define PACK64(output, higher, lower) output = ((uint64_t)higher << 32) | ((uint64_t)lower & 0xFFFFFFFF)
|
||||
#define PACK64(output, higher, lower) output = ((uint64_t)higher << 32 ) | ((uint64_t)lower & 0xFFFFFFFF)
|
||||
|
||||
struct msgq_header_t {
|
||||
uint64_t num_readers;
|
||||
@@ -1,13 +1,27 @@
|
||||
#include <time.h>
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <string>
|
||||
#include <mutex>
|
||||
|
||||
#include "openpilot/cereal/services.h"
|
||||
#include "openpilot/cereal/messaging/messaging.h"
|
||||
#include "services.h"
|
||||
#include "messaging.h"
|
||||
|
||||
const bool SIMULATION = (getenv("SIMULATION") != nullptr) && (std::string(getenv("SIMULATION")) == "1");
|
||||
|
||||
static inline uint64_t nanos_since_boot() {
|
||||
struct timespec t;
|
||||
clock_gettime(CLOCK_BOOTTIME, &t);
|
||||
return t.tv_sec * 1000000000ULL + t.tv_nsec;
|
||||
}
|
||||
|
||||
static const service *get_service(const char *name) {
|
||||
for (const auto &it : services) {
|
||||
if (strcmp(it.name, name) == 0) return ⁢
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static inline bool inList(const std::vector<const char *> &list, const char *value) {
|
||||
for (auto &v : list) {
|
||||
if (strcmp(value, v) == 0) return true;
|
||||
@@ -17,7 +31,7 @@ static inline bool inList(const std::vector<const char *> &list, const char *val
|
||||
|
||||
class MessageContext {
|
||||
public:
|
||||
MessageContext() : ctx_(nullptr) {}
|
||||
MessageContext() : ctx_(nullptr) {};
|
||||
~MessageContext() { delete ctx_; }
|
||||
inline Context *context() {
|
||||
std::call_once(init_flag, [=]() { ctx_ = Context::create(); });
|
||||
@@ -33,8 +47,8 @@ MessageContext message_context;
|
||||
struct SubMaster::SubMessage {
|
||||
std::string name;
|
||||
SubSocket *socket = nullptr;
|
||||
float freq = 0.0f;
|
||||
bool updated = false, alive = false, valid = false, ignore_alive;
|
||||
int freq = 0;
|
||||
bool updated = false, alive = false, valid = true, ignore_alive;
|
||||
uint64_t rcv_time = 0, rcv_frame = 0;
|
||||
void *allocated_msg_reader = nullptr;
|
||||
bool is_polled = false;
|
||||
@@ -47,17 +61,16 @@ SubMaster::SubMaster(const std::vector<const char *> &service_list, const std::v
|
||||
const char *address, const std::vector<const char *> &ignore_alive) {
|
||||
poller_ = Poller::create();
|
||||
for (auto name : service_list) {
|
||||
assert(services.count(std::string(name)) > 0);
|
||||
|
||||
service serv = services.at(std::string(name));
|
||||
SubSocket *socket = SubSocket::create(message_context.context(), name, address ? address : "127.0.0.1", true, true, serv.queue_size);
|
||||
const service *serv = get_service(name);
|
||||
assert(serv != nullptr);
|
||||
SubSocket *socket = SubSocket::create(message_context.context(), name, address ? address : "127.0.0.1", true);
|
||||
assert(socket != 0);
|
||||
bool is_polled = inList(poll, name) || poll.empty();
|
||||
if (is_polled) poller_->registerSocket(socket);
|
||||
SubMessage *m = new SubMessage{
|
||||
.name = name,
|
||||
.socket = socket,
|
||||
.freq = serv.frequency,
|
||||
.freq = serv->frequency,
|
||||
.ignore_alive = inList(ignore_alive, name),
|
||||
.allocated_msg_reader = malloc(sizeof(capnp::FlatArrayMessageReader)),
|
||||
.is_polled = is_polled};
|
||||
@@ -103,7 +116,7 @@ void SubMaster::update(int timeout) {
|
||||
void SubMaster::update_msgs(uint64_t current_time, const std::vector<std::pair<std::string, cereal::Event::Reader>> &messages){
|
||||
if (++frame == UINT64_MAX) frame = 1;
|
||||
|
||||
for (auto &kv : messages) {
|
||||
for(auto &kv : messages) {
|
||||
auto m_find = services_.find(kv.first);
|
||||
if (m_find == services_.end()){
|
||||
continue;
|
||||
@@ -171,7 +184,7 @@ uint64_t SubMaster::rcv_time(const char *name) const {
|
||||
|
||||
cereal::Event::Reader &SubMaster::operator[](const char *name) const {
|
||||
return services_.at(name)->event;
|
||||
}
|
||||
};
|
||||
|
||||
SubMaster::~SubMaster() {
|
||||
delete poller_;
|
||||
@@ -186,9 +199,8 @@ SubMaster::~SubMaster() {
|
||||
|
||||
PubMaster::PubMaster(const std::vector<const char *> &service_list) {
|
||||
for (auto name : service_list) {
|
||||
assert(services.count(name) > 0);
|
||||
service serv = services.at(std::string(name));
|
||||
PubSocket *socket = PubSocket::create(message_context.context(), name, true, serv.queue_size);
|
||||
assert(get_service(name) != nullptr);
|
||||
PubSocket *socket = PubSocket::create(message_context.context(), name);
|
||||
assert(socket);
|
||||
sockets_[name] = socket;
|
||||
}
|
||||
Executable
+103
@@ -0,0 +1,103 @@
|
||||
#!/usr/bin/env python3
|
||||
import os
|
||||
from typing import Optional
|
||||
|
||||
TICI = os.path.isfile('/TICI')
|
||||
RESERVED_PORT = 8022 # sshd
|
||||
STARTING_PORT = 8001
|
||||
|
||||
|
||||
def new_port(port: int):
|
||||
port += STARTING_PORT
|
||||
return port + 1 if port >= RESERVED_PORT else port
|
||||
|
||||
|
||||
class Service:
|
||||
def __init__(self, port: int, should_log: bool, frequency: float, decimation: Optional[int] = None):
|
||||
self.port = port
|
||||
self.should_log = should_log
|
||||
self.frequency = frequency
|
||||
self.decimation = decimation
|
||||
|
||||
DCAM_FREQ = 10. if not TICI else 20.
|
||||
|
||||
services = {
|
||||
# service: (should_log, frequency, qlog decimation (optional))
|
||||
# note: the "EncodeIdx" packets will still be in the log
|
||||
"sensorEvents": (True, 100., 100),
|
||||
"gpsNMEA": (True, 9.),
|
||||
"deviceState": (True, 2., 1),
|
||||
"can": (True, 100.),
|
||||
"controlsState": (True, 100., 10),
|
||||
"pandaStates": (True, 2., 1),
|
||||
"peripheralState": (True, 2., 1),
|
||||
"radarState": (True, 20., 5),
|
||||
"roadEncodeIdx": (False, 20., 1),
|
||||
"liveTracks": (True, 20.),
|
||||
"sendcan": (True, 100., 139),
|
||||
"logMessage": (True, 0.),
|
||||
"errorLogMessage": (True, 0., 1),
|
||||
"liveCalibration": (True, 4., 4),
|
||||
"androidLog": (True, 0.),
|
||||
"carState": (True, 100., 10),
|
||||
"carControl": (True, 100., 10),
|
||||
"longitudinalPlan": (True, 20., 5),
|
||||
"procLog": (True, 0.5),
|
||||
"gpsLocationExternal": (True, 10., 10),
|
||||
"ubloxGnss": (True, 10.),
|
||||
"qcomGnss": (True, 2.),
|
||||
"gnssMeasurements": (True, 10.),
|
||||
"clocks": (True, 1., 1),
|
||||
"ubloxRaw": (True, 20.),
|
||||
"liveLocationKalman": (True, 20., 5),
|
||||
"liveParameters": (True, 20., 5),
|
||||
"cameraOdometry": (True, 20., 5),
|
||||
"lateralPlan": (True, 20., 5),
|
||||
"thumbnail": (True, 0.2, 1),
|
||||
"carEvents": (True, 1., 1),
|
||||
"carParams": (True, 0.02, 1),
|
||||
"roadCameraState": (True, 20., 20),
|
||||
"driverCameraState": (True, DCAM_FREQ, DCAM_FREQ),
|
||||
"driverEncodeIdx": (False, DCAM_FREQ, 1),
|
||||
"driverState": (True, DCAM_FREQ, DCAM_FREQ / 2),
|
||||
"driverMonitoringState": (True, DCAM_FREQ, DCAM_FREQ / 2),
|
||||
"wideRoadEncodeIdx": (False, 20., 1),
|
||||
"wideRoadCameraState": (True, 20., 20),
|
||||
"modelV2": (True, 20., 40),
|
||||
"managerState": (True, 2., 1),
|
||||
"uploaderState": (True, 0., 1),
|
||||
"navInstruction": (True, 0., 10),
|
||||
"navRoute": (True, 0.),
|
||||
"navThumbnail": (True, 0.),
|
||||
"qRoadEncodeIdx": (False, 20.),
|
||||
|
||||
# debug
|
||||
"testJoystick": (True, 0.),
|
||||
"roadEncodeData": (False, 20.),
|
||||
"driverEncodeData": (False, DCAM_FREQ),
|
||||
"wideRoadEncodeData": (False, 20.),
|
||||
"qRoadEncodeData": (False, 20.),
|
||||
}
|
||||
service_list = {name: Service(new_port(idx), *vals) for # type: ignore
|
||||
idx, (name, vals) in enumerate(services.items())}
|
||||
|
||||
|
||||
def build_header():
|
||||
h = ""
|
||||
h += "/* THIS IS AN AUTOGENERATED FILE, PLEASE EDIT services.py */\n"
|
||||
h += "#ifndef __SERVICES_H\n"
|
||||
h += "#define __SERVICES_H\n"
|
||||
h += "struct service { char name[0x100]; int port; bool should_log; int frequency; int decimation; };\n"
|
||||
h += "static struct service services[] = {\n"
|
||||
for k, v in service_list.items():
|
||||
should_log = "true" if v.should_log else "false"
|
||||
decimation = -1 if v.decimation is None else v.decimation
|
||||
h += ' { "%s", %d, %s, %d, %d },\n' % \
|
||||
(k, v.port, should_log, v.frequency, decimation)
|
||||
h += "};\n"
|
||||
h += "#endif\n"
|
||||
return h
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
print(build_header())
|
||||
@@ -0,0 +1,121 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <sys/mman.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
|
||||
#ifdef __APPLE__
|
||||
#define getsocket() socket(AF_UNIX, SOCK_STREAM, 0)
|
||||
#else
|
||||
#define getsocket() socket(AF_UNIX, SOCK_SEQPACKET, 0)
|
||||
#endif
|
||||
|
||||
#include "ipc.h"
|
||||
|
||||
int ipc_connect(const char* socket_path) {
|
||||
int err;
|
||||
|
||||
int sock = getsocket();
|
||||
|
||||
if (sock < 0) return -1;
|
||||
struct sockaddr_un addr = {
|
||||
.sun_family = AF_UNIX,
|
||||
};
|
||||
snprintf(addr.sun_path, sizeof(addr.sun_path), "%s", socket_path);
|
||||
err = connect(sock, (struct sockaddr*)&addr, sizeof(addr));
|
||||
if (err != 0) {
|
||||
close(sock);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return sock;
|
||||
}
|
||||
|
||||
int ipc_bind(const char* socket_path) {
|
||||
int err;
|
||||
|
||||
unlink(socket_path);
|
||||
|
||||
int sock = getsocket();
|
||||
|
||||
struct sockaddr_un addr = {
|
||||
.sun_family = AF_UNIX,
|
||||
};
|
||||
snprintf(addr.sun_path, sizeof(addr.sun_path), "%s", socket_path);
|
||||
err = bind(sock, (struct sockaddr *)&addr, sizeof(addr));
|
||||
assert(err == 0);
|
||||
|
||||
err = listen(sock, 3);
|
||||
assert(err == 0);
|
||||
|
||||
return sock;
|
||||
}
|
||||
|
||||
|
||||
int ipc_sendrecv_with_fds(bool send, int fd, void *buf, size_t buf_size, int* fds, int num_fds,
|
||||
int *out_num_fds) {
|
||||
char control_buf[CMSG_SPACE(sizeof(int) * num_fds)];
|
||||
memset(control_buf, 0, CMSG_SPACE(sizeof(int) * num_fds));
|
||||
|
||||
struct iovec iov = {
|
||||
.iov_base = buf,
|
||||
.iov_len = buf_size,
|
||||
};
|
||||
struct msghdr msg = {
|
||||
.msg_iov = &iov,
|
||||
.msg_iovlen = 1,
|
||||
};
|
||||
|
||||
if (num_fds > 0) {
|
||||
assert(fds);
|
||||
|
||||
msg.msg_control = control_buf;
|
||||
msg.msg_controllen = CMSG_SPACE(sizeof(int) * num_fds);
|
||||
}
|
||||
|
||||
if (send) {
|
||||
if (num_fds) {
|
||||
struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
|
||||
assert(cmsg);
|
||||
cmsg->cmsg_level = SOL_SOCKET;
|
||||
cmsg->cmsg_type = SCM_RIGHTS;
|
||||
cmsg->cmsg_len = CMSG_LEN(sizeof(int) * num_fds);
|
||||
memcpy(CMSG_DATA(cmsg), fds, sizeof(int) * num_fds);
|
||||
}
|
||||
return sendmsg(fd, &msg, 0);
|
||||
} else {
|
||||
int r = recvmsg(fd, &msg, 0);
|
||||
if (r < 0) return r;
|
||||
|
||||
int recv_fds = 0;
|
||||
if (msg.msg_controllen > 0) {
|
||||
struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
|
||||
assert(cmsg);
|
||||
assert(cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS);
|
||||
recv_fds = (cmsg->cmsg_len - CMSG_LEN(0));
|
||||
assert(recv_fds > 0 && (recv_fds % sizeof(int)) == 0);
|
||||
recv_fds /= sizeof(int);
|
||||
|
||||
assert(fds && recv_fds <= num_fds);
|
||||
memcpy(fds, CMSG_DATA(cmsg), sizeof(int) * recv_fds);
|
||||
}
|
||||
|
||||
if (msg.msg_flags) {
|
||||
for (int i=0; i<recv_fds; i++) {
|
||||
close(fds[i]);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (fds) {
|
||||
assert(out_num_fds);
|
||||
*out_num_fds = recv_fds;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
#include <cstddef>
|
||||
|
||||
int ipc_connect(const char* socket_path);
|
||||
int ipc_bind(const char* socket_path);
|
||||
int ipc_sendrecv_with_fds(bool send, int fd, void *buf, size_t buf_size, int* fds, int num_fds,
|
||||
int *out_num_fds);
|
||||
@@ -0,0 +1,2 @@
|
||||
#define CATCH_CONFIG_MAIN
|
||||
#include "catch2/catch.hpp"
|
||||
@@ -0,0 +1,34 @@
|
||||
#include "visionbuf.h"
|
||||
|
||||
#define ALIGN(x, align) (((x) + (align)-1) & ~((align)-1))
|
||||
|
||||
void visionbuf_compute_aligned_width_and_height(int width, int height, int *aligned_w, int *aligned_h) {
|
||||
*aligned_w = width;
|
||||
*aligned_h = height;
|
||||
}
|
||||
|
||||
void VisionBuf::init_rgb(size_t init_width, size_t init_height, size_t init_stride) {
|
||||
this->rgb = true;
|
||||
this->width = init_width;
|
||||
this->height = init_height;
|
||||
this->stride = init_stride;
|
||||
}
|
||||
|
||||
void VisionBuf::init_yuv(size_t init_width, size_t init_height){
|
||||
this->rgb = false;
|
||||
this->width = init_width;
|
||||
this->height = init_height;
|
||||
|
||||
this->y = (uint8_t *)this->addr;
|
||||
this->u = this->y + (this->width * this->height);
|
||||
this->v = this->u + (this->width / 2 * this->height / 2);
|
||||
}
|
||||
|
||||
|
||||
uint64_t VisionBuf::get_frame_id() {
|
||||
return *frame_id;
|
||||
}
|
||||
|
||||
void VisionBuf::set_frame_id(uint64_t id) {
|
||||
*frame_id = id;
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
#pragma once
|
||||
#include "visionipc.h"
|
||||
|
||||
#define CL_USE_DEPRECATED_OPENCL_1_2_APIS
|
||||
#ifdef __APPLE__
|
||||
#include <OpenCL/cl.h>
|
||||
#else
|
||||
#include <CL/cl.h>
|
||||
#endif
|
||||
|
||||
#define VISIONBUF_SYNC_FROM_DEVICE 0
|
||||
#define VISIONBUF_SYNC_TO_DEVICE 1
|
||||
|
||||
enum VisionStreamType {
|
||||
VISION_STREAM_RGB_ROAD,
|
||||
VISION_STREAM_RGB_DRIVER,
|
||||
VISION_STREAM_RGB_WIDE_ROAD,
|
||||
|
||||
VISION_STREAM_ROAD,
|
||||
VISION_STREAM_DRIVER,
|
||||
VISION_STREAM_WIDE_ROAD,
|
||||
|
||||
VISION_STREAM_RGB_MAP,
|
||||
VISION_STREAM_MAX,
|
||||
};
|
||||
|
||||
class VisionBuf {
|
||||
public:
|
||||
size_t len = 0;
|
||||
size_t mmap_len = 0;
|
||||
void * addr = nullptr;
|
||||
uint64_t *frame_id;
|
||||
int fd = 0;
|
||||
|
||||
bool rgb = false;
|
||||
size_t width = 0;
|
||||
size_t height = 0;
|
||||
size_t stride = 0;
|
||||
|
||||
// YUV
|
||||
uint8_t * y = nullptr;
|
||||
uint8_t * u = nullptr;
|
||||
uint8_t * v = nullptr;
|
||||
|
||||
// Visionipc
|
||||
uint64_t server_id = 0;
|
||||
size_t idx = 0;
|
||||
VisionStreamType type;
|
||||
|
||||
// OpenCL
|
||||
cl_mem buf_cl = nullptr;
|
||||
cl_command_queue copy_q = nullptr;
|
||||
|
||||
// ion
|
||||
int handle = 0;
|
||||
|
||||
void allocate(size_t len);
|
||||
void import();
|
||||
void init_cl(cl_device_id device_id, cl_context ctx);
|
||||
void init_rgb(size_t width, size_t height, size_t stride);
|
||||
void init_yuv(size_t width, size_t height);
|
||||
int sync(int dir);
|
||||
int free();
|
||||
|
||||
void set_frame_id(uint64_t id);
|
||||
uint64_t get_frame_id();
|
||||
};
|
||||
|
||||
void visionbuf_compute_aligned_width_and_height(int width, int height, int *aligned_w, int *aligned_h);
|
||||
@@ -0,0 +1,94 @@
|
||||
#include "visionbuf.h"
|
||||
|
||||
#include <atomic>
|
||||
#include <stdio.h>
|
||||
#include <fcntl.h>
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
std::atomic<int> offset = 0;
|
||||
|
||||
static void *malloc_with_fd(size_t len, int *fd) {
|
||||
char full_path[0x100];
|
||||
|
||||
#ifdef __APPLE__
|
||||
snprintf(full_path, sizeof(full_path)-1, "/tmp/visionbuf_%d_%d", getpid(), offset++);
|
||||
#else
|
||||
snprintf(full_path, sizeof(full_path)-1, "/dev/shm/visionbuf_%d_%d", getpid(), offset++);
|
||||
#endif
|
||||
|
||||
*fd = open(full_path, O_RDWR | O_CREAT, 0664);
|
||||
assert(*fd >= 0);
|
||||
|
||||
unlink(full_path);
|
||||
|
||||
ftruncate(*fd, len);
|
||||
void *addr = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED, *fd, 0);
|
||||
assert(addr != MAP_FAILED);
|
||||
|
||||
return addr;
|
||||
}
|
||||
|
||||
void VisionBuf::allocate(size_t length) {
|
||||
this->len = length;
|
||||
this->mmap_len = this->len;
|
||||
this->addr = malloc_with_fd(this->len, &this->fd);
|
||||
this->frame_id = (uint64_t*)((uint8_t*)this->addr + this->len);
|
||||
}
|
||||
|
||||
void VisionBuf::init_cl(cl_device_id device_id, cl_context ctx){
|
||||
int err;
|
||||
|
||||
this->copy_q = clCreateCommandQueue(ctx, device_id, 0, &err);
|
||||
assert(err == 0);
|
||||
|
||||
this->buf_cl = clCreateBuffer(ctx, CL_MEM_READ_WRITE | CL_MEM_USE_HOST_PTR, this->len, this->addr, &err);
|
||||
assert(err == 0);
|
||||
}
|
||||
|
||||
|
||||
void VisionBuf::import(){
|
||||
assert(this->fd >= 0);
|
||||
this->addr = mmap(NULL, this->mmap_len, PROT_READ | PROT_WRITE, MAP_SHARED, this->fd, 0);
|
||||
assert(this->addr != MAP_FAILED);
|
||||
|
||||
this->frame_id = (uint64_t*)((uint8_t*)this->addr + this->len);
|
||||
}
|
||||
|
||||
|
||||
int VisionBuf::sync(int dir) {
|
||||
int err = 0;
|
||||
if (!this->buf_cl) return 0;
|
||||
|
||||
if (dir == VISIONBUF_SYNC_FROM_DEVICE) {
|
||||
err = clEnqueueReadBuffer(this->copy_q, this->buf_cl, CL_FALSE, 0, this->len, this->addr, 0, NULL, NULL);
|
||||
} else {
|
||||
err = clEnqueueWriteBuffer(this->copy_q, this->buf_cl, CL_FALSE, 0, this->len, this->addr, 0, NULL, NULL);
|
||||
}
|
||||
|
||||
if (err == 0){
|
||||
err = clFinish(this->copy_q);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int VisionBuf::free() {
|
||||
int err = 0;
|
||||
if (this->buf_cl){
|
||||
err = clReleaseMemObject(this->buf_cl);
|
||||
if (err != 0) return err;
|
||||
|
||||
err = clReleaseCommandQueue(this->copy_q);
|
||||
if (err != 0) return err;
|
||||
}
|
||||
|
||||
err = munmap(this->addr, this->len);
|
||||
if (err != 0) return err;
|
||||
|
||||
err = close(this->fd);
|
||||
return err;
|
||||
}
|
||||
@@ -0,0 +1,158 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <linux/ion.h>
|
||||
#include <CL/cl_ext.h>
|
||||
|
||||
#include <msm_ion.h>
|
||||
|
||||
#include "visionbuf.h"
|
||||
|
||||
// keep trying if x gets interrupted by a signal
|
||||
#define HANDLE_EINTR(x) \
|
||||
({ \
|
||||
decltype(x) ret; \
|
||||
int try_cnt = 0; \
|
||||
do { \
|
||||
ret = (x); \
|
||||
} while (ret == -1 && errno == EINTR && try_cnt++ < 100); \
|
||||
ret; \
|
||||
})
|
||||
|
||||
// just hard-code these for convenience
|
||||
// size_t device_page_size = 0;
|
||||
// clGetDeviceInfo(device_id, CL_DEVICE_PAGE_SIZE_QCOM,
|
||||
// sizeof(device_page_size), &device_page_size,
|
||||
// NULL);
|
||||
|
||||
// size_t padding_cl = 0;
|
||||
// clGetDeviceInfo(device_id, CL_DEVICE_EXT_MEM_PADDING_IN_BYTES_QCOM,
|
||||
// sizeof(padding_cl), &padding_cl,
|
||||
// NULL);
|
||||
#define DEVICE_PAGE_SIZE_CL 4096
|
||||
#define PADDING_CL 0
|
||||
|
||||
static int ion_fd = -1;
|
||||
static void ion_init() {
|
||||
if (ion_fd == -1) {
|
||||
ion_fd = open("/dev/ion", O_RDWR | O_NONBLOCK);
|
||||
}
|
||||
}
|
||||
|
||||
void VisionBuf::allocate(size_t length) {
|
||||
int err;
|
||||
|
||||
ion_init();
|
||||
|
||||
struct ion_allocation_data ion_alloc = {0};
|
||||
ion_alloc.len = length + PADDING_CL + sizeof(uint64_t);
|
||||
ion_alloc.align = 4096;
|
||||
ion_alloc.heap_id_mask = 1 << ION_IOMMU_HEAP_ID;
|
||||
ion_alloc.flags = ION_FLAG_CACHED;
|
||||
|
||||
err = HANDLE_EINTR(ioctl(ion_fd, ION_IOC_ALLOC, &ion_alloc));
|
||||
assert(err == 0);
|
||||
|
||||
struct ion_fd_data ion_fd_data = {0};
|
||||
ion_fd_data.handle = ion_alloc.handle;
|
||||
err = HANDLE_EINTR(ioctl(ion_fd, ION_IOC_SHARE, &ion_fd_data));
|
||||
assert(err == 0);
|
||||
|
||||
void *mmap_addr = mmap(NULL, ion_alloc.len,
|
||||
PROT_READ | PROT_WRITE,
|
||||
MAP_SHARED, ion_fd_data.fd, 0);
|
||||
assert(mmap_addr != MAP_FAILED);
|
||||
|
||||
memset(mmap_addr, 0, ion_alloc.len);
|
||||
|
||||
this->len = length;
|
||||
this->mmap_len = ion_alloc.len;
|
||||
this->addr = mmap_addr;
|
||||
this->handle = ion_alloc.handle;
|
||||
this->fd = ion_fd_data.fd;
|
||||
this->frame_id = (uint64_t*)((uint8_t*)this->addr + this->len + PADDING_CL);
|
||||
}
|
||||
|
||||
void VisionBuf::import(){
|
||||
int err;
|
||||
assert(this->fd >= 0);
|
||||
|
||||
ion_init();
|
||||
|
||||
// Get handle
|
||||
struct ion_fd_data fd_data = {0};
|
||||
fd_data.fd = this->fd;
|
||||
err = HANDLE_EINTR(ioctl(ion_fd, ION_IOC_IMPORT, &fd_data));
|
||||
assert(err == 0);
|
||||
|
||||
this->handle = fd_data.handle;
|
||||
this->addr = mmap(NULL, this->mmap_len, PROT_READ | PROT_WRITE, MAP_SHARED, this->fd, 0);
|
||||
assert(this->addr != MAP_FAILED);
|
||||
|
||||
this->frame_id = (uint64_t*)((uint8_t*)this->addr + this->len + PADDING_CL);
|
||||
}
|
||||
|
||||
void VisionBuf::init_cl(cl_device_id device_id, cl_context ctx) {
|
||||
int err;
|
||||
|
||||
assert(((uintptr_t)this->addr % DEVICE_PAGE_SIZE_CL) == 0);
|
||||
|
||||
cl_mem_ion_host_ptr ion_cl = {0};
|
||||
ion_cl.ext_host_ptr.allocation_type = CL_MEM_ION_HOST_PTR_QCOM;
|
||||
ion_cl.ext_host_ptr.host_cache_policy = CL_MEM_HOST_UNCACHED_QCOM;
|
||||
ion_cl.ion_filedesc = this->fd;
|
||||
ion_cl.ion_hostptr = this->addr;
|
||||
|
||||
this->buf_cl = clCreateBuffer(ctx,
|
||||
CL_MEM_USE_HOST_PTR | CL_MEM_EXT_HOST_PTR_QCOM,
|
||||
this->len, &ion_cl, &err);
|
||||
assert(err == 0);
|
||||
}
|
||||
|
||||
|
||||
int VisionBuf::sync(int dir) {
|
||||
struct ion_flush_data flush_data = {0};
|
||||
flush_data.handle = this->handle;
|
||||
flush_data.vaddr = this->addr;
|
||||
flush_data.offset = 0;
|
||||
flush_data.length = this->len;
|
||||
|
||||
// ION_IOC_INV_CACHES ~= DMA_FROM_DEVICE
|
||||
// ION_IOC_CLEAN_CACHES ~= DMA_TO_DEVICE
|
||||
// ION_IOC_CLEAN_INV_CACHES ~= DMA_BIDIRECTIONAL
|
||||
|
||||
struct ion_custom_data custom_data = {0};
|
||||
|
||||
assert(dir == VISIONBUF_SYNC_FROM_DEVICE || dir == VISIONBUF_SYNC_TO_DEVICE);
|
||||
custom_data.cmd = (dir == VISIONBUF_SYNC_FROM_DEVICE) ?
|
||||
ION_IOC_INV_CACHES : ION_IOC_CLEAN_CACHES;
|
||||
|
||||
custom_data.arg = (unsigned long)&flush_data;
|
||||
return HANDLE_EINTR(ioctl(ion_fd, ION_IOC_CUSTOM, &custom_data));
|
||||
}
|
||||
|
||||
int VisionBuf::free() {
|
||||
int err = 0;
|
||||
|
||||
if (this->buf_cl){
|
||||
err = clReleaseMemObject(this->buf_cl);
|
||||
if (err != 0) return err;
|
||||
}
|
||||
|
||||
err = munmap(this->addr, this->mmap_len);
|
||||
if (err != 0) return err;
|
||||
|
||||
err = close(this->fd);
|
||||
if (err != 0) return err;
|
||||
|
||||
struct ion_handle_data handle_data = {.handle = this->handle};
|
||||
return HANDLE_EINTR(ioctl(ion_fd, ION_IOC_FREE, &handle_data));
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
|
||||
constexpr int VISIONIPC_MAX_FDS = 128;
|
||||
|
||||
struct VisionIpcBufExtra {
|
||||
uint32_t frame_id;
|
||||
uint64_t timestamp_sof;
|
||||
uint64_t timestamp_eof;
|
||||
};
|
||||
|
||||
struct VisionIpcPacket {
|
||||
uint64_t server_id;
|
||||
size_t idx;
|
||||
struct VisionIpcBufExtra extra;
|
||||
};
|
||||
@@ -0,0 +1,40 @@
|
||||
# distutils: language = c++
|
||||
#cython: language_level=3
|
||||
|
||||
from libcpp.string cimport string
|
||||
from libcpp.vector cimport vector
|
||||
from libc.stdint cimport uint32_t, uint64_t
|
||||
from libcpp cimport bool
|
||||
|
||||
cdef extern from "visionbuf.h":
|
||||
cdef enum VisionStreamType:
|
||||
pass
|
||||
|
||||
cdef cppclass VisionBuf:
|
||||
void * addr
|
||||
size_t len
|
||||
size_t width
|
||||
size_t height
|
||||
size_t stride
|
||||
void set_frame_id(uint64_t id)
|
||||
|
||||
cdef extern from "visionipc.h":
|
||||
struct VisionIpcBufExtra:
|
||||
uint32_t frame_id
|
||||
uint64_t timestamp_sof
|
||||
uint64_t timestamp_eof
|
||||
|
||||
cdef extern from "visionipc_server.h":
|
||||
cdef cppclass VisionIpcServer:
|
||||
VisionIpcServer(string, void*, void*)
|
||||
void create_buffers(VisionStreamType, size_t, bool, size_t, size_t)
|
||||
VisionBuf * get_buffer(VisionStreamType)
|
||||
void send(VisionBuf *, VisionIpcBufExtra *, bool)
|
||||
void start_listener()
|
||||
|
||||
cdef extern from "visionipc_client.h":
|
||||
cdef cppclass VisionIpcClient:
|
||||
VisionIpcClient(string, VisionStreamType, bool, void*, void*)
|
||||
VisionBuf * recv(VisionIpcBufExtra *, int)
|
||||
bool connect(bool)
|
||||
bool is_connected()
|
||||
@@ -0,0 +1,129 @@
|
||||
#include <chrono>
|
||||
#include <cassert>
|
||||
#include <iostream>
|
||||
#include <thread>
|
||||
|
||||
#include "visionipc/ipc.h"
|
||||
#include "visionipc/visionipc_client.h"
|
||||
#include "visionipc/visionipc_server.h"
|
||||
#include "logger/logger.h"
|
||||
|
||||
VisionIpcClient::VisionIpcClient(std::string name, VisionStreamType type, bool conflate, cl_device_id device_id, cl_context ctx) : name(name), type(type), device_id(device_id), ctx(ctx) {
|
||||
msg_ctx = Context::create();
|
||||
sock = SubSocket::create(msg_ctx, get_endpoint_name(name, type), "127.0.0.1", conflate, false);
|
||||
|
||||
poller = Poller::create();
|
||||
poller->registerSocket(sock);
|
||||
}
|
||||
|
||||
// Connect is not thread safe. Do not use the buffers while calling connect
|
||||
bool VisionIpcClient::connect(bool blocking){
|
||||
connected = false;
|
||||
|
||||
// Cleanup old buffers on reconnect
|
||||
for (size_t i = 0; i < num_buffers; i++){
|
||||
if (buffers[i].free() != 0) {
|
||||
LOGE("Failed to free buffer %zu", i);
|
||||
}
|
||||
}
|
||||
|
||||
num_buffers = 0;
|
||||
|
||||
// Connect to server socket and ask for all FDs of type
|
||||
std::string path = "/tmp/visionipc_" + name;
|
||||
|
||||
int socket_fd = -1;
|
||||
while (socket_fd < 0) {
|
||||
socket_fd = ipc_connect(path.c_str());
|
||||
|
||||
if (socket_fd < 0) {
|
||||
if (blocking){
|
||||
std::cout << "VisionIpcClient connecting" << std::endl;
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Send stream type to server to request FDs
|
||||
int r = ipc_sendrecv_with_fds(true, socket_fd, &type, sizeof(type), nullptr, 0, nullptr);
|
||||
assert(r == sizeof(type));
|
||||
|
||||
// Get FDs
|
||||
int fds[VISIONIPC_MAX_FDS];
|
||||
VisionBuf bufs[VISIONIPC_MAX_FDS];
|
||||
r = ipc_sendrecv_with_fds(false, socket_fd, &bufs, sizeof(bufs), fds, VISIONIPC_MAX_FDS, &num_buffers);
|
||||
|
||||
assert(num_buffers >= 0);
|
||||
assert(r == sizeof(VisionBuf) * num_buffers);
|
||||
|
||||
// Import buffers
|
||||
for (size_t i = 0; i < num_buffers; i++){
|
||||
buffers[i] = bufs[i];
|
||||
buffers[i].fd = fds[i];
|
||||
buffers[i].import();
|
||||
if (buffers[i].rgb) {
|
||||
buffers[i].init_rgb(buffers[i].width, buffers[i].height, buffers[i].stride);
|
||||
} else {
|
||||
buffers[i].init_yuv(buffers[i].width, buffers[i].height);
|
||||
}
|
||||
|
||||
if (device_id) buffers[i].init_cl(device_id, ctx);
|
||||
}
|
||||
|
||||
close(socket_fd);
|
||||
connected = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
VisionBuf * VisionIpcClient::recv(VisionIpcBufExtra * extra, const int timeout_ms){
|
||||
auto p = poller->poll(timeout_ms);
|
||||
|
||||
if (!p.size()){
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Message * r = sock->receive(true);
|
||||
if (r == nullptr){
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Get buffer
|
||||
assert(r->getSize() == sizeof(VisionIpcPacket));
|
||||
VisionIpcPacket *packet = (VisionIpcPacket*)r->getData();
|
||||
|
||||
assert(packet->idx < num_buffers);
|
||||
VisionBuf * buf = &buffers[packet->idx];
|
||||
|
||||
if (buf->server_id != packet->server_id){
|
||||
connected = false;
|
||||
delete r;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (extra) {
|
||||
*extra = packet->extra;
|
||||
}
|
||||
|
||||
if (buf->sync(VISIONBUF_SYNC_TO_DEVICE) != 0) {
|
||||
LOGE("Failed to sync buffer");
|
||||
}
|
||||
|
||||
delete r;
|
||||
return buf;
|
||||
}
|
||||
|
||||
|
||||
|
||||
VisionIpcClient::~VisionIpcClient(){
|
||||
for (size_t i = 0; i < num_buffers; i++){
|
||||
if (buffers[i].free() != 0) {
|
||||
LOGE("Failed to free buffer %zu", i);
|
||||
}
|
||||
}
|
||||
|
||||
delete sock;
|
||||
delete poller;
|
||||
delete msg_ctx;
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
#pragma once
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "messaging/messaging.h"
|
||||
#include "visionipc/visionipc.h"
|
||||
#include "visionipc/visionbuf.h"
|
||||
|
||||
class VisionIpcClient {
|
||||
private:
|
||||
std::string name;
|
||||
Context * msg_ctx;
|
||||
SubSocket * sock;
|
||||
Poller * poller;
|
||||
|
||||
VisionStreamType type;
|
||||
|
||||
cl_device_id device_id = nullptr;
|
||||
cl_context ctx = nullptr;
|
||||
|
||||
void init_msgq(bool conflate);
|
||||
|
||||
public:
|
||||
bool connected = false;
|
||||
int num_buffers = 0;
|
||||
VisionBuf buffers[VISIONIPC_MAX_FDS];
|
||||
VisionIpcClient(std::string name, VisionStreamType type, bool conflate, cl_device_id device_id=nullptr, cl_context ctx=nullptr);
|
||||
~VisionIpcClient();
|
||||
VisionBuf * recv(VisionIpcBufExtra * extra=nullptr, const int timeout_ms=100);
|
||||
bool connect(bool blocking=true);
|
||||
bool is_connected() { return connected; }
|
||||
};
|
||||
@@ -0,0 +1,95 @@
|
||||
# distutils: language = c++
|
||||
# cython: c_string_encoding=ascii, language_level=3
|
||||
|
||||
import sys
|
||||
import numpy as np
|
||||
cimport numpy as cnp
|
||||
from cython.view cimport array
|
||||
from libc.string cimport memcpy
|
||||
from libc.stdint cimport uint32_t, uint64_t
|
||||
from libcpp cimport bool
|
||||
from libcpp.string cimport string
|
||||
|
||||
from .visionipc cimport VisionIpcServer as cppVisionIpcServer
|
||||
from .visionipc cimport VisionIpcClient as cppVisionIpcClient
|
||||
from .visionipc cimport VisionBuf as cppVisionBuf
|
||||
from .visionipc cimport VisionIpcBufExtra
|
||||
|
||||
cpdef enum VisionStreamType:
|
||||
VISION_STREAM_RGB_ROAD
|
||||
VISION_STREAM_RGB_DRIVER
|
||||
VISION_STREAM_RGB_WIDE_ROAD
|
||||
VISION_STREAM_ROAD
|
||||
VISION_STREAM_DRIVER
|
||||
VISION_STREAM_WIDE_ROAD
|
||||
VISION_STREAM_RGB_MAP
|
||||
|
||||
|
||||
cdef class VisionIpcServer:
|
||||
cdef cppVisionIpcServer * server
|
||||
|
||||
def __init__(self, string name):
|
||||
self.server = new cppVisionIpcServer(name, NULL, NULL)
|
||||
|
||||
def create_buffers(self, VisionStreamType tp, size_t num_buffers, bool rgb, size_t width, size_t height):
|
||||
self.server.create_buffers(tp, num_buffers, rgb, width, height)
|
||||
|
||||
def send(self, VisionStreamType tp, const unsigned char[:] data, uint32_t frame_id=0, uint64_t timestamp_sof=0, uint64_t timestamp_eof=0):
|
||||
cdef cppVisionBuf * buf = self.server.get_buffer(tp)
|
||||
|
||||
# Populate buffer
|
||||
assert buf.len == len(data)
|
||||
memcpy(buf.addr, &data[0], len(data))
|
||||
buf.set_frame_id(frame_id)
|
||||
|
||||
cdef VisionIpcBufExtra extra
|
||||
extra.frame_id = frame_id
|
||||
extra.timestamp_sof = timestamp_sof
|
||||
extra.timestamp_eof = timestamp_eof
|
||||
|
||||
self.server.send(buf, &extra, False)
|
||||
|
||||
def start_listener(self):
|
||||
self.server.start_listener()
|
||||
|
||||
def __dealloc__(self):
|
||||
del self.server
|
||||
|
||||
|
||||
cdef class VisionIpcClient:
|
||||
cdef cppVisionBuf * buf
|
||||
cdef cppVisionIpcClient * client
|
||||
|
||||
def __cinit__(self, string name, VisionStreamType stream, bool conflate):
|
||||
self.client = new cppVisionIpcClient(name, stream, conflate, NULL, NULL)
|
||||
self.buf = NULL
|
||||
|
||||
def __dealloc__(self):
|
||||
del self.client
|
||||
|
||||
@property
|
||||
def width(self):
|
||||
return None if not self.buf else self.buf.width
|
||||
|
||||
@property
|
||||
def height(self):
|
||||
return None if not self.buf else self.buf.height
|
||||
|
||||
@property
|
||||
def stride(self):
|
||||
return None if not self.buf else self.buf.stride
|
||||
|
||||
def recv(self, int timeout_ms=100):
|
||||
self.buf = self.client.recv(NULL, timeout_ms)
|
||||
if not self.buf:
|
||||
return None
|
||||
cdef cnp.ndarray dat = np.empty(self.buf.len, dtype=np.uint8)
|
||||
cdef char[:] dat_view = dat
|
||||
memcpy(&dat_view[0], self.buf.addr, self.buf.len)
|
||||
return dat
|
||||
|
||||
def connect(self, bool blocking):
|
||||
return self.client.connect(blocking)
|
||||
|
||||
def is_connected(self):
|
||||
return self.client.is_connected()
|
||||
@@ -0,0 +1,184 @@
|
||||
#include <iostream>
|
||||
#include <chrono>
|
||||
#include <cassert>
|
||||
#include <random>
|
||||
|
||||
#include <poll.h>
|
||||
#include <sys/socket.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "messaging/messaging.h"
|
||||
#include "visionipc/ipc.h"
|
||||
#include "visionipc/visionipc_server.h"
|
||||
#include "logger/logger.h"
|
||||
|
||||
std::string get_endpoint_name(std::string name, VisionStreamType type){
|
||||
if (messaging_use_zmq()){
|
||||
assert(name == "camerad" || name == "navd");
|
||||
return std::to_string(9000 + static_cast<int>(type));
|
||||
} else {
|
||||
return "visionipc_" + name + "_" + std::to_string(type);
|
||||
}
|
||||
}
|
||||
|
||||
VisionIpcServer::VisionIpcServer(std::string name, cl_device_id device_id, cl_context ctx) : name(name), device_id(device_id), ctx(ctx) {
|
||||
msg_ctx = Context::create();
|
||||
|
||||
std::random_device rd("/dev/urandom");
|
||||
std::uniform_int_distribution<uint64_t> distribution(0,std::numeric_limits<uint64_t>::max());
|
||||
server_id = distribution(rd);
|
||||
}
|
||||
|
||||
void VisionIpcServer::create_buffers(VisionStreamType type, size_t num_buffers, bool rgb, size_t width, size_t height){
|
||||
// TODO: assert that this type is not created yet
|
||||
assert(num_buffers < VISIONIPC_MAX_FDS);
|
||||
int aligned_w = 0, aligned_h = 0;
|
||||
|
||||
size_t size = 0;
|
||||
size_t stride = 0; // Only used for RGB
|
||||
|
||||
if (rgb) {
|
||||
visionbuf_compute_aligned_width_and_height(width, height, &aligned_w, &aligned_h);
|
||||
size = (size_t)aligned_w * (size_t)aligned_h * 3;
|
||||
stride = aligned_w * 3;
|
||||
} else {
|
||||
size = width * height * 3 / 2;
|
||||
}
|
||||
|
||||
// Create map + alloc requested buffers
|
||||
for (size_t i = 0; i < num_buffers; i++){
|
||||
VisionBuf* buf = new VisionBuf();
|
||||
buf->allocate(size);
|
||||
buf->idx = i;
|
||||
buf->type = type;
|
||||
|
||||
if (device_id) buf->init_cl(device_id, ctx);
|
||||
|
||||
rgb ? buf->init_rgb(width, height, stride) : buf->init_yuv(width, height);
|
||||
|
||||
buffers[type].push_back(buf);
|
||||
}
|
||||
|
||||
cur_idx[type] = 0;
|
||||
|
||||
// Create msgq publisher for each of the `name` + type combos
|
||||
// TODO: compute port number directly if using zmq
|
||||
sockets[type] = PubSocket::create(msg_ctx, get_endpoint_name(name, type), false);
|
||||
}
|
||||
|
||||
|
||||
void VisionIpcServer::start_listener(){
|
||||
listener_thread = std::thread(&VisionIpcServer::listener, this);
|
||||
}
|
||||
|
||||
|
||||
void VisionIpcServer::listener(){
|
||||
std::cout << "Starting listener for: " << name << std::endl;
|
||||
|
||||
std::string path = "/tmp/visionipc_" + name;
|
||||
int sock = ipc_bind(path.c_str());
|
||||
assert(sock >= 0);
|
||||
|
||||
while (!should_exit){
|
||||
// Wait for incoming connection
|
||||
struct pollfd polls[1] = {{0}};
|
||||
polls[0].fd = sock;
|
||||
polls[0].events = POLLIN;
|
||||
|
||||
int ret = poll(polls, 1, 100);
|
||||
if (ret < 0) {
|
||||
if (errno == EINTR || errno == EAGAIN) continue;
|
||||
std::cout << "poll failed, stopping listener" << std::endl;
|
||||
break;
|
||||
}
|
||||
|
||||
if (should_exit) break;
|
||||
if (!polls[0].revents) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Handle incoming request
|
||||
int fd = accept(sock, NULL, NULL);
|
||||
assert(fd >= 0);
|
||||
|
||||
VisionStreamType type = VisionStreamType::VISION_STREAM_MAX;
|
||||
int r = ipc_sendrecv_with_fds(false, fd, &type, sizeof(type), nullptr, 0, nullptr);
|
||||
assert(r == sizeof(type));
|
||||
if (buffers.count(type) <= 0) {
|
||||
std::cout << "got request for invalid buffer type: " << type << std::endl;
|
||||
close(fd);
|
||||
continue;
|
||||
}
|
||||
|
||||
int fds[VISIONIPC_MAX_FDS];
|
||||
int num_fds = buffers[type].size();
|
||||
VisionBuf bufs[VISIONIPC_MAX_FDS];
|
||||
|
||||
for (int i = 0; i < num_fds; i++){
|
||||
fds[i] = buffers[type][i]->fd;
|
||||
bufs[i] = *buffers[type][i];
|
||||
|
||||
// Remove some private openCL/ion metadata
|
||||
bufs[i].buf_cl = 0;
|
||||
bufs[i].copy_q = 0;
|
||||
bufs[i].handle = 0;
|
||||
|
||||
bufs[i].server_id = server_id;
|
||||
}
|
||||
|
||||
r = ipc_sendrecv_with_fds(true, fd, &bufs, sizeof(VisionBuf) * num_fds, fds, num_fds, nullptr);
|
||||
|
||||
close(fd);
|
||||
}
|
||||
|
||||
std::cout << "Stopping listener for: " << name << std::endl;
|
||||
close(sock);
|
||||
}
|
||||
|
||||
|
||||
|
||||
VisionBuf * VisionIpcServer::get_buffer(VisionStreamType type){
|
||||
// Do we want to keep track if the buffer has been sent out yet and warn user?
|
||||
assert(buffers.count(type));
|
||||
auto b = buffers[type];
|
||||
return b[cur_idx[type]++ % b.size()];
|
||||
}
|
||||
|
||||
void VisionIpcServer::send(VisionBuf * buf, VisionIpcBufExtra * extra, bool sync){
|
||||
if (sync) {
|
||||
if (buf->sync(VISIONBUF_SYNC_FROM_DEVICE) != 0) {
|
||||
LOGE("Failed to sync buffer");
|
||||
}
|
||||
}
|
||||
assert(buffers.count(buf->type));
|
||||
assert(buf->idx < buffers[buf->type].size());
|
||||
|
||||
// Send over correct msgq socket
|
||||
VisionIpcPacket packet = {0};
|
||||
packet.server_id = server_id;
|
||||
packet.idx = buf->idx;
|
||||
packet.extra = *extra;
|
||||
|
||||
sockets[buf->type]->send((char*)&packet, sizeof(packet));
|
||||
}
|
||||
|
||||
VisionIpcServer::~VisionIpcServer(){
|
||||
should_exit = true;
|
||||
listener_thread.join();
|
||||
|
||||
// VisionBuf cleanup
|
||||
for( auto const& [type, buf] : buffers ) {
|
||||
for (VisionBuf* b : buf){
|
||||
if (b->free() != 0) {
|
||||
LOGE("Failed to free buffer");
|
||||
}
|
||||
delete b;
|
||||
}
|
||||
}
|
||||
|
||||
// Messaging cleanup
|
||||
for( auto const& [type, sock] : sockets ) {
|
||||
delete sock;
|
||||
}
|
||||
delete msg_ctx;
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
#pragma once
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <atomic>
|
||||
#include <map>
|
||||
|
||||
#include "messaging/messaging.h"
|
||||
#include "visionipc/visionipc.h"
|
||||
#include "visionipc/visionbuf.h"
|
||||
|
||||
std::string get_endpoint_name(std::string name, VisionStreamType type);
|
||||
|
||||
class VisionIpcServer {
|
||||
private:
|
||||
cl_device_id device_id = nullptr;
|
||||
cl_context ctx = nullptr;
|
||||
uint64_t server_id;
|
||||
|
||||
std::atomic<bool> should_exit = false;
|
||||
std::string name;
|
||||
std::thread listener_thread;
|
||||
|
||||
std::map<VisionStreamType, std::atomic<size_t> > cur_idx;
|
||||
std::map<VisionStreamType, std::vector<VisionBuf*> > buffers;
|
||||
std::map<VisionStreamType, std::map<VisionBuf*, size_t> > idxs;
|
||||
|
||||
Context * msg_ctx;
|
||||
std::map<VisionStreamType, PubSocket*> sockets;
|
||||
|
||||
void listener(void);
|
||||
|
||||
public:
|
||||
VisionIpcServer(std::string name, cl_device_id device_id=nullptr, cl_context ctx=nullptr);
|
||||
~VisionIpcServer();
|
||||
|
||||
VisionBuf * get_buffer(VisionStreamType type);
|
||||
|
||||
void create_buffers(VisionStreamType type, size_t num_buffers, bool rgb, size_t width, size_t height);
|
||||
void send(VisionBuf * buf, VisionIpcBufExtra * extra, bool sync=true);
|
||||
void start_listener();
|
||||
};
|
||||
@@ -0,0 +1,137 @@
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
|
||||
#include "catch2/catch.hpp"
|
||||
#include "visionipc_server.h"
|
||||
#include "visionipc_client.h"
|
||||
|
||||
static void zmq_sleep(int milliseconds=1000){
|
||||
if (messaging_use_zmq()){
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(milliseconds));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Connecting"){
|
||||
VisionIpcServer server("camerad");
|
||||
server.create_buffers(VISION_STREAM_ROAD, 1, false, 100, 100);
|
||||
server.start_listener();
|
||||
|
||||
VisionIpcClient client = VisionIpcClient("camerad", VISION_STREAM_ROAD, false);
|
||||
REQUIRE(client.connect());
|
||||
|
||||
REQUIRE(client.connected);
|
||||
}
|
||||
|
||||
TEST_CASE("Check buffers"){
|
||||
size_t width = 100, height = 200, num_buffers = 5;
|
||||
VisionIpcServer server("camerad");
|
||||
server.create_buffers(VISION_STREAM_ROAD, num_buffers, false, width, height);
|
||||
server.start_listener();
|
||||
|
||||
VisionIpcClient client = VisionIpcClient("camerad", VISION_STREAM_ROAD, false);
|
||||
REQUIRE(client.connect());
|
||||
|
||||
REQUIRE(client.buffers[0].width == width);
|
||||
REQUIRE(client.buffers[0].height == height);
|
||||
REQUIRE(client.buffers[0].len);
|
||||
REQUIRE(client.num_buffers == num_buffers);
|
||||
}
|
||||
|
||||
TEST_CASE("Check yuv/rgb"){
|
||||
VisionIpcServer server("camerad");
|
||||
server.create_buffers(VISION_STREAM_ROAD, 1, false, 100, 100);
|
||||
server.create_buffers(VISION_STREAM_RGB_ROAD, 1, true, 100, 100);
|
||||
server.start_listener();
|
||||
|
||||
VisionIpcClient client_yuv = VisionIpcClient("camerad", VISION_STREAM_ROAD, false);
|
||||
VisionIpcClient client_rgb = VisionIpcClient("camerad", VISION_STREAM_RGB_ROAD, false);
|
||||
client_yuv.connect();
|
||||
client_rgb.connect();
|
||||
|
||||
REQUIRE(client_rgb.buffers[0].rgb == true);
|
||||
REQUIRE(client_yuv.buffers[0].rgb == false);
|
||||
}
|
||||
|
||||
TEST_CASE("Send single buffer"){
|
||||
VisionIpcServer server("camerad");
|
||||
server.create_buffers(VISION_STREAM_ROAD, 1, true, 100, 100);
|
||||
server.start_listener();
|
||||
|
||||
VisionIpcClient client = VisionIpcClient("camerad", VISION_STREAM_ROAD, false);
|
||||
REQUIRE(client.connect());
|
||||
zmq_sleep();
|
||||
|
||||
VisionBuf * buf = server.get_buffer(VISION_STREAM_ROAD);
|
||||
REQUIRE(buf != nullptr);
|
||||
|
||||
*((uint64_t*)buf->addr) = 1234;
|
||||
|
||||
VisionIpcBufExtra extra = {0};
|
||||
extra.frame_id = 1337;
|
||||
buf->set_frame_id(extra.frame_id);
|
||||
|
||||
server.send(buf, &extra);
|
||||
|
||||
VisionIpcBufExtra extra_recv = {0};
|
||||
VisionBuf * recv_buf = client.recv(&extra_recv);
|
||||
REQUIRE(recv_buf != nullptr);
|
||||
REQUIRE(*(uint64_t*)recv_buf->addr == 1234);
|
||||
REQUIRE(extra_recv.frame_id == extra.frame_id);
|
||||
REQUIRE(recv_buf->get_frame_id() == extra.frame_id);
|
||||
}
|
||||
|
||||
|
||||
TEST_CASE("Test no conflate"){
|
||||
VisionIpcServer server("camerad");
|
||||
server.create_buffers(VISION_STREAM_ROAD, 1, true, 100, 100);
|
||||
server.start_listener();
|
||||
|
||||
VisionIpcClient client = VisionIpcClient("camerad", VISION_STREAM_ROAD, false);
|
||||
REQUIRE(client.connect());
|
||||
zmq_sleep();
|
||||
|
||||
VisionBuf * buf = server.get_buffer(VISION_STREAM_ROAD);
|
||||
REQUIRE(buf != nullptr);
|
||||
|
||||
VisionIpcBufExtra extra = {0};
|
||||
extra.frame_id = 1;
|
||||
server.send(buf, &extra);
|
||||
extra.frame_id = 2;
|
||||
server.send(buf, &extra);
|
||||
|
||||
VisionIpcBufExtra extra_recv = {0};
|
||||
VisionBuf * recv_buf = client.recv(&extra_recv);
|
||||
REQUIRE(recv_buf != nullptr);
|
||||
REQUIRE(extra_recv.frame_id == 1);
|
||||
|
||||
recv_buf = client.recv(&extra_recv);
|
||||
REQUIRE(recv_buf != nullptr);
|
||||
REQUIRE(extra_recv.frame_id == 2);
|
||||
}
|
||||
|
||||
TEST_CASE("Test conflate"){
|
||||
VisionIpcServer server("camerad");
|
||||
server.create_buffers(VISION_STREAM_ROAD, 1, true, 100, 100);
|
||||
server.start_listener();
|
||||
|
||||
VisionIpcClient client = VisionIpcClient("camerad", VISION_STREAM_ROAD, true);
|
||||
REQUIRE(client.connect());
|
||||
zmq_sleep();
|
||||
|
||||
VisionBuf * buf = server.get_buffer(VISION_STREAM_ROAD);
|
||||
REQUIRE(buf != nullptr);
|
||||
|
||||
VisionIpcBufExtra extra = {0};
|
||||
extra.frame_id = 1;
|
||||
server.send(buf, &extra);
|
||||
extra.frame_id = 2;
|
||||
server.send(buf, &extra);
|
||||
|
||||
VisionIpcBufExtra extra_recv = {0};
|
||||
VisionBuf * recv_buf = client.recv(&extra_recv);
|
||||
REQUIRE(recv_buf != nullptr);
|
||||
REQUIRE(extra_recv.frame_id == 2);
|
||||
|
||||
recv_buf = client.recv(&extra_recv);
|
||||
REQUIRE(recv_buf == nullptr);
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
*.cpp
|
||||
@@ -0,0 +1,34 @@
|
||||
Import('env', 'envCython', 'arch', 'SHARED')
|
||||
|
||||
if SHARED:
|
||||
fxn = env.SharedLibrary
|
||||
else:
|
||||
fxn = env.Library
|
||||
|
||||
common_libs = [
|
||||
'params.cc',
|
||||
'statlog.cc',
|
||||
'swaglog.cc',
|
||||
'util.cc',
|
||||
'gpio.cc',
|
||||
'i2c.cc',
|
||||
'watchdog.cc',
|
||||
]
|
||||
|
||||
_common = fxn('common', common_libs, LIBS="json11")
|
||||
|
||||
files = [
|
||||
'clutil.cc',
|
||||
'visionimg.cc',
|
||||
]
|
||||
|
||||
_gpucommon = fxn('gpucommon', files)
|
||||
Export('_common', '_gpucommon')
|
||||
|
||||
if GetOption('test'):
|
||||
env.Program('tests/test_util', ['tests/test_util.cc'], LIBS=[_common])
|
||||
env.Program('tests/test_swaglog', ['tests/test_swaglog.cc'], LIBS=[_common, 'json11', 'zmq', 'pthread'])
|
||||
|
||||
# Cython
|
||||
envCython.Program('clock.so', 'clock.pyx')
|
||||
envCython.Program('params_pyx.so', 'params_pyx.pyx', LIBS=envCython['LIBS'] + [_common, 'zmq', 'json11'])
|
||||
@@ -0,0 +1,46 @@
|
||||
import jwt
|
||||
import os
|
||||
import requests
|
||||
from datetime import datetime, timedelta
|
||||
from common.basedir import PERSIST
|
||||
from selfdrive.version import get_version
|
||||
|
||||
API_HOST = os.getenv('API_HOST', 'https://api.commadotai.com')
|
||||
|
||||
class Api():
|
||||
def __init__(self, dongle_id):
|
||||
self.dongle_id = dongle_id
|
||||
with open(PERSIST+'/comma/id_rsa') as f:
|
||||
self.private_key = f.read()
|
||||
|
||||
def get(self, *args, **kwargs):
|
||||
return self.request('GET', *args, **kwargs)
|
||||
|
||||
def post(self, *args, **kwargs):
|
||||
return self.request('POST', *args, **kwargs)
|
||||
|
||||
def request(self, method, endpoint, timeout=None, access_token=None, **params):
|
||||
return api_get(endpoint, method=method, timeout=timeout, access_token=access_token, **params)
|
||||
|
||||
def get_token(self):
|
||||
now = datetime.utcnow()
|
||||
payload = {
|
||||
'identity': self.dongle_id,
|
||||
'nbf': now,
|
||||
'iat': now,
|
||||
'exp': now + timedelta(hours=1)
|
||||
}
|
||||
token = jwt.encode(payload, self.private_key, algorithm='RS256')
|
||||
if isinstance(token, bytes):
|
||||
token = token.decode('utf8')
|
||||
return token
|
||||
|
||||
|
||||
def api_get(endpoint, method='GET', timeout=None, access_token=None, **params):
|
||||
headers = {}
|
||||
if access_token is not None:
|
||||
headers['Authorization'] = "JWT " + access_token
|
||||
|
||||
headers['User-Agent'] = "openpilot-" + get_version()
|
||||
|
||||
return requests.request(method, API_HOST + "/" + endpoint, timeout=timeout, headers=headers, params=params)
|
||||
@@ -0,0 +1,11 @@
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
from selfdrive.hardware import PC
|
||||
|
||||
BASEDIR = os.path.abspath(os.path.join(os.path.dirname(os.path.realpath(__file__)), "../"))
|
||||
|
||||
if PC:
|
||||
PERSIST = os.path.join(str(Path.home()), ".comma", "persist")
|
||||
else:
|
||||
PERSIST = "/persist"
|
||||
@@ -0,0 +1,24 @@
|
||||
# distutils: language = c++
|
||||
# cython: language_level = 3
|
||||
from posix.time cimport clock_gettime, timespec, CLOCK_MONOTONIC_RAW, clockid_t
|
||||
|
||||
IF UNAME_SYSNAME == "Darwin":
|
||||
# Darwin doesn't have a CLOCK_BOOTTIME
|
||||
CLOCK_BOOTTIME = CLOCK_MONOTONIC_RAW
|
||||
ELSE:
|
||||
from posix.time cimport CLOCK_BOOTTIME
|
||||
|
||||
cdef double readclock(clockid_t clock_id):
|
||||
cdef timespec ts
|
||||
cdef double current
|
||||
|
||||
clock_gettime(clock_id, &ts)
|
||||
current = ts.tv_sec + (ts.tv_nsec / 1000000000.)
|
||||
return current
|
||||
|
||||
def monotonic_time():
|
||||
return readclock(CLOCK_MONOTONIC_RAW)
|
||||
|
||||
def sec_since_boot():
|
||||
return readclock(CLOCK_BOOTTIME)
|
||||
|
||||
@@ -0,0 +1,193 @@
|
||||
#include "common/clutil.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
|
||||
#include "common/util.h"
|
||||
|
||||
namespace { // helper functions
|
||||
|
||||
template <typename Func, typename Id, typename Name>
|
||||
std::string get_info(Func get_info_func, Id id, Name param_name) {
|
||||
size_t size = 0;
|
||||
CL_CHECK(get_info_func(id, param_name, 0, NULL, &size));
|
||||
std::string info(size, '\0');
|
||||
CL_CHECK(get_info_func(id, param_name, size, info.data(), NULL));
|
||||
return info;
|
||||
}
|
||||
inline std::string get_platform_info(cl_platform_id id, cl_platform_info name) { return get_info(&clGetPlatformInfo, id, name); }
|
||||
inline std::string get_device_info(cl_device_id id, cl_device_info name) { return get_info(&clGetDeviceInfo, id, name); }
|
||||
|
||||
void cl_print_info(cl_platform_id platform, cl_device_id device) {
|
||||
size_t work_group_size = 0;
|
||||
cl_device_type device_type = 0;
|
||||
clGetDeviceInfo(device, CL_DEVICE_MAX_WORK_GROUP_SIZE, sizeof(work_group_size), &work_group_size, NULL);
|
||||
clGetDeviceInfo(device, CL_DEVICE_TYPE, sizeof(device_type), &device_type, NULL);
|
||||
const char *type_str = "Other...";
|
||||
switch (device_type) {
|
||||
case CL_DEVICE_TYPE_CPU: type_str ="CL_DEVICE_TYPE_CPU"; break;
|
||||
case CL_DEVICE_TYPE_GPU: type_str = "CL_DEVICE_TYPE_GPU"; break;
|
||||
case CL_DEVICE_TYPE_ACCELERATOR: type_str = "CL_DEVICE_TYPE_ACCELERATOR"; break;
|
||||
}
|
||||
|
||||
std::cout << "vendor: " << get_platform_info(platform, CL_PLATFORM_VENDOR) << std::endl
|
||||
<< "platform version: " << get_platform_info(platform, CL_PLATFORM_VERSION) << std::endl
|
||||
<< "profile: " << get_platform_info(platform, CL_PLATFORM_PROFILE) << std::endl
|
||||
<< "extensions: " << get_platform_info(platform, CL_PLATFORM_EXTENSIONS) << std::endl
|
||||
<< "name :" << get_device_info(device, CL_DEVICE_NAME) << std::endl
|
||||
<< "device version :" << get_device_info(device, CL_DEVICE_VERSION) << std::endl
|
||||
<< "max work group size :" << work_group_size << std::endl
|
||||
<< "type = " << device_type << " = " << type_str << std::endl;
|
||||
}
|
||||
|
||||
void cl_print_build_errors(cl_program program, cl_device_id device) {
|
||||
cl_build_status status;
|
||||
clGetProgramBuildInfo(program, device, CL_PROGRAM_BUILD_STATUS, sizeof(status), &status, NULL);
|
||||
size_t log_size;
|
||||
clGetProgramBuildInfo(program, device, CL_PROGRAM_BUILD_LOG, 0, NULL, &log_size);
|
||||
std::string log(log_size, '\0');
|
||||
clGetProgramBuildInfo(program, device, CL_PROGRAM_BUILD_LOG, log_size, &log[0], NULL);
|
||||
|
||||
std::cout << "build failed; status=" << status << ", log:" << std::endl << log << std::endl;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
cl_device_id cl_get_device_id(cl_device_type device_type) {
|
||||
cl_uint num_platforms = 0;
|
||||
CL_CHECK(clGetPlatformIDs(0, NULL, &num_platforms));
|
||||
std::unique_ptr<cl_platform_id[]> platform_ids = std::make_unique<cl_platform_id[]>(num_platforms);
|
||||
CL_CHECK(clGetPlatformIDs(num_platforms, &platform_ids[0], NULL));
|
||||
|
||||
for (size_t i = 0; i < num_platforms; ++i) {
|
||||
std::cout << "platform[" << i << "] CL_PLATFORM_NAME: " << get_platform_info(platform_ids[i], CL_PLATFORM_NAME) << std::endl;
|
||||
// Get first device
|
||||
if (cl_device_id device_id = NULL; clGetDeviceIDs(platform_ids[i], device_type, 1, &device_id, NULL) == 0 && device_id) {
|
||||
cl_print_info(platform_ids[i], device_id);
|
||||
return device_id;
|
||||
}
|
||||
}
|
||||
std::cout << "No valid openCL platform found" << std::endl;
|
||||
assert(0);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
cl_program cl_program_from_file(cl_context ctx, cl_device_id device_id, const char* path, const char* args) {
|
||||
return cl_program_from_source(ctx, device_id, util::read_file(path), args);
|
||||
}
|
||||
|
||||
cl_program cl_program_from_source(cl_context ctx, cl_device_id device_id, const std::string& src, const char* args) {
|
||||
cl_program prg = CL_CHECK_ERR(clCreateProgramWithSource(ctx, 1, (const char*[]){src.c_str()}, NULL, &err));
|
||||
if (int err = clBuildProgram(prg, 1, &device_id, args, NULL, NULL); err != 0) {
|
||||
cl_print_build_errors(prg, device_id);
|
||||
assert(0);
|
||||
}
|
||||
return prg;
|
||||
}
|
||||
|
||||
cl_program cl_program_from_binary(cl_context ctx, cl_device_id device_id, const uint8_t* binary, size_t length, const char* args) {
|
||||
cl_program prg = CL_CHECK_ERR(clCreateProgramWithBinary(ctx, 1, &device_id, &length, (const uint8_t*[]){binary}, NULL, &err));
|
||||
if (int err = clBuildProgram(prg, 1, &device_id, args, NULL, NULL); err != 0) {
|
||||
cl_print_build_errors(prg, device_id);
|
||||
assert(0);
|
||||
}
|
||||
return prg;
|
||||
}
|
||||
|
||||
// Given a cl code and return a string representation
|
||||
#define CL_ERR_TO_STR(err) case err: return #err
|
||||
const char* cl_get_error_string(int err) {
|
||||
switch (err) {
|
||||
CL_ERR_TO_STR(CL_SUCCESS);
|
||||
CL_ERR_TO_STR(CL_DEVICE_NOT_FOUND);
|
||||
CL_ERR_TO_STR(CL_DEVICE_NOT_AVAILABLE);
|
||||
CL_ERR_TO_STR(CL_COMPILER_NOT_AVAILABLE);
|
||||
CL_ERR_TO_STR(CL_MEM_OBJECT_ALLOCATION_FAILURE);
|
||||
CL_ERR_TO_STR(CL_OUT_OF_RESOURCES);
|
||||
CL_ERR_TO_STR(CL_OUT_OF_HOST_MEMORY);
|
||||
CL_ERR_TO_STR(CL_PROFILING_INFO_NOT_AVAILABLE);
|
||||
CL_ERR_TO_STR(CL_MEM_COPY_OVERLAP);
|
||||
CL_ERR_TO_STR(CL_IMAGE_FORMAT_MISMATCH);
|
||||
CL_ERR_TO_STR(CL_IMAGE_FORMAT_NOT_SUPPORTED);
|
||||
CL_ERR_TO_STR(CL_MAP_FAILURE);
|
||||
CL_ERR_TO_STR(CL_MISALIGNED_SUB_BUFFER_OFFSET);
|
||||
CL_ERR_TO_STR(CL_EXEC_STATUS_ERROR_FOR_EVENTS_IN_WAIT_LIST);
|
||||
CL_ERR_TO_STR(CL_COMPILE_PROGRAM_FAILURE);
|
||||
CL_ERR_TO_STR(CL_LINKER_NOT_AVAILABLE);
|
||||
CL_ERR_TO_STR(CL_LINK_PROGRAM_FAILURE);
|
||||
CL_ERR_TO_STR(CL_DEVICE_PARTITION_FAILED);
|
||||
CL_ERR_TO_STR(CL_KERNEL_ARG_INFO_NOT_AVAILABLE);
|
||||
CL_ERR_TO_STR(CL_INVALID_VALUE);
|
||||
CL_ERR_TO_STR(CL_INVALID_DEVICE_TYPE);
|
||||
CL_ERR_TO_STR(CL_INVALID_PLATFORM);
|
||||
CL_ERR_TO_STR(CL_INVALID_DEVICE);
|
||||
CL_ERR_TO_STR(CL_INVALID_CONTEXT);
|
||||
CL_ERR_TO_STR(CL_INVALID_QUEUE_PROPERTIES);
|
||||
CL_ERR_TO_STR(CL_INVALID_COMMAND_QUEUE);
|
||||
CL_ERR_TO_STR(CL_INVALID_HOST_PTR);
|
||||
CL_ERR_TO_STR(CL_INVALID_MEM_OBJECT);
|
||||
CL_ERR_TO_STR(CL_INVALID_IMAGE_FORMAT_DESCRIPTOR);
|
||||
CL_ERR_TO_STR(CL_INVALID_IMAGE_SIZE);
|
||||
CL_ERR_TO_STR(CL_INVALID_SAMPLER);
|
||||
CL_ERR_TO_STR(CL_INVALID_BINARY);
|
||||
CL_ERR_TO_STR(CL_INVALID_BUILD_OPTIONS);
|
||||
CL_ERR_TO_STR(CL_INVALID_PROGRAM);
|
||||
CL_ERR_TO_STR(CL_INVALID_PROGRAM_EXECUTABLE);
|
||||
CL_ERR_TO_STR(CL_INVALID_KERNEL_NAME);
|
||||
CL_ERR_TO_STR(CL_INVALID_KERNEL_DEFINITION);
|
||||
CL_ERR_TO_STR(CL_INVALID_KERNEL);
|
||||
CL_ERR_TO_STR(CL_INVALID_ARG_INDEX);
|
||||
CL_ERR_TO_STR(CL_INVALID_ARG_VALUE);
|
||||
CL_ERR_TO_STR(CL_INVALID_ARG_SIZE);
|
||||
CL_ERR_TO_STR(CL_INVALID_KERNEL_ARGS);
|
||||
CL_ERR_TO_STR(CL_INVALID_WORK_DIMENSION);
|
||||
CL_ERR_TO_STR(CL_INVALID_WORK_GROUP_SIZE);
|
||||
CL_ERR_TO_STR(CL_INVALID_WORK_ITEM_SIZE);
|
||||
CL_ERR_TO_STR(CL_INVALID_GLOBAL_OFFSET);
|
||||
CL_ERR_TO_STR(CL_INVALID_EVENT_WAIT_LIST);
|
||||
CL_ERR_TO_STR(CL_INVALID_EVENT);
|
||||
CL_ERR_TO_STR(CL_INVALID_OPERATION);
|
||||
CL_ERR_TO_STR(CL_INVALID_GL_OBJECT);
|
||||
CL_ERR_TO_STR(CL_INVALID_BUFFER_SIZE);
|
||||
CL_ERR_TO_STR(CL_INVALID_MIP_LEVEL);
|
||||
CL_ERR_TO_STR(CL_INVALID_GLOBAL_WORK_SIZE);
|
||||
CL_ERR_TO_STR(CL_INVALID_PROPERTY);
|
||||
CL_ERR_TO_STR(CL_INVALID_IMAGE_DESCRIPTOR);
|
||||
CL_ERR_TO_STR(CL_INVALID_COMPILER_OPTIONS);
|
||||
CL_ERR_TO_STR(CL_INVALID_LINKER_OPTIONS);
|
||||
CL_ERR_TO_STR(CL_INVALID_DEVICE_PARTITION_COUNT);
|
||||
case -69: return "CL_INVALID_PIPE_SIZE";
|
||||
case -70: return "CL_INVALID_DEVICE_QUEUE";
|
||||
case -71: return "CL_INVALID_SPEC_ID";
|
||||
case -72: return "CL_MAX_SIZE_RESTRICTION_EXCEEDED";
|
||||
case -1002: return "CL_INVALID_D3D10_DEVICE_KHR";
|
||||
case -1003: return "CL_INVALID_D3D10_RESOURCE_KHR";
|
||||
case -1004: return "CL_D3D10_RESOURCE_ALREADY_ACQUIRED_KHR";
|
||||
case -1005: return "CL_D3D10_RESOURCE_NOT_ACQUIRED_KHR";
|
||||
case -1006: return "CL_INVALID_D3D11_DEVICE_KHR";
|
||||
case -1007: return "CL_INVALID_D3D11_RESOURCE_KHR";
|
||||
case -1008: return "CL_D3D11_RESOURCE_ALREADY_ACQUIRED_KHR";
|
||||
case -1009: return "CL_D3D11_RESOURCE_NOT_ACQUIRED_KHR";
|
||||
case -1010: return "CL_INVALID_DX9_MEDIA_ADAPTER_KHR";
|
||||
case -1011: return "CL_INVALID_DX9_MEDIA_SURFACE_KHR";
|
||||
case -1012: return "CL_DX9_MEDIA_SURFACE_ALREADY_ACQUIRED_KHR";
|
||||
case -1013: return "CL_DX9_MEDIA_SURFACE_NOT_ACQUIRED_KHR";
|
||||
case -1093: return "CL_INVALID_EGL_OBJECT_KHR";
|
||||
case -1092: return "CL_EGL_RESOURCE_NOT_ACQUIRED_KHR";
|
||||
case -1001: return "CL_PLATFORM_NOT_FOUND_KHR";
|
||||
case -1057: return "CL_DEVICE_PARTITION_FAILED_EXT";
|
||||
case -1058: return "CL_INVALID_PARTITION_COUNT_EXT";
|
||||
case -1059: return "CL_INVALID_PARTITION_NAME_EXT";
|
||||
case -1094: return "CL_INVALID_ACCELERATOR_INTEL";
|
||||
case -1095: return "CL_INVALID_ACCELERATOR_TYPE_INTEL";
|
||||
case -1096: return "CL_INVALID_ACCELERATOR_DESCRIPTOR_INTEL";
|
||||
case -1097: return "CL_ACCELERATOR_TYPE_NOT_SUPPORTED_INTEL";
|
||||
case -1000: return "CL_INVALID_GL_SHAREGROUP_REFERENCE_KHR";
|
||||
case -1098: return "CL_INVALID_VA_API_MEDIA_ADAPTER_INTEL";
|
||||
case -1099: return "CL_INVALID_VA_API_MEDIA_SURFACE_INTEL";
|
||||
case -1100: return "CL_VA_API_MEDIA_SURFACE_ALREADY_ACQUIRED_INTEL";
|
||||
case -1101: return "CL_VA_API_MEDIA_SURFACE_NOT_ACQUIRED_INTEL";
|
||||
default: return "CL_UNKNOWN_ERROR";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef __APPLE__
|
||||
#include <OpenCL/cl.h>
|
||||
#else
|
||||
#include <CL/cl.h>
|
||||
#endif
|
||||
|
||||
#include <string>
|
||||
|
||||
#define CL_CHECK(_expr) \
|
||||
do { \
|
||||
assert(CL_SUCCESS == (_expr)); \
|
||||
} while (0)
|
||||
|
||||
#define CL_CHECK_ERR(_expr) \
|
||||
({ \
|
||||
cl_int err = CL_INVALID_VALUE; \
|
||||
__typeof__(_expr) _ret = _expr; \
|
||||
assert(_ret&& err == CL_SUCCESS); \
|
||||
_ret; \
|
||||
})
|
||||
|
||||
cl_device_id cl_get_device_id(cl_device_type device_type);
|
||||
cl_program cl_program_from_source(cl_context ctx, cl_device_id device_id, const std::string& src, const char* args = nullptr);
|
||||
cl_program cl_program_from_binary(cl_context ctx, cl_device_id device_id, const uint8_t* binary, size_t length, const char* args = nullptr);
|
||||
cl_program cl_program_from_file(cl_context ctx, cl_device_id device_id, const char* path, const char* args);
|
||||
const char* cl_get_error_string(int err);
|
||||
@@ -1,6 +1,5 @@
|
||||
import numpy as np
|
||||
|
||||
|
||||
class Conversions:
|
||||
# Speed
|
||||
MPH_TO_KPH = 1.609344
|
||||
@@ -0,0 +1,9 @@
|
||||
# remove all keys that end in DEPRECATED
|
||||
def strip_deprecated_keys(d):
|
||||
for k in list(d.keys()):
|
||||
if isinstance(k, str):
|
||||
if k.endswith('DEPRECATED'):
|
||||
d.pop(k)
|
||||
elif isinstance(d[k], dict):
|
||||
strip_deprecated_keys(d[k])
|
||||
return d
|
||||
@@ -0,0 +1,55 @@
|
||||
import os
|
||||
import sys
|
||||
import fcntl
|
||||
import hashlib
|
||||
import platform
|
||||
from cffi import FFI
|
||||
|
||||
def suffix():
|
||||
if platform.system() == "Darwin":
|
||||
return ".dylib"
|
||||
else:
|
||||
return ".so"
|
||||
|
||||
def ffi_wrap(name, c_code, c_header, tmpdir="/tmp/ccache", cflags="", libraries=None):
|
||||
if libraries is None:
|
||||
libraries = []
|
||||
|
||||
cache = name + "_" + hashlib.sha1(c_code.encode('utf-8')).hexdigest()
|
||||
try:
|
||||
os.mkdir(tmpdir)
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
fd = os.open(tmpdir, 0)
|
||||
fcntl.flock(fd, fcntl.LOCK_EX)
|
||||
try:
|
||||
sys.path.append(tmpdir)
|
||||
try:
|
||||
mod = __import__(cache)
|
||||
except Exception:
|
||||
print(f"cache miss {cache}")
|
||||
compile_code(cache, c_code, c_header, tmpdir, cflags, libraries)
|
||||
mod = __import__(cache)
|
||||
finally:
|
||||
os.close(fd)
|
||||
|
||||
return mod.ffi, mod.lib
|
||||
|
||||
|
||||
def compile_code(name, c_code, c_header, directory, cflags="", libraries=None):
|
||||
if libraries is None:
|
||||
libraries = []
|
||||
|
||||
ffibuilder = FFI()
|
||||
ffibuilder.set_source(name, c_code, source_extension='.cpp', libraries=libraries)
|
||||
ffibuilder.cdef(c_header)
|
||||
os.environ['OPT'] = "-fwrapv -O2 -DNDEBUG -std=c++1z"
|
||||
os.environ['CFLAGS'] = cflags
|
||||
ffibuilder.compile(verbose=True, debug=False, tmpdir=directory)
|
||||
|
||||
|
||||
def wrap_compiled(name, directory):
|
||||
sys.path.append(directory)
|
||||
mod = __import__(name)
|
||||
return mod.ffi, mod.lib
|
||||
@@ -0,0 +1,99 @@
|
||||
import os
|
||||
import shutil
|
||||
import tempfile
|
||||
from atomicwrites import AtomicWriter
|
||||
|
||||
|
||||
def mkdirs_exists_ok(path):
|
||||
if path.startswith('http://') or path.startswith('https://'):
|
||||
raise ValueError('URL path')
|
||||
try:
|
||||
os.makedirs(path)
|
||||
except OSError:
|
||||
if not os.path.isdir(path):
|
||||
raise
|
||||
|
||||
|
||||
def rm_not_exists_ok(path):
|
||||
try:
|
||||
os.remove(path)
|
||||
except OSError:
|
||||
if os.path.exists(path):
|
||||
raise
|
||||
|
||||
|
||||
def rm_tree_or_link(path):
|
||||
if os.path.islink(path):
|
||||
os.unlink(path)
|
||||
elif os.path.isdir(path):
|
||||
shutil.rmtree(path)
|
||||
|
||||
|
||||
def get_tmpdir_on_same_filesystem(path):
|
||||
normpath = os.path.normpath(path)
|
||||
parts = normpath.split("/")
|
||||
if len(parts) > 1 and parts[1] == "scratch":
|
||||
return "/scratch/tmp"
|
||||
elif len(parts) > 2 and parts[2] == "runner":
|
||||
return f"/{parts[1]}/runner/tmp"
|
||||
return "/tmp"
|
||||
|
||||
|
||||
class NamedTemporaryDir():
|
||||
def __init__(self, temp_dir=None):
|
||||
self._path = tempfile.mkdtemp(dir=temp_dir)
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
return self._path
|
||||
|
||||
def close(self):
|
||||
shutil.rmtree(self._path)
|
||||
|
||||
def __enter__(self):
|
||||
return self
|
||||
|
||||
def __exit__(self, exc_type, exc_value, traceback):
|
||||
self.close()
|
||||
|
||||
|
||||
class CallbackReader:
|
||||
"""Wraps a file, but overrides the read method to also
|
||||
call a callback function with the number of bytes read so far."""
|
||||
def __init__(self, f, callback, *args):
|
||||
self.f = f
|
||||
self.callback = callback
|
||||
self.cb_args = args
|
||||
self.total_read = 0
|
||||
|
||||
def __getattr__(self, attr):
|
||||
return getattr(self.f, attr)
|
||||
|
||||
def read(self, *args, **kwargs):
|
||||
chunk = self.f.read(*args, **kwargs)
|
||||
self.total_read += len(chunk)
|
||||
self.callback(*self.cb_args, self.total_read)
|
||||
return chunk
|
||||
|
||||
|
||||
def _get_fileobject_func(writer, temp_dir):
|
||||
def _get_fileobject():
|
||||
return writer.get_fileobject(dir=temp_dir)
|
||||
return _get_fileobject
|
||||
|
||||
def atomic_write_on_fs_tmp(path, **kwargs):
|
||||
"""Creates an atomic writer using a temporary file in a temporary directory
|
||||
on the same filesystem as path.
|
||||
"""
|
||||
# TODO(mgraczyk): This use of AtomicWriter relies on implementation details to set the temp
|
||||
# directory.
|
||||
writer = AtomicWriter(path, **kwargs)
|
||||
return writer._open(_get_fileobject_func(writer, get_tmpdir_on_same_filesystem(path)))
|
||||
|
||||
|
||||
def atomic_write_in_dir(path, **kwargs):
|
||||
"""Creates an atomic writer using a temporary file in the same directory
|
||||
as the destination file.
|
||||
"""
|
||||
writer = AtomicWriter(path, **kwargs)
|
||||
return writer._open(_get_fileobject_func(writer, os.path.dirname(path)))
|
||||
@@ -0,0 +1,18 @@
|
||||
class FirstOrderFilter:
|
||||
# first order filter
|
||||
def __init__(self, x0, rc, dt, initialized=True):
|
||||
self.x = x0
|
||||
self.dt = dt
|
||||
self.update_alpha(rc)
|
||||
self.initialized = initialized
|
||||
|
||||
def update_alpha(self, rc):
|
||||
self.alpha = self.dt / (rc + self.dt)
|
||||
|
||||
def update(self, x):
|
||||
if self.initialized:
|
||||
self.x = (1. - self.alpha) * self.x + self.alpha * x
|
||||
else:
|
||||
self.initialized = True
|
||||
self.x = x
|
||||
return self.x
|
||||
@@ -0,0 +1,32 @@
|
||||
#include "common/gpio.h"
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include "common/util.h"
|
||||
|
||||
// We assume that all pins have already been exported on boot,
|
||||
// and that we have permission to write to them.
|
||||
|
||||
int gpio_init(int pin_nr, bool output) {
|
||||
char pin_dir_path[50];
|
||||
int pin_dir_path_len = snprintf(pin_dir_path, sizeof(pin_dir_path),
|
||||
"/sys/class/gpio/gpio%d/direction", pin_nr);
|
||||
if(pin_dir_path_len <= 0) {
|
||||
return -1;
|
||||
}
|
||||
const char *value = output ? "out" : "in";
|
||||
return util::write_file(pin_dir_path, (void*)value, strlen(value));
|
||||
}
|
||||
|
||||
int gpio_set(int pin_nr, bool high) {
|
||||
char pin_val_path[50];
|
||||
int pin_val_path_len = snprintf(pin_val_path, sizeof(pin_val_path),
|
||||
"/sys/class/gpio/gpio%d/value", pin_nr);
|
||||
if(pin_val_path_len <= 0) {
|
||||
return -1;
|
||||
}
|
||||
return util::write_file(pin_val_path, (void*)(high ? "1" : "0"), 1);
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
// Pin definitions
|
||||
#ifdef QCOM2
|
||||
#define GPIO_HUB_RST_N 30
|
||||
#define GPIO_UBLOX_RST_N 32
|
||||
#define GPIO_UBLOX_SAFEBOOT_N 33
|
||||
#define GPIO_UBLOX_PWR_EN 34
|
||||
#define GPIO_STM_RST_N 124
|
||||
#define GPIO_STM_BOOT0 134
|
||||
#else
|
||||
#define GPIO_HUB_RST_N 0
|
||||
#define GPIO_UBLOX_RST_N 0
|
||||
#define GPIO_UBLOX_SAFEBOOT_N 0
|
||||
#define GPIO_UBLOX_PWR_EN 0
|
||||
#define GPIO_STM_RST_N 0
|
||||
#define GPIO_STM_BOOT0 0
|
||||
#endif
|
||||
|
||||
int gpio_init(int pin_nr, bool output);
|
||||
int gpio_set(int pin_nr, bool high);
|
||||
@@ -0,0 +1,14 @@
|
||||
def gpio_init(pin: int, output: bool) -> None:
|
||||
try:
|
||||
with open(f"/sys/class/gpio/gpio{pin}/direction", 'wb') as f:
|
||||
f.write(b"out" if output else b"in")
|
||||
except Exception as e:
|
||||
print(f"Failed to set gpio {pin} direction: {e}")
|
||||
|
||||
|
||||
def gpio_set(pin: int, high: bool) -> None:
|
||||
try:
|
||||
with open(f"/sys/class/gpio/gpio{pin}/value", 'wb') as f:
|
||||
f.write(b"1" if high else b"0")
|
||||
except Exception as e:
|
||||
print(f"Failed to set gpio {pin} value: {e}")
|
||||
@@ -0,0 +1,87 @@
|
||||
#include "common/i2c.h"
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <cassert>
|
||||
#include <cstdio>
|
||||
#include <stdexcept>
|
||||
|
||||
#include "common/util.h"
|
||||
#include "common/swaglog.h"
|
||||
#include "common/util.h"
|
||||
|
||||
#define UNUSED(x) (void)(x)
|
||||
|
||||
#ifdef QCOM2
|
||||
// TODO: decide if we want to isntall libi2c-dev everywhere
|
||||
extern "C" {
|
||||
#include <linux/i2c-dev.h>
|
||||
#include <i2c/smbus.h>
|
||||
}
|
||||
|
||||
I2CBus::I2CBus(uint8_t bus_id) {
|
||||
char bus_name[20];
|
||||
snprintf(bus_name, 20, "/dev/i2c-%d", bus_id);
|
||||
|
||||
i2c_fd = HANDLE_EINTR(open(bus_name, O_RDWR));
|
||||
if(i2c_fd < 0) {
|
||||
throw std::runtime_error("Failed to open I2C bus");
|
||||
}
|
||||
}
|
||||
|
||||
I2CBus::~I2CBus() {
|
||||
if(i2c_fd >= 0) { close(i2c_fd); }
|
||||
}
|
||||
|
||||
int I2CBus::read_register(uint8_t device_address, uint register_address, uint8_t *buffer, uint8_t len) {
|
||||
int ret = 0;
|
||||
|
||||
ret = HANDLE_EINTR(ioctl(i2c_fd, I2C_SLAVE, device_address));
|
||||
if(ret < 0) { goto fail; }
|
||||
|
||||
ret = i2c_smbus_read_i2c_block_data(i2c_fd, register_address, len, buffer);
|
||||
if((ret < 0) || (ret != len)) { goto fail; }
|
||||
|
||||
fail:
|
||||
return ret;
|
||||
}
|
||||
|
||||
int I2CBus::set_register(uint8_t device_address, uint register_address, uint8_t data) {
|
||||
int ret = 0;
|
||||
|
||||
ret = HANDLE_EINTR(ioctl(i2c_fd, I2C_SLAVE, device_address));
|
||||
if(ret < 0) { goto fail; }
|
||||
|
||||
ret = i2c_smbus_write_byte_data(i2c_fd, register_address, data);
|
||||
if(ret < 0) { goto fail; }
|
||||
|
||||
fail:
|
||||
return ret;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
I2CBus::I2CBus(uint8_t bus_id) {
|
||||
UNUSED(bus_id);
|
||||
i2c_fd = -1;
|
||||
}
|
||||
|
||||
I2CBus::~I2CBus() {}
|
||||
|
||||
int I2CBus::read_register(uint8_t device_address, uint register_address, uint8_t *buffer, uint8_t len) {
|
||||
UNUSED(device_address);
|
||||
UNUSED(register_address);
|
||||
UNUSED(buffer);
|
||||
UNUSED(len);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int I2CBus::set_register(uint8_t device_address, uint register_address, uint8_t data) {
|
||||
UNUSED(device_address);
|
||||
UNUSED(register_address);
|
||||
UNUSED(data);
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
class I2CBus {
|
||||
private:
|
||||
int i2c_fd;
|
||||
|
||||
public:
|
||||
I2CBus(uint8_t bus_id);
|
||||
~I2CBus();
|
||||
|
||||
int read_register(uint8_t device_address, uint register_address, uint8_t *buffer, uint8_t len);
|
||||
int set_register(uint8_t device_address, uint register_address, uint8_t data);
|
||||
};
|
||||
@@ -0,0 +1 @@
|
||||
simple_kalman_impl.c
|
||||
@@ -0,0 +1,3 @@
|
||||
Import('envCython')
|
||||
|
||||
envCython.Program('simple_kalman_impl.so', 'simple_kalman_impl.pyx')
|
||||
@@ -0,0 +1,3 @@
|
||||
# pylint: skip-file
|
||||
from common.kalman.simple_kalman_impl import KF1D as KF1D
|
||||
assert KF1D
|
||||
@@ -0,0 +1,18 @@
|
||||
# cython: language_level = 3
|
||||
|
||||
cdef class KF1D:
|
||||
cdef public:
|
||||
double x0_0
|
||||
double x1_0
|
||||
double K0_0
|
||||
double K1_0
|
||||
double A0_0
|
||||
double A0_1
|
||||
double A1_0
|
||||
double A1_1
|
||||
double C0_0
|
||||
double C0_1
|
||||
double A_K_0
|
||||
double A_K_1
|
||||
double A_K_2
|
||||
double A_K_3
|
||||
@@ -0,0 +1,37 @@
|
||||
# distutils: language = c++
|
||||
# cython: language_level=3
|
||||
|
||||
cdef class KF1D:
|
||||
def __init__(self, x0, A, C, K):
|
||||
self.x0_0 = x0[0][0]
|
||||
self.x1_0 = x0[1][0]
|
||||
self.A0_0 = A[0][0]
|
||||
self.A0_1 = A[0][1]
|
||||
self.A1_0 = A[1][0]
|
||||
self.A1_1 = A[1][1]
|
||||
self.C0_0 = C[0]
|
||||
self.C0_1 = C[1]
|
||||
self.K0_0 = K[0][0]
|
||||
self.K1_0 = K[1][0]
|
||||
|
||||
self.A_K_0 = self.A0_0 - self.K0_0 * self.C0_0
|
||||
self.A_K_1 = self.A0_1 - self.K0_0 * self.C0_1
|
||||
self.A_K_2 = self.A1_0 - self.K1_0 * self.C0_0
|
||||
self.A_K_3 = self.A1_1 - self.K1_0 * self.C0_1
|
||||
|
||||
def update(self, meas):
|
||||
cdef double x0_0 = self.A_K_0 * self.x0_0 + self.A_K_1 * self.x1_0 + self.K0_0 * meas
|
||||
cdef double x1_0 = self.A_K_2 * self.x0_0 + self.A_K_3 * self.x1_0 + self.K1_0 * meas
|
||||
self.x0_0 = x0_0
|
||||
self.x1_0 = x1_0
|
||||
|
||||
return [self.x0_0, self.x1_0]
|
||||
|
||||
@property
|
||||
def x(self):
|
||||
return [[self.x0_0], [self.x1_0]]
|
||||
|
||||
@x.setter
|
||||
def x(self, x):
|
||||
self.x0_0 = x[0][0]
|
||||
self.x1_0 = x[1][0]
|
||||
@@ -0,0 +1,23 @@
|
||||
import numpy as np
|
||||
|
||||
|
||||
class KF1D:
|
||||
# this EKF assumes constant covariance matrix, so calculations are much simpler
|
||||
# the Kalman gain also needs to be precomputed using the control module
|
||||
|
||||
def __init__(self, x0, A, C, K):
|
||||
self.x = x0
|
||||
self.A = A
|
||||
self.C = np.atleast_2d(C)
|
||||
self.K = K
|
||||
|
||||
self.A_K = self.A - np.dot(self.K, self.C)
|
||||
|
||||
# K matrix needs to be pre-computed as follow:
|
||||
# import control
|
||||
# (x, l, K) = control.dare(np.transpose(self.A), np.transpose(self.C), Q, R)
|
||||
# self.K = np.transpose(K)
|
||||
|
||||
def update(self, meas):
|
||||
self.x = np.dot(self.A_K, self.x) + np.dot(self.K, meas)
|
||||
return self.x
|
||||
@@ -0,0 +1,87 @@
|
||||
import unittest
|
||||
import random
|
||||
import timeit
|
||||
import numpy as np
|
||||
|
||||
from common.kalman.simple_kalman import KF1D
|
||||
from common.kalman.simple_kalman_old import KF1D as KF1D_old
|
||||
|
||||
|
||||
class TestSimpleKalman(unittest.TestCase):
|
||||
def setUp(self):
|
||||
dt = 0.01
|
||||
x0_0 = 0.0
|
||||
x1_0 = 0.0
|
||||
A0_0 = 1.0
|
||||
A0_1 = dt
|
||||
A1_0 = 0.0
|
||||
A1_1 = 1.0
|
||||
C0_0 = 1.0
|
||||
C0_1 = 0.0
|
||||
K0_0 = 0.12287673
|
||||
K1_0 = 0.29666309
|
||||
|
||||
self.kf_old = KF1D_old(x0=np.array([[x0_0], [x1_0]]),
|
||||
A=np.array([[A0_0, A0_1], [A1_0, A1_1]]),
|
||||
C=np.array([C0_0, C0_1]),
|
||||
K=np.array([[K0_0], [K1_0]]))
|
||||
|
||||
self.kf = KF1D(x0=[[x0_0], [x1_0]],
|
||||
A=[[A0_0, A0_1], [A1_0, A1_1]],
|
||||
C=[C0_0, C0_1],
|
||||
K=[[K0_0], [K1_0]])
|
||||
|
||||
def test_getter_setter(self):
|
||||
self.kf.x = [[1.0], [1.0]]
|
||||
self.assertEqual(self.kf.x, [[1.0], [1.0]])
|
||||
|
||||
def update_returns_state(self):
|
||||
x = self.kf.update(100)
|
||||
self.assertEqual(x, self.kf.x)
|
||||
|
||||
def test_old_equal_new(self):
|
||||
for _ in range(1000):
|
||||
v_wheel = random.uniform(0, 200)
|
||||
|
||||
x_old = self.kf_old.update(v_wheel)
|
||||
x = self.kf.update(v_wheel)
|
||||
|
||||
# Compare the output x, verify that the error is less than 1e-4
|
||||
np.testing.assert_almost_equal(x_old[0], x[0])
|
||||
np.testing.assert_almost_equal(x_old[1], x[1])
|
||||
|
||||
def test_new_is_faster(self):
|
||||
setup = """
|
||||
import numpy as np
|
||||
|
||||
from common.kalman.simple_kalman import KF1D
|
||||
from common.kalman.simple_kalman_old import KF1D as KF1D_old
|
||||
|
||||
dt = 0.01
|
||||
x0_0 = 0.0
|
||||
x1_0 = 0.0
|
||||
A0_0 = 1.0
|
||||
A0_1 = dt
|
||||
A1_0 = 0.0
|
||||
A1_1 = 1.0
|
||||
C0_0 = 1.0
|
||||
C0_1 = 0.0
|
||||
K0_0 = 0.12287673
|
||||
K1_0 = 0.29666309
|
||||
|
||||
kf_old = KF1D_old(x0=np.array([[x0_0], [x1_0]]),
|
||||
A=np.array([[A0_0, A0_1], [A1_0, A1_1]]),
|
||||
C=np.array([C0_0, C0_1]),
|
||||
K=np.array([[K0_0], [K1_0]]))
|
||||
|
||||
kf = KF1D(x0=[[x0_0], [x1_0]],
|
||||
A=[[A0_0, A0_1], [A1_0, A1_1]],
|
||||
C=[C0_0, C0_1],
|
||||
K=[[K0_0], [K1_0]])
|
||||
"""
|
||||
kf_speed = timeit.timeit("kf.update(1234)", setup=setup, number=10000)
|
||||
kf_old_speed = timeit.timeit("kf_old.update(1234)", setup=setup, number=10000)
|
||||
self.assertTrue(kf_speed < kf_old_speed / 4)
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
@@ -8,7 +8,6 @@ import uuid
|
||||
import socket
|
||||
import logging
|
||||
import traceback
|
||||
import numpy as np
|
||||
from threading import local
|
||||
from collections import OrderedDict
|
||||
from contextlib import contextmanager
|
||||
@@ -16,8 +15,6 @@ from contextlib import contextmanager
|
||||
LOG_TIMESTAMPS = "LOG_TIMESTAMPS" in os.environ
|
||||
|
||||
def json_handler(obj):
|
||||
if isinstance(obj, np.bool_):
|
||||
return bool(obj)
|
||||
# if isinstance(obj, (datetime.date, datetime.time)):
|
||||
# return obj.isoformat()
|
||||
return repr(obj)
|
||||
@@ -156,9 +153,9 @@ class SwagLogger(logging.Logger):
|
||||
def bind_global(self, **kwargs):
|
||||
self.global_ctx.update(kwargs)
|
||||
|
||||
def event(self, event, *args, **kwargs):
|
||||
def event(self, event_name, *args, **kwargs):
|
||||
evt = NiceOrderedDict()
|
||||
evt['event'] = event
|
||||
evt['event'] = event_name
|
||||
if args:
|
||||
evt['args'] = args
|
||||
evt.update(kwargs)
|
||||
@@ -199,7 +196,8 @@ class SwagLogger(logging.Logger):
|
||||
co = f.f_code
|
||||
filename = os.path.normcase(co.co_filename)
|
||||
|
||||
if filename == _srcfile:
|
||||
# TODO: is this pylint exception correct?
|
||||
if filename == _srcfile: # pylint: disable=comparison-with-callable
|
||||
f = f.f_back
|
||||
continue
|
||||
sinfo = None
|
||||
@@ -0,0 +1,85 @@
|
||||
#pragma once
|
||||
|
||||
typedef struct vec3 {
|
||||
float v[3];
|
||||
} vec3;
|
||||
|
||||
typedef struct vec4 {
|
||||
float v[4];
|
||||
} vec4;
|
||||
|
||||
typedef struct mat3 {
|
||||
float v[3*3];
|
||||
} mat3;
|
||||
|
||||
typedef struct mat4 {
|
||||
float v[4*4];
|
||||
} mat4;
|
||||
|
||||
static inline mat3 matmul3(const mat3 &a, const mat3 &b) {
|
||||
mat3 ret = {{0.0}};
|
||||
for (int r=0; r<3; r++) {
|
||||
for (int c=0; c<3; c++) {
|
||||
float v = 0.0;
|
||||
for (int k=0; k<3; k++) {
|
||||
v += a.v[r*3+k] * b.v[k*3+c];
|
||||
}
|
||||
ret.v[r*3+c] = v;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline vec3 matvecmul3(const mat3 &a, const vec3 &b) {
|
||||
vec3 ret = {{0.0}};
|
||||
for (int r=0; r<3; r++) {
|
||||
for (int c=0; c<3; c++) {
|
||||
ret.v[r] += a.v[r*3+c] * b.v[c];
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline mat4 matmul(const mat4 &a, const mat4 &b) {
|
||||
mat4 ret = {{0.0}};
|
||||
for (int r=0; r<4; r++) {
|
||||
for (int c=0; c<4; c++) {
|
||||
float v = 0.0;
|
||||
for (int k=0; k<4; k++) {
|
||||
v += a.v[r*4+k] * b.v[k*4+c];
|
||||
}
|
||||
ret.v[r*4+c] = v;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline vec4 matvecmul(const mat4 &a, const vec4 &b) {
|
||||
vec4 ret = {{0.0}};
|
||||
for (int r=0; r<4; r++) {
|
||||
for (int c=0; c<4; c++) {
|
||||
ret.v[r] += a.v[r*4+c] * b.v[c];
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
// scales the input and output space of a transformation matrix
|
||||
// that assumes pixel-center origin.
|
||||
static inline mat3 transform_scale_buffer(const mat3 &in, float s) {
|
||||
// in_pt = ( transform(out_pt/s + 0.5) - 0.5) * s
|
||||
|
||||
mat3 transform_out = {{
|
||||
1.0f/s, 0.0f, 0.5f,
|
||||
0.0f, 1.0f/s, 0.5f,
|
||||
0.0f, 0.0f, 1.0f,
|
||||
}};
|
||||
|
||||
mat3 transform_in = {{
|
||||
s, 0.0f, -0.5f*s,
|
||||
0.0f, s, -0.5f*s,
|
||||
0.0f, 0.0f, 1.0f,
|
||||
}};
|
||||
|
||||
return matmul3(transform_in, matmul3(in, transform_out));
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user