mirror of
https://github.com/dragonpilot/dragonpilot.git
synced 2026-06-25 16:02:14 +08:00
dragonpilot beta3
date: 2023-12-23T21:19:29 commit: 38612b14f1a8aa49d1c6ef61bd67f5a095abb3f9
This commit is contained in:
@@ -46,7 +46,6 @@ selfdrive/boardd/boardd
|
||||
selfdrive/logcatd/logcatd
|
||||
selfdrive/mapd/default_speeds_by_region.json
|
||||
system/proclogd/proclogd
|
||||
selfdrive/ui/_ui
|
||||
selfdrive/ui/translations/alerts_generated.h
|
||||
selfdrive/ui/translations/tmp
|
||||
selfdrive/test/longitudinal_maneuvers/out
|
||||
|
||||
+22
-1
@@ -1,4 +1,25 @@
|
||||
dragonpilot beta3 2023.11.20
|
||||
dragonpilot beta3 2023.12.23
|
||||
=======================
|
||||
* Comma 0.9.6 release
|
||||
* New driving model (Blue Diamond).
|
||||
* Vision model trained on more data
|
||||
* Improved driving performance
|
||||
* AGNOS 9
|
||||
* comma body streaming and controls over WebRTC
|
||||
* Hyundai Staria 2023 support thanks to sunnyhaibin!
|
||||
* Kia Niro Plug-in Hybrid 2022 support thanks to sunnyhaibin!
|
||||
* Toyota RAV4 2023 support
|
||||
* Toyota RAV4 Hybrid 2023 support
|
||||
* DP HIGHLIGHT:
|
||||
* Flight Panel (compass, height, pitch).
|
||||
* Model Confidence indicator (in the max speed box).
|
||||
* New version of dynamic e2e controller with better detection logic.
|
||||
* Adjustable lane change speed (default 20mph, OFF = no control during lane change)
|
||||
* TSS2 long, Dynamic Follow and Accel profile tune has been updated.
|
||||
* Re-added: Full Screen Nav, Lane Priority Mode and Vision Turn Controller.
|
||||
* Remove Frogai Agressive take off and re-add improved krkeegen sng boot.
|
||||
|
||||
dragonpilot beta3 [2023.11.20]
|
||||
=======================
|
||||
* Comma 0.9.5 release
|
||||
* New driving model (Farm Ville)
|
||||
|
||||
@@ -1,18 +1,4 @@
|
||||

|
||||
|
||||
Table of Contents
|
||||
=======================
|
||||
|
||||
* [What is openpilot?](#what-is-openpilot)
|
||||
* [Running in a car](#running-on-a-dedicated-device-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)
|
||||
|
||||
---
|
||||
[](https://comma.ai/shop/comma-3x)
|
||||
|
||||
What is openpilot?
|
||||
------
|
||||
@@ -21,35 +7,22 @@ What is openpilot?
|
||||
|
||||
<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>
|
||||
<td><a href="https://youtu.be/NmBfgOanCyk" title="Video By Greer Viau"><img src="https://github.com/commaai/openpilot/assets/8762862/2f7112ae-f748-4f39-b617-fabd689c3772"></a></td>
|
||||
<td><a href="https://youtu.be/VHKyqZ7t8Gw" title="Video By Logan LeGrand"><img src="https://github.com/commaai/openpilot/assets/8762862/92351544-2833-40d7-9e0b-7ef7ae37ec4c"></a></td>
|
||||
<td><a href="https://youtu.be/SUIZYzxtMQs" title="A drive to Taco Bell"><img src="https://github.com/commaai/openpilot/assets/8762862/05ceefc5-2628-439c-a9b2-89ce77dc6f63"></a></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
Running on a dedicated device in a car
|
||||
------
|
||||
|
||||
To use openpilot in a car, you need four things
|
||||
1. **Supported Device:** A comma 3/3X. You can purchase these devices from (https://comma.ai/shop/comma-3x)
|
||||
|
||||
2. **Software:** The setup procedure for the comma 3/3X allows users to enter a URL for custom software.
|
||||
To install the release version of openpilot, use the URL `openpilot.comma.ai`.
|
||||
To install openpilot master (for more advanced users), use the URL `installer.comma.ai/commaai/master`. You can replace "commaai" with another GitHub username to install a fork.
|
||||
|
||||
3. **Supported Car:** Ensure that you have one of [the 250+ supported cars](docs/CARS.md). openpilot supports a wide range of car makes including Honda, Toyota, Hyundai, Nissan, Kia, Chrysler, Lexus, Acura, Audi, VW, Ford, and many more.
|
||||
If your car is not officially listed as supported but has adaptive cruise control and lane-keeping assist, it's likely capable of running openpilot.
|
||||
|
||||
To use openpilot in a car, you need four things:
|
||||
1. **Supported Device:** a comma 3/3X, available at [comma.ai/shop](https://comma.ai/shop/comma-3x).
|
||||
2. **Software:** The setup procedure for the comma 3/3X allows users to enter a URL for custom software. Use the URL `openpilot.comma.ai` to install the release version.
|
||||
3. **Supported Car:** Ensure that you have one of [the 250+ supported cars](docs/CARS.md).
|
||||
4. **Car Harness:** You will also need a [car harness](https://comma.ai/shop/car-harness) to connect your comma 3/3X to your car.
|
||||
We have detailed instructions for [how to install the harness and device in a car](https://comma.ai/setup).
|
||||
|
||||
We have detailed instructions for [how to install the harness and device in a car](https://comma.ai/setup).
|
||||
|
||||
Running on PC
|
||||
------
|
||||
@@ -58,22 +31,32 @@ All openpilot services can run as usual on a PC without requiring special hardwa
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
You can also run openpilot in simulation [with the MetaDrive simulator](tools/sim/README.md). This allows openpilot to drive around a virtual car on your Ubuntu machine.
|
||||
|
||||
A PC running openpilot can also control your vehicle if it is connected to a [webcam](https://github.com/commaai/openpilot/tree/master/tools/webcam), a [black panda](https://comma.ai/shop/products/panda), and a [harness](https://comma.ai/shop/products/car-harness).
|
||||
|
||||
Community and Contributing
|
||||
------
|
||||
|
||||
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).
|
||||
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).
|
||||
|
||||
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).
|
||||
* Join the [community Discord](https://discord.comma.ai)
|
||||
* Check out [the contributing docs](docs/CONTRIBUTING.md)
|
||||
* Code documentation lives at https://docs.comma.ai
|
||||
* Information about running openpilot lives on the [community 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#open-positions) and offers lots of [bounties](docs/BOUNTIES.md).
|
||||
|
||||
Want to get paid to work on openpilot? [comma is hiring](https://comma.ai/jobs#open-positions).
|
||||
Safety and Testing
|
||||
----
|
||||
|
||||
And [follow us on Twitter](https://twitter.com/comma_ai).
|
||||
* openpilot observes [ISO26262](https://en.wikipedia.org/wiki/ISO_26262) 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.
|
||||
|
||||
User Data and comma Account
|
||||
------
|
||||
@@ -87,49 +70,6 @@ The driver-facing camera is only logged if you explicitly opt-in in settings. Th
|
||||
|
||||
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
|
||||
└── system # Generic services
|
||||
├── camerad # Driver to capture images from the camera sensors
|
||||
├── hardware # Hardware abstraction classes
|
||||
├── logcatd # systemd journal as a service
|
||||
├── loggerd # Logger and uploader of car data
|
||||
├── proclogd # Logs information from /proc
|
||||
├── sensord # IMU interface code
|
||||
└── ubloxd # u-blox GNSS module interface code
|
||||
└── 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
|
||||
├── car # Car specific code to read states and control actuators
|
||||
├── controls # Planning and controls
|
||||
├── debug # Tools to help you debug and do car ports
|
||||
├── locationd # Precise localization and vehicle parameter estimation
|
||||
├── manager # Daemon that starts/stops all other daemons as needed
|
||||
├── modeld # Driving and monitoring model runners
|
||||
├── monitoring # Daemon to determine driver attention
|
||||
├── navd # Turn-by-turn navigation
|
||||
├── test # Unit tests, system tests, and a car simulator
|
||||
└── ui # The UI
|
||||
|
||||
Licensing
|
||||
------
|
||||
|
||||
@@ -145,5 +85,5 @@ 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://codecov.io/gh/commaai/openpilot)
|
||||
|
||||
+16
-3
@@ -1,4 +1,16 @@
|
||||
Version 0.9.5 (2023-XX-XX)
|
||||
Version 0.9.6 (20XX-XX-XX)
|
||||
========================
|
||||
* New driving model
|
||||
* Vision model trained on more data
|
||||
* Improved driving performance
|
||||
* AGNOS 9
|
||||
* comma body streaming and controls over WebRTC
|
||||
* Hyundai Staria 2023 support thanks to sunnyhaibin!
|
||||
* Kia Niro Plug-in Hybrid 2022 support thanks to sunnyhaibin!
|
||||
* Toyota RAV4 2023 support
|
||||
* Toyota RAV4 Hybrid 2023 support
|
||||
|
||||
Version 0.9.5 (2023-11-17)
|
||||
========================
|
||||
* New driving model
|
||||
* Improved navigate on openpilot performance using navigation instructions as an additional model input
|
||||
@@ -8,11 +20,12 @@ Version 0.9.5 (2023-XX-XX)
|
||||
* 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, alamo3, and sshane!
|
||||
* 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 Sorento Hybrid 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)
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1 +1 @@
|
||||
const uint8_t gitversion[8] = "bcce255c";
|
||||
const uint8_t gitversion[8] = "4ba36d72";
|
||||
|
||||
@@ -484,6 +484,7 @@ struct CarParams {
|
||||
openpilotLongitudinalControl @37 :Bool; # is openpilot doing the longitudinal control?
|
||||
carVin @38 :Text; # VIN number queried during fingerprinting
|
||||
dashcamOnly @41: Bool;
|
||||
passive @73: Bool; # is openpilot in control?
|
||||
transmissionType @43 :TransmissionType;
|
||||
carFw @44 :List(CarFw);
|
||||
|
||||
|
||||
+5
-27
@@ -32,24 +32,11 @@ struct LiveMapData @0x81c2f05a394cf4af {
|
||||
}
|
||||
|
||||
struct LongitudinalPlanExt @0xaedffd8f31e7b55d {
|
||||
visionTurnControllerState @0 :VisionTurnControllerState;
|
||||
visionTurnSpeed @1 :Float32;
|
||||
speedLimitControlState @2 :SpeedLimitControlState;
|
||||
speedLimit @3 :Float32;
|
||||
speedLimitOffset @4 :Float32;
|
||||
distToSpeedLimit @5 :Float32;
|
||||
isMapSpeedLimit @6 :Bool;
|
||||
speedLimitPercOffset @7 :Bool;
|
||||
speedLimitValueOffset @8 :Float32;
|
||||
|
||||
distToTurn @9 :Float32;
|
||||
turnSpeed @10 :Float32;
|
||||
turnSpeedControlState @11 :SpeedLimitControlState;
|
||||
turnSign @12 :Int16;
|
||||
|
||||
dpE2EIsBlended @13 :Bool;
|
||||
longitudinalPlanExtSource @14 :LongitudinalPlanExtSource;
|
||||
de2eIsEnabled @15 :Bool;
|
||||
dpE2EIsBlended @0 :Bool;
|
||||
de2eIsEnabled @1 :Bool;
|
||||
visionTurnControllerState @2 :VisionTurnControllerState;
|
||||
visionTurnSpeed @3 :Float32;
|
||||
longitudinalPlanExtSource @4 :LongitudinalPlanExtSource;
|
||||
|
||||
enum LongitudinalPlanExtSource {
|
||||
cruise @0;
|
||||
@@ -58,15 +45,6 @@ struct LongitudinalPlanExt @0xaedffd8f31e7b55d {
|
||||
lead2 @3;
|
||||
e2e @4;
|
||||
turn @5;
|
||||
limit @6;
|
||||
turnlimit @7;
|
||||
}
|
||||
|
||||
enum SpeedLimitControlState {
|
||||
inactive @0; # No speed limit set or not enabled by parameter.
|
||||
tempInactive @1; # User wants to ignore speed limit until it changes.
|
||||
adapting @2; # Reducing speed to match new speed limit.
|
||||
active @3; # Cruising at speed limit.
|
||||
}
|
||||
|
||||
enum VisionTurnControllerState {
|
||||
|
||||
+330
-315
@@ -3502,7 +3502,7 @@ const ::capnp::_::RawSchema s_f5a5e26c954e339e = {
|
||||
};
|
||||
#endif // !CAPNP_LITE
|
||||
CAPNP_DEFINE_ENUM(AudibleAlert_f5a5e26c954e339e, f5a5e26c954e339e);
|
||||
static const ::capnp::_::AlignedData<1269> b_8c69372490aaa9da = {
|
||||
static const ::capnp::_::AlignedData<1284> b_8c69372490aaa9da = {
|
||||
{ 0, 0, 0, 0, 5, 0, 6, 0,
|
||||
218, 169, 170, 144, 36, 55, 105, 140,
|
||||
10, 0, 0, 0, 1, 0, 17, 0,
|
||||
@@ -3512,7 +3512,7 @@ static const ::capnp::_::AlignedData<1269> b_8c69372490aaa9da = {
|
||||
21, 0, 0, 0, 162, 0, 0, 0,
|
||||
29, 0, 0, 0, 231, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
17, 1, 0, 0, 87, 15, 0, 0,
|
||||
17, 1, 0, 0, 143, 15, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
99, 97, 114, 46, 99, 97, 112, 110,
|
||||
@@ -3581,497 +3581,504 @@ static const ::capnp::_::AlignedData<1269> b_8c69372490aaa9da = {
|
||||
101, 0, 0, 0, 0, 0, 0, 0,
|
||||
78, 101, 116, 119, 111, 114, 107, 76,
|
||||
111, 99, 97, 116, 105, 111, 110, 0,
|
||||
24, 1, 0, 0, 3, 0, 4, 0,
|
||||
28, 1, 0, 0, 3, 0, 4, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 1, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
153, 7, 0, 0, 66, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
148, 7, 0, 0, 3, 0, 1, 0,
|
||||
160, 7, 0, 0, 2, 0, 1, 0,
|
||||
1, 0, 0, 0, 1, 0, 0, 0,
|
||||
0, 0, 1, 0, 1, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
157, 7, 0, 0, 122, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
156, 7, 0, 0, 3, 0, 1, 0,
|
||||
168, 7, 0, 0, 2, 0, 1, 0,
|
||||
4, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 1, 0, 2, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
165, 7, 0, 0, 170, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
168, 7, 0, 0, 3, 0, 1, 0,
|
||||
180, 7, 0, 0, 2, 0, 1, 0,
|
||||
5, 0, 0, 0, 1, 0, 0, 0,
|
||||
0, 0, 1, 0, 3, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
177, 7, 0, 0, 82, 0, 0, 0,
|
||||
181, 7, 0, 0, 66, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
176, 7, 0, 0, 3, 0, 1, 0,
|
||||
188, 7, 0, 0, 2, 0, 1, 0,
|
||||
51, 0, 0, 0, 2, 0, 0, 0,
|
||||
0, 0, 1, 0, 4, 0, 0, 0,
|
||||
1, 0, 0, 0, 1, 0, 0, 0,
|
||||
0, 0, 1, 0, 1, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
185, 7, 0, 0, 186, 0, 0, 0,
|
||||
185, 7, 0, 0, 122, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
188, 7, 0, 0, 3, 0, 1, 0,
|
||||
200, 7, 0, 0, 2, 0, 1, 0,
|
||||
6, 0, 0, 0, 3, 0, 0, 0,
|
||||
0, 0, 1, 0, 5, 0, 0, 0,
|
||||
184, 7, 0, 0, 3, 0, 1, 0,
|
||||
196, 7, 0, 0, 2, 0, 1, 0,
|
||||
4, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 1, 0, 2, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
197, 7, 0, 0, 82, 0, 0, 0,
|
||||
193, 7, 0, 0, 170, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
196, 7, 0, 0, 3, 0, 1, 0,
|
||||
208, 7, 0, 0, 2, 0, 1, 0,
|
||||
52, 0, 0, 0, 4, 0, 0, 0,
|
||||
0, 0, 1, 0, 6, 0, 0, 0,
|
||||
5, 0, 0, 0, 1, 0, 0, 0,
|
||||
0, 0, 1, 0, 3, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
205, 7, 0, 0, 170, 0, 0, 0,
|
||||
205, 7, 0, 0, 82, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
208, 7, 0, 0, 3, 0, 1, 0,
|
||||
220, 7, 0, 0, 2, 0, 1, 0,
|
||||
10, 0, 0, 0, 1, 0, 0, 0,
|
||||
0, 0, 1, 0, 7, 0, 0, 0,
|
||||
204, 7, 0, 0, 3, 0, 1, 0,
|
||||
216, 7, 0, 0, 2, 0, 1, 0,
|
||||
52, 0, 0, 0, 2, 0, 0, 0,
|
||||
0, 0, 1, 0, 4, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
217, 7, 0, 0, 122, 0, 0, 0,
|
||||
213, 7, 0, 0, 186, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
216, 7, 0, 0, 3, 0, 1, 0,
|
||||
228, 7, 0, 0, 2, 0, 1, 0,
|
||||
11, 0, 0, 0, 2, 0, 0, 0,
|
||||
0, 0, 1, 0, 8, 0, 0, 0,
|
||||
6, 0, 0, 0, 3, 0, 0, 0,
|
||||
0, 0, 1, 0, 5, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
225, 7, 0, 0, 114, 0, 0, 0,
|
||||
225, 7, 0, 0, 82, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
224, 7, 0, 0, 3, 0, 1, 0,
|
||||
236, 7, 0, 0, 2, 0, 1, 0,
|
||||
57, 0, 0, 0, 1, 0, 0, 0,
|
||||
0, 0, 1, 0, 9, 0, 0, 0,
|
||||
53, 0, 0, 0, 4, 0, 0, 0,
|
||||
0, 0, 1, 0, 6, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
233, 7, 0, 0, 178, 0, 0, 0,
|
||||
233, 7, 0, 0, 170, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
236, 7, 0, 0, 3, 0, 1, 0,
|
||||
248, 7, 0, 0, 2, 0, 1, 0,
|
||||
56, 0, 0, 0, 6, 0, 0, 0,
|
||||
10, 0, 0, 0, 1, 0, 0, 0,
|
||||
0, 0, 1, 0, 7, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
245, 7, 0, 0, 122, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
244, 7, 0, 0, 3, 0, 1, 0,
|
||||
0, 8, 0, 0, 2, 0, 1, 0,
|
||||
11, 0, 0, 0, 2, 0, 0, 0,
|
||||
0, 0, 1, 0, 8, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
253, 7, 0, 0, 114, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
252, 7, 0, 0, 3, 0, 1, 0,
|
||||
8, 8, 0, 0, 2, 0, 1, 0,
|
||||
58, 0, 0, 0, 1, 0, 0, 0,
|
||||
0, 0, 1, 0, 9, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
5, 8, 0, 0, 178, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
8, 8, 0, 0, 3, 0, 1, 0,
|
||||
20, 8, 0, 0, 2, 0, 1, 0,
|
||||
57, 0, 0, 0, 6, 0, 0, 0,
|
||||
0, 0, 1, 0, 10, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
245, 7, 0, 0, 178, 0, 0, 0,
|
||||
17, 8, 0, 0, 178, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
248, 7, 0, 0, 3, 0, 1, 0,
|
||||
4, 8, 0, 0, 2, 0, 1, 0,
|
||||
62, 0, 0, 0, 2, 0, 0, 0,
|
||||
20, 8, 0, 0, 3, 0, 1, 0,
|
||||
32, 8, 0, 0, 2, 0, 1, 0,
|
||||
63, 0, 0, 0, 2, 0, 0, 0,
|
||||
0, 0, 1, 0, 11, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 8, 0, 0, 170, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
4, 8, 0, 0, 3, 0, 1, 0,
|
||||
32, 8, 0, 0, 2, 0, 1, 0,
|
||||
63, 0, 0, 0, 3, 0, 0, 0,
|
||||
0, 0, 1, 0, 12, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
29, 8, 0, 0, 162, 0, 0, 0,
|
||||
29, 8, 0, 0, 170, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
32, 8, 0, 0, 3, 0, 1, 0,
|
||||
60, 8, 0, 0, 2, 0, 1, 0,
|
||||
64, 0, 0, 0, 4, 0, 0, 0,
|
||||
0, 0, 1, 0, 13, 0, 0, 0,
|
||||
64, 0, 0, 0, 3, 0, 0, 0,
|
||||
0, 0, 1, 0, 12, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
57, 8, 0, 0, 154, 0, 0, 0,
|
||||
57, 8, 0, 0, 162, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
60, 8, 0, 0, 3, 0, 1, 0,
|
||||
88, 8, 0, 0, 2, 0, 1, 0,
|
||||
65, 0, 0, 0, 5, 0, 0, 0,
|
||||
0, 0, 1, 0, 14, 0, 0, 0,
|
||||
65, 0, 0, 0, 4, 0, 0, 0,
|
||||
0, 0, 1, 0, 13, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
85, 8, 0, 0, 146, 0, 0, 0,
|
||||
85, 8, 0, 0, 154, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
88, 8, 0, 0, 3, 0, 1, 0,
|
||||
116, 8, 0, 0, 2, 0, 1, 0,
|
||||
66, 0, 0, 0, 6, 0, 0, 0,
|
||||
0, 0, 1, 0, 15, 0, 0, 0,
|
||||
66, 0, 0, 0, 5, 0, 0, 0,
|
||||
0, 0, 1, 0, 14, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
113, 8, 0, 0, 170, 0, 0, 0,
|
||||
113, 8, 0, 0, 146, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
116, 8, 0, 0, 3, 0, 1, 0,
|
||||
144, 8, 0, 0, 2, 0, 1, 0,
|
||||
67, 0, 0, 0, 7, 0, 0, 0,
|
||||
0, 0, 1, 0, 16, 0, 0, 0,
|
||||
67, 0, 0, 0, 6, 0, 0, 0,
|
||||
0, 0, 1, 0, 15, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
141, 8, 0, 0, 162, 0, 0, 0,
|
||||
141, 8, 0, 0, 170, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
144, 8, 0, 0, 3, 0, 1, 0,
|
||||
172, 8, 0, 0, 2, 0, 1, 0,
|
||||
68, 0, 0, 0, 7, 0, 0, 0,
|
||||
0, 0, 1, 0, 16, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
169, 8, 0, 0, 162, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
172, 8, 0, 0, 3, 0, 1, 0,
|
||||
200, 8, 0, 0, 2, 0, 1, 0,
|
||||
16, 0, 0, 0, 4, 0, 0, 0,
|
||||
0, 0, 1, 0, 17, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
169, 8, 0, 0, 42, 0, 0, 0,
|
||||
197, 8, 0, 0, 42, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
164, 8, 0, 0, 3, 0, 1, 0,
|
||||
176, 8, 0, 0, 2, 0, 1, 0,
|
||||
192, 8, 0, 0, 3, 0, 1, 0,
|
||||
204, 8, 0, 0, 2, 0, 1, 0,
|
||||
17, 0, 0, 0, 5, 0, 0, 0,
|
||||
0, 0, 1, 0, 18, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
173, 8, 0, 0, 82, 0, 0, 0,
|
||||
201, 8, 0, 0, 82, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
172, 8, 0, 0, 3, 0, 1, 0,
|
||||
184, 8, 0, 0, 2, 0, 1, 0,
|
||||
200, 8, 0, 0, 3, 0, 1, 0,
|
||||
212, 8, 0, 0, 2, 0, 1, 0,
|
||||
18, 0, 0, 0, 6, 0, 0, 0,
|
||||
0, 0, 1, 0, 19, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
181, 8, 0, 0, 114, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
180, 8, 0, 0, 3, 0, 1, 0,
|
||||
192, 8, 0, 0, 2, 0, 1, 0,
|
||||
19, 0, 0, 0, 7, 0, 0, 0,
|
||||
0, 0, 1, 0, 20, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
189, 8, 0, 0, 90, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
188, 8, 0, 0, 3, 0, 1, 0,
|
||||
200, 8, 0, 0, 2, 0, 1, 0,
|
||||
20, 0, 0, 0, 8, 0, 0, 0,
|
||||
0, 0, 1, 0, 21, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
197, 8, 0, 0, 122, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
196, 8, 0, 0, 3, 0, 1, 0,
|
||||
208, 8, 0, 0, 2, 0, 1, 0,
|
||||
21, 0, 0, 0, 9, 0, 0, 0,
|
||||
0, 0, 1, 0, 22, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
205, 8, 0, 0, 146, 0, 0, 0,
|
||||
209, 8, 0, 0, 114, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
208, 8, 0, 0, 3, 0, 1, 0,
|
||||
220, 8, 0, 0, 2, 0, 1, 0,
|
||||
19, 0, 0, 0, 7, 0, 0, 0,
|
||||
0, 0, 1, 0, 20, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
217, 8, 0, 0, 90, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
216, 8, 0, 0, 3, 0, 1, 0,
|
||||
228, 8, 0, 0, 2, 0, 1, 0,
|
||||
20, 0, 0, 0, 8, 0, 0, 0,
|
||||
0, 0, 1, 0, 21, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
225, 8, 0, 0, 122, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
224, 8, 0, 0, 3, 0, 1, 0,
|
||||
236, 8, 0, 0, 2, 0, 1, 0,
|
||||
21, 0, 0, 0, 9, 0, 0, 0,
|
||||
0, 0, 1, 0, 22, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
233, 8, 0, 0, 146, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
236, 8, 0, 0, 3, 0, 1, 0,
|
||||
248, 8, 0, 0, 2, 0, 1, 0,
|
||||
23, 0, 0, 0, 10, 0, 0, 0,
|
||||
0, 0, 1, 0, 23, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
217, 8, 0, 0, 154, 0, 0, 0,
|
||||
245, 8, 0, 0, 154, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
220, 8, 0, 0, 3, 0, 1, 0,
|
||||
232, 8, 0, 0, 2, 0, 1, 0,
|
||||
248, 8, 0, 0, 3, 0, 1, 0,
|
||||
4, 9, 0, 0, 2, 0, 1, 0,
|
||||
24, 0, 0, 0, 11, 0, 0, 0,
|
||||
0, 0, 1, 0, 24, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
229, 8, 0, 0, 146, 0, 0, 0,
|
||||
1, 9, 0, 0, 146, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
232, 8, 0, 0, 3, 0, 1, 0,
|
||||
244, 8, 0, 0, 2, 0, 1, 0,
|
||||
4, 9, 0, 0, 3, 0, 1, 0,
|
||||
16, 9, 0, 0, 2, 0, 1, 0,
|
||||
25, 0, 0, 0, 8, 0, 0, 0,
|
||||
0, 0, 1, 0, 25, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
241, 8, 0, 0, 154, 0, 0, 0,
|
||||
13, 9, 0, 0, 154, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
244, 8, 0, 0, 3, 0, 1, 0,
|
||||
0, 9, 0, 0, 2, 0, 1, 0,
|
||||
16, 9, 0, 0, 3, 0, 1, 0,
|
||||
28, 9, 0, 0, 2, 0, 1, 0,
|
||||
27, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 0, 0, 0, 0, 0, 0,
|
||||
104, 149, 51, 53, 10, 88, 252, 147,
|
||||
253, 8, 0, 0, 114, 0, 0, 0,
|
||||
25, 9, 0, 0, 114, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
28, 0, 0, 0, 5, 0, 0, 0,
|
||||
0, 0, 1, 0, 28, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
233, 8, 0, 0, 130, 0, 0, 0,
|
||||
5, 9, 0, 0, 130, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
232, 8, 0, 0, 3, 0, 1, 0,
|
||||
244, 8, 0, 0, 2, 0, 1, 0,
|
||||
4, 9, 0, 0, 3, 0, 1, 0,
|
||||
16, 9, 0, 0, 2, 0, 1, 0,
|
||||
30, 0, 0, 0, 12, 0, 0, 0,
|
||||
0, 0, 1, 0, 29, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
241, 8, 0, 0, 106, 0, 0, 0,
|
||||
13, 9, 0, 0, 106, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
240, 8, 0, 0, 3, 0, 1, 0,
|
||||
252, 8, 0, 0, 2, 0, 1, 0,
|
||||
68, 0, 0, 0, 6, 0, 0, 0,
|
||||
12, 9, 0, 0, 3, 0, 1, 0,
|
||||
24, 9, 0, 0, 2, 0, 1, 0,
|
||||
69, 0, 0, 0, 6, 0, 0, 0,
|
||||
0, 0, 1, 0, 30, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
249, 8, 0, 0, 234, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 9, 0, 0, 3, 0, 1, 0,
|
||||
12, 9, 0, 0, 2, 0, 1, 0,
|
||||
32, 0, 0, 0, 7, 0, 0, 0,
|
||||
0, 0, 1, 0, 31, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
9, 9, 0, 0, 130, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
8, 9, 0, 0, 3, 0, 1, 0,
|
||||
20, 9, 0, 0, 2, 0, 1, 0,
|
||||
37, 0, 0, 0, 13, 0, 0, 0,
|
||||
0, 0, 1, 0, 32, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
17, 9, 0, 0, 90, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
16, 9, 0, 0, 3, 0, 1, 0,
|
||||
28, 9, 0, 0, 2, 0, 1, 0,
|
||||
53, 0, 0, 0, 14, 0, 0, 0,
|
||||
0, 0, 1, 0, 33, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
25, 9, 0, 0, 194, 0, 0, 0,
|
||||
21, 9, 0, 0, 234, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
28, 9, 0, 0, 3, 0, 1, 0,
|
||||
40, 9, 0, 0, 2, 0, 1, 0,
|
||||
32, 0, 0, 0, 7, 0, 0, 0,
|
||||
0, 0, 1, 0, 31, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
37, 9, 0, 0, 130, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
36, 9, 0, 0, 3, 0, 1, 0,
|
||||
48, 9, 0, 0, 2, 0, 1, 0,
|
||||
37, 0, 0, 0, 13, 0, 0, 0,
|
||||
0, 0, 1, 0, 32, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
45, 9, 0, 0, 90, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
44, 9, 0, 0, 3, 0, 1, 0,
|
||||
56, 9, 0, 0, 2, 0, 1, 0,
|
||||
54, 0, 0, 0, 14, 0, 0, 0,
|
||||
0, 0, 1, 0, 33, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
53, 9, 0, 0, 194, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
56, 9, 0, 0, 3, 0, 1, 0,
|
||||
68, 9, 0, 0, 2, 0, 1, 0,
|
||||
33, 0, 0, 0, 30, 0, 0, 0,
|
||||
0, 0, 1, 0, 34, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
37, 9, 0, 0, 138, 0, 0, 0,
|
||||
65, 9, 0, 0, 138, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
40, 9, 0, 0, 3, 0, 1, 0,
|
||||
52, 9, 0, 0, 2, 0, 1, 0,
|
||||
68, 9, 0, 0, 3, 0, 1, 0,
|
||||
80, 9, 0, 0, 2, 0, 1, 0,
|
||||
34, 0, 0, 0, 8, 0, 0, 0,
|
||||
0, 0, 1, 0, 35, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
49, 9, 0, 0, 138, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
52, 9, 0, 0, 3, 0, 1, 0,
|
||||
64, 9, 0, 0, 2, 0, 1, 0,
|
||||
39, 0, 0, 0, 16, 0, 0, 0,
|
||||
0, 0, 1, 0, 36, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
61, 9, 0, 0, 154, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
64, 9, 0, 0, 3, 0, 1, 0,
|
||||
76, 9, 0, 0, 2, 0, 1, 0,
|
||||
42, 0, 0, 0, 9, 0, 0, 0,
|
||||
0, 0, 1, 0, 37, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
73, 9, 0, 0, 234, 0, 0, 0,
|
||||
77, 9, 0, 0, 138, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
80, 9, 0, 0, 3, 0, 1, 0,
|
||||
92, 9, 0, 0, 2, 0, 1, 0,
|
||||
39, 0, 0, 0, 16, 0, 0, 0,
|
||||
0, 0, 1, 0, 36, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
89, 9, 0, 0, 154, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
92, 9, 0, 0, 3, 0, 1, 0,
|
||||
104, 9, 0, 0, 2, 0, 1, 0,
|
||||
42, 0, 0, 0, 9, 0, 0, 0,
|
||||
0, 0, 1, 0, 37, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
101, 9, 0, 0, 234, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
108, 9, 0, 0, 3, 0, 1, 0,
|
||||
120, 9, 0, 0, 2, 0, 1, 0,
|
||||
43, 0, 0, 0, 10, 0, 0, 0,
|
||||
0, 0, 1, 0, 38, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
89, 9, 0, 0, 58, 0, 0, 0,
|
||||
117, 9, 0, 0, 58, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
84, 9, 0, 0, 3, 0, 1, 0,
|
||||
96, 9, 0, 0, 2, 0, 1, 0,
|
||||
54, 0, 0, 0, 10, 0, 0, 0,
|
||||
112, 9, 0, 0, 3, 0, 1, 0,
|
||||
124, 9, 0, 0, 2, 0, 1, 0,
|
||||
55, 0, 0, 0, 10, 0, 0, 0,
|
||||
0, 0, 1, 0, 39, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
93, 9, 0, 0, 186, 0, 0, 0,
|
||||
121, 9, 0, 0, 186, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
96, 9, 0, 0, 3, 0, 1, 0,
|
||||
108, 9, 0, 0, 2, 0, 1, 0,
|
||||
124, 9, 0, 0, 3, 0, 1, 0,
|
||||
136, 9, 0, 0, 2, 0, 1, 0,
|
||||
44, 0, 0, 0, 11, 0, 0, 0,
|
||||
0, 0, 1, 0, 41, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
105, 9, 0, 0, 98, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
104, 9, 0, 0, 3, 0, 1, 0,
|
||||
116, 9, 0, 0, 2, 0, 1, 0,
|
||||
58, 0, 0, 0, 31, 0, 0, 0,
|
||||
0, 0, 1, 0, 42, 0, 0, 0,
|
||||
1, 0, 0, 0, 0, 0, 0, 0,
|
||||
113, 9, 0, 0, 234, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
120, 9, 0, 0, 3, 0, 1, 0,
|
||||
132, 9, 0, 0, 2, 0, 1, 0,
|
||||
45, 0, 0, 0, 34, 0, 0, 0,
|
||||
0, 0, 1, 0, 43, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
129, 9, 0, 0, 138, 0, 0, 0,
|
||||
133, 9, 0, 0, 98, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
132, 9, 0, 0, 3, 0, 1, 0,
|
||||
144, 9, 0, 0, 2, 0, 1, 0,
|
||||
46, 0, 0, 0, 11, 0, 0, 0,
|
||||
0, 0, 1, 0, 44, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
141, 9, 0, 0, 50, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
136, 9, 0, 0, 3, 0, 1, 0,
|
||||
164, 9, 0, 0, 2, 0, 1, 0,
|
||||
47, 0, 0, 0, 18, 0, 0, 0,
|
||||
0, 0, 1, 0, 45, 0, 0, 0,
|
||||
59, 0, 0, 0, 31, 0, 0, 0,
|
||||
0, 0, 1, 0, 42, 0, 0, 0,
|
||||
1, 0, 0, 0, 0, 0, 0, 0,
|
||||
161, 9, 0, 0, 114, 0, 0, 0,
|
||||
141, 9, 0, 0, 234, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
148, 9, 0, 0, 3, 0, 1, 0,
|
||||
160, 9, 0, 0, 2, 0, 1, 0,
|
||||
46, 0, 0, 0, 34, 0, 0, 0,
|
||||
0, 0, 1, 0, 43, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
157, 9, 0, 0, 138, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
160, 9, 0, 0, 3, 0, 1, 0,
|
||||
172, 9, 0, 0, 2, 0, 1, 0,
|
||||
60, 0, 0, 0, 12, 0, 0, 0,
|
||||
47, 0, 0, 0, 11, 0, 0, 0,
|
||||
0, 0, 1, 0, 44, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
169, 9, 0, 0, 50, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
164, 9, 0, 0, 3, 0, 1, 0,
|
||||
192, 9, 0, 0, 2, 0, 1, 0,
|
||||
48, 0, 0, 0, 18, 0, 0, 0,
|
||||
0, 0, 1, 0, 45, 0, 0, 0,
|
||||
1, 0, 0, 0, 0, 0, 0, 0,
|
||||
189, 9, 0, 0, 114, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
188, 9, 0, 0, 3, 0, 1, 0,
|
||||
200, 9, 0, 0, 2, 0, 1, 0,
|
||||
61, 0, 0, 0, 12, 0, 0, 0,
|
||||
0, 0, 1, 0, 46, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
169, 9, 0, 0, 218, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
176, 9, 0, 0, 3, 0, 1, 0,
|
||||
188, 9, 0, 0, 2, 0, 1, 0,
|
||||
29, 0, 0, 0, 19, 0, 0, 0,
|
||||
0, 0, 1, 0, 47, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
185, 9, 0, 0, 130, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
184, 9, 0, 0, 3, 0, 1, 0,
|
||||
196, 9, 0, 0, 2, 0, 1, 0,
|
||||
26, 0, 0, 0, 12, 0, 0, 0,
|
||||
0, 0, 1, 0, 48, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
193, 9, 0, 0, 114, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
192, 9, 0, 0, 3, 0, 1, 0,
|
||||
204, 9, 0, 0, 2, 0, 1, 0,
|
||||
48, 0, 0, 0, 35, 0, 0, 0,
|
||||
0, 0, 1, 0, 49, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
201, 9, 0, 0, 146, 0, 0, 0,
|
||||
197, 9, 0, 0, 218, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
204, 9, 0, 0, 3, 0, 1, 0,
|
||||
216, 9, 0, 0, 2, 0, 1, 0,
|
||||
49, 0, 0, 0, 40, 0, 0, 0,
|
||||
0, 0, 1, 0, 50, 0, 0, 0,
|
||||
29, 0, 0, 0, 19, 0, 0, 0,
|
||||
0, 0, 1, 0, 47, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
213, 9, 0, 0, 130, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
212, 9, 0, 0, 3, 0, 1, 0,
|
||||
224, 9, 0, 0, 2, 0, 1, 0,
|
||||
59, 0, 0, 0, 21, 0, 0, 0,
|
||||
26, 0, 0, 0, 12, 0, 0, 0,
|
||||
0, 0, 1, 0, 48, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
221, 9, 0, 0, 114, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
220, 9, 0, 0, 3, 0, 1, 0,
|
||||
232, 9, 0, 0, 2, 0, 1, 0,
|
||||
49, 0, 0, 0, 35, 0, 0, 0,
|
||||
0, 0, 1, 0, 49, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
229, 9, 0, 0, 146, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
232, 9, 0, 0, 3, 0, 1, 0,
|
||||
244, 9, 0, 0, 2, 0, 1, 0,
|
||||
50, 0, 0, 0, 40, 0, 0, 0,
|
||||
0, 0, 1, 0, 50, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
241, 9, 0, 0, 130, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
240, 9, 0, 0, 3, 0, 1, 0,
|
||||
252, 9, 0, 0, 2, 0, 1, 0,
|
||||
60, 0, 0, 0, 21, 0, 0, 0,
|
||||
0, 0, 1, 0, 51, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
221, 9, 0, 0, 178, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
224, 9, 0, 0, 3, 0, 1, 0,
|
||||
236, 9, 0, 0, 2, 0, 1, 0,
|
||||
36, 0, 0, 0, 22, 0, 0, 0,
|
||||
0, 0, 1, 0, 52, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
233, 9, 0, 0, 146, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
236, 9, 0, 0, 3, 0, 1, 0,
|
||||
248, 9, 0, 0, 2, 0, 1, 0,
|
||||
61, 0, 0, 0, 23, 0, 0, 0,
|
||||
0, 0, 1, 0, 53, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
245, 9, 0, 0, 226, 0, 0, 0,
|
||||
249, 9, 0, 0, 178, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
252, 9, 0, 0, 3, 0, 1, 0,
|
||||
8, 10, 0, 0, 2, 0, 1, 0,
|
||||
69, 0, 0, 0, 24, 0, 0, 0,
|
||||
0, 0, 1, 0, 54, 0, 0, 0,
|
||||
36, 0, 0, 0, 22, 0, 0, 0,
|
||||
0, 0, 1, 0, 52, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
5, 10, 0, 0, 242, 0, 0, 0,
|
||||
5, 10, 0, 0, 146, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
12, 10, 0, 0, 3, 0, 1, 0,
|
||||
24, 10, 0, 0, 2, 0, 1, 0,
|
||||
2, 0, 0, 0, 13, 0, 0, 0,
|
||||
0, 0, 1, 0, 55, 0, 0, 0,
|
||||
8, 10, 0, 0, 3, 0, 1, 0,
|
||||
20, 10, 0, 0, 2, 0, 1, 0,
|
||||
62, 0, 0, 0, 23, 0, 0, 0,
|
||||
0, 0, 1, 0, 53, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
21, 10, 0, 0, 138, 0, 0, 0,
|
||||
17, 10, 0, 0, 226, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
24, 10, 0, 0, 3, 0, 1, 0,
|
||||
36, 10, 0, 0, 2, 0, 1, 0,
|
||||
70, 0, 0, 0, 24, 0, 0, 0,
|
||||
0, 0, 1, 0, 54, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
33, 10, 0, 0, 242, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
40, 10, 0, 0, 3, 0, 1, 0,
|
||||
52, 10, 0, 0, 2, 0, 1, 0,
|
||||
2, 0, 0, 0, 13, 0, 0, 0,
|
||||
0, 0, 1, 0, 55, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
49, 10, 0, 0, 138, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
52, 10, 0, 0, 3, 0, 1, 0,
|
||||
64, 10, 0, 0, 2, 0, 1, 0,
|
||||
7, 0, 0, 0, 14, 0, 0, 0,
|
||||
0, 0, 1, 0, 56, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
33, 10, 0, 0, 82, 0, 0, 0,
|
||||
61, 10, 0, 0, 82, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
32, 10, 0, 0, 3, 0, 1, 0,
|
||||
44, 10, 0, 0, 2, 0, 1, 0,
|
||||
55, 0, 0, 0, 15, 0, 0, 0,
|
||||
60, 10, 0, 0, 3, 0, 1, 0,
|
||||
72, 10, 0, 0, 2, 0, 1, 0,
|
||||
56, 0, 0, 0, 15, 0, 0, 0,
|
||||
0, 0, 1, 0, 57, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
41, 10, 0, 0, 202, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
48, 10, 0, 0, 3, 0, 1, 0,
|
||||
60, 10, 0, 0, 2, 0, 1, 0,
|
||||
41, 0, 0, 0, 25, 0, 0, 0,
|
||||
0, 0, 1, 0, 58, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
57, 10, 0, 0, 34, 1, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
68, 10, 0, 0, 3, 0, 1, 0,
|
||||
80, 10, 0, 0, 2, 0, 1, 0,
|
||||
31, 0, 0, 0, 26, 0, 0, 0,
|
||||
0, 0, 1, 0, 59, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
77, 10, 0, 0, 106, 0, 0, 0,
|
||||
69, 10, 0, 0, 202, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
76, 10, 0, 0, 3, 0, 1, 0,
|
||||
88, 10, 0, 0, 2, 0, 1, 0,
|
||||
35, 0, 0, 0, 27, 0, 0, 0,
|
||||
0, 0, 1, 0, 60, 0, 0, 0,
|
||||
41, 0, 0, 0, 25, 0, 0, 0,
|
||||
0, 0, 1, 0, 58, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
85, 10, 0, 0, 82, 0, 0, 0,
|
||||
85, 10, 0, 0, 34, 1, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
84, 10, 0, 0, 3, 0, 1, 0,
|
||||
96, 10, 0, 0, 2, 0, 1, 0,
|
||||
40, 0, 0, 0, 28, 0, 0, 0,
|
||||
0, 0, 1, 0, 61, 0, 0, 0,
|
||||
96, 10, 0, 0, 3, 0, 1, 0,
|
||||
108, 10, 0, 0, 2, 0, 1, 0,
|
||||
31, 0, 0, 0, 26, 0, 0, 0,
|
||||
0, 0, 1, 0, 59, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
93, 10, 0, 0, 34, 1, 0, 0,
|
||||
105, 10, 0, 0, 106, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
104, 10, 0, 0, 3, 0, 1, 0,
|
||||
116, 10, 0, 0, 2, 0, 1, 0,
|
||||
35, 0, 0, 0, 27, 0, 0, 0,
|
||||
0, 0, 1, 0, 60, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
113, 10, 0, 0, 82, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
112, 10, 0, 0, 3, 0, 1, 0,
|
||||
124, 10, 0, 0, 2, 0, 1, 0,
|
||||
40, 0, 0, 0, 28, 0, 0, 0,
|
||||
0, 0, 1, 0, 61, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
121, 10, 0, 0, 34, 1, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
132, 10, 0, 0, 3, 0, 1, 0,
|
||||
144, 10, 0, 0, 2, 0, 1, 0,
|
||||
12, 0, 0, 0, 13, 0, 0, 0,
|
||||
0, 0, 1, 0, 62, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
113, 10, 0, 0, 114, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
112, 10, 0, 0, 3, 0, 1, 0,
|
||||
140, 10, 0, 0, 2, 0, 1, 0,
|
||||
50, 0, 0, 0, 29, 0, 0, 0,
|
||||
0, 0, 1, 0, 63, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
137, 10, 0, 0, 138, 0, 0, 0,
|
||||
141, 10, 0, 0, 114, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
140, 10, 0, 0, 3, 0, 1, 0,
|
||||
152, 10, 0, 0, 2, 0, 1, 0,
|
||||
8, 0, 0, 0, 30, 0, 0, 0,
|
||||
0, 0, 1, 0, 64, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
149, 10, 0, 0, 50, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
144, 10, 0, 0, 3, 0, 1, 0,
|
||||
156, 10, 0, 0, 2, 0, 1, 0,
|
||||
13, 0, 0, 0, 41, 0, 0, 0,
|
||||
0, 0, 1, 0, 65, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
153, 10, 0, 0, 178, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
156, 10, 0, 0, 3, 0, 1, 0,
|
||||
168, 10, 0, 0, 2, 0, 1, 0,
|
||||
3, 0, 0, 0, 224, 3, 0, 0,
|
||||
0, 0, 1, 0, 66, 0, 0, 0,
|
||||
51, 0, 0, 0, 29, 0, 0, 0,
|
||||
0, 0, 1, 0, 63, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
165, 10, 0, 0, 58, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
160, 10, 0, 0, 3, 0, 1, 0,
|
||||
172, 10, 0, 0, 2, 0, 1, 0,
|
||||
14, 0, 0, 0, 32, 0, 0, 0,
|
||||
0, 0, 1, 0, 68, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
169, 10, 0, 0, 130, 0, 0, 0,
|
||||
165, 10, 0, 0, 138, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
168, 10, 0, 0, 3, 0, 1, 0,
|
||||
180, 10, 0, 0, 2, 0, 1, 0,
|
||||
15, 0, 0, 0, 225, 3, 0, 0,
|
||||
0, 0, 1, 0, 69, 0, 0, 0,
|
||||
8, 0, 0, 0, 30, 0, 0, 0,
|
||||
0, 0, 1, 0, 64, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
177, 10, 0, 0, 114, 0, 0, 0,
|
||||
177, 10, 0, 0, 50, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
176, 10, 0, 0, 3, 0, 1, 0,
|
||||
188, 10, 0, 0, 2, 0, 1, 0,
|
||||
38, 0, 0, 0, 226, 3, 0, 0,
|
||||
0, 0, 1, 0, 70, 0, 0, 0,
|
||||
172, 10, 0, 0, 3, 0, 1, 0,
|
||||
184, 10, 0, 0, 2, 0, 1, 0,
|
||||
13, 0, 0, 0, 41, 0, 0, 0,
|
||||
0, 0, 1, 0, 65, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
185, 10, 0, 0, 114, 0, 0, 0,
|
||||
181, 10, 0, 0, 178, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
184, 10, 0, 0, 3, 0, 1, 0,
|
||||
196, 10, 0, 0, 2, 0, 1, 0,
|
||||
9, 0, 0, 0, 227, 3, 0, 0,
|
||||
0, 0, 1, 0, 71, 0, 0, 0,
|
||||
3, 0, 0, 0, 224, 3, 0, 0,
|
||||
0, 0, 1, 0, 66, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
193, 10, 0, 0, 18, 1, 0, 0,
|
||||
193, 10, 0, 0, 58, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
188, 10, 0, 0, 3, 0, 1, 0,
|
||||
200, 10, 0, 0, 2, 0, 1, 0,
|
||||
14, 0, 0, 0, 32, 0, 0, 0,
|
||||
0, 0, 1, 0, 68, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
197, 10, 0, 0, 130, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
196, 10, 0, 0, 3, 0, 1, 0,
|
||||
208, 10, 0, 0, 2, 0, 1, 0,
|
||||
15, 0, 0, 0, 225, 3, 0, 0,
|
||||
0, 0, 1, 0, 69, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
205, 10, 0, 0, 114, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
204, 10, 0, 0, 3, 0, 1, 0,
|
||||
216, 10, 0, 0, 2, 0, 1, 0,
|
||||
38, 0, 0, 0, 226, 3, 0, 0,
|
||||
0, 0, 1, 0, 70, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
213, 10, 0, 0, 114, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
212, 10, 0, 0, 3, 0, 1, 0,
|
||||
224, 10, 0, 0, 2, 0, 1, 0,
|
||||
9, 0, 0, 0, 227, 3, 0, 0,
|
||||
0, 0, 1, 0, 71, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
221, 10, 0, 0, 18, 1, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
232, 10, 0, 0, 3, 0, 1, 0,
|
||||
244, 10, 0, 0, 2, 0, 1, 0,
|
||||
22, 0, 0, 0, 33, 0, 0, 0,
|
||||
0, 0, 1, 0, 72, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
213, 10, 0, 0, 162, 0, 0, 0,
|
||||
241, 10, 0, 0, 162, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
216, 10, 0, 0, 3, 0, 1, 0,
|
||||
228, 10, 0, 0, 2, 0, 1, 0,
|
||||
244, 10, 0, 0, 3, 0, 1, 0,
|
||||
0, 11, 0, 0, 2, 0, 1, 0,
|
||||
45, 0, 0, 0, 228, 3, 0, 0,
|
||||
0, 0, 1, 0, 73, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
253, 10, 0, 0, 66, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
248, 10, 0, 0, 3, 0, 1, 0,
|
||||
4, 11, 0, 0, 2, 0, 1, 0,
|
||||
99, 97, 114, 78, 97, 109, 101, 0,
|
||||
12, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
@@ -4770,6 +4777,14 @@ static const ::capnp::_::AlignedData<1269> b_8c69372490aaa9da = {
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
10, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
112, 97, 115, 115, 105, 118, 101, 0,
|
||||
1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, }
|
||||
};
|
||||
@@ -4787,11 +4802,11 @@ static const ::capnp::_::RawSchema* const d_8c69372490aaa9da[] = {
|
||||
&s_e836349c6056b0c9,
|
||||
&s_ff99e3682a833c51,
|
||||
};
|
||||
static const uint16_t m_8c69372490aaa9da[] = {63, 66, 15, 16, 1, 42, 0, 37, 19, 44, 39, 29, 6, 54, 4, 5, 2, 68, 47, 62, 53, 13, 14, 55, 38, 46, 26, 59, 56, 25, 17, 65, 52, 7, 49, 8, 48, 64, 36, 3, 43, 34, 22, 60, 9, 40, 10, 31, 51, 67, 35, 33, 27, 45, 11, 12, 32, 20, 21, 58, 30, 50, 69, 23, 24, 41, 57, 28, 61, 18};
|
||||
static const uint16_t i_8c69372490aaa9da[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69};
|
||||
static const uint16_t m_8c69372490aaa9da[] = {63, 66, 15, 16, 1, 42, 0, 37, 19, 44, 39, 29, 6, 54, 4, 5, 2, 68, 47, 62, 53, 13, 14, 55, 38, 46, 26, 59, 56, 25, 17, 65, 52, 7, 49, 8, 48, 64, 36, 70, 3, 43, 34, 22, 60, 9, 40, 10, 31, 51, 67, 35, 33, 27, 45, 11, 12, 32, 20, 21, 58, 30, 50, 69, 23, 24, 41, 57, 28, 61, 18};
|
||||
static const uint16_t i_8c69372490aaa9da[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70};
|
||||
const ::capnp::_::RawSchema s_8c69372490aaa9da = {
|
||||
0x8c69372490aaa9da, b_8c69372490aaa9da.words, 1269, d_8c69372490aaa9da, m_8c69372490aaa9da,
|
||||
10, 70, i_8c69372490aaa9da, nullptr, nullptr, { &s_8c69372490aaa9da, nullptr, nullptr, 0, 0, nullptr }
|
||||
0x8c69372490aaa9da, b_8c69372490aaa9da.words, 1284, d_8c69372490aaa9da, m_8c69372490aaa9da,
|
||||
10, 71, i_8c69372490aaa9da, nullptr, nullptr, { &s_8c69372490aaa9da, nullptr, nullptr, 0, 0, nullptr }
|
||||
};
|
||||
#endif // !CAPNP_LITE
|
||||
static const ::capnp::_::AlignedData<85> b_e836349c6056b0c9 = {
|
||||
|
||||
@@ -2345,6 +2345,8 @@ public:
|
||||
|
||||
inline float getTireStiffnessFactor() const;
|
||||
|
||||
inline bool getPassive() const;
|
||||
|
||||
private:
|
||||
::capnp::_::StructReader _reader;
|
||||
template <typename, ::capnp::Kind>
|
||||
@@ -2641,6 +2643,9 @@ public:
|
||||
inline float getTireStiffnessFactor();
|
||||
inline void setTireStiffnessFactor(float value);
|
||||
|
||||
inline bool getPassive();
|
||||
inline void setPassive(bool value);
|
||||
|
||||
private:
|
||||
::capnp::_::StructBuilder _builder;
|
||||
template <typename, ::capnp::Kind>
|
||||
@@ -7159,6 +7164,20 @@ inline void CarParams::Builder::setTireStiffnessFactor(float value) {
|
||||
::capnp::bounded<33>() * ::capnp::ELEMENTS, value);
|
||||
}
|
||||
|
||||
inline bool CarParams::Reader::getPassive() const {
|
||||
return _reader.getDataField<bool>(
|
||||
::capnp::bounded<996>() * ::capnp::ELEMENTS);
|
||||
}
|
||||
|
||||
inline bool CarParams::Builder::getPassive() {
|
||||
return _builder.getDataField<bool>(
|
||||
::capnp::bounded<996>() * ::capnp::ELEMENTS);
|
||||
}
|
||||
inline void CarParams::Builder::setPassive(bool value) {
|
||||
_builder.setDataField<bool>(
|
||||
::capnp::bounded<996>() * ::capnp::ELEMENTS, value);
|
||||
}
|
||||
|
||||
inline ::cereal::CarParams::SafetyModel CarParams::SafetyConfig::Reader::getSafetyModel() const {
|
||||
return _reader.getDataField< ::cereal::CarParams::SafetyModel>(
|
||||
::capnp::bounded<0>() * ::capnp::ELEMENTS);
|
||||
|
||||
+52
-300
@@ -382,17 +382,17 @@ const ::capnp::_::RawSchema s_81c2f05a394cf4af = {
|
||||
0, 20, i_81c2f05a394cf4af, nullptr, nullptr, { &s_81c2f05a394cf4af, nullptr, nullptr, 0, 0, nullptr }
|
||||
};
|
||||
#endif // !CAPNP_LITE
|
||||
static const ::capnp::_::AlignedData<302> b_aedffd8f31e7b55d = {
|
||||
static const ::capnp::_::AlignedData<115> b_aedffd8f31e7b55d = {
|
||||
{ 0, 0, 0, 0, 5, 0, 6, 0,
|
||||
93, 181, 231, 49, 143, 253, 223, 174,
|
||||
13, 0, 0, 0, 1, 0, 5, 0,
|
||||
13, 0, 0, 0, 1, 0, 2, 0,
|
||||
89, 10, 85, 29, 102, 186, 38, 181,
|
||||
0, 0, 7, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
21, 0, 0, 0, 10, 1, 0, 0,
|
||||
37, 0, 0, 0, 55, 0, 0, 0,
|
||||
37, 0, 0, 0, 39, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
101, 0, 0, 0, 135, 3, 0, 0,
|
||||
81, 0, 0, 0, 31, 1, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
99, 117, 115, 116, 111, 109, 46, 99,
|
||||
@@ -400,137 +400,73 @@ static const ::capnp::_::AlignedData<302> b_aedffd8f31e7b55d = {
|
||||
103, 105, 116, 117, 100, 105, 110, 97,
|
||||
108, 80, 108, 97, 110, 69, 120, 116,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
12, 0, 0, 0, 1, 0, 1, 0,
|
||||
8, 0, 0, 0, 1, 0, 1, 0,
|
||||
206, 31, 115, 238, 186, 117, 42, 180,
|
||||
17, 0, 0, 0, 210, 0, 0, 0,
|
||||
2, 8, 153, 155, 54, 132, 119, 158,
|
||||
25, 0, 0, 0, 186, 0, 0, 0,
|
||||
9, 0, 0, 0, 210, 0, 0, 0,
|
||||
218, 174, 84, 82, 132, 78, 18, 209,
|
||||
29, 0, 0, 0, 210, 0, 0, 0,
|
||||
17, 0, 0, 0, 210, 0, 0, 0,
|
||||
76, 111, 110, 103, 105, 116, 117, 100,
|
||||
105, 110, 97, 108, 80, 108, 97, 110,
|
||||
69, 120, 116, 83, 111, 117, 114, 99,
|
||||
101, 0, 0, 0, 0, 0, 0, 0,
|
||||
83, 112, 101, 101, 100, 76, 105, 109,
|
||||
105, 116, 67, 111, 110, 116, 114, 111,
|
||||
108, 83, 116, 97, 116, 101, 0, 0,
|
||||
86, 105, 115, 105, 111, 110, 84, 117,
|
||||
114, 110, 67, 111, 110, 116, 114, 111,
|
||||
108, 108, 101, 114, 83, 116, 97, 116,
|
||||
101, 0, 0, 0, 0, 0, 0, 0,
|
||||
64, 0, 0, 0, 3, 0, 4, 0,
|
||||
20, 0, 0, 0, 3, 0, 4, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 1, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
177, 1, 0, 0, 210, 0, 0, 0,
|
||||
125, 0, 0, 0, 122, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
184, 1, 0, 0, 3, 0, 1, 0,
|
||||
196, 1, 0, 0, 2, 0, 1, 0,
|
||||
124, 0, 0, 0, 3, 0, 1, 0,
|
||||
136, 0, 0, 0, 2, 0, 1, 0,
|
||||
1, 0, 0, 0, 1, 0, 0, 0,
|
||||
0, 0, 1, 0, 1, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
193, 1, 0, 0, 130, 0, 0, 0,
|
||||
133, 0, 0, 0, 114, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
192, 1, 0, 0, 3, 0, 1, 0,
|
||||
204, 1, 0, 0, 2, 0, 1, 0,
|
||||
132, 0, 0, 0, 3, 0, 1, 0,
|
||||
144, 0, 0, 0, 2, 0, 1, 0,
|
||||
2, 0, 0, 0, 1, 0, 0, 0,
|
||||
0, 0, 1, 0, 2, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
201, 1, 0, 0, 186, 0, 0, 0,
|
||||
141, 0, 0, 0, 210, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
204, 1, 0, 0, 3, 0, 1, 0,
|
||||
216, 1, 0, 0, 2, 0, 1, 0,
|
||||
3, 0, 0, 0, 2, 0, 0, 0,
|
||||
148, 0, 0, 0, 3, 0, 1, 0,
|
||||
160, 0, 0, 0, 2, 0, 1, 0,
|
||||
3, 0, 0, 0, 1, 0, 0, 0,
|
||||
0, 0, 1, 0, 3, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
213, 1, 0, 0, 90, 0, 0, 0,
|
||||
157, 0, 0, 0, 130, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
212, 1, 0, 0, 3, 0, 1, 0,
|
||||
224, 1, 0, 0, 2, 0, 1, 0,
|
||||
4, 0, 0, 0, 3, 0, 0, 0,
|
||||
156, 0, 0, 0, 3, 0, 1, 0,
|
||||
168, 0, 0, 0, 2, 0, 1, 0,
|
||||
4, 0, 0, 0, 4, 0, 0, 0,
|
||||
0, 0, 1, 0, 4, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
221, 1, 0, 0, 138, 0, 0, 0,
|
||||
165, 0, 0, 0, 210, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
224, 1, 0, 0, 3, 0, 1, 0,
|
||||
236, 1, 0, 0, 2, 0, 1, 0,
|
||||
5, 0, 0, 0, 4, 0, 0, 0,
|
||||
0, 0, 1, 0, 5, 0, 0, 0,
|
||||
172, 0, 0, 0, 3, 0, 1, 0,
|
||||
184, 0, 0, 0, 2, 0, 1, 0,
|
||||
100, 112, 69, 50, 69, 73, 115, 66,
|
||||
108, 101, 110, 100, 101, 100, 0, 0,
|
||||
1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
233, 1, 0, 0, 138, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
236, 1, 0, 0, 3, 0, 1, 0,
|
||||
248, 1, 0, 0, 2, 0, 1, 0,
|
||||
6, 0, 0, 0, 160, 0, 0, 0,
|
||||
0, 0, 1, 0, 6, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
245, 1, 0, 0, 130, 0, 0, 0,
|
||||
1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
244, 1, 0, 0, 3, 0, 1, 0,
|
||||
0, 2, 0, 0, 2, 0, 1, 0,
|
||||
7, 0, 0, 0, 161, 0, 0, 0,
|
||||
0, 0, 1, 0, 7, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
253, 1, 0, 0, 170, 0, 0, 0,
|
||||
100, 101, 50, 101, 73, 115, 69, 110,
|
||||
97, 98, 108, 101, 100, 0, 0, 0,
|
||||
1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 2, 0, 0, 3, 0, 1, 0,
|
||||
12, 2, 0, 0, 2, 0, 1, 0,
|
||||
8, 0, 0, 0, 6, 0, 0, 0,
|
||||
0, 0, 1, 0, 8, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
9, 2, 0, 0, 178, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
12, 2, 0, 0, 3, 0, 1, 0,
|
||||
24, 2, 0, 0, 2, 0, 1, 0,
|
||||
9, 0, 0, 0, 7, 0, 0, 0,
|
||||
0, 0, 1, 0, 9, 0, 0, 0,
|
||||
1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
21, 2, 0, 0, 90, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
20, 2, 0, 0, 3, 0, 1, 0,
|
||||
32, 2, 0, 0, 2, 0, 1, 0,
|
||||
10, 0, 0, 0, 8, 0, 0, 0,
|
||||
0, 0, 1, 0, 10, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
29, 2, 0, 0, 82, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
28, 2, 0, 0, 3, 0, 1, 0,
|
||||
40, 2, 0, 0, 2, 0, 1, 0,
|
||||
11, 0, 0, 0, 11, 0, 0, 0,
|
||||
0, 0, 1, 0, 11, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
37, 2, 0, 0, 178, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
40, 2, 0, 0, 3, 0, 1, 0,
|
||||
52, 2, 0, 0, 2, 0, 1, 0,
|
||||
12, 0, 0, 0, 18, 0, 0, 0,
|
||||
0, 0, 1, 0, 12, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
49, 2, 0, 0, 74, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
48, 2, 0, 0, 3, 0, 1, 0,
|
||||
60, 2, 0, 0, 2, 0, 1, 0,
|
||||
13, 0, 0, 0, 162, 0, 0, 0,
|
||||
0, 0, 1, 0, 13, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
57, 2, 0, 0, 122, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
56, 2, 0, 0, 3, 0, 1, 0,
|
||||
68, 2, 0, 0, 2, 0, 1, 0,
|
||||
14, 0, 0, 0, 19, 0, 0, 0,
|
||||
0, 0, 1, 0, 14, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
65, 2, 0, 0, 210, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
72, 2, 0, 0, 3, 0, 1, 0,
|
||||
84, 2, 0, 0, 2, 0, 1, 0,
|
||||
15, 0, 0, 0, 163, 0, 0, 0,
|
||||
0, 0, 1, 0, 15, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
81, 2, 0, 0, 114, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
80, 2, 0, 0, 3, 0, 1, 0,
|
||||
92, 2, 0, 0, 2, 0, 1, 0,
|
||||
118, 105, 115, 105, 111, 110, 84, 117,
|
||||
114, 110, 67, 111, 110, 116, 114, 111,
|
||||
108, 108, 101, 114, 83, 116, 97, 116,
|
||||
@@ -551,120 +487,6 @@ static const ::capnp::_::AlignedData<302> b_aedffd8f31e7b55d = {
|
||||
10, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
115, 112, 101, 101, 100, 76, 105, 109,
|
||||
105, 116, 67, 111, 110, 116, 114, 111,
|
||||
108, 83, 116, 97, 116, 101, 0, 0,
|
||||
15, 0, 0, 0, 0, 0, 0, 0,
|
||||
2, 8, 153, 155, 54, 132, 119, 158,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
15, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
115, 112, 101, 101, 100, 76, 105, 109,
|
||||
105, 116, 0, 0, 0, 0, 0, 0,
|
||||
10, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
10, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
115, 112, 101, 101, 100, 76, 105, 109,
|
||||
105, 116, 79, 102, 102, 115, 101, 116,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
10, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
10, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
100, 105, 115, 116, 84, 111, 83, 112,
|
||||
101, 101, 100, 76, 105, 109, 105, 116,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
10, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
10, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
105, 115, 77, 97, 112, 83, 112, 101,
|
||||
101, 100, 76, 105, 109, 105, 116, 0,
|
||||
1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
115, 112, 101, 101, 100, 76, 105, 109,
|
||||
105, 116, 80, 101, 114, 99, 79, 102,
|
||||
102, 115, 101, 116, 0, 0, 0, 0,
|
||||
1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
115, 112, 101, 101, 100, 76, 105, 109,
|
||||
105, 116, 86, 97, 108, 117, 101, 79,
|
||||
102, 102, 115, 101, 116, 0, 0, 0,
|
||||
10, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
10, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
100, 105, 115, 116, 84, 111, 84, 117,
|
||||
114, 110, 0, 0, 0, 0, 0, 0,
|
||||
10, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
10, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
116, 117, 114, 110, 83, 112, 101, 101,
|
||||
100, 0, 0, 0, 0, 0, 0, 0,
|
||||
10, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
10, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
116, 117, 114, 110, 83, 112, 101, 101,
|
||||
100, 67, 111, 110, 116, 114, 111, 108,
|
||||
83, 116, 97, 116, 101, 0, 0, 0,
|
||||
15, 0, 0, 0, 0, 0, 0, 0,
|
||||
2, 8, 153, 155, 54, 132, 119, 158,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
15, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
116, 117, 114, 110, 83, 105, 103, 110,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
3, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
3, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
100, 112, 69, 50, 69, 73, 115, 66,
|
||||
108, 101, 110, 100, 101, 100, 0, 0,
|
||||
1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
108, 111, 110, 103, 105, 116, 117, 100,
|
||||
105, 110, 97, 108, 80, 108, 97, 110,
|
||||
69, 120, 116, 83, 111, 117, 114, 99,
|
||||
@@ -674,33 +496,23 @@ static const ::capnp::_::AlignedData<302> b_aedffd8f31e7b55d = {
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
15, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
100, 101, 50, 101, 73, 115, 69, 110,
|
||||
97, 98, 108, 101, 100, 0, 0, 0,
|
||||
1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, }
|
||||
};
|
||||
::capnp::word const* const bp_aedffd8f31e7b55d = b_aedffd8f31e7b55d.words;
|
||||
#if !CAPNP_LITE
|
||||
static const ::capnp::_::RawSchema* const d_aedffd8f31e7b55d[] = {
|
||||
&s_9e7784369b990802,
|
||||
&s_b42a75baee731fce,
|
||||
&s_d1124e845254aeda,
|
||||
};
|
||||
static const uint16_t m_aedffd8f31e7b55d[] = {15, 5, 9, 13, 6, 14, 3, 2, 4, 7, 8, 12, 10, 11, 0, 1};
|
||||
static const uint16_t i_aedffd8f31e7b55d[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15};
|
||||
static const uint16_t m_aedffd8f31e7b55d[] = {1, 0, 4, 2, 3};
|
||||
static const uint16_t i_aedffd8f31e7b55d[] = {0, 1, 2, 3, 4};
|
||||
const ::capnp::_::RawSchema s_aedffd8f31e7b55d = {
|
||||
0xaedffd8f31e7b55d, b_aedffd8f31e7b55d.words, 302, d_aedffd8f31e7b55d, m_aedffd8f31e7b55d,
|
||||
3, 16, i_aedffd8f31e7b55d, nullptr, nullptr, { &s_aedffd8f31e7b55d, nullptr, nullptr, 0, 0, nullptr }
|
||||
0xaedffd8f31e7b55d, b_aedffd8f31e7b55d.words, 115, d_aedffd8f31e7b55d, m_aedffd8f31e7b55d,
|
||||
2, 5, i_aedffd8f31e7b55d, nullptr, nullptr, { &s_aedffd8f31e7b55d, nullptr, nullptr, 0, 0, nullptr }
|
||||
};
|
||||
#endif // !CAPNP_LITE
|
||||
static const ::capnp::_::AlignedData<55> b_b42a75baee731fce = {
|
||||
static const ::capnp::_::AlignedData<46> b_b42a75baee731fce = {
|
||||
{ 0, 0, 0, 0, 5, 0, 6, 0,
|
||||
206, 31, 115, 238, 186, 117, 42, 180,
|
||||
33, 0, 0, 0, 2, 0, 0, 0,
|
||||
@@ -710,7 +522,7 @@ static const ::capnp::_::AlignedData<55> b_b42a75baee731fce = {
|
||||
21, 0, 0, 0, 218, 1, 0, 0,
|
||||
49, 0, 0, 0, 7, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
45, 0, 0, 0, 199, 0, 0, 0,
|
||||
45, 0, 0, 0, 151, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
99, 117, 115, 116, 111, 109, 46, 99,
|
||||
@@ -722,101 +534,41 @@ static const ::capnp::_::AlignedData<55> b_b42a75baee731fce = {
|
||||
110, 69, 120, 116, 83, 111, 117, 114,
|
||||
99, 101, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 1, 0, 1, 0,
|
||||
32, 0, 0, 0, 1, 0, 2, 0,
|
||||
24, 0, 0, 0, 1, 0, 2, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
89, 0, 0, 0, 58, 0, 0, 0,
|
||||
65, 0, 0, 0, 58, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 0, 0, 0, 0, 0, 0,
|
||||
81, 0, 0, 0, 50, 0, 0, 0,
|
||||
57, 0, 0, 0, 50, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
2, 0, 0, 0, 0, 0, 0, 0,
|
||||
73, 0, 0, 0, 50, 0, 0, 0,
|
||||
49, 0, 0, 0, 50, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
3, 0, 0, 0, 0, 0, 0, 0,
|
||||
65, 0, 0, 0, 50, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
4, 0, 0, 0, 0, 0, 0, 0,
|
||||
57, 0, 0, 0, 34, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
5, 0, 0, 0, 0, 0, 0, 0,
|
||||
49, 0, 0, 0, 42, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
6, 0, 0, 0, 0, 0, 0, 0,
|
||||
41, 0, 0, 0, 50, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
7, 0, 0, 0, 0, 0, 0, 0,
|
||||
33, 0, 0, 0, 82, 0, 0, 0,
|
||||
4, 0, 0, 0, 0, 0, 0, 0,
|
||||
33, 0, 0, 0, 34, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
5, 0, 0, 0, 0, 0, 0, 0,
|
||||
25, 0, 0, 0, 42, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
99, 114, 117, 105, 115, 101, 0, 0,
|
||||
108, 101, 97, 100, 48, 0, 0, 0,
|
||||
108, 101, 97, 100, 49, 0, 0, 0,
|
||||
108, 101, 97, 100, 50, 0, 0, 0,
|
||||
101, 50, 101, 0, 0, 0, 0, 0,
|
||||
116, 117, 114, 110, 0, 0, 0, 0,
|
||||
108, 105, 109, 105, 116, 0, 0, 0,
|
||||
116, 117, 114, 110, 108, 105, 109, 105,
|
||||
116, 0, 0, 0, 0, 0, 0, 0, }
|
||||
116, 117, 114, 110, 0, 0, 0, 0, }
|
||||
};
|
||||
::capnp::word const* const bp_b42a75baee731fce = b_b42a75baee731fce.words;
|
||||
#if !CAPNP_LITE
|
||||
static const uint16_t m_b42a75baee731fce[] = {0, 4, 1, 2, 3, 6, 5, 7};
|
||||
static const uint16_t m_b42a75baee731fce[] = {0, 4, 1, 2, 3, 5};
|
||||
const ::capnp::_::RawSchema s_b42a75baee731fce = {
|
||||
0xb42a75baee731fce, b_b42a75baee731fce.words, 55, nullptr, m_b42a75baee731fce,
|
||||
0, 8, nullptr, nullptr, nullptr, { &s_b42a75baee731fce, nullptr, nullptr, 0, 0, nullptr }
|
||||
0xb42a75baee731fce, b_b42a75baee731fce.words, 46, nullptr, m_b42a75baee731fce,
|
||||
0, 6, nullptr, nullptr, nullptr, { &s_b42a75baee731fce, nullptr, nullptr, 0, 0, nullptr }
|
||||
};
|
||||
#endif // !CAPNP_LITE
|
||||
CAPNP_DEFINE_ENUM(LongitudinalPlanExtSource_b42a75baee731fce, b42a75baee731fce);
|
||||
static const ::capnp::_::AlignedData<40> b_9e7784369b990802 = {
|
||||
{ 0, 0, 0, 0, 5, 0, 6, 0,
|
||||
2, 8, 153, 155, 54, 132, 119, 158,
|
||||
33, 0, 0, 0, 2, 0, 0, 0,
|
||||
93, 181, 231, 49, 143, 253, 223, 174,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
21, 0, 0, 0, 194, 1, 0, 0,
|
||||
45, 0, 0, 0, 7, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
41, 0, 0, 0, 103, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
99, 117, 115, 116, 111, 109, 46, 99,
|
||||
97, 112, 110, 112, 58, 76, 111, 110,
|
||||
103, 105, 116, 117, 100, 105, 110, 97,
|
||||
108, 80, 108, 97, 110, 69, 120, 116,
|
||||
46, 83, 112, 101, 101, 100, 76, 105,
|
||||
109, 105, 116, 67, 111, 110, 116, 114,
|
||||
111, 108, 83, 116, 97, 116, 101, 0,
|
||||
0, 0, 0, 0, 1, 0, 1, 0,
|
||||
16, 0, 0, 0, 1, 0, 2, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
41, 0, 0, 0, 74, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 0, 0, 0, 0, 0, 0,
|
||||
37, 0, 0, 0, 106, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
2, 0, 0, 0, 0, 0, 0, 0,
|
||||
33, 0, 0, 0, 74, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
3, 0, 0, 0, 0, 0, 0, 0,
|
||||
29, 0, 0, 0, 58, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
105, 110, 97, 99, 116, 105, 118, 101,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
116, 101, 109, 112, 73, 110, 97, 99,
|
||||
116, 105, 118, 101, 0, 0, 0, 0,
|
||||
97, 100, 97, 112, 116, 105, 110, 103,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
97, 99, 116, 105, 118, 101, 0, 0, }
|
||||
};
|
||||
::capnp::word const* const bp_9e7784369b990802 = b_9e7784369b990802.words;
|
||||
#if !CAPNP_LITE
|
||||
static const uint16_t m_9e7784369b990802[] = {3, 2, 0, 1};
|
||||
const ::capnp::_::RawSchema s_9e7784369b990802 = {
|
||||
0x9e7784369b990802, b_9e7784369b990802.words, 40, nullptr, m_9e7784369b990802,
|
||||
0, 4, nullptr, nullptr, nullptr, { &s_9e7784369b990802, nullptr, nullptr, 0, 0, nullptr }
|
||||
};
|
||||
#endif // !CAPNP_LITE
|
||||
CAPNP_DEFINE_ENUM(SpeedLimitControlState_9e7784369b990802, 9e7784369b990802);
|
||||
static const ::capnp::_::AlignedData<40> b_d1124e845254aeda = {
|
||||
{ 0, 0, 0, 0, 5, 0, 6, 0,
|
||||
218, 174, 84, 82, 132, 78, 18, 209,
|
||||
|
||||
+45
-266
@@ -24,18 +24,8 @@ enum class LongitudinalPlanExtSource_b42a75baee731fce: uint16_t {
|
||||
LEAD2,
|
||||
E2E,
|
||||
TURN,
|
||||
LIMIT,
|
||||
TURNLIMIT,
|
||||
};
|
||||
CAPNP_DECLARE_ENUM(LongitudinalPlanExtSource, b42a75baee731fce);
|
||||
CAPNP_DECLARE_SCHEMA(9e7784369b990802);
|
||||
enum class SpeedLimitControlState_9e7784369b990802: uint16_t {
|
||||
INACTIVE,
|
||||
TEMP_INACTIVE,
|
||||
ADAPTING,
|
||||
ACTIVE,
|
||||
};
|
||||
CAPNP_DECLARE_ENUM(SpeedLimitControlState, 9e7784369b990802);
|
||||
CAPNP_DECLARE_SCHEMA(d1124e845254aeda);
|
||||
enum class VisionTurnControllerState_d1124e845254aeda: uint16_t {
|
||||
DISABLED,
|
||||
@@ -81,13 +71,11 @@ struct LongitudinalPlanExt {
|
||||
class Pipeline;
|
||||
typedef ::capnp::schemas::LongitudinalPlanExtSource_b42a75baee731fce LongitudinalPlanExtSource;
|
||||
|
||||
typedef ::capnp::schemas::SpeedLimitControlState_9e7784369b990802 SpeedLimitControlState;
|
||||
|
||||
typedef ::capnp::schemas::VisionTurnControllerState_d1124e845254aeda VisionTurnControllerState;
|
||||
|
||||
|
||||
struct _capnpPrivate {
|
||||
CAPNP_DECLARE_STRUCT_HEADER(aedffd8f31e7b55d, 5, 0)
|
||||
CAPNP_DECLARE_STRUCT_HEADER(aedffd8f31e7b55d, 2, 0)
|
||||
#if !CAPNP_LITE
|
||||
static constexpr ::capnp::_::RawBrandedSchema const* brand() { return &schema->defaultBrand; }
|
||||
#endif // !CAPNP_LITE
|
||||
@@ -427,38 +415,16 @@ public:
|
||||
}
|
||||
#endif // !CAPNP_LITE
|
||||
|
||||
inline bool getDpE2EIsBlended() const;
|
||||
|
||||
inline bool getDe2eIsEnabled() const;
|
||||
|
||||
inline ::cereal::LongitudinalPlanExt::VisionTurnControllerState getVisionTurnControllerState() const;
|
||||
|
||||
inline float getVisionTurnSpeed() const;
|
||||
|
||||
inline ::cereal::LongitudinalPlanExt::SpeedLimitControlState getSpeedLimitControlState() const;
|
||||
|
||||
inline float getSpeedLimit() const;
|
||||
|
||||
inline float getSpeedLimitOffset() const;
|
||||
|
||||
inline float getDistToSpeedLimit() const;
|
||||
|
||||
inline bool getIsMapSpeedLimit() const;
|
||||
|
||||
inline bool getSpeedLimitPercOffset() const;
|
||||
|
||||
inline float getSpeedLimitValueOffset() const;
|
||||
|
||||
inline float getDistToTurn() const;
|
||||
|
||||
inline float getTurnSpeed() const;
|
||||
|
||||
inline ::cereal::LongitudinalPlanExt::SpeedLimitControlState getTurnSpeedControlState() const;
|
||||
|
||||
inline ::int16_t getTurnSign() const;
|
||||
|
||||
inline bool getDpE2EIsBlended() const;
|
||||
|
||||
inline ::cereal::LongitudinalPlanExt::LongitudinalPlanExtSource getLongitudinalPlanExtSource() const;
|
||||
|
||||
inline bool getDe2eIsEnabled() const;
|
||||
|
||||
private:
|
||||
::capnp::_::StructReader _reader;
|
||||
template <typename, ::capnp::Kind>
|
||||
@@ -487,54 +453,21 @@ public:
|
||||
inline ::kj::StringTree toString() const { return asReader().toString(); }
|
||||
#endif // !CAPNP_LITE
|
||||
|
||||
inline bool getDpE2EIsBlended();
|
||||
inline void setDpE2EIsBlended(bool value);
|
||||
|
||||
inline bool getDe2eIsEnabled();
|
||||
inline void setDe2eIsEnabled(bool value);
|
||||
|
||||
inline ::cereal::LongitudinalPlanExt::VisionTurnControllerState getVisionTurnControllerState();
|
||||
inline void setVisionTurnControllerState( ::cereal::LongitudinalPlanExt::VisionTurnControllerState value);
|
||||
|
||||
inline float getVisionTurnSpeed();
|
||||
inline void setVisionTurnSpeed(float value);
|
||||
|
||||
inline ::cereal::LongitudinalPlanExt::SpeedLimitControlState getSpeedLimitControlState();
|
||||
inline void setSpeedLimitControlState( ::cereal::LongitudinalPlanExt::SpeedLimitControlState value);
|
||||
|
||||
inline float getSpeedLimit();
|
||||
inline void setSpeedLimit(float value);
|
||||
|
||||
inline float getSpeedLimitOffset();
|
||||
inline void setSpeedLimitOffset(float value);
|
||||
|
||||
inline float getDistToSpeedLimit();
|
||||
inline void setDistToSpeedLimit(float value);
|
||||
|
||||
inline bool getIsMapSpeedLimit();
|
||||
inline void setIsMapSpeedLimit(bool value);
|
||||
|
||||
inline bool getSpeedLimitPercOffset();
|
||||
inline void setSpeedLimitPercOffset(bool value);
|
||||
|
||||
inline float getSpeedLimitValueOffset();
|
||||
inline void setSpeedLimitValueOffset(float value);
|
||||
|
||||
inline float getDistToTurn();
|
||||
inline void setDistToTurn(float value);
|
||||
|
||||
inline float getTurnSpeed();
|
||||
inline void setTurnSpeed(float value);
|
||||
|
||||
inline ::cereal::LongitudinalPlanExt::SpeedLimitControlState getTurnSpeedControlState();
|
||||
inline void setTurnSpeedControlState( ::cereal::LongitudinalPlanExt::SpeedLimitControlState value);
|
||||
|
||||
inline ::int16_t getTurnSign();
|
||||
inline void setTurnSign( ::int16_t value);
|
||||
|
||||
inline bool getDpE2EIsBlended();
|
||||
inline void setDpE2EIsBlended(bool value);
|
||||
|
||||
inline ::cereal::LongitudinalPlanExt::LongitudinalPlanExtSource getLongitudinalPlanExtSource();
|
||||
inline void setLongitudinalPlanExtSource( ::cereal::LongitudinalPlanExt::LongitudinalPlanExtSource value);
|
||||
|
||||
inline bool getDe2eIsEnabled();
|
||||
inline void setDe2eIsEnabled(bool value);
|
||||
|
||||
private:
|
||||
::capnp::_::StructBuilder _builder;
|
||||
template <typename, ::capnp::Kind>
|
||||
@@ -1535,18 +1468,46 @@ inline void LiveMapData::Builder::setLastGpsBearingAccuracyDeg(float value) {
|
||||
::capnp::bounded<15>() * ::capnp::ELEMENTS, value);
|
||||
}
|
||||
|
||||
inline bool LongitudinalPlanExt::Reader::getDpE2EIsBlended() const {
|
||||
return _reader.getDataField<bool>(
|
||||
::capnp::bounded<0>() * ::capnp::ELEMENTS);
|
||||
}
|
||||
|
||||
inline bool LongitudinalPlanExt::Builder::getDpE2EIsBlended() {
|
||||
return _builder.getDataField<bool>(
|
||||
::capnp::bounded<0>() * ::capnp::ELEMENTS);
|
||||
}
|
||||
inline void LongitudinalPlanExt::Builder::setDpE2EIsBlended(bool value) {
|
||||
_builder.setDataField<bool>(
|
||||
::capnp::bounded<0>() * ::capnp::ELEMENTS, value);
|
||||
}
|
||||
|
||||
inline bool LongitudinalPlanExt::Reader::getDe2eIsEnabled() const {
|
||||
return _reader.getDataField<bool>(
|
||||
::capnp::bounded<1>() * ::capnp::ELEMENTS);
|
||||
}
|
||||
|
||||
inline bool LongitudinalPlanExt::Builder::getDe2eIsEnabled() {
|
||||
return _builder.getDataField<bool>(
|
||||
::capnp::bounded<1>() * ::capnp::ELEMENTS);
|
||||
}
|
||||
inline void LongitudinalPlanExt::Builder::setDe2eIsEnabled(bool value) {
|
||||
_builder.setDataField<bool>(
|
||||
::capnp::bounded<1>() * ::capnp::ELEMENTS, value);
|
||||
}
|
||||
|
||||
inline ::cereal::LongitudinalPlanExt::VisionTurnControllerState LongitudinalPlanExt::Reader::getVisionTurnControllerState() const {
|
||||
return _reader.getDataField< ::cereal::LongitudinalPlanExt::VisionTurnControllerState>(
|
||||
::capnp::bounded<0>() * ::capnp::ELEMENTS);
|
||||
::capnp::bounded<1>() * ::capnp::ELEMENTS);
|
||||
}
|
||||
|
||||
inline ::cereal::LongitudinalPlanExt::VisionTurnControllerState LongitudinalPlanExt::Builder::getVisionTurnControllerState() {
|
||||
return _builder.getDataField< ::cereal::LongitudinalPlanExt::VisionTurnControllerState>(
|
||||
::capnp::bounded<0>() * ::capnp::ELEMENTS);
|
||||
::capnp::bounded<1>() * ::capnp::ELEMENTS);
|
||||
}
|
||||
inline void LongitudinalPlanExt::Builder::setVisionTurnControllerState( ::cereal::LongitudinalPlanExt::VisionTurnControllerState value) {
|
||||
_builder.setDataField< ::cereal::LongitudinalPlanExt::VisionTurnControllerState>(
|
||||
::capnp::bounded<0>() * ::capnp::ELEMENTS, value);
|
||||
::capnp::bounded<1>() * ::capnp::ELEMENTS, value);
|
||||
}
|
||||
|
||||
inline float LongitudinalPlanExt::Reader::getVisionTurnSpeed() const {
|
||||
@@ -1563,200 +1524,18 @@ inline void LongitudinalPlanExt::Builder::setVisionTurnSpeed(float value) {
|
||||
::capnp::bounded<1>() * ::capnp::ELEMENTS, value);
|
||||
}
|
||||
|
||||
inline ::cereal::LongitudinalPlanExt::SpeedLimitControlState LongitudinalPlanExt::Reader::getSpeedLimitControlState() const {
|
||||
return _reader.getDataField< ::cereal::LongitudinalPlanExt::SpeedLimitControlState>(
|
||||
::capnp::bounded<1>() * ::capnp::ELEMENTS);
|
||||
}
|
||||
|
||||
inline ::cereal::LongitudinalPlanExt::SpeedLimitControlState LongitudinalPlanExt::Builder::getSpeedLimitControlState() {
|
||||
return _builder.getDataField< ::cereal::LongitudinalPlanExt::SpeedLimitControlState>(
|
||||
::capnp::bounded<1>() * ::capnp::ELEMENTS);
|
||||
}
|
||||
inline void LongitudinalPlanExt::Builder::setSpeedLimitControlState( ::cereal::LongitudinalPlanExt::SpeedLimitControlState value) {
|
||||
_builder.setDataField< ::cereal::LongitudinalPlanExt::SpeedLimitControlState>(
|
||||
::capnp::bounded<1>() * ::capnp::ELEMENTS, value);
|
||||
}
|
||||
|
||||
inline float LongitudinalPlanExt::Reader::getSpeedLimit() const {
|
||||
return _reader.getDataField<float>(
|
||||
::capnp::bounded<2>() * ::capnp::ELEMENTS);
|
||||
}
|
||||
|
||||
inline float LongitudinalPlanExt::Builder::getSpeedLimit() {
|
||||
return _builder.getDataField<float>(
|
||||
::capnp::bounded<2>() * ::capnp::ELEMENTS);
|
||||
}
|
||||
inline void LongitudinalPlanExt::Builder::setSpeedLimit(float value) {
|
||||
_builder.setDataField<float>(
|
||||
::capnp::bounded<2>() * ::capnp::ELEMENTS, value);
|
||||
}
|
||||
|
||||
inline float LongitudinalPlanExt::Reader::getSpeedLimitOffset() const {
|
||||
return _reader.getDataField<float>(
|
||||
::capnp::bounded<3>() * ::capnp::ELEMENTS);
|
||||
}
|
||||
|
||||
inline float LongitudinalPlanExt::Builder::getSpeedLimitOffset() {
|
||||
return _builder.getDataField<float>(
|
||||
::capnp::bounded<3>() * ::capnp::ELEMENTS);
|
||||
}
|
||||
inline void LongitudinalPlanExt::Builder::setSpeedLimitOffset(float value) {
|
||||
_builder.setDataField<float>(
|
||||
::capnp::bounded<3>() * ::capnp::ELEMENTS, value);
|
||||
}
|
||||
|
||||
inline float LongitudinalPlanExt::Reader::getDistToSpeedLimit() const {
|
||||
return _reader.getDataField<float>(
|
||||
::capnp::bounded<4>() * ::capnp::ELEMENTS);
|
||||
}
|
||||
|
||||
inline float LongitudinalPlanExt::Builder::getDistToSpeedLimit() {
|
||||
return _builder.getDataField<float>(
|
||||
::capnp::bounded<4>() * ::capnp::ELEMENTS);
|
||||
}
|
||||
inline void LongitudinalPlanExt::Builder::setDistToSpeedLimit(float value) {
|
||||
_builder.setDataField<float>(
|
||||
::capnp::bounded<4>() * ::capnp::ELEMENTS, value);
|
||||
}
|
||||
|
||||
inline bool LongitudinalPlanExt::Reader::getIsMapSpeedLimit() const {
|
||||
return _reader.getDataField<bool>(
|
||||
::capnp::bounded<160>() * ::capnp::ELEMENTS);
|
||||
}
|
||||
|
||||
inline bool LongitudinalPlanExt::Builder::getIsMapSpeedLimit() {
|
||||
return _builder.getDataField<bool>(
|
||||
::capnp::bounded<160>() * ::capnp::ELEMENTS);
|
||||
}
|
||||
inline void LongitudinalPlanExt::Builder::setIsMapSpeedLimit(bool value) {
|
||||
_builder.setDataField<bool>(
|
||||
::capnp::bounded<160>() * ::capnp::ELEMENTS, value);
|
||||
}
|
||||
|
||||
inline bool LongitudinalPlanExt::Reader::getSpeedLimitPercOffset() const {
|
||||
return _reader.getDataField<bool>(
|
||||
::capnp::bounded<161>() * ::capnp::ELEMENTS);
|
||||
}
|
||||
|
||||
inline bool LongitudinalPlanExt::Builder::getSpeedLimitPercOffset() {
|
||||
return _builder.getDataField<bool>(
|
||||
::capnp::bounded<161>() * ::capnp::ELEMENTS);
|
||||
}
|
||||
inline void LongitudinalPlanExt::Builder::setSpeedLimitPercOffset(bool value) {
|
||||
_builder.setDataField<bool>(
|
||||
::capnp::bounded<161>() * ::capnp::ELEMENTS, value);
|
||||
}
|
||||
|
||||
inline float LongitudinalPlanExt::Reader::getSpeedLimitValueOffset() const {
|
||||
return _reader.getDataField<float>(
|
||||
::capnp::bounded<6>() * ::capnp::ELEMENTS);
|
||||
}
|
||||
|
||||
inline float LongitudinalPlanExt::Builder::getSpeedLimitValueOffset() {
|
||||
return _builder.getDataField<float>(
|
||||
::capnp::bounded<6>() * ::capnp::ELEMENTS);
|
||||
}
|
||||
inline void LongitudinalPlanExt::Builder::setSpeedLimitValueOffset(float value) {
|
||||
_builder.setDataField<float>(
|
||||
::capnp::bounded<6>() * ::capnp::ELEMENTS, value);
|
||||
}
|
||||
|
||||
inline float LongitudinalPlanExt::Reader::getDistToTurn() const {
|
||||
return _reader.getDataField<float>(
|
||||
::capnp::bounded<7>() * ::capnp::ELEMENTS);
|
||||
}
|
||||
|
||||
inline float LongitudinalPlanExt::Builder::getDistToTurn() {
|
||||
return _builder.getDataField<float>(
|
||||
::capnp::bounded<7>() * ::capnp::ELEMENTS);
|
||||
}
|
||||
inline void LongitudinalPlanExt::Builder::setDistToTurn(float value) {
|
||||
_builder.setDataField<float>(
|
||||
::capnp::bounded<7>() * ::capnp::ELEMENTS, value);
|
||||
}
|
||||
|
||||
inline float LongitudinalPlanExt::Reader::getTurnSpeed() const {
|
||||
return _reader.getDataField<float>(
|
||||
::capnp::bounded<8>() * ::capnp::ELEMENTS);
|
||||
}
|
||||
|
||||
inline float LongitudinalPlanExt::Builder::getTurnSpeed() {
|
||||
return _builder.getDataField<float>(
|
||||
::capnp::bounded<8>() * ::capnp::ELEMENTS);
|
||||
}
|
||||
inline void LongitudinalPlanExt::Builder::setTurnSpeed(float value) {
|
||||
_builder.setDataField<float>(
|
||||
::capnp::bounded<8>() * ::capnp::ELEMENTS, value);
|
||||
}
|
||||
|
||||
inline ::cereal::LongitudinalPlanExt::SpeedLimitControlState LongitudinalPlanExt::Reader::getTurnSpeedControlState() const {
|
||||
return _reader.getDataField< ::cereal::LongitudinalPlanExt::SpeedLimitControlState>(
|
||||
::capnp::bounded<11>() * ::capnp::ELEMENTS);
|
||||
}
|
||||
|
||||
inline ::cereal::LongitudinalPlanExt::SpeedLimitControlState LongitudinalPlanExt::Builder::getTurnSpeedControlState() {
|
||||
return _builder.getDataField< ::cereal::LongitudinalPlanExt::SpeedLimitControlState>(
|
||||
::capnp::bounded<11>() * ::capnp::ELEMENTS);
|
||||
}
|
||||
inline void LongitudinalPlanExt::Builder::setTurnSpeedControlState( ::cereal::LongitudinalPlanExt::SpeedLimitControlState value) {
|
||||
_builder.setDataField< ::cereal::LongitudinalPlanExt::SpeedLimitControlState>(
|
||||
::capnp::bounded<11>() * ::capnp::ELEMENTS, value);
|
||||
}
|
||||
|
||||
inline ::int16_t LongitudinalPlanExt::Reader::getTurnSign() const {
|
||||
return _reader.getDataField< ::int16_t>(
|
||||
::capnp::bounded<18>() * ::capnp::ELEMENTS);
|
||||
}
|
||||
|
||||
inline ::int16_t LongitudinalPlanExt::Builder::getTurnSign() {
|
||||
return _builder.getDataField< ::int16_t>(
|
||||
::capnp::bounded<18>() * ::capnp::ELEMENTS);
|
||||
}
|
||||
inline void LongitudinalPlanExt::Builder::setTurnSign( ::int16_t value) {
|
||||
_builder.setDataField< ::int16_t>(
|
||||
::capnp::bounded<18>() * ::capnp::ELEMENTS, value);
|
||||
}
|
||||
|
||||
inline bool LongitudinalPlanExt::Reader::getDpE2EIsBlended() const {
|
||||
return _reader.getDataField<bool>(
|
||||
::capnp::bounded<162>() * ::capnp::ELEMENTS);
|
||||
}
|
||||
|
||||
inline bool LongitudinalPlanExt::Builder::getDpE2EIsBlended() {
|
||||
return _builder.getDataField<bool>(
|
||||
::capnp::bounded<162>() * ::capnp::ELEMENTS);
|
||||
}
|
||||
inline void LongitudinalPlanExt::Builder::setDpE2EIsBlended(bool value) {
|
||||
_builder.setDataField<bool>(
|
||||
::capnp::bounded<162>() * ::capnp::ELEMENTS, value);
|
||||
}
|
||||
|
||||
inline ::cereal::LongitudinalPlanExt::LongitudinalPlanExtSource LongitudinalPlanExt::Reader::getLongitudinalPlanExtSource() const {
|
||||
return _reader.getDataField< ::cereal::LongitudinalPlanExt::LongitudinalPlanExtSource>(
|
||||
::capnp::bounded<19>() * ::capnp::ELEMENTS);
|
||||
::capnp::bounded<4>() * ::capnp::ELEMENTS);
|
||||
}
|
||||
|
||||
inline ::cereal::LongitudinalPlanExt::LongitudinalPlanExtSource LongitudinalPlanExt::Builder::getLongitudinalPlanExtSource() {
|
||||
return _builder.getDataField< ::cereal::LongitudinalPlanExt::LongitudinalPlanExtSource>(
|
||||
::capnp::bounded<19>() * ::capnp::ELEMENTS);
|
||||
::capnp::bounded<4>() * ::capnp::ELEMENTS);
|
||||
}
|
||||
inline void LongitudinalPlanExt::Builder::setLongitudinalPlanExtSource( ::cereal::LongitudinalPlanExt::LongitudinalPlanExtSource value) {
|
||||
_builder.setDataField< ::cereal::LongitudinalPlanExt::LongitudinalPlanExtSource>(
|
||||
::capnp::bounded<19>() * ::capnp::ELEMENTS, value);
|
||||
}
|
||||
|
||||
inline bool LongitudinalPlanExt::Reader::getDe2eIsEnabled() const {
|
||||
return _reader.getDataField<bool>(
|
||||
::capnp::bounded<163>() * ::capnp::ELEMENTS);
|
||||
}
|
||||
|
||||
inline bool LongitudinalPlanExt::Builder::getDe2eIsEnabled() {
|
||||
return _builder.getDataField<bool>(
|
||||
::capnp::bounded<163>() * ::capnp::ELEMENTS);
|
||||
}
|
||||
inline void LongitudinalPlanExt::Builder::setDe2eIsEnabled(bool value) {
|
||||
_builder.setDataField<bool>(
|
||||
::capnp::bounded<163>() * ::capnp::ELEMENTS, value);
|
||||
::capnp::bounded<4>() * ::capnp::ELEMENTS, value);
|
||||
}
|
||||
|
||||
inline bool LateralPlanExt::Reader::hasDPathWLinesX() const {
|
||||
|
||||
+146
-140
@@ -2202,7 +2202,7 @@ const ::capnp::_::RawSchema s_ddb169f01e102879 = {
|
||||
};
|
||||
#endif // !CAPNP_LITE
|
||||
CAPNP_DEFINE_ENUM(FrameType_ddb169f01e102879, ddb169f01e102879);
|
||||
static const ::capnp::_::AlignedData<30> b_d810b1e7705dd69c = {
|
||||
static const ::capnp::_::AlignedData<34> b_d810b1e7705dd69c = {
|
||||
{ 0, 0, 0, 0, 5, 0, 6, 0,
|
||||
156, 214, 93, 112, 231, 177, 16, 216,
|
||||
20, 0, 0, 0, 2, 0, 0, 0,
|
||||
@@ -2212,7 +2212,7 @@ static const ::capnp::_::AlignedData<30> b_d810b1e7705dd69c = {
|
||||
21, 0, 0, 0, 2, 1, 0, 0,
|
||||
33, 0, 0, 0, 7, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
29, 0, 0, 0, 79, 0, 0, 0,
|
||||
29, 0, 0, 0, 103, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
108, 111, 103, 46, 99, 97, 112, 110,
|
||||
@@ -2220,26 +2220,30 @@ static const ::capnp::_::AlignedData<30> b_d810b1e7705dd69c = {
|
||||
97, 116, 97, 46, 73, 109, 97, 103,
|
||||
101, 83, 101, 110, 115, 111, 114, 0,
|
||||
0, 0, 0, 0, 1, 0, 1, 0,
|
||||
12, 0, 0, 0, 1, 0, 2, 0,
|
||||
16, 0, 0, 0, 1, 0, 2, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
29, 0, 0, 0, 66, 0, 0, 0,
|
||||
41, 0, 0, 0, 66, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 0, 0, 0, 0, 0, 0,
|
||||
21, 0, 0, 0, 58, 0, 0, 0,
|
||||
33, 0, 0, 0, 58, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
2, 0, 0, 0, 0, 0, 0, 0,
|
||||
13, 0, 0, 0, 66, 0, 0, 0,
|
||||
25, 0, 0, 0, 66, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
3, 0, 0, 0, 0, 0, 0, 0,
|
||||
17, 0, 0, 0, 66, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
117, 110, 107, 110, 111, 119, 110, 0,
|
||||
97, 114, 48, 50, 51, 49, 0, 0,
|
||||
111, 120, 48, 51, 99, 49, 48, 0, }
|
||||
111, 120, 48, 51, 99, 49, 48, 0,
|
||||
111, 115, 48, 52, 99, 49, 48, 0, }
|
||||
};
|
||||
::capnp::word const* const bp_d810b1e7705dd69c = b_d810b1e7705dd69c.words;
|
||||
#if !CAPNP_LITE
|
||||
static const uint16_t m_d810b1e7705dd69c[] = {1, 2, 0};
|
||||
static const uint16_t m_d810b1e7705dd69c[] = {1, 3, 2, 0};
|
||||
const ::capnp::_::RawSchema s_d810b1e7705dd69c = {
|
||||
0xd810b1e7705dd69c, b_d810b1e7705dd69c.words, 30, nullptr, m_d810b1e7705dd69c,
|
||||
0, 3, nullptr, nullptr, nullptr, { &s_d810b1e7705dd69c, nullptr, nullptr, 0, 0, nullptr }
|
||||
0xd810b1e7705dd69c, b_d810b1e7705dd69c.words, 34, nullptr, m_d810b1e7705dd69c,
|
||||
0, 4, nullptr, nullptr, nullptr, { &s_d810b1e7705dd69c, nullptr, nullptr, 0, 0, nullptr }
|
||||
};
|
||||
#endif // !CAPNP_LITE
|
||||
CAPNP_DEFINE_ENUM(ImageSensor_d810b1e7705dd69c, d810b1e7705dd69c);
|
||||
@@ -4618,7 +4622,7 @@ const ::capnp::_::RawSchema s_b98c64ea27898ea0 = {
|
||||
0, 2, i_b98c64ea27898ea0, nullptr, nullptr, { &s_b98c64ea27898ea0, nullptr, nullptr, 0, 0, nullptr }
|
||||
};
|
||||
#endif // !CAPNP_LITE
|
||||
static const ::capnp::_::AlignedData<643> b_a7649e2575e4591e = {
|
||||
static const ::capnp::_::AlignedData<645> b_a7649e2575e4591e = {
|
||||
{ 0, 0, 0, 0, 5, 0, 6, 0,
|
||||
30, 89, 228, 117, 37, 158, 100, 167,
|
||||
10, 0, 0, 0, 1, 0, 9, 0,
|
||||
@@ -4656,14 +4660,14 @@ static const ::capnp::_::AlignedData<643> b_a7649e2575e4591e = {
|
||||
80, 97, 110, 100, 97, 67, 97, 110,
|
||||
83, 116, 97, 116, 101, 0, 0, 0,
|
||||
148, 0, 0, 0, 3, 0, 4, 0,
|
||||
29, 0, 0, 0, 0, 0, 0, 0,
|
||||
28, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 1, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
253, 3, 0, 0, 66, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
248, 3, 0, 0, 3, 0, 1, 0,
|
||||
4, 4, 0, 0, 2, 0, 1, 0,
|
||||
30, 0, 0, 0, 1, 0, 0, 0,
|
||||
29, 0, 0, 0, 1, 0, 0, 0,
|
||||
0, 0, 1, 0, 1, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 4, 0, 0, 66, 0, 0, 0,
|
||||
@@ -4677,244 +4681,244 @@ static const ::capnp::_::AlignedData<643> b_a7649e2575e4591e = {
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
4, 4, 0, 0, 3, 0, 1, 0,
|
||||
16, 4, 0, 0, 2, 0, 1, 0,
|
||||
22, 0, 0, 0, 65, 0, 0, 0,
|
||||
21, 0, 0, 0, 65, 0, 0, 0,
|
||||
0, 0, 1, 0, 3, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
13, 4, 0, 0, 130, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
12, 4, 0, 0, 3, 0, 1, 0,
|
||||
24, 4, 0, 0, 2, 0, 1, 0,
|
||||
1, 0, 0, 0, 66, 0, 0, 0,
|
||||
30, 0, 0, 0, 66, 0, 0, 0,
|
||||
0, 0, 1, 0, 4, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
21, 4, 0, 0, 186, 0, 0, 0,
|
||||
21, 4, 0, 0, 10, 1, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
24, 4, 0, 0, 3, 0, 1, 0,
|
||||
36, 4, 0, 0, 2, 0, 1, 0,
|
||||
32, 4, 0, 0, 3, 0, 1, 0,
|
||||
44, 4, 0, 0, 2, 0, 1, 0,
|
||||
31, 0, 0, 0, 67, 0, 0, 0,
|
||||
0, 0, 1, 0, 5, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
33, 4, 0, 0, 2, 1, 0, 0,
|
||||
41, 4, 0, 0, 2, 1, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
40, 4, 0, 0, 3, 0, 1, 0,
|
||||
52, 4, 0, 0, 2, 0, 1, 0,
|
||||
48, 4, 0, 0, 3, 0, 1, 0,
|
||||
60, 4, 0, 0, 2, 0, 1, 0,
|
||||
32, 0, 0, 0, 68, 0, 0, 0,
|
||||
0, 0, 1, 0, 6, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
49, 4, 0, 0, 138, 0, 0, 0,
|
||||
57, 4, 0, 0, 138, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
52, 4, 0, 0, 3, 0, 1, 0,
|
||||
64, 4, 0, 0, 2, 0, 1, 0,
|
||||
2, 0, 0, 0, 3, 0, 0, 0,
|
||||
60, 4, 0, 0, 3, 0, 1, 0,
|
||||
72, 4, 0, 0, 2, 0, 1, 0,
|
||||
1, 0, 0, 0, 3, 0, 0, 0,
|
||||
0, 0, 1, 0, 7, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
61, 4, 0, 0, 138, 0, 0, 0,
|
||||
69, 4, 0, 0, 138, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
64, 4, 0, 0, 3, 0, 1, 0,
|
||||
76, 4, 0, 0, 2, 0, 1, 0,
|
||||
3, 0, 0, 0, 4, 0, 0, 0,
|
||||
72, 4, 0, 0, 3, 0, 1, 0,
|
||||
84, 4, 0, 0, 2, 0, 1, 0,
|
||||
2, 0, 0, 0, 4, 0, 0, 0,
|
||||
0, 0, 1, 0, 8, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
73, 4, 0, 0, 138, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
76, 4, 0, 0, 3, 0, 1, 0,
|
||||
88, 4, 0, 0, 2, 0, 1, 0,
|
||||
4, 0, 0, 0, 5, 0, 0, 0,
|
||||
0, 0, 1, 0, 9, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
85, 4, 0, 0, 114, 0, 0, 0,
|
||||
81, 4, 0, 0, 138, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
84, 4, 0, 0, 3, 0, 1, 0,
|
||||
96, 4, 0, 0, 2, 0, 1, 0,
|
||||
5, 0, 0, 0, 5, 0, 0, 0,
|
||||
0, 0, 1, 0, 10, 0, 0, 0,
|
||||
3, 0, 0, 0, 5, 0, 0, 0,
|
||||
0, 0, 1, 0, 9, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
93, 4, 0, 0, 82, 0, 0, 0,
|
||||
93, 4, 0, 0, 114, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
92, 4, 0, 0, 3, 0, 1, 0,
|
||||
104, 4, 0, 0, 2, 0, 1, 0,
|
||||
4, 0, 0, 0, 5, 0, 0, 0,
|
||||
0, 0, 1, 0, 10, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
101, 4, 0, 0, 82, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
100, 4, 0, 0, 3, 0, 1, 0,
|
||||
112, 4, 0, 0, 2, 0, 1, 0,
|
||||
33, 0, 0, 0, 12, 0, 0, 0,
|
||||
0, 0, 1, 0, 11, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
101, 4, 0, 0, 178, 0, 0, 0,
|
||||
109, 4, 0, 0, 178, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
104, 4, 0, 0, 3, 0, 1, 0,
|
||||
116, 4, 0, 0, 2, 0, 1, 0,
|
||||
112, 4, 0, 0, 3, 0, 1, 0,
|
||||
124, 4, 0, 0, 2, 0, 1, 0,
|
||||
34, 0, 0, 0, 13, 0, 0, 0,
|
||||
0, 0, 1, 0, 12, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
113, 4, 0, 0, 186, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
116, 4, 0, 0, 3, 0, 1, 0,
|
||||
128, 4, 0, 0, 2, 0, 1, 0,
|
||||
6, 0, 0, 0, 69, 0, 0, 0,
|
||||
0, 0, 1, 0, 13, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
125, 4, 0, 0, 98, 0, 0, 0,
|
||||
121, 4, 0, 0, 186, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
124, 4, 0, 0, 3, 0, 1, 0,
|
||||
136, 4, 0, 0, 2, 0, 1, 0,
|
||||
25, 0, 0, 0, 14, 0, 0, 0,
|
||||
0, 0, 1, 0, 14, 0, 0, 0,
|
||||
5, 0, 0, 0, 69, 0, 0, 0,
|
||||
0, 0, 1, 0, 13, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
133, 4, 0, 0, 98, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
132, 4, 0, 0, 3, 0, 1, 0,
|
||||
144, 4, 0, 0, 2, 0, 1, 0,
|
||||
7, 0, 0, 0, 15, 0, 0, 0,
|
||||
0, 0, 1, 0, 15, 0, 0, 0,
|
||||
24, 0, 0, 0, 14, 0, 0, 0,
|
||||
0, 0, 1, 0, 14, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
141, 4, 0, 0, 98, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
140, 4, 0, 0, 3, 0, 1, 0,
|
||||
152, 4, 0, 0, 2, 0, 1, 0,
|
||||
8, 0, 0, 0, 70, 0, 0, 0,
|
||||
6, 0, 0, 0, 15, 0, 0, 0,
|
||||
0, 0, 1, 0, 15, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
149, 4, 0, 0, 98, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
148, 4, 0, 0, 3, 0, 1, 0,
|
||||
160, 4, 0, 0, 2, 0, 1, 0,
|
||||
7, 0, 0, 0, 70, 0, 0, 0,
|
||||
0, 0, 1, 0, 16, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
149, 4, 0, 0, 138, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
152, 4, 0, 0, 3, 0, 1, 0,
|
||||
164, 4, 0, 0, 2, 0, 1, 0,
|
||||
9, 0, 0, 0, 8, 0, 0, 0,
|
||||
0, 0, 1, 0, 17, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
161, 4, 0, 0, 58, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
156, 4, 0, 0, 3, 0, 1, 0,
|
||||
168, 4, 0, 0, 2, 0, 1, 0,
|
||||
10, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 1, 0, 18, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
165, 4, 0, 0, 58, 0, 0, 0,
|
||||
157, 4, 0, 0, 138, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
160, 4, 0, 0, 3, 0, 1, 0,
|
||||
188, 4, 0, 0, 2, 0, 1, 0,
|
||||
23, 0, 0, 0, 9, 0, 0, 0,
|
||||
172, 4, 0, 0, 2, 0, 1, 0,
|
||||
8, 0, 0, 0, 8, 0, 0, 0,
|
||||
0, 0, 1, 0, 17, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
169, 4, 0, 0, 58, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
164, 4, 0, 0, 3, 0, 1, 0,
|
||||
176, 4, 0, 0, 2, 0, 1, 0,
|
||||
9, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 1, 0, 18, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
173, 4, 0, 0, 58, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
168, 4, 0, 0, 3, 0, 1, 0,
|
||||
196, 4, 0, 0, 2, 0, 1, 0,
|
||||
22, 0, 0, 0, 9, 0, 0, 0,
|
||||
0, 0, 1, 0, 19, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
185, 4, 0, 0, 130, 0, 0, 0,
|
||||
193, 4, 0, 0, 130, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
184, 4, 0, 0, 3, 0, 1, 0,
|
||||
196, 4, 0, 0, 2, 0, 1, 0,
|
||||
192, 4, 0, 0, 3, 0, 1, 0,
|
||||
204, 4, 0, 0, 2, 0, 1, 0,
|
||||
35, 0, 0, 0, 20, 0, 0, 0,
|
||||
0, 0, 1, 0, 20, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
193, 4, 0, 0, 178, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
196, 4, 0, 0, 3, 0, 1, 0,
|
||||
208, 4, 0, 0, 2, 0, 1, 0,
|
||||
16, 0, 0, 0, 21, 0, 0, 0,
|
||||
0, 0, 1, 0, 21, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
205, 4, 0, 0, 114, 0, 0, 0,
|
||||
201, 4, 0, 0, 178, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
204, 4, 0, 0, 3, 0, 1, 0,
|
||||
216, 4, 0, 0, 2, 0, 1, 0,
|
||||
11, 0, 0, 0, 71, 0, 0, 0,
|
||||
0, 0, 1, 0, 22, 0, 0, 0,
|
||||
15, 0, 0, 0, 21, 0, 0, 0,
|
||||
0, 0, 1, 0, 21, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
213, 4, 0, 0, 114, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
212, 4, 0, 0, 3, 0, 1, 0,
|
||||
224, 4, 0, 0, 2, 0, 1, 0,
|
||||
27, 0, 0, 0, 22, 0, 0, 0,
|
||||
10, 0, 0, 0, 71, 0, 0, 0,
|
||||
0, 0, 1, 0, 22, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
221, 4, 0, 0, 114, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
220, 4, 0, 0, 3, 0, 1, 0,
|
||||
232, 4, 0, 0, 2, 0, 1, 0,
|
||||
26, 0, 0, 0, 22, 0, 0, 0,
|
||||
0, 0, 1, 0, 23, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
221, 4, 0, 0, 178, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
224, 4, 0, 0, 3, 0, 1, 0,
|
||||
236, 4, 0, 0, 2, 0, 1, 0,
|
||||
24, 0, 0, 0, 12, 0, 0, 0,
|
||||
0, 0, 1, 0, 24, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
233, 4, 0, 0, 130, 0, 0, 0,
|
||||
229, 4, 0, 0, 178, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
232, 4, 0, 0, 3, 0, 1, 0,
|
||||
244, 4, 0, 0, 2, 0, 1, 0,
|
||||
12, 0, 0, 0, 13, 0, 0, 0,
|
||||
0, 0, 1, 0, 25, 0, 0, 0,
|
||||
23, 0, 0, 0, 12, 0, 0, 0,
|
||||
0, 0, 1, 0, 24, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
241, 4, 0, 0, 114, 0, 0, 0,
|
||||
241, 4, 0, 0, 130, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
240, 4, 0, 0, 3, 0, 1, 0,
|
||||
252, 4, 0, 0, 2, 0, 1, 0,
|
||||
11, 0, 0, 0, 13, 0, 0, 0,
|
||||
0, 0, 1, 0, 25, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
249, 4, 0, 0, 114, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
248, 4, 0, 0, 3, 0, 1, 0,
|
||||
4, 5, 0, 0, 2, 0, 1, 0,
|
||||
36, 0, 0, 0, 14, 0, 0, 0,
|
||||
0, 0, 1, 0, 26, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
249, 4, 0, 0, 186, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
252, 4, 0, 0, 3, 0, 1, 0,
|
||||
8, 5, 0, 0, 2, 0, 1, 0,
|
||||
26, 0, 0, 0, 23, 0, 0, 0,
|
||||
0, 0, 1, 0, 27, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
5, 5, 0, 0, 98, 0, 0, 0,
|
||||
1, 5, 0, 0, 186, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
4, 5, 0, 0, 3, 0, 1, 0,
|
||||
16, 5, 0, 0, 2, 0, 1, 0,
|
||||
13, 0, 0, 0, 9, 0, 0, 0,
|
||||
0, 0, 1, 0, 28, 0, 0, 0,
|
||||
25, 0, 0, 0, 23, 0, 0, 0,
|
||||
0, 0, 1, 0, 27, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
13, 5, 0, 0, 74, 0, 0, 0,
|
||||
13, 5, 0, 0, 98, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
12, 5, 0, 0, 3, 0, 1, 0,
|
||||
24, 5, 0, 0, 2, 0, 1, 0,
|
||||
19, 0, 0, 0, 1, 0, 0, 0,
|
||||
0, 0, 1, 0, 29, 0, 0, 0,
|
||||
12, 0, 0, 0, 9, 0, 0, 0,
|
||||
0, 0, 1, 0, 28, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
21, 5, 0, 0, 82, 0, 0, 0,
|
||||
21, 5, 0, 0, 74, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
20, 5, 0, 0, 3, 0, 1, 0,
|
||||
32, 5, 0, 0, 2, 0, 1, 0,
|
||||
20, 0, 0, 0, 2, 0, 0, 0,
|
||||
0, 0, 1, 0, 30, 0, 0, 0,
|
||||
18, 0, 0, 0, 1, 0, 0, 0,
|
||||
0, 0, 1, 0, 29, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
29, 5, 0, 0, 82, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
28, 5, 0, 0, 3, 0, 1, 0,
|
||||
40, 5, 0, 0, 2, 0, 1, 0,
|
||||
21, 0, 0, 0, 3, 0, 0, 0,
|
||||
0, 0, 1, 0, 31, 0, 0, 0,
|
||||
19, 0, 0, 0, 2, 0, 0, 0,
|
||||
0, 0, 1, 0, 30, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
37, 5, 0, 0, 82, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
36, 5, 0, 0, 3, 0, 1, 0,
|
||||
48, 5, 0, 0, 2, 0, 1, 0,
|
||||
28, 0, 0, 0, 224, 1, 0, 0,
|
||||
20, 0, 0, 0, 3, 0, 0, 0,
|
||||
0, 0, 1, 0, 31, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
45, 5, 0, 0, 82, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
44, 5, 0, 0, 3, 0, 1, 0,
|
||||
56, 5, 0, 0, 2, 0, 1, 0,
|
||||
27, 0, 0, 0, 224, 1, 0, 0,
|
||||
0, 0, 1, 0, 32, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
45, 5, 0, 0, 178, 0, 0, 0,
|
||||
53, 5, 0, 0, 178, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
48, 5, 0, 0, 3, 0, 1, 0,
|
||||
60, 5, 0, 0, 2, 0, 1, 0,
|
||||
15, 0, 0, 0, 31, 0, 0, 0,
|
||||
56, 5, 0, 0, 3, 0, 1, 0,
|
||||
68, 5, 0, 0, 2, 0, 1, 0,
|
||||
14, 0, 0, 0, 31, 0, 0, 0,
|
||||
0, 0, 1, 0, 33, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
57, 5, 0, 0, 178, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
60, 5, 0, 0, 3, 0, 1, 0,
|
||||
72, 5, 0, 0, 2, 0, 1, 0,
|
||||
14, 0, 0, 0, 61, 0, 0, 0,
|
||||
0, 0, 1, 0, 34, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
69, 5, 0, 0, 114, 0, 0, 0,
|
||||
65, 5, 0, 0, 178, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
68, 5, 0, 0, 3, 0, 1, 0,
|
||||
80, 5, 0, 0, 2, 0, 1, 0,
|
||||
17, 0, 0, 0, 16, 0, 0, 0,
|
||||
0, 0, 1, 0, 35, 0, 0, 0,
|
||||
13, 0, 0, 0, 61, 0, 0, 0,
|
||||
0, 0, 1, 0, 34, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
77, 5, 0, 0, 98, 0, 0, 0,
|
||||
77, 5, 0, 0, 114, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
76, 5, 0, 0, 3, 0, 1, 0,
|
||||
88, 5, 0, 0, 2, 0, 1, 0,
|
||||
18, 0, 0, 0, 17, 0, 0, 0,
|
||||
0, 0, 1, 0, 36, 0, 0, 0,
|
||||
16, 0, 0, 0, 16, 0, 0, 0,
|
||||
0, 0, 1, 0, 35, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
85, 5, 0, 0, 98, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
84, 5, 0, 0, 3, 0, 1, 0,
|
||||
96, 5, 0, 0, 2, 0, 1, 0,
|
||||
17, 0, 0, 0, 17, 0, 0, 0,
|
||||
0, 0, 1, 0, 36, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
93, 5, 0, 0, 98, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
92, 5, 0, 0, 3, 0, 1, 0,
|
||||
104, 5, 0, 0, 2, 0, 1, 0,
|
||||
118, 111, 108, 116, 97, 103, 101, 0,
|
||||
8, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
@@ -4951,7 +4955,9 @@ static const ::capnp::_::AlignedData<643> b_a7649e2575e4591e = {
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
103, 97, 115, 73, 110, 116, 101, 114,
|
||||
99, 101, 112, 116, 111, 114, 68, 101,
|
||||
116, 101, 99, 116, 101, 100, 0, 0,
|
||||
116, 101, 99, 116, 101, 100, 68, 69,
|
||||
80, 82, 69, 67, 65, 84, 69, 68,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
@@ -5277,7 +5283,7 @@ static const ::capnp::_::RawSchema* const d_a7649e2575e4591e[] = {
|
||||
static const uint16_t m_a7649e2575e4591e[] = {23, 29, 30, 31, 3, 1, 28, 11, 34, 15, 18, 4, 9, 21, 6, 22, 13, 2, 25, 10, 16, 7, 14, 27, 26, 20, 32, 19, 24, 35, 36, 33, 5, 8, 17, 12, 0};
|
||||
static const uint16_t i_a7649e2575e4591e[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36};
|
||||
const ::capnp::_::RawSchema s_a7649e2575e4591e = {
|
||||
0xa7649e2575e4591e, b_a7649e2575e4591e.words, 643, d_a7649e2575e4591e, m_a7649e2575e4591e,
|
||||
0xa7649e2575e4591e, b_a7649e2575e4591e.words, 645, d_a7649e2575e4591e, m_a7649e2575e4591e,
|
||||
7, 37, i_a7649e2575e4591e, nullptr, nullptr, { &s_a7649e2575e4591e, nullptr, nullptr, 0, 0, nullptr }
|
||||
};
|
||||
#endif // !CAPNP_LITE
|
||||
@@ -27600,7 +27606,7 @@ static const ::capnp::_::AlignedData<2162> b_d314cfd957229c11 = {
|
||||
37, 0, 189, 255, 0, 0, 0, 0,
|
||||
0, 0, 1, 0, 68, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
61, 17, 0, 0, 82, 0, 0, 0,
|
||||
61, 17, 0, 0, 106, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
60, 17, 0, 0, 3, 0, 1, 0,
|
||||
88, 17, 0, 0, 2, 0, 1, 0,
|
||||
@@ -28704,8 +28710,8 @@ static const ::capnp::_::AlignedData<2162> b_d314cfd957229c11 = {
|
||||
1, 0, 1, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
99, 97, 114, 69, 118, 101, 110, 116,
|
||||
115, 0, 0, 0, 0, 0, 0, 0,
|
||||
111, 110, 114, 111, 97, 100, 69, 118,
|
||||
101, 110, 116, 115, 0, 0, 0, 0,
|
||||
14, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
@@ -29358,7 +29364,7 @@ static const ::capnp::_::RawSchema* const d_d314cfd957229c11[] = {
|
||||
&s_fe346a9de48d9b50,
|
||||
&s_fe35ad896ffaeacf,
|
||||
};
|
||||
static const uint16_t m_d314cfd957229c11[] = {98, 101, 30, 20, 55, 42, 60, 63, 5, 23, 68, 69, 22, 28, 35, 7, 110, 111, 112, 113, 114, 115, 116, 124, 125, 126, 6, 70, 87, 76, 71, 59, 92, 85, 26, 10, 91, 21, 48, 3, 41, 40, 99, 100, 1, 65, 64, 109, 32, 96, 19, 8, 46, 25, 72, 51, 44, 37, 107, 62, 36, 61, 94, 16, 14, 122, 119, 120, 117, 121, 118, 49, 18, 0, 24, 108, 95, 78, 105, 103, 9, 75, 82, 104, 83, 38, 84, 27, 54, 58, 56, 47, 53, 45, 12, 81, 80, 33, 89, 90, 31, 13, 2, 86, 15, 17, 4, 11, 73, 97, 123, 52, 66, 43, 34, 39, 102, 57, 50, 106, 79, 93, 67, 74, 88, 77, 29};
|
||||
static const uint16_t m_d314cfd957229c11[] = {98, 101, 30, 20, 55, 42, 60, 63, 5, 23, 69, 22, 28, 35, 7, 110, 111, 112, 113, 114, 115, 116, 124, 125, 126, 6, 70, 87, 76, 71, 59, 92, 85, 26, 10, 91, 21, 48, 3, 41, 40, 99, 100, 1, 65, 64, 109, 32, 96, 19, 8, 46, 25, 72, 51, 44, 37, 107, 62, 36, 61, 94, 16, 14, 122, 119, 120, 117, 121, 118, 49, 18, 0, 24, 108, 95, 78, 105, 103, 9, 75, 82, 104, 83, 38, 84, 27, 68, 54, 58, 56, 47, 53, 45, 12, 81, 80, 33, 89, 90, 31, 13, 2, 86, 15, 17, 4, 11, 73, 97, 123, 52, 66, 43, 34, 39, 102, 57, 50, 106, 79, 93, 67, 74, 88, 77, 29};
|
||||
static const uint16_t i_d314cfd957229c11[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 0, 67};
|
||||
const ::capnp::_::RawSchema s_d314cfd957229c11 = {
|
||||
0xd314cfd957229c11, b_d314cfd957229c11.words, 2162, d_d314cfd957229c11, m_d314cfd957229c11,
|
||||
|
||||
+38
-37
@@ -58,6 +58,7 @@ enum class ImageSensor_d810b1e7705dd69c: uint16_t {
|
||||
UNKNOWN,
|
||||
AR0231,
|
||||
OX03C10,
|
||||
OS04C10,
|
||||
};
|
||||
CAPNP_DECLARE_ENUM(ImageSensor, d810b1e7705dd69c);
|
||||
CAPNP_DECLARE_SCHEMA(bcc3efbac41d2048);
|
||||
@@ -2402,7 +2403,7 @@ struct Event {
|
||||
LATERAL_PLAN,
|
||||
KALMAN_ODOMETRY_D_E_P_R_E_C_A_T_E_D,
|
||||
THUMBNAIL,
|
||||
CAR_EVENTS,
|
||||
ONROAD_EVENTS,
|
||||
CAR_PARAMS,
|
||||
DRIVER_CAMERA_STATE,
|
||||
DRIVER_MONITORING_STATE,
|
||||
@@ -5443,7 +5444,7 @@ public:
|
||||
|
||||
inline bool getControlsAllowed() const;
|
||||
|
||||
inline bool getGasInterceptorDetected() const;
|
||||
inline bool getGasInterceptorDetectedDEPRECATED() const;
|
||||
|
||||
inline bool getStartedSignalDetectedDEPRECATED() const;
|
||||
|
||||
@@ -5553,8 +5554,8 @@ public:
|
||||
inline bool getControlsAllowed();
|
||||
inline void setControlsAllowed(bool value);
|
||||
|
||||
inline bool getGasInterceptorDetected();
|
||||
inline void setGasInterceptorDetected(bool value);
|
||||
inline bool getGasInterceptorDetectedDEPRECATED();
|
||||
inline void setGasInterceptorDetectedDEPRECATED(bool value);
|
||||
|
||||
inline bool getStartedSignalDetectedDEPRECATED();
|
||||
inline void setStartedSignalDetectedDEPRECATED(bool value);
|
||||
@@ -18844,9 +18845,9 @@ public:
|
||||
|
||||
inline bool getValid() const;
|
||||
|
||||
inline bool isCarEvents() const;
|
||||
inline bool hasCarEvents() const;
|
||||
inline ::capnp::List< ::cereal::CarEvent, ::capnp::Kind::STRUCT>::Reader getCarEvents() const;
|
||||
inline bool isOnroadEvents() const;
|
||||
inline bool hasOnroadEvents() const;
|
||||
inline ::capnp::List< ::cereal::CarEvent, ::capnp::Kind::STRUCT>::Reader getOnroadEvents() const;
|
||||
|
||||
inline bool isCarParams() const;
|
||||
inline bool hasCarParams() const;
|
||||
@@ -19643,13 +19644,13 @@ public:
|
||||
inline bool getValid();
|
||||
inline void setValid(bool value);
|
||||
|
||||
inline bool isCarEvents();
|
||||
inline bool hasCarEvents();
|
||||
inline ::capnp::List< ::cereal::CarEvent, ::capnp::Kind::STRUCT>::Builder getCarEvents();
|
||||
inline void setCarEvents( ::capnp::List< ::cereal::CarEvent, ::capnp::Kind::STRUCT>::Reader value);
|
||||
inline ::capnp::List< ::cereal::CarEvent, ::capnp::Kind::STRUCT>::Builder initCarEvents(unsigned int size);
|
||||
inline void adoptCarEvents(::capnp::Orphan< ::capnp::List< ::cereal::CarEvent, ::capnp::Kind::STRUCT>>&& value);
|
||||
inline ::capnp::Orphan< ::capnp::List< ::cereal::CarEvent, ::capnp::Kind::STRUCT>> disownCarEvents();
|
||||
inline bool isOnroadEvents();
|
||||
inline bool hasOnroadEvents();
|
||||
inline ::capnp::List< ::cereal::CarEvent, ::capnp::Kind::STRUCT>::Builder getOnroadEvents();
|
||||
inline void setOnroadEvents( ::capnp::List< ::cereal::CarEvent, ::capnp::Kind::STRUCT>::Reader value);
|
||||
inline ::capnp::List< ::cereal::CarEvent, ::capnp::Kind::STRUCT>::Builder initOnroadEvents(unsigned int size);
|
||||
inline void adoptOnroadEvents(::capnp::Orphan< ::capnp::List< ::cereal::CarEvent, ::capnp::Kind::STRUCT>>&& value);
|
||||
inline ::capnp::Orphan< ::capnp::List< ::cereal::CarEvent, ::capnp::Kind::STRUCT>> disownOnroadEvents();
|
||||
|
||||
inline bool isCarParams();
|
||||
inline bool hasCarParams();
|
||||
@@ -25183,16 +25184,16 @@ inline void PandaState::Builder::setControlsAllowed(bool value) {
|
||||
::capnp::bounded<65>() * ::capnp::ELEMENTS, value);
|
||||
}
|
||||
|
||||
inline bool PandaState::Reader::getGasInterceptorDetected() const {
|
||||
inline bool PandaState::Reader::getGasInterceptorDetectedDEPRECATED() const {
|
||||
return _reader.getDataField<bool>(
|
||||
::capnp::bounded<66>() * ::capnp::ELEMENTS);
|
||||
}
|
||||
|
||||
inline bool PandaState::Builder::getGasInterceptorDetected() {
|
||||
inline bool PandaState::Builder::getGasInterceptorDetectedDEPRECATED() {
|
||||
return _builder.getDataField<bool>(
|
||||
::capnp::bounded<66>() * ::capnp::ELEMENTS);
|
||||
}
|
||||
inline void PandaState::Builder::setGasInterceptorDetected(bool value) {
|
||||
inline void PandaState::Builder::setGasInterceptorDetectedDEPRECATED(bool value) {
|
||||
_builder.setDataField<bool>(
|
||||
::capnp::bounded<66>() * ::capnp::ELEMENTS, value);
|
||||
}
|
||||
@@ -49623,55 +49624,55 @@ inline void Event::Builder::setValid(bool value) {
|
||||
::capnp::bounded<80>() * ::capnp::ELEMENTS, value, true);
|
||||
}
|
||||
|
||||
inline bool Event::Reader::isCarEvents() const {
|
||||
return which() == Event::CAR_EVENTS;
|
||||
inline bool Event::Reader::isOnroadEvents() const {
|
||||
return which() == Event::ONROAD_EVENTS;
|
||||
}
|
||||
inline bool Event::Builder::isCarEvents() {
|
||||
return which() == Event::CAR_EVENTS;
|
||||
inline bool Event::Builder::isOnroadEvents() {
|
||||
return which() == Event::ONROAD_EVENTS;
|
||||
}
|
||||
inline bool Event::Reader::hasCarEvents() const {
|
||||
if (which() != Event::CAR_EVENTS) return false;
|
||||
inline bool Event::Reader::hasOnroadEvents() const {
|
||||
if (which() != Event::ONROAD_EVENTS) return false;
|
||||
return !_reader.getPointerField(
|
||||
::capnp::bounded<0>() * ::capnp::POINTERS).isNull();
|
||||
}
|
||||
inline bool Event::Builder::hasCarEvents() {
|
||||
if (which() != Event::CAR_EVENTS) return false;
|
||||
inline bool Event::Builder::hasOnroadEvents() {
|
||||
if (which() != Event::ONROAD_EVENTS) return false;
|
||||
return !_builder.getPointerField(
|
||||
::capnp::bounded<0>() * ::capnp::POINTERS).isNull();
|
||||
}
|
||||
inline ::capnp::List< ::cereal::CarEvent, ::capnp::Kind::STRUCT>::Reader Event::Reader::getCarEvents() const {
|
||||
KJ_IREQUIRE((which() == Event::CAR_EVENTS),
|
||||
inline ::capnp::List< ::cereal::CarEvent, ::capnp::Kind::STRUCT>::Reader Event::Reader::getOnroadEvents() const {
|
||||
KJ_IREQUIRE((which() == Event::ONROAD_EVENTS),
|
||||
"Must check which() before get()ing a union member.");
|
||||
return ::capnp::_::PointerHelpers< ::capnp::List< ::cereal::CarEvent, ::capnp::Kind::STRUCT>>::get(_reader.getPointerField(
|
||||
::capnp::bounded<0>() * ::capnp::POINTERS));
|
||||
}
|
||||
inline ::capnp::List< ::cereal::CarEvent, ::capnp::Kind::STRUCT>::Builder Event::Builder::getCarEvents() {
|
||||
KJ_IREQUIRE((which() == Event::CAR_EVENTS),
|
||||
inline ::capnp::List< ::cereal::CarEvent, ::capnp::Kind::STRUCT>::Builder Event::Builder::getOnroadEvents() {
|
||||
KJ_IREQUIRE((which() == Event::ONROAD_EVENTS),
|
||||
"Must check which() before get()ing a union member.");
|
||||
return ::capnp::_::PointerHelpers< ::capnp::List< ::cereal::CarEvent, ::capnp::Kind::STRUCT>>::get(_builder.getPointerField(
|
||||
::capnp::bounded<0>() * ::capnp::POINTERS));
|
||||
}
|
||||
inline void Event::Builder::setCarEvents( ::capnp::List< ::cereal::CarEvent, ::capnp::Kind::STRUCT>::Reader value) {
|
||||
inline void Event::Builder::setOnroadEvents( ::capnp::List< ::cereal::CarEvent, ::capnp::Kind::STRUCT>::Reader value) {
|
||||
_builder.setDataField<Event::Which>(
|
||||
::capnp::bounded<4>() * ::capnp::ELEMENTS, Event::CAR_EVENTS);
|
||||
::capnp::bounded<4>() * ::capnp::ELEMENTS, Event::ONROAD_EVENTS);
|
||||
::capnp::_::PointerHelpers< ::capnp::List< ::cereal::CarEvent, ::capnp::Kind::STRUCT>>::set(_builder.getPointerField(
|
||||
::capnp::bounded<0>() * ::capnp::POINTERS), value);
|
||||
}
|
||||
inline ::capnp::List< ::cereal::CarEvent, ::capnp::Kind::STRUCT>::Builder Event::Builder::initCarEvents(unsigned int size) {
|
||||
inline ::capnp::List< ::cereal::CarEvent, ::capnp::Kind::STRUCT>::Builder Event::Builder::initOnroadEvents(unsigned int size) {
|
||||
_builder.setDataField<Event::Which>(
|
||||
::capnp::bounded<4>() * ::capnp::ELEMENTS, Event::CAR_EVENTS);
|
||||
::capnp::bounded<4>() * ::capnp::ELEMENTS, Event::ONROAD_EVENTS);
|
||||
return ::capnp::_::PointerHelpers< ::capnp::List< ::cereal::CarEvent, ::capnp::Kind::STRUCT>>::init(_builder.getPointerField(
|
||||
::capnp::bounded<0>() * ::capnp::POINTERS), size);
|
||||
}
|
||||
inline void Event::Builder::adoptCarEvents(
|
||||
inline void Event::Builder::adoptOnroadEvents(
|
||||
::capnp::Orphan< ::capnp::List< ::cereal::CarEvent, ::capnp::Kind::STRUCT>>&& value) {
|
||||
_builder.setDataField<Event::Which>(
|
||||
::capnp::bounded<4>() * ::capnp::ELEMENTS, Event::CAR_EVENTS);
|
||||
::capnp::bounded<4>() * ::capnp::ELEMENTS, Event::ONROAD_EVENTS);
|
||||
::capnp::_::PointerHelpers< ::capnp::List< ::cereal::CarEvent, ::capnp::Kind::STRUCT>>::adopt(_builder.getPointerField(
|
||||
::capnp::bounded<0>() * ::capnp::POINTERS), kj::mv(value));
|
||||
}
|
||||
inline ::capnp::Orphan< ::capnp::List< ::cereal::CarEvent, ::capnp::Kind::STRUCT>> Event::Builder::disownCarEvents() {
|
||||
KJ_IREQUIRE((which() == Event::CAR_EVENTS),
|
||||
inline ::capnp::Orphan< ::capnp::List< ::cereal::CarEvent, ::capnp::Kind::STRUCT>> Event::Builder::disownOnroadEvents() {
|
||||
KJ_IREQUIRE((which() == Event::ONROAD_EVENTS),
|
||||
"Must check which() before get()ing a union member.");
|
||||
return ::capnp::_::PointerHelpers< ::capnp::List< ::cereal::CarEvent, ::capnp::Kind::STRUCT>>::disown(_builder.getPointerField(
|
||||
::capnp::bounded<0>() * ::capnp::POINTERS));
|
||||
|
||||
Binary file not shown.
+3
-2
@@ -166,6 +166,7 @@ struct FrameData {
|
||||
unknown @0;
|
||||
ar0231 @1;
|
||||
ox03c10 @2;
|
||||
os04c10 @3;
|
||||
}
|
||||
|
||||
frameLengthDEPRECATED @3 :Int32;
|
||||
@@ -408,7 +409,6 @@ struct DeviceState @0xa4d8b5af2aa492eb {
|
||||
|
||||
struct PandaState @0xa7649e2575e4591e {
|
||||
ignitionLine @2 :Bool;
|
||||
gasInterceptorDetected @4 :Bool;
|
||||
rxBufferOverflow @7 :UInt32;
|
||||
txBufferOverflow @8 :UInt32;
|
||||
gmlanSendErrs @9 :UInt32;
|
||||
@@ -541,6 +541,7 @@ struct PandaState @0xa7649e2575e4591e {
|
||||
}
|
||||
}
|
||||
|
||||
gasInterceptorDetectedDEPRECATED @4 :Bool;
|
||||
startedSignalDetectedDEPRECATED @5 :Bool;
|
||||
hasGpsDEPRECATED @6 :Bool;
|
||||
fanSpeedRpmDEPRECATED @11 :UInt16;
|
||||
@@ -2226,7 +2227,7 @@ struct Event {
|
||||
liveTorqueParameters @94 :LiveTorqueParametersData;
|
||||
cameraOdometry @63 :CameraOdometry;
|
||||
thumbnail @66: Thumbnail;
|
||||
carEvents @68: List(Car.CarEvent);
|
||||
onroadEvents @68: List(Car.CarEvent);
|
||||
carParams @69: Car.CarParams;
|
||||
driverMonitoringState @71: DriverMonitoringState;
|
||||
liveLocationKalman @72 :LiveLocationKalman;
|
||||
|
||||
@@ -41,10 +41,13 @@ def log_from_bytes(dat: bytes) -> capnp.lib.capnp._DynamicStructReader:
|
||||
return msg
|
||||
|
||||
|
||||
def new_message(service: Optional[str] = None, size: Optional[int] = None) -> capnp.lib.capnp._DynamicStructBuilder:
|
||||
dat = log.Event.new_message()
|
||||
dat.logMonoTime = int(time.monotonic() * 1e9)
|
||||
dat.valid = True
|
||||
def new_message(service: Optional[str], size: Optional[int] = None, **kwargs) -> capnp.lib.capnp._DynamicStructBuilder:
|
||||
args = {
|
||||
'valid': False,
|
||||
'logMonoTime': int(time.monotonic() * 1e9),
|
||||
**kwargs
|
||||
}
|
||||
dat = log.Event.new_message(**args)
|
||||
if service is not None:
|
||||
if size is None:
|
||||
dat.init(service)
|
||||
@@ -154,7 +157,7 @@ def recv_one_retry(sock: SubSocket) -> capnp.lib.capnp._DynamicStructReader:
|
||||
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"):
|
||||
ignore_valid: 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}
|
||||
@@ -174,6 +177,7 @@ class SubMaster:
|
||||
|
||||
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
|
||||
self.ignore_valid = [] if ignore_valid is None else ignore_valid
|
||||
self.simulation = bool(int(os.getenv("SIMULATION", "0")))
|
||||
|
||||
for s in services:
|
||||
@@ -187,9 +191,10 @@ class SubMaster:
|
||||
except capnp.lib.capnp.KjException: # pylint: disable=c-extension-no-member
|
||||
data = new_message(s, 0) # lists
|
||||
|
||||
self.data[s] = getattr(data, s)
|
||||
self.data[s] = getattr(data.as_reader(), s)
|
||||
self.logMonoTime[s] = 0
|
||||
self.valid[s] = data.valid
|
||||
# TODO: this should default to False
|
||||
self.valid[s] = True
|
||||
|
||||
def __getitem__(self, s: str) -> capnp.lib.capnp._DynamicStructReader:
|
||||
return self.data[s]
|
||||
@@ -266,7 +271,7 @@ class SubMaster:
|
||||
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)
|
||||
return all(self.valid[s] for s in service_list if s not in self.ignore_valid)
|
||||
|
||||
def all_checks(self, service_list=None) -> bool:
|
||||
if service_list is None: # check all
|
||||
|
||||
Binary file not shown.
+1660
-432
File diff suppressed because it is too large
Load Diff
Binary file not shown.
+1
-1
@@ -44,7 +44,7 @@ static std::map<std::string, service> services = {
|
||||
{ "cameraOdometry", {"cameraOdometry", 8038, true, 20, 5}},
|
||||
{ "lateralPlan", {"lateralPlan", 8039, true, 20, 5}},
|
||||
{ "thumbnail", {"thumbnail", 8040, true, 0, 1}},
|
||||
{ "carEvents", {"carEvents", 8041, true, 1, 1}},
|
||||
{ "onroadEvents", {"onroadEvents", 8041, true, 1, 1}},
|
||||
{ "carParams", {"carParams", 8042, true, 0, 1}},
|
||||
{ "roadCameraState", {"roadCameraState", 8043, true, 20, 20}},
|
||||
{ "driverCameraState", {"driverCameraState", 8044, true, 20, 20}},
|
||||
|
||||
+1
-1
@@ -60,7 +60,7 @@ services: dict[str, tuple] = {
|
||||
"cameraOdometry": (True, 20., 5),
|
||||
"lateralPlan": (True, 20., 5),
|
||||
"thumbnail": (True, 0.2, 1),
|
||||
"carEvents": (True, 1., 1),
|
||||
"onroadEvents": (True, 1., 1),
|
||||
"carParams": (True, 0.02, 1),
|
||||
"roadCameraState": (True, 20., 20),
|
||||
"driverCameraState": (True, 20., 20),
|
||||
|
||||
+3181
-1356
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@@ -2,7 +2,7 @@ import jwt
|
||||
import os
|
||||
import requests
|
||||
from datetime import datetime, timedelta
|
||||
from openpilot.common.basedir import PERSIST
|
||||
from openpilot.system.hardware.hw import Paths
|
||||
from openpilot.system.version import get_version
|
||||
|
||||
API_HOST = os.getenv('API_HOST', 'https://api.commadotai.com')
|
||||
@@ -10,7 +10,7 @@ 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:
|
||||
with open(Paths.persist_root()+'/comma/id_rsa') as f:
|
||||
self.private_key = f.read()
|
||||
|
||||
def get(self, *args, **kwargs):
|
||||
|
||||
@@ -1,11 +1,4 @@
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
from openpilot.system.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"
|
||||
|
||||
@@ -4,16 +4,6 @@ import tempfile
|
||||
from atomicwrites import AtomicWriter
|
||||
|
||||
|
||||
def mkdirs_exists_ok(path):
|
||||
if path.startswith(('http://', '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)
|
||||
|
||||
@@ -9,4 +9,4 @@ def get_kalman_gain(dt, A, C, Q, R, iterations=100):
|
||||
S = C.dot(P).dot(C.T) + R
|
||||
K = P.dot(C.T).dot(np.linalg.inv(S))
|
||||
P = (np.eye(len(P)) - K.dot(C)).dot(P)
|
||||
return K
|
||||
return K
|
||||
|
||||
+1004
-326
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@@ -0,0 +1,12 @@
|
||||
class lazy_property():
|
||||
"""Defines a property whose value will be computed only once and as needed.
|
||||
|
||||
This can only be used on instance methods.
|
||||
"""
|
||||
def __init__(self, func):
|
||||
self._func = func
|
||||
|
||||
def __get__(self, obj_self, cls):
|
||||
value = self._func(obj_self)
|
||||
setattr(obj_self, self._func.__name__, value)
|
||||
return value
|
||||
@@ -1,38 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include "common/mat.h"
|
||||
#include "system/hardware/hw.h"
|
||||
|
||||
const int TRAJECTORY_SIZE = 33;
|
||||
const int LAT_MPC_N = 16;
|
||||
const int LON_MPC_N = 32;
|
||||
const float MIN_DRAW_DISTANCE = 10.0;
|
||||
const float MAX_DRAW_DISTANCE = 100.0;
|
||||
|
||||
const float RYG_GREEN = 0.01165;
|
||||
const float RYG_YELLOW = 0.06157;
|
||||
|
||||
template <typename T, size_t size>
|
||||
constexpr std::array<T, size> build_idxs(float max_val) {
|
||||
std::array<T, size> result{};
|
||||
for (int i = 0; i < size; ++i) {
|
||||
result[i] = max_val * ((i / (double)(size - 1)) * (i / (double)(size - 1)));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
constexpr auto T_IDXS = build_idxs<double, TRAJECTORY_SIZE>(10.0);
|
||||
constexpr auto T_IDXS_FLOAT = build_idxs<float, TRAJECTORY_SIZE>(10.0);
|
||||
constexpr auto X_IDXS = build_idxs<double, TRAJECTORY_SIZE>(192.0);
|
||||
constexpr auto X_IDXS_FLOAT = build_idxs<float, TRAJECTORY_SIZE>(192.0);
|
||||
|
||||
const mat3 FCAM_INTRINSIC_MATRIX = (mat3){{2648.0, 0.0, 1928.0 / 2,
|
||||
0.0, 2648.0, 1208.0 / 2,
|
||||
0.0, 0.0, 1.0}};
|
||||
|
||||
// tici ecam focal probably wrong? magnification is not consistent across frame
|
||||
// Need to retrain model before this can be changed
|
||||
const mat3 ECAM_INTRINSIC_MATRIX = (mat3){{567.0, 0.0, 1928.0 / 2,
|
||||
0.0, 567.0, 1208.0 / 2,
|
||||
0.0, 0.0, 1.0}};
|
||||
@@ -0,0 +1,22 @@
|
||||
import numpy as np
|
||||
|
||||
|
||||
def deep_interp_np(x, xp, fp, axis=None):
|
||||
if axis is not None:
|
||||
fp = fp.swapaxes(0,axis)
|
||||
x = np.atleast_1d(x)
|
||||
xp = np.array(xp)
|
||||
if len(xp) < 2:
|
||||
return np.repeat(fp, len(x), axis=0)
|
||||
if min(np.diff(xp)) < 0:
|
||||
raise RuntimeError('Bad x array for interpolation')
|
||||
j = np.searchsorted(xp, x) - 1
|
||||
j = np.clip(j, 0, len(xp)-2)
|
||||
d = np.divide(x - xp[j], xp[j + 1] - xp[j], out=np.ones_like(x, dtype=np.float64), where=xp[j + 1] - xp[j] != 0)
|
||||
vals_interp = (fp[j].T*(1 - d)).T + (fp[j + 1].T*d).T
|
||||
if axis is not None:
|
||||
vals_interp = vals_interp.swapaxes(0,axis)
|
||||
if len(vals_interp) == 1:
|
||||
return vals_interp[0]
|
||||
else:
|
||||
return vals_interp
|
||||
+19
-2
@@ -1,21 +1,28 @@
|
||||
#pragma once
|
||||
|
||||
#include <future>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "common/queue.h"
|
||||
|
||||
enum ParamKeyType {
|
||||
PERSISTENT = 0x02,
|
||||
CLEAR_ON_MANAGER_START = 0x04,
|
||||
CLEAR_ON_ONROAD_TRANSITION = 0x08,
|
||||
CLEAR_ON_OFFROAD_TRANSITION = 0x10,
|
||||
DONT_LOG = 0x20,
|
||||
DEVELOPMENT_ONLY = 0x40,
|
||||
ALL = 0xFFFFFFFF
|
||||
};
|
||||
|
||||
class Params {
|
||||
public:
|
||||
explicit Params(const std::string &path = {});
|
||||
~Params();
|
||||
// Not copyable.
|
||||
Params(const Params&) = delete;
|
||||
Params& operator=(const Params&) = delete;
|
||||
@@ -24,7 +31,7 @@ public:
|
||||
bool checkKey(const std::string &key);
|
||||
ParamKeyType getKeyType(const std::string &key);
|
||||
inline std::string getParamPath(const std::string &key = {}) {
|
||||
return params_path + prefix + (key.empty() ? "" : "/" + key);
|
||||
return params_path + params_prefix + (key.empty() ? "" : "/" + key);
|
||||
}
|
||||
|
||||
// Delete a value
|
||||
@@ -46,8 +53,18 @@ public:
|
||||
inline int putBool(const std::string &key, bool val) {
|
||||
return put(key.c_str(), val ? "1" : "0", 1);
|
||||
}
|
||||
void putNonBlocking(const std::string &key, const std::string &val);
|
||||
inline void putBoolNonBlocking(const std::string &key, bool val) {
|
||||
putNonBlocking(key, val ? "1" : "0");
|
||||
}
|
||||
|
||||
private:
|
||||
void asyncWriteThread();
|
||||
|
||||
std::string params_path;
|
||||
std::string prefix;
|
||||
std::string params_prefix;
|
||||
|
||||
// for nonblocking write
|
||||
std::future<void> future;
|
||||
SafeQueue<std::pair<std::string, std::string>> queue;
|
||||
};
|
||||
|
||||
+1
-4
@@ -1,10 +1,7 @@
|
||||
from openpilot.common.params_pyx import Params, ParamKeyType, UnknownKeyName, put_nonblocking, \
|
||||
put_bool_nonblocking
|
||||
from openpilot.common.params_pyx import Params, ParamKeyType, UnknownKeyName
|
||||
assert Params
|
||||
assert ParamKeyType
|
||||
assert UnknownKeyName
|
||||
assert put_nonblocking
|
||||
assert put_bool_nonblocking
|
||||
|
||||
if __name__ == "__main__":
|
||||
import sys
|
||||
|
||||
+4111
-3518
File diff suppressed because it is too large
Load Diff
+15
-8
@@ -3,7 +3,6 @@
|
||||
from libcpp cimport bool
|
||||
from libcpp.string cimport string
|
||||
from libcpp.vector cimport vector
|
||||
import threading
|
||||
|
||||
cdef extern from "common/params.h":
|
||||
cpdef enum ParamKeyType:
|
||||
@@ -11,6 +10,7 @@ cdef extern from "common/params.h":
|
||||
CLEAR_ON_MANAGER_START
|
||||
CLEAR_ON_ONROAD_TRANSITION
|
||||
CLEAR_ON_OFFROAD_TRANSITION
|
||||
DEVELOPMENT_ONLY
|
||||
ALL
|
||||
|
||||
cdef cppclass c_Params "Params":
|
||||
@@ -19,6 +19,8 @@ cdef extern from "common/params.h":
|
||||
bool getBool(string, bool) nogil
|
||||
int remove(string) nogil
|
||||
int put(string, string) nogil
|
||||
void putNonBlocking(string, string) nogil
|
||||
void putBoolNonBlocking(string, bool) nogil
|
||||
int putBool(string, bool) nogil
|
||||
bool checkKey(string) nogil
|
||||
string getParamPath(string) nogil
|
||||
@@ -79,7 +81,7 @@ cdef class Params:
|
||||
"""
|
||||
Warning: This function blocks until the param is written to disk!
|
||||
In very rare cases this can take over a second, and your code will hang.
|
||||
Use the put_nonblocking helper function in time sensitive code, but
|
||||
Use the put_nonblocking, put_bool_nonblocking in time sensitive code, but
|
||||
in general try to avoid writing params as much as possible.
|
||||
"""
|
||||
cdef string k = self.check_key(key)
|
||||
@@ -92,6 +94,17 @@ cdef class Params:
|
||||
with nogil:
|
||||
self.p.putBool(k, val)
|
||||
|
||||
def put_nonblocking(self, key, dat):
|
||||
cdef string k = self.check_key(key)
|
||||
cdef string dat_bytes = ensure_bytes(dat)
|
||||
with nogil:
|
||||
self.p.putNonBlocking(k, dat_bytes)
|
||||
|
||||
def put_bool_nonblocking(self, key, bool val):
|
||||
cdef string k = self.check_key(key)
|
||||
with nogil:
|
||||
self.p.putBoolNonBlocking(k, val)
|
||||
|
||||
def remove(self, key):
|
||||
cdef string k = self.check_key(key)
|
||||
with nogil:
|
||||
@@ -103,9 +116,3 @@ cdef class Params:
|
||||
|
||||
def all_keys(self):
|
||||
return self.p.allKeys()
|
||||
|
||||
def put_nonblocking(key, val, d=""):
|
||||
threading.Thread(target=lambda: Params(d).put(key, val)).start()
|
||||
|
||||
def put_bool_nonblocking(key, bool val, d=""):
|
||||
threading.Thread(target=lambda: Params(d).put_bool(key, val)).start()
|
||||
|
||||
Binary file not shown.
@@ -0,0 +1,46 @@
|
||||
import os
|
||||
import shutil
|
||||
import uuid
|
||||
|
||||
from typing import Optional
|
||||
|
||||
from openpilot.common.params import Params
|
||||
from openpilot.system.hardware.hw import Paths
|
||||
|
||||
class OpenpilotPrefix:
|
||||
def __init__(self, prefix: Optional[str] = None, clean_dirs_on_exit: bool = True):
|
||||
self.prefix = prefix if prefix else str(uuid.uuid4().hex[0:15])
|
||||
self.msgq_path = os.path.join('/dev/shm', self.prefix)
|
||||
self.clean_dirs_on_exit = clean_dirs_on_exit
|
||||
|
||||
def __enter__(self):
|
||||
self.original_prefix = os.environ.get('OPENPILOT_PREFIX', None)
|
||||
os.environ['OPENPILOT_PREFIX'] = self.prefix
|
||||
try:
|
||||
os.mkdir(self.msgq_path)
|
||||
except FileExistsError:
|
||||
pass
|
||||
os.makedirs(Paths.log_root(), exist_ok=True)
|
||||
|
||||
return self
|
||||
|
||||
def __exit__(self, exc_type, exc_obj, exc_tb):
|
||||
if self.clean_dirs_on_exit:
|
||||
self.clean_dirs()
|
||||
try:
|
||||
del os.environ['OPENPILOT_PREFIX']
|
||||
if self.original_prefix is not None:
|
||||
os.environ['OPENPILOT_PREFIX'] = self.original_prefix
|
||||
except KeyError:
|
||||
pass
|
||||
return False
|
||||
|
||||
def clean_dirs(self):
|
||||
symlink_path = Params().get_param_path()
|
||||
if os.path.exists(symlink_path):
|
||||
shutil.rmtree(os.path.realpath(symlink_path), ignore_errors=True)
|
||||
os.remove(symlink_path)
|
||||
shutil.rmtree(self.msgq_path, ignore_errors=True)
|
||||
shutil.rmtree(Paths.log_root(), ignore_errors=True)
|
||||
shutil.rmtree(Paths.download_cache_root(), ignore_errors=True)
|
||||
shutil.rmtree(Paths.comma_home(), ignore_errors=True)
|
||||
@@ -0,0 +1,30 @@
|
||||
import time
|
||||
import functools
|
||||
|
||||
from openpilot.common.swaglog import cloudlog
|
||||
|
||||
|
||||
def retry(attempts=3, delay=1.0, ignore_failure=False):
|
||||
def decorator(func):
|
||||
@functools.wraps(func)
|
||||
def wrapper(*args, **kwargs):
|
||||
for _ in range(attempts):
|
||||
try:
|
||||
return func(*args, **kwargs)
|
||||
except Exception:
|
||||
cloudlog.exception(f"{func.__name__} failed, trying again")
|
||||
time.sleep(delay)
|
||||
|
||||
if ignore_failure:
|
||||
cloudlog.error(f"{func.__name__} failed after retry")
|
||||
else:
|
||||
raise Exception(f"{func.__name__} failed after retry")
|
||||
return wrapper
|
||||
return decorator
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
@retry(attempts=10)
|
||||
def abc():
|
||||
raise ValueError("abc failed :(")
|
||||
abc()
|
||||
+1
-1
@@ -3,4 +3,4 @@ import datetime
|
||||
MIN_DATE = datetime.datetime(year=2023, month=6, day=1)
|
||||
|
||||
def system_time_valid():
|
||||
return datetime.datetime.now() > MIN_DATE
|
||||
return datetime.datetime.now() > MIN_DATE
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Binary file not shown.
+1
-1
@@ -1 +1 @@
|
||||
#define COMMA_VERSION "2023.11.20"
|
||||
#define COMMA_VERSION "2023.12.23"
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
import sys
|
||||
import pygame
|
||||
import cv2
|
||||
|
||||
class Window:
|
||||
def __init__(self, w, h, caption="window", double=False, halve=False):
|
||||
self.w = w
|
||||
self.h = h
|
||||
pygame.display.init()
|
||||
pygame.display.set_caption(caption)
|
||||
self.double = double
|
||||
self.halve = halve
|
||||
if self.double:
|
||||
self.rw, self.rh = w*2, h*2
|
||||
elif self.halve:
|
||||
self.rw, self.rh = w//2, h//2
|
||||
else:
|
||||
self.rw, self.rh = w, h
|
||||
self.screen = pygame.display.set_mode((self.rw, self.rh))
|
||||
pygame.display.flip()
|
||||
|
||||
# hack for xmonad, it shrinks the window by 6 pixels after the display.flip
|
||||
if self.screen.get_width() != self.rw:
|
||||
self.screen = pygame.display.set_mode((self.rw+(self.rw-self.screen.get_width()), self.rh+(self.rh-self.screen.get_height())))
|
||||
pygame.display.flip()
|
||||
|
||||
def draw(self, out):
|
||||
pygame.event.pump()
|
||||
if self.double:
|
||||
out2 = cv2.resize(out, (self.w*2, self.h*2))
|
||||
pygame.surfarray.blit_array(self.screen, out2.swapaxes(0, 1))
|
||||
elif self.halve:
|
||||
out2 = cv2.resize(out, (self.w//2, self.h//2))
|
||||
pygame.surfarray.blit_array(self.screen, out2.swapaxes(0, 1))
|
||||
else:
|
||||
pygame.surfarray.blit_array(self.screen, out.swapaxes(0, 1))
|
||||
pygame.display.flip()
|
||||
|
||||
def getkey(self):
|
||||
while 1:
|
||||
event = pygame.event.wait()
|
||||
if event.type == pygame.QUIT:
|
||||
pygame.quit()
|
||||
sys.exit()
|
||||
if event.type == pygame.KEYDOWN:
|
||||
return event.key
|
||||
|
||||
def getclick(self):
|
||||
for event in pygame.event.get():
|
||||
if event.type == pygame.MOUSEBUTTONDOWN:
|
||||
mx, my = pygame.mouse.get_pos()
|
||||
return mx, my
|
||||
|
||||
if __name__ == "__main__":
|
||||
import numpy as np
|
||||
win = Window(200, 200, double=True)
|
||||
img: np.ndarray = np.zeros((200, 200, 3), np.uint8)
|
||||
while 1:
|
||||
print("draw")
|
||||
img += 1
|
||||
win.draw(img)
|
||||
+87
-80
@@ -4,19 +4,19 @@
|
||||
|
||||
A supported vehicle is one that just works when you install a comma device. All supported cars provide a better experience than any stock system. Supported vehicles reference the US market unless otherwise specified.
|
||||
|
||||
# 267 Supported Cars
|
||||
# 273 Supported Cars
|
||||
|
||||
|Make|Model|Supported Package|ACC|No ACC accel below|No ALC below|Steering Torque|Resume from stop|<a href="##"><img width=2000></a>Hardware Needed<br> |Video|
|
||||
|---|---|---|:---:|:---:|:---:|:---:|:---:|:---:|:---:|
|
||||
|Acura|ILX 2016-19|AcuraWatch Plus|openpilot|25 mph|25 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Honda Nidec connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Acura&model=ILX 2016-19">Buy Here</a></sub></details>||
|
||||
|Acura|RDX 2016-18|AcuraWatch Plus|openpilot|25 mph|12 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Honda Nidec connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Acura&model=RDX 2016-18">Buy Here</a></sub></details>||
|
||||
|Acura|RDX 2019-22|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|3 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Honda Bosch A connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Acura&model=RDX 2019-22">Buy Here</a></sub></details>||
|
||||
|Audi|A3 2014-19|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Audi&model=A3 2014-19">Buy Here</a></sub></details>||
|
||||
|Audi|A3 Sportback e-tron 2017-18|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Audi&model=A3 Sportback e-tron 2017-18">Buy Here</a></sub></details>||
|
||||
|Audi|Q2 2018|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Audi&model=Q2 2018">Buy Here</a></sub></details>||
|
||||
|Audi|Q3 2019-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Audi&model=Q3 2019-23">Buy Here</a></sub></details>||
|
||||
|Audi|RS3 2018|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Audi&model=RS3 2018">Buy Here</a></sub></details>||
|
||||
|Audi|S3 2015-17|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Audi&model=S3 2015-17">Buy Here</a></sub></details>||
|
||||
|Audi|A3 2014-19|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,13</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Audi&model=A3 2014-19">Buy Here</a></sub></details>||
|
||||
|Audi|A3 Sportback e-tron 2017-18|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,13</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Audi&model=A3 Sportback e-tron 2017-18">Buy Here</a></sub></details>||
|
||||
|Audi|Q2 2018|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,13</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Audi&model=Q2 2018">Buy Here</a></sub></details>||
|
||||
|Audi|Q3 2019-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,13</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Audi&model=Q3 2019-23">Buy Here</a></sub></details>||
|
||||
|Audi|RS3 2018|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,13</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Audi&model=RS3 2018">Buy Here</a></sub></details>||
|
||||
|Audi|S3 2015-17|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,13</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Audi&model=S3 2015-17">Buy Here</a></sub></details>||
|
||||
|Buick|LaCrosse 2017-19[<sup>4</sup>](#footnotes)|Driver Confidence Package 2|openpilot|18 mph|7 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-II connector<br>- 1 comma 3X<br>- 2 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Buick&model=LaCrosse 2017-19">Buy Here</a></sub></details>||
|
||||
|Cadillac|Escalade 2017[<sup>4</sup>](#footnotes)|Driver Assist Package|openpilot|0 mph|7 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-II connector<br>- 1 comma 3X<br>- 2 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Cadillac&model=Escalade 2017">Buy Here</a></sub></details>||
|
||||
|Cadillac|Escalade ESV 2016[<sup>4</sup>](#footnotes)|Adaptive Cruise Control (ACC) & LKAS|openpilot|0 mph|7 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-II connector<br>- 1 comma 3X<br>- 2 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Cadillac&model=Escalade ESV 2016">Buy Here</a></sub></details>||
|
||||
@@ -34,7 +34,7 @@ A supported vehicle is one that just works when you install a comma device. All
|
||||
|comma|body|All|openpilot|0 mph|0 mph|[](##)|[](##)|None||
|
||||
|Ford|Bronco Sport 2021-22|Co-Pilot360 Assist+|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Ford Q3 connector<br>- 1 RJ45 cable (7 ft)<br>- 1 angled mount (8 degrees)<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Ford&model=Bronco Sport 2021-22">Buy Here</a></sub></details>||
|
||||
|Ford|Escape 2020-22|Co-Pilot360 Assist+|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Ford Q3 connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Ford&model=Escape 2020-22">Buy Here</a></sub></details>||
|
||||
|Ford|Explorer 2020-22|Co-Pilot360 Assist+|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Ford Q3 connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Ford&model=Explorer 2020-22">Buy Here</a></sub></details>||
|
||||
|Ford|Explorer 2020-23|Co-Pilot360 Assist+|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Ford Q3 connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Ford&model=Explorer 2020-23">Buy Here</a></sub></details>||
|
||||
|Ford|Focus 2018[<sup>3</sup>](#footnotes)|Adaptive Cruise Control with Lane Centering|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Ford Q3 connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Ford&model=Focus 2018">Buy Here</a></sub></details>||
|
||||
|Ford|Kuga 2020-22|Adaptive Cruise Control with Lane Centering|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Ford Q3 connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Ford&model=Kuga 2020-22">Buy Here</a></sub></details>||
|
||||
|Ford|Maverick 2022|LARIAT Luxury|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Ford Q3 connector<br>- 1 RJ45 cable (7 ft)<br>- 1 angled mount (8 degrees)<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Ford&model=Maverick 2022">Buy Here</a></sub></details>||
|
||||
@@ -73,6 +73,7 @@ A supported vehicle is one that just works when you install a comma device. All
|
||||
|Honda|Pilot 2016-22|Honda Sensing|openpilot|25 mph|12 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Honda Nidec connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Honda&model=Pilot 2016-22">Buy Here</a></sub></details>||
|
||||
|Honda|Ridgeline 2017-23|Honda Sensing|openpilot|25 mph|12 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Honda Nidec connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Honda&model=Ridgeline 2017-23">Buy Here</a></sub></details>||
|
||||
|Hyundai|Azera 2022|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai K connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Hyundai&model=Azera 2022">Buy Here</a></sub></details>||
|
||||
|Hyundai|Azera Hybrid 2019|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai C connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Hyundai&model=Azera Hybrid 2019">Buy Here</a></sub></details>||
|
||||
|Hyundai|Azera Hybrid 2020|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai K connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Hyundai&model=Azera Hybrid 2020">Buy Here</a></sub></details>||
|
||||
|Hyundai|Custin 2023|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai K connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Hyundai&model=Custin 2023">Buy Here</a></sub></details>||
|
||||
|Hyundai|Elantra 2017-18|Smart Cruise Control (SCC)|Stock|19 mph|32 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai B connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Hyundai&model=Elantra 2017-18">Buy Here</a></sub></details>||
|
||||
@@ -96,25 +97,26 @@ A supported vehicle is one that just works when you install a comma device. All
|
||||
|Hyundai|Kona Electric 2018-21|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai G connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Hyundai&model=Kona Electric 2018-21">Buy Here</a></sub></details>||
|
||||
|Hyundai|Kona Electric 2022-23|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai O connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Hyundai&model=Kona Electric 2022-23">Buy Here</a></sub></details>||
|
||||
|Hyundai|Kona Electric (with HDA II, Korea only) 2023[<sup>6</sup>](#footnotes)|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai R connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Hyundai&model=Kona Electric (with HDA II, Korea only) 2023">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=U2fOCmcQ8hw" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Hyundai|Kona Hybrid 2020|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai I connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Hyundai&model=Kona Hybrid 2020">Buy Here</a></sub></details>|<a href="https://youtu.be/0dwpAHiZgFo" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Hyundai|Kona Hybrid 2020|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai I connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Hyundai&model=Kona Hybrid 2020">Buy Here</a></sub></details>||
|
||||
|Hyundai|Palisade 2020-22|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai H connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Hyundai&model=Palisade 2020-22">Buy Here</a></sub></details>|<a href="https://youtu.be/TAnDqjF4fDY?t=456" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Hyundai|Santa Cruz 2022-23[<sup>6</sup>](#footnotes)|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai N connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Hyundai&model=Santa Cruz 2022-23">Buy Here</a></sub></details>||
|
||||
|Hyundai|Santa Fe 2019-20|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai D connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Hyundai&model=Santa Fe 2019-20">Buy Here</a></sub></details>||
|
||||
|Hyundai|Santa Cruz 2022-23[<sup>6</sup>](#footnotes)|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai N connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Hyundai&model=Santa Cruz 2022-23">Buy Here</a></sub></details>||
|
||||
|Hyundai|Santa Fe 2019-20|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai D connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Hyundai&model=Santa Fe 2019-20">Buy Here</a></sub></details>|<a href="https://youtu.be/bjDR0YjM__s" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Hyundai|Santa Fe 2021-23|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai L connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Hyundai&model=Santa Fe 2021-23">Buy Here</a></sub></details>|<a href="https://youtu.be/VnHzSTygTS4" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Hyundai|Santa Fe Hybrid 2022-23|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai L connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Hyundai&model=Santa Fe Hybrid 2022-23">Buy Here</a></sub></details>||
|
||||
|Hyundai|Santa Fe Plug-in Hybrid 2022-23|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai L connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Hyundai&model=Santa Fe Plug-in Hybrid 2022-23">Buy Here</a></sub></details>||
|
||||
|Hyundai|Sonata 2018-19|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai E connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Hyundai&model=Sonata 2018-19">Buy Here</a></sub></details>||
|
||||
|Hyundai|Sonata 2020-23|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai A connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Hyundai&model=Sonata 2020-23">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=ix63r9kE3Fw" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Hyundai|Sonata Hybrid 2020-23|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai A connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Hyundai&model=Sonata Hybrid 2020-23">Buy Here</a></sub></details>||
|
||||
|Hyundai|Staria 2023[<sup>6</sup>](#footnotes)|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai K connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Hyundai&model=Staria 2023">Buy Here</a></sub></details>||
|
||||
|Hyundai|Tucson 2021|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|19 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai L connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Hyundai&model=Tucson 2021">Buy Here</a></sub></details>||
|
||||
|Hyundai|Tucson 2022[<sup>6</sup>](#footnotes)|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai N connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Hyundai&model=Tucson 2022">Buy Here</a></sub></details>||
|
||||
|Hyundai|Tucson 2023[<sup>6</sup>](#footnotes)|All|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai N connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Hyundai&model=Tucson 2023">Buy Here</a></sub></details>||
|
||||
|Hyundai|Tucson 2022[<sup>6</sup>](#footnotes)|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai N connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Hyundai&model=Tucson 2022">Buy Here</a></sub></details>||
|
||||
|Hyundai|Tucson 2023[<sup>6</sup>](#footnotes)|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai N connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Hyundai&model=Tucson 2023">Buy Here</a></sub></details>||
|
||||
|Hyundai|Tucson Diesel 2019|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai L connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Hyundai&model=Tucson Diesel 2019">Buy Here</a></sub></details>||
|
||||
|Hyundai|Tucson Hybrid 2022-24[<sup>6</sup>](#footnotes)|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai N connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Hyundai&model=Tucson Hybrid 2022-24">Buy Here</a></sub></details>||
|
||||
|Hyundai|Veloster 2019-20|Smart Cruise Control (SCC)|Stock|5 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai E connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Hyundai&model=Veloster 2019-20">Buy Here</a></sub></details>||
|
||||
|Jeep|Grand Cherokee 2016-18|Adaptive Cruise Control (ACC)|Stock|0 mph|9 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 FCA connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Jeep&model=Grand Cherokee 2016-18">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=eLR9o2JkuRk" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Jeep|Grand Cherokee 2019-21|Adaptive Cruise Control (ACC)|Stock|0 mph|39 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 FCA connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Jeep&model=Grand Cherokee 2019-21">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=jBe4lWnRSu4" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Kia|Carnival 2023-24[<sup>6</sup>](#footnotes)|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai A connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Kia&model=Carnival 2023-24">Buy Here</a></sub></details>||
|
||||
|Kia|Carnival 2022-24[<sup>6</sup>](#footnotes)|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai A connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Kia&model=Carnival 2022-24">Buy Here</a></sub></details>||
|
||||
|Kia|Carnival (China only) 2023[<sup>6</sup>](#footnotes)|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai K connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Kia&model=Carnival (China only) 2023">Buy Here</a></sub></details>||
|
||||
|Kia|Ceed 2019|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai E connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Kia&model=Ceed 2019">Buy Here</a></sub></details>||
|
||||
|Kia|EV6 (Southeast Asia only) 2022-23[<sup>6</sup>](#footnotes)|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai P connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Kia&model=EV6 (Southeast Asia only) 2022-23">Buy Here</a></sub></details>||
|
||||
@@ -134,6 +136,7 @@ A supported vehicle is one that just works when you install a comma device. All
|
||||
|Kia|Niro Hybrid 2023[<sup>6</sup>](#footnotes)|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai A connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Kia&model=Niro Hybrid 2023">Buy Here</a></sub></details>||
|
||||
|Kia|Niro Plug-in Hybrid 2018-19|All|Stock|10 mph|32 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai C connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Kia&model=Niro Plug-in Hybrid 2018-19">Buy Here</a></sub></details>||
|
||||
|Kia|Niro Plug-in Hybrid 2020|All|Stock|0 mph|32 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai D connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Kia&model=Niro Plug-in Hybrid 2020">Buy Here</a></sub></details>||
|
||||
|Kia|Niro Plug-in Hybrid 2022|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai F connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Kia&model=Niro Plug-in Hybrid 2022">Buy Here</a></sub></details>||
|
||||
|Kia|Optima 2017|Advanced Smart Cruise Control|Stock|0 mph|32 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai B connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Kia&model=Optima 2017">Buy Here</a></sub></details>||
|
||||
|Kia|Optima 2019-20|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai G connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Kia&model=Optima 2019-20">Buy Here</a></sub></details>||
|
||||
|Kia|Optima Hybrid 2019|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai H connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Kia&model=Optima Hybrid 2019">Buy Here</a></sub></details>||
|
||||
@@ -143,16 +146,17 @@ A supported vehicle is one that just works when you install a comma device. All
|
||||
|Kia|Sorento 2021-23[<sup>6</sup>](#footnotes)|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai K connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Kia&model=Sorento 2021-23">Buy Here</a></sub></details>||
|
||||
|Kia|Sorento Hybrid 2021-23[<sup>6</sup>](#footnotes)|All|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai A connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Kia&model=Sorento Hybrid 2021-23">Buy Here</a></sub></details>||
|
||||
|Kia|Sorento Plug-in Hybrid 2022-23[<sup>6</sup>](#footnotes)|All|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai A connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Kia&model=Sorento Plug-in Hybrid 2022-23">Buy Here</a></sub></details>||
|
||||
|Kia|Sportage 2023[<sup>6</sup>](#footnotes)|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai N connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Kia&model=Sportage 2023">Buy Here</a></sub></details>||
|
||||
|Kia|Sportage 2023[<sup>6</sup>](#footnotes)|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai N connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Kia&model=Sportage 2023">Buy Here</a></sub></details>||
|
||||
|Kia|Sportage Hybrid 2023[<sup>6</sup>](#footnotes)|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai N connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Kia&model=Sportage Hybrid 2023">Buy Here</a></sub></details>||
|
||||
|Kia|Stinger 2018-20|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai C connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Kia&model=Stinger 2018-20">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=MJ94qoofYw0" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Kia|Stinger 2022|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai K connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Kia&model=Stinger 2022">Buy Here</a></sub></details>||
|
||||
|Kia|Stinger 2022-23|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai K connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Kia&model=Stinger 2022-23">Buy Here</a></sub></details>||
|
||||
|Kia|Telluride 2020-22|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai H connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Kia&model=Telluride 2020-22">Buy Here</a></sub></details>||
|
||||
|Lexus|CT Hybrid 2017-18|Lexus Safety System+|openpilot available[<sup>2</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Toyota A connector<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Lexus&model=CT Hybrid 2017-18">Buy Here</a></sub></details>||
|
||||
|Lexus|ES 2017-18|All|openpilot available[<sup>2</sup>](#footnotes)|19 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Toyota A connector<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Lexus&model=ES 2017-18">Buy Here</a></sub></details>||
|
||||
|Lexus|ES 2019-24|All|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Toyota A connector<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Lexus&model=ES 2019-24">Buy Here</a></sub></details>||
|
||||
|Lexus|ES Hybrid 2017-18|All|openpilot available[<sup>2</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Toyota A connector<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Lexus&model=ES Hybrid 2017-18">Buy Here</a></sub></details>||
|
||||
|Lexus|ES Hybrid 2019-23|All|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Toyota A connector<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Lexus&model=ES Hybrid 2019-23">Buy Here</a></sub></details>|<a href="https://youtu.be/BZ29osRVJeg?t=12" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Lexus|GS F 2016|All|Stock|19 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Toyota A connector<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Lexus&model=GS F 2016">Buy Here</a></sub></details>||
|
||||
|Lexus|IS 2017-19|All|Stock|19 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Toyota A connector<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Lexus&model=IS 2017-19">Buy Here</a></sub></details>||
|
||||
|Lexus|IS 2022-23|All|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Toyota A connector<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Lexus&model=IS 2022-23">Buy Here</a></sub></details>||
|
||||
|Lexus|NX 2018-19|All|openpilot available[<sup>2</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Toyota A connector<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Lexus&model=NX 2018-19">Buy Here</a></sub></details>||
|
||||
@@ -168,8 +172,8 @@ A supported vehicle is one that just works when you install a comma device. All
|
||||
|Lexus|RX Hybrid 2020-22|All|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Toyota A connector<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Lexus&model=RX Hybrid 2020-22">Buy Here</a></sub></details>||
|
||||
|Lexus|UX Hybrid 2019-23|All|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Toyota A connector<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Lexus&model=UX Hybrid 2019-23">Buy Here</a></sub></details>||
|
||||
|Lincoln|Aviator 2020-21|Co-Pilot360 Plus|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Ford Q3 connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Lincoln&model=Aviator 2020-21">Buy Here</a></sub></details>||
|
||||
|MAN|eTGE 2020-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|31 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 angled mount (8 degrees)<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=MAN&model=eTGE 2020-23">Buy Here</a></sub></details>|<a href="https://youtu.be/4100gLeabmo" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|MAN|TGE 2017-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|31 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 angled mount (8 degrees)<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=MAN&model=TGE 2017-23">Buy Here</a></sub></details>|<a href="https://youtu.be/4100gLeabmo" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|MAN|eTGE 2020-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,13</sup>](#footnotes)|0 mph|31 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 angled mount (8 degrees)<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=MAN&model=eTGE 2020-23">Buy Here</a></sub></details>|<a href="https://youtu.be/4100gLeabmo" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|MAN|TGE 2017-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,13</sup>](#footnotes)|0 mph|31 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 angled mount (8 degrees)<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=MAN&model=TGE 2017-23">Buy Here</a></sub></details>|<a href="https://youtu.be/4100gLeabmo" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Mazda|CX-5 2022-24|All|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Mazda connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Mazda&model=CX-5 2022-24">Buy Here</a></sub></details>||
|
||||
|Mazda|CX-9 2021-23|All|Stock|0 mph|28 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Mazda connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Mazda&model=CX-9 2021-23">Buy Here</a></sub></details>|<a href="https://youtu.be/dA3duO4a0O4" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Nissan|Altima 2019-20|ProPILOT Assist|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Nissan B connector<br>- 1 RJ45 cable (7 ft)<br>- 1 USB-C coupler<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Nissan&model=Altima 2019-20">Buy Here</a></sub></details>||
|
||||
@@ -177,26 +181,26 @@ A supported vehicle is one that just works when you install a comma device. All
|
||||
|Nissan|Rogue 2018-20|ProPILOT Assist|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Nissan A connector<br>- 1 RJ45 cable (7 ft)<br>- 1 USB-C coupler<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Nissan&model=Rogue 2018-20">Buy Here</a></sub></details>||
|
||||
|Nissan|X-Trail 2017|ProPILOT Assist|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Nissan A connector<br>- 1 RJ45 cable (7 ft)<br>- 1 USB-C coupler<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Nissan&model=X-Trail 2017">Buy Here</a></sub></details>||
|
||||
|Ram|1500 2019-23|Adaptive Cruise Control (ACC)|Stock|0 mph|32 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Ram connector<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Ram&model=1500 2019-23">Buy Here</a></sub></details>||
|
||||
|SEAT|Ateca 2018|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=SEAT&model=Ateca 2018">Buy Here</a></sub></details>||
|
||||
|SEAT|Leon 2014-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=SEAT&model=Leon 2014-20">Buy Here</a></sub></details>||
|
||||
|Subaru|Ascent 2019-21|All[<sup>7</sup>](#footnotes)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Subaru A connector<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Subaru&model=Ascent 2019-21">Buy Here</a></sub></details><details><summary>Tools</summary><sub>- 1 Pry Tool<br>- 1 Socket Wrench 8mm or 5/16" (deep)</sub></details>||
|
||||
|Subaru|Crosstrek 2018-19|EyeSight Driver Assistance[<sup>7</sup>](#footnotes)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Subaru A connector<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Subaru&model=Crosstrek 2018-19">Buy Here</a></sub></details><details><summary>Tools</summary><sub>- 1 Pry Tool<br>- 1 Socket Wrench 8mm or 5/16" (deep)</sub></details>|<a href="https://youtu.be/Agww7oE1k-s?t=26" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Subaru|Crosstrek 2020-23|EyeSight Driver Assistance[<sup>7</sup>](#footnotes)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Subaru A connector<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Subaru&model=Crosstrek 2020-23">Buy Here</a></sub></details><details><summary>Tools</summary><sub>- 1 Pry Tool<br>- 1 Socket Wrench 8mm or 5/16" (deep)</sub></details>||
|
||||
|Subaru|Forester 2019-21|All[<sup>7</sup>](#footnotes)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Subaru A connector<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Subaru&model=Forester 2019-21">Buy Here</a></sub></details><details><summary>Tools</summary><sub>- 1 Pry Tool<br>- 1 Socket Wrench 8mm or 5/16" (deep)</sub></details>||
|
||||
|Subaru|Impreza 2017-19|EyeSight Driver Assistance[<sup>7</sup>](#footnotes)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Subaru A connector<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Subaru&model=Impreza 2017-19">Buy Here</a></sub></details><details><summary>Tools</summary><sub>- 1 Pry Tool<br>- 1 Socket Wrench 8mm or 5/16" (deep)</sub></details>||
|
||||
|Subaru|Impreza 2020-22|EyeSight Driver Assistance[<sup>7</sup>](#footnotes)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Subaru A connector<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Subaru&model=Impreza 2020-22">Buy Here</a></sub></details><details><summary>Tools</summary><sub>- 1 Pry Tool<br>- 1 Socket Wrench 8mm or 5/16" (deep)</sub></details>||
|
||||
|SEAT|Ateca 2018|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,13</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=SEAT&model=Ateca 2018">Buy Here</a></sub></details>||
|
||||
|SEAT|Leon 2014-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,13</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=SEAT&model=Leon 2014-20">Buy Here</a></sub></details>||
|
||||
|Subaru|Ascent 2019-21|All[<sup>7</sup>](#footnotes)|openpilot available[<sup>1,8</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Subaru A connector<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Subaru&model=Ascent 2019-21">Buy Here</a></sub></details><details><summary>Tools</summary><sub>- 1 Pry Tool<br>- 1 Socket Wrench 8mm or 5/16" (deep)</sub></details>||
|
||||
|Subaru|Crosstrek 2018-19|EyeSight Driver Assistance[<sup>7</sup>](#footnotes)|openpilot available[<sup>1,8</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Subaru A connector<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Subaru&model=Crosstrek 2018-19">Buy Here</a></sub></details><details><summary>Tools</summary><sub>- 1 Pry Tool<br>- 1 Socket Wrench 8mm or 5/16" (deep)</sub></details>|<a href="https://youtu.be/Agww7oE1k-s?t=26" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Subaru|Crosstrek 2020-23|EyeSight Driver Assistance[<sup>7</sup>](#footnotes)|openpilot available[<sup>1,8</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Subaru A connector<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Subaru&model=Crosstrek 2020-23">Buy Here</a></sub></details><details><summary>Tools</summary><sub>- 1 Pry Tool<br>- 1 Socket Wrench 8mm or 5/16" (deep)</sub></details>||
|
||||
|Subaru|Forester 2019-21|All[<sup>7</sup>](#footnotes)|openpilot available[<sup>1,8</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Subaru A connector<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Subaru&model=Forester 2019-21">Buy Here</a></sub></details><details><summary>Tools</summary><sub>- 1 Pry Tool<br>- 1 Socket Wrench 8mm or 5/16" (deep)</sub></details>||
|
||||
|Subaru|Impreza 2017-19|EyeSight Driver Assistance[<sup>7</sup>](#footnotes)|openpilot available[<sup>1,8</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Subaru A connector<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Subaru&model=Impreza 2017-19">Buy Here</a></sub></details><details><summary>Tools</summary><sub>- 1 Pry Tool<br>- 1 Socket Wrench 8mm or 5/16" (deep)</sub></details>||
|
||||
|Subaru|Impreza 2020-22|EyeSight Driver Assistance[<sup>7</sup>](#footnotes)|openpilot available[<sup>1,8</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Subaru A connector<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Subaru&model=Impreza 2020-22">Buy Here</a></sub></details><details><summary>Tools</summary><sub>- 1 Pry Tool<br>- 1 Socket Wrench 8mm or 5/16" (deep)</sub></details>||
|
||||
|Subaru|Legacy 2020-22|All[<sup>7</sup>](#footnotes)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Subaru B connector<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Subaru&model=Legacy 2020-22">Buy Here</a></sub></details><details><summary>Tools</summary><sub>- 1 Pry Tool<br>- 1 Socket Wrench 8mm or 5/16" (deep)</sub></details>||
|
||||
|Subaru|Outback 2020-22|All[<sup>7</sup>](#footnotes)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Subaru B connector<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Subaru&model=Outback 2020-22">Buy Here</a></sub></details><details><summary>Tools</summary><sub>- 1 Pry Tool<br>- 1 Socket Wrench 8mm or 5/16" (deep)</sub></details>||
|
||||
|Subaru|XV 2018-19|EyeSight Driver Assistance[<sup>7</sup>](#footnotes)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Subaru A connector<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Subaru&model=XV 2018-19">Buy Here</a></sub></details><details><summary>Tools</summary><sub>- 1 Pry Tool<br>- 1 Socket Wrench 8mm or 5/16" (deep)</sub></details>|<a href="https://youtu.be/Agww7oE1k-s?t=26" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Subaru|XV 2020-21|EyeSight Driver Assistance[<sup>7</sup>](#footnotes)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Subaru A connector<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Subaru&model=XV 2020-21">Buy Here</a></sub></details><details><summary>Tools</summary><sub>- 1 Pry Tool<br>- 1 Socket Wrench 8mm or 5/16" (deep)</sub></details>||
|
||||
|Škoda|Fabia 2022-23[<sup>11</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Škoda&model=Fabia 2022-23">Buy Here</a></sub></details>[<sup>13</sup>](#footnotes)||
|
||||
|Škoda|Kamiq 2021[<sup>9,11</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Škoda&model=Kamiq 2021">Buy Here</a></sub></details>[<sup>13</sup>](#footnotes)||
|
||||
|Škoda|Karoq 2019-23[<sup>11</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Škoda&model=Karoq 2019-23">Buy Here</a></sub></details>||
|
||||
|Škoda|Kodiaq 2017-23[<sup>11</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Škoda&model=Kodiaq 2017-23">Buy Here</a></sub></details>||
|
||||
|Škoda|Octavia 2015-19[<sup>11</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Škoda&model=Octavia 2015-19">Buy Here</a></sub></details>||
|
||||
|Škoda|Octavia RS 2016[<sup>11</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Škoda&model=Octavia RS 2016">Buy Here</a></sub></details>||
|
||||
|Škoda|Scala 2020-23[<sup>11</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Škoda&model=Scala 2020-23">Buy Here</a></sub></details>[<sup>13</sup>](#footnotes)||
|
||||
|Škoda|Superb 2015-22[<sup>11</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Škoda&model=Superb 2015-22">Buy Here</a></sub></details>||
|
||||
|Subaru|XV 2018-19|EyeSight Driver Assistance[<sup>7</sup>](#footnotes)|openpilot available[<sup>1,8</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Subaru A connector<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Subaru&model=XV 2018-19">Buy Here</a></sub></details><details><summary>Tools</summary><sub>- 1 Pry Tool<br>- 1 Socket Wrench 8mm or 5/16" (deep)</sub></details>|<a href="https://youtu.be/Agww7oE1k-s?t=26" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Subaru|XV 2020-21|EyeSight Driver Assistance[<sup>7</sup>](#footnotes)|openpilot available[<sup>1,8</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Subaru A connector<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Subaru&model=XV 2020-21">Buy Here</a></sub></details><details><summary>Tools</summary><sub>- 1 Pry Tool<br>- 1 Socket Wrench 8mm or 5/16" (deep)</sub></details>||
|
||||
|Škoda|Fabia 2022-23[<sup>12</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,13</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Škoda&model=Fabia 2022-23">Buy Here</a></sub></details>[<sup>14</sup>](#footnotes)||
|
||||
|Škoda|Kamiq 2021-23[<sup>10,12</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,13</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Škoda&model=Kamiq 2021-23">Buy Here</a></sub></details>[<sup>14</sup>](#footnotes)||
|
||||
|Škoda|Karoq 2019-23[<sup>12</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,13</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Škoda&model=Karoq 2019-23">Buy Here</a></sub></details>||
|
||||
|Škoda|Kodiaq 2017-23[<sup>12</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,13</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Škoda&model=Kodiaq 2017-23">Buy Here</a></sub></details>||
|
||||
|Škoda|Octavia 2015-19[<sup>12</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,13</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Škoda&model=Octavia 2015-19">Buy Here</a></sub></details>||
|
||||
|Škoda|Octavia RS 2016[<sup>12</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,13</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Škoda&model=Octavia RS 2016">Buy Here</a></sub></details>||
|
||||
|Škoda|Scala 2020-23[<sup>12</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,13</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Škoda&model=Scala 2020-23">Buy Here</a></sub></details>[<sup>14</sup>](#footnotes)||
|
||||
|Škoda|Superb 2015-22[<sup>12</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,13</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Škoda&model=Superb 2015-22">Buy Here</a></sub></details>||
|
||||
|Toyota|Alphard 2019-20|All|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Toyota A connector<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Toyota&model=Alphard 2019-20">Buy Here</a></sub></details>||
|
||||
|Toyota|Alphard Hybrid 2021|All|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Toyota A connector<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Toyota&model=Alphard Hybrid 2021">Buy Here</a></sub></details>||
|
||||
|Toyota|Avalon 2016|Toyota Safety Sense P|openpilot available[<sup>2</sup>](#footnotes)|19 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Toyota A connector<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Toyota&model=Avalon 2016">Buy Here</a></sub></details>||
|
||||
@@ -209,8 +213,8 @@ A supported vehicle is one that just works when you install a comma device. All
|
||||
|Toyota|C-HR 2021|All|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Toyota A connector<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Toyota&model=C-HR 2021">Buy Here</a></sub></details>||
|
||||
|Toyota|C-HR Hybrid 2017-20|All|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Toyota A connector<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Toyota&model=C-HR Hybrid 2017-20">Buy Here</a></sub></details>||
|
||||
|Toyota|C-HR Hybrid 2021-22|All|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Toyota A connector<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Toyota&model=C-HR Hybrid 2021-22">Buy Here</a></sub></details>||
|
||||
|Toyota|Camry 2018-20|All|Stock|0 mph[<sup>8</sup>](#footnotes)|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Toyota A connector<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Toyota&model=Camry 2018-20">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=fkcjviZY9CM" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Toyota|Camry 2021-23|All|openpilot|0 mph[<sup>8</sup>](#footnotes)|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Toyota A connector<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Toyota&model=Camry 2021-23">Buy Here</a></sub></details>||
|
||||
|Toyota|Camry 2018-20|All|Stock|0 mph[<sup>9</sup>](#footnotes)|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Toyota A connector<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Toyota&model=Camry 2018-20">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=fkcjviZY9CM" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Toyota|Camry 2021-24|All|openpilot|0 mph[<sup>9</sup>](#footnotes)|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Toyota A connector<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Toyota&model=Camry 2021-24">Buy Here</a></sub></details>||
|
||||
|Toyota|Camry Hybrid 2018-20|All|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Toyota A connector<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Toyota&model=Camry Hybrid 2018-20">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=Q2DYY0AWKgk" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Toyota|Camry Hybrid 2021-24|All|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Toyota A connector<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Toyota&model=Camry Hybrid 2021-24">Buy Here</a></sub></details>||
|
||||
|Toyota|Corolla 2017-19|All|openpilot available[<sup>2</sup>](#footnotes)|19 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Toyota A connector<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Toyota&model=Corolla 2017-19">Buy Here</a></sub></details>||
|
||||
@@ -235,46 +239,48 @@ A supported vehicle is one that just works when you install a comma device. All
|
||||
|Toyota|RAV4 2017-18|All|openpilot available[<sup>2</sup>](#footnotes)|19 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Toyota A connector<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Toyota&model=RAV4 2017-18">Buy Here</a></sub></details>||
|
||||
|Toyota|RAV4 2019-21|All|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Toyota A connector<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Toyota&model=RAV4 2019-21">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=wJxjDd42gGA" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Toyota|RAV4 2022|All|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Toyota A connector<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Toyota&model=RAV4 2022">Buy Here</a></sub></details>||
|
||||
|Toyota|RAV4 2023|All|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Toyota A connector<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Toyota&model=RAV4 2023">Buy Here</a></sub></details>||
|
||||
|Toyota|RAV4 Hybrid 2016|Toyota Safety Sense P|openpilot available[<sup>2</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Toyota A connector<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Toyota&model=RAV4 Hybrid 2016">Buy Here</a></sub></details>|<a href="https://youtu.be/LhT5VzJVfNI?t=26" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Toyota|RAV4 Hybrid 2017-18|All|openpilot available[<sup>2</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Toyota A connector<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Toyota&model=RAV4 Hybrid 2017-18">Buy Here</a></sub></details>|<a href="https://youtu.be/LhT5VzJVfNI?t=26" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Toyota|RAV4 Hybrid 2019-21|All|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Toyota A connector<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Toyota&model=RAV4 Hybrid 2019-21">Buy Here</a></sub></details>||
|
||||
|Toyota|RAV4 Hybrid 2022|All|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Toyota A connector<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Toyota&model=RAV4 Hybrid 2022">Buy Here</a></sub></details>|<a href="https://youtu.be/U0nH9cnrFB0" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Toyota|RAV4 Hybrid 2023|All|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Toyota A connector<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Toyota&model=RAV4 Hybrid 2023">Buy Here</a></sub></details>||
|
||||
|Toyota|Sienna 2018-20|All|openpilot available[<sup>2</sup>](#footnotes)|19 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Toyota A connector<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Toyota&model=Sienna 2018-20">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=q1UPOo4Sh68" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Volkswagen|Arteon 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Volkswagen&model=Arteon 2018-23">Buy Here</a></sub></details>|<a href="https://youtu.be/FAomFKPFlDA" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Volkswagen|Arteon eHybrid 2020-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Volkswagen&model=Arteon eHybrid 2020-23">Buy Here</a></sub></details>|<a href="https://youtu.be/FAomFKPFlDA" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Volkswagen|Arteon R 2020-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Volkswagen&model=Arteon R 2020-23">Buy Here</a></sub></details>|<a href="https://youtu.be/FAomFKPFlDA" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Volkswagen|Atlas 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Volkswagen&model=Atlas 2018-23">Buy Here</a></sub></details>||
|
||||
|Volkswagen|Atlas Cross Sport 2021-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Volkswagen&model=Atlas Cross Sport 2021-22">Buy Here</a></sub></details>||
|
||||
|Volkswagen|California 2021-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|31 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 angled mount (8 degrees)<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Volkswagen&model=California 2021-23">Buy Here</a></sub></details>||
|
||||
|Volkswagen|Caravelle 2020|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|31 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 angled mount (8 degrees)<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Volkswagen&model=Caravelle 2020">Buy Here</a></sub></details>||
|
||||
|Volkswagen|CC 2018-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Volkswagen&model=CC 2018-22">Buy Here</a></sub></details>|<a href="https://youtu.be/FAomFKPFlDA" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Volkswagen|Crafter 2017-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|31 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 angled mount (8 degrees)<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Volkswagen&model=Crafter 2017-23">Buy Here</a></sub></details>|<a href="https://youtu.be/4100gLeabmo" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Volkswagen|e-Crafter 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|31 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 angled mount (8 degrees)<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Volkswagen&model=e-Crafter 2018-23">Buy Here</a></sub></details>|<a href="https://youtu.be/4100gLeabmo" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Volkswagen|e-Golf 2014-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Volkswagen&model=e-Golf 2014-20">Buy Here</a></sub></details>||
|
||||
|Volkswagen|Golf 2015-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Volkswagen&model=Golf 2015-20">Buy Here</a></sub></details>||
|
||||
|Volkswagen|Golf Alltrack 2015-19|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Volkswagen&model=Golf Alltrack 2015-19">Buy Here</a></sub></details>||
|
||||
|Volkswagen|Golf GTD 2015-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Volkswagen&model=Golf GTD 2015-20">Buy Here</a></sub></details>||
|
||||
|Volkswagen|Golf GTE 2015-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Volkswagen&model=Golf GTE 2015-20">Buy Here</a></sub></details>||
|
||||
|Volkswagen|Golf GTI 2015-21|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Volkswagen&model=Golf GTI 2015-21">Buy Here</a></sub></details>||
|
||||
|Volkswagen|Golf R 2015-19|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Volkswagen&model=Golf R 2015-19">Buy Here</a></sub></details>||
|
||||
|Volkswagen|Golf SportsVan 2015-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Volkswagen&model=Golf SportsVan 2015-20">Buy Here</a></sub></details>||
|
||||
|Volkswagen|Grand California 2019-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|31 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 angled mount (8 degrees)<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Volkswagen&model=Grand California 2019-23">Buy Here</a></sub></details>|<a href="https://youtu.be/4100gLeabmo" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Volkswagen|Jetta 2018-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Volkswagen&model=Jetta 2018-22">Buy Here</a></sub></details>||
|
||||
|Volkswagen|Jetta GLI 2021-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Volkswagen&model=Jetta GLI 2021-22">Buy Here</a></sub></details>||
|
||||
|Volkswagen|Passat 2015-22[<sup>10</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Volkswagen&model=Passat 2015-22">Buy Here</a></sub></details>||
|
||||
|Volkswagen|Passat Alltrack 2015-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Volkswagen&model=Passat Alltrack 2015-22">Buy Here</a></sub></details>||
|
||||
|Volkswagen|Passat GTE 2015-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Volkswagen&model=Passat GTE 2015-22">Buy Here</a></sub></details>||
|
||||
|Volkswagen|Polo 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Volkswagen&model=Polo 2018-23">Buy Here</a></sub></details>[<sup>13</sup>](#footnotes)||
|
||||
|Volkswagen|Polo GTI 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Volkswagen&model=Polo GTI 2018-23">Buy Here</a></sub></details>[<sup>13</sup>](#footnotes)||
|
||||
|Volkswagen|T-Cross 2021|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Volkswagen&model=T-Cross 2021">Buy Here</a></sub></details>[<sup>13</sup>](#footnotes)||
|
||||
|Volkswagen|T-Roc 2021|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Volkswagen&model=T-Roc 2021">Buy Here</a></sub></details>[<sup>13</sup>](#footnotes)||
|
||||
|Volkswagen|Taos 2022-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Volkswagen&model=Taos 2022-23">Buy Here</a></sub></details>||
|
||||
|Volkswagen|Teramont 2018-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Volkswagen&model=Teramont 2018-22">Buy Here</a></sub></details>||
|
||||
|Volkswagen|Teramont Cross Sport 2021-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Volkswagen&model=Teramont Cross Sport 2021-22">Buy Here</a></sub></details>||
|
||||
|Volkswagen|Teramont X 2021-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Volkswagen&model=Teramont X 2021-22">Buy Here</a></sub></details>||
|
||||
|Volkswagen|Tiguan 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Volkswagen&model=Tiguan 2018-23">Buy Here</a></sub></details>||
|
||||
|Volkswagen|Tiguan eHybrid 2021-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Volkswagen&model=Tiguan eHybrid 2021-23">Buy Here</a></sub></details>||
|
||||
|Volkswagen|Touran 2016-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Volkswagen&model=Touran 2016-23">Buy Here</a></sub></details>||
|
||||
|Volkswagen|Arteon 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,13</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Volkswagen&model=Arteon 2018-23">Buy Here</a></sub></details>|<a href="https://youtu.be/FAomFKPFlDA" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Volkswagen|Arteon eHybrid 2020-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,13</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Volkswagen&model=Arteon eHybrid 2020-23">Buy Here</a></sub></details>|<a href="https://youtu.be/FAomFKPFlDA" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Volkswagen|Arteon R 2020-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,13</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Volkswagen&model=Arteon R 2020-23">Buy Here</a></sub></details>|<a href="https://youtu.be/FAomFKPFlDA" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Volkswagen|Atlas 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,13</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Volkswagen&model=Atlas 2018-23">Buy Here</a></sub></details>||
|
||||
|Volkswagen|Atlas Cross Sport 2020-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,13</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Volkswagen&model=Atlas Cross Sport 2020-22">Buy Here</a></sub></details>||
|
||||
|Volkswagen|California 2021-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,13</sup>](#footnotes)|0 mph|31 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 angled mount (8 degrees)<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Volkswagen&model=California 2021-23">Buy Here</a></sub></details>||
|
||||
|Volkswagen|Caravelle 2020|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,13</sup>](#footnotes)|0 mph|31 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 angled mount (8 degrees)<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Volkswagen&model=Caravelle 2020">Buy Here</a></sub></details>||
|
||||
|Volkswagen|CC 2018-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,13</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Volkswagen&model=CC 2018-22">Buy Here</a></sub></details>|<a href="https://youtu.be/FAomFKPFlDA" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Volkswagen|Crafter 2017-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,13</sup>](#footnotes)|0 mph|31 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 angled mount (8 degrees)<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Volkswagen&model=Crafter 2017-23">Buy Here</a></sub></details>|<a href="https://youtu.be/4100gLeabmo" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Volkswagen|e-Crafter 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,13</sup>](#footnotes)|0 mph|31 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 angled mount (8 degrees)<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Volkswagen&model=e-Crafter 2018-23">Buy Here</a></sub></details>|<a href="https://youtu.be/4100gLeabmo" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Volkswagen|e-Golf 2014-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,13</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Volkswagen&model=e-Golf 2014-20">Buy Here</a></sub></details>||
|
||||
|Volkswagen|Golf 2015-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,13</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Volkswagen&model=Golf 2015-20">Buy Here</a></sub></details>||
|
||||
|Volkswagen|Golf Alltrack 2015-19|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,13</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Volkswagen&model=Golf Alltrack 2015-19">Buy Here</a></sub></details>||
|
||||
|Volkswagen|Golf GTD 2015-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,13</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Volkswagen&model=Golf GTD 2015-20">Buy Here</a></sub></details>||
|
||||
|Volkswagen|Golf GTE 2015-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,13</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Volkswagen&model=Golf GTE 2015-20">Buy Here</a></sub></details>||
|
||||
|Volkswagen|Golf GTI 2015-21|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,13</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Volkswagen&model=Golf GTI 2015-21">Buy Here</a></sub></details>||
|
||||
|Volkswagen|Golf R 2015-19|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,13</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Volkswagen&model=Golf R 2015-19">Buy Here</a></sub></details>||
|
||||
|Volkswagen|Golf SportsVan 2015-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,13</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Volkswagen&model=Golf SportsVan 2015-20">Buy Here</a></sub></details>||
|
||||
|Volkswagen|Grand California 2019-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,13</sup>](#footnotes)|0 mph|31 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 angled mount (8 degrees)<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Volkswagen&model=Grand California 2019-23">Buy Here</a></sub></details>|<a href="https://youtu.be/4100gLeabmo" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Volkswagen|Jetta 2018-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,13</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Volkswagen&model=Jetta 2018-22">Buy Here</a></sub></details>||
|
||||
|Volkswagen|Jetta GLI 2021-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,13</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Volkswagen&model=Jetta GLI 2021-22">Buy Here</a></sub></details>||
|
||||
|Volkswagen|Passat 2015-22[<sup>11</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,13</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Volkswagen&model=Passat 2015-22">Buy Here</a></sub></details>||
|
||||
|Volkswagen|Passat Alltrack 2015-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,13</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Volkswagen&model=Passat Alltrack 2015-22">Buy Here</a></sub></details>||
|
||||
|Volkswagen|Passat GTE 2015-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,13</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Volkswagen&model=Passat GTE 2015-22">Buy Here</a></sub></details>||
|
||||
|Volkswagen|Polo 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,13</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Volkswagen&model=Polo 2018-23">Buy Here</a></sub></details>[<sup>14</sup>](#footnotes)||
|
||||
|Volkswagen|Polo GTI 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,13</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Volkswagen&model=Polo GTI 2018-23">Buy Here</a></sub></details>[<sup>14</sup>](#footnotes)||
|
||||
|Volkswagen|T-Cross 2021|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,13</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Volkswagen&model=T-Cross 2021">Buy Here</a></sub></details>[<sup>14</sup>](#footnotes)||
|
||||
|Volkswagen|T-Roc 2018-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,13</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Volkswagen&model=T-Roc 2018-22">Buy Here</a></sub></details>[<sup>14</sup>](#footnotes)||
|
||||
|Volkswagen|Taos 2022-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,13</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Volkswagen&model=Taos 2022-23">Buy Here</a></sub></details>||
|
||||
|Volkswagen|Teramont 2018-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,13</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Volkswagen&model=Teramont 2018-22">Buy Here</a></sub></details>||
|
||||
|Volkswagen|Teramont Cross Sport 2021-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,13</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Volkswagen&model=Teramont Cross Sport 2021-22">Buy Here</a></sub></details>||
|
||||
|Volkswagen|Teramont X 2021-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,13</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Volkswagen&model=Teramont X 2021-22">Buy Here</a></sub></details>||
|
||||
|Volkswagen|Tiguan 2018-24|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,13</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Volkswagen&model=Tiguan 2018-24">Buy Here</a></sub></details>||
|
||||
|Volkswagen|Tiguan eHybrid 2021-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,13</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Volkswagen&model=Tiguan eHybrid 2021-23">Buy Here</a></sub></details>||
|
||||
|Volkswagen|Touran 2016-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,13</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Volkswagen&model=Touran 2016-23">Buy Here</a></sub></details>||
|
||||
|
||||
### Footnotes
|
||||
<sup>1</sup>openpilot Longitudinal Control (Alpha) is available behind a toggle; the toggle is only available in non-release branches such as `devel` or `master-ci`. <br />
|
||||
@@ -284,12 +290,13 @@ A supported vehicle is one that just works when you install a comma device. All
|
||||
<sup>5</sup>2019 Honda Civic 1.6L Diesel Sedan does not have ALC below 12mph. <br />
|
||||
<sup>6</sup>Requires a <a href="https://comma.ai/shop/can-fd-panda-kit" target="_blank">CAN FD panda kit</a> if not using comma 3X for this <a href="https://en.wikipedia.org/wiki/CAN_FD" target="_blank">CAN FD car</a>. <br />
|
||||
<sup>7</sup>In the non-US market, openpilot requires the car to come equipped with EyeSight with Lane Keep Assistance. <br />
|
||||
<sup>8</sup>openpilot operates above 28mph for Camry 4CYL L, 4CYL LE and 4CYL SE which don't have Full-Speed Range Dynamic Radar Cruise Control. <br />
|
||||
<sup>9</sup>Not including the China market Kamiq, which is based on the (currently) unsupported PQ34 platform. <br />
|
||||
<sup>10</sup>Refers only to the MQB-based European B8 Passat, not the NMS Passat in the USA/China/Mideast markets. <br />
|
||||
<sup>11</sup>Some Škoda vehicles are equipped with heated windshields, which are known to block GPS signal needed for some comma 3X functionality. <br />
|
||||
<sup>12</sup>Only available for vehicles using a gateway (J533) harness. At this time, vehicles using a camera harness are limited to using stock ACC. <br />
|
||||
<sup>13</sup>Model-years 2022 and beyond may have a combined CAN gateway and BCM, which is supported by openpilot in software, but doesn't yet have a harness available from the comma store. <br />
|
||||
<sup>8</sup>Enabling longitudinal control (alpha) will disable all EyeSight functionality, including AEB, LDW, and RAB. <br />
|
||||
<sup>9</sup>openpilot operates above 28mph for Camry 4CYL L, 4CYL LE and 4CYL SE which don't have Full-Speed Range Dynamic Radar Cruise Control. <br />
|
||||
<sup>10</sup>Not including the China market Kamiq, which is based on the (currently) unsupported PQ34 platform. <br />
|
||||
<sup>11</sup>Refers only to the MQB-based European B8 Passat, not the NMS Passat in the USA/China/Mideast markets. <br />
|
||||
<sup>12</sup>Some Škoda vehicles are equipped with heated windshields, which are known to block GPS signal needed for some comma 3X functionality. <br />
|
||||
<sup>13</sup>Only available for vehicles using a gateway (J533) harness. At this time, vehicles using a camera harness are limited to using stock ACC. <br />
|
||||
<sup>14</sup>Model-years 2022 and beyond may have a combined CAN gateway and BCM, which is supported by openpilot in software, but doesn't yet have a harness available from the comma store. <br />
|
||||
|
||||
## Community Maintained Cars
|
||||
Although they're not upstream, the community has openpilot running on other makes and models. See the 'Community Supported Models' section of each make [on our wiki](https://wiki.comma.ai/).
|
||||
@@ -318,7 +325,7 @@ If your car has the following packages or features, then it's a good candidate f
|
||||
|
||||
### FlexRay
|
||||
|
||||
All the cars that openpilot supports use a [CAN bus](https://en.wikipedia.org/wiki/CAN_bus) for communication between all the car's computers, however a CAN bus isn't the only way that the cars in your computer can communicate. Most, if not all, vehicles from the following manufacturers use [FlexRay](https://en.wikipedia.org/wiki/FlexRay) instead of a CAN bus: **BMW, Mercedes, Audi, Land Rover, and some Volvo**. These cars may one day be supported, but we have no immediate plans to support FlexRay.
|
||||
All the cars that openpilot supports use a [CAN bus](https://en.wikipedia.org/wiki/CAN_bus) for communication between all the car's computers, however a CAN bus isn't the only way that the computers in your car can communicate. Most, if not all, vehicles from the following manufacturers use [FlexRay](https://en.wikipedia.org/wiki/FlexRay) instead of a CAN bus: **BMW, Mercedes, Audi, Land Rover, and some Volvo**. These cars may one day be supported, but we have no immediate plans to support FlexRay.
|
||||
|
||||
### Toyota Security
|
||||
|
||||
|
||||
+43
-26
@@ -1,26 +1,50 @@
|
||||
# How to contribute
|
||||
|
||||
Our software is open source so you can solve your own problems without needing help from others. And if you solve a problem and are so kind, you can upstream it for the rest of the world to use. Check out our [post about externalization](https://blog.comma.ai/a-2020-theme-externalization/).
|
||||
|
||||
Most open source development activity is coordinated through our [GitHub Discussions](https://github.com/commaai/openpilot/discussions) and [Discord](https://discord.comma.ai). A lot of documentation is available at https://docs.comma.ai and on our [blog](https://blog.comma.ai/).
|
||||
Our software is open source so you can solve your own problems without needing help from others. And if you solve a problem and are so kind, you can upstream it for the rest of the world to use. Check out our [post about externalization](https://blog.comma.ai/a-2020-theme-externalization/). Development activity is coordinated through our GitHub Issues, [GitHub Discussions](https://github.com/commaai/openpilot/discussions), and [Discord](https://discord.comma.ai).
|
||||
|
||||
### Getting Started
|
||||
|
||||
* Setup your [development environment](../tools/)
|
||||
* Join our [Discord](https://discord.comma.ai)
|
||||
* Make sure you have a [GitHub account](https://github.com/signup/free)
|
||||
* Fork [our repositories](https://github.com/commaai) on GitHub
|
||||
* Setup your [development environment](../tools/)
|
||||
* Read about the [development workflow](WORKFLOW.md)
|
||||
* Join our [Discord](https://discord.comma.ai)
|
||||
* Docs are at https://docs.comma.ai and https://blog.comma.ai
|
||||
|
||||
## What contributions are we looking for?
|
||||
|
||||
**openpilot's priorities are [safety](SAFETY.md), stability, quality, and features, in that order.** openpilot is part of comma's mission to *solve self-driving cars while delivering shippable intermediaries*, and **all** developoment is towards that goal.
|
||||
|
||||
### What gets merged?
|
||||
|
||||
The probability of a pull request being merged is a function of its value to the project and the effort it will take us to get it merged.
|
||||
If a PR offers *some* value but will take lots of time to get merged, it will be closed.
|
||||
Simple, well-tested bug fixes are the easiest to merge, and new features are the hardest to get merged.
|
||||
|
||||
All of these are examples of good PRs:
|
||||
* typo fix: https://github.com/commaai/openpilot/pull/30678
|
||||
* removing unused code: https://github.com/commaai/openpilot/pull/30573
|
||||
* simple car model port: https://github.com/commaai/openpilot/pull/30245
|
||||
* car brand port: https://github.com/commaai/openpilot/pull/23331
|
||||
|
||||
### What doesn't get merged?
|
||||
|
||||
* **arbitrary style changes**: code is art, and it's up to the author to make it beautiful
|
||||
* **500+ line PRs**: clean it up, break it up into smaller PRs, or both
|
||||
* **PRs without a clear goal**: every PR must have a singular and clear goal
|
||||
* **UI design changes**: we do not have a good review process for this yet
|
||||
* **New features**: We believe openpilot is mostly feature-complete, and the rest is a matter of refinement and fixing bugs. As a result of this, most feature PRs will be immediately closed, however the beauty of open source is that forks can and do offer features that upstream openpilot doesn't.
|
||||
|
||||
### First contribution
|
||||
Try out some of these first pull requests ideas to dive into the codebase:
|
||||
|
||||
* Increase our [mypy](http://mypy-lang.org/) coverage
|
||||
* Write some documentation
|
||||
* Tackle an open [good first issue](https://github.com/commaai/openpilot/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22)
|
||||
Check out any [good first issue](https://github.com/commaai/openpilot/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22) to get started.
|
||||
|
||||
### What do I need to contribute?
|
||||
|
||||
A lot of openpilot work requires only a PC, and some requires a comma device.
|
||||
Most car-related contributions require access to that car, plus a comma device installed in the car.
|
||||
|
||||
## Pull Requests
|
||||
|
||||
Pull requests should be against the master branch. Welcomed contributions include bug reports, car ports, and any [open issue](https://github.com/commaai/openpilot/issues). If you're unsure about a contribution, feel free to open a discussion, issue, or draft PR to discuss the problem you're trying to solve.
|
||||
Pull requests should be against the master branch. If you're unsure about a contribution, feel free to open a discussion, issue, or draft PR to discuss the problem you're trying to solve.
|
||||
|
||||
A good pull request has all of the following:
|
||||
* a clearly stated purpose
|
||||
@@ -31,18 +55,11 @@ A good pull request has all of the following:
|
||||
* if you've improved your car's tuning, post before and after plots
|
||||
* passes the CI tests
|
||||
|
||||
### Car Ports
|
||||
## Contributing without Code
|
||||
|
||||
We've released a [Model Port guide](https://blog.comma.ai/openpilot-port-guide-for-toyota-models/) for porting to Toyota/Lexus models.
|
||||
|
||||
If you port openpilot to a substantially new car brand, see this more generic [Brand Port guide](https://blog.comma.ai/how-to-write-a-car-port-for-openpilot/).
|
||||
|
||||
## Testing
|
||||
|
||||
### Automated Testing
|
||||
|
||||
All PRs and commits are automatically checked by GitHub Actions. Check out `.github/workflows/` for what GitHub Actions runs. Any new tests should be added to GitHub Actions.
|
||||
|
||||
### Code Style and Linting
|
||||
|
||||
Code is automatically checked for style by GitHub Actions as part of the automated tests. You can also run these tests yourself by running `pre-commit run --all`.
|
||||
* Report bugs in GitHub issues.
|
||||
* Report driving issues in the `#driving-feedback` Discord channel.
|
||||
* Consider opting into driver camera uploads to improve the driver monitoring model.
|
||||
* Connect your device to Wi-Fi regularly, so that we can pull data for training better driving models.
|
||||
* Run the `nightly` branch and report issues. This branch is like `master` but it's built just like a release.
|
||||
* Annotate images in the [comma10k dateset](https://github.com/commaai/comma10k).
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
import logging
|
||||
import os
|
||||
|
||||
from .astro_dog import AstroDog
|
||||
assert AstroDog is not None
|
||||
|
||||
# setup logging
|
||||
LOGLEVEL = os.environ.get('LOGLEVEL', 'INFO').upper()
|
||||
logging.basicConfig(level=LOGLEVEL, format='%(message)s')
|
||||
@@ -1,396 +0,0 @@
|
||||
from collections import defaultdict
|
||||
from concurrent.futures import ThreadPoolExecutor
|
||||
from typing import DefaultDict, Dict, Iterable, List, Optional, Union
|
||||
|
||||
from .constants import SECS_IN_DAY, SECS_IN_HR
|
||||
from .helpers import ConstellationId, get_constellation, get_closest, get_el_az, TimeRangeHolder
|
||||
from .ephemeris import Ephemeris, EphemerisType, GLONASSEphemeris, GPSEphemeris, PolyEphemeris, parse_sp3_orbits, parse_rinex_nav_msg_gps, \
|
||||
parse_rinex_nav_msg_glonass
|
||||
from .downloader import download_orbits_gps, download_orbits_russia_src, download_nav, download_ionex, download_dcb, download_prediction_orbits_russia_src
|
||||
from .downloader import download_cors_station
|
||||
from .trop import saast
|
||||
from .iono import IonexMap, parse_ionex, get_slant_delay
|
||||
from .dcb import DCB, parse_dcbs
|
||||
from .gps_time import GPSTime
|
||||
from .dgps import get_closest_station_names, parse_dgps
|
||||
from . import constants
|
||||
|
||||
MAX_DGPS_DISTANCE = 100_000 # in meters, because we're not barbarians
|
||||
|
||||
|
||||
class AstroDog:
|
||||
'''
|
||||
auto_update: flag indicating whether laika should fetch files from web automatically
|
||||
cache_dir: directory where data files are downloaded to and cached
|
||||
dgps: flag indicating whether laika should use dgps (CORS)
|
||||
data to calculate pseudorange corrections
|
||||
valid_const: list of constellation identifiers laika will try process
|
||||
valid_ephem_types: set of ephemeris types that are allowed to use and download.
|
||||
Default is set to use all orbit ephemeris types
|
||||
clear_old_ephemeris: flag indicating if ephemeris for an individual satellite should be overwritten when new ephemeris is added.
|
||||
'''
|
||||
|
||||
def __init__(self, auto_update=True,
|
||||
cache_dir='/tmp/gnss/',
|
||||
dgps=False,
|
||||
valid_const=(ConstellationId.GPS, ConstellationId.GLONASS),
|
||||
valid_ephem_types=EphemerisType.all_orbits(),
|
||||
clear_old_ephemeris=False):
|
||||
|
||||
for const in valid_const:
|
||||
if not isinstance(const, ConstellationId):
|
||||
raise TypeError(f"valid_const must be a list of ConstellationId, got {const}")
|
||||
|
||||
self.auto_update = auto_update
|
||||
self.cache_dir = cache_dir
|
||||
self.clear_old_ephemeris = clear_old_ephemeris
|
||||
self.dgps = dgps
|
||||
if not isinstance(valid_ephem_types, Iterable):
|
||||
valid_ephem_types = [valid_ephem_types]
|
||||
self.pull_orbit = len(set(EphemerisType.all_orbits()) & set(valid_ephem_types)) > 0
|
||||
self.pull_nav = EphemerisType.NAV in valid_ephem_types
|
||||
self.use_qcom_poly = EphemerisType.QCOM_POLY in valid_ephem_types
|
||||
self.valid_const = valid_const
|
||||
self.valid_ephem_types = valid_ephem_types
|
||||
|
||||
self.orbit_fetched_times = TimeRangeHolder()
|
||||
self.navs_fetched_times = TimeRangeHolder()
|
||||
self.dcbs_fetched_times = TimeRangeHolder()
|
||||
|
||||
self.dgps_delays = []
|
||||
self.ionex_maps: List[IonexMap] = []
|
||||
self.orbits: DefaultDict[str, List[PolyEphemeris]] = defaultdict(list)
|
||||
self.qcom_polys: DefaultDict[str, List[PolyEphemeris]] = defaultdict(list)
|
||||
self.navs: DefaultDict[str, List[Union[GPSEphemeris, GLONASSEphemeris]]] = defaultdict(list)
|
||||
self.dcbs: DefaultDict[str, List[DCB]] = defaultdict(list)
|
||||
|
||||
self.cached_ionex: Optional[IonexMap] = None
|
||||
self.cached_dgps = None
|
||||
self.cached_orbit: DefaultDict[str, Optional[PolyEphemeris]] = defaultdict(lambda: None)
|
||||
self.cached_qcom_polys: DefaultDict[str, Optional[PolyEphemeris]] = defaultdict(lambda: None)
|
||||
self.cached_nav: DefaultDict[str, Union[GPSEphemeris, GLONASSEphemeris, None]] = defaultdict(lambda: None)
|
||||
self.cached_dcb: DefaultDict[str, Optional[DCB]] = defaultdict(lambda: None)
|
||||
|
||||
def get_ionex(self, time) -> Optional[IonexMap]:
|
||||
ionex: Optional[IonexMap] = self._get_latest_valid_data(self.ionex_maps, self.cached_ionex, self.get_ionex_data, time)
|
||||
if ionex is None:
|
||||
if self.auto_update:
|
||||
raise RuntimeError("Pulled ionex, but still can't get valid for time " + str(time))
|
||||
else:
|
||||
self.cached_ionex = ionex
|
||||
return ionex
|
||||
|
||||
def get_nav(self, prn, time):
|
||||
skip_download = time in self.navs_fetched_times
|
||||
nav = self._get_latest_valid_data(self.navs[prn], self.cached_nav[prn], self.get_nav_data, time, skip_download)
|
||||
if nav is not None:
|
||||
self.cached_nav[prn] = nav
|
||||
return nav
|
||||
|
||||
@staticmethod
|
||||
def _select_valid_temporal_items(item_dict, time, cache):
|
||||
'''Returns only valid temporal item for specific time from currently fetched
|
||||
data.'''
|
||||
result = {}
|
||||
for prn, temporal_objects in item_dict.items():
|
||||
cached = cache[prn]
|
||||
if cached is not None and cached.valid(time):
|
||||
obj = cached
|
||||
else:
|
||||
obj = get_closest(time, temporal_objects)
|
||||
if obj is None or not obj.valid(time):
|
||||
continue
|
||||
cache[prn] = obj
|
||||
result[prn] = obj
|
||||
return result
|
||||
|
||||
def get_all_ephem_prns(self):
|
||||
return set(self.orbits.keys()).union(set(self.navs.keys())).union(set(self.qcom_polys.keys()))
|
||||
|
||||
def get_navs(self, time):
|
||||
if time not in self.navs_fetched_times:
|
||||
self.get_nav_data(time)
|
||||
return AstroDog._select_valid_temporal_items(self.navs, time, self.cached_nav)
|
||||
|
||||
def get_orbit(self, prn: str, time: GPSTime):
|
||||
skip_download = time in self.orbit_fetched_times
|
||||
orbit = self._get_latest_valid_data(self.orbits[prn], self.cached_orbit[prn], self.get_orbit_data, time, skip_download)
|
||||
if orbit is not None:
|
||||
self.cached_orbit[prn] = orbit
|
||||
return orbit
|
||||
|
||||
def get_qcom_poly(self, prn: str, time: GPSTime):
|
||||
poly = self._get_latest_valid_data(self.qcom_polys[prn], self.cached_qcom_polys[prn], None, time, True)
|
||||
if poly is not None:
|
||||
self.cached_qcom_polys[prn] = poly
|
||||
return poly
|
||||
|
||||
def get_orbits(self, time):
|
||||
if time not in self.orbit_fetched_times:
|
||||
self.get_orbit_data(time)
|
||||
return AstroDog._select_valid_temporal_items(self.orbits, time, self.cached_orbit)
|
||||
|
||||
def get_dcb(self, prn, time):
|
||||
skip_download = time in self.dcbs_fetched_times
|
||||
dcb = self._get_latest_valid_data(self.dcbs[prn], self.cached_dcb[prn], self.get_dcb_data, time, skip_download)
|
||||
if dcb is not None:
|
||||
self.cached_dcb[prn] = dcb
|
||||
return dcb
|
||||
|
||||
def get_dgps_corrections(self, time, recv_pos):
|
||||
latest_data = self._get_latest_valid_data(self.dgps_delays, self.cached_dgps, self.get_dgps_data, time, recv_pos=recv_pos)
|
||||
if latest_data is None:
|
||||
if self.auto_update:
|
||||
raise RuntimeError("Pulled dgps, but still can't get valid for time " + str(time))
|
||||
else:
|
||||
self.cached_dgps = latest_data
|
||||
return latest_data
|
||||
|
||||
def add_qcom_polys(self, new_ephems: Dict[str, List[Ephemeris]]):
|
||||
self._add_ephems(new_ephems, self.qcom_polys)
|
||||
|
||||
def add_orbits(self, new_ephems: Dict[str, List[Ephemeris]]):
|
||||
self._add_ephems(new_ephems, self.orbits)
|
||||
|
||||
def add_navs(self, new_ephems: Dict[str, List[Ephemeris]]):
|
||||
self._add_ephems(new_ephems, self.navs)
|
||||
|
||||
def _add_ephems(self, new_ephems: Dict[str, List[Ephemeris]], ephems_dict):
|
||||
for k, v in new_ephems.items():
|
||||
if len(v) > 0:
|
||||
if self.clear_old_ephemeris:
|
||||
ephems_dict[k] = v
|
||||
else:
|
||||
ephems_dict[k].extend(v)
|
||||
|
||||
def add_ephem_fetched_time(self, ephems, fetched_times):
|
||||
min_epochs = []
|
||||
max_epochs = []
|
||||
for v in ephems.values():
|
||||
if len(v) > 0:
|
||||
min_ephem, max_ephem = self.get_epoch_range(v)
|
||||
min_epochs.append(min_ephem)
|
||||
max_epochs.append(max_ephem)
|
||||
if len(min_epochs) > 0:
|
||||
min_epoch = min(min_epochs)
|
||||
max_epoch = max(max_epochs)
|
||||
fetched_times.add(min_epoch, max_epoch)
|
||||
|
||||
def get_nav_data(self, time):
|
||||
def download_and_parse(constellation, parse_rinex_nav_func):
|
||||
file_path = download_nav(time, cache_dir=self.cache_dir, constellation=constellation)
|
||||
return parse_rinex_nav_func(file_path) if file_path else {}
|
||||
|
||||
fetched_ephems = {}
|
||||
|
||||
if ConstellationId.GPS in self.valid_const:
|
||||
fetched_ephems = download_and_parse(ConstellationId.GPS, parse_rinex_nav_msg_gps)
|
||||
if ConstellationId.GLONASS in self.valid_const:
|
||||
for k, v in download_and_parse(ConstellationId.GLONASS, parse_rinex_nav_msg_glonass).items():
|
||||
fetched_ephems.setdefault(k, []).extend(v)
|
||||
self.add_navs(fetched_ephems)
|
||||
|
||||
if sum([len(v) for v in fetched_ephems.values()]) == 0:
|
||||
begin_day = GPSTime(time.week, SECS_IN_DAY * (time.tow // SECS_IN_DAY))
|
||||
end_day = GPSTime(time.week, SECS_IN_DAY * (1 + (time.tow // SECS_IN_DAY)))
|
||||
self.navs_fetched_times.add(begin_day, end_day)
|
||||
|
||||
def download_parse_orbit(self, gps_time: GPSTime, skip_before_epoch=None) -> Dict[str, List[PolyEphemeris]]:
|
||||
# Download multiple days to be able to polyfit at the start-end of the day
|
||||
time_steps = [gps_time - SECS_IN_DAY, gps_time, gps_time + SECS_IN_DAY]
|
||||
with ThreadPoolExecutor() as executor:
|
||||
futures_other = [executor.submit(download_orbits_russia_src, t, self.cache_dir, self.valid_ephem_types) for t in time_steps]
|
||||
futures_gps = None
|
||||
if ConstellationId.GPS in self.valid_const:
|
||||
futures_gps = [executor.submit(download_orbits_gps, t, self.cache_dir, self.valid_ephem_types) for t in time_steps]
|
||||
|
||||
ephems_other = parse_sp3_orbits([f.result() for f in futures_other if f.result()], self.valid_const, skip_before_epoch)
|
||||
ephems_us = parse_sp3_orbits([f.result() for f in futures_gps if f.result()], self.valid_const, skip_before_epoch) if futures_gps else {}
|
||||
|
||||
return {k: ephems_other.get(k, []) + ephems_us.get(k, []) for k in set(list(ephems_other.keys()) + list(ephems_us.keys()))}
|
||||
|
||||
def download_parse_prediction_orbit(self, gps_time: GPSTime):
|
||||
assert EphemerisType.ULTRA_RAPID_ORBIT in self.valid_ephem_types
|
||||
skip_until_epoch = gps_time - 2 * SECS_IN_HR
|
||||
|
||||
result = download_prediction_orbits_russia_src(gps_time, self.cache_dir)
|
||||
if result is not None:
|
||||
result = [result]
|
||||
elif ConstellationId.GPS in self.valid_const:
|
||||
# Slower fallback. Russia src prediction orbits are published from 2022
|
||||
result = [download_orbits_gps(t, self.cache_dir, self.valid_ephem_types) for t in [gps_time - SECS_IN_DAY, gps_time]]
|
||||
if result is None:
|
||||
return {}
|
||||
return parse_sp3_orbits(result, self.valid_const, skip_until_epoch=skip_until_epoch)
|
||||
|
||||
def get_orbit_data(self, time: GPSTime, only_predictions=False):
|
||||
if only_predictions:
|
||||
ephems_sp3 = self.download_parse_prediction_orbit(time)
|
||||
else:
|
||||
ephems_sp3 = self.download_parse_orbit(time)
|
||||
if sum([len(v) for v in ephems_sp3.values()]) < 5:
|
||||
raise RuntimeError(f'No orbit data found. For Time {time.as_datetime()} constellations {self.valid_const} valid ephem types {self.valid_ephem_types}')
|
||||
self.add_ephem_fetched_time(ephems_sp3, self.orbit_fetched_times)
|
||||
self.add_orbits(ephems_sp3)
|
||||
|
||||
def get_dcb_data(self, time):
|
||||
file_path_dcb = download_dcb(time, cache_dir=self.cache_dir)
|
||||
dcbs = parse_dcbs(file_path_dcb, self.valid_const)
|
||||
for dcb in dcbs:
|
||||
self.dcbs[dcb.prn].append(dcb)
|
||||
|
||||
if len(dcbs) != 0:
|
||||
min_epoch, max_epoch = self.get_epoch_range(dcbs)
|
||||
self.dcbs_fetched_times.add(min_epoch, max_epoch)
|
||||
|
||||
def get_epoch_range(self, new_ephems):
|
||||
min_ephem = min(new_ephems, key=lambda e: e.epoch)
|
||||
max_ephem = max(new_ephems, key=lambda e: e.epoch)
|
||||
min_epoch = min_ephem.epoch - min_ephem.max_time_diff
|
||||
max_epoch = max_ephem.epoch + max_ephem.max_time_diff
|
||||
return min_epoch, max_epoch
|
||||
|
||||
def get_ionex_data(self, time):
|
||||
file_path_ionex = download_ionex(time, cache_dir=self.cache_dir)
|
||||
ionex_maps = parse_ionex(file_path_ionex)
|
||||
for im in ionex_maps:
|
||||
self.ionex_maps.append(im)
|
||||
|
||||
def get_dgps_data(self, time, recv_pos):
|
||||
station_names = get_closest_station_names(recv_pos, k=8, max_distance=MAX_DGPS_DISTANCE, cache_dir=self.cache_dir)
|
||||
for station_name in station_names:
|
||||
file_path_station = download_cors_station(time, station_name, cache_dir=self.cache_dir)
|
||||
if file_path_station:
|
||||
dgps = parse_dgps(station_name, file_path_station,
|
||||
self, max_distance=MAX_DGPS_DISTANCE,
|
||||
required_constellations=self.valid_const)
|
||||
if dgps is not None:
|
||||
self.dgps_delays.append(dgps)
|
||||
break
|
||||
|
||||
def get_tgd_from_nav(self, prn, time):
|
||||
if get_constellation(prn) not in self.valid_const:
|
||||
return None
|
||||
|
||||
eph = self.get_nav(prn, time)
|
||||
|
||||
if eph:
|
||||
return eph.get_tgd()
|
||||
return None
|
||||
|
||||
def get_eph(self, prn, time):
|
||||
if get_constellation(prn) not in self.valid_const:
|
||||
return None
|
||||
eph = None
|
||||
if self.pull_orbit:
|
||||
eph = self.get_orbit(prn, time)
|
||||
if not eph and self.pull_nav:
|
||||
eph = self.get_nav(prn, time)
|
||||
if not eph and self.use_qcom_poly:
|
||||
eph = self.get_qcom_poly(prn, time)
|
||||
return eph
|
||||
|
||||
def get_sat_info(self, prn, time):
|
||||
eph = self.get_eph(prn, time)
|
||||
if eph:
|
||||
return eph.get_sat_info(time)
|
||||
return None
|
||||
|
||||
def get_all_sat_info(self, time):
|
||||
ephs = {}
|
||||
if self.pull_orbit:
|
||||
ephs = self.get_orbits(time)
|
||||
if len(ephs) == 0 and self.pull_nav:
|
||||
ephs = self.get_navs(time)
|
||||
|
||||
return {prn: eph.get_sat_info(time) for prn, eph in ephs.items()}
|
||||
|
||||
def get_glonass_channel(self, prn, time):
|
||||
nav = self.get_nav(prn, time)
|
||||
if nav:
|
||||
return nav.channel
|
||||
return None
|
||||
|
||||
def get_frequency(self, prn, time, signal='C1C'):
|
||||
if get_constellation(prn) == ConstellationId.GPS:
|
||||
switch = {'1': constants.GPS_L1,
|
||||
'2': constants.GPS_L2,
|
||||
'5': constants.GPS_L5,
|
||||
'6': constants.GALILEO_E6,
|
||||
'7': constants.GALILEO_E5B,
|
||||
'8': constants.GALILEO_E5AB}
|
||||
freq = switch.get(signal[1])
|
||||
if freq:
|
||||
return freq
|
||||
raise NotImplementedError("Dont know this GPS frequency: ", signal, prn)
|
||||
elif get_constellation(prn) == ConstellationId.GLONASS:
|
||||
n = self.get_glonass_channel(prn, time)
|
||||
if n is None:
|
||||
return None
|
||||
switch = {'1': constants.GLONASS_L1 + n * constants.GLONASS_L1_DELTA,
|
||||
'2': constants.GLONASS_L2 + n * constants.GLONASS_L2_DELTA,
|
||||
'5': constants.GLONASS_L5 + n * constants.GLONASS_L5_DELTA,
|
||||
'6': constants.GALILEO_E6,
|
||||
'7': constants.GALILEO_E5B,
|
||||
'8': constants.GALILEO_E5AB}
|
||||
freq = switch.get(signal[1])
|
||||
if freq:
|
||||
return freq
|
||||
raise NotImplementedError("Dont know this GLONASS frequency: ", signal, prn)
|
||||
|
||||
def get_delay(self, prn, time, rcv_pos, no_dgps=False, signal='C1C', freq=None):
|
||||
sat_info = self.get_sat_info(prn, time)
|
||||
if sat_info is None:
|
||||
return None
|
||||
sat_pos = sat_info[0]
|
||||
el, az = get_el_az(rcv_pos, sat_pos)
|
||||
if el < 0.2:
|
||||
return None
|
||||
if self.dgps and not no_dgps:
|
||||
return self._get_delay_dgps(prn, rcv_pos, time)
|
||||
|
||||
ionex = self.get_ionex(time)
|
||||
if not freq and ionex is not None:
|
||||
freq = self.get_frequency(prn, time, signal)
|
||||
dcb = self.get_dcb(prn, time)
|
||||
# When using internet we expect all data or return None
|
||||
if self.auto_update and (ionex is None or dcb is None or freq is None):
|
||||
return None
|
||||
if ionex is not None:
|
||||
iono_delay = ionex.get_delay(rcv_pos, az, el, sat_pos, time, freq)
|
||||
else:
|
||||
# 5m vertical delay is a good default
|
||||
iono_delay = get_slant_delay(rcv_pos, az, el, sat_pos, time, freq, vertical_delay=5.0)
|
||||
trop_delay = saast(rcv_pos, el)
|
||||
code_bias = dcb.get_delay(signal) if dcb is not None else 0.
|
||||
return iono_delay + trop_delay + code_bias
|
||||
|
||||
def _get_delay_dgps(self, prn, rcv_pos, time):
|
||||
dgps_corrections = self.get_dgps_corrections(time, rcv_pos)
|
||||
if dgps_corrections is None:
|
||||
return None
|
||||
return dgps_corrections.get_delay(prn, time)
|
||||
|
||||
def _get_latest_valid_data(self, data, latest_data, download_data_func, time, skip_download=False, recv_pos=None):
|
||||
def is_valid(latest_data):
|
||||
if recv_pos is None:
|
||||
return latest_data is not None and latest_data.valid(time)
|
||||
else:
|
||||
return latest_data is not None and latest_data.valid(time, recv_pos)
|
||||
|
||||
if is_valid(latest_data):
|
||||
return latest_data
|
||||
|
||||
latest_data = get_closest(time, data, recv_pos=recv_pos)
|
||||
if is_valid(latest_data):
|
||||
return latest_data
|
||||
if skip_download or not self.auto_update:
|
||||
return None
|
||||
if recv_pos is not None:
|
||||
download_data_func(time, recv_pos)
|
||||
else:
|
||||
download_data_func(time)
|
||||
latest_data = get_closest(time, data, recv_pos=recv_pos)
|
||||
if is_valid(latest_data):
|
||||
return latest_data
|
||||
return None
|
||||
@@ -1,34 +0,0 @@
|
||||
# These are all from IS-GPS-200G unless otherwise noted
|
||||
|
||||
SPEED_OF_LIGHT = 2.99792458e8 # m/s
|
||||
|
||||
# Physical parameters of the Earth
|
||||
EARTH_GM = 3.986005e14 # m^3/s^2 (gravitational constant * mass of earth)
|
||||
EARTH_RADIUS = 6.3781e6 # m
|
||||
EARTH_ROTATION_RATE = 7.2921151467e-005 # rad/s (WGS84 earth rotation rate)
|
||||
|
||||
# GPS system parameters:
|
||||
GPS_L1 = l1 = 1.57542e9 # Hz
|
||||
GPS_L2 = l2 = 1.22760e9 # Hz
|
||||
GPS_L5 = l5 = 1.17645e9 # Hz Also E5
|
||||
|
||||
#GLONASS system parameters
|
||||
#TODO this is old convention
|
||||
GLONASS_L1 = 1.602e9
|
||||
GLONASS_L1_DELTA = 0.5625e6
|
||||
GLONASS_L2 = 1.246e9
|
||||
GLONASS_L2_DELTA = 0.4375e6
|
||||
GLONASS_L5 = 1.201e9
|
||||
GLONASS_L5_DELTA = 0.4375e6
|
||||
|
||||
#Galileo system parameters: # Has additional frequencies on E6
|
||||
#Source RINEX 2.11 document
|
||||
GALILEO_E5B = 1.207140e9 # Hz
|
||||
GALILEO_E5AB = 1.191795e9 # Hz
|
||||
GALILEO_E6 = 1.27875e9 # Hz
|
||||
|
||||
SECS_IN_MIN = 60
|
||||
SECS_IN_HR = 60*SECS_IN_MIN
|
||||
SECS_IN_DAY = 24*SECS_IN_HR
|
||||
SECS_IN_WEEK = 7*SECS_IN_DAY
|
||||
SECS_IN_YEAR = 365*SECS_IN_DAY
|
||||
@@ -1,84 +0,0 @@
|
||||
from datetime import datetime
|
||||
from .constants import SECS_IN_HR, SECS_IN_WEEK, \
|
||||
SPEED_OF_LIGHT, GPS_L1, GPS_L2
|
||||
from .gps_time import GPSTime
|
||||
from .helpers import get_constellation
|
||||
import warnings
|
||||
|
||||
|
||||
class DCB:
|
||||
def __init__(self, prn, data):
|
||||
self.max_time_diff = 2*SECS_IN_WEEK
|
||||
self.prn = prn
|
||||
self.epoch = data['epoch']
|
||||
self.healthy = True
|
||||
if 'C1W_C2W' in data:
|
||||
self.C1W_C2W = data['C1W_C2W']
|
||||
elif 'C1P_C2P' in data:
|
||||
self.C1W_C2W = data['C1P_C2P']
|
||||
else:
|
||||
self.healthy = False
|
||||
if 'C1C_C1W' in data:
|
||||
self.C1C_C1W = data['C1C_C1W']
|
||||
elif 'C1C_C1P' in data:
|
||||
self.C1C_C1W = data['C1C_C1P']
|
||||
else:
|
||||
self.healthy = False
|
||||
|
||||
def valid(self, time):
|
||||
return abs(time - self.epoch) <= self.max_time_diff and self.healthy
|
||||
|
||||
def get_delay(self, signal):
|
||||
if signal == 'C1C':
|
||||
return (- SPEED_OF_LIGHT*1e-9*self.C1W_C2W*GPS_L2**2/(GPS_L1**2 - GPS_L2**2)
|
||||
+ SPEED_OF_LIGHT*1e-9*self.C1C_C1W)
|
||||
if signal == 'C2P':
|
||||
return (- SPEED_OF_LIGHT*1e-9*self.C1W_C2W*GPS_L1**2/(GPS_L1**2 - GPS_L2**2))
|
||||
if signal == 'C1P':
|
||||
return (SPEED_OF_LIGHT*1e-9*self.C1C_C1W)
|
||||
## Todo: update dcb database and get delay to include additional signals
|
||||
if signal == 'C2C':
|
||||
warnings.warn("Differential code bias not implemented for signal C2C", UserWarning)
|
||||
return 0
|
||||
if signal == 'C5C':
|
||||
warnings.warn("Differential code bias not implemented for signal C5C", UserWarning)
|
||||
return 0
|
||||
if signal == 'C6C':
|
||||
warnings.warn("Differential code bias not implemented for signal C6C", UserWarning)
|
||||
return 0
|
||||
if signal == 'C7C':
|
||||
warnings.warn("Differential code bias not implemented for signal C7C", UserWarning)
|
||||
return 0
|
||||
if signal == 'C8C':
|
||||
warnings.warn("Differential code bias not implemented for signal C8C", UserWarning)
|
||||
return 0
|
||||
|
||||
|
||||
def parse_dcbs(file_name, SUPPORTED_CONSTELLATIONS):
|
||||
with open(file_name, 'r+') as DCB_file:
|
||||
contents = DCB_file.readlines()
|
||||
data_started = False
|
||||
dcbs_dict = {}
|
||||
for line in contents:
|
||||
if not data_started:
|
||||
if line[1:4] == 'DSB':
|
||||
data_started = True
|
||||
else:
|
||||
continue
|
||||
line_components = line.split()
|
||||
if len(line_components[2]) < 3:
|
||||
break
|
||||
prn = line_components[2]
|
||||
if get_constellation(prn) not in SUPPORTED_CONSTELLATIONS:
|
||||
continue
|
||||
dcb_type = line_components[3] + '_' + line_components[4]
|
||||
epoch = GPSTime.from_datetime(datetime.strptime(line_components[5], '%Y:%j:%f')) + 12*SECS_IN_HR
|
||||
if prn not in dcbs_dict:
|
||||
dcbs_dict[prn] = {}
|
||||
dcbs_dict[prn][dcb_type] = float(line_components[8])
|
||||
dcbs_dict[prn]['epoch'] = epoch
|
||||
|
||||
dcbs = []
|
||||
for prn in dcbs_dict:
|
||||
dcbs.append(DCB(prn, dcbs_dict[prn]))
|
||||
return dcbs
|
||||
-161
@@ -1,161 +0,0 @@
|
||||
import os
|
||||
import numpy as np
|
||||
from datetime import datetime
|
||||
|
||||
from .gps_time import GPSTime
|
||||
from .constants import SECS_IN_YEAR
|
||||
from . import raw_gnss as raw
|
||||
from . import opt
|
||||
from .rinex_file import RINEXFile
|
||||
from .downloader import download_cors_coords
|
||||
from .helpers import get_constellation, ConstellationId
|
||||
|
||||
def mean_filter(delay):
|
||||
d2 = delay.copy()
|
||||
max_step = 10
|
||||
for i in range(max_step, len(delay) - max_step):
|
||||
finite_idxs = np.where(np.isfinite(delay[i - max_step:i + max_step]))
|
||||
if max_step in finite_idxs[0]:
|
||||
step = min([max_step, finite_idxs[0][-1] - max_step, max_step - finite_idxs[0][0]])
|
||||
d2[i] = np.nanmean(delay[i - step:i + step + 1])
|
||||
return d2
|
||||
|
||||
|
||||
def download_and_parse_station_postions(cors_station_positions_path, cache_dir):
|
||||
if not os.path.isfile(cors_station_positions_path):
|
||||
cors_stations = {}
|
||||
coord_file_paths = download_cors_coords(cache_dir=cache_dir)
|
||||
for coord_file_path in coord_file_paths:
|
||||
try:
|
||||
station_id = coord_file_path.split('/')[-1][:4]
|
||||
with open(coord_file_path, 'r+') as coord_file:
|
||||
contents = coord_file.readlines()
|
||||
phase_center = False
|
||||
for line_number in range(len(contents)):
|
||||
if 'L1 Phase Center' in contents[line_number]:
|
||||
phase_center = True
|
||||
if not phase_center and 'ITRF2014 POSITION' in contents[line_number]:
|
||||
velocity = [float(contents[line_number+8].split()[3]),
|
||||
float(contents[line_number+9].split()[3]),
|
||||
float(contents[line_number+10].split()[3])]
|
||||
if phase_center and 'ITRF2014 POSITION' in contents[line_number]:
|
||||
epoch = GPSTime.from_datetime(datetime(2005,1,1))
|
||||
position = [float(contents[line_number+2].split()[3]),
|
||||
float(contents[line_number+3].split()[3]),
|
||||
float(contents[line_number+4].split()[3])]
|
||||
cors_stations[station_id] = [epoch, position, velocity]
|
||||
break
|
||||
except ValueError:
|
||||
pass
|
||||
cors_station_positions_file = open(cors_station_positions_path, 'wb')
|
||||
np.save(cors_station_positions_file, cors_stations)
|
||||
cors_station_positions_file.close()
|
||||
|
||||
|
||||
def get_closest_station_names(pos, k=5, max_distance=100000, cache_dir='/tmp/gnss/'):
|
||||
from scipy.spatial import cKDTree
|
||||
|
||||
cors_station_positions_dict = load_cors_station_positions(cache_dir)
|
||||
station_ids = list(cors_station_positions_dict.keys())
|
||||
station_positions = []
|
||||
for station_id in station_ids:
|
||||
station_positions.append(cors_station_positions_dict[station_id][1])
|
||||
tree = cKDTree(station_positions)
|
||||
distances, idxs = tree.query(pos, k=k, distance_upper_bound=max_distance)
|
||||
return np.array(station_ids)[idxs]
|
||||
|
||||
|
||||
def load_cors_station_positions(cache_dir):
|
||||
cors_station_positions_path = cache_dir + 'cors_coord/cors_station_positions'
|
||||
download_and_parse_station_postions(cors_station_positions_path, cache_dir)
|
||||
with open(cors_station_positions_path, 'rb') as f:
|
||||
return np.load(f, allow_pickle=True).item() # pylint: disable=unexpected-keyword-arg
|
||||
|
||||
|
||||
def get_station_position(station_id, cache_dir='/tmp/gnss/', time=GPSTime.from_datetime(datetime.utcnow())):
|
||||
cors_station_positions_dict = load_cors_station_positions(cache_dir)
|
||||
epoch, pos, vel = cors_station_positions_dict[station_id]
|
||||
return ((time - epoch)/SECS_IN_YEAR)*np.array(vel) + np.array(pos)
|
||||
|
||||
|
||||
def parse_dgps(station_id, station_obs_file_path, dog, max_distance=100000, required_constellations=[ConstellationId.GPS]):
|
||||
station_pos = get_station_position(station_id, cache_dir=dog.cache_dir)
|
||||
obsdata = RINEXFile(station_obs_file_path)
|
||||
measurements = raw.read_rinex_obs(obsdata)
|
||||
|
||||
# if not all constellations in first 100 epochs bail
|
||||
detected_constellations = set()
|
||||
for m in sum(measurements[:100],[]):
|
||||
detected_constellations.add(get_constellation(m.prn))
|
||||
for constellation in required_constellations:
|
||||
if constellation not in detected_constellations:
|
||||
return None
|
||||
|
||||
proc_measurements = []
|
||||
for measurement in measurements:
|
||||
proc_measurements.append(raw.process_measurements(measurement, dog=dog))
|
||||
# sample at 30s
|
||||
if len(proc_measurements) > 2880:
|
||||
proc_measurements = proc_measurements[::int(len(proc_measurements)/2880)]
|
||||
if len(proc_measurements) != 2880:
|
||||
return None
|
||||
|
||||
station_delays = {}
|
||||
n = len(proc_measurements)
|
||||
for signal in ['C1C', 'C2P']:
|
||||
times = []
|
||||
station_delays[signal] = {}
|
||||
for i, proc_measurement in enumerate(proc_measurements):
|
||||
times.append(proc_measurement[0].recv_time)
|
||||
Fx_pos = opt.pr_residual(proc_measurement, signal=signal)
|
||||
residual, _ = Fx_pos(list(station_pos) + [0,0])
|
||||
residual = -np.array(residual)
|
||||
for j, m in enumerate(proc_measurement):
|
||||
prn = m.prn
|
||||
if prn not in station_delays[signal]:
|
||||
station_delays[signal][prn] = np.nan*np.ones(n)
|
||||
station_delays[signal][prn][i] = residual[j]
|
||||
assert len(times) == n
|
||||
|
||||
# TODO crude way to get dgps station's clock errors,
|
||||
# could this be biased? Only use GPS for convenience.
|
||||
model_delays = {}
|
||||
for prn in station_delays['C1C']:
|
||||
if get_constellation(prn) == ConstellationId.GPS:
|
||||
model_delays[prn] = np.nan*np.zeros(n)
|
||||
for i in range(n):
|
||||
model_delays[prn][i] = dog.get_delay(prn, times[i], station_pos, no_dgps=True)
|
||||
station_clock_errs = np.zeros(n)
|
||||
for i in range(n):
|
||||
station_clock_errs[i] = np.nanmean([(station_delays['C1C'][prn][i] - model_delays[prn][i]) for prn in model_delays])
|
||||
|
||||
# remove clock errors and smooth out signal
|
||||
for prn in station_delays['C1C']:
|
||||
station_delays['C1C'][prn] = mean_filter(station_delays['C1C'][prn] - station_clock_errs)
|
||||
for prn in station_delays['C2P']:
|
||||
station_delays['C2P'][prn] = station_delays['C2P'][prn] - station_clock_errs
|
||||
|
||||
return DGPSDelay(station_id, station_pos, station_delays,
|
||||
times, max_distance)
|
||||
|
||||
|
||||
class DGPSDelay:
|
||||
def __init__(self, station_id, station_pos,
|
||||
station_delays, station_delays_t, max_distance):
|
||||
self.id = station_id
|
||||
self.pos = station_pos
|
||||
self.delays = station_delays
|
||||
self.delays_t = station_delays_t
|
||||
self.max_distance = max_distance
|
||||
|
||||
def get_delay(self, prn, time, signal='C1C'):
|
||||
time_index = int((time - self.delays_t[0])/30)
|
||||
assert abs(self.delays_t[time_index] - time) < 30
|
||||
if prn in self.delays[signal] and np.isfinite(self.delays[signal][prn][time_index]):
|
||||
return self.delays[signal][prn][time_index]
|
||||
return None
|
||||
|
||||
def valid(self, time, recv_pos):
|
||||
return (np.linalg.norm(recv_pos - self.pos) <= self.max_distance and
|
||||
time - self.delays_t[0] > -30 and
|
||||
self.delays_t[-1] - time > -30)
|
||||
@@ -1,473 +0,0 @@
|
||||
import certifi
|
||||
import ftplib
|
||||
import hatanaka
|
||||
import os
|
||||
import pycurl
|
||||
import re
|
||||
import time
|
||||
import socket
|
||||
import logging
|
||||
|
||||
from datetime import datetime, timedelta
|
||||
from urllib.parse import urlparse
|
||||
from io import BytesIO
|
||||
from ftplib import FTP_TLS, FTP
|
||||
|
||||
from atomicwrites import atomic_write
|
||||
|
||||
from laika.ephemeris import EphemerisType
|
||||
from .constants import SECS_IN_HR, SECS_IN_DAY, SECS_IN_WEEK
|
||||
from .gps_time import GPSTime, tow_to_datetime
|
||||
from .helpers import ConstellationId
|
||||
|
||||
dir_path = os.path.dirname(os.path.realpath(__file__))
|
||||
|
||||
class DownloadFailed(Exception):
|
||||
pass
|
||||
|
||||
|
||||
def retryable(f):
|
||||
"""
|
||||
Decorator to allow us to pass multiple URLs from which to download.
|
||||
Automatically retry the request with the next URL on failure
|
||||
"""
|
||||
def wrapped(url_bases, *args, **kwargs):
|
||||
if isinstance(url_bases, str):
|
||||
# only one url passed, don't do the retry thing
|
||||
return f(url_bases, *args, **kwargs)
|
||||
|
||||
# not a string, must be a list of url_bases
|
||||
for url_base in url_bases:
|
||||
try:
|
||||
return f(url_base, *args, **kwargs)
|
||||
except DownloadFailed as e:
|
||||
logging.warning(e)
|
||||
# none of them succeeded
|
||||
raise DownloadFailed("Multiple URL failures attempting to pull file(s)")
|
||||
return wrapped
|
||||
|
||||
|
||||
def ftp_connect(url):
|
||||
parsed = urlparse(url)
|
||||
assert parsed.scheme == 'ftp'
|
||||
try:
|
||||
domain = parsed.netloc
|
||||
ftp = ftplib.FTP(domain, timeout=10)
|
||||
ftp.login()
|
||||
except (OSError, ftplib.error_perm):
|
||||
raise DownloadFailed("Could not connect/auth to: " + domain)
|
||||
try:
|
||||
ftp.cwd(parsed.path)
|
||||
except ftplib.error_perm:
|
||||
raise DownloadFailed("Permission failure with folder: " + url)
|
||||
return ftp
|
||||
|
||||
|
||||
@retryable
|
||||
def list_dir(url):
|
||||
parsed = urlparse(url)
|
||||
if parsed.scheme == 'ftp':
|
||||
try:
|
||||
ftp = ftp_connect(url)
|
||||
return ftp.nlst()
|
||||
except ftplib.error_perm:
|
||||
raise DownloadFailed("Permission failure listing folder: " + url)
|
||||
else:
|
||||
# just connect and do simple url parsing
|
||||
listing = https_download_file(url)
|
||||
urls = re.findall(b"<a href=\"([^\"]+)\">", listing)
|
||||
# decode the urls to normal strings. If they are complicated paths, ignore them
|
||||
return [name.decode("latin1") for name in urls if name and b"/" not in name[1:]]
|
||||
|
||||
|
||||
def ftp_download_files(url_base, folder_path, cacheDir, filenames):
|
||||
"""
|
||||
Like download file, but more of them. Keeps a persistent FTP connection open
|
||||
to be more efficient.
|
||||
"""
|
||||
folder_path_abs = os.path.join(cacheDir, folder_path)
|
||||
|
||||
ftp = ftp_connect(url_base + folder_path)
|
||||
|
||||
filepaths = []
|
||||
for filename in filenames:
|
||||
# os.path.join will be dumb if filename has a leading /
|
||||
# if there is a / in the filename, then it's using a different folder
|
||||
filename = filename.lstrip("/")
|
||||
if "/" in filename:
|
||||
continue
|
||||
filepath = os.path.join(folder_path_abs, filename)
|
||||
logging.debug("pulling from", url_base, "to", filepath)
|
||||
|
||||
if not os.path.isfile(filepath):
|
||||
os.makedirs(folder_path_abs, exist_ok=True)
|
||||
try:
|
||||
ftp.retrbinary('RETR ' + filename, open(filepath, 'wb').write)
|
||||
except (ftplib.error_perm):
|
||||
raise DownloadFailed("Could not download file from: " + url_base + folder_path + filename)
|
||||
except (socket.timeout):
|
||||
raise DownloadFailed("Read timed out from: " + url_base + folder_path + filename)
|
||||
filepaths.append(filepath)
|
||||
else:
|
||||
filepaths.append(filepath)
|
||||
return filepaths
|
||||
|
||||
|
||||
def http_download_files(url_base, folder_path, cacheDir, filenames):
|
||||
"""
|
||||
Similar to ftp_download_files, attempt to download multiple files faster than
|
||||
just downloading them one-by-one.
|
||||
Returns a list of filepaths instead of the raw data
|
||||
"""
|
||||
folder_path_abs = os.path.join(cacheDir, folder_path)
|
||||
|
||||
def write_function(disk_path, handle):
|
||||
def do_write(data):
|
||||
open(disk_path, "wb").write(data)
|
||||
|
||||
return do_write
|
||||
|
||||
fetcher = pycurl.CurlMulti()
|
||||
fetcher.setopt(pycurl.M_PIPELINING, 3)
|
||||
fetcher.setopt(pycurl.M_MAX_HOST_CONNECTIONS, 64)
|
||||
fetcher.setopt(pycurl.M_MAX_TOTAL_CONNECTIONS, 64)
|
||||
filepaths = []
|
||||
for filename in filenames:
|
||||
# os.path.join will be dumb if filename has a leading /
|
||||
# if there is a / in the filename, then it's using a different folder
|
||||
filename = filename.lstrip("/")
|
||||
if "/" in filename:
|
||||
continue
|
||||
filepath = os.path.join(folder_path_abs, filename)
|
||||
if not os.path.isfile(filepath):
|
||||
logging.debug("pulling from", url_base, "to", filepath)
|
||||
os.makedirs(folder_path_abs, exist_ok=True)
|
||||
url_path = url_base + folder_path + filename
|
||||
handle = pycurl.Curl()
|
||||
handle.setopt(pycurl.URL, url_path)
|
||||
handle.setopt(pycurl.CONNECTTIMEOUT, 10)
|
||||
handle.setopt(pycurl.WRITEFUNCTION, write_function(filepath, handle))
|
||||
fetcher.add_handle(handle)
|
||||
filepaths.append(filepath)
|
||||
|
||||
requests_processing = len(filepaths)
|
||||
timeout = 10.0 # after 10 seconds of nothing happening, restart
|
||||
deadline = time.time() + timeout
|
||||
while requests_processing and time.time() < deadline:
|
||||
while True:
|
||||
ret, cur_requests_processing = fetcher.perform()
|
||||
if ret != pycurl.E_CALL_MULTI_PERFORM:
|
||||
_, success, failed = fetcher.info_read()
|
||||
break
|
||||
if requests_processing > cur_requests_processing:
|
||||
deadline = time.time() + timeout
|
||||
requests_processing = cur_requests_processing
|
||||
|
||||
if fetcher.select(1) < 0:
|
||||
continue
|
||||
|
||||
# if there are downloads left to be done, repeat, and don't overwrite
|
||||
_, requests_processing = fetcher.perform()
|
||||
if requests_processing > 0:
|
||||
logging.warning("some requests stalled, retrying them")
|
||||
return http_download_files(url_base, folder_path, cacheDir, filenames)
|
||||
|
||||
return filepaths
|
||||
|
||||
|
||||
def https_download_file(url):
|
||||
crl = pycurl.Curl()
|
||||
crl.setopt(crl.CAINFO, certifi.where())
|
||||
crl.setopt(crl.URL, url)
|
||||
crl.setopt(crl.FOLLOWLOCATION, True)
|
||||
crl.setopt(crl.SSL_CIPHER_LIST, 'DEFAULT@SECLEVEL=1')
|
||||
crl.setopt(crl.COOKIEJAR, '/tmp/cddis_cookies')
|
||||
crl.setopt(pycurl.CONNECTTIMEOUT, 10)
|
||||
|
||||
buf = BytesIO()
|
||||
crl.setopt(crl.WRITEDATA, buf)
|
||||
crl.perform()
|
||||
response = crl.getinfo(pycurl.RESPONSE_CODE)
|
||||
crl.close()
|
||||
|
||||
if response != 200:
|
||||
raise DownloadFailed('HTTPS error ' + str(response))
|
||||
return buf.getvalue()
|
||||
|
||||
|
||||
def ftp_download_file(url):
|
||||
parsed = urlparse(url)
|
||||
is_sftp = parsed.scheme == "sftp"
|
||||
try:
|
||||
buf = BytesIO()
|
||||
with FTP_TLS(parsed.hostname) if is_sftp else FTP(parsed.hostname) as ftp:
|
||||
ftp.login(user='anonymous')
|
||||
if is_sftp:
|
||||
ftp.prot_p()
|
||||
ftp.retrbinary('RETR ' + parsed.path, buf.write)
|
||||
return buf.getvalue()
|
||||
except ftplib.all_errors as e:
|
||||
raise DownloadFailed(e)
|
||||
|
||||
|
||||
@retryable
|
||||
def download_files(url_base, folder_path, cacheDir, filenames):
|
||||
parsed = urlparse(url_base)
|
||||
if parsed.scheme == 'ftp':
|
||||
return ftp_download_files(url_base, folder_path, cacheDir, filenames)
|
||||
else:
|
||||
return http_download_files(url_base, folder_path, cacheDir, filenames)
|
||||
|
||||
|
||||
@retryable
|
||||
def download_file(url_base, folder_path, filename_zipped):
|
||||
url = url_base + folder_path + filename_zipped
|
||||
logging.debug('Downloading ' + url)
|
||||
if url.startswith('https://'):
|
||||
return https_download_file(url)
|
||||
elif url.startswith(('ftp://', 'sftp://')):
|
||||
return ftp_download_file(url)
|
||||
raise NotImplementedError('Did not find supported url scheme')
|
||||
|
||||
|
||||
def download_and_cache_file_return_first_success(url_bases, folder_and_file_names, cache_dir, compression='', overwrite=False, raise_error=False):
|
||||
last_error = None
|
||||
for folder_path, filename in folder_and_file_names:
|
||||
try:
|
||||
file = download_and_cache_file(url_bases, folder_path, cache_dir, filename, compression, overwrite)
|
||||
return file
|
||||
except DownloadFailed as e:
|
||||
last_error = e
|
||||
|
||||
if last_error and raise_error:
|
||||
raise last_error
|
||||
|
||||
|
||||
def download_and_cache_file(url_base, folder_path: str, cache_dir: str, filename: str, compression='', overwrite=False):
|
||||
filename_zipped = filename + compression
|
||||
folder_path_abs = os.path.join(cache_dir, folder_path)
|
||||
filepath = str(hatanaka.get_decompressed_path(os.path.join(folder_path_abs, filename)))
|
||||
|
||||
filepath_attempt = filepath + '.attempt_time'
|
||||
|
||||
if os.path.exists(filepath_attempt):
|
||||
with open(filepath_attempt, 'r') as rf:
|
||||
last_attempt_time = float(rf.read())
|
||||
if time.time() - last_attempt_time < SECS_IN_HR:
|
||||
raise DownloadFailed(f"Too soon to try downloading {folder_path + filename_zipped} from {url_base} again since last attempt")
|
||||
if not os.path.isfile(filepath) or overwrite:
|
||||
try:
|
||||
data_zipped = download_file(url_base, folder_path, filename_zipped)
|
||||
except (DownloadFailed, pycurl.error, socket.timeout):
|
||||
unix_time = time.time()
|
||||
os.makedirs(folder_path_abs, exist_ok=True)
|
||||
with atomic_write(filepath_attempt, mode='w', overwrite=True) as wf:
|
||||
wf.write(str(unix_time))
|
||||
raise DownloadFailed(f"Could not download {folder_path + filename_zipped} from {url_base} ")
|
||||
|
||||
os.makedirs(folder_path_abs, exist_ok=True)
|
||||
ephem_bytes = hatanaka.decompress(data_zipped)
|
||||
try:
|
||||
with atomic_write(filepath, mode='wb', overwrite=overwrite) as f:
|
||||
f.write(ephem_bytes)
|
||||
except FileExistsError:
|
||||
# Only happens when same file is downloaded in parallel by another process.
|
||||
pass
|
||||
return filepath
|
||||
|
||||
|
||||
# Currently, only GPS and Glonass are supported for daily and hourly data.
|
||||
CONSTELLATION_NASA_CHAR = {ConstellationId.GPS: 'n', ConstellationId.GLONASS: 'g'}
|
||||
|
||||
|
||||
def download_nav(time: GPSTime, cache_dir, constellation: ConstellationId):
|
||||
t = time.as_datetime()
|
||||
try:
|
||||
if constellation not in CONSTELLATION_NASA_CHAR:
|
||||
return None
|
||||
c = CONSTELLATION_NASA_CHAR[constellation]
|
||||
if GPSTime.from_datetime(datetime.utcnow()) - time > SECS_IN_DAY:
|
||||
url_bases = (
|
||||
'https://github.com/commaai/gnss-data/raw/master/gnss/data/daily/',
|
||||
'sftp://gdc.cddis.eosdis.nasa.gov/gnss/data/daily/',
|
||||
)
|
||||
filename = t.strftime(f"brdc%j0.%y{c}")
|
||||
folder_path = t.strftime(f'%Y/%j/%y{c}/')
|
||||
compression = '.gz' if folder_path >= '2020/335/' else '.Z'
|
||||
return download_and_cache_file(url_bases, folder_path, cache_dir+'daily_nav/', filename, compression)
|
||||
else:
|
||||
url_bases = (
|
||||
'https://github.com/commaai/gnss-data-hourly/raw/master/',
|
||||
'sftp://gdc.cddis.eosdis.nasa.gov/gnss/data/hourly/',
|
||||
)
|
||||
times = [t, (t - timedelta(hours=1))]
|
||||
folder_and_filenames = [(t.strftime('%Y/%j/'), t.strftime(f"hour%j0.%y{c}")) for t in times]
|
||||
compression = '.gz' if folder_and_filenames[0][0] >= '2020/336/' else '.Z'
|
||||
# always overwrite as this file is appended
|
||||
return download_and_cache_file_return_first_success(url_bases,
|
||||
folder_and_filenames, cache_dir+'hourly_nav/', compression, overwrite=True)
|
||||
except DownloadFailed:
|
||||
pass
|
||||
|
||||
|
||||
def download_orbits_gps_cod0(time, cache_dir, ephem_types):
|
||||
url_bases = (
|
||||
'https://github.com/commaai/gnss-data/raw/master/gnss/products/',
|
||||
'sftp://gdc.cddis.eosdis.nasa.gov/gnss/products/',
|
||||
)
|
||||
|
||||
if EphemerisType.ULTRA_RAPID_ORBIT not in ephem_types:
|
||||
# TODO: raise error here
|
||||
return None
|
||||
|
||||
tm = tow_to_datetime(time.tow, time.week).timetuple()
|
||||
doy = str(tm.tm_yday).zfill(3)
|
||||
filename = f"COD0OPSULT_{tm.tm_year}{doy}0000_02D_05M_ORB.SP3"
|
||||
# TODO: add hour management
|
||||
|
||||
folder_path = "%i/" % time.week
|
||||
folder_file_names = [(folder_path, filename)]
|
||||
return download_and_cache_file_return_first_success(url_bases, folder_file_names, cache_dir+'cddis_products/', compression='.gz')
|
||||
|
||||
|
||||
def download_orbits_gps(time, cache_dir, ephem_types):
|
||||
url_bases = (
|
||||
'https://github.com/commaai/gnss-data/raw/master/gnss/products/',
|
||||
'sftp://gdc.cddis.eosdis.nasa.gov/gnss/products/',
|
||||
'ftp://igs.ign.fr/pub/igs/products/',
|
||||
)
|
||||
folder_path = "%i/" % time.week
|
||||
filenames = []
|
||||
time_str = "%i%i" % (time.week, time.day)
|
||||
# Download filenames in order of quality. Final -> Rapid -> Ultra-Rapid(newest first)
|
||||
if EphemerisType.FINAL_ORBIT in ephem_types and GPSTime.from_datetime(datetime.utcnow()) - time > 3 * SECS_IN_WEEK:
|
||||
filenames.append(f"igs{time_str}.sp3")
|
||||
if EphemerisType.RAPID_ORBIT in ephem_types:
|
||||
filenames.append(f"igr{time_str}.sp3")
|
||||
if EphemerisType.ULTRA_RAPID_ORBIT in ephem_types:
|
||||
filenames.extend([f"igu{time_str}_18.sp3",
|
||||
f"igu{time_str}_12.sp3",
|
||||
f"igu{time_str}_06.sp3",
|
||||
f"igu{time_str}_00.sp3"])
|
||||
folder_file_names = [(folder_path, filename) for filename in filenames]
|
||||
ret = download_and_cache_file_return_first_success(url_bases, folder_file_names, cache_dir+'cddis_products/', compression='.Z')
|
||||
if ret is not None:
|
||||
return ret
|
||||
|
||||
# fallback to COD0 Ultra Rapid Orbits
|
||||
return download_orbits_gps_cod0(time, cache_dir, ephem_types)
|
||||
|
||||
|
||||
def download_prediction_orbits_russia_src(gps_time, cache_dir):
|
||||
# Download single file that contains Ultra_Rapid predictions for GPS, GLONASS and other constellations
|
||||
t = gps_time.as_datetime()
|
||||
# Files exist starting at 29-01-2022
|
||||
if t < datetime(2022, 1, 29):
|
||||
return None
|
||||
url_bases = 'https://github.com/commaai/gnss-data-alt/raw/master/MCC/PRODUCTS/'
|
||||
folder_path = t.strftime('%y%j/ultra/')
|
||||
file_prefix = "Stark_1D_" + t.strftime('%y%m%d')
|
||||
|
||||
# Predictions are 24H so previous day can also be used.
|
||||
prev_day = (t - timedelta(days=1))
|
||||
file_prefix_prev = "Stark_1D_" + prev_day.strftime('%y%m%d')
|
||||
folder_path_prev = prev_day.strftime('%y%j/ultra/')
|
||||
|
||||
current_day = GPSTime.from_datetime(datetime(t.year, t.month, t.day))
|
||||
# Ultra-Orbit is published in gnss-data-alt every 10th minute past the 5,11,17,23 hour.
|
||||
# Predictions published are delayed by around 10 hours.
|
||||
# Download latest file that includes gps_time with 20 minutes margin.:
|
||||
if gps_time > current_day + 23.5 * SECS_IN_HR:
|
||||
prev_day, current_day = [], [6, 12]
|
||||
elif gps_time > current_day + 17.5 * SECS_IN_HR:
|
||||
prev_day, current_day = [], [0, 6]
|
||||
elif gps_time > current_day + 11.5 * SECS_IN_HR:
|
||||
prev_day, current_day = [18], [0]
|
||||
elif gps_time > current_day + 5.5 * SECS_IN_HR:
|
||||
prev_day, current_day = [12, 18], []
|
||||
else:
|
||||
prev_day, current_day = [6, 12], []
|
||||
# Example: Stark_1D_22060100.sp3
|
||||
folder_and_file_names = [(folder_path, file_prefix + f"{h:02}.sp3") for h in reversed(current_day)] + \
|
||||
[(folder_path_prev, file_prefix_prev + f"{h:02}.sp3") for h in reversed(prev_day)]
|
||||
return download_and_cache_file_return_first_success(url_bases, folder_and_file_names, cache_dir+'russian_products/', raise_error=True)
|
||||
|
||||
|
||||
def download_orbits_russia_src(time, cache_dir, ephem_types):
|
||||
# Orbits from russian source. Contains GPS, GLONASS, GALILEO, BEIDOU
|
||||
url_bases = (
|
||||
'https://github.com/commaai/gnss-data-alt/raw/master/MCC/PRODUCTS/',
|
||||
'ftp://ftp.glonass-iac.ru/MCC/PRODUCTS/',
|
||||
)
|
||||
t = time.as_datetime()
|
||||
folder_paths = []
|
||||
current_gps_time = GPSTime.from_datetime(datetime.utcnow())
|
||||
filename = "Sta%i%i.sp3" % (time.week, time.day)
|
||||
if EphemerisType.FINAL_ORBIT in ephem_types and current_gps_time - time > 2 * SECS_IN_WEEK:
|
||||
folder_paths.append(t.strftime('%y%j/final/'))
|
||||
if EphemerisType.RAPID_ORBIT in ephem_types:
|
||||
folder_paths.append(t.strftime('%y%j/rapid/'))
|
||||
if EphemerisType.ULTRA_RAPID_ORBIT in ephem_types:
|
||||
folder_paths.append(t.strftime('%y%j/ultra/'))
|
||||
folder_file_names = [(folder_path, filename) for folder_path in folder_paths]
|
||||
return download_and_cache_file_return_first_success(url_bases, folder_file_names, cache_dir+'russian_products/')
|
||||
|
||||
|
||||
def download_ionex(time, cache_dir):
|
||||
t = time.as_datetime()
|
||||
url_bases = (
|
||||
'https://github.com/commaai/gnss-data/raw/master/gnss/products/ionex/',
|
||||
'sftp://gdc.cddis.eosdis.nasa.gov/gnss/products/ionex/',
|
||||
'ftp://igs.ensg.ign.fr/pub/igs/products/ionosphere/',
|
||||
'ftp://gssc.esa.int/gnss/products/ionex/',
|
||||
)
|
||||
folder_path = t.strftime('%Y/%j/')
|
||||
filenames = [t.strftime("codg%j0.%yi"), t.strftime("c1pg%j0.%yi"), t.strftime("c2pg%j0.%yi")]
|
||||
|
||||
folder_file_names = [(folder_path, f) for f in filenames]
|
||||
return download_and_cache_file_return_first_success(url_bases, folder_file_names, cache_dir+'ionex/', compression='.Z', raise_error=True)
|
||||
|
||||
|
||||
def download_dcb(time, cache_dir):
|
||||
filenames = []
|
||||
folder_paths = []
|
||||
url_bases = (
|
||||
'https://github.com/commaai/gnss-data/raw/master/gnss/products/bias/',
|
||||
'sftp://gdc.cddis.eosdis.nasa.gov/gnss/products/bias/',
|
||||
'ftp://igs.ign.fr/pub/igs/products/mgex/dcb/',
|
||||
)
|
||||
# seem to be a lot of data missing, so try many days
|
||||
for time_step in [time - i * SECS_IN_DAY for i in range(14)]:
|
||||
t = time_step.as_datetime()
|
||||
folder_paths.append(t.strftime('%Y/'))
|
||||
filenames.append(t.strftime("CAS0MGXRAP_%Y%j0000_01D_01D_DCB.BSX"))
|
||||
|
||||
return download_and_cache_file_return_first_success(url_bases, list(zip(folder_paths, filenames)), cache_dir+'dcb/', compression='.gz', raise_error=True)
|
||||
|
||||
|
||||
def download_cors_coords(cache_dir):
|
||||
cache_subdir = cache_dir + 'cors_coord/'
|
||||
url_bases = (
|
||||
'https://geodesy.noaa.gov/corsdata/coord/coord_14/',
|
||||
'https://alt.ngs.noaa.gov/corsdata/coord/coord_14/',
|
||||
)
|
||||
file_names = list_dir(url_bases)
|
||||
file_names = [file_name for file_name in file_names if file_name.endswith('coord.txt')]
|
||||
filepaths = download_files(url_bases, '', cache_subdir, file_names)
|
||||
return filepaths
|
||||
|
||||
|
||||
def download_cors_station(time, station_name, cache_dir):
|
||||
t = time.as_datetime()
|
||||
folder_path = t.strftime('%Y/%j/') + station_name + '/'
|
||||
filename = station_name + t.strftime("%j0.%yd")
|
||||
url_bases = (
|
||||
'https://geodesy.noaa.gov/corsdata/rinex/',
|
||||
'https://alt.ngs.noaa.gov/corsdata/rinex/',
|
||||
)
|
||||
try:
|
||||
filepath = download_and_cache_file(url_bases, folder_path, cache_dir+'cors_obs/', filename, compression='.gz')
|
||||
return filepath
|
||||
except DownloadFailed:
|
||||
logging.warning("File not downloaded, check availability on server.")
|
||||
return None
|
||||
@@ -1,106 +0,0 @@
|
||||
@0xb3ca6d2462778bb1;
|
||||
|
||||
struct Ephemeris {
|
||||
# This is according to the rinex (2?) format
|
||||
svId @0 :UInt16;
|
||||
year @1 :UInt16;
|
||||
month @2 :UInt16;
|
||||
day @3 :UInt16;
|
||||
hour @4 :UInt16;
|
||||
minute @5 :UInt16;
|
||||
second @6 :Float32;
|
||||
af0 @7 :Float64;
|
||||
af1 @8 :Float64;
|
||||
af2 @9 :Float64;
|
||||
|
||||
iode @10 :Float64;
|
||||
crs @11 :Float64;
|
||||
deltaN @12 :Float64;
|
||||
m0 @13 :Float64;
|
||||
|
||||
cuc @14 :Float64;
|
||||
ecc @15 :Float64;
|
||||
cus @16 :Float64;
|
||||
a @17 :Float64; # note that this is not the root!!
|
||||
|
||||
toe @18 :Float64;
|
||||
cic @19 :Float64;
|
||||
omega0 @20 :Float64;
|
||||
cis @21 :Float64;
|
||||
|
||||
i0 @22 :Float64;
|
||||
crc @23 :Float64;
|
||||
omega @24 :Float64;
|
||||
omegaDot @25 :Float64;
|
||||
|
||||
iDot @26 :Float64;
|
||||
codesL2 @27 :Float64;
|
||||
gpsWeekDEPRECATED @28 :Float64;
|
||||
l2 @29 :Float64;
|
||||
|
||||
svAcc @30 :Float64;
|
||||
svHealth @31 :Float64;
|
||||
tgd @32 :Float64;
|
||||
iodc @33 :Float64;
|
||||
|
||||
transmissionTime @34 :Float64;
|
||||
fitInterval @35 :Float64;
|
||||
|
||||
toc @36 :Float64;
|
||||
|
||||
ionoCoeffsValid @37 :Bool;
|
||||
ionoAlpha @38 :List(Float64);
|
||||
ionoBeta @39 :List(Float64);
|
||||
|
||||
towCount @40 :UInt32;
|
||||
toeWeek @41 :UInt16;
|
||||
tocWeek @42 :UInt16;
|
||||
}
|
||||
|
||||
struct GlonassEphemeris {
|
||||
svId @0 :UInt16;
|
||||
year @1 :UInt16;
|
||||
dayInYear @2 :UInt16;
|
||||
hour @3 :UInt16;
|
||||
minute @4 :UInt16;
|
||||
second @5 :Float32;
|
||||
|
||||
x @6 :Float64;
|
||||
xVel @7 :Float64;
|
||||
xAccel @8 :Float64;
|
||||
y @9 :Float64;
|
||||
yVel @10 :Float64;
|
||||
yAccel @11 :Float64;
|
||||
z @12 :Float64;
|
||||
zVel @13 :Float64;
|
||||
zAccel @14 :Float64;
|
||||
|
||||
svType @15 :UInt8;
|
||||
svURA @16 :Float32;
|
||||
age @17 :UInt8;
|
||||
|
||||
svHealth @18 :UInt8;
|
||||
tkDEPRECATED @19 :UInt16;
|
||||
tb @20 :UInt16;
|
||||
|
||||
tauN @21 :Float64;
|
||||
deltaTauN @22 :Float64;
|
||||
gammaN @23 :Float64;
|
||||
|
||||
p1 @24 :UInt8;
|
||||
p2 @25 :UInt8;
|
||||
p3 @26 :UInt8;
|
||||
p4 @27 :UInt8;
|
||||
|
||||
freqNumDEPRECATED @28 :UInt32;
|
||||
|
||||
n4 @29 :UInt8;
|
||||
nt @30 :UInt16;
|
||||
freqNum @31 :Int16;
|
||||
tkSeconds @32 :UInt32;
|
||||
}
|
||||
|
||||
struct EphemerisCache {
|
||||
gpsEphemerides @0 :List(Ephemeris);
|
||||
glonassEphemerides @1 :List(GlonassEphemeris);
|
||||
}
|
||||
@@ -1,498 +0,0 @@
|
||||
import warnings
|
||||
from abc import ABC, abstractmethod
|
||||
from collections import defaultdict
|
||||
from enum import IntEnum
|
||||
from typing import Dict, List, Optional
|
||||
|
||||
import numpy as np
|
||||
import numpy.polynomial.polynomial as poly
|
||||
from datetime import datetime
|
||||
from math import sin, cos, sqrt, fabs, atan2
|
||||
|
||||
from .gps_time import GPSTime, utc_to_gpst
|
||||
from .constants import SPEED_OF_LIGHT, SECS_IN_MIN, SECS_IN_HR, SECS_IN_DAY, \
|
||||
EARTH_ROTATION_RATE, EARTH_GM
|
||||
from .helpers import get_constellation, get_prn_from_nmea_id
|
||||
|
||||
import capnp
|
||||
import os
|
||||
capnp.remove_import_hook()
|
||||
capnp_path = os.path.abspath(os.path.join(os.path.dirname(os.path.realpath(__file__)), "ephemeris.capnp"))
|
||||
ephemeris_structs = capnp.load(capnp_path)
|
||||
|
||||
|
||||
def read4(f, rinex_ver):
|
||||
line = f.readline()[:-1]
|
||||
if rinex_ver == 2:
|
||||
line = ' ' + line # Shift 1 char to the right
|
||||
line = line.replace('D', 'E') # Handle bizarro float format
|
||||
return float(line[4:23]), float(line[23:42]), float(line[42:61]), float(line[61:80])
|
||||
|
||||
|
||||
class EphemerisType(IntEnum):
|
||||
# Matches the enum in log.capnp
|
||||
NAV = 0
|
||||
FINAL_ORBIT = 1
|
||||
RAPID_ORBIT = 2
|
||||
ULTRA_RAPID_ORBIT = 3
|
||||
QCOM_POLY = 4
|
||||
|
||||
@staticmethod
|
||||
def all_orbits():
|
||||
return EphemerisType.FINAL_ORBIT, EphemerisType.RAPID_ORBIT, EphemerisType.ULTRA_RAPID_ORBIT
|
||||
|
||||
@classmethod
|
||||
def from_file_name(cls, file_name: str):
|
||||
if "/final" in file_name or "/igs" in file_name:
|
||||
return EphemerisType.FINAL_ORBIT
|
||||
if "/rapid" in file_name or "/igr" in file_name:
|
||||
return EphemerisType.RAPID_ORBIT
|
||||
if "/ultra" in file_name or "/igu" in file_name or "COD0OPSULT" in file_name:
|
||||
return EphemerisType.ULTRA_RAPID_ORBIT
|
||||
raise RuntimeError(f"Ephemeris type not found in filename: {file_name}")
|
||||
|
||||
|
||||
class Ephemeris(ABC):
|
||||
|
||||
def __init__(self, prn: str, epoch: GPSTime, eph_type: EphemerisType, healthy: bool, max_time_diff: float,
|
||||
file_epoch: Optional[GPSTime] = None, file_name=None):
|
||||
self.prn = prn
|
||||
self.epoch = epoch
|
||||
self.eph_type = eph_type
|
||||
self.healthy = healthy
|
||||
self.max_time_diff = max_time_diff
|
||||
self.file_epoch = file_epoch
|
||||
self.file_name = file_name
|
||||
self.file_source = '' if file_name is None else file_name.split('/')[-1][:3] # File source for the ephemeris (e.g. igu, igr, Sta)
|
||||
|
||||
def valid(self, time):
|
||||
return abs(time - self.epoch) <= self.max_time_diff
|
||||
|
||||
def __repr__(self):
|
||||
time = self.epoch.as_datetime().strftime('%Y-%m-%dT%H:%M:%S.%f')
|
||||
return f"<{self.__class__.__name__} from {self.prn} at {time}>"
|
||||
|
||||
def get_sat_info(self, time: GPSTime):
|
||||
"""
|
||||
Returns: (pos, vel, clock_err, clock_rate_err, ephemeris)
|
||||
"""
|
||||
if not self.healthy:
|
||||
return None
|
||||
return list(self._get_sat_info(time)) + [self]
|
||||
|
||||
@abstractmethod
|
||||
def _get_sat_info(self, time):
|
||||
pass
|
||||
|
||||
|
||||
class GLONASSEphemeris(Ephemeris):
|
||||
def __init__(self, data, file_name=None):
|
||||
self.epoch = GPSTime.from_glonass(data.n4, data.nt, data.tb*15*SECS_IN_MIN)
|
||||
super().__init__('R%02i' % data.svId, self.epoch, EphemerisType.NAV, data.svHealth==0, max_time_diff=25*SECS_IN_MIN, file_name=file_name)
|
||||
self.data = data
|
||||
self.epoch = GPSTime.from_glonass(data.n4, data.nt, data.tb*15 * SECS_IN_MIN)
|
||||
self.channel = data.freqNum
|
||||
|
||||
def _get_sat_info(self, time: GPSTime):
|
||||
# see the russian doc for this:
|
||||
# http://gauss.gge.unb.ca/GLONASS.ICD.pdf
|
||||
|
||||
eph = self.data
|
||||
tdiff = time - self.epoch
|
||||
# Clock correction (except for general relativity which is applied later)
|
||||
clock_err = -eph.tauN + tdiff * eph.gammaN
|
||||
clock_rate_err = eph.gammaN
|
||||
|
||||
def glonass_diff_eq(state, acc):
|
||||
J2 = 1.0826257e-3
|
||||
mu = 3.9860044e14
|
||||
omega = 7.292115e-5
|
||||
ae = 6378136.0
|
||||
r = np.sqrt(state[0]**2 + state[1]**2 + state[2]**2)
|
||||
ders = np.zeros(6)
|
||||
if r**2 < 0:
|
||||
return ders
|
||||
a = 1.5 * J2 * mu * (ae**2)/ (r**5)
|
||||
b = 5 * (state[2]**2) / (r**2)
|
||||
c = -mu/(r**3) - a*(1-b)
|
||||
|
||||
ders[0:3] = state[3:6]
|
||||
ders[3] = (c + omega**2)*state[0] + 2*omega*state[4] + acc[0]
|
||||
ders[4] = (c + omega**2)*state[1] - 2*omega*state[3] + acc[1]
|
||||
ders[5] = (c - 2*a)*state[2] + acc[2]
|
||||
return ders
|
||||
|
||||
init_state = np.empty(6)
|
||||
init_state[0] = eph.x
|
||||
init_state[1] = eph.y
|
||||
init_state[2] = eph.z
|
||||
init_state[3] = eph.xVel
|
||||
init_state[4] = eph.yVel
|
||||
init_state[5] = eph.zVel
|
||||
init_state = 1000*init_state
|
||||
acc = 1000*np.array([eph.xAccel, eph.yAccel, eph.zAccel])
|
||||
state = init_state
|
||||
tstep = 90
|
||||
if tdiff < 0:
|
||||
tt = -tstep
|
||||
elif tdiff > 0:
|
||||
tt = tstep
|
||||
while abs(tdiff) > 1e-9:
|
||||
if abs(tdiff) < tstep:
|
||||
tt = tdiff
|
||||
k1 = glonass_diff_eq(state, acc)
|
||||
k2 = glonass_diff_eq(state + k1*tt/2, -acc)
|
||||
k3 = glonass_diff_eq(state + k2*tt/2, -acc)
|
||||
k4 = glonass_diff_eq(state + k3*tt, -acc)
|
||||
state += (k1 + 2*k2 + 2*k3 + k4)*tt/6.0
|
||||
tdiff -= tt
|
||||
|
||||
pos = state[0:3]
|
||||
vel = state[3:6]
|
||||
return pos, vel, clock_err, clock_rate_err
|
||||
|
||||
|
||||
class PolyEphemeris(Ephemeris):
|
||||
def __init__(self, prn: str, data, epoch: GPSTime, ephem_type: EphemerisType,
|
||||
file_epoch: Optional[GPSTime] = None, file_name: Optional[str] = None, healthy=True, tgd=0,
|
||||
max_time_diff: int=SECS_IN_HR):
|
||||
super().__init__(prn, epoch, ephem_type, healthy, max_time_diff=max_time_diff, file_epoch=file_epoch, file_name=file_name)
|
||||
self.data = data
|
||||
self.tgd = tgd
|
||||
|
||||
def _get_sat_info(self, time: GPSTime):
|
||||
dt = time - self.data['t0']
|
||||
deg = self.data['deg']
|
||||
deg_t = self.data['deg_t']
|
||||
indices = np.arange(deg+1)[:,np.newaxis]
|
||||
sat_pos = np.sum((dt**indices)*self.data['xyz'], axis=0)
|
||||
indices = indices[1:]
|
||||
sat_vel = np.sum(indices*(dt**(indices-1)*self.data['xyz'][1:]), axis=0)
|
||||
time_err = sum((dt**p)*self.data['clock'][deg_t-p] for p in range(deg_t+1))
|
||||
time_err_rate = sum(p*(dt**(p-1))*self.data['clock'][deg_t-p] for p in range(1,deg_t+1))
|
||||
time_err_with_rel = time_err - 2*np.inner(sat_pos, sat_vel)/SPEED_OF_LIGHT**2
|
||||
return sat_pos, sat_vel, time_err_with_rel, time_err_rate
|
||||
|
||||
|
||||
class GPSEphemeris(Ephemeris):
|
||||
def __init__(self, data, file_name=None):
|
||||
self.toe = GPSTime(data.toeWeek, data.toe)
|
||||
self.toc = GPSTime(data.tocWeek, data.toc)
|
||||
self.epoch = self.toc
|
||||
|
||||
super().__init__('G%02i' % data.svId, self.epoch, EphemerisType.NAV, data.svHealth==0, max_time_diff=2*SECS_IN_HR, file_name=file_name)
|
||||
self.max_time_diff_tgd = SECS_IN_DAY
|
||||
self.data = data
|
||||
self.sqrta = np.sqrt(data.a)
|
||||
|
||||
def get_tgd(self):
|
||||
return self.datatgd
|
||||
|
||||
def _get_sat_info(self, time: GPSTime):
|
||||
eph = self.data
|
||||
tdiff = time - self.toc # Time of clock
|
||||
clock_err = eph.af0 + tdiff * (eph.af1 + tdiff * eph.af2)
|
||||
clock_rate_err = eph.af1 + 2 * tdiff * eph.af2\
|
||||
|
||||
# Orbit propagation
|
||||
tdiff = time - self.toe # Time of ephemeris (might be different from time of clock)
|
||||
|
||||
# Calculate position per IS-GPS-200D p 97 Table 20-IV
|
||||
a = self.sqrta * self.sqrta # [m] Semi-major axis
|
||||
ma_dot = sqrt(EARTH_GM / (a * a * a)) + eph.deltaN # [rad/sec] Corrected mean motion
|
||||
ma = eph.m0 + ma_dot * tdiff # [rad] Corrected mean anomaly
|
||||
|
||||
# Iteratively solve for the Eccentric Anomaly (from Keith Alter and David Johnston)
|
||||
ea = ma # Starting value for E
|
||||
|
||||
ea_old = 2222
|
||||
while fabs(ea - ea_old) > 1.0E-14:
|
||||
ea_old = ea
|
||||
tempd1 = 1.0 - eph.ecc * cos(ea_old)
|
||||
ea = ea + (ma - ea_old + eph.ecc * sin(ea_old)) / tempd1
|
||||
ea_dot = ma_dot / tempd1
|
||||
|
||||
# Relativistic correction term
|
||||
einstein = -4.442807633E-10 * eph.ecc * self.sqrta * sin(ea)
|
||||
|
||||
# Begin calc for True Anomaly and Argument of Latitude
|
||||
tempd2 = sqrt(1.0 - eph.ecc * eph.ecc)
|
||||
# [rad] Argument of Latitude = True Anomaly + Argument of Perigee
|
||||
al = atan2(tempd2 * sin(ea), cos(ea) - eph.ecc) + eph.omega
|
||||
al_dot = tempd2 * ea_dot / tempd1
|
||||
|
||||
# Calculate corrected argument of latitude based on position
|
||||
cal = al + eph.cus * sin(2.0 * al) + eph.cuc * cos(2.0 * al)
|
||||
cal_dot = al_dot * (1.0 + 2.0 * (eph.cus * cos(2.0 * al) -
|
||||
eph.cuc * sin(2.0 * al)))
|
||||
|
||||
# Calculate corrected radius based on argument of latitude
|
||||
r = a * tempd1 + eph.crc * cos(2.0 * al) + eph.crs * sin(2.0 * al)
|
||||
r_dot = (a * eph.ecc * sin(ea) * ea_dot +
|
||||
2.0 * al_dot * (eph.crs * cos(2.0 * al) -
|
||||
eph.crc * sin(2.0 * al)))
|
||||
|
||||
# Calculate inclination based on argument of latitude
|
||||
inc = (eph.i0 + eph.iDot * tdiff +
|
||||
eph.cic * cos(2.0 * al) +
|
||||
eph.cis * sin(2.0 * al))
|
||||
inc_dot = (eph.iDot +
|
||||
2.0 * al_dot * (eph.cis * cos(2.0 * al) -
|
||||
eph.cic * sin(2.0 * al)))
|
||||
|
||||
# Calculate position and velocity in orbital plane
|
||||
x = r * cos(cal)
|
||||
y = r * sin(cal)
|
||||
x_dot = r_dot * cos(cal) - y * cal_dot
|
||||
y_dot = r_dot * sin(cal) + x * cal_dot
|
||||
|
||||
# Corrected longitude of ascending node
|
||||
om_dot = eph.omegaDot - EARTH_ROTATION_RATE
|
||||
om = eph.omega0 + tdiff * om_dot - EARTH_ROTATION_RATE * self.toe.tow
|
||||
|
||||
# Compute the satellite's position in Earth-Centered Earth-Fixed coordinates
|
||||
pos = np.empty(3)
|
||||
pos[0] = x * cos(om) - y * cos(inc) * sin(om)
|
||||
pos[1] = x * sin(om) + y * cos(inc) * cos(om)
|
||||
pos[2] = y * sin(inc)
|
||||
|
||||
tempd3 = y_dot * cos(inc) - y * sin(inc) * inc_dot
|
||||
|
||||
# Compute the satellite's velocity in Earth-Centered Earth-Fixed coordinates
|
||||
vel = np.empty(3)
|
||||
vel[0] = -om_dot * pos[1] + x_dot * cos(om) - tempd3 * sin(om)
|
||||
vel[1] = om_dot * pos[0] + x_dot * sin(om) + tempd3 * cos(om)
|
||||
vel[2] = y * cos(inc) * inc_dot + y_dot * sin(inc)
|
||||
|
||||
clock_err += einstein
|
||||
|
||||
return pos, vel, clock_err, clock_rate_err
|
||||
|
||||
|
||||
def parse_sp3_orbits(file_names, supported_constellations, skip_until_epoch: Optional[GPSTime] = None) -> Dict[str, List[PolyEphemeris]]:
|
||||
if skip_until_epoch is None:
|
||||
skip_until_epoch = GPSTime(0, 0)
|
||||
data: Dict[str, List] = {}
|
||||
for file_name in file_names:
|
||||
if file_name is None:
|
||||
continue
|
||||
with open(file_name) as f:
|
||||
ephem_type = EphemerisType.from_file_name(file_name)
|
||||
file_epoch = None
|
||||
while True:
|
||||
line = f.readline()[:-1]
|
||||
if not line:
|
||||
break
|
||||
# epoch header
|
||||
if line[0:2] == '* ':
|
||||
year = int(line[3:7])
|
||||
month = int(line[8:10])
|
||||
day = int(line[11:13])
|
||||
hour = int(line[14:16])
|
||||
minute = int(line[17:19])
|
||||
second = int(float(line[20:31]))
|
||||
epoch = GPSTime.from_datetime(datetime(year, month, day, hour, minute, second))
|
||||
if file_epoch is None:
|
||||
file_epoch = epoch
|
||||
# pos line
|
||||
elif line[0] == 'P':
|
||||
# Skipping data can reduce the time significantly when parsing the ephemeris
|
||||
if epoch < skip_until_epoch:
|
||||
continue
|
||||
prn = line[1:4].replace(' ', '0')
|
||||
# In old SP3 files vehicle ID doesn't contain constellation
|
||||
# identifier. We assume that constellation is GPS when missing.
|
||||
if prn[0] == '0':
|
||||
prn = 'G' + prn[1:]
|
||||
if get_constellation(prn) not in supported_constellations:
|
||||
continue
|
||||
if prn not in data:
|
||||
data[prn] = []
|
||||
#TODO this is a crappy way to deal with overlapping ultra rapid
|
||||
if len(data[prn]) < 1 or epoch - data[prn][-1][1] > 0:
|
||||
parsed = [(ephem_type, file_epoch, file_name),
|
||||
epoch,
|
||||
1e3 * float(line[4:18]),
|
||||
1e3 * float(line[18:32]),
|
||||
1e3 * float(line[32:46]),
|
||||
1e-6 * float(line[46:60])]
|
||||
if (np.array(parsed[2:]) != 0).all():
|
||||
data[prn].append(parsed)
|
||||
ephems = {}
|
||||
for prn in data:
|
||||
ephems[prn] = read_prn_data(data, prn)
|
||||
return ephems
|
||||
|
||||
|
||||
def read_prn_data(data, prn, deg=16, deg_t=1):
|
||||
np_data_prn = np.array(data[prn], dtype=object)
|
||||
# Currently, don't even bother with satellites that have unhealthy times
|
||||
if len(np_data_prn) == 0 or (np_data_prn[:, 5] > .99).any():
|
||||
return []
|
||||
ephems = []
|
||||
for i in range(len(np_data_prn) - deg):
|
||||
epoch_index = i + deg // 2
|
||||
epoch = np_data_prn[epoch_index][1]
|
||||
measurements = np_data_prn[i:i + deg + 1, 1:5]
|
||||
|
||||
times = (measurements[:, 0] - epoch).astype(float)
|
||||
if not (np.diff(times) != 900).any() and not (np.diff(times) != 300).any():
|
||||
continue
|
||||
|
||||
poly_data = {}
|
||||
poly_data['t0'] = epoch
|
||||
with warnings.catch_warnings():
|
||||
warnings.simplefilter("ignore") # Ignores: UserWarning: The value of the smallest subnormal for <class 'numpy.float64'> type is zero.
|
||||
poly_data['xyz'] = poly.polyfit(times, measurements[:, 1:].astype(float), deg)
|
||||
poly_data['clock'] = [(np_data_prn[epoch_index + 1][5] - np_data_prn[epoch_index - 1][5]) / 1800, np_data_prn[epoch_index][5]]
|
||||
poly_data['deg'] = deg
|
||||
poly_data['deg_t'] = deg_t
|
||||
# It can happen that a mix of orbit ephemeris types are used in the polyfit.
|
||||
ephem_type, file_epoch, file_name = np_data_prn[epoch_index][0]
|
||||
ephems.append(PolyEphemeris(prn, poly_data, epoch, ephem_type, file_epoch, file_name, healthy=True))
|
||||
return ephems
|
||||
|
||||
|
||||
def parse_rinex_nav_msg_gps(file_name):
|
||||
ephems = defaultdict(list)
|
||||
got_header = False
|
||||
rinex_ver = None
|
||||
#ion_alpha = None
|
||||
#ion_beta = None
|
||||
f = open(file_name)
|
||||
while True:
|
||||
line = f.readline()[:-1]
|
||||
if not line:
|
||||
break
|
||||
if not got_header:
|
||||
if rinex_ver is None:
|
||||
if line[60:80] != "RINEX VERSION / TYPE":
|
||||
raise RuntimeError("Doesn't appear to be a RINEX file")
|
||||
rinex_ver = int(float(line[0:9]))
|
||||
if line[20] != "N":
|
||||
raise RuntimeError("Doesn't appear to be a Navigation Message file")
|
||||
#if line[60:69] == "ION ALPHA":
|
||||
# line = line.replace('D', 'E') # Handle bizarro float format
|
||||
# ion_alpha= [float(line[3:14]), float(line[15:26]), float(line[27:38]), float(line[39:50])]
|
||||
#if line[60:68] == "ION BETA":
|
||||
# line = line.replace('D', 'E') # Handle bizarro float format
|
||||
# ion_beta= [float(line[3:14]), float(line[15:26]), float(line[27:38]), float(line[39:50])]
|
||||
if line[60:73] == "END OF HEADER":
|
||||
#ion = ion_alpha + ion_beta
|
||||
got_header = True
|
||||
continue
|
||||
if rinex_ver == 3:
|
||||
if line[0] != 'G':
|
||||
continue
|
||||
if rinex_ver == 3:
|
||||
sv_id = int(line[1:3])
|
||||
epoch = GPSTime.from_datetime(datetime.strptime(line[4:23], "%y %m %d %H %M %S"))
|
||||
elif rinex_ver == 2:
|
||||
sv_id = int(line[0:2])
|
||||
# 2000 year is in RINEX file as 0, but Python requires two digit year: 00
|
||||
epoch_str = line[3:20]
|
||||
if epoch_str[0] == ' ':
|
||||
epoch_str = '0' + epoch_str[1:]
|
||||
epoch = GPSTime.from_datetime(datetime.strptime(epoch_str, "%y %m %d %H %M %S"))
|
||||
line = ' ' + line # Shift 1 char to the right
|
||||
|
||||
line = line.replace('D', 'E') # Handle bizarro float format
|
||||
e = {'svId': sv_id}
|
||||
# TODO are TOC and TOE the same?
|
||||
e['toc'] = epoch.tow
|
||||
e['tocWeek'] = epoch.week
|
||||
e['af0'] = float(line[23:42])
|
||||
e['af1'] = float(line[42:61])
|
||||
e['af2'] = float(line[61:80])
|
||||
|
||||
e['iode'], e['crs'], e['deltaN'], e['m0'] = read4(f, rinex_ver)
|
||||
e['cuc'], e['ecc'], e['cus'], sqrta = read4(f, rinex_ver)
|
||||
e['a'] = sqrta ** 2
|
||||
e['toe'], e['cic'], e['omega0'], e['cis'] = read4(f, rinex_ver)
|
||||
e['i0'], e['crc'], e['omega'], e['omegaDot'] = read4(f, rinex_ver)
|
||||
e['iDot'], e['codesL2'], e['toeWeek'], l2_pflag = read4(f, rinex_ver)
|
||||
e['svAcc'], e['svHealth'], e['tgd'], e['iodc'] = read4(f, rinex_ver)
|
||||
f.readline() # Discard last row
|
||||
|
||||
data_struct = ephemeris_structs.Ephemeris.new_message(**e)
|
||||
|
||||
ephem = GPSEphemeris(data_struct, file_name=file_name)
|
||||
ephems[ephem.prn].append(ephem)
|
||||
f.close()
|
||||
return ephems
|
||||
|
||||
|
||||
def parse_rinex_nav_msg_glonass(file_name):
|
||||
ephems = defaultdict(list)
|
||||
f = open(file_name)
|
||||
got_header = False
|
||||
rinex_ver = None
|
||||
while True:
|
||||
line = f.readline()[:-1]
|
||||
if not line:
|
||||
break
|
||||
if not got_header:
|
||||
if rinex_ver is None:
|
||||
if line[60:80] != "RINEX VERSION / TYPE":
|
||||
raise RuntimeError("Doesn't appear to be a RINEX file")
|
||||
rinex_ver = int(float(line[0:9]))
|
||||
if line[20] != "G":
|
||||
raise RuntimeError("Doesn't appear to be a Navigation Message file")
|
||||
if line[60:73] == "END OF HEADER":
|
||||
got_header = True
|
||||
continue
|
||||
if rinex_ver == 3:
|
||||
sv_id = int(line[1:3])
|
||||
|
||||
epoch = utc_to_gpst(GPSTime.from_datetime(datetime.strptime(line[4:23], "%y %m %d %H %M %S")))
|
||||
elif rinex_ver == 2:
|
||||
sv_id = int(line[0:2])
|
||||
epoch = utc_to_gpst(GPSTime.from_datetime(datetime.strptime(line[3:20], "%y %m %d %H %M %S")))
|
||||
line = ' ' + line # Shift 1 char to the right
|
||||
|
||||
line = line.replace('D', 'E') # Handle bizarro float format
|
||||
e = {'svId': sv_id}
|
||||
e['n4'], e['nt'], toe_seconds = epoch.as_glonass()
|
||||
tb = toe_seconds / (15 * SECS_IN_MIN)
|
||||
|
||||
|
||||
e['tb'] = tb
|
||||
|
||||
e['tauN'] = -float(line[23:42])
|
||||
e['gammaN'] = float(line[42:61])
|
||||
e['tkSeconds'] = float(line[61:80])
|
||||
|
||||
e['x'], e['xVel'], e['xAccel'], e['svHealth'] = read4(f, rinex_ver)
|
||||
e['y'], e['yVel'], e['yAccel'], e['freqNum'] = read4(f, rinex_ver)
|
||||
e['z'], e['zVel'], e['zAccel'], e['age'] = read4(f, rinex_ver)
|
||||
|
||||
# TODO unclear why glonass sometimes has nav messages 3s after correct one
|
||||
if abs(tb - int(tb)) > 1e-3:
|
||||
continue
|
||||
|
||||
|
||||
data_struct = ephemeris_structs.GlonassEphemeris.new_message(**e)
|
||||
ephem = GLONASSEphemeris(data_struct, file_name=file_name)
|
||||
|
||||
ephems[ephem.prn].append(ephem)
|
||||
f.close()
|
||||
return ephems
|
||||
|
||||
|
||||
def parse_qcom_ephem(qcom_poly):
|
||||
svId = qcom_poly.svId
|
||||
prn = get_prn_from_nmea_id(svId)
|
||||
epoch = GPSTime(qcom_poly.gpsWeek, qcom_poly.gpsTow)
|
||||
|
||||
data = qcom_poly
|
||||
poly_data = {}
|
||||
poly_data['t0'] = epoch
|
||||
poly_data['xyz'] = np.array([
|
||||
[data.xyz0[0], data.xyzN[0], data.xyzN[1], data.xyzN[2]],
|
||||
[data.xyz0[1], data.xyzN[3], data.xyzN[4], data.xyzN[5]],
|
||||
[data.xyz0[2], data.xyzN[6], data.xyzN[7], data.xyzN[8]] ]).T
|
||||
|
||||
poly_data['clock'] = [1e-3*data.other[3], 1e-3*data.other[2], 1e-3*data.other[1], 1e-3*data.other[0]]
|
||||
poly_data['deg'] = 3
|
||||
poly_data['deg_t'] = 3
|
||||
return PolyEphemeris(prn, poly_data, epoch, ephem_type=EphemerisType.QCOM_POLY, max_time_diff=300, file_name='qcom')
|
||||
@@ -1,203 +0,0 @@
|
||||
import datetime
|
||||
|
||||
|
||||
def datetime_to_tow(t):
|
||||
"""
|
||||
Convert a Python datetime object to GPS Week and Time Of Week.
|
||||
Does *not* convert from UTC to GPST.
|
||||
Fractional seconds are supported.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
t : datetime
|
||||
A time to be converted, on the GPST timescale.
|
||||
mod1024 : bool, optional
|
||||
If True (default), the week number will be output in 10-bit form.
|
||||
|
||||
Returns
|
||||
-------
|
||||
week, tow : tuple (int, float)
|
||||
The GPS week number and time-of-week.
|
||||
"""
|
||||
# DateTime to GPS week and TOW
|
||||
wk_ref = datetime.datetime(2014, 2, 16, 0, 0, 0, 0, None)
|
||||
refwk = 1780
|
||||
wk = (t - wk_ref).days // 7 + refwk
|
||||
tow = ((t - wk_ref) - datetime.timedelta((wk - refwk) * 7.0)).total_seconds()
|
||||
return wk, tow
|
||||
|
||||
|
||||
def tow_to_datetime(tow, week):
|
||||
"""
|
||||
Convert a GPS Week and Time Of Week to Python datetime object.
|
||||
Does *not* convert from GPST to UTC.
|
||||
Fractional seconds are supported.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
tow : time of week in seconds
|
||||
|
||||
weeks : gps week
|
||||
|
||||
|
||||
Returns
|
||||
-------
|
||||
t : datetime
|
||||
Python datetime
|
||||
"""
|
||||
# GPS week and TOW to DateTime
|
||||
t = datetime.datetime(1980, 1, 6, 0, 0, 0, 0, None)
|
||||
t += datetime.timedelta(seconds=tow)
|
||||
t += datetime.timedelta(weeks=week)
|
||||
return t
|
||||
|
||||
|
||||
def get_leap_seconds(time):
|
||||
# TODO use library for this
|
||||
if time <= GPSTime.from_datetime(datetime.datetime(2006, 1, 1)):
|
||||
raise ValueError("Don't know how many leap seconds to use before 2006")
|
||||
elif time <= GPSTime.from_datetime(datetime.datetime(2009, 1, 1)):
|
||||
return 14
|
||||
elif time <= GPSTime.from_datetime(datetime.datetime(2012, 7, 1)):
|
||||
return 15
|
||||
elif time <= GPSTime.from_datetime(datetime.datetime(2015, 7, 1)):
|
||||
return 16
|
||||
elif time <= GPSTime.from_datetime(datetime.datetime(2017, 1, 1)):
|
||||
return 17
|
||||
else:
|
||||
return 18
|
||||
|
||||
|
||||
def gpst_to_utc(t_gpst):
|
||||
t_utc = t_gpst - get_leap_seconds(t_gpst)
|
||||
if utc_to_gpst(t_utc) - t_gpst != 0:
|
||||
return t_utc + 1
|
||||
else:
|
||||
return t_utc
|
||||
|
||||
|
||||
def utc_to_gpst(t_utc):
|
||||
t_gpst = t_utc + get_leap_seconds(t_utc)
|
||||
return t_gpst
|
||||
|
||||
|
||||
class GPSTime:
|
||||
"""
|
||||
GPS time class to add and subtract [week, tow]
|
||||
"""
|
||||
def __init__(self, week, tow):
|
||||
self.week = week
|
||||
self.tow = tow
|
||||
self.seconds_in_week = 604800
|
||||
|
||||
@classmethod
|
||||
def from_datetime(cls, datetime):
|
||||
week, tow = datetime_to_tow(datetime)
|
||||
return cls(week, tow)
|
||||
|
||||
@classmethod
|
||||
def from_glonass(cls, cycle, days, tow):
|
||||
# https://en.wikipedia.org/wiki/GLONASS
|
||||
# Day number (1 to 1461) within a four-year interval
|
||||
# starting on 1 January of the last leap year
|
||||
t = datetime.datetime(1992, 1, 1, 0, 0, 0, 0, None)
|
||||
t += datetime.timedelta(days=cycle*(365*4+1)+(days-1))
|
||||
# according to Moscow decree time.
|
||||
t -= datetime.timedelta(hours=3)
|
||||
t += datetime.timedelta(seconds=tow)
|
||||
ret = cls.from_datetime(t)
|
||||
return utc_to_gpst(ret)
|
||||
|
||||
@classmethod
|
||||
def from_meas(cls, meas):
|
||||
return cls(meas[1], meas[2])
|
||||
|
||||
def __sub__(self, other):
|
||||
if isinstance(other, type(self)):
|
||||
return (self.week - other.week)*self.seconds_in_week + self.tow - other.tow
|
||||
elif isinstance(other, float) or isinstance(other, int):
|
||||
new_week = self.week
|
||||
new_tow = self.tow - other
|
||||
while new_tow < 0:
|
||||
new_tow += self.seconds_in_week
|
||||
new_week -= 1
|
||||
return GPSTime(new_week, new_tow)
|
||||
raise NotImplementedError(f"subtracting {other} from {self}")
|
||||
|
||||
def __add__(self, other):
|
||||
if isinstance(other, float) or isinstance(other, int):
|
||||
new_week = self.week
|
||||
new_tow = self.tow + other
|
||||
while new_tow >= self.seconds_in_week:
|
||||
new_tow -= self.seconds_in_week
|
||||
new_week += 1
|
||||
return GPSTime(new_week, new_tow)
|
||||
raise NotImplementedError(f"adding {other} from {self}")
|
||||
|
||||
def __lt__(self, other):
|
||||
return self - other < 0
|
||||
|
||||
def __gt__(self, other):
|
||||
return self - other > 0
|
||||
|
||||
def __le__(self, other):
|
||||
return self - other <= 0
|
||||
|
||||
def __ge__(self, other):
|
||||
return self - other >= 0
|
||||
|
||||
def __eq__(self, other):
|
||||
return self - other == 0
|
||||
|
||||
def as_datetime(self):
|
||||
return tow_to_datetime(self.tow, self.week)
|
||||
|
||||
def as_glonass(self):
|
||||
time_utc = gpst_to_utc(self)
|
||||
datetime_utc = time_utc.as_datetime()
|
||||
datetime_glonass = datetime_utc + datetime.timedelta(hours=3)
|
||||
|
||||
year = datetime_glonass.year
|
||||
cycle = (year - 1992) // 4
|
||||
days = (datetime_glonass - datetime.datetime(1992 + cycle*4, 1, 1)).days + 1
|
||||
tod = (datetime_glonass - datetime_glonass.replace(hour=0, minute=0, second=0, microsecond=0)).total_seconds()
|
||||
return cycle, days, tod
|
||||
|
||||
def as_unix_timestamp(self):
|
||||
return (gpst_to_utc(self).as_datetime() - datetime.datetime(1970, 1, 1)).total_seconds()
|
||||
|
||||
@property
|
||||
def day(self):
|
||||
return int(self.tow/(24*3600))
|
||||
|
||||
def __repr__(self):
|
||||
return f"GPSTime(week={self.week}, tow={self.tow})"
|
||||
|
||||
|
||||
class TimeSyncer:
|
||||
"""
|
||||
Converts logmonotime to gps_time and vice versa
|
||||
"""
|
||||
def __init__(self, mono_time, gps_time):
|
||||
self.ref_mono_time = mono_time
|
||||
self.ref_gps_time = gps_time
|
||||
|
||||
@classmethod
|
||||
def from_datetime(cls, datetime):
|
||||
week, tow = datetime_to_tow(datetime)
|
||||
return cls(week, tow)
|
||||
|
||||
@classmethod
|
||||
def from_logs(cls, raw_qcom_measurement_report, clocks):
|
||||
#TODO
|
||||
#return cls(week, mono_time, gps_time)
|
||||
return None
|
||||
|
||||
def mono2gps(self, mono_time):
|
||||
return self.ref_gps_time + mono_time - self.ref_mono_time
|
||||
|
||||
def gps2mono(self, gps_time):
|
||||
return gps_time - self.ref_gps_time + self.ref_mono_time
|
||||
|
||||
def __str__(self):
|
||||
return f"Reference mono time: {self.ref_mono_time} \n Reference gps time: {self.ref_gps_time}"
|
||||
@@ -1,221 +0,0 @@
|
||||
from enum import IntEnum
|
||||
from typing import Dict
|
||||
|
||||
import numpy as np
|
||||
from .lib.coordinates import LocalCoord
|
||||
|
||||
|
||||
class ConstellationId(IntEnum):
|
||||
# Int values match Ublox gnssid version 8
|
||||
GPS = 0
|
||||
SBAS = 1
|
||||
GALILEO = 2
|
||||
BEIDOU = 3
|
||||
IMES = 4
|
||||
QZNSS = 5
|
||||
GLONASS = 6
|
||||
# Not supported by Ublox:
|
||||
IRNSS = 7
|
||||
|
||||
def to_rinex_char(self) -> str:
|
||||
# returns single character id
|
||||
return RINEX_CONSTELLATION_TO_ID[self]
|
||||
|
||||
@classmethod
|
||||
def from_rinex_char(cls, c: str):
|
||||
if c in RINEX_ID_TO_CONSTELLATION:
|
||||
return RINEX_ID_TO_CONSTELLATION[c]
|
||||
else:
|
||||
raise ValueError("Unknown rinex constellation id: ", c)
|
||||
|
||||
@classmethod
|
||||
def from_qcom_source(cls, report_source: int):
|
||||
if report_source == 0:
|
||||
return ConstellationId.GPS
|
||||
if report_source == 1:
|
||||
return ConstellationId.GLONASS
|
||||
if report_source == 2:
|
||||
return ConstellationId.BEIDOU
|
||||
if report_source == 6:
|
||||
return ConstellationId.SBAS
|
||||
raise NotImplementedError('Only GPS (0), GLONASS (1), BEIDOU (2) and SBAS (6) are supported from qcom, not:', {report_source})
|
||||
|
||||
|
||||
# From https://gpsd.gitlab.io/gpsd/NMEA.html#_satellite_ids
|
||||
# NmeaId is the unique 3 digits id for every satellite globally. (Example: 001, 201)
|
||||
# SvId is the 2 digits satellite id that is unique within a constellation. (Get the unique satellite with the constellation id. Examples: G01, R01)
|
||||
CONSTELLATION_TO_NMEA_RANGES = {
|
||||
# NmeaId ranges for each constellation with its svId offset.
|
||||
# constellation: [(start, end, svIdOffset)]
|
||||
# svId = nmeaId + offset
|
||||
ConstellationId.GPS: [(1, 32, 0)], # svId [1,32]
|
||||
ConstellationId.SBAS: [(33, 64, -32), (120, 158, -87)], # svId [1,71]
|
||||
ConstellationId.GLONASS: [(65, 96, -64)], # svId [1,31]
|
||||
ConstellationId.IMES: [(173, 182, -172)], # svId [1,9]
|
||||
ConstellationId.QZNSS: [(193, 200, -192)], # svId [1,28] # todo should be QZSS
|
||||
ConstellationId.BEIDOU: [(201, 235, -200), (401, 437, -365)], # svId 1-72
|
||||
ConstellationId.GALILEO: [(301, 336, -300)] # svId 1-36
|
||||
}
|
||||
#
|
||||
# # Source: RINEX 3.04
|
||||
RINEX_CONSTELLATION_TO_ID: Dict[ConstellationId, str] = {
|
||||
ConstellationId.GPS: 'G',
|
||||
ConstellationId.GLONASS: 'R',
|
||||
ConstellationId.SBAS: 'S',
|
||||
ConstellationId.GALILEO: 'E',
|
||||
ConstellationId.BEIDOU: 'C',
|
||||
ConstellationId.QZNSS: 'J',
|
||||
ConstellationId.IRNSS: 'I'
|
||||
}
|
||||
|
||||
# Make above dictionary bidirectional map:
|
||||
# Now you can ask for constellation using:
|
||||
# >>> RINEX_CONSTELLATION_IDENTIFIERS['R']
|
||||
# "GLONASS"
|
||||
RINEX_ID_TO_CONSTELLATION: Dict[str, ConstellationId] = {con_id: con for con, con_id in RINEX_CONSTELLATION_TO_ID.items()}
|
||||
|
||||
|
||||
def get_el_az(pos, sat_pos):
|
||||
converter = LocalCoord.from_ecef(pos)
|
||||
sat_ned = converter.ecef2ned(sat_pos)
|
||||
sat_range = np.linalg.norm(sat_ned)
|
||||
|
||||
el = np.arcsin(-sat_ned[2] / sat_range) # pylint: disable=unsubscriptable-object
|
||||
az = np.arctan2(sat_ned[1], sat_ned[0]) # pylint: disable=unsubscriptable-object
|
||||
return el, az
|
||||
|
||||
|
||||
def get_closest(time, candidates, recv_pos=None):
|
||||
if recv_pos is None:
|
||||
# Takes a list of object that have an epoch(GPSTime) value
|
||||
# and return the one that is closest the given time (GPSTime)
|
||||
return min(candidates, key=lambda candidate: abs(time - candidate.epoch), default=None)
|
||||
|
||||
return min(
|
||||
(candidate for candidate in candidates if candidate.valid(time, recv_pos)),
|
||||
key=lambda candidate: np.linalg.norm(recv_pos - candidate.pos),
|
||||
default=None,
|
||||
)
|
||||
|
||||
def get_constellation(prn: str):
|
||||
identifier = prn[0]
|
||||
return ConstellationId.from_rinex_char(identifier)
|
||||
|
||||
def get_sv_id(prn: str):
|
||||
return int(prn[1:])
|
||||
|
||||
def get_constellation_and_sv_id(nmea_id):
|
||||
for c, ranges in CONSTELLATION_TO_NMEA_RANGES.items():
|
||||
for (start, end, sv_id_offset) in ranges:
|
||||
if start <= nmea_id <= end:
|
||||
sv_id = nmea_id + sv_id_offset
|
||||
return c, sv_id
|
||||
|
||||
raise ValueError(f"constellation not found for nmeaid {nmea_id}")
|
||||
|
||||
|
||||
def get_prn_from_nmea_id(nmea_id: int):
|
||||
c_id, sv_id = get_constellation_and_sv_id(nmea_id)
|
||||
return "%s%02d" % (c_id.to_rinex_char(), sv_id)
|
||||
|
||||
|
||||
def get_nmea_id_from_prn(prn: str):
|
||||
constellation = get_constellation(prn)
|
||||
sv_id = int(prn[1:]) # satellite id
|
||||
return get_nmea_id_from_constellation_and_svid(constellation, sv_id)
|
||||
|
||||
|
||||
def get_nmea_id_from_constellation_and_svid(constellation: ConstellationId, sv_id: int):
|
||||
ranges = CONSTELLATION_TO_NMEA_RANGES[constellation]
|
||||
for (start, end, sv_id_offset) in ranges:
|
||||
new_nmea_id = sv_id - sv_id_offset
|
||||
if start <= new_nmea_id <= end:
|
||||
return new_nmea_id
|
||||
|
||||
raise ValueError(f"NMEA ID not found for constellation {constellation.name} with satellite id {sv_id}")
|
||||
|
||||
|
||||
def rinex3_obs_from_rinex2_obs(observable):
|
||||
if observable == 'P2':
|
||||
return 'C2P'
|
||||
if len(observable) == 2:
|
||||
return observable + 'C'
|
||||
raise NotImplementedError("Don't know this: " + observable)
|
||||
|
||||
|
||||
class TimeRangeHolder:
|
||||
'''Class to support test if date is in any of the multiple, sparse ranges'''
|
||||
|
||||
def __init__(self):
|
||||
# Sorted list
|
||||
self._ranges = []
|
||||
|
||||
def _previous_and_contains_index(self, time):
|
||||
prev = None
|
||||
current = None
|
||||
|
||||
for idx, (start, end) in enumerate(self._ranges):
|
||||
# Time may be in next range
|
||||
if time > end:
|
||||
continue
|
||||
|
||||
# Time isn't in any next range
|
||||
if time < start:
|
||||
prev = idx - 1
|
||||
current = None
|
||||
# Time is in current range
|
||||
else:
|
||||
prev = idx - 1
|
||||
current = idx
|
||||
break
|
||||
|
||||
# Break in last loop
|
||||
if prev is None:
|
||||
prev = len(self._ranges) - 1
|
||||
|
||||
return prev, current
|
||||
|
||||
def add(self, start_time, end_time):
|
||||
prev_start, current_start = self._previous_and_contains_index(start_time)
|
||||
_, current_end = self._previous_and_contains_index(end_time)
|
||||
|
||||
# Merge ranges
|
||||
if current_start is not None and current_end is not None:
|
||||
# If ranges are different then merge
|
||||
if current_start != current_end:
|
||||
new_start, _ = self._ranges[current_start]
|
||||
_, new_end = self._ranges[current_end]
|
||||
new_range = (new_start, new_end)
|
||||
# Required reversed order to correct remove
|
||||
del self._ranges[current_end]
|
||||
del self._ranges[current_start]
|
||||
self._ranges.insert(current_start, new_range)
|
||||
# Extend range - left
|
||||
elif current_start is not None:
|
||||
new_start, _ = self._ranges[current_start]
|
||||
new_range = (new_start, end_time)
|
||||
del self._ranges[current_start]
|
||||
self._ranges.insert(current_start, new_range)
|
||||
# Extend range - right
|
||||
elif current_end is not None:
|
||||
_, new_end = self._ranges[current_end]
|
||||
new_range = (start_time, new_end)
|
||||
del self._ranges[current_end]
|
||||
self._ranges.insert(prev_start + 1, new_range)
|
||||
# Create new range
|
||||
else:
|
||||
new_range = (start_time, end_time)
|
||||
self._ranges.insert(prev_start + 1, new_range)
|
||||
|
||||
def __contains__(self, time):
|
||||
for start, end in self._ranges:
|
||||
# Time may be in next range
|
||||
if time > end:
|
||||
continue
|
||||
|
||||
# Time isn't in any next range
|
||||
if time < start:
|
||||
return False
|
||||
# Time is in current range
|
||||
return True
|
||||
return False
|
||||
-256
@@ -1,256 +0,0 @@
|
||||
import datetime as dt
|
||||
import numpy as np
|
||||
import re
|
||||
from math import cos, sin, pi, floor
|
||||
from .constants import SECS_IN_MIN, SECS_IN_HR, EARTH_RADIUS
|
||||
from .lib.coordinates import LocalCoord
|
||||
from .gps_time import GPSTime
|
||||
|
||||
# Altitude of Ionospheric-pierce-point
|
||||
IPP_ALT = 6821000
|
||||
|
||||
def get_alpha_beta(rcv_pos, el):
|
||||
geocentric_alt = np.linalg.norm(rcv_pos)
|
||||
alpha = np.pi/2 + el
|
||||
arcsin_arg = geocentric_alt*np.sin(alpha)/IPP_ALT
|
||||
beta = np.arcsin(np.clip(arcsin_arg, -1, 1))
|
||||
return alpha, beta
|
||||
|
||||
def get_slant_delay(rcv_pos, az, el, sat_pos, time, freq, vertical_delay):
|
||||
alpha, beta = get_alpha_beta(rcv_pos, el)
|
||||
slant_delay = vertical_delay * ((1 - ((EARTH_RADIUS * np.sin(beta)) /
|
||||
(EARTH_RADIUS + 3.5e5))**2)**(-0.5))
|
||||
return slant_delay
|
||||
|
||||
def closest_in_list(lst, val, num=2):
|
||||
"""
|
||||
Returns two (`num` in general) closest values of `val` in list `lst`
|
||||
"""
|
||||
idxs = sorted(lst, key=lambda x: abs(x - val))[:num]
|
||||
return sorted(list(lst).index(x) for x in idxs)
|
||||
|
||||
|
||||
def get_header_line(headr, proprty):
|
||||
"""
|
||||
:param headr: the header of the RINEX-file
|
||||
:param proprty: string-like property to search for (e.g. 'delta-utc')
|
||||
:return: the string of the ``headr`` containing ``property``
|
||||
"""
|
||||
pattern = re.compile(proprty, re.IGNORECASE)
|
||||
for d in headr:
|
||||
if pattern.search(d):
|
||||
return d
|
||||
|
||||
|
||||
def get_header_body(file_path):
|
||||
"""
|
||||
Opens `file_path`, reads file and returns header and body
|
||||
separated with "END OF HEADER"
|
||||
:param file_path: path to RINEX-like file
|
||||
:return: header, body (arrays of lines)
|
||||
"""
|
||||
with open(file_path) as fd:
|
||||
data = fd.readlines()
|
||||
for j, d in enumerate(data):
|
||||
if "END OF HEADER" in d:
|
||||
header_end = j
|
||||
break
|
||||
return data[:header_end], data[header_end + 1:]
|
||||
|
||||
|
||||
def get_int_from_header(hdr, seq):
|
||||
"""
|
||||
Returns the first int from the line that contains `seq` of lines `hdr`.
|
||||
In fact, _header_ here may not be header of RINEX/IONEX, just some set of lines.
|
||||
"""
|
||||
return int(get_header_line(hdr, seq).split()[0])
|
||||
|
||||
def compute_grid_lats_lons(data):
|
||||
grid = np.array([], dtype='uint16')
|
||||
lats = np.array([])
|
||||
for j, line in enumerate(data[1:]):
|
||||
if "LAT" in line:
|
||||
lat, lon1, lon2, dlon, h = (float(line[x:x + 6]) for x in range(2, 32, 6))
|
||||
lats = np.append(lats, lat)
|
||||
row_length = (lon2 - lon1) / dlon + 1 # total number of values of longitudes
|
||||
next_lines_with_numbers = int(np.ceil(row_length / 16))
|
||||
elems_in_row = [
|
||||
min(16, int(row_length - i * 16)) for i in range(next_lines_with_numbers)
|
||||
]
|
||||
row = np.array([], dtype='int16')
|
||||
for i, elem in enumerate(elems_in_row):
|
||||
row = np.append(
|
||||
row,
|
||||
np.array(
|
||||
[int(data[j + 2 + i][5 * x:5 * x + 5]) for x in range(elem)],
|
||||
dtype='int16',
|
||||
),
|
||||
)
|
||||
if len(grid) > 0:
|
||||
grid = np.vstack((grid, row))
|
||||
else:
|
||||
grid = np.append(grid, row)
|
||||
lons = np.linspace(lon1, lon2, int(row_length))
|
||||
return (grid, lats, lons)
|
||||
|
||||
|
||||
class IonexMap:
|
||||
def __init__(self, exp, data1, data2):
|
||||
self.exp = exp
|
||||
self.t1 = GPSTime.from_datetime(dt.datetime(*[int(d) for d in data1[0].split()[:6]]))
|
||||
self.t2 = GPSTime.from_datetime(dt.datetime(*[int(d) for d in data2[0].split()[:6]]))
|
||||
assert self.t2 - self.t1 == SECS_IN_HR
|
||||
assert len(data1) == len(data2)
|
||||
|
||||
self.max_time_diff = SECS_IN_MIN*30
|
||||
self.epoch = self.t1 + self.max_time_diff
|
||||
|
||||
self.grid_TEC1, self.lats, self.lons = compute_grid_lats_lons(data1)
|
||||
self.grid_TEC2, self.lats, self.lons = compute_grid_lats_lons(data2)
|
||||
|
||||
def valid(self, time):
|
||||
return abs(time - self.epoch) <= self.max_time_diff
|
||||
|
||||
@staticmethod
|
||||
def find_nearest(lst, val):
|
||||
return (np.abs(lst - val)).argmin()
|
||||
|
||||
def get_TEC(self, pos, time):
|
||||
"""
|
||||
Returns TEC in a position `pos` of ionosphere
|
||||
:param pos: (lat, lon) [deg, deg]
|
||||
:return:
|
||||
"""
|
||||
if pos[0] in self.lats and pos[1] in self.lons:
|
||||
lat = self.find_nearest(self.lats, pos[0])
|
||||
lon = self.find_nearest(self.lons, pos[1])
|
||||
E = self.grid_TEC1[lat][lon] + self.grid_TEC2[lat][lon]
|
||||
return E
|
||||
lat_idxs = closest_in_list(self.lats, pos[0])
|
||||
lon_idxs = closest_in_list(self.lons, pos[1])
|
||||
lat0, lat1 = self.lats[lat_idxs[0]], self.lats[lat_idxs[1]]
|
||||
lon0, lon1 = self.lons[lon_idxs[0]], self.lons[lon_idxs[1]]
|
||||
dlat = lat1 - lat0
|
||||
dlon = lon1 - lon0
|
||||
p = float(pos[0] - lat0) / dlat
|
||||
q = float(pos[1] - lon0) / dlon
|
||||
|
||||
(E00, E10), (E01, E11) = self.grid_TEC1[lat_idxs[0]:lat_idxs[1] + 1, lon_idxs[0]:lon_idxs[1] + 1]
|
||||
TEC_1 = ((1 - p) * (1 - q) * E00 + p * (1 - q) * E01 + (1 - p) * q * E10 + p * q * E11)
|
||||
(E00, E10), (E01, E11) = self.grid_TEC2[lat_idxs[0]:lat_idxs[1] + 1, lon_idxs[0]:lon_idxs[1] + 1]
|
||||
TEC_2 = ((1 - p) * (1 - q) * E00 + p * (1 - q) * E01 + (1 - p) * q * E10 + p * q * E11)
|
||||
|
||||
return (1 - (time - self.t1)/SECS_IN_HR)*TEC_1 + ((time - self.t1)/SECS_IN_HR)*TEC_2
|
||||
|
||||
def get_delay(self, rcv_pos, az, el, sat_pos, time, freq):
|
||||
# To get a delay from a TEC map, we need to calculate
|
||||
# the ionospheric pierce point, geometry described here
|
||||
# https://en.wikipedia.org/wiki/Ionospheric_pierce_point
|
||||
alpha, beta = get_alpha_beta(rcv_pos, el)
|
||||
conv = LocalCoord.from_ecef(rcv_pos)
|
||||
gamma = np.pi - alpha - beta
|
||||
geocentric_alt = np.linalg.norm(rcv_pos)
|
||||
ipp_dist = geocentric_alt*np.sin(gamma)/np.sin(beta)
|
||||
ipp_ned = conv.ecef2ned(sat_pos)*(ipp_dist)/np.linalg.norm(sat_pos)
|
||||
ipp_geo = conv.ned2geodetic(ipp_ned)
|
||||
factor = 40.30E16 / (freq**2) * 10**(self.exp)
|
||||
vertical_delay = self.get_TEC(ipp_geo, time) * factor
|
||||
slant_delay = get_slant_delay(rcv_pos, az, el, sat_pos, time, freq, vertical_delay)
|
||||
return slant_delay
|
||||
|
||||
@staticmethod
|
||||
def round_to_grid(number, base):
|
||||
return int(base * round(float(number) / base))
|
||||
|
||||
|
||||
def parse_ionex(ionex_file):
|
||||
"""
|
||||
:param ionex_file: path to the IONEX file
|
||||
:return: TEC interpolation function `f( (lat,lon), datetime )`
|
||||
"""
|
||||
header, body = get_header_body(ionex_file)
|
||||
|
||||
exponent = get_int_from_header(header, "EXPONENT")
|
||||
maps_count = get_int_from_header(header, "MAPS IN FILE")
|
||||
# =============
|
||||
# Separate maps
|
||||
# =============
|
||||
map_start_idx = []
|
||||
map_end_idx = []
|
||||
|
||||
for j, line in enumerate(body):
|
||||
if "START OF TEC MAP" in line:
|
||||
map_start_idx += [j]
|
||||
elif "END OF TEC MAP" in line:
|
||||
map_end_idx += [j]
|
||||
if maps_count != len(map_start_idx):
|
||||
raise LookupError("Parsing error: the number of maps in the header " +
|
||||
"is not equal to the number of maps in the body.")
|
||||
if len(map_start_idx) != len(map_end_idx):
|
||||
raise IndexError("Starts end ends numbers are not equal.")
|
||||
map_dates = []
|
||||
for i in range(maps_count):
|
||||
date_components = body[map_start_idx[i] + 1].split()[:6]
|
||||
map_dates.append(dt.datetime(*[int(d) for d in date_components]))
|
||||
|
||||
maps = []
|
||||
iono_map = iono_map_prev = None
|
||||
for m in range(maps_count):
|
||||
iono_map_prev = iono_map
|
||||
iono_map = body[map_start_idx[m] + 1:map_end_idx[m]]
|
||||
if iono_map and iono_map_prev:
|
||||
maps += [IonexMap(exponent, iono_map_prev, iono_map)]
|
||||
return maps
|
||||
|
||||
|
||||
def klobuchar(pos, az, el, time, iono_coeffs):
|
||||
"""
|
||||
Details are taken from [5]: IS-GPS-200H, Fig. 20-4
|
||||
Note: result is referred to the GPS L₁ frequency;
|
||||
if the user is operating on the GPS L₂ frequency, the correction term must
|
||||
be multiplied by γ = f₂²/f₁¹ = 0.6071850227694382
|
||||
:param pos: [lat, lon, alt] in radians and meters
|
||||
"""
|
||||
|
||||
tow = time.tow
|
||||
if pos[2] < -1E3 or el < 0:
|
||||
return 0.0
|
||||
if len(iono_coeffs) < 8:
|
||||
return None
|
||||
|
||||
# earth centered angle (semi-circle)
|
||||
psi = 0.0137 / (el / pi + 0.11) - 0.022
|
||||
|
||||
# subionospheric latitude/longitude (semi-circle)
|
||||
phi = pos[0] / pi + psi * cos(az)
|
||||
if phi > 0.416:
|
||||
phi = 0.416
|
||||
elif phi < -0.416:
|
||||
phi = -0.416
|
||||
lam = pos[1] / pi + psi * sin(az) / cos(phi * pi)
|
||||
|
||||
# geomagnetic latitude (semi-circle) */
|
||||
phi += 0.064 * cos((lam - 1.617) * pi)
|
||||
|
||||
# local time (s)
|
||||
tt = 43200.0 * lam + tow
|
||||
tt -= floor(tt / 86400.0) * 86400.0 # 0<=tt<86400
|
||||
|
||||
# slant factor
|
||||
f = 1.0 + 16.0 * pow(0.53 - el / pi, 3.0)
|
||||
|
||||
# ionospheric delay
|
||||
amp = iono_coeffs[0] + phi * (iono_coeffs[1] + phi *
|
||||
(iono_coeffs[2] + phi * iono_coeffs[3]))
|
||||
per = iono_coeffs[4] + phi * (iono_coeffs[5] + phi *
|
||||
(iono_coeffs[6] + phi * iono_coeffs[7]))
|
||||
if amp < 0.0:
|
||||
amp = 0.
|
||||
if per < 72000.0:
|
||||
per = 72000.0
|
||||
x = 2.0 * pi * (tt - 50400.0) / per
|
||||
|
||||
mul = 5E-9
|
||||
if abs(x) < 1.57:
|
||||
mul = (5E-9 + amp * (1.0 + x * x * (-0.5 + x * x / 24.0)))
|
||||
return 2.99792458E8 * f * mul
|
||||
@@ -1,106 +0,0 @@
|
||||
import numpy as np
|
||||
"""
|
||||
Coordinate transformation module. All methods accept arrays as input
|
||||
with each row as a position.
|
||||
"""
|
||||
|
||||
|
||||
a = 6378137
|
||||
b = 6356752.3142
|
||||
esq = 6.69437999014 * 0.001
|
||||
e1sq = 6.73949674228 * 0.001
|
||||
|
||||
|
||||
def geodetic2ecef(geodetic, radians=False):
|
||||
geodetic = np.array(geodetic)
|
||||
input_shape = geodetic.shape
|
||||
geodetic = np.atleast_2d(geodetic)
|
||||
|
||||
ratio = 1.0 if radians else (np.pi / 180.0)
|
||||
lat = ratio*geodetic[:,0]
|
||||
lon = ratio*geodetic[:,1]
|
||||
alt = geodetic[:,2]
|
||||
|
||||
xi = np.sqrt(1 - esq * np.sin(lat)**2)
|
||||
x = (a / xi + alt) * np.cos(lat) * np.cos(lon)
|
||||
y = (a / xi + alt) * np.cos(lat) * np.sin(lon)
|
||||
z = (a / xi * (1 - esq) + alt) * np.sin(lat)
|
||||
ecef = np.array([x, y, z]).T
|
||||
return ecef.reshape(input_shape)
|
||||
|
||||
|
||||
def ecef2geodetic(ecef, radians=False):
|
||||
"""
|
||||
Convert ECEF coordinates to geodetic using ferrari's method
|
||||
"""
|
||||
# Save shape and export column
|
||||
ecef = np.atleast_1d(ecef)
|
||||
input_shape = ecef.shape
|
||||
ecef = np.atleast_2d(ecef)
|
||||
x, y, z = ecef[:, 0], ecef[:, 1], ecef[:, 2]
|
||||
|
||||
ratio = 1.0 if radians else (180.0 / np.pi)
|
||||
|
||||
# Conver from ECEF to geodetic using Ferrari's methods
|
||||
# https://en.wikipedia.org/wiki/Geographic_coordinate_conversion#Ferrari.27s_solution
|
||||
r = np.sqrt(x * x + y * y)
|
||||
Esq = a * a - b * b
|
||||
F = 54 * b * b * z * z
|
||||
G = r * r + (1 - esq) * z * z - esq * Esq
|
||||
C = (esq * esq * F * r * r) / (pow(G, 3))
|
||||
S = np.cbrt(1 + C + np.sqrt(C * C + 2 * C))
|
||||
P = F / (3 * pow((S + 1 / S + 1), 2) * G * G)
|
||||
Q = np.sqrt(1 + 2 * esq * esq * P)
|
||||
r_0 = -(P * esq * r) / (1 + Q) + np.sqrt(0.5 * a * a*(1 + 1.0 / Q) -
|
||||
P * (1 - esq) * z * z / (Q * (1 + Q)) - 0.5 * P * r * r)
|
||||
U = np.sqrt(pow((r - esq * r_0), 2) + z * z)
|
||||
V = np.sqrt(pow((r - esq * r_0), 2) + (1 - esq) * z * z)
|
||||
Z_0 = b * b * z / (a * V)
|
||||
h = U * (1 - b * b / (a * V))
|
||||
lat = ratio*np.arctan((z + e1sq * Z_0) / r)
|
||||
lon = ratio*np.arctan2(y, x)
|
||||
|
||||
# stack the new columns and return to the original shape
|
||||
geodetic = np.column_stack((lat, lon, h))
|
||||
return geodetic.reshape(input_shape)
|
||||
|
||||
class LocalCoord:
|
||||
"""
|
||||
Allows conversions to local frames. In this case NED.
|
||||
That is: North East Down from the start position in
|
||||
meters.
|
||||
"""
|
||||
def __init__(self, init_geodetic, init_ecef):
|
||||
self.init_ecef = init_ecef
|
||||
lat, lon, _ = (np.pi/180)*np.array(init_geodetic)
|
||||
self.ned2ecef_matrix = np.array([[-np.sin(lat)*np.cos(lon), -np.sin(lon), -np.cos(lat)*np.cos(lon)],
|
||||
[-np.sin(lat)*np.sin(lon), np.cos(lon), -np.cos(lat)*np.sin(lon)],
|
||||
[np.cos(lat), 0, -np.sin(lat)]])
|
||||
self.ecef2ned_matrix = self.ned2ecef_matrix.T
|
||||
|
||||
@classmethod
|
||||
def from_geodetic(cls, init_geodetic):
|
||||
init_ecef = geodetic2ecef(init_geodetic)
|
||||
return LocalCoord(init_geodetic, init_ecef)
|
||||
|
||||
@classmethod
|
||||
def from_ecef(cls, init_ecef):
|
||||
init_geodetic = ecef2geodetic(init_ecef)
|
||||
return LocalCoord(init_geodetic, init_ecef)
|
||||
|
||||
def ecef2ned(self, ecef):
|
||||
ecef = np.array(ecef)
|
||||
return np.dot(self.ecef2ned_matrix, (ecef - self.init_ecef).T).T
|
||||
|
||||
def ned2ecef(self, ned):
|
||||
ned = np.array(ned)
|
||||
# Transpose so that init_ecef will broadcast correctly for 1d or 2d ned.
|
||||
return (np.dot(self.ned2ecef_matrix, ned.T).T + self.init_ecef)
|
||||
|
||||
def geodetic2ned(self, geodetic):
|
||||
ecef = geodetic2ecef(geodetic)
|
||||
return self.ecef2ned(ecef)
|
||||
|
||||
def ned2geodetic(self, ned):
|
||||
ecef = self.ned2ecef(ned)
|
||||
return ecef2geodetic(ecef)
|
||||
@@ -1,291 +0,0 @@
|
||||
import numpy as np
|
||||
from numpy import dot, inner, array, linalg
|
||||
from .coordinates import LocalCoord
|
||||
|
||||
|
||||
'''
|
||||
Vectorized functions that transform between
|
||||
rotation matrices, euler angles and quaternions.
|
||||
All support lists, array or array of arrays as inputs.
|
||||
Supports both x2y and y_from_x format (y_from_x preferred!).
|
||||
'''
|
||||
|
||||
def euler2quat(eulers):
|
||||
eulers = array(eulers)
|
||||
if len(eulers.shape) > 1:
|
||||
output_shape = (-1,4)
|
||||
else:
|
||||
output_shape = (4,)
|
||||
eulers = np.atleast_2d(eulers)
|
||||
gamma, theta, psi = eulers[:,0], eulers[:,1], eulers[:,2]
|
||||
|
||||
cos_half_gamma = np.cos(gamma / 2)
|
||||
cos_half_theta = np.cos(theta / 2)
|
||||
cos_half_psi = np.cos(psi / 2)
|
||||
sin_half_gamma = np.sin(gamma / 2)
|
||||
sin_half_theta = np.sin(theta / 2)
|
||||
sin_half_psi = np.sin(psi / 2)
|
||||
q0 = cos_half_gamma * cos_half_theta * cos_half_psi + sin_half_gamma * sin_half_theta * sin_half_psi
|
||||
q1 = sin_half_gamma * cos_half_theta * cos_half_psi - cos_half_gamma * sin_half_theta * sin_half_psi
|
||||
q2 = cos_half_gamma * sin_half_theta * cos_half_psi + sin_half_gamma * cos_half_theta * sin_half_psi
|
||||
q3 = cos_half_gamma * cos_half_theta * sin_half_psi - sin_half_gamma * sin_half_theta * cos_half_psi
|
||||
|
||||
quats = array([q0, q1, q2, q3]).T
|
||||
for i in range(len(quats)):
|
||||
if quats[i,0] < 0:
|
||||
quats[i] = -quats[i]
|
||||
return quats.reshape(output_shape)
|
||||
|
||||
|
||||
def quat2euler(quats):
|
||||
quats = array(quats)
|
||||
if len(quats.shape) > 1:
|
||||
output_shape = (-1,3)
|
||||
else:
|
||||
output_shape = (3,)
|
||||
quats = np.atleast_2d(quats)
|
||||
q0, q1, q2, q3 = quats[:,0], quats[:,1], quats[:,2], quats[:,3]
|
||||
|
||||
gamma = np.arctan2(2 * (q0 * q1 + q2 * q3), 1 - 2 * (q1**2 + q2**2))
|
||||
theta = np.arcsin(2 * (q0 * q2 - q3 * q1))
|
||||
psi = np.arctan2(2 * (q0 * q3 + q1 * q2), 1 - 2 * (q2**2 + q3**2))
|
||||
|
||||
eulers = array([gamma, theta, psi]).T
|
||||
return eulers.reshape(output_shape)
|
||||
|
||||
|
||||
def quat2rot(quats):
|
||||
quats = array(quats)
|
||||
input_shape = quats.shape
|
||||
quats = np.atleast_2d(quats)
|
||||
Rs = np.zeros((quats.shape[0], 3, 3))
|
||||
q0 = quats[:, 0]
|
||||
q1 = quats[:, 1]
|
||||
q2 = quats[:, 2]
|
||||
q3 = quats[:, 3]
|
||||
Rs[:, 0, 0] = q0 * q0 + q1 * q1 - q2 * q2 - q3 * q3
|
||||
Rs[:, 0, 1] = 2 * (q1 * q2 - q0 * q3)
|
||||
Rs[:, 0, 2] = 2 * (q0 * q2 + q1 * q3)
|
||||
Rs[:, 1, 0] = 2 * (q1 * q2 + q0 * q3)
|
||||
Rs[:, 1, 1] = q0 * q0 - q1 * q1 + q2 * q2 - q3 * q3
|
||||
Rs[:, 1, 2] = 2 * (q2 * q3 - q0 * q1)
|
||||
Rs[:, 2, 0] = 2 * (q1 * q3 - q0 * q2)
|
||||
Rs[:, 2, 1] = 2 * (q0 * q1 + q2 * q3)
|
||||
Rs[:, 2, 2] = q0 * q0 - q1 * q1 - q2 * q2 + q3 * q3
|
||||
|
||||
if len(input_shape) < 2:
|
||||
return Rs[0]
|
||||
return Rs
|
||||
|
||||
|
||||
def rot2quat(rots):
|
||||
input_shape = rots.shape
|
||||
if len(input_shape) < 3:
|
||||
rots = array([rots])
|
||||
K3 = np.empty((len(rots), 4, 4))
|
||||
K3[:, 0, 0] = (rots[:, 0, 0] - rots[:, 1, 1] - rots[:, 2, 2]) / 3.0
|
||||
K3[:, 0, 1] = (rots[:, 1, 0] + rots[:, 0, 1]) / 3.0
|
||||
K3[:, 0, 2] = (rots[:, 2, 0] + rots[:, 0, 2]) / 3.0
|
||||
K3[:, 0, 3] = (rots[:, 1, 2] - rots[:, 2, 1]) / 3.0
|
||||
K3[:, 1, 0] = K3[:, 0, 1]
|
||||
K3[:, 1, 1] = (rots[:, 1, 1] - rots[:, 0, 0] - rots[:, 2, 2]) / 3.0
|
||||
K3[:, 1, 2] = (rots[:, 2, 1] + rots[:, 1, 2]) / 3.0
|
||||
K3[:, 1, 3] = (rots[:, 2, 0] - rots[:, 0, 2]) / 3.0
|
||||
K3[:, 2, 0] = K3[:, 0, 2]
|
||||
K3[:, 2, 1] = K3[:, 1, 2]
|
||||
K3[:, 2, 2] = (rots[:, 2, 2] - rots[:, 0, 0] - rots[:, 1, 1]) / 3.0
|
||||
K3[:, 2, 3] = (rots[:, 0, 1] - rots[:, 1, 0]) / 3.0
|
||||
K3[:, 3, 0] = K3[:, 0, 3]
|
||||
K3[:, 3, 1] = K3[:, 1, 3]
|
||||
K3[:, 3, 2] = K3[:, 2, 3]
|
||||
K3[:, 3, 3] = (rots[:, 0, 0] + rots[:, 1, 1] + rots[:, 2, 2]) / 3.0
|
||||
q = np.empty((len(rots), 4))
|
||||
for i in range(len(rots)):
|
||||
_, eigvecs = linalg.eigh(K3[i].T)
|
||||
eigvecs = eigvecs[:,3:]
|
||||
q[i, 0] = eigvecs[-1]
|
||||
q[i, 1:] = -eigvecs[:-1].flatten()
|
||||
if q[i, 0] < 0:
|
||||
q[i] = -q[i]
|
||||
|
||||
if len(input_shape) < 3:
|
||||
return q[0]
|
||||
return q
|
||||
|
||||
|
||||
def euler2rot(eulers):
|
||||
return rotations_from_quats(euler2quat(eulers))
|
||||
|
||||
|
||||
def rot2euler(rots):
|
||||
return quat2euler(quats_from_rotations(rots))
|
||||
|
||||
|
||||
quats_from_rotations = rot2quat
|
||||
quat_from_rot = rot2quat
|
||||
rotations_from_quats = quat2rot
|
||||
rot_from_quat= quat2rot
|
||||
rot_from_quat= quat2rot
|
||||
euler_from_rot = rot2euler
|
||||
euler_from_quat = quat2euler
|
||||
rot_from_euler = euler2rot
|
||||
quat_from_euler = euler2quat
|
||||
|
||||
|
||||
'''
|
||||
Random helpers below
|
||||
'''
|
||||
|
||||
|
||||
def quat_product(q, r):
|
||||
t = np.zeros(4)
|
||||
t[0] = r[0] * q[0] - r[1] * q[1] - r[2] * q[2] - r[3] * q[3]
|
||||
t[1] = r[0] * q[1] + r[1] * q[0] - r[2] * q[3] + r[3] * q[2]
|
||||
t[2] = r[0] * q[2] + r[1] * q[3] + r[2] * q[0] - r[3] * q[1]
|
||||
t[3] = r[0] * q[3] - r[1] * q[2] + r[2] * q[1] + r[3] * q[0]
|
||||
return t
|
||||
|
||||
|
||||
def rot_matrix(roll, pitch, yaw):
|
||||
cr, sr = np.cos(roll), np.sin(roll)
|
||||
cp, sp = np.cos(pitch), np.sin(pitch)
|
||||
cy, sy = np.cos(yaw), np.sin(yaw)
|
||||
rr = array([[1,0,0],[0, cr,-sr],[0, sr, cr]])
|
||||
rp = array([[cp,0,sp],[0, 1,0],[-sp, 0, cp]])
|
||||
ry = array([[cy,-sy,0],[sy, cy,0],[0, 0, 1]])
|
||||
return ry.dot(rp.dot(rr))
|
||||
|
||||
|
||||
def rot(axis, angle):
|
||||
# Rotates around an arbitrary axis
|
||||
ret_1 = (1 - np.cos(angle)) * array([[axis[0]**2, axis[0] * axis[1], axis[0] * axis[2]], [
|
||||
axis[1] * axis[0], axis[1]**2, axis[1] * axis[2]
|
||||
], [axis[2] * axis[0], axis[2] * axis[1], axis[2]**2]])
|
||||
ret_2 = np.cos(angle) * np.eye(3)
|
||||
ret_3 = np.sin(angle) * array([[0, -axis[2], axis[1]], [axis[2], 0, -axis[0]],
|
||||
[-axis[1], axis[0], 0]])
|
||||
return ret_1 + ret_2 + ret_3
|
||||
|
||||
|
||||
def ecef_euler_from_ned(ned_ecef_init, ned_pose):
|
||||
'''
|
||||
Got it from here:
|
||||
Using Rotations to Build Aerospace Coordinate Systems
|
||||
-Don Koks
|
||||
'''
|
||||
converter = LocalCoord.from_ecef(ned_ecef_init)
|
||||
x0 = converter.ned2ecef([1, 0, 0]) - converter.ned2ecef([0, 0, 0])
|
||||
y0 = converter.ned2ecef([0, 1, 0]) - converter.ned2ecef([0, 0, 0])
|
||||
z0 = converter.ned2ecef([0, 0, 1]) - converter.ned2ecef([0, 0, 0])
|
||||
|
||||
x1 = rot(z0, ned_pose[2]).dot(x0)
|
||||
y1 = rot(z0, ned_pose[2]).dot(y0)
|
||||
z1 = rot(z0, ned_pose[2]).dot(z0)
|
||||
|
||||
x2 = rot(y1, ned_pose[1]).dot(x1)
|
||||
y2 = rot(y1, ned_pose[1]).dot(y1)
|
||||
z2 = rot(y1, ned_pose[1]).dot(z1)
|
||||
|
||||
x3 = rot(x2, ned_pose[0]).dot(x2)
|
||||
y3 = rot(x2, ned_pose[0]).dot(y2)
|
||||
#z3 = rot(x2, ned_pose[0]).dot(z2)
|
||||
|
||||
x0 = array([1, 0, 0])
|
||||
y0 = array([0, 1, 0])
|
||||
z0 = array([0, 0, 1])
|
||||
|
||||
psi = np.arctan2(inner(x3, y0), inner(x3, x0))
|
||||
theta = np.arctan2(-inner(x3, z0), np.sqrt(inner(x3, x0)**2 + inner(x3, y0)**2))
|
||||
y2 = rot(z0, psi).dot(y0)
|
||||
z2 = rot(y2, theta).dot(z0)
|
||||
phi = np.arctan2(inner(y3, z2), inner(y3, y2))
|
||||
|
||||
ret = array([phi, theta, psi])
|
||||
return ret
|
||||
|
||||
|
||||
def ned_euler_from_ecef(ned_ecef_init, ecef_poses):
|
||||
'''
|
||||
Got the math from here:
|
||||
Using Rotations to Build Aerospace Coordinate Systems
|
||||
-Don Koks
|
||||
|
||||
Also accepts array of ecef_poses and array of ned_ecef_inits.
|
||||
Where each row is a pose and an ecef_init.
|
||||
'''
|
||||
ned_ecef_init = array(ned_ecef_init)
|
||||
ecef_poses = array(ecef_poses)
|
||||
output_shape = ecef_poses.shape
|
||||
ned_ecef_init = np.atleast_2d(ned_ecef_init)
|
||||
if ned_ecef_init.shape[0] == 1:
|
||||
ned_ecef_init = np.tile(ned_ecef_init[0], (output_shape[0], 1))
|
||||
ecef_poses = np.atleast_2d(ecef_poses)
|
||||
|
||||
ned_poses = np.zeros(ecef_poses.shape)
|
||||
for i, ecef_pose in enumerate(ecef_poses):
|
||||
converter = LocalCoord.from_ecef(ned_ecef_init[i])
|
||||
x0 = array([1, 0, 0])
|
||||
y0 = array([0, 1, 0])
|
||||
z0 = array([0, 0, 1])
|
||||
|
||||
x1 = rot(z0, ecef_pose[2]).dot(x0)
|
||||
y1 = rot(z0, ecef_pose[2]).dot(y0)
|
||||
z1 = rot(z0, ecef_pose[2]).dot(z0)
|
||||
|
||||
x2 = rot(y1, ecef_pose[1]).dot(x1)
|
||||
y2 = rot(y1, ecef_pose[1]).dot(y1)
|
||||
z2 = rot(y1, ecef_pose[1]).dot(z1)
|
||||
|
||||
x3 = rot(x2, ecef_pose[0]).dot(x2)
|
||||
y3 = rot(x2, ecef_pose[0]).dot(y2)
|
||||
#z3 = rot(x2, ecef_pose[0]).dot(z2)
|
||||
|
||||
x0 = converter.ned2ecef([1, 0, 0]) - converter.ned2ecef([0, 0, 0])
|
||||
y0 = converter.ned2ecef([0, 1, 0]) - converter.ned2ecef([0, 0, 0])
|
||||
z0 = converter.ned2ecef([0, 0, 1]) - converter.ned2ecef([0, 0, 0])
|
||||
|
||||
psi = np.arctan2(inner(x3, y0), inner(x3, x0))
|
||||
theta = np.arctan2(-inner(x3, z0), np.sqrt(inner(x3, x0)**2 + inner(x3, y0)**2))
|
||||
y2 = rot(z0, psi).dot(y0)
|
||||
z2 = rot(y2, theta).dot(z0)
|
||||
phi = np.arctan2(inner(y3, z2), inner(y3, y2))
|
||||
ned_poses[i] = array([phi, theta, psi])
|
||||
|
||||
return ned_poses.reshape(output_shape)
|
||||
|
||||
|
||||
def ecef2car(car_ecef, psi, theta, points_ecef, ned_converter):
|
||||
"""
|
||||
TODO: add roll rotation
|
||||
Converts an array of points in ecef coordinates into
|
||||
x-forward, y-left, z-up coordinates
|
||||
Parameters
|
||||
----------
|
||||
psi: yaw, radian
|
||||
theta: pitch, radian
|
||||
Returns
|
||||
-------
|
||||
[x, y, z] coordinates in car frame
|
||||
"""
|
||||
|
||||
# input is an array of points in ecef cocrdinates
|
||||
# output is an array of points in car's coordinate (x-front, y-left, z-up)
|
||||
|
||||
# convert points to NED
|
||||
points_ned = []
|
||||
for p in points_ecef:
|
||||
points_ned.append(ned_converter.ecef2ned_matrix.dot(array(p) - car_ecef))
|
||||
|
||||
points_ned = np.vstack(points_ned).T
|
||||
|
||||
# n, e, d -> x, y, z
|
||||
# Calculate relative positions and rotate wrt to heading and pitch of car
|
||||
invert_R = array([[1., 0., 0.], [0., -1., 0.], [0., 0., -1.]])
|
||||
|
||||
c, s = np.cos(psi), np.sin(psi)
|
||||
yaw_R = array([[c, s, 0.], [-s, c, 0.], [0., 0., 1.]])
|
||||
|
||||
c, s = np.cos(theta), np.sin(theta)
|
||||
pitch_R = array([[c, 0., -s], [0., 1., 0.], [s, 0., c]])
|
||||
|
||||
return dot(pitch_R, dot(yaw_R, dot(invert_R, points_ned)))
|
||||
-192
@@ -1,192 +0,0 @@
|
||||
import sympy
|
||||
import numpy as np
|
||||
from typing import List
|
||||
|
||||
from .constants import EARTH_ROTATION_RATE, SPEED_OF_LIGHT
|
||||
from .helpers import ConstellationId
|
||||
from .raw_gnss import GNSSMeasurement
|
||||
|
||||
|
||||
def gauss_newton(fun, b, M, xtol=1e-8, max_n=25):
|
||||
|
||||
W = np.linalg.inv(M)
|
||||
for _ in range(max_n):
|
||||
# Compute function and jacobian on current estimate
|
||||
r, J = fun(b)
|
||||
|
||||
# Update estimate, WLS https://en.wikipedia.org/wiki/Weighted_least_squares
|
||||
delta = np.linalg.pinv(J.T.dot(W).dot(J)).dot(J.T).dot(W) @ r
|
||||
b -= delta
|
||||
|
||||
# Check step size for stopping condition
|
||||
if np.linalg.norm(delta) < xtol:
|
||||
break
|
||||
|
||||
r, J = fun(b)
|
||||
Mb = np.linalg.pinv(J.T.dot(W).dot(J))
|
||||
x_std = np.sqrt(np.diagonal(Mb))
|
||||
return b, r, x_std
|
||||
|
||||
|
||||
def calc_pos_fix(measurements, posfix_functions=None, x0=None, signal='C1C', min_measurements=5):
|
||||
'''
|
||||
Calculates gps fix using gauss newton method
|
||||
To solve the problem a minimal of 4 measurements are required.
|
||||
If Glonass is included 5 are required to solve for the additional free variable.
|
||||
returns:
|
||||
0 -> list with positions
|
||||
1 -> pseudorange errs
|
||||
'''
|
||||
if x0 is None:
|
||||
x0 = [0, 0, 0, 0, 0]
|
||||
|
||||
if len(measurements) < min_measurements:
|
||||
return [],[],[]
|
||||
|
||||
Fx_pos = pr_residual(measurements, posfix_functions, signal=signal, no_nans=True)
|
||||
meas_cov = np.diag([meas.observables_std[signal]**2 for meas in measurements])
|
||||
|
||||
x, residual, x_std = gauss_newton(Fx_pos, x0, meas_cov)
|
||||
return x.tolist(), residual.tolist(), x_std
|
||||
|
||||
|
||||
def calc_vel_fix(measurements, est_pos, velfix_function=None, v0=None, signal='D1C', min_measurements=5):
|
||||
'''
|
||||
Calculates gps velocity fix using gauss newton method
|
||||
returns:
|
||||
0 -> list with velocities
|
||||
1 -> pseudorange_rate errs
|
||||
'''
|
||||
if v0 is None:
|
||||
v0 = [0, 0, 0, 0]
|
||||
|
||||
if len(measurements) < min_measurements:
|
||||
return [], [], []
|
||||
|
||||
Fx_vel = prr_residual(measurements, est_pos, velfix_function, signal=signal, no_nans=True)
|
||||
meas_cov = np.diag([meas.observables_std[signal]**2 for meas in measurements])
|
||||
|
||||
v, residual, x_std = gauss_newton(Fx_vel, v0, meas_cov)
|
||||
return v.tolist(), residual.tolist(), x_std
|
||||
|
||||
|
||||
def get_posfix_sympy_fun(constellation):
|
||||
# Unknowns
|
||||
x, y, z = sympy.Symbol('x'), sympy.Symbol('y'), sympy.Symbol('z')
|
||||
bc = sympy.Symbol('bc')
|
||||
bg = sympy.Symbol('bg')
|
||||
zero_theta = sympy.Symbol('zero_theta')
|
||||
var = [x, y, z, bc, bg]
|
||||
|
||||
# Knowns
|
||||
pr = sympy.Symbol('pr')
|
||||
sat_x, sat_y, sat_z = sympy.Symbol('sat_x'), sympy.Symbol('sat_y'), sympy.Symbol('sat_z')
|
||||
|
||||
theta = (EARTH_ROTATION_RATE * (pr - bc) / SPEED_OF_LIGHT)*zero_theta
|
||||
val = sympy.sqrt(
|
||||
(sat_x * sympy.cos(theta) + sat_y * sympy.sin(theta) - x) ** 2 +
|
||||
(sat_y * sympy.cos(theta) - sat_x * sympy.sin(theta) - y) ** 2 +
|
||||
(sat_z - z) ** 2
|
||||
)
|
||||
|
||||
if constellation == ConstellationId.GLONASS:
|
||||
res = val - (pr - bc - bg)
|
||||
elif constellation == ConstellationId.GPS:
|
||||
res = val - (pr - bc)
|
||||
else:
|
||||
raise NotImplementedError(f"Constellation {constellation} not supported")
|
||||
|
||||
res = [res] + [sympy.diff(res, v) for v in var]
|
||||
|
||||
return sympy.lambdify([x, y, z, bc, bg, pr, zero_theta, sat_x, sat_y, sat_z], res, modules=["numpy"])
|
||||
|
||||
|
||||
def get_velfix_sympy_func():
|
||||
# implementing this without sympy.Matrix gives a 2x speedup at generation
|
||||
|
||||
# knowns, receiver position, satellite position, satellite velocity
|
||||
ep_x, ep_y, ep_z = sympy.Symbol('ep_x'), sympy.Symbol('ep_y'), sympy.Symbol('ep_z')
|
||||
est_pos = np.array([ep_x, ep_y, ep_z])
|
||||
sp_x, sp_y, sp_z = sympy.Symbol('sp_x'), sympy.Symbol('sp_y'), sympy.Symbol('sp_z')
|
||||
sat_pos = np.array([sp_x, sp_y, sp_z])
|
||||
sv_x, sv_y, sv_z = sympy.Symbol('sv_x'), sympy.Symbol('sv_y'), sympy.Symbol('sv_z')
|
||||
sat_vel = np.array([sv_x, sv_y, sv_z])
|
||||
observables = sympy.Symbol('observables')
|
||||
|
||||
# unknown, receiver velocity
|
||||
v_x, v_y, v_z = sympy.Symbol('v_x'), sympy.Symbol('v_y'), sympy.Symbol('v_z')
|
||||
vel = np.array([v_x, v_y, v_z])
|
||||
vel_o = sympy.Symbol('vel_o')
|
||||
|
||||
loss = sat_pos - est_pos
|
||||
loss /= sympy.sqrt(loss.dot(loss))
|
||||
res = loss.dot(sat_vel - vel) - (observables - vel_o)
|
||||
|
||||
res = [res] + [sympy.diff(res, v) for v in [v_x, v_y, v_z, vel_o]]
|
||||
|
||||
return sympy.lambdify([
|
||||
ep_x, ep_y, ep_z, sp_x, sp_y, sp_z,
|
||||
sv_x, sv_y, sv_z, observables,
|
||||
v_x, v_y, v_z, vel_o
|
||||
],
|
||||
res, modules=["numpy"])
|
||||
|
||||
|
||||
def pr_residual(measurements: List[GNSSMeasurement], posfix_functions=None, signal='C1C', no_nans=False):
|
||||
|
||||
if posfix_functions is None:
|
||||
posfix_functions = {constellation: get_posfix_sympy_fun(constellation) for constellation in (ConstellationId.GPS, ConstellationId.GLONASS)}
|
||||
|
||||
def Fx_pos(inp):
|
||||
vals, gradients = [], []
|
||||
|
||||
for meas in measurements:
|
||||
if signal in meas.observables_final and np.isfinite(meas.observables_final[signal]):
|
||||
pr = meas.observables_final[signal]
|
||||
sat_pos = meas.sat_pos_final
|
||||
zero_theta = 0
|
||||
elif signal in meas.observables and np.isfinite(meas.observables[signal]) and meas.processed:
|
||||
pr = meas.observables[signal]
|
||||
pr += meas.sat_clock_err * SPEED_OF_LIGHT
|
||||
sat_pos = meas.sat_pos
|
||||
zero_theta = 1
|
||||
else:
|
||||
if not no_nans:
|
||||
vals.append(np.nan)
|
||||
gradients.append(np.nan)
|
||||
continue
|
||||
|
||||
val, *gradient = posfix_functions[meas.constellation_id](*inp, pr, zero_theta, *sat_pos)
|
||||
vals.append(val)
|
||||
gradients.append(gradient)
|
||||
return np.asarray(vals), np.asarray(gradients)
|
||||
return Fx_pos
|
||||
|
||||
|
||||
def prr_residual(measurements: List[GNSSMeasurement], est_pos, velfix_function=None, signal='D1C', no_nans=False):
|
||||
|
||||
if velfix_function is None:
|
||||
velfix_function = get_velfix_sympy_func()
|
||||
|
||||
def Fx_vel(vel):
|
||||
vals, gradients = [], []
|
||||
|
||||
for meas in measurements:
|
||||
if signal not in meas.observables or not np.isfinite(meas.observables[signal]):
|
||||
if not no_nans:
|
||||
vals.append(np.nan)
|
||||
gradients.append(np.nan)
|
||||
continue
|
||||
|
||||
sat_pos = meas.sat_pos_final if meas.corrected else meas.sat_pos
|
||||
|
||||
val, *gradient = velfix_function(est_pos[0], est_pos[1], est_pos[2],
|
||||
sat_pos[0], sat_pos[1], sat_pos[2],
|
||||
meas.sat_vel[0], meas.sat_vel[1], meas.sat_vel[2],
|
||||
meas.observables[signal],
|
||||
vel[0], vel[1], vel[2], vel[3])
|
||||
vals.append(val)
|
||||
gradients.append(gradient)
|
||||
|
||||
return np.asarray(vals), np.asarray(gradients)
|
||||
return Fx_vel
|
||||
@@ -1,381 +0,0 @@
|
||||
from math import sqrt
|
||||
from typing import Dict, List, Optional, Union
|
||||
|
||||
import numpy as np
|
||||
import datetime
|
||||
import struct
|
||||
|
||||
from . import constants
|
||||
from .ephemeris import Ephemeris
|
||||
from .lib.coordinates import LocalCoord
|
||||
from .gps_time import GPSTime
|
||||
from .helpers import ConstellationId, get_constellation_and_sv_id, get_nmea_id_from_constellation_and_svid, \
|
||||
rinex3_obs_from_rinex2_obs
|
||||
|
||||
|
||||
def array_from_normal_meas(meas):
|
||||
return np.concatenate(([meas.get_nmea_id()],
|
||||
[meas.recv_time_week],
|
||||
[meas.recv_time_sec],
|
||||
[meas.glonass_freq],
|
||||
[meas.observables['C1C']],
|
||||
[meas.observables_std['C1C']],
|
||||
[meas.observables['D1C']],
|
||||
[meas.observables_std['D1C']],
|
||||
[meas.observables['S1C']],
|
||||
[meas.observables['L1C']]))
|
||||
|
||||
|
||||
def normal_meas_from_array(arr):
|
||||
observables, observables_std = {}, {}
|
||||
observables['C1C'] = arr[4]
|
||||
observables_std['C1C'] = arr[5]
|
||||
observables['D1C'] = arr[6]
|
||||
observables_std['D1C'] = arr[7]
|
||||
observables['S1C'] = arr[8]
|
||||
observables['L1C'] = arr[9]
|
||||
constellation_id, sv_id = get_constellation_and_sv_id(nmea_id=arr[0])
|
||||
return GNSSMeasurement(constellation_id, sv_id, arr[1], arr[2],
|
||||
observables, observables_std, arr[3])
|
||||
|
||||
|
||||
class GNSSMeasurement:
|
||||
PRN = 0
|
||||
RECV_TIME_WEEK = 1
|
||||
RECV_TIME_SEC = 2
|
||||
GLONASS_FREQ = 3
|
||||
|
||||
PR = 4
|
||||
PR_STD = 5
|
||||
PRR = 6
|
||||
PRR_STD = 7
|
||||
|
||||
SAT_POS = slice(8, 11)
|
||||
SAT_VEL = slice(11, 14)
|
||||
|
||||
def __init__(self, constellation_id: ConstellationId, sv_id: int, recv_time_week: int, recv_time_sec: float, observables: Dict[str, float],
|
||||
observables_std: Dict[str, float], glonass_freq: Union[int, float, None] = None):
|
||||
# Metadata
|
||||
# prn: unique satellite id
|
||||
self.prn = "%s%02d" % (constellation_id.to_rinex_char(), sv_id) # satellite ID in rinex convention
|
||||
self.constellation_id = constellation_id
|
||||
self.sv_id = sv_id # satellite id per constellation
|
||||
|
||||
self.recv_time_week = recv_time_week
|
||||
self.recv_time_sec = recv_time_sec
|
||||
self.recv_time = GPSTime(recv_time_week, recv_time_sec)
|
||||
self.glonass_freq = glonass_freq # glonass channel
|
||||
|
||||
# Measurements
|
||||
self.observables = observables
|
||||
self.observables_std = observables_std
|
||||
|
||||
# flags
|
||||
self.processed = False
|
||||
self.corrected = False
|
||||
|
||||
# sat info
|
||||
self.sat_pos = np.array([np.nan, np.nan, np.nan])
|
||||
self.sat_vel = np.array([np.nan, np.nan, np.nan])
|
||||
self.sat_clock_err = np.nan
|
||||
self.sat_ephemeris: Optional[Ephemeris] = None
|
||||
|
||||
self.sat_pos_final = np.array([np.nan, np.nan, np.nan]) # sat_pos in receiver time's ECEF frame instead of satellite time's ECEF frame
|
||||
self.observables_final: Dict[str, float] = {}
|
||||
|
||||
def process(self, dog):
|
||||
sat_time = self.recv_time - self.observables['C1C']/constants.SPEED_OF_LIGHT
|
||||
sat_info = dog.get_sat_info(self.prn, sat_time)
|
||||
if sat_info is None:
|
||||
return False
|
||||
self.sat_pos, self.sat_vel, self.sat_clock_err, _, self.sat_ephemeris = sat_info
|
||||
self.processed = True
|
||||
return True
|
||||
|
||||
def correct(self, est_pos, dog, correct_delay=True):
|
||||
for obs in self.observables:
|
||||
if obs[0] == 'C': # or obs[0] == 'L':
|
||||
if correct_delay:
|
||||
delay = dog.get_delay(self.prn, self.recv_time, est_pos, signal=obs)
|
||||
else:
|
||||
delay = 0.0
|
||||
if delay is not None:
|
||||
self.observables_final[obs] = (self.observables[obs] +
|
||||
self.sat_clock_err*constants.SPEED_OF_LIGHT -
|
||||
delay)
|
||||
else:
|
||||
self.observables_final[obs] = self.observables[obs]
|
||||
if 'C1C' in self.observables_final and 'C2P' in self.observables_final:
|
||||
self.observables_final['IOF'] = (((constants.GPS_L1**2)*self.observables_final['C1C'] -
|
||||
(constants.GPS_L2**2)*self.observables_final['C2P'])/
|
||||
(constants.GPS_L1**2 - constants.GPS_L2**2))
|
||||
|
||||
geometric_range = np.linalg.norm(self.sat_pos - est_pos)
|
||||
theta_1 = constants.EARTH_ROTATION_RATE * geometric_range / constants.SPEED_OF_LIGHT
|
||||
self.sat_pos_final = np.array([self.sat_pos[0] * np.cos(theta_1) + self.sat_pos[1] * np.sin(theta_1),
|
||||
self.sat_pos[1] * np.cos(theta_1) - self.sat_pos[0] * np.sin(theta_1),
|
||||
self.sat_pos[2]])
|
||||
if 'C1C' in self.observables_final and np.isfinite(self.observables_final['C1C']):
|
||||
self.corrected = True
|
||||
return True
|
||||
return False
|
||||
|
||||
def as_array(self, only_corrected=True):
|
||||
observables = self.observables_final
|
||||
sat_pos = self.sat_pos_final
|
||||
if not self.corrected:
|
||||
if only_corrected:
|
||||
raise NotImplementedError('Only corrected measurements can be put into arrays')
|
||||
else:
|
||||
observables = self.observables
|
||||
sat_pos = self.sat_pos
|
||||
ret = np.array([self.get_nmea_id(), self.recv_time_week, self.recv_time_sec, self.glonass_freq,
|
||||
observables['C1C'], self.observables_std['C1C'],
|
||||
observables['D1C'], self.observables_std['D1C']])
|
||||
return np.concatenate((ret, sat_pos, self.sat_vel))
|
||||
|
||||
def __repr__(self):
|
||||
time = self.recv_time.as_datetime().strftime('%Y-%m-%dT%H:%M:%S.%f')
|
||||
return f"<GNSSMeasurement from {self.prn} at {time}>"
|
||||
|
||||
def get_nmea_id(self):
|
||||
return get_nmea_id_from_constellation_and_svid(self.constellation_id, self.sv_id)
|
||||
|
||||
|
||||
def process_measurements(measurements: List[GNSSMeasurement], dog) -> List[GNSSMeasurement]:
|
||||
proc_measurements = []
|
||||
for meas in measurements:
|
||||
if meas.process(dog):
|
||||
proc_measurements.append(meas)
|
||||
return proc_measurements
|
||||
|
||||
|
||||
def correct_measurements(measurements: List[GNSSMeasurement], est_pos, dog, correct_delay=True) -> List[GNSSMeasurement]:
|
||||
corrected_measurements = []
|
||||
for meas in measurements:
|
||||
if meas.correct(est_pos, dog, correct_delay=correct_delay):
|
||||
corrected_measurements.append(meas)
|
||||
return corrected_measurements
|
||||
|
||||
|
||||
def group_measurements_by_epoch(measurements):
|
||||
meas_filt_by_t = [[measurements[0]]]
|
||||
for m in measurements[1:]:
|
||||
if abs(m.recv_time - meas_filt_by_t[-1][-1].recv_time) > 1e-9:
|
||||
meas_filt_by_t.append([])
|
||||
meas_filt_by_t[-1].append(m)
|
||||
return meas_filt_by_t
|
||||
|
||||
|
||||
def group_measurements_by_sat(measurements):
|
||||
measurements_by_sat = {}
|
||||
sats = {m.prn for m in measurements}
|
||||
for sat in sats:
|
||||
measurements_by_sat[sat] = [m for m in measurements if m.prn == sat]
|
||||
return measurements_by_sat
|
||||
|
||||
def gps_time_from_qcom_report(gnss_msg):
|
||||
if gnss_msg.which() == 'measurementReport':
|
||||
report = gnss_msg.measurementReport
|
||||
constellation = ConstellationId.from_qcom_source(report.source)
|
||||
if constellation in [ConstellationId.GPS, ConstellationId.SBAS]:
|
||||
report_time = GPSTime(report.gpsWeek, report.milliseconds / 1000.0)
|
||||
elif constellation == ConstellationId.GLONASS:
|
||||
report_time = GPSTime.from_glonass(report.glonassCycleNumber,
|
||||
report.glonassNumberOfDays,
|
||||
report.milliseconds / 1000.0)
|
||||
else:
|
||||
raise NotImplementedError(f'Unknownconstellation {report.source}')
|
||||
else:
|
||||
report = gnss_msg.drMeasurementReport
|
||||
constellation = ConstellationId.from_qcom_source(report.source)
|
||||
if ConstellationId.from_qcom_source(report.source) in [ConstellationId.GPS, ConstellationId.SBAS]:
|
||||
report_time = GPSTime(report.gpsWeek, report.gpsMilliseconds / 1000.0)
|
||||
elif constellation == ConstellationId.GLONASS:
|
||||
report_time = GPSTime.from_glonass(report.glonassYear,
|
||||
report.glonassDay,
|
||||
report.glonassMilliseconds / 1000.0)
|
||||
else:
|
||||
raise NotImplementedError(f'Unknownconstellation {report.source}')
|
||||
return report_time
|
||||
|
||||
def get_measurements_from_qcom_reports(reports):
|
||||
new_meas_dr = []
|
||||
new_meas = []
|
||||
for gnss_msg in reports:
|
||||
if gnss_msg.which() == 'drMeasurementReport':
|
||||
new_meas_dr.extend(read_raw_qcom(gnss_msg.drMeasurementReport))
|
||||
else:
|
||||
new_meas.extend(read_raw_qcom(gnss_msg.measurementReport))
|
||||
sat_dict_dr = {meas.prn: meas for meas in new_meas_dr}
|
||||
out_meas = []
|
||||
for meas in new_meas:
|
||||
if meas.prn in sat_dict_dr:
|
||||
# Sometimes DR measurements are complete garbage, in those cases non-DR measurements are still sane, so cross-check
|
||||
if abs(meas.observables['C1C'] - sat_dict_dr[meas.prn].observables['C1C']) < 1000:
|
||||
meas.observables['C1C'] = sat_dict_dr[meas.prn].observables['C1C']
|
||||
meas.observables_std['C1C'] = sat_dict_dr[meas.prn].observables_std['C1C']
|
||||
out_meas.append(meas)
|
||||
return out_meas
|
||||
|
||||
def read_raw_qcom(report):
|
||||
dr = 'DrMeasurementReport' in str(report.schema)
|
||||
# Only gps/sbas and glonass are supported
|
||||
constellation_id = ConstellationId.from_qcom_source(report.source)
|
||||
if constellation_id in [ConstellationId.GPS, ConstellationId.SBAS]: # gps/sbas
|
||||
if dr:
|
||||
recv_tow = report.gpsMilliseconds / 1000.0 # seconds
|
||||
time_bias_ms = struct.unpack("f", struct.pack("I", report.gpsTimeBiasMs))[0]
|
||||
else:
|
||||
recv_tow = report.milliseconds / 1000.0 # seconds
|
||||
time_bias_ms = report.timeBias
|
||||
recv_time = GPSTime(report.gpsWeek, recv_tow)
|
||||
elif constellation_id == ConstellationId.GLONASS:
|
||||
if dr:
|
||||
recv_tow = report.glonassMilliseconds / 1000.0 # seconds
|
||||
recv_time = GPSTime.from_glonass(report.glonassYear, report.glonassDay, recv_tow)
|
||||
time_bias_ms = report.glonassTimeBias
|
||||
else:
|
||||
recv_tow = report.milliseconds / 1000.0 # seconds
|
||||
recv_time = GPSTime.from_glonass(report.glonassCycleNumber, report.glonassNumberOfDays, recv_tow)
|
||||
time_bias_ms = report.timeBias
|
||||
else:
|
||||
raise NotImplementedError('Only GPS (0), SBAS (1) and GLONASS (6) are supported from qcom, not:', {report.source})
|
||||
# logging.debug(recv_time, report.source, time_bias_ms, dr)
|
||||
measurements = []
|
||||
for i in report.sv:
|
||||
# todo change svId to nmea_id in cereal message. Or better: change the publisher to publish correct svId's, since constellation id is also given
|
||||
nmea_id = i.svId
|
||||
if nmea_id == 255:
|
||||
# TODO nmea_id is not valid. Fix publisher
|
||||
continue
|
||||
_, sv_id = get_constellation_and_sv_id(nmea_id)
|
||||
if not i.measurementStatus.measurementNotUsable and i.measurementStatus.satelliteTimeIsKnown and i.measurementStatus.freshMeasurementIndicator:
|
||||
observables, observables_std = {}, {}
|
||||
if dr:
|
||||
sat_tow = (i.filteredMeasurementIntegral + i.filteredMeasurementFraction + i.latency + time_bias_ms) / 1000
|
||||
else:
|
||||
sat_tow = (i.unfilteredMeasurementIntegral + i.unfilteredMeasurementFraction + i.latency + time_bias_ms) / 1000
|
||||
observables['C1C'] = (recv_tow - sat_tow)*constants.SPEED_OF_LIGHT
|
||||
observables_std['C1C'] = i.unfilteredTimeUncertainty * 1e-3 * constants.SPEED_OF_LIGHT # always use unfiltered std, filtered std is bigger?
|
||||
if i.measurementStatus.fineOrCoarseVelocity:
|
||||
# about 10x better, perhaps filtered with carrier phase?
|
||||
observables['D1C'] = i.fineSpeed
|
||||
observables_std['D1C'] = sqrt(i.fineSpeedUncertainty) # sqrt empirically makes performance much better, might be wrong
|
||||
else:
|
||||
observables['D1C'] = i.unfilteredSpeed
|
||||
observables_std['D1C'] = i.unfilteredSpeedUncertainty
|
||||
observables['S1C'] = (i.carrierNoise/100.) if i.carrierNoise != 0 else np.nan
|
||||
observables['L1C'] = np.nan
|
||||
# logging.debug(" %.5f %3d %10.2f %7.2f %7.2f %.2f %d" % (recv_time.tow, nmea_id,
|
||||
# observables['C1C'], observables_std['C1C'],
|
||||
# observables_std['D1C'], observables['S1C'], i.latency), i.observationState, i.measurementStatus.fineOrCoarseVelocity)
|
||||
glonass_freq = (i.glonassFrequencyIndex - 7) if constellation_id == ConstellationId.GLONASS else np.nan
|
||||
measurements.append(GNSSMeasurement(constellation_id, sv_id,
|
||||
recv_time.week,
|
||||
recv_time.tow,
|
||||
observables,
|
||||
observables_std,
|
||||
glonass_freq))
|
||||
return measurements
|
||||
|
||||
|
||||
def read_raw_ublox(report) -> List[GNSSMeasurement]:
|
||||
recv_tow = report.rcvTow # seconds
|
||||
recv_week = report.gpsWeek
|
||||
measurements = []
|
||||
for i in report.measurements:
|
||||
# only add Gps and Glonass fixes
|
||||
if i.gnssId in [ConstellationId.GPS, ConstellationId.GLONASS]:
|
||||
if i.svId > 32 or i.pseudorange > 2**32:
|
||||
continue
|
||||
observables = {}
|
||||
observables_std = {}
|
||||
if i.trackingStatus.pseudorangeValid and i.sigId == 0:
|
||||
observables['C1C'] = i.pseudorange
|
||||
# Empirically it seems obvious ublox's std is
|
||||
# actually a variation
|
||||
observables_std['C1C'] = sqrt(i.pseudorangeStdev)*10
|
||||
if i.gnssId == ConstellationId.GLONASS:
|
||||
glonass_freq = i.glonassFrequencyIndex - 7
|
||||
observables['D1C'] = -(constants.SPEED_OF_LIGHT / (constants.GLONASS_L1 + glonass_freq * constants.GLONASS_L1_DELTA)) * i.doppler
|
||||
else: # GPS
|
||||
glonass_freq = np.nan
|
||||
observables['D1C'] = -(constants.SPEED_OF_LIGHT / constants.GPS_L1) * i.doppler
|
||||
observables_std['D1C'] = (constants.SPEED_OF_LIGHT / constants.GPS_L1) * i.dopplerStdev
|
||||
observables['S1C'] = i.cno
|
||||
if i.trackingStatus.carrierPhaseValid:
|
||||
observables['L1C'] = i.carrierCycles
|
||||
else:
|
||||
observables['L1C'] = np.nan
|
||||
|
||||
measurements.append(GNSSMeasurement(ConstellationId(i.gnssId), i.svId, recv_week, recv_tow,
|
||||
observables, observables_std, glonass_freq))
|
||||
return measurements
|
||||
|
||||
|
||||
def read_rinex_obs(obsdata) -> List[List[GNSSMeasurement]]:
|
||||
measurements: List[List[GNSSMeasurement]] = []
|
||||
obsdata_keys = list(obsdata.data.keys())
|
||||
first_sat = obsdata_keys[0]
|
||||
n = len(obsdata.data[first_sat]['Epochs'])
|
||||
for i in range(n):
|
||||
recv_time_datetime = obsdata.data[first_sat]['Epochs'][i]
|
||||
recv_time_datetime = recv_time_datetime.astype(datetime.datetime)
|
||||
recv_time = GPSTime.from_datetime(recv_time_datetime)
|
||||
measurements.append([])
|
||||
for sat_str in obsdata_keys:
|
||||
if np.isnan(obsdata.data[sat_str]['C1'][i]):
|
||||
continue
|
||||
observables, observables_std = {}, {}
|
||||
for obs in obsdata.data[sat_str]:
|
||||
if obs == 'Epochs':
|
||||
continue
|
||||
rinex3_obs_key = rinex3_obs_from_rinex2_obs(obs)
|
||||
observables[rinex3_obs_key] = obsdata.data[sat_str][obs][i]
|
||||
observables_std[rinex3_obs_key] = 1.
|
||||
|
||||
constellation_id, sv_id = get_constellation_and_sv_id(int(sat_str))
|
||||
measurements[-1].append(GNSSMeasurement(constellation_id, sv_id,
|
||||
recv_time.week, recv_time.tow,
|
||||
observables, observables_std))
|
||||
return measurements
|
||||
|
||||
|
||||
def get_Q(recv_pos, sat_positions):
|
||||
local = LocalCoord.from_ecef(recv_pos)
|
||||
sat_positions_rel = local.ecef2ned(sat_positions)
|
||||
sat_distances = np.linalg.norm(sat_positions_rel, axis=1)
|
||||
A = np.column_stack((sat_positions_rel[:,0]/sat_distances, # pylint: disable=unsubscriptable-object
|
||||
sat_positions_rel[:,1]/sat_distances, # pylint: disable=unsubscriptable-object
|
||||
sat_positions_rel[:,2]/sat_distances, # pylint: disable=unsubscriptable-object
|
||||
-np.ones(len(sat_distances))))
|
||||
if A.shape[0] < 4 or np.linalg.matrix_rank(A) < 4:
|
||||
return np.inf*np.ones((4,4))
|
||||
Q = np.linalg.inv(A.T.dot(A))
|
||||
return Q
|
||||
|
||||
|
||||
def get_DOP(recv_pos, sat_positions):
|
||||
Q = get_Q(recv_pos, sat_positions)
|
||||
return np.sqrt(np.trace(Q))
|
||||
|
||||
|
||||
def get_HDOP(recv_pos, sat_positions):
|
||||
Q = get_Q(recv_pos, sat_positions)
|
||||
return np.sqrt(np.trace(Q[:2,:2]))
|
||||
|
||||
|
||||
def get_VDOP(recv_pos, sat_positions):
|
||||
Q = get_Q(recv_pos, sat_positions)
|
||||
return np.sqrt(Q[2,2])
|
||||
|
||||
|
||||
def get_TDOP(recv_pos, sat_positions):
|
||||
Q = get_Q(recv_pos, sat_positions)
|
||||
return np.sqrt(Q[3,3])
|
||||
|
||||
|
||||
def get_PDOP(recv_pos, sat_positions):
|
||||
Q = get_Q(recv_pos, sat_positions)
|
||||
return np.sqrt(np.trace(Q[:3,:3]))
|
||||
@@ -1,251 +0,0 @@
|
||||
|
||||
# Copyright (C) 2014 Swift Navigation Inc.
|
||||
#
|
||||
# This source is subject to the license found in the file 'LICENSE' which must
|
||||
# be be distributed together with this source. All other rights reserved.
|
||||
#
|
||||
# THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND,
|
||||
# EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED
|
||||
# WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
|
||||
|
||||
import datetime
|
||||
import numpy as np
|
||||
import logging
|
||||
|
||||
def floatornan(x):
|
||||
if x == '' or x[-1] == ' ':
|
||||
return np.NaN
|
||||
return float(x)
|
||||
|
||||
|
||||
def digitorzero(x):
|
||||
if x == ' ' or x == '':
|
||||
return 0
|
||||
return int(x)
|
||||
|
||||
|
||||
def padline(l, n=16):
|
||||
x = len(l)
|
||||
x_ = n * ((x + n - 1) // n)
|
||||
padded = l + ' ' * (x_ - x)
|
||||
while len(padded) < 70:
|
||||
padded += ' ' * 16
|
||||
return padded
|
||||
|
||||
|
||||
TOTAL_SATS = 132 # Increased to support Galileo
|
||||
|
||||
|
||||
class DownloadError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class RINEXFile:
|
||||
def __init__(self, filename, rate=None):
|
||||
self.rate = rate
|
||||
try:
|
||||
with open(filename) as f:
|
||||
self._read_header(f)
|
||||
self._read_data(f)
|
||||
except TypeError:
|
||||
logging.exception("TypeError, file likely not downloaded.")
|
||||
raise DownloadError("file download failure")
|
||||
except FileNotFoundError:
|
||||
logging.exception("File not found in directory.")
|
||||
raise DownloadError("file missing in download cache")
|
||||
def _read_header(self, f):
|
||||
version_line = padline(f.readline(), 80)
|
||||
|
||||
self.version = float(version_line[0:9])
|
||||
if (self.version > 2.11):
|
||||
raise ValueError(
|
||||
f"RINEX file versions > 2.11 not supported (file version {self.version:f})")
|
||||
|
||||
self.filetype = version_line[20]
|
||||
if self.filetype not in "ONGM": # Check valid file type
|
||||
raise ValueError(f"RINEX file type '{self.filetype}' not supported")
|
||||
if self.filetype != 'O':
|
||||
raise ValueError("Only 'OBSERVATION DATA' RINEX files are currently supported")
|
||||
|
||||
self.gnss = version_line[40]
|
||||
if self.gnss not in " GRSEM": # Check valid satellite system
|
||||
raise ValueError(f"Satellite system '{self.filetype}' not supported")
|
||||
if self.gnss == ' ':
|
||||
self.gnss = 'G'
|
||||
if self.gnss != 'G':
|
||||
#raise ValueError("Only GPS data currently supported")
|
||||
pass
|
||||
|
||||
self.comment = ""
|
||||
while True: # Read the rest of the header
|
||||
line = padline(f.readline(), 80)
|
||||
label = line[60:80].rstrip()
|
||||
if label == "END OF HEADER":
|
||||
break
|
||||
if label == "COMMENT":
|
||||
self.comment += line[:60] + '\n'
|
||||
if label == "MARKER NAME":
|
||||
self.marker_name = line[:60].rstrip()
|
||||
if self.marker_name == '':
|
||||
self.marker_name = 'UNKNOWN'
|
||||
if label == "# / TYPES OF OBSERV":
|
||||
# RINEX files can have multiple line headers
|
||||
# This code handles the case
|
||||
try:
|
||||
n_obs = int(line[0:6])
|
||||
self.obs_types = []
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
if n_obs <= 9:
|
||||
for i in range(0, n_obs):
|
||||
self.obs_types.append(line[10 + 6 * i:12 + 6 * i])
|
||||
if n_obs > 9:
|
||||
for i in range(0, 9):
|
||||
self.obs_types.append(line[10 + 6 * i:12 + 6 * i])
|
||||
n_obs -= 9
|
||||
|
||||
def _read_next_non_comment(self, f):
|
||||
line = f.readline()
|
||||
while line and line.find('COMMENT') != -1:
|
||||
line = f.readline()
|
||||
return line
|
||||
|
||||
def _read_epoch_header(self, f):
|
||||
epoch_hdr = self._read_next_non_comment(f)
|
||||
if epoch_hdr == '':
|
||||
return None
|
||||
# ignore any line with these three strings
|
||||
skippable = ('0.0000000 4 5', 'MARKER NUMBER', ' 4 1')
|
||||
while any(skip in epoch_hdr for skip in skippable):
|
||||
epoch_hdr = self._read_next_non_comment(f)
|
||||
|
||||
if epoch_hdr == '':
|
||||
return None
|
||||
|
||||
year = int(epoch_hdr[1:3])
|
||||
if year >= 80:
|
||||
year += 1900
|
||||
else:
|
||||
year += 2000
|
||||
month = int(epoch_hdr[4:6])
|
||||
day = int(epoch_hdr[7:9])
|
||||
hour = int(epoch_hdr[10:12])
|
||||
minute = int(epoch_hdr[13:15])
|
||||
second = int(epoch_hdr[15:18])
|
||||
microsecond = int(
|
||||
epoch_hdr[19:25]) # Discard the least sig. fig. (use microseconds only).
|
||||
epoch = datetime.datetime(year, month, day, hour, minute, second, microsecond)
|
||||
|
||||
flag = int(epoch_hdr[28])
|
||||
allowed_flags = {0, 3, 4}
|
||||
if flag not in allowed_flags:
|
||||
raise ValueError("Don't know how to handle epoch flag %d in epoch header:\n%s" %
|
||||
(flag, epoch_hdr))
|
||||
|
||||
n_sats = int(epoch_hdr[29:32])
|
||||
if flag > 1: # event flag: nsats is number of records
|
||||
for i in range(n_sats):
|
||||
f.readline()
|
||||
return None
|
||||
|
||||
sats = []
|
||||
for i in range(0, n_sats):
|
||||
if ((i % 12) == 0) and (i > 0):
|
||||
epoch_hdr = f.readline()
|
||||
sats.append(epoch_hdr[(32 + (i % 12) * 3):(35 + (i % 12) * 3)])
|
||||
|
||||
return epoch, flag, sats
|
||||
|
||||
def _read_obs(self, f, n_sat, sat_map):
|
||||
obs = np.empty((TOTAL_SATS, len(self.obs_types)), dtype=np.float64) * np.NaN
|
||||
lli = np.zeros((TOTAL_SATS, len(self.obs_types)), dtype=np.uint8)
|
||||
signal_strength = np.zeros((TOTAL_SATS, len(self.obs_types)), dtype=np.uint8)
|
||||
|
||||
for i in range(n_sat):
|
||||
# Join together observations for a single satellite if split across lines.
|
||||
obs_line = ''.join(
|
||||
padline(f.readline()[:-1], 16) for _ in range((len(self.obs_types) + 4) // 5))
|
||||
for j in range(len(self.obs_types)):
|
||||
obs_record = obs_line[16 * j:16 * (j + 1)]
|
||||
obs[int(sat_map[i]), j] = floatornan(obs_record[0:14])
|
||||
lli[int(sat_map[i]), j] = digitorzero(obs_record[14:15])
|
||||
signal_strength[int(sat_map[i]), j] = digitorzero(obs_record[15:16])
|
||||
|
||||
return obs, lli, signal_strength
|
||||
|
||||
def _skip_obs(self, f, n_sat):
|
||||
for i in range(n_sat):
|
||||
for _ in range((len(self.obs_types) + 4) // 5):
|
||||
f.readline()
|
||||
|
||||
def _read_data_chunk(self, f, CHUNK_SIZE=10000):
|
||||
obss = np.empty(
|
||||
(CHUNK_SIZE, TOTAL_SATS, len(self.obs_types)), dtype=np.float64) * np.NaN
|
||||
llis = np.zeros((CHUNK_SIZE, TOTAL_SATS, len(self.obs_types)), dtype=np.uint8)
|
||||
signal_strengths = np.zeros(
|
||||
(CHUNK_SIZE, TOTAL_SATS, len(self.obs_types)), dtype=np.uint8)
|
||||
epochs = np.zeros(CHUNK_SIZE, dtype='datetime64[us]')
|
||||
flags = np.zeros(CHUNK_SIZE, dtype=np.uint8)
|
||||
|
||||
i = 0
|
||||
while True:
|
||||
hdr = self._read_epoch_header(f)
|
||||
if hdr is None:
|
||||
break
|
||||
# data faster than desired rate: ignore it
|
||||
if self.rate and (hdr[0].microsecond or hdr[0].second % self.rate != 0):
|
||||
self._skip_obs(f, len(hdr[2]))
|
||||
continue
|
||||
epoch, flags[i], sats = hdr
|
||||
epochs[i] = np.datetime64(epoch)
|
||||
sat_map = np.ones(len(sats)) * -1
|
||||
for n, sat in enumerate(sats):
|
||||
if sat[0] == 'G':
|
||||
sat_map[n] = int(sat[1:]) - 1
|
||||
if sat[0] == 'R':
|
||||
sat_map[n] = int(sat[1:]) - 1 + 64
|
||||
obss[i], llis[i], signal_strengths[i] = self._read_obs(f, len(sats), sat_map)
|
||||
i += 1
|
||||
if i >= CHUNK_SIZE:
|
||||
break
|
||||
|
||||
return obss[:i], llis[:i], signal_strengths[:i], epochs[:i], flags[:i]
|
||||
|
||||
def _read_data(self, f):
|
||||
self.data = {}
|
||||
while True:
|
||||
obss, llis, signal_strengths, epochs, flags = self._read_data_chunk(f)
|
||||
if obss.shape[0] == 0:
|
||||
break
|
||||
|
||||
for i, sv in enumerate(['%02d' % d for d in range(1, TOTAL_SATS+1)]):
|
||||
if sv not in self.data:
|
||||
self.data[sv] = {}
|
||||
for j, obs_type in enumerate(self.obs_types):
|
||||
if obs_type in self.data[sv]:
|
||||
self.data[sv][obs_type] = np.append(self.data[sv][obs_type], obss[:, i, j])
|
||||
else:
|
||||
self.data[sv][obs_type] = obss[:, i, j]
|
||||
if 'Epochs' in self.data[sv]:
|
||||
self.data[sv]['Epochs'] = np.append(self.data[sv]['Epochs'], epochs)
|
||||
else:
|
||||
self.data[sv]['Epochs'] = epochs
|
||||
for sat in list(self.data.keys()):
|
||||
if np.all(np.isnan(self.data[sat]['C1'])):
|
||||
del self.data[sat]
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
from numpy import cos, exp, pi
|
||||
from .lib.coordinates import ecef2geodetic
|
||||
|
||||
|
||||
def saast(pos, el, humi=0.75, temp0=15.0):
|
||||
"""
|
||||
Function from RTKlib: https://github.com/tomojitakasu/RTKLIB/blob/master/src/rtkcmn.c#L3362-3362
|
||||
with no changes
|
||||
:param time: time
|
||||
:param pos: receiver position {ecef} m)
|
||||
:param el: azimuth/elevation angle {az,el} (rad) -- we do not use az
|
||||
:param humi: relative humidity
|
||||
:param temp0: temperature (Celsius)
|
||||
:return: tropospheric delay (m)
|
||||
"""
|
||||
pos_rad = ecef2geodetic(pos, radians=True)
|
||||
if pos_rad[2] < -1E3 or 1E4 < pos_rad[2] or el <= 0:
|
||||
return 0.0
|
||||
|
||||
# /* standard atmosphere */
|
||||
hgt = 0.0 if pos_rad[2] < 0.0 else pos_rad[2]
|
||||
|
||||
pres = 1013.25 * pow(1.0 - 2.2557E-5 * hgt, 5.2568)
|
||||
temp = temp0 - 6.5E-3 * hgt + 273.16
|
||||
e = 6.108 * humi * exp((17.15 * temp - 4684.0) / (temp - 38.45))
|
||||
|
||||
# /* saastamoninen model */
|
||||
z = pi / 2.0 - el
|
||||
trph = 0.0022768 * pres / (
|
||||
1.0 - 0.00266 * cos(2.0 * pos_rad[0]) - 0.00028 * hgt / 1E3) / cos(z)
|
||||
trpw = 0.002277 * (1255.0 / temp + 0.05) * e / cos(z)
|
||||
return trph + trpw
|
||||
+3
-3
@@ -14,10 +14,10 @@ function agnos_init {
|
||||
echo -n openpilot > /data/params/d/GithubUsername
|
||||
cat /usr/comma/setup_keys > /data/params/d/GithubSshKeys
|
||||
fi
|
||||
|
||||
|
||||
# wait longer for weston to come up
|
||||
if [ -f "$BASEDIR/prebuilt" ]; then
|
||||
sleep 3
|
||||
sleep 5
|
||||
fi
|
||||
|
||||
# TODO: move this to agnos
|
||||
@@ -90,7 +90,7 @@ function launch {
|
||||
|
||||
# start manager
|
||||
cd selfdrive/manager
|
||||
./custom_dep.py && ./build.py && ./manager.py
|
||||
./build.py && ./manager.py
|
||||
|
||||
# if broken, keep on screen error
|
||||
while true; do sleep 1; done
|
||||
|
||||
+1
-1
@@ -7,7 +7,7 @@ export OPENBLAS_NUM_THREADS=1
|
||||
export VECLIB_MAXIMUM_THREADS=1
|
||||
|
||||
if [ -z "$AGNOS_VERSION" ]; then
|
||||
export AGNOS_VERSION="8.2"
|
||||
export AGNOS_VERSION="9.1"
|
||||
fi
|
||||
|
||||
if [ -z "$PASSIVE" ]; then
|
||||
|
||||
@@ -54,7 +54,7 @@ public:
|
||||
bool ignore_checksum = false;
|
||||
bool ignore_counter = false;
|
||||
|
||||
bool parse(uint64_t sec, const std::vector<uint8_t> &dat);
|
||||
bool parse(uint64_t nanos, const std::vector<uint8_t> &dat);
|
||||
bool update_counter_generic(int64_t v, int cnt_size);
|
||||
};
|
||||
|
||||
@@ -69,9 +69,9 @@ private:
|
||||
public:
|
||||
bool can_valid = false;
|
||||
bool bus_timeout = false;
|
||||
uint64_t first_sec = 0;
|
||||
uint64_t last_sec = 0;
|
||||
uint64_t last_nonempty_sec = 0;
|
||||
uint64_t first_nanos = 0;
|
||||
uint64_t last_nanos = 0;
|
||||
uint64_t last_nonempty_nanos = 0;
|
||||
uint64_t bus_timeout_threshold = 0;
|
||||
uint64_t can_invalid_cnt = CAN_INVALID_CNT;
|
||||
|
||||
@@ -81,10 +81,10 @@ public:
|
||||
#ifndef DYNAMIC_CAPNP
|
||||
void update_string(const std::string &data, bool sendcan);
|
||||
void update_strings(const std::vector<std::string> &data, std::vector<SignalValue> &vals, bool sendcan);
|
||||
void UpdateCans(uint64_t sec, const capnp::List<cereal::CanData>::Reader& cans);
|
||||
void UpdateCans(uint64_t nanos, const capnp::List<cereal::CanData>::Reader& cans);
|
||||
#endif
|
||||
void UpdateCans(uint64_t sec, const capnp::DynamicStruct::Reader& cans);
|
||||
void UpdateValid(uint64_t sec);
|
||||
void UpdateCans(uint64_t nanos, const capnp::DynamicStruct::Reader& cans);
|
||||
void UpdateValid(uint64_t nanos);
|
||||
void query_latest(std::vector<SignalValue> &vals, uint64_t last_ts = 0);
|
||||
};
|
||||
|
||||
|
||||
Binary file not shown.
+873
-197
File diff suppressed because it is too large
Load Diff
Binary file not shown.
+1071
-272
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@@ -59,11 +59,9 @@ BO_ 500 DAS_3: 8 XXX
|
||||
SG_ DISABLE_FUEL_SHUTOFF : 23|1@1+ (1,0) [0|0] "" XXX
|
||||
SG_ GR_MAX_REQ : 32|4@1+ (1,0) [0|0] "" XXX
|
||||
SG_ ACC_DECEL_REQ : 36|3@1+ (1,0) [0|0] "" XXX
|
||||
SG_ STS : 46|2@1+ (1,0) [0|0] "" XXX
|
||||
SG_ ACC_FAULTED : 47|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ ACC_FAULTED : 46|2@1+ (1,0) [0|0] "" XXX
|
||||
SG_ COLLISION_BRK_PREP : 48|1@1+ (1,0) [0|0] "" XXX
|
||||
SG_ ACC_BRK_PREP : 49|1@1+ (1,0) [0|0] "" XXX
|
||||
SG_ BRAKE_BOOL_1 : 36|1@0+ (1,0) [0|3] "" XXX
|
||||
SG_ COUNTER : 55|4@0+ (1,0) [0|15] "" XXX
|
||||
SG_ CHECKSUM : 63|8@0+ (1,0) [0|255] "" XXX
|
||||
|
||||
@@ -74,6 +72,7 @@ BO_ 501 DAS_4: 8 XXX
|
||||
SG_ ACC_DISTANCE_CONFIG_2 : 41|2@0+ (1,0) [0|3] "" XXX
|
||||
SG_ SPEED_DIGITAL : 63|8@0+ (1,0) [0|255] "mph" XXX
|
||||
SG_ ACC_STATE : 38|3@0+ (1,0) [0|7] "" XXX
|
||||
SG_ FCW_BRAKE_ENABLED : 29|1@0+ (1,0) [0|1] "" XXX
|
||||
|
||||
BO_ 544 EPS_2: 8 XXX
|
||||
SG_ LKAS_STATE : 23|4@0+ (1,0) [0|15] "" XXX
|
||||
@@ -103,12 +102,23 @@ BO_ 571 CRUISE_BUTTONS: 3 XXX
|
||||
SG_ COUNTER : 15|4@0+ (1,0) [0|15] "" XXX
|
||||
SG_ CHECKSUM : 23|8@0+ (1,0) [0|255] "" XXX
|
||||
|
||||
BO_ 625 DAS_5: 8 XXX
|
||||
SG_ FCW_STATE : 2|1@1+ (1,0) [0|0] "" XXX
|
||||
SG_ FCW_DISTANCE : 3|2@1+ (1,0) [0|0] "" XXX
|
||||
SG_ ACCFCW_MESSAGE : 12|4@1+ (1,0) [0|0] "" XXX
|
||||
SG_ SET_SPEED_KPH : 24|8@1+ (1,0) [0|250] "km/h" XXX
|
||||
SG_ WHEEL_TORQUE_REQUEST : 38|15@0+ (1,-7767) [-7767|24999] "Nm" XXX
|
||||
SG_ WHEEL_TORQUE_REQUEST_ACTIVE : 39|1@1+ (1,0) [0|0] "" XXX
|
||||
SG_ COUNTER : 55|4@0+ (1,0) [0|15] "" XXX
|
||||
SG_ CHECKSUM : 63|8@0+ (1,0) [0|255] "" XXX
|
||||
|
||||
BO_ 678 DAS_6: 8 XXX
|
||||
SG_ LKAS_ICON_COLOR : 1|2@0+ (1,0) [0|3] "" XXX
|
||||
SG_ LKAS_LANE_LINES : 19|4@0+ (1,0) [0|1] "" XXX
|
||||
SG_ LKAS_ALERTS : 27|4@0+ (1,0) [0|1] "" XXX
|
||||
SG_ CAR_MODEL : 15|8@0+ (1,0) [0|255] "" XXX
|
||||
SG_ AUTO_HIGH_BEAM_ON : 47|1@1+ (1,0) [0|0] "" XXX
|
||||
SG_ LKAS_DISABLED : 56|1@1+ (1,0) [0|0] "" XXX
|
||||
|
||||
BO_ 720 BSM_1: 6 XXX
|
||||
SG_ RIGHT_STATUS : 5|1@0+ (1,0) [0|1] "" XXX
|
||||
@@ -278,12 +288,6 @@ BO_ 324 SPEED_2: 8 XXX
|
||||
BO_ 832 UNKNOWN_340: 8 XXX
|
||||
SG_ SPEED_DIGITAL : 63|8@0+ (1,0) [0|255] "mph" XXX
|
||||
|
||||
BO_ 625 ACC_1: 8 XXX
|
||||
SG_ SPEED : 31|8@0+ (1,0) [0|255] "km/h" XXX
|
||||
SG_ ACCEL_PERHAPS : 39|16@0+ (1,0) [0|255] "" XXX
|
||||
SG_ CHECKSUM : 63|8@0+ (1,0) [0|255] "" XXX
|
||||
SG_ COUNTER : 55|4@0+ (1,0) [0|15] "" XXX
|
||||
|
||||
BO_ 268 ACC_10c: 8 XXX
|
||||
SG_ BRAKE_PERHAPS : 48|1@0+ (1,0) [0|3] "" XXX
|
||||
SG_ COUNTER : 55|4@0+ (1,0) [0|15] "" XXX
|
||||
|
||||
@@ -61,11 +61,9 @@ BO_ 153 DAS_3: 8 XXX
|
||||
SG_ DISABLE_FUEL_SHUTOFF : 23|1@1+ (1,0) [0|0] "" XXX
|
||||
SG_ GR_MAX_REQ : 32|4@1+ (1,0) [0|0] "" XXX
|
||||
SG_ ACC_DECEL_REQ : 36|3@1+ (1,0) [0|0] "" XXX
|
||||
SG_ STS : 46|2@1+ (1,0) [0|0] "" XXX
|
||||
SG_ ACC_FAULTED : 47|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ ACC_FAULTED : 46|2@1+ (1,0) [0|0] "" XXX
|
||||
SG_ COLLISION_BRK_PREP : 48|1@1+ (1,0) [0|0] "" XXX
|
||||
SG_ ACC_BRK_PREP : 49|1@1+ (1,0) [0|0] "" XXX
|
||||
SG_ BRAKE_BOOL_1 : 36|1@0+ (1,0) [0|3] "" XXX
|
||||
SG_ COUNTER : 55|4@0+ (1,0) [0|15] "" XXX
|
||||
SG_ CHECKSUM : 63|8@0+ (1,0) [0|255] "" XXX
|
||||
|
||||
@@ -76,6 +74,7 @@ BO_ 232 DAS_4: 8 XXX
|
||||
SG_ ACC_DISTANCE_CONFIG_2 : 41|2@0+ (1,0) [0|3] "" XXX
|
||||
SG_ SPEED_DIGITAL : 63|8@0+ (1,0) [0|255] "mph" XXX
|
||||
SG_ ACC_STATE : 38|3@0+ (1,0) [0|7] "" XXX
|
||||
SG_ FCW_BRAKE_ENABLED : 29|1@0+ (1,0) [0|1] "" XXX
|
||||
|
||||
BO_ 49 EPS_2: 8 XXX
|
||||
SG_ LKAS_STATE : 23|4@0+ (1,0) [0|15] "" XXX
|
||||
@@ -105,12 +104,23 @@ BO_ 177 CRUISE_BUTTONS: 3 XXX
|
||||
SG_ COUNTER : 15|4@0+ (1,0) [0|15] "" XXX
|
||||
SG_ CHECKSUM : 23|8@0+ (1,0) [0|255] "" XXX
|
||||
|
||||
BO_ 163 DAS_5: 8 XXX
|
||||
SG_ FCW_STATE : 2|1@1+ (1,0) [0|0] "" XXX
|
||||
SG_ FCW_DISTANCE : 3|2@1+ (1,0) [0|0] "" XXX
|
||||
SG_ ACCFCW_MESSAGE : 12|4@1+ (1,0) [0|0] "" XXX
|
||||
SG_ SET_SPEED_KPH : 24|8@1+ (1,0) [0|250] "km/h" XXX
|
||||
SG_ WHEEL_TORQUE_REQUEST : 38|15@0+ (1,-7767) [-7767|24999] "Nm" XXX
|
||||
SG_ WHEEL_TORQUE_REQUEST_ACTIVE : 39|1@1+ (1,0) [0|0] "" XXX
|
||||
SG_ COUNTER : 55|4@0+ (1,0) [0|15] "" XXX
|
||||
SG_ CHECKSUM : 63|8@0+ (1,0) [0|255] "" XXX
|
||||
|
||||
BO_ 250 DAS_6: 8 XXX
|
||||
SG_ LKAS_ICON_COLOR : 1|2@0+ (1,0) [0|3] "" XXX
|
||||
SG_ LKAS_LANE_LINES : 19|4@0+ (1,0) [0|1] "" XXX
|
||||
SG_ LKAS_ALERTS : 27|4@0+ (1,0) [0|1] "" XXX
|
||||
SG_ CAR_MODEL : 15|8@0+ (1,0) [0|255] "" XXX
|
||||
SG_ AUTO_HIGH_BEAM_ON : 47|1@1+ (1,0) [0|0] "" XXX
|
||||
SG_ LKAS_DISABLED : 56|1@1+ (1,0) [0|0] "" XXX
|
||||
|
||||
BO_ 720 BSM_1: 6 XXX
|
||||
SG_ RIGHT_STATUS : 5|1@0+ (1,0) [0|1] "" XXX
|
||||
|
||||
@@ -61,11 +61,9 @@ BO_ 500 DAS_3: 8 XXX
|
||||
SG_ DISABLE_FUEL_SHUTOFF : 23|1@1+ (1,0) [0|0] "" XXX
|
||||
SG_ GR_MAX_REQ : 32|4@1+ (1,0) [0|0] "" XXX
|
||||
SG_ ACC_DECEL_REQ : 36|3@1+ (1,0) [0|0] "" XXX
|
||||
SG_ STS : 46|2@1+ (1,0) [0|0] "" XXX
|
||||
SG_ ACC_FAULTED : 47|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ ACC_FAULTED : 46|2@1+ (1,0) [0|0] "" XXX
|
||||
SG_ COLLISION_BRK_PREP : 48|1@1+ (1,0) [0|0] "" XXX
|
||||
SG_ ACC_BRK_PREP : 49|1@1+ (1,0) [0|0] "" XXX
|
||||
SG_ BRAKE_BOOL_1 : 36|1@0+ (1,0) [0|3] "" XXX
|
||||
SG_ COUNTER : 55|4@0+ (1,0) [0|15] "" XXX
|
||||
SG_ CHECKSUM : 63|8@0+ (1,0) [0|255] "" XXX
|
||||
|
||||
@@ -76,6 +74,7 @@ BO_ 501 DAS_4: 8 XXX
|
||||
SG_ ACC_DISTANCE_CONFIG_2 : 41|2@0+ (1,0) [0|3] "" XXX
|
||||
SG_ SPEED_DIGITAL : 63|8@0+ (1,0) [0|255] "mph" XXX
|
||||
SG_ ACC_STATE : 38|3@0+ (1,0) [0|7] "" XXX
|
||||
SG_ FCW_BRAKE_ENABLED : 29|1@0+ (1,0) [0|1] "" XXX
|
||||
|
||||
BO_ 544 EPS_2: 8 XXX
|
||||
SG_ LKAS_STATE : 23|4@0+ (1,0) [0|15] "" XXX
|
||||
@@ -105,12 +104,23 @@ BO_ 570 CRUISE_BUTTONS: 3 XXX
|
||||
SG_ COUNTER : 15|4@0+ (1,0) [0|15] "" XXX
|
||||
SG_ CHECKSUM : 23|8@0+ (1,0) [0|255] "" XXX
|
||||
|
||||
BO_ 625 DAS_5: 8 XXX
|
||||
SG_ FCW_STATE : 2|1@1+ (1,0) [0|0] "" XXX
|
||||
SG_ FCW_DISTANCE : 3|2@1+ (1,0) [0|0] "" XXX
|
||||
SG_ ACCFCW_MESSAGE : 12|4@1+ (1,0) [0|0] "" XXX
|
||||
SG_ SET_SPEED_KPH : 24|8@1+ (1,0) [0|250] "km/h" XXX
|
||||
SG_ WHEEL_TORQUE_REQUEST : 38|15@0+ (1,-7767) [-7767|24999] "Nm" XXX
|
||||
SG_ WHEEL_TORQUE_REQUEST_ACTIVE : 39|1@1+ (1,0) [0|0] "" XXX
|
||||
SG_ COUNTER : 55|4@0+ (1,0) [0|15] "" XXX
|
||||
SG_ CHECKSUM : 63|8@0+ (1,0) [0|255] "" XXX
|
||||
|
||||
BO_ 629 DAS_6: 8 XXX
|
||||
SG_ LKAS_ICON_COLOR : 1|2@0+ (1,0) [0|3] "" XXX
|
||||
SG_ LKAS_LANE_LINES : 19|4@0+ (1,0) [0|1] "" XXX
|
||||
SG_ LKAS_ALERTS : 27|4@0+ (1,0) [0|1] "" XXX
|
||||
SG_ CAR_MODEL : 15|8@0+ (1,0) [0|255] "" XXX
|
||||
SG_ AUTO_HIGH_BEAM_ON : 47|1@1+ (1,0) [0|0] "" XXX
|
||||
SG_ LKAS_DISABLED : 56|1@1+ (1,0) [0|0] "" XXX
|
||||
|
||||
BO_ 720 BSM_1: 6 XXX
|
||||
SG_ RIGHT_STATUS : 5|1@0+ (1,0) [0|1] "" XXX
|
||||
|
||||
@@ -374,7 +374,6 @@ BO_ 1302 ODOMETER: 8 XXX
|
||||
SG_ CHECKSUM : 59|4@0+ (1,0) [0|3] "" EON
|
||||
|
||||
CM_ SG_ 401 GEAR "10 = reverse, 11 = transition";
|
||||
CM_ SG_ 450 EPB_STATE "3 \"engaged\" 2 \"disengaging\" 1 \"engaging\" 0 \"disengaged\"";
|
||||
CM_ SG_ 806 REVERSE_LIGHT "Might be reverse gear selected and not the lights";
|
||||
|
||||
VAL_ 399 STEER_STATUS 7 "permanent_fault" 6 "tmp_fault" 5 "fault_1" 4 "no_torque_alert_2" 3 "low_speed_lockout" 2 "no_torque_alert_1" 0 "normal" ;
|
||||
|
||||
@@ -109,6 +109,10 @@ BO_ 1265 CLU11: 4 CLU
|
||||
SG_ CF_Clu_AmpInfo : 25|1@1+ (1.0,0.0) [0.0|1.0] "" BCM
|
||||
SG_ CF_Clu_AliveCnt1 : 28|4@1+ (1.0,0.0) [0.0|15.0] "" AHLS,EMS,EPB,LDWS_LKAS,MDPS,SCC
|
||||
|
||||
BO_ 1260 Sign_Detection: 8 XXX
|
||||
SG_ SpeedLim_Nav_Cam : 40|8@1+ (1,0) [0|255] "km/h / mph" XXX
|
||||
SG_ SpeedLim_Nav_Cam2 : 48|8@1+ (1,0) [0|255] "km/h / mph" XXX
|
||||
|
||||
BO_ 1492 TMU_GW_PE_01: 8 CLU
|
||||
SG_ TMU_IVRActivity : 0|2@1+ (1.0,0.0) [0.0|3.0] "" DATC
|
||||
SG_ TMU_PhoneActivity : 2|2@1+ (1.0,0.0) [0.0|3.0] "" DATC
|
||||
|
||||
@@ -570,7 +570,7 @@ BO_ 401 STEERING_LTA: 8 XXX
|
||||
SG_ CHECKSUM : 63|8@0+ (1,0) [0|255] "" XXX
|
||||
SG_ SETME_X3 : 29|2@0+ (1,0) [0|3] "" XXX
|
||||
SG_ PERCENTAGE : 39|8@0+ (1,0) [0|255] "" XXX
|
||||
SG_ SETME_X64 : 47|8@0+ (1,0) [0|255] "" XXX
|
||||
SG_ TORQUE_WIND_DOWN : 47|8@0+ (1,0) [0|255] "" XXX
|
||||
SG_ ANGLE : 55|8@0- (0.5,0) [0|255] "" XXX
|
||||
SG_ STEER_ANGLE_CMD : 15|16@0- (0.0573,0) [-540|540] "" XXX
|
||||
SG_ STEER_REQUEST_2 : 25|1@0+ (1,0) [0|1] "" XXX
|
||||
@@ -611,7 +611,7 @@ BO_ 1014 BSM: 8 XXX
|
||||
SG_ APPROACHING_ENABLED : 15|1@0+ (1,0) [0|1] "" XXX
|
||||
|
||||
CM_ SG_ 401 PERCENTAGE "driver override percentage (0-100), very close to steeringPressed in OP";
|
||||
CM_ SG_ 401 SETME_X64 "ramps to 0 smoothly then back on falling edge of STEER_REQUEST if BIT isn't 1";
|
||||
CM_ SG_ 401 TORQUE_WIND_DOWN "used to wind down torque on user override";
|
||||
CM_ SG_ 401 ANGLE "angle of car relative to lane center on LTA camera";
|
||||
CM_ SG_ 401 STEER_ANGLE_CMD "desired angle, OEM steers up to 95 degrees, no angle limit but torque will bottom out";
|
||||
CM_ SG_ 401 CLEAR_HOLD_STEERING_ALERT "set to 1 when user clears LKAS_HUD->LDA_ALERT ('Hold Steering') by applying torque to steering wheel";
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
Programming
|
||||
----
|
||||
|
||||
**Panda**
|
||||
## Programming
|
||||
|
||||
```
|
||||
./flash.py # flash application
|
||||
./recover.py # flash bootstub
|
||||
```
|
||||
|
||||
## Debugging
|
||||
|
||||
To print out the serial console from the STM32, run `tests/debug_console.py`
|
||||
|
||||
Troubleshooting
|
||||
----
|
||||
|
||||
@@ -16,5 +17,4 @@ If panda is blinking fast with green LED, use `flash.py`.
|
||||
|
||||
Otherwise if LED is off and panda can't be seen with `lsusb` command, use [panda paw](https://comma.ai/shop/products/panda-paw) to go into DFU mode.
|
||||
|
||||
|
||||
If your device has an internal panda and none of the above works, try running `../tests/reflash_internal_panda.py`.
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1 +1 @@
|
||||
const uint8_t gitversion[] = "DEV-bcce255c-DEBUG";
|
||||
const uint8_t gitversion[] = "DEV-4ba36d72-DEBUG";
|
||||
|
||||
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user