IQ.Pilot Release Commit @ 3807439

This commit is contained in:
IQ.Lvbs CI [bot]
2026-07-21 19:07:37 -05:00
parent ccb06b3624
commit 0952a162ef
98 changed files with 6193 additions and 1808 deletions
+1
View File
@@ -0,0 +1 @@
Wen
-99
View File
@@ -1,102 +1,3 @@
IQ.Lvbs License v0.1a
Copyright (c) 2026 IQ.Lvbs LLC, a part of Project Teal Lvbs Inc. All Rights Reserved.
DEFINITIONS
"Software" refers to IQ.Pilot, konn3kt, and all associated source code,
documentation, and assets owned by the Copyright Holder.
"Open Components" refers to portions of the Software explicitly marked as
open source.
"Proprietary Components" refers to all portions of the Software not made
available to the public in source form.
"Copyright Holder" refers to IQ.Lvbs LLC, a part of Project Teal Lvbs Inc.
GRANT OF LICENSE
Subject to the terms of this license, you are granted a limited,
non-exclusive, revocable license to:
1. View, study, and learn from the Open Components
2. Modify the Open Components for personal, internal, or open-source public use
3. Run the Software for personal, non-commercial purposes
RESTRICTIONS
You may NOT:
1. Claim ownership of any part of the Software, excluding your own
modifications that do not incorporate Proprietary Components.
2. Reverse engineer, decompile, disassemble, or in any way attempt to
circumvent the obfuscation of the Proprietary Components.
3. Use the Software or any derivative for commercial purposes without
explicit written permission from the Copyright Holder.
4. Remove or alter any copyright notices or this license.
5. Sublicense, sell, or transfer rights to the Software.
6. Use the Software and/or its source code to compete with or create a
substantially similar product.
7. Use the Software in closed source software not licensed by IQ.Lvbs LLC.
CONSEQUENCES OF VIOLATION
In the event any Restriction is violated, any product created using inspiration from, or source code from, IQ.Pilot or Konn3kt shall be subject to a licensing fee determined solely by the Copyright Holder. Additionally, the violating party hereby grants IQ.Lvbs LLC an exclusive, irrevocable, worldwide, royalty-free license to use any and all assets from the infringing product on IQ.Lvbs webpages, advertising materials, and in any other manner IQ.Lvbs sees fit.
OWNERSHIP
All rights, title, and interest in the Software remain exclusively with the
Copyright Holder. Any modifications, improvements, or derivative works you
create based on the Software are owned by the Copyright Holder. By
contributing modifications, you irrevocably assign all rights to the
Copyright Holder.
PROPRIETARY COMPONENTS
The Proprietary Components are provided in binary or obfuscated form only.
Reverse engineering, decompilation, or disassembly of Proprietary Components
is strictly prohibited. Violation of this provision entitles IQ.Lvbs LLC to
pursue all available legal remedies to protect its intellectual property and
trade secrets.
NO WARRANTY
THE SOFTWARE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND. THE COPYRIGHT
HOLDER DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT
LIMITED TO MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND
NON-INFRINGEMENT.
LIMITATION OF LIABILITY
IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES, OR
OTHER LIABILITY ARISING FROM THE USE OF THE SOFTWARE. THE USER ACCEPTS FULL
RESPONSIBILITY FOR ANY AND ALL LIABILITIES WHEN USING IQ.LVBS SOFTWARE.
TERMINATION
This license terminates automatically if you violate any of its terms. Upon
termination, you must destroy all copies of the Software in your possession.
The Copyright Holder reserves the right to revoke this license at any time
for any reason.
GOVERNING LAW
This license shall be governed by the laws of the State of Illinois, United
States of America. Any disputes arising under this license shall be subject
to the exclusive jurisdiction of the courts located in Henry County, Illinois.
---
For commercial licensing inquiries, contact: support@iqlvbs.com
Copyright (c) 2020, Comma.ai, Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
-8
View File
@@ -96,11 +96,3 @@ to the exclusive jurisdiction of the courts located in Henry County, Illinois.
---
For commercial licensing inquiries, contact: support@iqlvbs.com
Copyright (c) 2020, Comma.ai, Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+3
View File
@@ -0,0 +1,3 @@
include iqdbc/car/car.capnp
include iqdbc/car/include/c++.capnp
recursive-include iqdbc/safety *.h
-100
View File
@@ -1,100 +0,0 @@
## IQ.Pilot Car-Port Credits
This file is intended to track per-platform and per-tuning attribution inside the car-port-side included inside of IQ.Pilot.
It does not replace the repository-wide license files for IQ.Pilot itself or any other components not listed here.
## Credits:
### Hyundai / Kia / Genesis (HKG) - Sunnypilot
#### Contributors
Full credit for the Hyundai / Kia / Genesis port, along with the HKG-specific tuning work used in this tree, belongs to:
- Jason Wen
- James Vecellio-Grant
- sunnypilot
#### Attribution
This repository carries HKG-side tuning and port lineage that originated from, and/or is verbatim their work. They are the authors of the Sunnypilot-side HKG port and its tuning, and are accredited as such, IQ.Lvbs claims no copyright, credit, or authorship of the components listed below.
#### Upstream License Notice
The HKG work attributed above is acknowledged here under LICENSE notice:
```text
MIT License
Copyright (c) 2024 Jason Wen, James Vecellio-Grant, Sunnypilot, and a number of other contributors.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
```
#### Scope
This licensing is limited to `iqdbc_repo/iqdbc/car/hyundai` and `iqdbc_repo/iqdbc/iqpilot/car/hyundai`.
### Hyundai / Kia / Genesis (HKG) - carrotpilot
#### Contributors
Full credit for the carrotpilot-side HKG tuning lineage carried in this tree belongs to:
- carrotpilot devs (ajoutam)
#### Attribution
This repository carries HKG-side tuning and port lineage that originated from, and/or is verbatim their work. They are the authors of the carrotpilot-side HKG tuning credited here, and are accredited as such, IQ.Lvbs claims no copyright, credit, or authorship of the components listed below.
#### Upstream License Notice
The HKG work attributed above is acknowledged here under LICENSE notice:
```text
Copyright (c) 2018, Comma.ai, Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
```
#### Scope
This licensing is limited to `iqdbc_repo/iqdbc/car/hyundai` and `iqdbc_repo/iqdbc/iqpilot/car/hyundai`.
### Honda
#### Contributors
Full credit for the Honda-specific tuning lineage carried in this tree belongs to:
- MVL-Boston on GitHub
- mvl3c on Discord
#### Attribution
This repository carries Honda tuning work derived from MVL's tuning effort's, that originated from, and/or is verbatim their work, and are accredited as such, IQ.Lvbs claims no copyright, credit, or authorship of the components listed below.
#### Scope
This licensing is limited to `iqdbc_repo/iqdbc/car/honda` and `iqdbc_repo/iqdbc/iqpilot/car/honda`.
+441
View File
@@ -0,0 +1,441 @@
<!--- AUTOGENERATED FROM selfdrive/car/CARS_template.md, DO NOT EDIT. --->
# Support Information for 410 Known Cars
|Make|Model|Package|Support Level|
|---|---|---|:---:|
|Acura|ADX 2025-26|All|[Community](#community)|
|Acura|ILX 2016-18|Technology Plus Package or AcuraWatch Plus|[Upstream](#upstream)|
|Acura|ILX 2019|All|[Upstream](#upstream)|
|Acura|Integra 2023-25|All|[Community](#community)|
|Acura|MDX 2015-16|Advance Package|[Community](#community)|
|Acura|MDX 2017-20|All|[Community](#community)|
|Acura|MDX 2022-24|All|[Community](#community)|
|Acura|MDX 2025-26|All except Type S|[Upstream](#upstream)|
|Acura|MDX Hybrid 2017-20|All|[Community](#community)|
|Acura|RDX 2016-18|AcuraWatch Plus or Advance Package|[Upstream](#upstream)|
|Acura|RDX 2019-21|All|[Upstream](#upstream)|
|Acura|RDX 2022-25|All|[Community](#community)|
|Acura|RLX 2017|Advance Package or Technology Package|[Community](#community)|
|Acura|TLX 2015-17|Advance Package|[Community](#community)|
|Acura|TLX 2018-20|All|[Community](#community)|
|Acura|TLX 2021|All|[Upstream](#upstream)|
|Acura|TLX 2022-23|All|[Community](#community)|
|Acura|TLX 2025|All|[Upstream](#upstream)|
|Acura|ZDX 2024|All|[Not compatible](#can-bus-security)|
|Audi|A3 2014-19|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)|
|Audi|A3 Sportback e-tron 2017-18|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)|
|Audi|A4 2016-24|All|[Not compatible](#flexray)|
|Audi|A5 2016-24|All|[Not compatible](#flexray)|
|Audi|Q2 2018|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)|
|Audi|Q3 2019-24|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)|
|Audi|Q5 2017-24|All|[Not compatible](#flexray)|
|Audi|RS3 2018|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)|
|Audi|S3 2015-17|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)|
|Cadillac|CT6 Non-ACC 2017-18|No Adaptive Cruise Control (Non-ACC)|[Dashcam mode](#dashcam)|
|Cadillac|XT5 Non-ACC 2018|No Adaptive Cruise Control (Non-ACC)|[Dashcam mode](#dashcam)|
|Chevrolet|Bolt EUV 2022-23|Premier or Premier Redline Trim, without Super Cruise Package|[Upstream](#upstream)|
|Chevrolet|Bolt EUV LT Non-ACC 2022-23|No Adaptive Cruise Control (Non-ACC)|[Dashcam mode](#dashcam)|
|Chevrolet|Bolt EV 2022-23|2LT Trim with Adaptive Cruise Control Package|[Upstream](#upstream)|
|Chevrolet|Bolt EV LT Non-ACC 2022-23|No Adaptive Cruise Control (Non-ACC)|[Dashcam mode](#dashcam)|
|Chevrolet|Bolt EV Non-ACC 2017|No Adaptive Cruise Control (Non-ACC)|[Community](community)|
|Chevrolet|Bolt EV Non-ACC 2018-21|No Adaptive Cruise Control (Non-ACC)|[Community](community)|
|Chevrolet|Equinox 2019-22|Adaptive Cruise Control (ACC)|[Upstream](#upstream)|
|Chevrolet|Equinox Non-ACC 2019-22|No Adaptive Cruise Control (Non-ACC)|[Dashcam mode](#dashcam)|
|Chevrolet|Malibu Non-ACC 2016-23|No Adaptive Cruise Control (Non-ACC)|[Community](community)|
|Chevrolet|Silverado 1500 2020-21|Safety Package II|[Upstream](#upstream)|
|Chevrolet|Suburban Non-ACC 2016-20|No Adaptive Cruise Control (Non-ACC)|[Dashcam mode](#dashcam)|
|Chevrolet|Trailblazer 2021-22|Adaptive Cruise Control (ACC)|[Upstream](#upstream)|
|Chevrolet|Trailblazer Non-ACC 2021-22|No Adaptive Cruise Control (Non-ACC)|[Dashcam mode](#dashcam)|
|Chrysler|Pacifica 2017-18|Adaptive Cruise Control (ACC)|[Upstream](#upstream)|
|Chrysler|Pacifica 2019-20|Adaptive Cruise Control (ACC)|[Upstream](#upstream)|
|Chrysler|Pacifica 2021-23|All|[Upstream](#upstream)|
|Chrysler|Pacifica Hybrid 2017-18|Adaptive Cruise Control (ACC)|[Upstream](#upstream)|
|Chrysler|Pacifica Hybrid 2019-25|Adaptive Cruise Control (ACC)|[Upstream](#upstream)|
|comma|body|All|[Upstream](#upstream)|
|CUPRA|Ateca 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)|
|Dodge|Durango 2020-21|Adaptive Cruise Control (ACC)|[Upstream](#upstream)|
|Ford|Bronco Sport 2021-24|Co-Pilot360 Assist+|[Upstream](#upstream)|
|Ford|Escape 2020-22|Co-Pilot360 Assist+|[Upstream](#upstream)|
|Ford|Escape 2023-24|Co-Pilot360 Assist+|[Upstream](#upstream)|
|Ford|Escape Hybrid 2020-22|Co-Pilot360 Assist+|[Upstream](#upstream)|
|Ford|Escape Hybrid 2023-24|Co-Pilot360 Assist+|[Upstream](#upstream)|
|Ford|Escape Plug-in Hybrid 2020-22|Co-Pilot360 Assist+|[Upstream](#upstream)|
|Ford|Escape Plug-in Hybrid 2023-24|Co-Pilot360 Assist+|[Upstream](#upstream)|
|Ford|Expedition 2022-24|Co-Pilot360 Assist 2.0|[Upstream](#upstream)|
|Ford|Explorer 2020-24|Co-Pilot360 Assist+|[Upstream](#upstream)|
|Ford|Explorer Hybrid 2020-24|Co-Pilot360 Assist+|[Upstream](#upstream)|
|Ford|F-150 2021-23|Co-Pilot360 Assist 2.0|[Upstream](#upstream)|
|Ford|F-150 Hybrid 2021-23|Co-Pilot360 Assist 2.0|[Upstream](#upstream)|
|Ford|Focus 2018|Adaptive Cruise Control with Lane Centering|[Upstream](#upstream)|
|Ford|Focus Hybrid 2018|Adaptive Cruise Control with Lane Centering|[Upstream](#upstream)|
|Ford|Kuga 2020-23|Adaptive Cruise Control with Lane Centering|[Upstream](#upstream)|
|Ford|Kuga Hybrid 2020-23|Adaptive Cruise Control with Lane Centering|[Upstream](#upstream)|
|Ford|Kuga Hybrid 2024|All|[Upstream](#upstream)|
|Ford|Kuga Plug-in Hybrid 2020-23|Adaptive Cruise Control with Lane Centering|[Upstream](#upstream)|
|Ford|Kuga Plug-in Hybrid 2024|All|[Upstream](#upstream)|
|Ford|Maverick 2022|LARIAT Luxury|[Upstream](#upstream)|
|Ford|Maverick 2023-24|Co-Pilot360 Assist|[Upstream](#upstream)|
|Ford|Maverick Hybrid 2022|LARIAT Luxury|[Upstream](#upstream)|
|Ford|Maverick Hybrid 2023-24|Co-Pilot360 Assist|[Upstream](#upstream)|
|Ford|Mustang Mach-E 2021-24|All|[Upstream](#upstream)|
|Ford|Ranger 2024|Adaptive Cruise Control with Lane Centering|[Upstream](#upstream)|
|Genesis|G70 2018|All|[Upstream](#upstream)|
|Genesis|G70 2019-21|All|[Upstream](#upstream)|
|Genesis|G70 2022-23|All|[Upstream](#upstream)|
|Genesis|G70 Non-SCC 2021|No Smart Cruise Control (Non-SCC)|[Dashcam mode](#dashcam)|
|Genesis|G80 2017|All|[Upstream](#upstream)|
|Genesis|G80 2018-19|All|[Upstream](#upstream)|
|Genesis|G80 (2.5T Advanced Trim, with HDA II) 2024|Highway Driving Assist II|[Upstream](#upstream)|
|Genesis|G90 2017-20|All|[Upstream](#upstream)|
|Genesis|GV60 (Advanced Trim) 2023|All|[Upstream](#upstream)|
|Genesis|GV60 (Performance Trim) 2022-23|All|[Upstream](#upstream)|
|Genesis|GV70 (2.5T Trim, without HDA II) 2022-24|All|[Upstream](#upstream)|
|Genesis|GV70 (3.5T Trim, without HDA II) 2022-23|All|[Upstream](#upstream)|
|Genesis|GV70 Electrified (Australia Only) 2022|All|[Upstream](#upstream)|
|Genesis|GV70 Electrified (with HDA II) 2023-24|Highway Driving Assist II|[Upstream](#upstream)|
|Genesis|GV80 2023|All|[Upstream](#upstream)|
|GMC|Sierra 1500 2020-21|Driver Alert Package II|[Upstream](#upstream)|
|GMC|Yukon 2019-20|Adaptive Cruise Control (ACC) & LKAS|[Dashcam mode](#dashcam)|
|Honda|Accord 2016-17|Honda Sensing|[Community](#community)|
|Honda|Accord 2018-22|All|[Upstream](#upstream)|
|Honda|Accord 2023-25|All|[Upstream](#upstream)|
|Honda|Accord Hybrid 2017|All|[Community](#community)|
|Honda|Accord Hybrid 2018-22|All|[Upstream](#upstream)|
|Honda|Accord Hybrid 2023-25|All|[Upstream](#upstream)|
|Honda|City (Brazil only) 2023|All|[Upstream](#upstream)|
|Honda|Civic 2016-18|Honda Sensing|[Upstream](#upstream)|
|Honda|Civic 2019-21|All|[Upstream](#upstream)|
|Honda|Civic 2022-24|All|[Upstream](#upstream)|
|Honda|Civic Hatchback 2017-18|Honda Sensing|[Upstream](#upstream)|
|Honda|Civic Hatchback 2019-21|All|[Upstream](#upstream)|
|Honda|Civic Hatchback 2022-24|All|[Upstream](#upstream)|
|Honda|Civic Hatchback Hybrid 2025-26|All|[Upstream](#upstream)|
|Honda|Civic Hatchback Hybrid (Europe only) 2023|All|[Upstream](#upstream)|
|Honda|Civic Hybrid 2025-26|All|[Upstream](#upstream)|
|Honda|Clarity 2018-21|Honda Sensing|[Community](community)|
|Honda|Clarity 2018-21|All|[Community](#community)|
|Honda|CR-V 2015-16|Touring Trim|[Upstream](#upstream)|
|Honda|CR-V 2017-22|Honda Sensing|[Upstream](#upstream)|
|Honda|CR-V 2023-26|All|[Upstream](#upstream)|
|Honda|CR-V Hybrid 2017-22|Honda Sensing|[Upstream](#upstream)|
|Honda|CR-V Hybrid 2023-26|All|[Upstream](#upstream)|
|Honda|e 2020|All|[Upstream](#upstream)|
|Honda|Fit 2018-20|Honda Sensing|[Upstream](#upstream)|
|Honda|Freed 2020|Honda Sensing|[Upstream](#upstream)|
|Honda|HR-V 2019-22|Honda Sensing|[Upstream](#upstream)|
|Honda|HR-V 2023-25|All|[Upstream](#upstream)|
|Honda|Insight 2019-22|All|[Upstream](#upstream)|
|Honda|Inspire 2018|All|[Upstream](#upstream)|
|Honda|N-Box 2018|All|[Upstream](#upstream)|
|Honda|Odyssey 2018-20|Honda Sensing|[Upstream](#upstream)|
|Honda|Odyssey 2021-26|All|[Upstream](#upstream)|
|Honda|Odyssey (Taiwan) 2018-19|Honda Sensing|[Upstream](#upstream)|
|Honda|Passport 2019-25|All|[Upstream](#upstream)|
|Honda|Passport 2026|All|[Upstream](#upstream)|
|Honda|Pilot 2016-22|Honda Sensing|[Upstream](#upstream)|
|Honda|Pilot 2023-25|All|[Upstream](#upstream)|
|Honda|Prologue 2024-25|All|[Not compatible](#can-bus-security)|
|Honda|Ridgeline 2017-25|Honda Sensing|[Upstream](#upstream)|
|Hyundai|Azera 2022|All|[Upstream](#upstream)|
|Hyundai|Azera Hybrid 2019|All|[Upstream](#upstream)|
|Hyundai|Azera Hybrid 2020|All|[Upstream](#upstream)|
|Hyundai|Bayon Non-SCC 2021|No Smart Cruise Control (Non-SCC)|[Dashcam mode](#dashcam)|
|Hyundai|Custin 2023|All|[Upstream](#upstream)|
|Hyundai|Elantra 2017-18|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Hyundai|Elantra 2019|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Hyundai|Elantra 2021-23|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Hyundai|Elantra GT 2017-20|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Hyundai|Elantra Hybrid 2021-23|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Hyundai|Elantra Non-SCC 2022|No Smart Cruise Control (Non-SCC)|[Community](community)|
|Hyundai|Genesis 2015-16|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Hyundai|i30 2017-19|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Hyundai|Ioniq 5 (Southeast Asia and Europe only) 2022-24|All|[Upstream](#upstream)|
|Hyundai|Ioniq 5 (with HDA II) 2022-24|Highway Driving Assist II|[Upstream](#upstream)|
|Hyundai|Ioniq 5 (without HDA II) 2022-24|Highway Driving Assist|[Upstream](#upstream)|
|Hyundai|Ioniq 6 (with HDA II) 2023-24|Highway Driving Assist II|[Upstream](#upstream)|
|Hyundai|Ioniq Electric 2019|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Hyundai|Ioniq Electric 2020|All|[Upstream](#upstream)|
|Hyundai|Ioniq Hybrid 2017-19|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Hyundai|Ioniq Hybrid 2020-22|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Hyundai|Ioniq Plug-in Hybrid 2019|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Hyundai|Ioniq Plug-in Hybrid 2020-22|All|[Upstream](#upstream)|
|Hyundai|Kona 2020|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Hyundai|Kona 2022-23|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Hyundai|Kona Electric 2018-21|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Hyundai|Kona Electric 2022-23|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Hyundai|Kona Electric (with HDA II, Korea only) 2023|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Hyundai|Kona Electric Non-SCC 2019|No Smart Cruise Control (Non-SCC)|[Community](community)|
|Hyundai|Kona Hybrid 2020|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Hyundai|Kona Non-SCC 2019|No Smart Cruise Control (Non-SCC)|[Community](community)|
|Hyundai|Nexo 2021|All|[Upstream](#upstream)|
|Hyundai|Palisade 2020-22|All|[Upstream](#upstream)|
|Hyundai|Palisade 2023-24|Highway Driving Assist II|[Community](#community)|
|Hyundai|Santa Cruz 2022-24|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Hyundai|Santa Fe 2019-20|All|[Upstream](#upstream)|
|Hyundai|Santa Fe 2021-23|All|[Upstream](#upstream)|
|Hyundai|Santa Fe Hybrid 2022-23|All|[Upstream](#upstream)|
|Hyundai|Santa Fe Plug-in Hybrid 2022-23|All|[Upstream](#upstream)|
|Hyundai|Sonata 2018-19|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Hyundai|Sonata 2020-23|All|[Upstream](#upstream)|
|Hyundai|Sonata Hybrid 2020-23|All|[Upstream](#upstream)|
|Hyundai|Staria 2023|All|[Upstream](#upstream)|
|Hyundai|Tucson 2021|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Hyundai|Tucson 2022|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Hyundai|Tucson 2023-24|All|[Upstream](#upstream)|
|Hyundai|Tucson Diesel 2019|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Hyundai|Tucson Hybrid 2022-24|All|[Upstream](#upstream)|
|Hyundai|Tucson Plug-in Hybrid 2024|All|[Upstream](#upstream)|
|Hyundai|Veloster 2019-20|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Jeep|Grand Cherokee 2016-18|Adaptive Cruise Control (ACC)|[Upstream](#upstream)|
|Jeep|Grand Cherokee 2019-21|Adaptive Cruise Control (ACC)|[Upstream](#upstream)|
|Kia|Carnival 2022-24|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Kia|Carnival (China only) 2023|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Kia|Ceed 2019-21|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Kia|Ceed Plug-in Hybrid Non-SCC 2022|No Smart Cruise Control (Non-SCC)|[Community](community)|
|Kia|EV6 (Southeast Asia only) 2022-24|All|[Upstream](#upstream)|
|Kia|EV6 (with HDA II) 2022-24|Highway Driving Assist II|[Upstream](#upstream)|
|Kia|EV6 (without HDA II) 2022-24|Highway Driving Assist|[Upstream](#upstream)|
|Kia|Forte 2019-21|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Kia|Forte 2022-23|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Kia|Forte Non-SCC 2019|No Smart Cruise Control (Non-SCC)|[Community](community)|
|Kia|Forte Non-SCC 2021|No Smart Cruise Control (Non-SCC)|[Dashcam mode](#dashcam)|
|Kia|K5 2021-24|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Kia|K5 Hybrid 2020-22|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Kia|K8 Hybrid (with HDA II) 2023|Highway Driving Assist II|[Upstream](#upstream)|
|Kia|Niro EV 2019|All|[Upstream](#upstream)|
|Kia|Niro EV 2020|All|[Upstream](#upstream)|
|Kia|Niro EV 2021|All|[Upstream](#upstream)|
|Kia|Niro EV 2022|All|[Upstream](#upstream)|
|Kia|Niro EV (with HDA II) 2025|Highway Driving Assist II|[Upstream](#upstream)|
|Kia|Niro EV (without HDA II) 2023-25|All|[Upstream](#upstream)|
|Kia|Niro Hybrid 2018|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Kia|Niro Hybrid 2021|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Kia|Niro Hybrid 2022|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Kia|Niro Hybrid 2023|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Kia|Niro Plug-in Hybrid 2018-19|All|[Upstream](#upstream)|
|Kia|Niro Plug-in Hybrid 2020|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Kia|Niro Plug-in Hybrid 2021|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Kia|Niro Plug-in Hybrid 2022|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Kia|Optima 2017|Advanced Smart Cruise Control|[Upstream](#upstream)|
|Kia|Optima 2019-20|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Kia|Optima Hybrid 2017|Advanced Smart Cruise Control|[Dashcam mode](#dashcam)|
|Kia|Optima Hybrid 2019|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Kia|Seltos 2021|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Kia|Seltos Non-SCC 2023-24|No Smart Cruise Control (Non-SCC)|[Dashcam mode](#dashcam)|
|Kia|Sorento 2018|Advanced Smart Cruise Control & LKAS|[Upstream](#upstream)|
|Kia|Sorento 2019|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Kia|Sorento 2021-23|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Kia|Sorento Hybrid 2021-23|All|[Upstream](#upstream)|
|Kia|Sorento Plug-in Hybrid 2022-23|All|[Upstream](#upstream)|
|Kia|Sportage 2023-24|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Kia|Sportage Hybrid 2023|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Kia|Stinger 2018-20|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Kia|Stinger 2022-23|All|[Upstream](#upstream)|
|Kia|Telluride 2020-22|All|[Upstream](#upstream)|
|Kia|Telluride 2023-24|Highway Driving Assist II|[Community](#community)|
|Lexus|CT Hybrid 2017-18|Lexus Safety System+|[Upstream](#upstream)|
|Lexus|ES 2017-18|All|[Upstream](#upstream)|
|Lexus|ES 2019-25|All|[Upstream](#upstream)|
|Lexus|ES Hybrid 2017-18|All|[Upstream](#upstream)|
|Lexus|ES Hybrid 2019-25|All|[Upstream](#upstream)|
|Lexus|GS F 2016|All|[Upstream](#upstream)|
|Lexus|IS 2017-19|All|[Upstream](#upstream)|
|Lexus|IS 2022-24|All|[Upstream](#upstream)|
|Lexus|LC 2024-25|All|[Upstream](#upstream)|
|Lexus|LS 2018|All except Lexus Safety System+ A|[Upstream](#upstream)|
|Lexus|NS 2022-25|All|[Not compatible](#can-bus-security)|
|Lexus|NX 2018-19|All|[Upstream](#upstream)|
|Lexus|NX 2020-21|All|[Upstream](#upstream)|
|Lexus|NX Hybrid 2018-19|All|[Upstream](#upstream)|
|Lexus|NX Hybrid 2020-21|All|[Upstream](#upstream)|
|Lexus|RC 2018-20|All|[Upstream](#upstream)|
|Lexus|RC 2023|All|[Upstream](#upstream)|
|Lexus|RX 2016|Lexus Safety System+|[Upstream](#upstream)|
|Lexus|RX 2017-19|All|[Upstream](#upstream)|
|Lexus|RX 2020-22|All|[Upstream](#upstream)|
|Lexus|RX Hybrid 2016|Lexus Safety System+|[Upstream](#upstream)|
|Lexus|RX Hybrid 2017-19|All|[Upstream](#upstream)|
|Lexus|RX Hybrid 2020-22|All|[Upstream](#upstream)|
|Lexus|UX Hybrid 2019-24|All|[Upstream](#upstream)|
|Lincoln|Aviator 2020-24|Co-Pilot360 Plus|[Upstream](#upstream)|
|Lincoln|Aviator Plug-in Hybrid 2020-24|Co-Pilot360 Plus|[Upstream](#upstream)|
|MAN|eTGE 2020-24|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)|
|MAN|TGE 2017-24|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)|
|Mazda|3 2017-18|All|[Dashcam mode](#dashcam)|
|Mazda|6 2017-20|All|[Dashcam mode](#dashcam)|
|Mazda|CX-5 2017-21|All|[Dashcam mode](#dashcam)|
|Mazda|CX-5 2022-25|All|[Upstream](#upstream)|
|Mazda|CX-9 2016-20|All|[Dashcam mode](#dashcam)|
|Mazda|CX-9 2021-23|All|[Upstream](#upstream)|
|Nissan|Altima 2019-20, 2024|ProPILOT Assist|[Upstream](#upstream)|
|Nissan|Leaf 2018-23|ProPILOT Assist|[Upstream](#upstream)|
|Nissan|Rogue 2018-20|ProPILOT Assist|[Upstream](#upstream)|
|Nissan|X-Trail 2017|ProPILOT Assist|[Upstream](#upstream)|
|Peugeot|208 2019-25|Adaptive Cruise Control (ACC) & Lane Assist|[Dashcam mode](#dashcam)|
|Porsche|Macan 2017-24|Adaptive Cruise Control (ACC) & Lane Assist|[Dashcam mode](#dashcam)|
|Ram|1500 2019-24|Adaptive Cruise Control (ACC)|[Upstream](#upstream)|
|Ram|2500 2020-24|Adaptive Cruise Control (ACC)|[Upstream](#upstream)|
|Ram|3500 2019-22|Adaptive Cruise Control (ACC)|[Upstream](#upstream)|
|Rivian|R1S 2022-24|All|[Upstream](#upstream)|
|Rivian|R1T 2022-24|All|[Upstream](#upstream)|
|SEAT|Alhambra 2018-20|Adaptive Cruise Control (ACC) & Lane Assist|[Dashcam mode](#dashcam)|
|SEAT|Ateca 2016-23|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)|
|SEAT|Leon 2014-20|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)|
|Subaru|Ascent 2019-21|All|[Upstream](#upstream)|
|Subaru|Ascent 2023|All|[Dashcam mode](#dashcam)|
|Subaru|Crosstrek 2018-19|EyeSight Driver Assistance|[Upstream](#upstream)|
|Subaru|Crosstrek 2020-23|EyeSight Driver Assistance|[Upstream](#upstream)|
|Subaru|Crosstrek Hybrid 2020|EyeSight Driver Assistance|[Dashcam mode](#dashcam)|
|Subaru|Forester 2017-18|EyeSight Driver Assistance|[Upstream](#upstream)|
|Subaru|Forester 2019-21|All|[Upstream](#upstream)|
|Subaru|Forester 2022-24|All|[Dashcam mode](#dashcam)|
|Subaru|Forester Hybrid 2020|EyeSight Driver Assistance|[Dashcam mode](#dashcam)|
|Subaru|Impreza 2017-19|EyeSight Driver Assistance|[Upstream](#upstream)|
|Subaru|Impreza 2020-22|EyeSight Driver Assistance|[Upstream](#upstream)|
|Subaru|Legacy 2015-18|EyeSight Driver Assistance|[Upstream](#upstream)|
|Subaru|Legacy 2020-22|All|[Upstream](#upstream)|
|Subaru|Outback 2015-17|EyeSight Driver Assistance|[Upstream](#upstream)|
|Subaru|Outback 2018-19|EyeSight Driver Assistance|[Upstream](#upstream)|
|Subaru|Outback 2020-22|All|[Upstream](#upstream)|
|Subaru|Outback 2023|All|[Dashcam mode](#dashcam)|
|Subaru|Solterra 2023-25|All|[Not compatible](#can-bus-security)|
|Subaru|XV 2018-19|EyeSight Driver Assistance|[Upstream](#upstream)|
|Subaru|XV 2020-21|EyeSight Driver Assistance|[Upstream](#upstream)|
|Škoda|Fabia 2022-23|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)|
|Škoda|Kamiq 2021-23|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)|
|Škoda|Karoq 2019-23|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)|
|Škoda|Kodiaq 2017-23|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)|
|Škoda|Octavia 2015-19|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)|
|Škoda|Octavia RS 2016|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)|
|Škoda|Octavia Scout 2017-19|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)|
|Škoda|Scala 2020-23|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)|
|Škoda|Superb 2015-22|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)|
|Tesla|Model 3 (with HW3) 2019-23|All|[Upstream](#upstream)|
|Tesla|Model 3 (with HW4) 2024-25|All|[Upstream](#upstream)|
|Tesla|Model X (with HW4) 2024|All|[Community](community)|
|Tesla|Model Y (with HW3) 2020-23|All|[Upstream](#upstream)|
|Tesla|Model Y (with HW4) 2024-25|All|[Upstream](#upstream)|
|Toyota|Alphard 2019-20|All|[Upstream](#upstream)|
|Toyota|Alphard Hybrid 2021|All|[Upstream](#upstream)|
|Toyota|Avalon 2016|Toyota Safety Sense P|[Upstream](#upstream)|
|Toyota|Avalon 2017-18|All|[Upstream](#upstream)|
|Toyota|Avalon 2019-21|All|[Upstream](#upstream)|
|Toyota|Avalon 2022|All|[Upstream](#upstream)|
|Toyota|Avalon Hybrid 2019-21|All|[Upstream](#upstream)|
|Toyota|Avalon Hybrid 2022|All|[Upstream](#upstream)|
|Toyota|bZ4x 2023-25|All|[Not compatible](#can-bus-security)|
|Toyota|C-HR 2017-20|All|[Upstream](#upstream)|
|Toyota|C-HR 2021|All|[Upstream](#upstream)|
|Toyota|C-HR Hybrid 2017-20|All|[Upstream](#upstream)|
|Toyota|C-HR Hybrid 2021-22|All|[Upstream](#upstream)|
|Toyota|Camry 2018-20|All|[Upstream](#upstream)|
|Toyota|Camry 2021-24|All|[Upstream](#upstream)|
|Toyota|Camry 2025|All|[Not compatible](#can-bus-security)|
|Toyota|Camry Hybrid 2018-20|All|[Upstream](#upstream)|
|Toyota|Camry Hybrid 2021-24|All|[Upstream](#upstream)|
|Toyota|Corolla 2017-19|All|[Upstream](#upstream)|
|Toyota|Corolla 2020-22|All|[Upstream](#upstream)|
|Toyota|Corolla Cross 2022-25|All|[Not compatible](#can-bus-security)|
|Toyota|Corolla Cross (Non-US only) 2020-23|All|[Upstream](#upstream)|
|Toyota|Corolla Cross Hybrid (Non-US only) 2020-22|All|[Upstream](#upstream)|
|Toyota|Corolla Hatchback 2019-22|All|[Upstream](#upstream)|
|Toyota|Corolla Hybrid 2020-22|All|[Upstream](#upstream)|
|Toyota|Corolla Hybrid (South America only) 2020-23|All|[Upstream](#upstream)|
|Toyota|Highlander 2017-19|All|[Upstream](#upstream)|
|Toyota|Highlander 2020-23|All|[Upstream](#upstream)|
|Toyota|Highlander 2025|All|[Not compatible](#can-bus-security)|
|Toyota|Highlander Hybrid 2017-19|All|[Upstream](#upstream)|
|Toyota|Highlander Hybrid 2020-23|All|[Upstream](#upstream)|
|Toyota|Mirai 2021|All|[Upstream](#upstream)|
|Toyota|Prius 2016|Toyota Safety Sense P|[Upstream](#upstream)|
|Toyota|Prius 2017-20|All|[Upstream](#upstream)|
|Toyota|Prius 2021-22|All|[Upstream](#upstream)|
|Toyota|Prius Prime 2017-20|All|[Upstream](#upstream)|
|Toyota|Prius Prime 2021-22|All|[Upstream](#upstream)|
|Toyota|Prius v 2017|Toyota Safety Sense P|[Upstream](#upstream)|
|Toyota|RAV4 2016|Toyota Safety Sense P|[Upstream](#upstream)|
|Toyota|RAV4 2017-18|All|[Upstream](#upstream)|
|Toyota|RAV4 2019-21|All|[Upstream](#upstream)|
|Toyota|RAV4 2022|All|[Upstream](#upstream)|
|Toyota|RAV4 2023-25|All|[Upstream](#upstream)|
|Toyota|RAV4 Hybrid 2016|Toyota Safety Sense P|[Upstream](#upstream)|
|Toyota|RAV4 Hybrid 2017-18|All|[Upstream](#upstream)|
|Toyota|RAV4 Hybrid 2019-21|All|[Upstream](#upstream)|
|Toyota|RAV4 Hybrid 2022|All|[Upstream](#upstream)|
|Toyota|RAV4 Hybrid 2023-25|All|[Upstream](#upstream)|
|Toyota|RAV4 Prime 2021-23|All|[Custom](#secoc-cars-with-recoverable-keys)|
|Toyota|RAV4 Prime 2024-25|All|[Not compatible](#can-bus-security)|
|Toyota|Sequoia 2023-25|All|[Not compatible](#can-bus-security)|
|Toyota|Sienna 2018-20|All|[Upstream](#upstream)|
|Toyota|Sienna 2021-23|All|[Custom](#secoc-cars-with-recoverable-keys)|
|Toyota|Sienna 2024-25|All|[Not compatible](#can-bus-security)|
|Toyota|Tundra 2022-25|All|[Not compatible](#can-bus-security)|
|Toyota|Venza 2021-25|All|[Not compatible](#can-bus-security)|
|Toyota|Yaris (Non-US only) 2020, 2023|All|[Custom](#secoc-cars-with-recoverable-keys)|
|Volkswagen|Arteon 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)|
|Volkswagen|Arteon eHybrid 2020-23|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)|
|Volkswagen|Arteon R 2020-23|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)|
|Volkswagen|Arteon Shooting Brake 2020-23|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)|
|Volkswagen|Atlas 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)|
|Volkswagen|Atlas Cross Sport 2020-22|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)|
|Volkswagen|Caddy 2019|Adaptive Cruise Control (ACC) & Lane Assist|[Dashcam mode](#dashcam)|
|Volkswagen|Caddy Maxi 2019|Adaptive Cruise Control (ACC) & Lane Assist|[Dashcam mode](#dashcam)|
|Volkswagen|California 2021-23|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)|
|Volkswagen|Caravelle 2020|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)|
|Volkswagen|CC 2018-22|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)|
|Volkswagen|Crafter 2017-24|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)|
|Volkswagen|e-Crafter 2018-24|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)|
|Volkswagen|e-Golf 2014-20|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)|
|Volkswagen|Golf 2015-20|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)|
|Volkswagen|Golf Alltrack 2015-19|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)|
|Volkswagen|Golf GTD 2015-20|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)|
|Volkswagen|Golf GTE 2015-20|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)|
|Volkswagen|Golf GTI 2015-21|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)|
|Volkswagen|Golf R 2015-19|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)|
|Volkswagen|Golf SportsVan 2015-20|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)|
|Volkswagen|Grand California 2019-24|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)|
|Volkswagen|Jetta 2015-18|Adaptive Cruise Control (ACC) & Lane Assist|[Dashcam mode](#dashcam)|
|Volkswagen|Jetta 2019-23|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)|
|Volkswagen|Jetta GLI 2021-23|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)|
|Volkswagen|Passat 2015-22|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)|
|Volkswagen|Passat Alltrack 2015-22|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)|
|Volkswagen|Passat GTE 2015-22|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)|
|Volkswagen|Passat NMS 2017-22|Adaptive Cruise Control (ACC) & Lane Assist|[Dashcam mode](#dashcam)|
|Volkswagen|Polo 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)|
|Volkswagen|Polo GTI 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)|
|Volkswagen|Sharan 2018-22|Adaptive Cruise Control (ACC) & Lane Assist|[Dashcam mode](#dashcam)|
|Volkswagen|T-Cross 2021|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)|
|Volkswagen|T-Roc 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)|
|Volkswagen|Taos 2022-24|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)|
|Volkswagen|Teramont 2018-22|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)|
|Volkswagen|Teramont Cross Sport 2021-22|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)|
|Volkswagen|Teramont X 2021-22|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)|
|Volkswagen|Tiguan 2018-24|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)|
|Volkswagen|Tiguan eHybrid 2021-23|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)|
|Volkswagen|Touran 2016-23|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)|
# Types of Support
**iqdbc can support many more cars than it currently does.** There are a few reasons your car may not be supported.
If your car doesn't fit into any of the incompatibility criteria here, then there's a good chance it can be supported!
We're adding support for new cars all the time. **We don't have a roadmap for car support**, and in fact, most car
support comes from users like you!
## Upstream
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.
## Custom
Vehicles in this category are not considered plug-and-play. Software support is included in upstream openpilot, but
these vehicles might not have a harness in the comma store, or the physical install might be at an unusual or cumbersome
location, or they might need unusual configuration after install. These vehicles will not work with release builds of
openpilot, but depending on the situation, development builds or custom forks may allow their use.
### SecOC cars with recoverable keys
For a small subset of SecOC-protected vehicles, tools may be available in the community to recover the SecOC keys. These
tools, and the recovery process, are not part of openpilot. If supplied with a valid SecOC key, development builds or
custom forks may work with these vehicles. Release builds of openpilot don't support SecOC.
+117
View File
@@ -0,0 +1,117 @@
#!/usr/bin/env python3
import time
import threading
import argparse
import numpy as np
from pprint import pprint
from inputs import get_gamepad
from kbhit import KBHit
from iqdbc.car.structs import CarControl
from iqdbc.car.panda_runner import PandaRunner
class Keyboard:
def __init__(self):
self.kb = KBHit()
self.axis_increment = 0.05 # 5% of full actuation each key press
self.axes_map = {'w': 'gb', 's': 'gb',
'a': 'steer', 'd': 'steer'}
self.axes_values = {'gb': 0., 'steer': 0.}
self.axes_order = ['gb', 'steer']
self.cancel = False
def update(self):
key = self.kb.getch().lower()
print(key)
self.cancel = False
if key == 'r':
self.axes_values = {ax: 0. for ax in self.axes_values}
elif key == 'c':
self.cancel = True
elif key in self.axes_map:
axis = self.axes_map[key]
incr = self.axis_increment if key in ['w', 'a'] else -self.axis_increment
self.axes_values[axis] = float(np.clip(self.axes_values[axis] + incr, -1, 1))
else:
return False
return True
class Joystick:
def __init__(self, gamepad=False):
# TODO: find a way to get this from API, perhaps "inputs" doesn't support it
if gamepad:
self.cancel_button = 'BTN_NORTH' # (BTN_NORTH=X, ABS_RZ=Right Trigger)
accel_axis = 'ABS_Y'
steer_axis = 'ABS_RX'
else:
self.cancel_button = 'BTN_TRIGGER'
accel_axis = 'ABS_Y'
steer_axis = 'ABS_RX'
self.min_axis_value = {accel_axis: 0., steer_axis: 0.}
self.max_axis_value = {accel_axis: 255., steer_axis: 255.}
self.axes_values = {accel_axis: 0., steer_axis: 0.}
self.axes_order = [accel_axis, steer_axis]
self.cancel = False
def update(self):
joystick_event = get_gamepad()[0]
event = (joystick_event.code, joystick_event.state)
if event[0] == self.cancel_button:
if event[1] == 1:
self.cancel = True
elif event[1] == 0: # state 0 is falling edge
self.cancel = False
elif event[0] in self.axes_values:
self.max_axis_value[event[0]] = max(event[1], self.max_axis_value[event[0]])
self.min_axis_value[event[0]] = min(event[1], self.min_axis_value[event[0]])
norm = -float(np.interp(event[1], [self.min_axis_value[event[0]], self.max_axis_value[event[0]]], [-1., 1.]))
self.axes_values[event[0]] = norm if abs(norm) > 0.05 else 0. # center can be noisy, deadzone of 5%
else:
return False
return True
def joystick_thread(joystick):
while True:
joystick.update()
def main(joystick):
threading.Thread(target=joystick_thread, args=(joystick,), daemon=True).start()
with PandaRunner() as p:
CC = CarControl(enabled=False)
while True:
CC.actuators.accel = float(4.0*np.clip(joystick.axes_values['gb'], -1, 1))
CC.actuators.torque = float(np.clip(joystick.axes_values['steer'], -1, 1))
pprint(CC)
p.read()
p.write(CC)
# 100Hz
time.sleep(0.01)
if __name__ == '__main__':
parser = argparse.ArgumentParser(description='Test the car interface with a joystick. Uses keyboard by default.',
formatter_class=argparse.ArgumentDefaultsHelpFormatter)
parser.add_argument('--mode', choices=['keyboard', 'gamepad', 'joystick'], default='keyboard')
args = parser.parse_args()
print()
joystick: Keyboard | Joystick
if args.mode == 'keyboard':
print('Gas/brake control: `W` and `S` keys')
print('Steering control: `A` and `D` keys')
print('Buttons')
print('- `R`: Resets axes')
print('- `C`: Cancel cruise control')
joystick = Keyboard()
else:
joystick = Joystick(gamepad=(args.mode == 'gamepad'))
main(joystick)
+60
View File
@@ -0,0 +1,60 @@
#!/usr/bin/env python3
import sys
import termios
import atexit
from select import select
STDIN_FD = sys.stdin.fileno()
class KBHit:
def __init__(self) -> None:
self.set_kbhit_terminal()
def set_kbhit_terminal(self) -> None:
# Save the terminal settings
self.old_term = termios.tcgetattr(STDIN_FD)
self.new_term = self.old_term.copy()
# New terminal setting unbuffered
self.new_term[3] &= ~(termios.ICANON | termios.ECHO)
termios.tcsetattr(STDIN_FD, termios.TCSAFLUSH, self.new_term)
# Support normal-terminal reset at exit
atexit.register(self.set_normal_term)
def set_normal_term(self) -> None:
termios.tcsetattr(STDIN_FD, termios.TCSAFLUSH, self.old_term)
@staticmethod
def getch() -> str:
return sys.stdin.read(1)
@staticmethod
def getarrow() -> int:
c = sys.stdin.read(3)[2]
vals = [65, 67, 66, 68]
return vals.index(ord(c))
@staticmethod
def kbhit():
''' Returns True if keyboard character was hit, False otherwise.
'''
return select([sys.stdin], [], [], 0)[0] != []
if __name__ == "__main__":
kb = KBHit()
print('Hit any key, or ESC to exit')
while True:
if kb.kbhit():
c = kb.getch()
if c == '\x1b': # ESC
break
print(c)
kb.set_normal_term()
+1 -1
View File
@@ -198,7 +198,7 @@ def get_checksum_state(dbc_name: str) -> ChecksumState | None:
return ChecksumState(8, -1, 7, -1, False, SignalType.FCA_GIORGIO_CHECKSUM, fca_giorgio_checksum)
elif dbc_name.startswith("comma_body"):
return ChecksumState(8, 4, 7, 3, False, SignalType.BODY_CHECKSUM, body_checksum)
elif dbc_name.startswith(("tesla_model3_party", "tesla_model3_vehicle")):
elif dbc_name.startswith("tesla_model3_party"):
return ChecksumState(8, -1, 0, -1, True, SignalType.TESLA_CHECKSUM, tesla_checksum, tesla_setup_signal)
elif dbc_name.startswith("psa_"):
return ChecksumState(4, 4, 7, 3, False, SignalType.PSA_CHECKSUM, psa_checksum)
@@ -67,32 +67,6 @@ class TestCanParserPacker:
parser.update([t, [msg]])
assert parser.can_valid
def test_lazy_add_not_ignore_alive(self):
"""
Accessing an undeclared message via parser.vl[...] lazily adds it via
_add_message(key) with the default freq=None, which is NOT the same as
declaring it with math.nan (ignore_alive=True). It's treated as "assume
~1Hz, must be seen within ~10s" — so if that message is never fed, the
parser is permanently invalid. Declaring an optional/rarely-sent message
with math.nan (or gating the .vl[...] read entirely) is required to avoid
this; see iqdbc/car/volkswagen/carstate.py's Diagnose_1/EPB_1 bugs.
"""
parser = CANParser(TEST_DBC, [], 0)
assert parser.can_valid
# lazily add STEERING_CONTROL by reading it, without ever declaring it
# or feeding any CAN data for it
_ = parser.vl["STEERING_CONTROL"]
state = parser.message_states[parser.dbc.name_to_msg["STEERING_CONTROL"].address]
assert not state.ignore_alive
# never becomes valid again, no matter how many times it's checked
# (can_valid debounces over MAX_BAD_COUNTER reads before flipping false)
for _ in range(MAX_BAD_COUNTER):
parser.can_valid
for _ in range(20):
assert not parser.can_valid
def test_parser_updated_list(self):
msgs = [("CAN_FD_MESSAGE", 10), ]
parser = CANParser(TEST_DBC, msgs, 0)
+74
View File
@@ -0,0 +1,74 @@
<!--- AUTOGENERATED FROM selfdrive/car/CARS_template.md, DO NOT EDIT. --->
# Support Information for {{all_car_docs | length}} Known Cars
|{{ExtraCarsColumn | map(attribute='value') | join('|') | replace(hardware_col_name, wide_hardware_col_name)}}|
|---|---|---|{% for _ in range((ExtraCarsColumn | length) - 3) %}{{':---:|'}}{% endfor +%}
{% for car_docs in all_car_docs %}
|{% for column in ExtraCarsColumn %}{{car_docs.get_extra_cars_column(column)}}|{% endfor %}
{% endfor %}
# Types of Support
**iqdbc can support many more cars than it currently does.** There are a few reasons your car may not be supported.
If your car doesn't fit into any of the incompatibility criteria here, then there's a good chance it can be supported!
We're adding support for new cars all the time. **We don't have a roadmap for car support**, and in fact, most car
support comes from users like you!
## Upstream
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.
## Under Review
A vehicle under review is one for which software support has been merged into upstream openpilot, but hasn't yet been
tested for drive quality and conformance with [comma safety guidelines](https://github.com/commaai/openpilot/blob/master/docs/SAFETY.md).
This is a normal part of the development and quality assurance process. This vehicle will not work when upstream
openpilot is installed, but custom forks may allow their use.
## Custom
Vehicles in this category are not considered plug-and-play. Software support is included in upstream openpilot, but
these vehicles might not have a harness in the comma store, or the physical install might be at an unusual or cumbersome
location, or they might need unusual configuration after install. These vehicles will not work with release builds of
openpilot, but depending on the situation, development builds or custom forks may allow their use.
### SecOC cars with recoverable keys
For a small subset of SecOC-protected vehicles, tools may be available in the community to recover the SecOC keys. These
tools, and the recovery process, are not part of openpilot. If supplied with a valid SecOC key, development builds or
custom forks may work with these vehicles. Release builds of openpilot don't support SecOC.
## Dashcam
Dashcam vehicles have software support in upstream openpilot, but will go into "dashcam mode" at startup and will not
engage. This may be due to known issues with driving safety or quality, or it may be a work in progress that isn't yet
ready for safety and quality review.
## Community
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/).
Some notable works-in-progress:
* Honda
* 2022-24 Acura RDX, commaai/iqdbc#1967
* Camera ACC stability improvements, commaai/iqdbc#2192
* Alpha longitudinal stability improvements, commaai/iqdbc#2347 and commaai/iqdbc#2165
## Incompatible
### CAN Bus Security
Vehicles with CAN security measures, such as AUTOSAR Secure Onboard Communication (SecOC) are not usable with openpilot
unless the owner can recover the message signing key and implement CAN message signing. Examples include certain newer
Toyota, and the GM Global B platform.
### 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.
+5 -5
View File
@@ -535,9 +535,14 @@ struct CarParams {
steerLimitAlert @28 :Bool;
steerLimitTimer @47 :Float32; # time before steerLimitAlert is issued
vEgoStopping @29 :Float32; # Speed at which the car goes into stopping state
vEgoStarting @59 :Float32; # Speed at which the car goes into starting state
steerControlType @34 :SteerControlType;
radarUnavailable @35 :Bool; # True when radar objects aren't visible on CAN or aren't parsed out
stopAccel @60 :Float32; # Required acceleration to keep vehicle stationary
stoppingDecelRate @52 :Float32; # m/s^2/s while trying to stop
startAccel @32 :Float32; # Required acceleration to get car moving
startingState @70 :Bool; # Does this car make use of special starting state
steerActuatorDelay @36 :Float32; # Steering wheel actuator delay in seconds
longitudinalActuatorDelay @58 :Float32; # Gas/Brake actuator delay in seconds
@@ -770,9 +775,4 @@ struct CarParams {
stoppingControlDEPRECATED @31 :Bool; # Does the car allow full control even at lows speeds when stopping
radarTimeStepDEPRECATED @45: Float32 = 0.05; # time delta between radar updates, 20Hz is very standard
enableDsuDEPRECATED @5 :Bool; # driving support unit
vEgoStartingDEPRECATED @59 :Float32;
startAccelDEPRECATED @32 :Float32;
startingStateDEPRECATED @70 :Bool;
vEgoStoppingDEPRECATED @29 :Float32;
stoppingDecelRateDEPRECATED @52 :Float32;
}
+1 -1
View File
@@ -87,7 +87,7 @@ def fingerprint(can_recv: CanRecvCallable, can_send: CanSendCallable, set_obd_mu
cached_params: CarParamsT | None,
fixed_fingerprint: str | None) -> tuple[str | None, dict, str, list[CarParams.CarFw], CarParams.FingerprintSource, bool]:
fixed_fingerprint = fixed_fingerprint or os.environ.get('FINGERPRINT', "")
skip_fw_query = os.environ.get('SKIP_FW_QUERY', False) or bool(fixed_fingerprint)
skip_fw_query = os.environ.get('SKIP_FW_QUERY', False)
disable_fw_cache = os.environ.get('DISABLE_FW_CACHE', False)
ecu_rx_addrs = set()
+2
View File
@@ -21,6 +21,7 @@ from iqdbc.car.extra_cars import CAR as EXTRA
EXTRA_CARS_MD_OUT = os.path.join(BASEDIR, "../", "../", "docs", "CARS.md")
EXTRA_CARS_MD_TEMPLATE = os.path.join(BASEDIR, "CARS_template.md")
# TODO: merge these platforms into normal car ports with SupportType flag
ExtraPlatform = Platform | EXTRA
@@ -105,6 +106,7 @@ if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Auto generates supportability info docs for all known cars",
formatter_class=argparse.ArgumentDefaultsHelpFormatter)
parser.add_argument("--template", default=EXTRA_CARS_MD_TEMPLATE, help="Override default template filename")
parser.add_argument("--out", default=EXTRA_CARS_MD_OUT, help="Override default generated filename")
args = parser.parse_args()
+3
View File
@@ -125,6 +125,9 @@ class CarInterface(CarInterfaceBase, CarInterfaceExt):
# Tuning for experimental long
ret.longitudinalTuning.kiV = [2.0, 1.5]
ret.stoppingDecelRate = 2.0 # reach brake quickly after enabling
ret.vEgoStopping = 0.25
ret.vEgoStarting = 0.25
if alpha_long:
ret.pcmCruise = False
@@ -23,10 +23,6 @@ MAX_ANGLE_FRAMES = 89
MAX_ANGLE_CONSECUTIVE_FRAMES = 2
def _use_stock_lkas_request_path(CP) -> bool:
return CP.carFingerprint == CAR.HYUNDAI_PALISADE
def process_hud_alert(enabled, fingerprint, hud_control):
sys_warning = (hud_control.visualAlert in (VisualAlert.steerRequired, VisualAlert.ldw))
@@ -96,12 +92,12 @@ class CarController(CarControllerBase, EsccCarController, LeadDataCarController,
else:
self.apply_torque_last = apply_torque
if not _use_stock_lkas_request_path(self.CP) and apply_torque == 0 and CC.latActive and CS.out.steeringPressed:
if apply_torque == 0 and CC.latActive and CS.out.steeringPressed:
apply_steer_req = False
# Hold torque with induced temporary fault when cutting the actuation bit
# FIXME: we don't use this with CAN FD?
torque_fault = False if _use_stock_lkas_request_path(self.CP) else (CC.latActive and not apply_steer_req)
torque_fault = CC.latActive and not apply_steer_req
# accel + longitudinal
accel = float(np.clip(actuators.accel, CarControllerParams.ACCEL_MIN, CarControllerParams.ACCEL_MAX))
+3 -14
View File
@@ -7,10 +7,6 @@ from iqdbc.iqpilot.car.hyundai.lead_data_ext import CanLeadData
hyundai_checksum = crcmod.mkCrcFun(0x11D, initCrc=0xFD, rev=False, xorOut=0xdf)
def _use_stock_scc_surrogates(CP) -> bool:
return CP.carFingerprint == CAR.HYUNDAI_PALISADE
def create_lkas11(packer, frame, CP, apply_torque, steer_req,
torque_fault, lkas11, sys_warning, sys_state, enabled,
left_lane, right_lane,
@@ -141,7 +137,7 @@ def create_acc_commands(packer, enabled, accel, upper_jerk, idx, lead_data: CanL
commands = []
def get_scc11_values():
values = {
return {
"MainMode_ACC": 1 if main_cruise_enabled else 0,
"TauGapSet": hud_control.leadDistanceBars,
"VSetDis": set_speed if enabled else 0,
@@ -152,11 +148,6 @@ def create_acc_commands(packer, enabled, accel, upper_jerk, idx, lead_data: CanL
"ACC_ObjRelSpd": lead_data.lead_rel_speed,
"ACC_ObjDist": int(lead_data.lead_distance), # close lead makes controls tighter
}
if _use_stock_scc_surrogates(CP):
values["ObjValid"] = 1
values["ACC_ObjStatus"] = 1
values["ACC_ObjDist"] = 1
return values
def get_scc12_values():
scc12_values = {
@@ -185,17 +176,15 @@ def create_acc_commands(packer, enabled, accel, upper_jerk, idx, lead_data: CanL
return values
def get_scc14_values():
values = {
return {
"ComfortBandUpper": tuning.comfort_band_upper, # stock usually is 0 but sometimes uses higher values
"ComfortBandLower": tuning.comfort_band_lower, # stock usually is 0 but sometimes uses higher values
"JerkUpperLimit": tuning.jerk_upper, # stock usually is 1.0 but sometimes uses higher values
"JerkLowerLimit": tuning.jerk_lower, # stock usually is 0.5 but sometimes uses higher values
"ACCMode": 2 if enabled and long_override else 1 if enabled else 4, # stock will always be 4 instead of 0 after first disengage
"ObjGap": lead_data.object_gap, # 5: >30, m, 4: 25-30 m, 3: 20-25 m, 2: < 20 m, 0: no lead
"ObjDistStat": lead_data.object_rel_gap,
}
if not _use_stock_scc_surrogates(CP):
values["ObjDistStat"] = lead_data.object_rel_gap
return values
def get_fca11_values():
return {
@@ -134,6 +134,9 @@ class CarInterface(CarInterfaceBase):
ret.radarUnavailable = RADAR_START_ADDR not in fingerprint[1] or Bus.radar not in DBC[ret.carFingerprint]
ret.openpilotLongitudinalControl = alpha_long and ret.alphaLongitudinalAvailable
ret.pcmCruise = not ret.openpilotLongitudinalControl
ret.startingState = True
ret.vEgoStarting = 0.1
ret.startAccel = 1.0
ret.longitudinalActuatorDelay = 0.5
if ret.openpilotLongitudinalControl:
@@ -1,61 +0,0 @@
from iqdbc.can import CANParser, CANPacker
from iqdbc.car import Bus
from iqdbc.car.hyundai import hyundaican
from iqdbc.car.hyundai.values import CAR, DBC
class DummyHudControl:
leadDistanceBars = 3
leadVisible = True
class DummyLeadData:
lead_visible = False
lead_rel_speed = -7
lead_distance = 42
object_gap = 5
object_rel_gap = 2
class DummyTuning:
comfort_band_upper = 0.2
comfort_band_lower = 0.3
jerk_upper = 1.7
jerk_lower = 1.2
desired_accel = 0.4
actual_accel = 0.3
stopping = False
class DummyCP:
carFingerprint = CAR.HYUNDAI_PALISADE
flags = 0
def _decode(msg_name: str, addr: int, dat: bytes):
cp = CANParser(DBC[CAR.HYUNDAI_PALISADE][Bus.pt], [(msg_name, 0)], 0)
cp.update([(0, [(addr, dat, 0)])])
return cp.vl[msg_name]
def test_palisade_uses_stock_scc_surrogates():
packer = CANPacker(DBC[CAR.HYUNDAI_PALISADE][Bus.pt])
cp = DummyCP()
hud = DummyHudControl()
tuning = DummyTuning()
lead = DummyLeadData()
scc11 = hyundaican.create_acc_commands(
packer, True, 0.5, 3.0, 1, lead, hud, 72, False, False, True, cp, False, tuning
)[0]
scc14 = hyundaican.create_acc_commands(
packer, True, 0.5, 3.0, 1, lead, hud, 72, False, False, True, cp, False, tuning
)[2]
scc11_vals = _decode("SCC11", scc11[0], scc11[1])
assert scc11_vals["ObjValid"] == 1
assert scc11_vals["ACC_ObjStatus"] == 1
assert scc11_vals["ACC_ObjDist"] == 1
scc14_vals = _decode("SCC14", scc14[0], scc14[1])
assert "ObjDistStat" not in scc14_vals or scc14_vals["ObjDistStat"] == 0
+3
View File
@@ -253,6 +253,9 @@ class CarInterfaceBase(ABC, CarInterfaceBaseIQ):
ret.steerRatioRear = 0. # no rear steering, at least on the listed cars aboveA
ret.openpilotLongitudinalControl = False
ret.stopAccel = -2.0
ret.stoppingDecelRate = 0.8 # brake_travel/s while trying to stop
ret.vEgoStopping = 0.5
ret.vEgoStarting = 0.5
ret.longitudinalTuning.kpBP = [0.]
ret.longitudinalTuning.kpV = [0.]
ret.longitudinalTuning.kiBP = [0.]
+1
View File
@@ -6,6 +6,7 @@ from iqdbc.car.vehicle_model import VehicleModel
FRICTION_THRESHOLD = 0.2
# ISO 11270
ISO_LATERAL_ACCEL = 3.0 # m/s^2
ISO_LATERAL_JERK = 5.0 # m/s^3
AVERAGE_ROAD_ROLL = 0.06 # ~3.4 degrees
+2 -2
View File
@@ -3,7 +3,7 @@ import os
import capnp
import urllib.parse
import warnings
from urllib.request import urlopen, Request
from urllib.request import urlopen
import zstandard as zstd
from iqdbc.car.common.basedir import BASEDIR
@@ -27,7 +27,7 @@ class LogReader:
_, ext = os.path.splitext(urllib.parse.urlparse(fn).path)
if fn.startswith("http"):
with urlopen(Request(fn, headers={"User-Agent": "iqdbc"})) as f:
with urlopen(fn) as f:
dat = f.read()
else:
with open(fn, "rb") as f:
+1
View File
@@ -32,6 +32,7 @@ class CarInterface(CarInterfaceBase):
ret.safetyConfigs[0].safetyParam |= RivianSafetyFlags.LONG_CONTROL.value
ret.longitudinalActuatorDelay = 0.35
ret.vEgoStopping = 0.25
ret.stopAccel = 0
return ret
-2
View File
@@ -1,2 +0,0 @@
# FIXME: gate by FingerPrint
TESLA_BLINKERS = False
@@ -3,13 +3,11 @@ from iqdbc.can import CANPacker
from iqdbc.car import Bus
from iqdbc.car.lateral import apply_steer_angle_limits_vm
from iqdbc.car.interfaces import CarControllerBase
from iqdbc.car.tesla import TESLA_BLINKERS
from iqdbc.car.tesla.teslacan import TeslaCAN
from iqdbc.car.tesla.values import CarControllerParams
from iqdbc.car.vehicle_model import VehicleModel
from openpilot.iqpilot.selfdrive.car.enhanced_stock_longitudinal_control import get_set_speed_kph_from_params
from iqdbc.iqpilot.car.tesla.coop_steering import CoopSteeringCarController
from iqdbc.iqpilot.car.tesla.values import TeslaFlagsIQ
def get_safety_CP():
@@ -30,11 +28,6 @@ class CarController(CarControllerBase):
# Vehicle model used for lateral limiting
self.VM = VehicleModel(get_safety_CP())
self.has_vehicle_bus = bool(CP_IQ.flags & TeslaFlagsIQ.HAS_VEHICLE_BUS)
self.body_controls_counter_last = -1
self.blinker_request_prev = False
self.blinker_cancel_frame = 0
def update(self, CC, CC_IQ, CS, now_nanos):
actuators = CC.actuators
can_sends = []
@@ -59,8 +52,6 @@ class CarController(CarControllerBase):
if self.frame % 4 == 0:
state = 13 if CC.cruiseControl.cancel or CS.das_accCancel else 4 # 4=ACC_ON, 13=ACC_CANCEL_GENERIC_SILENT
accel = float(np.clip(actuators.accel, CarControllerParams.ACCEL_MIN, CarControllerParams.ACCEL_MAX))
if not CC.longActive:
accel = 0.
cntr = (self.frame // 4) % 8
set_speed_kph = get_set_speed_kph_from_params(CC_IQ.params)
can_sends.append(self.tesla_can.create_longitudinal_command(state, accel, cntr, CS.out.vEgo, CC.longActive,
@@ -72,30 +63,6 @@ class CarController(CarControllerBase):
cntr = (CS.das_control["DAS_controlCounter"] + 1) % 8
can_sends.append(self.tesla_can.create_longitudinal_command(13, 0, cntr, CS.out.vEgo, False, True))
# Nav blinker control via DAS_bodyControls on the vehicle bus, phase-locked to the car's
# counter. Cancel on the trailing edge since the body controller latches the signal.
stock_dat = getattr(CS, 'das_body_controls_dat', b"")
# FIXME: gate by FingerPrint
if TESLA_BLINKERS and self.has_vehicle_bus and len(stock_dat) >= 8:
left_blinker = CC.leftBlinker
right_blinker = CC.rightBlinker
driver_opposes = (left_blinker and CS.out.rightBlinker) or (right_blinker and CS.out.leftBlinker)
if driver_opposes:
left_blinker = right_blinker = False
nav_requesting = left_blinker or right_blinker
if self.blinker_request_prev and not nav_requesting and not driver_opposes:
self.blinker_cancel_frame = self.frame + 150 # ~1.5 s
self.blinker_request_prev = nav_requesting
cancel = not nav_requesting and not driver_opposes and self.frame < self.blinker_cancel_frame
body_counter = stock_dat[6] >> 4
if body_counter != self.body_controls_counter_last:
can_sends.append(self.tesla_can.create_body_controls(stock_dat, left_blinker, right_blinker, cancel))
self.body_controls_counter_last = body_counter
# TODO: HUD control
new_actuators = actuators.as_builder()
new_actuators.steeringAngleDeg = self.apply_angle_last
+37 -26
View File
@@ -1,17 +1,13 @@
import copy
from iqdbc.can import CANDefine, CANParser
from iqdbc.car import Bus, create_button_events, structs
from iqdbc.car.carlog import carlog
from iqdbc.car.common.conversions import Conversions as CV
from iqdbc.car.interfaces import CarStateBase
from iqdbc.car.tesla import TESLA_BLINKERS
from iqdbc.car.tesla.teslacan import get_steer_ctrl_type
from iqdbc.car.tesla.values import DBC, CANBUS, GEAR_MAP, STEER_THRESHOLD, TeslaFlags
from iqdbc.iqpilot.car.tesla.carstate_ext import CarStateExt
from iqdbc.iqpilot.car.tesla.values import TeslaFlagsIQ
from openpilot.common.params import Params
from openpilot.system.proprietary_runtime._verified_import import import_verified_module
vehicle_state = import_verified_module("iqpilot_alc_private", "iqpilot_private.konn3kt.iqlvbs.vehicle_state")
ButtonType = structs.CarState.ButtonEvent.Type
@@ -26,12 +22,13 @@ class CarState(CarStateBase, CarStateExt):
self.summon = False
self.summon_prev = False
self.cruise_enabled_prev = False
self.fsd14_error_logged = False
self.suspected_fsd14 = False
self.suspected_fsd14_clear_frames = 0
self.hands_on_level = 0
self.acc_state_last = 0
self.das_control = None
self.das_body_controls_dat = b""
self._odometer_store = vehicle_state.VehicleOdometerStore(CP, Params())
self.cruise_override = False
def update_summon_state(self, summon_state: str, cruise_enabled: bool):
@@ -139,40 +136,54 @@ class CarState(CarStateBase, CarStateExt):
ret.stockAeb = cp_ap_party.vl["DAS_control"]["DAS_aebEvent"] == 1
# LKAS
steer_control_type = int(cp_ap_party.vl["DAS_steeringControl"]["DAS_steeringControlType"])
if self.CP.flags & TeslaFlags.LEGACY_DAS_STEERING:
steer_control_type >>= 1 # legacy firmware uses a 2-bit field, one bit up from the 3-bit signal
ret.stockLkas = steer_control_type == 2 # LANE_KEEP_ASSIST
# On FSD 14+, ANGLE_CONTROL behavior changed to allow user winddown while actuating.
# FSD switched from using ANGLE_CONTROL to LANE_KEEP_ASSIST to likely keep the old steering override disengage logic.
# LKAS switched from LANE_KEEP_ASSIST to ANGLE_CONTROL to likely allow overriding LKAS events smoothly
lkas_ctrl_type = get_steer_ctrl_type(self.CP.flags, 2)
ret.stockLkas = cp_ap_party.vl["DAS_steeringControl"]["DAS_steeringControlType"] == lkas_ctrl_type # LANE_KEEP_ASSIST
# Stock Autosteer should be disengaged (includes FSD)
# TODO: find for TESLA_MODEL_X and HW2.5 vehicles
if not (self.CP.flags & TeslaFlags.MISSING_DAS_SETTINGS):
ret.invalidLkasSetting = cp_ap_party.vl["DAS_status"]["DAS_autopilotState"] not in (0, 1, 2) # DISABLED, UNAVAILABLE, AVAILABLE
# Because we don't have FSD 14 detection outside of a set of FW, we should check if this FW is accidentally missing from FSD_14_FW
# 1. If in Autosteer or FSD, already caught by invalidLkasSetting
# 2. If in TACC and DAS ever sends ANGLE_CONTROL (1), we can infer it's trying to do LKAS on FSD 14+
# NOTE: Tesla's latest firmware changed ELDA (Emergency Lane Departure Assist) to use ANGLE_CONTROL (1)
# instead of EMERGENCY_LANE_KEEP (3). Exclude ELDA by checking eac_status so it doesn't latch suspected_fsd14.
eac_is_emergency = eac_status == "EMERGENCY_LANE_KEEP"
angle_control = cp_ap_party.vl["DAS_steeringControl"]["DAS_steeringControlType"] == 1 and not eac_is_emergency # ANGLE_CONTROL, excluding ELDA
if not ret.invalidLkasSetting and angle_control and not self.CP.flags & TeslaFlags.FSD_14:
self.suspected_fsd14 = True
self.suspected_fsd14_clear_frames = 0
if self.suspected_fsd14:
ret.invalidLkasSetting = True
if not self.fsd14_error_logged:
carlog.error("FSD 14 detected, but FW not in FSD_14_FW set")
self.fsd14_error_logged = True
# Un-latch if ANGLE_CONTROL has been absent for ~3 s (100 frames @ ~33 Hz).
# This allows re-engagement after transient triggers (e.g. if ELDA slips through on new FW variants).
if not angle_control:
self.suspected_fsd14_clear_frames += 1
if self.suspected_fsd14_clear_frames >= 100:
self.suspected_fsd14 = False
self.suspected_fsd14_clear_frames = 0
else:
self.suspected_fsd14_clear_frames = 0
# Buttons # ToDo: add Gap adjust button
# Messages needed by carcontroller
self.das_control = copy.copy(cp_ap_party.vl["DAS_control"])
# Raw stock DAS_bodyControls bytes (bus 2), used to ride the blinker on the vehicle bus.
# FIXME: gate by FingerPrint
if TESLA_BLINKERS and Bus.cam in can_parsers:
self.das_body_controls_dat = bytes(can_parsers[Bus.cam].dat.get(0x3E9, b""))
CarStateExt.update(self, ret, ret_iq, can_parsers)
if ret.odometer > 0.0:
ret.odometer = self._odometer_store.record(ret.odometer) or 0.0
return ret, ret_iq
@staticmethod
def get_can_parsers(CP, CP_IQ):
parsers = {
return {
Bus.party: CANParser(DBC[CP.carFingerprint][Bus.party], [], CANBUS.party),
Bus.ap_party: CANParser(DBC[CP.carFingerprint][Bus.party], [], CANBUS.autopilot_party),
**CarStateExt.get_parser(CP, CP_IQ),
}
# Stock DAS_bodyControls from the AP bus (bus 2) for the nav blinker.
if TESLA_BLINKERS and CP_IQ.flags & TeslaFlagsIQ.HAS_VEHICLE_BUS and Bus.adas in DBC[CP.carFingerprint]:
parsers[Bus.cam] = CANParser(DBC[CP.carFingerprint][Bus.adas], [("DAS_bodyControls", 2)], CANBUS.autopilot_party)
return parsers
+10 -9
View File
@@ -2,7 +2,7 @@ from iqdbc.car import Bus, get_safety_config, structs
from iqdbc.car.interfaces import CarInterfaceBase
from iqdbc.car.tesla.carcontroller import CarController
from iqdbc.car.tesla.carstate import CarState
from iqdbc.car.tesla.values import TeslaSafetyFlags, TeslaFlags, CANBUS, CAR, DBC, LEGACY_DAS_STEERING_FW, Ecu
from iqdbc.car.tesla.values import TeslaSafetyFlags, TeslaFlags, CANBUS, CAR, DBC, FSD_14_FW, Ecu
from iqdbc.car.tesla.radar_interface import RadarInterface, RADAR_START_ADDR
from iqdbc.iqpilot.car.tesla.values import TeslaFlagsIQ, TeslaSafetyFlagsIQ
@@ -41,10 +41,14 @@ class CarInterface(CarInterfaceBase):
ret.openpilotLongitudinalControl = True
ret.safetyConfigs[0].safetyParam |= TeslaSafetyFlags.LONG_CONTROL.value
legacy_das = any(fw.ecu == Ecu.eps and fw.fwVersion in LEGACY_DAS_STEERING_FW.get(candidate, []) for fw in car_fw)
if legacy_das:
ret.flags |= TeslaFlags.LEGACY_DAS_STEERING.value
ret.safetyConfigs[0].safetyParam |= TeslaSafetyFlags.LEGACY_DAS_STEERING.value
ret.vEgoStopping = 0.1
ret.vEgoStarting = 0.1
ret.stoppingDecelRate = 0.3
fsd_14 = any(fw.ecu == Ecu.eps and fw.fwVersion in FSD_14_FW.get(candidate, []) for fw in car_fw)
if fsd_14:
ret.flags |= TeslaFlags.FSD_14.value
ret.safetyConfigs[0].safetyParam |= TeslaSafetyFlags.FSD_14.value
ret.dashcamOnly = candidate in (CAR.TESLA_MODEL_X,) # dashcam only, pending find invalidLkasSetting signal
@@ -59,10 +63,7 @@ class CarInterface(CarInterfaceBase):
if candidate == CAR.TESLA_MODEL_X:
stock_cp.dashcamOnly = False
# Vehicle-bus messages can be slow enough to miss the initial capture window.
# Accept either the established 0x3DF marker or the absolute odometer frame.
vehicle_bus_seen = any(0x3DF in bus or 0x3B6 in bus for bus in fingerprint.values())
if vehicle_bus_seen:
if 0x3DF in fingerprint[1]:
ret.flags |= TeslaFlagsIQ.HAS_VEHICLE_BUS.value
ret.iqSafetyFlags |= TeslaSafetyFlagsIQ.HAS_VEHICLE_BUS
+13 -32
View File
@@ -3,6 +3,14 @@ from iqdbc.car.tesla.values import CANBUS, CarControllerParams, TeslaFlags
from iqdbc.car import DT_CTRL
def get_steer_ctrl_type(flags: int, ctrl_type: int) -> int:
# Returns the flipped signal value for DAS_steeringControlType on FSD 14
if flags & TeslaFlags.FSD_14:
return {1: 2, 2: 1}.get(ctrl_type, ctrl_type)
else:
return ctrl_type
class TeslaCAN:
def __init__(self, CP, packer):
self.CP = CP
@@ -10,15 +18,14 @@ class TeslaCAN:
self.l_jerk = 0.0
def create_steering_control(self, angle, enabled, control_type):
# control_type comes from coop_steering: ANGLE_CONTROL (1) normally, LANE_KEEP_ASSIST (2) when cooperative steering is enabled
control_type = control_type if enabled else 0
if self.CP.flags & TeslaFlags.LEGACY_DAS_STEERING:
control_type <<= 1 # legacy firmware uses a 2-bit field, one bit up from the 3-bit signal
# On FSD 14+, ANGLE_CONTROL behavior changed to allow user winddown while actuating.
# with openpilot, after overriding w/ ANGLE_CONTROL the wheel snaps back to the original angle abruptly
# so we now use LANE_KEEP_ASSIST to match stock FSD.
# see carstate.py for more details
values = {
"DAS_steeringAngleRequest": -angle,
"DAS_steeringHapticRequest": 0,
"DAS_steeringControlType": control_type,
"DAS_steeringControlType": get_steer_ctrl_type(self.CP.flags, control_type if enabled else 0),
}
return self.packer.make_can_msg("DAS_steeringControl", CANBUS.party, values)
@@ -53,32 +60,6 @@ class TeslaCAN:
return self.packer.make_can_msg("APS_eacMonitor", CANBUS.party, values)
def create_body_controls(self, stock_dat, left_blinker, right_blinker, cancel=False):
# Ride alongside the car's native DAS_bodyControls: copy the raw frame, override only the
# turn-indicator bits, and stamp counter + 1 so our frame supersedes the stock one.
dat = bytearray(stock_dat)
if len(dat) < 8:
dat.extend(b"\x00" * (8 - len(dat)))
if left_blinker or right_blinker:
turn_req = 1 if left_blinker else 2 # DAS_TURN_INDICATOR_LEFT / _RIGHT
dat[1] = (dat[1] & ~0x07) | (turn_req & 0x07)
dat[2] = (dat[2] & ~0x3C) | (1 << 2) # DAS_ACTIVE_NAV_LANE_CHANGE
elif cancel:
dat[1] = (dat[1] & ~0x07) | 0x03 # DAS_TURN_INDICATOR_CANCEL
dat[2] = (dat[2] & ~0x3C) | (4 << 2) # DAS_CANCEL_LANE_CHANGE
counter = (((dat[6] >> 4) + 1) & 0x0F)
dat[6] = (dat[6] & ~0xF0) | (counter << 4)
addr = 0x3E9
checksum = (addr & 0xFF) + ((addr >> 8) & 0xFF)
for i in range(7):
checksum += dat[i]
dat[7] = checksum & 0xFF
return addr, bytes(dat), CANBUS.vehicle
def tesla_checksum(address: int, sig, d: bytearray) -> int:
checksum = (address & 0xFF) + ((address >> 8) & 0xFF)
@@ -4,7 +4,6 @@ from iqdbc.car.tesla.teslacan import TeslaCAN
from iqdbc.car.tesla.radar_interface import RADAR_START_ADDR
from iqdbc.car.tesla.carcontroller import CarController
from iqdbc.car.tesla.values import CAR
from iqdbc.can import CANPacker, CANParser
class TestTeslaFingerprint:
@@ -32,19 +31,6 @@ class TestTeslaCan:
def make_can_msg(self, name, bus, values):
return name, bus, values
def test_vehicle_bus_odometer_decodes_kilometers(self):
packer = CANPacker("tesla_model3_vehicle")
parser = CANParser("tesla_model3_vehicle", [("ID3B6UI_odometer", 1)], 1)
message = packer.make_can_msg("ID3B6UI_odometer", 1, {
"UI_odometer": 29150.377,
"UI_odometerCounter": 1,
"UI_odometerChecksum": 0,
})
parser.update([1_000_000_000, [message]])
assert parser.vl["ID3B6UI_odometer"]["UI_odometer"] == 29150.377
def test_longitudinal_command_does_not_reference_missing_jerk_attr(self):
CP = CarInterface.get_non_essential_params(CAR.TESLA_MODEL_3)
tesla_can = TeslaCAN(CP, self.DummyPacker())
+9 -34
View File
@@ -79,41 +79,16 @@ FW_QUERY_CONFIG = FwQueryConfig(
]
)
# Cars with this EPS FW have a 2-bit DAS_steeringControlType and use TeslaFlags.LEGACY_DAS_STEERING
LEGACY_DAS_STEERING_FW = {
# Cars with this EPS FW have FSD 14 and use TeslaFlags.FSD_14
FSD_14_FW = {
CAR.TESLA_MODEL_3: [
b'TeM3_E014p10_0.0.0 (16),E014.17.00',
b'TeM3_E014p10_0.0.0 (16),EL014.17.00',
b'TeM3_ES014p11_0.0.0 (25),ES014.19.0',
b'TeMYG4_DCS_Update_0.0.0 (13),E4014.28.1',
b'TeMYG4_DCS_Update_0.0.0 (9),E4014.26.0',
b'TeMYG4_Legacy3Y_0.0.0 (2),E4015.02.0',
b'TeMYG4_Legacy3Y_0.0.0 (5),E4015.03.2',
b'TeMYG4_Legacy3Y_0.0.0 (5),E4L015.03.2',
b'TeMYG4_Main_0.0.0 (59),E4H014.29.0',
b'TeMYG4_Main_0.0.0 (65),E4H015.01.0',
b'TeMYG4_Main_0.0.0 (67),E4H015.02.1',
b'TeMYG4_SingleECU_0.0.0 (33),E4S014.27',
b'TeMYG4_Main_0.0.0 (77),E4HP015.04.5',
b'TeMYG4_Main_0.0.0 (78),E4HP015.05.0',
],
CAR.TESLA_MODEL_Y: [
b'TeM3_E014p10_0.0.0 (16),Y002.18.00',
b'TeM3_E014p10_0.0.0 (16),YP002.18.00',
b'TeM3_ES014p11_0.0.0 (16),YS002.17',
b'TeM3_ES014p11_0.0.0 (25),YS002.19.0',
b'TeMYG4_DCS_Update_0.0.0 (13),Y4002.27.1',
b'TeMYG4_DCS_Update_0.0.0 (13),Y4P002.27.1',
b'TeMYG4_DCS_Update_0.0.0 (9),Y4P002.25.0',
b'TeMYG4_Legacy3Y_0.0.0 (2),Y4003.02.0',
b'TeMYG4_Legacy3Y_0.0.0 (2),Y4P003.02.0',
b'TeMYG4_Legacy3Y_0.0.0 (5),Y4003.03.2',
b'TeMYG4_Legacy3Y_0.0.0 (5),Y4P003.03.2',
b'TeMYG4_SingleECU_0.0.0 (28),Y4S002.23.0',
b'TeMYG4_SingleECU_0.0.0 (33),Y4S002.26',
],
CAR.TESLA_MODEL_X: [
b'TeM3_SP_XP002p2_0.0.0 (23),XPR003.6.0',
b'TeM3_SP_XP002p2_0.0.0 (36),XPR003.10.0',
],
b'TeMYG4_Legacy3Y_0.0.0 (6),Y4003.04.0',
b'TeMYG4_Main_0.0.0 (77),Y4003.05.4',
]
}
@@ -164,12 +139,12 @@ class CarControllerParams:
class TeslaSafetyFlags(IntFlag):
LONG_CONTROL = 1
LEGACY_DAS_STEERING = 2
FSD_14 = 2
class TeslaFlags(IntFlag):
LONG_CONTROL = 1
LEGACY_DAS_STEERING = 2
FSD_14 = 2
MISSING_DAS_SETTINGS = 4
+1 -2
View File
@@ -301,8 +301,7 @@ routes = [
CarTestRoute("578742b26807f756|00000010--41ee3e5bec", VOLKSWAGEN.VOLKSWAGEN_JETTA_MK6),
CarTestRoute("58a7d3b707987d65/2021-03-25--17-26-37", VOLKSWAGEN.VOLKSWAGEN_JETTA_MK7),
CarTestRoute("4d134e099430fba2/2021-03-26--00-26-06", VOLKSWAGEN.VOLKSWAGEN_PASSAT_MK8),
CarTestRoute("b29ee8c5a0a735d1|000000dc--a384e9083e", VOLKSWAGEN.VOLKSWAGEN_PASSAT_NMS, segment=0),
CarTestRoute("0f53129ed44f6920|00000287--3efbddeb96", VOLKSWAGEN.VOLKSWAGEN_PASSAT_NMS, segment=0),
CarTestRoute("3cfdec54aa035f3f/2022-07-19--23-45-10", VOLKSWAGEN.VOLKSWAGEN_PASSAT_NMS),
CarTestRoute("0cd0b7f7e31a3853/2021-11-03--19-30-22", VOLKSWAGEN.VOLKSWAGEN_POLO_MK6),
CarTestRoute("064d1816e448f8eb/2022-09-29--15-32-34", VOLKSWAGEN.VOLKSWAGEN_SHARAN_MK2),
CarTestRoute("7d82b2f3a9115f1f/2021-10-21--15-39-42", VOLKSWAGEN.VOLKSWAGEN_TAOS_MK1),
@@ -1,133 +0,0 @@
#!/usr/bin/env python3
"""Real-CAN replay invariant tests for VW torque platforms (PQ / MQB / MLB).
Replays real konn3kt routes through the car interface with openpilot lateral
INACTIVE (latActive=False) and asserts openpilot never transmits an active-steering
HCA command (active status or non-zero torque). Re-transmitting the stock camera's
active HCA while not in control (stock-LKAS forwarding) leaves the EPS faulted for
the whole drive (LH2_Sta_HCA=FAULT) - the regression that bricked steering on a PQ
Passat NMS with a factory LKAS camera. Panda accepts these frames, so only a replay
invariant like this catches it.
Self-contained within iqdbc: routes are resolved through konn3kt's public
/v1/route/<id>/files endpoint (URLs are signed server-side, no auth/token needed).
"""
import json
import os
import urllib.parse
import urllib.request
from collections import Counter
import pytest
from iqdbc.can.parser import CANParser
from iqdbc.car import Bus, structs
from iqdbc.car.can_definitions import CanData
from iqdbc.car.car_helpers import can_fingerprint, interfaces
from iqdbc.car.logreader import LogReader
from iqdbc.car.volkswagen.values import CAR, DBC, VolkswagenFlags
API_HOST = os.environ.get("API_HOST", "https://api-iqlabs.konn3kt.com")
# konn3kt-hosted VW routes. (route_id, segment, platform, label)
# Add MQB routes here as konn3kt-hosted MQB logs become available.
VW_ROUTES = [
("b29ee8c5a0a735d1|000000dc--a384e9083e", 0, CAR.VOLKSWAGEN_PASSAT_NMS, "PQ with stock LKAS camera"),
("0f53129ed44f6920|00000287--3efbddeb96", 0, CAR.VOLKSWAGEN_PASSAT_NMS, "PQ without stock LKAS camera"),
]
# Per-platform HCA message: (address, msg, status signal, torque signal, active-status values)
HCA_INFO = {
"pq": (0xD2, "HCA_1", "HCA_Status", "LM_Offset", (5, 7)),
"mqb": (0x126, "HCA_01", "HCA_01_Status_HCA", "HCA_01_LM_Offset", (5, 6, 7)),
}
def _request_headers() -> dict[str, str]:
# konn3kt's edge rejects the default urllib User-Agent with 403. The routes are public
# (access returns early for public routes), but IQ.Pilot/Cabana tooling conventionally
# sends a Konn3kt user JWT, so include one when available (env or ~/.comma/auth.json).
headers = {"User-Agent": "iqdbc"}
token = os.environ.get("KONN3KT_ACCESS_TOKEN")
if not token:
try:
with open(os.path.expanduser("~/.comma/auth.json")) as f:
token = json.load(f).get("access_token")
except (OSError, ValueError):
token = None
if token:
headers["Authorization"] = f"JWT {token}"
return headers
def _rlog_url(route_id: str, segment: int) -> str:
req = urllib.request.Request(f"{API_HOST}/v1/route/{urllib.parse.quote(route_id, safe='|')}/files",
headers=_request_headers())
with urllib.request.urlopen(req, timeout=30) as f:
files = json.load(f)
for url in files.get("logs", []):
# path looks like /connectdata/<dongle>/<log>/<seg>/rlog.zst
parts = urllib.parse.urlparse(url).path.rstrip("/").split("/")
if len(parts) >= 2 and parts[-2] == str(segment):
return url
raise RuntimeError(f"no rlog for {route_id} segment {segment} (uploaded & public?)")
def _load_can(route_id: str, segment: int):
lr = LogReader(_rlog_url(route_id, segment), only_union_types=True, sort_by_time=True)
return [(m.logMonoTime, [CanData(c.address, c.dat, c.src) for c in m.can]) for m in lr if m.which() == "can"]
@pytest.mark.parametrize("route_id,segment,platform,label", VW_ROUTES)
def test_vw_inactive_steering_invariant(route_id, segment, platform, label):
can_msgs = _load_can(route_id, segment)
assert len(can_msgs) > 1000, f"insufficient CAN data for {label}: {len(can_msgs)} frames"
# fingerprint from a fresh iterator over the (unmutated) frame list
frame_iter = (frames for _, frames in can_msgs)
def can_recv(wait_for_one: bool = False):
return [next(frame_iter, [])]
_, fingerprint = can_fingerprint(can_recv)
CarInterface = interfaces[platform]
CP = CarInterface.get_params(platform, fingerprint, [], False, False, False)
CP_IQ = CarInterface.get_params_iq(CP, platform, fingerprint, [], False, False, False)
if CP.flags & (VolkswagenFlags.MEB | VolkswagenFlags.MQB_EVO):
pytest.skip("invariant covers torque-based VW platforms (PQ/MQB/MLB)")
key = "pq" if CP.flags & VolkswagenFlags.PQ else "mqb"
hca_addr, hca_msg, status_sig, torque_sig, active_status = HCA_INFO[key]
cp = CANParser(DBC[platform][Bus.pt], [(hca_msg, 0)], 0)
CI = CarInterface(CP, CP_IQ)
CC = structs.CarControl().as_reader() # latActive defaults to False
CC_IQ = structs.IQCarControl()
hca_seen = 0
violations = Counter()
for i, (mono, frames) in enumerate(can_msgs):
CI.update([(mono, frames)])
_, sendcan = CI.apply(CC, CC_IQ, mono)
if i < 300: # CarController / CANParser warmup
continue
for addr, dat, bus in sendcan:
if addr != hca_addr or bus != 0:
continue
hca_seen += 1
cp.update([(mono, [(addr, bytes(dat), 0)])])
if int(cp.vl[hca_msg][status_sig]) in active_status:
violations["active_status"] += 1
if abs(cp.vl[hca_msg][torque_sig]) > 0:
violations["nonzero_torque"] += 1
assert hca_seen > 50, f"{label}: no HCA steering messages transmitted to inspect"
assert not len(violations), \
f"{label}: openpilot TX'd active HCA while latActive=False: {dict(violations)}"
if __name__ == "__main__":
import sys
sys.exit(pytest.main([__file__, "-v"]))
@@ -67,7 +67,6 @@ legend = ["LAT_ACCEL_FACTOR", "MAX_LAT_ACCEL_MEASURED", "FRICTION"]
"VOLKSWAGEN_CADDY_MK3" = [1.2, 1.2, 0.1]
"VOLKSWAGEN_PASSAT_NMS" = [2.5, 2.5, 0.1]
"VOLKSWAGEN_SHARAN_MK2" = [2.5, 2.5, 0.1]
"SEAT_ALHAMBRA_MK1" = [2.5, 2.5, 0.1]
"HYUNDAI_SANTA_CRUZ_1ST_GEN" = [2.7, 2.7, 0.1]
"KIA_SPORTAGE_5TH_GEN" = [2.6, 2.6, 0.1]
"GENESIS_GV70_1ST_GEN" = [2.42, 2.42, 0.1]
@@ -76,7 +76,6 @@ legend = ["LAT_ACCEL_FACTOR", "MAX_LAT_ACCEL_MEASURED", "FRICTION"]
"VOLKSWAGEN_JETTA_MK6" = "VOLKSWAGEN_PASSAT_NMS"
"VOLKSWAGEN_PASSAT_MK7" = "VOLKSWAGEN_PASSAT_NMS"
"VOLKSWAGEN_PASSAT_NMS_PLUS" = "VOLKSWAGEN_PASSAT_NMS"
"VOLKSWAGEN_PASSAT_B7" = "VOLKSWAGEN_PASSAT_NMS"
"SUBARU_CROSSTREK_HYBRID" = "SUBARU_IMPREZA_2020"
"SUBARU_FORESTER_HYBRID" = "SUBARU_IMPREZA_2020"
+1 -4
View File
@@ -260,7 +260,6 @@ class CarController(CarControllerBase, GasInterceptorCarController):
freeze_integrator=actuators.longControlState != LongCtrlState.pid)
else:
self.long_pid.reset()
pcm_accel_cmd = 0.
# Along with rate limiting positive jerk above, this greatly improves gas response time
# Consider the net acceleration request that the PCM should be applying (pitch included)
@@ -270,9 +269,7 @@ class CarController(CarControllerBase, GasInterceptorCarController):
elif net_acceleration_request_min > 0.3:
self.permit_braking = False
sdsu_tssp_long_active = bool(self.CP_IQ.flags & ToyotaFlagsIQ.SMART_DSU) and \
(self.CP.carFingerprint not in TSS2_CAR) and CC.longActive
pcm_accel_cmd = actuators.accel if sdsu_tssp_long_active else pcm_accel_cmd
pcm_accel_cmd = pcm_accel_cmd if self.CP.carFingerprint in TSS2_CAR else actuators.accel
pcm_accel_cmd = float(np.clip(pcm_accel_cmd, self.params.ACCEL_MIN, self.params.ACCEL_MAX))
main_accel_cmd = 0. if self.CP.flags & ToyotaFlags.SECOC.value else pcm_accel_cmd
+8
View File
@@ -36,6 +36,7 @@ class CarInterface(CarInterfaceBase):
if ret.flags & ToyotaFlags.SECOC.value:
ret.secOcRequired = True
ret.safetyConfigs[0].safetyParam |= ToyotaSafetyFlags.SECOC.value
ret.dashcamOnly = is_release
if candidate in ANGLE_CONTROL_CAR:
ret.steerControlType = SteerControlType.angle
@@ -118,6 +119,10 @@ class CarInterface(CarInterfaceBase):
if candidate in TSS2_CAR:
ret.flags |= ToyotaFlags.RAISED_ACCEL_LIMIT.value
ret.vEgoStopping = 0.25
ret.vEgoStarting = 0.25
ret.stoppingDecelRate = 0.3 # reach stopping target smoothly
# Hybrids have much quicker longitudinal actuator response
if ret.flags & ToyotaFlags.HYBRID.value:
ret.longitudinalActuatorDelay = 0.05
@@ -127,6 +132,9 @@ class CarInterface(CarInterfaceBase):
@staticmethod
def _get_params_iq(stock_cp: structs.CarParams, ret: structs.IQCarParams, candidate, fingerprint: dict[int, dict[int, int]],
car_fw: list[structs.CarParams.CarFw], alpha_long: bool, is_release_iq: bool, docs: bool) -> structs.IQCarParams:
if stock_cp.flags & ToyotaFlags.SECOC.value and stock_cp.fingerprintSource == structs.CarParams.FingerprintSource.fixed:
stock_cp.dashcamOnly = False
if candidate in UNSUPPORTED_DSU_CAR:
ret.iqSafetyFlags |= ToyotaSafetyFlagsIQ.UNSUPPORTED_DSU
@@ -4,20 +4,26 @@ from iqdbc.car.toyota.values import CAR, ToyotaFlags
from iqdbc.iqpilot.car.toyota.values import ToyotaFlagsIQ
def test_secoc_toyota_not_dashcam_on_release():
# SecOC Toyotas are controllable regardless of branch or fingerprint source.
def test_forced_secoc_toyota_clears_dashcam_mode():
candidate = CAR.TOYOTA_SIENNA_4TH_GEN
# is_release=True (release branch) must not force dashcam mode
cp = CarInterface.get_params(candidate, gen_empty_fingerprint(), [], False, True, False)
assert cp.flags & ToyotaFlags.SECOC.value
assert cp.secOcRequired
assert cp.dashcamOnly
cp.fingerprintSource = structs.CarParams.FingerprintSource.fixed
CarInterface.get_params_iq(cp, candidate, gen_empty_fingerprint(), [], False, True, False)
assert not cp.dashcamOnly
# fw/can-sourced fingerprint (not forced) must also stay controllable
def test_automatic_secoc_toyota_release_stays_dashcam():
candidate = CAR.TOYOTA_SIENNA_4TH_GEN
cp = CarInterface.get_params(candidate, gen_empty_fingerprint(), [], False, True, False)
assert cp.dashcamOnly
cp.fingerprintSource = structs.CarParams.FingerprintSource.can
CarInterface.get_params_iq(cp, candidate, gen_empty_fingerprint(), [], False, True, False)
assert not cp.dashcamOnly
assert cp.dashcamOnly
def test_smart_dsu_clears_disable_radar_on_radar_acc_toyota():
+32 -124
View File
@@ -14,16 +14,10 @@ from iqdbc.car.common.conversions import Conversions as CV
from iqdbc.car.common.numpy_fast import clip, interp
from iqdbc.car.interfaces import CarControllerBase
from iqdbc.car.volkswagen import mlbcan, mqbcan, pqcan, mebcan
from iqdbc.car.volkswagen.pq_radar_handler import PQRadarHandler
from iqdbc.car.volkswagen.values import (
CanBus, CarControllerParams, MQB_A0_CARS, VolkswagenFlags, VolkswagenFlagsIQ, apply_pq_stopping_accel,
)
from iqdbc.car.volkswagen.values import CanBus, CarControllerParams, VolkswagenFlags, VolkswagenFlagsIQ
from iqdbc.car.volkswagen.mebutils import LongControlJerk, LongControlLimit, LatControlCurvature
from iqdbc.car.vehicle_model import VehicleModel
from openpilot.system.proprietary_runtime._verified_import import import_verified_module
iq_lvbs_alc = import_verified_module("iqpilot_alc_private", "iqpilot_private.konn3kt.iqlvbs.alc")
iq_lvbs_commander = import_verified_module("iqpilot_commander_private", "iqpilot_private.konn3kt.iqlvbs.iqlvbs_commander")
from openpilot.iqpilot.konn3kt.iqlvbs import alc as iq_lvbs_alc
iqpilot_path = os.path.join(os.path.dirname(__file__), '..', '..', '..')
sys.path.insert(0, iqpilot_path)
@@ -33,19 +27,9 @@ except ImportError:
pass
VisualAlert = structs.CarControl.HUDControl.VisualAlert
AudibleAlert = structs.CarControl.HUDControl.AudibleAlert
LongCtrlState = structs.CarControl.Actuators.LongControlState
def dVisual(CCS, CS):
if CCS == mqbcan:
decelV = CS.tsk_verzoeg_anf
elif CCS == pqcan:
decelV = CS.br8_acc_anf
else:
decelV = False
return decelV
class MQBStandstillManager:
BRAKE_TORQUE_RAMP_RATE = 2000.0 # Nm/s
ASSUMED_WHEEL_RADIUS = 0.328 # m, typical tire rolling radius
@@ -192,6 +176,7 @@ class MQBStandstillManager:
self.prev_accel = accel
return long_active, accel, stopping, starting, esp_starting_override, esp_stopping_override
class CarController(CarControllerBase):
def __init__(self, dbc_names, CP, CP_IQ):
super().__init__(dbc_names, CP, CP_IQ)
@@ -200,11 +185,8 @@ class CarController(CarControllerBase):
self.CAN = CanBus(CP)
self.packer_pt = CANPacker(dbc_names[Bus.pt])
self._pt_tx_bus = self.CAN.pt
if CP.flags & VolkswagenFlags.PQ:
self.CCS = pqcan
if CP.flags & VolkswagenFlagsIQ.IQ_PQ_LOWLINE:
self._pt_tx_bus = self.CAN.aux
elif CP.flags & VolkswagenFlags.MLB:
self.CCS = mlbcan
elif CP.flags & (VolkswagenFlags.MEB | VolkswagenFlags.MQB_EVO):
@@ -229,16 +211,13 @@ class CarController(CarControllerBase):
self.long_jerk_control = LongControlJerk(dt=(DT_CTRL * self.CCP.ACC_CONTROL_STEP)) if self.CP.flags & (VolkswagenFlags.MEB | VolkswagenFlags.MQB_EVO) else None
self.long_limit_control = LongControlLimit(dt=(DT_CTRL * self.CCP.ACC_CONTROL_STEP)) if self.CP.flags & (VolkswagenFlags.MEB | VolkswagenFlags.MQB_EVO) else None
self.gra_acc_counter_last = None
self.motor3_frame_last = None
self.motor3_was_stopping = False
self.motor3_resuming = False
self.sng_handoff_active = False
self.acc_counter_seeded = False
self.klr_counter_last = None
self.eps_timer_soft_disable_alert = False
self.hca_frame_timer_running = 0
self.hca_frame_same_torque = 0
self.accel_last = 0
self.accel_diff = 0
self.long_deviation = 0
self.long_jerklimit = 0
self.HCA_Status = 3
@@ -252,43 +231,19 @@ class CarController(CarControllerBase):
self.radar_disabled_warning_timer = 0
# Check once at init whether the DBC includes MEB_AWV_01 (AEB HUD for radar-disabled camera harness cars)
self._has_aeb_hud_msg = "MEB_AWV_01" in self.packer_pt.dbc.name_to_msg
self.eps_timer_workaround = bool(CP.flags & (VolkswagenFlags.MLB | VolkswagenFlagsIQ.IQ_PQ_TIMEBOMB))
self._pq_patch_checked = not bool(CP.flags & VolkswagenFlags.PQ) or self.eps_timer_workaround
self.eps_timer_workaround = bool(CP.flags & VolkswagenFlags.MLB)
self.hca_frame_timer_resetting = 0
self.hca_frame_low_torque = 0
self.long_override_counter = 0
self.long_disabled_counter = 0
self.standstill_manager = MQBStandstillManager(CP.mass, self.CCP.ACCEL_MIN) if self.CCS == mqbcan else None
self.radar_handler = PQRadarHandler(self.CAN) if self.CCS is pqcan else None
self.blend_stock_radar = False
self.unavailable = False
self.unavailable_hold = 0
self.VM = VehicleModel(CP)
self.is_mqb_a0 = self._is_mqb_a0_car(CP.carFingerprint)
self.LateralController = (
LatControlCurvature(self.CCP.CURVATURE_PID, self.CCP.CURVATURE_LIMITS.CURVATURE_MAX, 1 / (DT_CTRL * self.CCP.STEER_STEP))
if (CP.flags & (VolkswagenFlags.MEB | VolkswagenFlags.MQB_EVO))
else None
)
@staticmethod
def _is_mqb_a0_car(candidate) -> bool:
return candidate in MQB_A0_CARS
def _get_mqb_steering_torque_scale(self, v_ego: float, enabled: bool) -> float:
if enabled and self.CCS == mqbcan:
return float(np.interp(v_ego, [0.4, 3.5, 4.0], [0.8, 0.95, 1.0]))
return 1.0
def _should_spam_mqb_a0_resume(self, CS, enabled: bool) -> bool:
return bool(
enabled and
self.is_mqb_a0 and
self.CCS == mqbcan and
CS.out.standstill and
self.frame % 50 < 15
)
def update(self, CC, CC_IQ, CS, now_nanos):
actuators = CC.actuators
hud_control = CC.hudControl
@@ -297,23 +252,10 @@ class CarController(CarControllerBase):
apply_torque = 0
pqhca5or7Toggle = self._params.get_bool("pqhca5or7Toggle")
eBrakeActive = self._params.get_bool("eBrakeActive")
iq_mqb_steering_lockout = self._params.get_bool("iqMqbSteeringLockout")
iq_mqb_acc_resume = self._params.get_bool("iqMqbAccResume")
self.blend_stock_radar = self._params.get_bool("IQDynamicBlendStockRadar")
if not self._pq_patch_checked:
self._pq_patch_checked = True
self.eps_timer_workaround = not self._params.get_bool("VwPqEpsPatched")
blend_active = bool(self.blend_stock_radar and self.radar_handler is not None and self.CP.openpilotLongitudinalControl)
AngleLateralControl = iq_lvbs_alc.angle_lateral_control_enabled(self, CS)
self.entering = CS.vw_iq_lvbs_alc_entering
self.active = CS.vw_iq_lvbs_alc_active
if hud_control.audibleAlert == AudibleAlert.refuse:
self.unavailable_hold = self.CCP.IQ_PQ_UNAVAILABLE_HUD_FRAMES
else:
self.unavailable_hold = max(0, self.unavailable_hold - 1)
self.unavailable = self.unavailable_hold > 0
CS.force_rhd_for_bsm = getattr(CC, "forceRHDForBSM", False)
CS.enable_predicative_speed_limit = getattr(CC.cruiseControl, "speedLimitPredicative", False)
CS.enable_pred_react_to_speed_limits = getattr(CC.cruiseControl, "speedLimitPredReactToSL", False)
@@ -354,8 +296,7 @@ class CarController(CarControllerBase):
self.steering_power_last = steering_power
else:
if CC.latActive and not AngleLateralControl:
torque_scale = self._get_mqb_steering_torque_scale(CS.out.vEgo, iq_mqb_steering_lockout)
new_torque = int(round(actuators.torque * self.CCP.STEER_MAX * torque_scale))
new_torque = int(round(actuators.torque * self.CCP.STEER_MAX))
apply_torque = apply_driver_steer_torque_limits(new_torque, self.apply_torque_last, CS.out.steeringTorque, self.CCP)
self.hca_frame_timer_running += self.CCP.STEER_STEP
if self.apply_torque_last == apply_torque:
@@ -400,10 +341,10 @@ class CarController(CarControllerBase):
self.eps_timer_soft_disable_alert = self.hca_frame_timer_running > self.CCP.STEER_TIME_ALERT / DT_CTRL
self.apply_torque_last = apply_torque
if not (AngleLateralControl and self.CCS in (mqbcan, pqcan, mlbcan)):
can_sends.append(self.CCS.create_hca_steering_control(self.packer_pt, self._pt_tx_bus, output_torque, self.HCA_Status))
if not (AngleLateralControl and self.CCS in (mqbcan, pqcan)):
can_sends.append(self.CCS.create_hca_steering_control(self.packer_pt, self.CAN.pt, output_torque, self.HCA_Status))
if self.CP.flags & VolkswagenFlags.STOCK_HCA_PRESENT and self.CCS == mqbcan:
if self.CP.flags & VolkswagenFlags.STOCK_HCA_PRESENT:
ea_simulated_torque = float(np.clip(apply_torque * 2, -self.CCP.STEER_MAX, self.CCP.STEER_MAX))
if abs(CS.out.steeringTorque) > abs(ea_simulated_torque):
ea_simulated_torque = CS.out.steeringTorque
@@ -438,7 +379,7 @@ class CarController(CarControllerBase):
if self.frame % self.CCP.ACC_CONTROL_STEP == 0 and self.CP.openpilotLongitudinalControl and not CS.out.radarDisableFailed:
stopping = actuators.longControlState == LongCtrlState.stopping
if self.CP.flags & (VolkswagenFlags.MEB | VolkswagenFlags.MQB_EVO):
starting = actuators.longControlState == LongCtrlState.pid and (CS.esp_hold_confirmation or CS.out.vEgo < 0.25)
starting = actuators.longControlState == LongCtrlState.starting and CS.out.vEgo <= self.CP.vEgoStarting
accel = float(np.clip(actuators.accel, self.CCP.ACCEL_MIN, self.CCP.ACCEL_MAX) if CC.enabled else 0)
long_override = CC.cruiseControl.override or CS.out.gasPressed
@@ -468,7 +409,7 @@ class CarController(CarControllerBase):
self.accel_last = accel
else:
stopping = actuators.longControlState == LongCtrlState.stopping
starting = actuators.longControlState == LongCtrlState.pid and (CS.esp_hold_confirmation or CS.out.vEgo < 0.25)
starting = actuators.longControlState == LongCtrlState.pid and (CS.esp_hold_confirmation or CS.out.vEgo < self.CP.vEgoStopping)
long_active = CC.longActive
accel = actuators.accel
esp_starting_override = None
@@ -483,16 +424,10 @@ class CarController(CarControllerBase):
acc_control = self.CCS.acc_control_value(CS.out.cruiseState.available, long_active, CC.cruiseControl.override, CS.out.accFaulted)
accel = float(np.clip(accel, self.CCP.ACCEL_MIN, self.CCP.ACCEL_MAX) if (long_active or CC.cruiseControl.override) else 0)
self.long_jerklimit = float(interp(accel, [-1.5, -0.3, 0.05], [2.0, 0.52, 0.30]))
self.long_deviation = float(interp(accel, [-0.3, 0.1], [0.0, 0.20]))
self.accel_diff = (0.0019 * (accel - self.accel_last)) + (1 - 0.0019) * self.accel_diff
self.long_jerklimit = 3.0 # AendGrad (0.01 * (np.clip(abs(accel), 0.7, 2))) + (1 - 0.01) * self.long_jerklimit
self.long_deviation = 0.2 # RegelAbw np.interp(abs(accel - self.accel_diff), [0, 0.3, 1.0], [0.02, 0.04, 0.08])
self.accel_last = accel
if blend_active and getattr(CC_IQ, "useRadarAccel", False) and CS.acc_radar_sta_adr == 1 and not self.radar_handler.failed:
accel = float(np.clip(CS.acc_radar_sollbeschl, self.CCP.ACCEL_MIN, self.CCP.ACCEL_MAX))
self.long_jerklimit = CS.acc_radar_aendgrad
self.long_deviation = CS.acc_radar_regelabw
self.accel_last = accel
if self.CCS == mqbcan:
can_sends.extend(self.CCS.create_acc_accel_control(
self.packer_pt, self.CAN.pt, CS.acc_type, accel, acc_control, stopping, starting, CS.esp_hold_confirmation,
@@ -500,19 +435,10 @@ class CarController(CarControllerBase):
esp_starting_override=esp_starting_override, esp_stopping_override=esp_stopping_override,
))
else:
accel = apply_pq_stopping_accel(self.CP.carFingerprint, accel, stopping)
sng_ecd_enabled = bool(self.CP.flags & VolkswagenFlagsIQ.IQ_PQ_SNG_ECD)
low_speed = CS.out.vEgo <= self.CCP.SNG_HANDOFF_SPEED
if sng_ecd_enabled and long_active and low_speed and accel <= 0.05 and not starting:
self.sng_handoff_active = True
else:
self.sng_handoff_active = False
sng_decel_req = float(np.clip(accel, self.CCP.ACCEL_MIN, self.CCP.SNG_HOLD_DECEL_MAX)) if self.sng_handoff_active else 0.0
can_sends.extend(self.CCS.create_acc_accel_control(self.packer_pt, self.CAN.pt, CS.acc_type, accel, acc_control, stopping and not self.motor3_resuming, starting, CS.esp_hold_confirmation, self.long_deviation, self.long_jerklimit, eBrakeActive, sng_active=self.sng_handoff_active))
if sng_ecd_enabled:
can_sends.append(self.CCS.create_sng_handoff_control(self.packer_pt, self.CAN.aux, self.sng_handoff_active, sng_decel_req))
can_sends.extend(self.CCS.create_acc_accel_control(
self.packer_pt, self.CAN.pt, CS.acc_type, accel, acc_control, stopping, starting, CS.esp_hold_confirmation,
self.long_deviation, self.long_jerklimit, eBrakeActive,
))
if (self.CP.flags & VolkswagenFlags.DISABLE_RADAR) and self.CP.openpilotLongitudinalControl and not CS.out.radarDisableFailed:
if self.CP.flags & (VolkswagenFlags.MEB | VolkswagenFlags.MQB_EVO):
@@ -542,16 +468,15 @@ class CarController(CarControllerBase):
can_sends.append(self.CCS.create_lka_hud_control(self.packer_pt, self.CAN.pt, CS.ldw_stock_values, CC.latActive, CS.out.steeringPressed,
hud_alert, hud_control, sound_alert))
else:
can_sends.append(self.CCS.create_lka_hud_control(self.packer_pt, self._pt_tx_bus, CS.ldw_stock_values, CC.latActive, steering_pressed_hud,
can_sends.append(self.CCS.create_lka_hud_control(self.packer_pt, self.CAN.pt, CS.ldw_stock_values, CC.latActive, steering_pressed_hud,
hud_alert, hud_control, self.entering, self.CCS is pqcan and AngleLateralControl, self.active))
if hud_control.leadDistanceBars != self.lead_distance_bars_last:
self.distance_bar_frame = self.frame
if self.frame % self.CCP.ACC_HUD_STEP == 0 and self.CP.openpilotLongitudinalControl:
fcw_alert = hud_control.visualAlert == VisualAlert.fcw
d_unresponsive = hud_control.driverUnresponsive
if self.CP.flags & (VolkswagenFlags.MEB | VolkswagenFlags.MQB_EVO):
fcw_alert = hud_control.visualAlert == VisualAlert.fcw
show_distance_bars = self.frame - self.distance_bar_frame < 400
gap = max(8, CS.out.vEgo * hud_control.leadFollowTime)
distance = max(8, hud_control.leadDistance) if hud_control.leadDistance != 0 else 0
@@ -573,29 +498,23 @@ class CarController(CarControllerBase):
CS.esp_hold_confirmation, distance, gap, fcw_alert, acc_hud_event, speed_limit))
else:
leadDistance = min(8, hud_control.leadDistance) if hud_control.leadDistance != 0 else 0
fcw_alert = hud_control.visualAlert == VisualAlert.fcw
self.leadDistanceBars = min(3, hud_control.leadDistanceBars)
acc_hud_status = self.CCS.acc_hud_status_value(CS.out.cruiseState.available, CS.out.accFaulted, CC.longActive, CC.cruiseControl.override)
set_speed = hud_control.setSpeed * CV.MS_TO_KPH
decel = dVisual(self.CCS, CS)
can_sends.append(self.CCS.create_acc_hud_control(self.packer_pt, self.CAN.pt, acc_hud_status, set_speed, leadDistance, self.leadDistanceBars, fcw_alert, hud_control.leadVisible, self.unavailable, decel, d_unresponsive))
can_sends.append(self.CCS.create_acc_hud_control(self.packer_pt, self.CAN.pt, acc_hud_status, set_speed,
leadDistance, self.leadDistanceBars, fcw_alert, hud_control.leadVisible))
if self.CP.flags & VolkswagenFlags.PQ:
iq_lvbs_commander.update_turn_signals(self, CC, CS, can_sends)
if self.frame % 2 == 0:
self.blinkerActive = CS.leftBlinkerUpdate or CS.rightBlinkerUpdate
leftBlinker = CC.leftBlinker if not self.blinkerActive else False
rightBlinker = CC.rightBlinker if not self.blinkerActive else False
can_sends.append(self.CCS.create_blinker_control(self.packer_pt, self.CAN.pt, leftBlinker, rightBlinker))
if self.CP.openpilotLongitudinalControl and (self.CP.flags & VolkswagenFlags.PQ):
if blend_active:
can_sends.extend(self.radar_handler.update(
self.packer_pt, self.frame, CS,
blend_active=True,
engage_req=getattr(CC_IQ, "radarEngageReq", False),
cancel_req=getattr(CC_IQ, "radarCancelReq", False),
set_speed_kph=getattr(CC_IQ, "radarSetSpeedKph", 0.0),
gap_bars=getattr(CC_IQ, "radarGapBars", 0),
v_ego=CS.out.vEgo,
))
elif self.frame % 2 == 0:
if self.frame % 2 == 0:
can_sends.append(self.CCS.filter_motor2(self.packer_pt, self.CAN.ext, CS.motor2_stock))
can_sends.append(self.CCS.filter_motor5(self.packer_pt, self.CAN.ext, CS.motor5_stock))
gra_send_ready = self.CP.pcmCruise and CS.gra_stock_values["COUNTER"] != self.gra_acc_counter_last
if self.CP.flags & (VolkswagenFlags.MEB | VolkswagenFlags.MQB_EVO):
@@ -607,25 +526,15 @@ class CarController(CarControllerBase):
stock_cancel_pressed = bool(CS.gra_stock_values["GRA_Abbrechen"])
cancel_cmd = stock_cancel_pressed or CC.cruiseControl.cancel
resume_cmd = CC.cruiseControl.resume or self._should_spam_mqb_a0_resume(CS, iq_mqb_acc_resume)
if gra_send_ready and (cancel_cmd or resume_cmd):
if gra_send_ready and (cancel_cmd or CC.cruiseControl.resume):
bus_send = self.CAN.aux if self.CP.flags & VolkswagenFlags.PQ else self.CAN.ext
can_sends.append(self.CCS.create_acc_buttons_control(self.packer_pt, bus_send, CS.gra_stock_values,
cancel=cancel_cmd, resume=resume_cmd))
cancel=cancel_cmd, resume=CC.cruiseControl.resume))
if self.CP.openpilotLongitudinalControl and self.CCS == pqcan and not blend_active:
if self.CP.openpilotLongitudinalControl and self.CCS == pqcan:
if self.frame % 3:
can_sends.append(self.CCS.create_gra_neu(self.packer_pt, self.CAN.ext, CS.gra_stock_values, CC.longActive))
if self.CP.flags & VolkswagenFlagsIQ.IQ_PQ_ACC_FTS_EPB:
is_stopping = actuators.longControlState == LongCtrlState.stopping
if CS.out.vEgo > 0.5 or not CC.longActive:
self.motor3_resuming = False
elif (self.motor3_was_stopping and not is_stopping and self.CP.openpilotLongitudinalControl and CC.longActive):
self.motor3_resuming = True
if self.motor3_resuming and CS.motor3_stock:
can_sends.append(self.CCS.create_motor3_resume(self.packer_pt, self.CAN.aux, CS.motor1_stock, CS.motor3_stock, resume=True))
self.motor3_was_stopping = is_stopping
new_actuators = actuators.as_builder()
new_actuators.torque = self.apply_torque_last / self.CCP.STEER_MAX
@@ -638,6 +547,5 @@ class CarController(CarControllerBase):
self.lead_distance_bars_last = hud_control.leadDistanceBars
self.gra_acc_counter_last = CS.gra_stock_values["COUNTER"]
self.motor3_frame_last = CS.motor3_frame
self.frame += 1
return new_actuators, can_sends
+28 -108
View File
@@ -12,10 +12,7 @@ from iqdbc.car.common.conversions import Conversions as CV
from iqdbc.car.volkswagen.values import CAR, DBC, CanBus, NetworkLocation, RADAR_DISABLE_STATE, TransmissionType, GearShifter, \
CarControllerParams, VolkswagenFlags, VolkswagenFlagsIQ
from iqdbc.car.volkswagen.speed_limit_manager import SpeedLimitManager
from openpilot.system.proprietary_runtime._verified_import import import_verified_module
iq_lvbs_alc = import_verified_module("iqpilot_alc_private", "iqpilot_private.konn3kt.iqlvbs.alc")
vehicle_state = import_verified_module("iqpilot_alc_private", "iqpilot_private.konn3kt.iqlvbs.vehicle_state")
from openpilot.iqpilot.konn3kt.iqlvbs import alc as iq_lvbs_alc
iqpilot_path = os.path.join(os.path.dirname(__file__), '..', '..', '..')
sys.path.insert(0, iqpilot_path)
@@ -48,14 +45,6 @@ class CarState(CarStateBase):
self.ea_hud_stock_values = {}
self.ea_control_stock_values = {}
self.acc_type = 0
self.acc_radar_sollbeschl = 0.0
self.acc_radar_regelabw = 0.0
self.acc_radar_aendgrad = 0.0
self.acc_radar_sta_adr = 0
self.acc_radar_fehler = False
self.acc_radar_v_wunsch = 0.0
self.acc_radar_sta_acc = 0
self.epb_freigabe_ver = False
self.acc_stock_counters: dict[str, int] = {}
self.esp_stopping = False
self.tsk_brake_torque = 0.0
@@ -96,20 +85,7 @@ class CarState(CarStateBase):
self.PQ_ALC_Status_raw = 0
self.alcOverrideAlert = False
self.bremse8_stock = None
self.br8_acc_anf = False
self.tsk_verzoeg_anf = False
self.cruise_main_switch = False
self.motor3_stock = {}
self.motor1_stock = {}
self.motor3_frame = 0
self.motor1_frame = 0
self._odometer_store = vehicle_state.VehicleOdometerStore(CP, self._params)
def _update_odometer(self, ret: structs.CarState, raw_km: float) -> None:
"""Publish the cluster value while proprietary Konn3kt code owns persistence."""
odometer_km = self._odometer_store.record(raw_km)
if odometer_km is not None:
ret.odometer = odometer_km
def _apply_iq_private_flags(self, ret_iq: structs.IQCarState) -> None:
ret_iq.alcOverrideAlert = bool(self.alcOverrideAlert)
@@ -141,9 +117,6 @@ class CarState(CarStateBase):
def _update_mqb_iq_alc_state(self, pt_cp):
iq_lvbs_alc.update_mqb_carstate_alc_state(self, pt_cp)
def _update_mlb_iq_alc_state(self, pt_cp):
iq_lvbs_alc.update_mlb_carstate_alc_state(self, pt_cp)
def _update_pq_iq_alc_state(self, pt_cp):
iq_lvbs_alc.update_pq_carstate_alc_state(self, pt_cp)
@@ -153,8 +126,7 @@ class CarState(CarStateBase):
ext_cp = pt_cp if self.CP.networkLocation == NetworkLocation.fwdCamera else cam_cp
if self.CP.flags & VolkswagenFlags.PQ:
aux_cp = can_parsers.get(Bus.aux)
return self.update_pq(pt_cp, cam_cp, ext_cp, aux_cp)
return self.update_pq(pt_cp, cam_cp, ext_cp)
elif self.CP.flags & VolkswagenFlags.MLB:
br_cp = can_parsers[Bus.aux]
return self.update_mlb(pt_cp, br_cp, cam_cp, ext_cp)
@@ -246,7 +218,6 @@ class CarState(CarStateBase):
self.grade = pt_cp.vl["Motor_16"]["TSK_Steigung"]
acc_limiter_mode = False if cc_only else ext_cp.vl["ACC_02"]["ACC_Gesetzte_Zeitluecke"] == 0
speed_limiter_mode = bool(pt_cp.vl["TSK_06"]["TSK_Limiter_ausgewaehlt"])
self.tsk_verzoeg_anf = bool(pt_cp.vl["TSK_06"]["TSK_Freig_Verzoeg_Anf"])
self._update_mqb_iq_alc_state(pt_cp)
@@ -285,15 +256,12 @@ class CarState(CarStateBase):
ret.buttonEvents = self.create_button_events(pt_cp, self.CCP.BUTTONS)
ret.lowSpeedAlert = self.update_low_speed_alert(ret.vEgo)
allow_lat_only = self._params.get_bool("AllowLateralWhenLongUnavailable") and self._params.get_bool("AolEnabled")
cruise_main_switch = bool(self.gra_stock_values["GRA_Hauptschalter"])
ret.cruiseFaultLateralMode = allow_lat_only and ret.accFaulted and cruise_main_switch
ret.lateralAvailable = ret.cruiseState.available or ret.cruiseFaultLateralMode
ret.blockPcmEnable = ret.cruiseFaultLateralMode
ret.cruiseFaultLateralMode = False
ret.lateralAvailable = ret.cruiseState.available
ret.blockPcmEnable = False
ret.fuelGauge = pt_cp.vl["Kombi_02"]["KBI_Inhalt_Tank"] / 55.0
ret.fuelTankLevelL = pt_cp.vl["Kombi_02"]["KBI_Inhalt_Tank"] # raw liters for konn3kt
self._update_odometer(ret, aux_cp.vl["Kombi_02"]["KBI_Kilometerstand"])
self.cruise_faulted = ret.accFaulted
self._apply_iq_private_flags(ret_iq)
@@ -409,7 +377,6 @@ class CarState(CarStateBase):
psd_06_values = main_cp.vl["PSD_06"] if self.CP.flags & VolkswagenFlags.STOCK_PSD_PRESENT else {}
psd_06_values = pt_cp.vl["PSD_06"] if not psd_06_values and self.CP.flags & VolkswagenFlags.STOCK_PSD_06_PRESENT else psd_06_values
diagnose_01_values = pt_cp.vl["Diagnose_01"] if self.CP.flags & VolkswagenFlags.STOCK_DIAGNOSE_01_PRESENT else {}
self._update_odometer(ret, pt_cp.vl["Diagnose_01"]["KBI_Kilometerstand"])
if self.enable_speed_limit_predicative and not self.enable_predicative_speed_limit:
self.enable_predicative_speed_limit = True
@@ -434,8 +401,10 @@ class CarState(CarStateBase):
ret.espDisabled = bool(pt_cp.vl["ESP_21"]["ESP_Tastung_passiv"])
ret.espActive = bool(pt_cp.vl["ESP_21"]["ESP_Eingriff"])
allow_lat_only = self._params.get_bool("AllowLateralWhenLongUnavailable") and self._params.get_bool("AolEnabled")
cruise_fault_candidate = allow_lat_only and ret.accFaulted and bool(pt_cp.vl["GRA_ACC_01"]["GRA_Hauptschalter"])
allow_lat_only = self._params.get_bool("AllowLateralWhenLongUnavailable")
cruise_main_switch = bool(pt_cp.vl["GRA_ACC_01"]["GRA_Hauptschalter"])
cruise_fault_candidate = allow_lat_only and ret.accFaulted and cruise_main_switch
if cruise_fault_candidate:
self.cruise_faulted_frames += 1
self.cruise_fault_clear_frames = 0
@@ -449,10 +418,12 @@ class CarState(CarStateBase):
self.cruise_fault_lateral_active = False
else:
self.cruise_fault_clear_frames = 0
if not allow_lat_only:
self.cruise_fault_lateral_active = False
self.cruise_faulted_frames = 0
self.cruise_fault_clear_frames = 0
ret.cruiseFaultLateralMode = self.cruise_fault_lateral_active
ret.lateralAvailable = ret.cruiseState.available or ret.cruiseFaultLateralMode
ret.blockPcmEnable = ret.cruiseFaultLateralMode
@@ -482,10 +453,9 @@ class CarState(CarStateBase):
self._apply_iq_private_flags(ret_iq)
return ret, ret_iq
def update_pq(self, pt_cp, cam_cp, ext_cp, aux_cp=None) -> tuple[structs.CarState, structs.IQCarState]:
def update_pq(self, pt_cp, cam_cp, ext_cp) -> tuple[structs.CarState, structs.IQCarState]:
ret = structs.CarState()
ret_iq = structs.IQCarState()
sng_ecd_enabled = bool(self.CP.flags & VolkswagenFlagsIQ.IQ_PQ_SNG_ECD)
# vEgo obtained from Bremse_1 vehicle speed rather than Bremse_3 wheel speeds because Bremse_3 isn't present on NSF
ret.vEgoRaw = pt_cp.vl["Bremse_1"]["BR1_Rad_kmh"] * CV.KPH_TO_MS
@@ -498,13 +468,13 @@ class CarState(CarStateBase):
ret.steeringTorque = pt_cp.vl["Lenkhilfe_3"]["LH3_LM"] * (1, -1)[int(pt_cp.vl["Lenkhilfe_3"]["LH3_LMSign"])]
ret.steeringPressed = abs(ret.steeringTorque) > self.CCP.STEER_DRIVER_ALLOWANCE
hca_status = self.CCP.hca_status_values.get(pt_cp.vl["Lenkhilfe_2"]["LH2_Sta_HCA"])
ret.steerFaultTemporary, ret.steerFaultPermanent = self.update_hca_state(hca_status, ready_confirms_init=False)
ret.steerFaultTemporary, ret.steerFaultPermanent = self.update_hca_state(hca_status)
# Update gas, brakes, and gearshift.
ret.gasPressed = pt_cp.vl["Motor_3"]["MO3_Pedalwert"] > 0
ret.brake = pt_cp.vl["Bremse_5"]["BR5_Bremsdruck"] / 250.0 # FIXME: this is pressure in Bar, not sure what OP expects
ret.brakePressed = bool(pt_cp.vl["Motor_2"]["MO2_BLS"])
ret.parkingBrake = False # bool(pt_cp.vl["Kombi_1"]["Bremsinfo"])
ret.parkingBrake = bool(pt_cp.vl["Kombi_1"]["Bremsinfo"])
# Update gear and/or clutch position data.
if self.CP.transmissionType == TransmissionType.automatic:
@@ -533,11 +503,8 @@ class CarState(CarStateBase):
# Consume blind-spot monitoring info/warning LED states, if available.
# Infostufe: BSM LED on, Warnung: BSM LED flashing
if self.CP.enableBsm:
blindspot_li = bool(ext_cp.vl["SWA_1"]["SWA_Infostufe_SWA_li"]) or bool(ext_cp.vl["SWA_1"]["SWA_Warnung_SWA_li"])
blindspot_re = bool(ext_cp.vl["SWA_1"]["SWA_Infostufe_SWA_re"]) or bool(ext_cp.vl["SWA_1"]["SWA_Warnung_SWA_re"])
force_rhd = self.force_rhd_for_bsm or self._params.get_bool("ForceRHDForBSM")
ret.leftBlindspot = blindspot_re if force_rhd else blindspot_li
ret.rightBlindspot = blindspot_li if force_rhd else blindspot_re
ret.leftBlindspot = bool(ext_cp.vl["SWA_1"]["SWA_Infostufe_SWA_li"]) or bool(ext_cp.vl["SWA_1"]["SWA_Warnung_SWA_li"])
ret.rightBlindspot = bool(ext_cp.vl["SWA_1"]["SWA_Infostufe_SWA_re"]) or bool(ext_cp.vl["SWA_1"]["SWA_Warnung_SWA_re"])
# Consume factory LDW data relevant for factory SWA (Lane Change Assist)
# and capture it for forwarding to the blind spot radar controller
@@ -557,18 +524,16 @@ class CarState(CarStateBase):
# Update ACC radar status.
self.acc_type = 0 if cc_only else ext_cp.vl["ACC_System"]["ACS_Typ_ACC"]
cruise_main_switch = bool(pt_cp.vl["Motor_5"]["MO5_GRA_Hauptsch"])
self.cruise_main_switch = cruise_main_switch
cruise_tsk_status = bool(pt_cp.vl["Motor_2"]["MO2_Status_TSK"])
self.cruise_main_switch = cruise_main_switch or cruise_tsk_status
MO2_StaGRA = pt_cp.vl["Motor_2"]["MO2_Sta_GRA"] in (1, 2)
ACS_StaADR = False if cc_only else ext_cp.vl["ACC_System"]["ACS_Sta_ADR"] == 1
cruiseActive = MO2_StaGRA or ACS_StaADR
self.epb_freigabe_ver = bool(aux_cp.vl["EPB_1"]["EP1_Freigabe_Ver"]) if sng_ecd_enabled and not cc_only else False
sng_holding = sng_ecd_enabled and self.epb_freigabe_ver
if cruiseActive or sng_holding:
if cruiseActive:
self.last_cruiseActive = True
elif not MO2_StaGRA and not ACS_StaADR and not sng_holding:
elif not MO2_StaGRA and not ACS_StaADR:
self.last_cruiseActive = False
ret.cruiseState.enabled = self.last_cruiseActive
self.br8_acc_anf = bool(pt_cp.vl["Bremse_8"]["BR8_Sta_ACC_Anf"]) if not cc_only else False
if self.CP.pcmCruise:
if cc_only:
@@ -579,9 +544,9 @@ class CarState(CarStateBase):
cruise_faulted = pt_cp.vl["Motor_2"]["MO2_Sta_GRA"] == 3
ret.accFaulted = cruise_faulted
ret.cruiseState.available = cruise_main_switch and not cruise_faulted
ret.cruiseState.available = (cruise_main_switch or cruise_tsk_status) and not cruise_faulted
allow_lat_only = self._params.get_bool("AllowLateralWhenLongUnavailable") and self._params.get_bool("AolEnabled")
cruise_main_available = cruise_main_switch
cruise_main_available = cruise_main_switch or cruise_tsk_status
ret.cruiseFaultLateralMode = allow_lat_only and cruise_faulted and cruise_main_available
ret.lateralAvailable = ret.cruiseState.available or ret.cruiseFaultLateralMode
ret.blockPcmEnable = ret.cruiseFaultLateralMode
@@ -598,33 +563,6 @@ class CarState(CarStateBase):
ret.cruiseState.speed = 0
self.motor2_stock = pt_cp.vl["Motor_2"]
self.motor5_stock = pt_cp.vl["Motor_5"]
if self.CP.flags & VolkswagenFlagsIQ.IQ_PQ_ACC_FTS_EPB:
self.motor3_stock = aux_cp.vl["Motor_3"]
self.motor1_stock = aux_cp.vl["Motor_1"]
self.motor3_frame += 1
self.motor1_frame += 1
if cc_only:
self.acc_radar_sollbeschl = 0.0
self.acc_radar_regelabw = 0.0
self.acc_radar_aendgrad = 0.0
self.acc_radar_sta_adr = 0
self.acc_radar_fehler = False
self.acc_radar_v_wunsch = 0.0
self.acc_radar_sta_acc = 0
else:
self.acc_radar_sollbeschl = ext_cp.vl["ACC_System"]["ACS_Sollbeschl"]
self.acc_radar_regelabw = ext_cp.vl["ACC_System"]["ACS_zul_Regelabw"]
self.acc_radar_aendgrad = ext_cp.vl["ACC_System"]["ACS_max_AendGrad"]
self.acc_radar_sta_adr = int(ext_cp.vl["ACC_System"]["ACS_Sta_ADR"])
self.acc_radar_fehler = bool(ext_cp.vl["ACC_System"]["ACS_Fehler"])
self.acc_radar_v_wunsch = ext_cp.vl["ACC_GRA_Anzeige"]["ACA_V_Wunsch"]
self.acc_radar_sta_acc = int(ext_cp.vl["ACC_GRA_Anzeige"]["ACA_StaACC"])
ret_iq.accRadarStaAdr = self.acc_radar_sta_adr
ret_iq.accRadarFehler = self.acc_radar_fehler
# Update button states for turn signals and ACC controls, capture all ACC button state/config for passthrough
ret.leftBlinker, ret.rightBlinker = self.update_blinker_from_stalk(300, pt_cp.vl["Gate_Komf_1"]["GK1_Blinker_li"],
@@ -639,15 +577,8 @@ class CarState(CarStateBase):
ret.lowSpeedAlert = self.update_low_speed_alert(ret.vEgo)
ret.fuelGauge = 0 # pt_cp.vl["Kombi_1"]["Tankinhalt"] / 55.0
ret.fuelTankLevelL = 0 # pt_cp.vl["Kombi_1"]["Tankinhalt"] # raw liters for konn3kt
if aux_cp is not None:
self._update_odometer(ret, aux_cp.vl["Kombi_3"]["Kilometerstand"])
if self.CP.flags & VolkswagenFlagsIQ.IQ_PQ_ACC_FTS_EPB:
ret.cruiseState.standstill = self.CP.pcmCruise and bool(pt_cp.vl["Bremse_5"]["BR5_Stillstand"]) and ret.cruiseState.enabled
elif sng_ecd_enabled:
ret.cruiseState.standstill = sng_holding and ret.standstill
ret.fuelGauge = pt_cp.vl["Kombi_1"]["Tankinhalt"] / 55.0
ret.fuelTankLevelL = pt_cp.vl["Kombi_1"]["Tankinhalt"] # raw liters for konn3kt
self.cruise_faulted = ret.accFaulted
self.frame += 1
@@ -684,7 +615,6 @@ class CarState(CarStateBase):
ret.accFaulted = pt_cp.vl["TSK_02"]["TSK_Status"] in (3,)
self.parse_mlb_mqb_steering_state(ret, pt_cp)
self._update_mlb_iq_alc_state(pt_cp)
ret.brake = pt_cp.vl["ESP_05"]["ESP_Bremsdruck"] / 250.0
brake_pedal_pressed = bool(pt_cp.vl["Motor_03"]["MO_Fahrer_bremst"])
@@ -718,7 +648,6 @@ class CarState(CarStateBase):
ret.fuelGauge = br_cp.vl["Kombi_02"]["KBI_Inhalt_Tank"] / 55.0
ret.fuelTankLevelL = br_cp.vl["Kombi_02"]["KBI_Inhalt_Tank"] # raw liters for konn3kt
self._update_odometer(ret, br_cp.vl["Kombi_02"]["KBI_Kilometerstand"])
ret.buttonEvents = self.create_button_events(pt_cp, self.CCP.BUTTONS)
@@ -751,11 +680,10 @@ class CarState(CarStateBase):
ret.steerFaultTemporary, ret.steerFaultPermanent = self.update_hca_state(hca_status, drive_mode)
return
def update_hca_state(self, hca_status, drive_mode=True, ready_confirms_init=True):
def update_hca_state(self, hca_status, drive_mode=True):
# Treat FAULT as temporary for worst likely EPS recovery time, for cars without factory Lane Assist
# DISABLED means the EPS hasn't been configured to support Lane Assist
init_statuses = ("DISABLED", "READY", "ACTIVE") if ready_confirms_init else ("DISABLED", "ACTIVE")
self.eps_init_complete = self.eps_init_complete or hca_status in init_statuses or self.frame > 1000
self.eps_init_complete = self.eps_init_complete or (hca_status in ("DISABLED", "READY", "ACTIVE") or self.frame > 600)
perm_fault = drive_mode and hca_status == "DISABLED" or (self.eps_init_complete and hca_status == "FAULT")
temp_fault = drive_mode and hca_status in ("REJECTED", "PREEMPTED") or not self.eps_init_complete
return temp_fault, perm_fault
@@ -807,11 +735,8 @@ class CarState(CarStateBase):
]
if CP.flags & VolkswagenFlags.MLB:
pt_messages += [
("Blinkmodi_01", math.nan), # From J519 BCM (is inactive when no lights active, 50Hz when active)
("Kombi_02", math.nan), # Auxiliary-bus cluster odometer
("Blinkmodi_01", math.nan) # From J519 BCM (is inactive when no lights active, 50Hz when active)
]
else:
pt_messages += [("Kombi_02", math.nan)] # Auxiliary-bus cluster odometer
if CP.flags & VolkswagenFlags.STOCK_HCA_PRESENT:
cam_messages += [
("HCA_01", 1), # From R242 Driver assistance camera, 50Hz if steering/1Hz if not
@@ -825,12 +750,8 @@ class CarState(CarStateBase):
@staticmethod
def get_can_parsers_pq(CP):
aux_messages = [("Kombi_3", math.nan)] # Bus 1 cluster odometer
if CP.flags & VolkswagenFlagsIQ.IQ_PQ_ACC_FTS_EPB:
aux_messages.append(("Motor_3", 0))
return {
Bus.pt: CANParser(DBC[CP.carFingerprint][Bus.pt], [], CanBus(CP).powertrain),
Bus.aux: CANParser(DBC[CP.carFingerprint][Bus.pt], aux_messages, CanBus(CP).aux),
Bus.pt: CANParser(DBC[CP.carFingerprint][Bus.pt], [], CanBus(CP).pt),
Bus.cam: CANParser(DBC[CP.carFingerprint][Bus.pt], [], CanBus(CP).cam),
}
@@ -842,7 +763,6 @@ class CarState(CarStateBase):
# TA_01 lives on bus 0 (car ECU / OP-generated when long is active).
# math.nan → ignore_alive=True so it never contributes to can_valid.
("TA_01", math.nan),
("Diagnose_01", math.nan), # Bus 0 cluster odometer
]
if CP.networkLocation == NetworkLocation.fwdCamera:
pt_messages.append(("AWV_03", 1))
@@ -681,14 +681,6 @@ FW_VERSIONS = {
b'\xf1\x873QF907572A \xf1\x890132',
],
},
CAR.VOLKSWAGEN_PASSAT_B7: {
(Ecu.engine, 0x7e0, None): [
b'\xf1\x8703L906018RE\xf1\x899979',
],
(Ecu.fwdCamera, 0x74f, None): [
b'\xf1\x873AA980654D \xf1\x890300\xf1\x82\x0143',
],
},
CAR.VOLKSWAGEN_POLO_MK6: {
(Ecu.engine, 0x7e0, None): [
b'\xf1\x8704C906025H \xf1\x895177',
@@ -724,17 +716,6 @@ FW_VERSIONS = {
b'\xf1\x877N0907572C \xf1\x890211\xf1\x82\x0153',
],
},
CAR.SEAT_ALHAMBRA_MK1: {
(Ecu.engine, 0x7e0, None): [
b'\xf1\x8704L906016HE\xf1\x894635',
],
(Ecu.srs, 0x715, None): [
b'\xf1\x877N0959655D \xf1\x890016\xf1\x82\x0801100705----10--',
],
(Ecu.fwdRadar, 0x757, None): [
b'\xf1\x877N0907572C \xf1\x890211\xf1\x82\x0153',
],
},
CAR.VOLKSWAGEN_TAOS_MK1: {
(Ecu.engine, 0x7e0, None): [
b'\xf1\x8704E906025CK\xf1\x892228',
+23 -48
View File
@@ -1,16 +1,15 @@
import time
from iqdbc.car.common.conversions import Conversions as CV
from iqdbc.car import get_safety_config, structs, uds
from iqdbc.car.carlog import carlog
from iqdbc.car.interfaces import CarInterfaceBase
from iqdbc.car.isotp_parallel_query import IsoTpParallelQuery
from iqdbc.car.volkswagen.carcontroller import CarController
from iqdbc.car.volkswagen.carstate import CarState
from iqdbc.car.volkswagen.values import (
CAR, CanBus, DashcamOnlyReason, NetworkLocation, RADAR_DISABLE_STATE, TransmissionType,
VolkswagenFlags, VolkswagenSafetyFlags, VolkswagenFlagsIQ, get_longitudinal_stopping_speed_override,
)
from iqdbc.car.volkswagen.values import CanBus, CAR, DashcamOnlyReason, NetworkLocation, RADAR_DISABLE_STATE, TransmissionType, VolkswagenFlags, VolkswagenSafetyFlags, VolkswagenFlagsIQ
from iqdbc.car.volkswagen.radar_interface import RadarInterface
from iqdbc.car.common.conversions import Conversions as CV
import sys
import os
iqpilot_path = os.path.join(os.path.dirname(__file__), '..', '..', '..')
@@ -20,11 +19,6 @@ try:
except ImportError:
pass
try:
from openpilot.system.proprietary_runtime._verified_import import import_verified_module
except Exception:
import_verified_module = None
class CarInterface(CarInterfaceBase):
CarState = CarState
@@ -45,16 +39,9 @@ class CarInterface(CarInterfaceBase):
if ret.flags & VolkswagenFlags.PQ:
# Set global PQ35/PQ46/NMS parameters
safety_configs = [get_safety_config(structs.CarParams.SafetyModel.volkswagenPq)]
if candidate == CAR.SEAT_ALHAMBRA_MK1:
ret.flags |= VolkswagenFlagsIQ.IQ_PQ_TIMEBOMB.value
if not (ret.flags & VolkswagenFlagsIQ.IQ_PQ_TIMEBOMB) and _params.get_bool("VwPqEpsPatched"):
ret.minSteerSpeed = 0
if angle_lat_enabled:
ret.flags |= VolkswagenFlagsIQ.IQ_LVBS_ALC_MODULE.value
safety_configs[0].safetyParam |= VolkswagenSafetyFlags.PQ_ALC_MODULE.value
if alpha_long:
ret.flags |= VolkswagenFlagsIQ.IQ_PQ_SNG_ECD.value
safety_configs[0].safetyParam |= VolkswagenSafetyFlags.PQ_SNG_ECD.value
ret.enableBsm = 0x3BA in fingerprint[0] # SWA_1
if 0x440 in fingerprint[0] or docs: # Getriebe_1
@@ -72,13 +59,6 @@ class CarInterface(CarInterfaceBase):
else:
ret.flags |= VolkswagenFlagsIQ.IQ_CC_ONLY_NO_RADAR.value
cc_only_flags = VolkswagenFlagsIQ.IQ_CC_ONLY | VolkswagenFlagsIQ.IQ_CC_ONLY_NO_RADAR
if ret.flags & cc_only_flags:
safety_configs[0].safetyParam |= VolkswagenSafetyFlags.PQ_NO_CAM_BUS.value
if (ret.flags & cc_only_flags) and not fingerprint[0]:
ret.flags |= VolkswagenFlagsIQ.IQ_PQ_LOWLINE.value
safety_configs[0].safetyParam |= VolkswagenSafetyFlags.PQ_LOWLINE.value
if any(msg in fingerprint[1] for msg in (0x1A0, 0xC2)): # Bremse_1, Lenkwinkel_1
ret.networkLocation = NetworkLocation.gateway
else:
@@ -184,20 +164,19 @@ class CarInterface(CarInterfaceBase):
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
elif ret.flags & VolkswagenFlags.MLB:
ret.steerActuatorDelay = 0.2
if angle_lat_enabled:
ret.steerControlType = structs.CarParams.SteerControlType.angle
ret.steerAtStandstill = bool(joystick_mode)
else:
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
elif ret.flags & (VolkswagenFlags.MEB | VolkswagenFlags.MQB_EVO):
ret.steerActuatorDelay = 0.3
else:
ret.steerActuatorDelay = 0.1
ret.lateralTuning.pid.kpBP = [0.]
ret.lateralTuning.pid.kiBP = [0.]
ret.lateralTuning.pid.kf = 0.00006
ret.lateralTuning.pid.kpV = [0.6]
ret.lateralTuning.pid.kiV = [0.2]
if angle_lat_enabled:
ret.steerControlType = structs.CarParams.SteerControlType.angle
ret.steerAtStandstill = bool(joystick_mode)
else:
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
# Global longitudinal tuning defaults, can be overridden per-vehicle
@@ -221,12 +200,17 @@ class CarInterface(CarInterfaceBase):
if candidate == CAR.PORSCHE_MACAN_MK1:
ret.steerActuatorDelay = 0.07
if candidate == CAR.VOLKSWAGEN_PASSAT_B7 or CAR.SEAT_ALHAMBRA_MK1:
ret.flags |= VolkswagenFlagsIQ.IQ_PQ_ACC_FTS_EPB.value
safety_configs[0].safetyParam |= VolkswagenSafetyFlags.PQ_ACC_FTS_EPB.value
ret.pcmCruise = not ret.openpilotLongitudinalControl
ret.stopAccel = -0.55
if ret.flags & (VolkswagenFlags.MEB | VolkswagenFlags.MQB_EVO):
ret.startingState = True
ret.startAccel = 0.8
ret.vEgoStarting = 0.5
ret.vEgoStopping = 0.1
ret.stopAccel = -0.55
else:
ret.stopAccel = -0.55
ret.vEgoStarting = 0.1
ret.vEgoStopping = 1.5 * CV.KPH_TO_MS if ret.flags & VolkswagenFlags.PQ else 0.1
ret.autoResumeSng = ret.minEnableSpeed == -1
CAN = CanBus(fingerprint=fingerprint)
if CAN.pt >= 4:
@@ -237,18 +221,10 @@ class CarInterface(CarInterfaceBase):
@staticmethod
def pre_init(CP: structs.CarParams, CP_IQ: structs.IQCarParams, can_recv, can_send):
if not (CP.flags & VolkswagenFlags.PQ) or (CP.flags & VolkswagenFlagsIQ.IQ_PQ_TIMEBOMB) or import_verified_module is None:
return
try:
params = Params()
if params.get_bool("VwPqEpsPatched"):
return
flasher = import_verified_module("iqpilot_hephaestusd_private", "iqpilot_private.konn3kt.hephaestus.vw_pq_flasher")
status = flasher.check_eps_patch_status(1, can_recv, can_send)
except Exception:
return
if status == "patched":
params.put_bool("VwPqEpsPatched", True)
# Engine-on check moved to init(): if radar can't be disabled, radarDisableFailed=True
# gates only long control (carcontroller line ~308) while lateral still works.
# Full dashcam mode here was too aggressive — lateral doesn't need radar disabled.
pass
@staticmethod
def init(CP: structs.CarParams, CP_IQ: structs.IQCarParams, can_recv, can_send):
@@ -345,5 +321,4 @@ class CarInterface(CarInterfaceBase):
@staticmethod
def _get_params_iq(stock_cp: structs.CarParams, ret: structs.IQCarParams, candidate, fingerprint: dict[int, dict[int, int]], car_fw: list[structs.CarParams.CarFw], alpha_long: bool, is_release_iq: bool, docs: bool) -> structs.IQCarParams:
ret.longitudinalStoppingSpeedOverride = get_longitudinal_stopping_speed_override(candidate, stock_cp.flags)
return ret
+1 -1
View File
@@ -28,7 +28,7 @@ def create_steering_control(packer, bus, apply_curvature, lkas_enabled, power):
values = {
"Curvature": abs(apply_curvature), # in rad/m
"Curvature_VZ": 1 if apply_curvature > 0 and lkas_enabled else 0,
"Power": power if lkas_enabled else 0,
"Power": 100 if lkas_enabled else 0, # TEST: hard 100%, no ramp
"RequestStatus": 4 if lkas_enabled else 2,
"HighSendRate": lkas_enabled,
}
+1 -1
View File
@@ -270,5 +270,5 @@ class LatControlCurvature():
error = desired_curvature - actual_curvature
freeze_integrator = CC.steerLimited or CS.vEgo < 5
output_curvature = self.pid.update(error, feedforward=desired_curvature_corr, speed=CS.vEgo,
freeze_integrator=freeze_integrator, override=CS.steeringPressed)
freeze_integrator=freeze_integrator, override=False)
return output_curvature
+1 -1
View File
@@ -45,7 +45,7 @@ def create_acc_accel_control(packer, bus, acc_type, accel, acc_control, stopping
return [packer.make_can_msg("ACC_05", bus, values)]
def create_acc_hud_control(packer, bus, acc_hud_status, set_speed, leadDistance, distanceBars, fcw_alert, leadVisible, unavailable, decel, d_unresponsive):
def create_acc_hud_control(packer, bus, acc_hud_status, set_speed, leadDistance, distanceBars, fcw_alert, leadVisible):
values = {}
return packer.make_can_msg("ACC_02", bus, values)
+7 -11
View File
@@ -114,10 +114,10 @@ def create_acc_accel_control(packer, bus, acc_type, accel, acc_control, stopping
"ACC_Status_ACC": acc_control,
"ACC_StartStopp_Info": acc_enabled,
"ACC_Sollbeschleunigung_02": accel if acc_enabled else 3.01,
"ACC_zul_Regelabw_unten": 0.2,
"ACC_zul_Regelabw_oben": 0.2,
"ACC_neg_Sollbeschl_Grad_02": 3.0 if acc_enabled else 0,
"ACC_pos_Sollbeschl_Grad_02": 3.0 if acc_enabled else 0,
"ACC_zul_Regelabw_unten": comfortBand if acc_enabled else 0.2,
"ACC_zul_Regelabw_oben": comfortBand if acc_enabled else 0.2,
"ACC_neg_Sollbeschl_Grad_02": jerkLimit if acc_enabled else 0,
"ACC_pos_Sollbeschl_Grad_02": jerkLimit if acc_enabled else 0,
"ACC_Anfahren": starting if acc_enabled else False,
"ACC_Anhalten": stopping if acc_enabled else False,
}
@@ -149,16 +149,12 @@ def create_acc_accel_control(packer, bus, acc_type, accel, acc_control, stopping
return commands
def create_acc_hud_control(packer, bus, acc_hud_status, set_speed, leadDistance, distanceBars, fcw_alert, leadVisible, unavailable, decel, d_unresponsive):
priodisp = 0 if fcw_alert else 1 if (acc_hud_status == 4 or decel or leadVisible) else 2 if (acc_hud_status in (3, 2)) else 0
leadDistanceBars = distanceBars + 1 if distanceBars in (1, 2, 3) else 2
def create_acc_hud_control(packer, bus, acc_hud_status, set_speed, leadDistance, distanceBars, fcw_alert, leadVisible):
values = {
"ACC_Status_Anzeige": acc_hud_status,
"ACC_Wunschgeschw_02": set_speed if set_speed < 250 else 327.36,
"ACC_Gesetzte_Zeitluecke": leadDistanceBars,
"ACC_Optischer_Fahrerhinweis": 1 if fcw_alert else 0,
"ACC_Display_Prio": priodisp,
"ACC_Relevantes_Objekt": leadDistanceBars,
"ACC_Gesetzte_Zeitluecke": distanceBars,
"ACC_Display_Prio": 3,
"ACC_Abstandsindex": leadDistance if leadVisible else 0,
"ACC_Akustik_02": fcw_alert,
}
@@ -1,106 +0,0 @@
"""
Copyright © IQ.Lvbs, apart of Project Teal Lvbs, All Rights Reserved, licensed under https://konn3kt.com/tos/
"""
from iqdbc.car.common.conversions import Conversions as CV
from iqdbc.car.volkswagen import pqcan
class PQRadarHandler:
GRA_STEP = 3 # GRA_Neu cadence (~33 Hz), matches stock stalk module
SPOOF_STEP = 2 # Motor_2 / Motor_5 spoof cadence (50 Hz)
ENGAGE_FLOOR = 1.0 * CV.KPH_TO_MS # never SET at/below 1 kph
REENGAGE_FLOOR = 2.0 * CV.KPH_TO_MS # hysteresis: only (re)engage above 2 kph
SETSPEED_TOL_KPH = 1.0 # don't chase set-speed within this band
SHORT_STEP_KPH = 1.0 * CV.MPH_TO_KPH # GRA_*_kurz ~= 1 mph
LONG_STEP_KPH = 5.0 * CV.MPH_TO_KPH # GRA_*_lang ~= 5 mph
TAP_RELEASE_CYCLES = 2 # GRA cycles to release between set-speed taps
def __init__(self, CAN):
self.bus = CAN.ext # radar lives on bus 2 (ext/cam)
self.counter = 0
self.failed = False # ACS_Fehler / irrev_Fehler -> radar dead for the drive
self.want_engaged = False # our belief the radar cruise should be on
self._press_phase = 0 # alternate press/release for SET/cancel discrete edges
self._tap_cooldown = 0 # set-speed tap rate limiter
def reset(self):
self.want_engaged = False
self._press_phase = 0
self._tap_cooldown = 0
@staticmethod
def _map_gap_bars(gap_bars):
if not gap_bars:
return None
return int(min(3, max(1, gap_bars)))
def update(self, packer, frame, CS, *, blend_active, engage_req, cancel_req,
set_speed_kph, gap_bars, v_ego):
can_sends = []
if not blend_active:
self.reset()
return can_sends
if CS.acc_radar_fehler or CS.acc_radar_sta_adr == 3:
self.failed = True
radar_active = (CS.acc_radar_sta_adr == 1) and not self.failed
if self.failed:
self.want_engaged = False
elif cancel_req:
self.want_engaged = False
elif engage_req and v_ego > self.REENGAGE_FLOOR:
self.want_engaged = True
if (frame % self.SPOOF_STEP) == 0:
# Mirror the stock engage handshake ORDER: the radar leads (SET -> ADR active) and only THEN
# does the engine report GRA regulating. Asserting MO2_Sta_GRA=1 before the radar's ADR is
# active presents an impossible state ("engine regulating but ADR didn't initiate it") and the
# radar latches an irreversible fault. So only assert cruise-active once ADR is actually active;
# relay Motor_5 (main switch) as verbatim OEM passthrough throughout.
hold_engaged = self.want_engaged and radar_active
can_sends.append(pqcan.filter_motor2(packer, self.bus, CS.motor2_stock, gra_active=hold_engaged))
can_sends.append(pqcan.filter_motor5(packer, self.bus, CS.motor5_stock, gra_active=False))
if (frame % self.GRA_STEP) == 0:
set_btn = resume_btn = cancel = up_s = down_s = up_l = down_l = False
self._press_phase ^= 1
pressing = self._press_phase == 0
if self.failed:
pass
elif cancel_req:
# Only actually press cancel when the radar is engaged (or overdriven) -- otherwise it's a
# no-op against a passive/off radar and just spams GRA_Abbrechen at ~16 Hz. Fires while
# active and stops once the radar leaves the active state.
cancel = pressing and radar_active
elif self.want_engaged and not radar_active and v_ego > self.ENGAGE_FLOOR:
# Engage with RESUME (GRA_Recall), not SET. Ground-truth from an OEM ACC engage (radar leads:
# RESUME -> ACS_Sta_ADR 2->1 in ~90ms -> engine MO2_Sta_GRA 0->1 ~80ms later) shows this radar
# engages from passive on GRA_Recall; it ignores GRA_Neu_Setzen from that state.
resume_btn = pressing
elif self.want_engaged and radar_active:
if self._tap_cooldown > 0:
self._tap_cooldown -= 1
elif set_speed_kph > 0:
delta = set_speed_kph - CS.acc_radar_v_wunsch
if abs(delta) >= self.SETSPEED_TOL_KPH:
big = abs(delta) >= self.LONG_STEP_KPH
if delta > 0:
up_l, up_s = big, not big
else:
down_l, down_s = big, not big
self._tap_cooldown = self.TAP_RELEASE_CYCLES
self.counter = (self.counter + 1) % 16
can_sends.append(pqcan.create_radar_gra(
packer, self.bus, CS.gra_stock_values, self.counter,
set_btn=set_btn, resume=resume_btn, cancel=cancel, up_short=up_s, down_short=down_s,
up_long=up_l, down_long=down_l, zeitluecke=self._map_gap_bars(gap_bars),
))
return can_sends
+19 -73
View File
@@ -1,7 +1,3 @@
"""
Copyright © IQ.Lvbs, apart of Project Teal Lvbs, All Rights Reserved, licensed under https://konn3kt.com/tos/
"""
def create_hca_steering_control(packer, bus, apply_torque, HCA_Status):
values = {
"LM_Offset": abs(apply_torque),
@@ -99,20 +95,20 @@ def acc_hud_status_value(main_switch_on, acc_faulted, longActive, longOverride):
return hud_status
def create_acc_accel_control(packer, bus, acc_type, accel, acc_control, stopping, starting, esp_hold, comfortBand, jerkLimit, eBrakeActive, sng_active=False):
def create_acc_accel_control(packer, bus, acc_type, accel, acc_control, stopping, starting, esp_hold, comfortBand, jerkLimit, eBrakeActive):
commands = []
acc_enabled = acc_control == 1 and not sng_active
acc_enabled = acc_control == 1
values = {
"ACS_Sta_ADR": 0 if sng_active else acc_control,
"ACS_Sta_ADR": acc_control,
"ACS_StSt_Info": acc_enabled,
"ACS_Typ_ACC": acc_type,
"ACS_Anhaltewunsch": (acc_type == 1 and stopping or eBrakeActive) or sng_active,
"ACS_Anhaltewunsch": acc_type == 1 and stopping or eBrakeActive,
"ACS_FreigSollB": acc_enabled,
"ACS_Sollbeschl": accel if acc_enabled else 3.01,
"ACS_zul_Regelabw": comfortBand if acc_enabled else 1.27,
"ACS_max_AendGrad": jerkLimit if acc_enabled else 5.08,
"ACS_Schubabsch": 0,
"ACS_Schubabsch": 1 if acc_enabled and (accel > 0.05) else 0,
"ACS_MomEingriff": 0,
"ACS_ADR_Schub": 0,
}
@@ -122,14 +118,6 @@ def create_acc_accel_control(packer, bus, acc_type, accel, acc_control, stopping
return commands
def create_sng_handoff_control(packer, bus, handoff_active, decel_req):
values = {
"SNG_HandoffActive": handoff_active,
"SNG_DecelReq": decel_req if handoff_active else 0.0,
}
return packer.make_can_msg("SNG_1", bus, values)
def create_blinker_control(packer, bus, leftBlinker, rightBlinker):
values = {
"BM_rechts": rightBlinker,
@@ -138,71 +126,29 @@ def create_blinker_control(packer, bus, leftBlinker, rightBlinker):
return packer.make_can_msg("Blinkmodi_02", bus, values)
def create_acc_hud_control(packer, bus, acc_hud_status, set_speed, leadDistance, distanceBars, fcw_alert, leadVisible, unavailable, decel, d_unresponsive):
priodisp = 0 if fcw_alert else 1 if (acc_hud_status == 4 or decel or leadVisible) else 2 if (acc_hud_status in (3, 2)) else 0
leadDistanceBars = distanceBars + 1 if distanceBars in (1, 2, 3) else 2
def create_acc_hud_control(packer, bus, acc_hud_status, set_speed, leadDistance, distanceBars, fcw_alert, leadVisible):
if distanceBars == 1:
leadDistanceBars = 2
elif distanceBars == 2:
leadDistanceBars = 3
elif distanceBars == 3:
leadDistanceBars = 4
else:
leadDistanceBars = 2
values = {
"ACA_StaACC": acc_hud_status,
"ACA_AnzDisplay": 1 if acc_hud_status in (3, 4) else 0,
"ACA_Zeitluecke": leadDistanceBars,
"ACA_V_Wunsch": set_speed,
"ACA_gemZeitl": min(15, max(1, int(round(leadDistance)))) if leadVisible else 0,
"ACA_PrioDisp": priodisp,
"ACA_Akustik1": d_unresponsive,
# "ACA_Fahrerhinw": unavailable,
"ACA_PrioDisp": 3,
"ACA_Akustik2": fcw_alert,
"ACA_ACC_Verz": decel,
}
return packer.make_can_msg("ACC_GRA_Anzeige", bus, values)
def filter_motor2(packer, bus, motor2_stock, gra_active=False):
values = dict(motor2_stock)
if gra_active:
values.update({
"MO2_Sta_GRA": 1,
"MO2_Status_TSK": 1,
})
else:
values.update({
"MO2_Sta_GRA": 0,
})
return packer.make_can_msg("Motor_2", bus, values)
def filter_motor5(packer, bus, motor5_stock, gra_active=False):
values = dict(motor5_stock)
if gra_active:
values["MO5_GRA_Hauptsch"] = 1
return packer.make_can_msg("Motor_5", bus, values)
def create_motor3_resume(packer, bus, motor1_stock, motor3_stock, resume=False):
values = dict(motor3_stock)
values_motor1 = dict(motor1_stock)
if resume:
values["MO3_Pedalwert"] = values_motor1["MO1_Pedalwert"]
return packer.make_can_msg("Motor_3", bus, values)
def create_radar_gra(packer, bus, gra_stock, counter, set_btn=False, cancel=False, resume=False,
up_short=False, down_short=False, up_long=False, down_long=False, zeitluecke=None):
values = {s: gra_stock[s] for s in [
"GRA_Hauptschalt", # ACC main switch passthrough
"GRA_Typ_Hauptschalt", # momentary vs latching
"GRA_Kodierinfo", # configuration
"GRA_Sender", # CAN originator
]}
def filter_motor2(packer, bus, motor2_stock):
values = motor2_stock
values.update({
"COUNTER": counter % 16,
"GRA_Neu_Setzen": 1 if set_btn else 0,
"GRA_Abbrechen": 1 if cancel else 0,
"GRA_Recall": 1 if resume else 0,
"GRA_Up_kurz": 1 if up_short else 0,
"GRA_Down_kurz": 1 if down_short else 0,
"GRA_Up_lang": 1 if up_long else 0,
"GRA_Down_lang": 1 if down_long else 0,
"MO2_Sta_GRA": 0,
})
if zeitluecke is not None:
values["GRA_Zeitluecke"] = zeitluecke
return packer.make_can_msg("GRA_Neu", bus, values)
return packer.make_can_msg("Motor_2", bus, values)
@@ -1,49 +0,0 @@
from types import SimpleNamespace
from iqdbc.car.volkswagen import mqbcan, pqcan
from iqdbc.car.volkswagen.carcontroller import CarController
from iqdbc.car.volkswagen.values import CAR, MQB_A0_CARS
def test_is_mqb_a0_car_matches_expected_platforms():
assert CAR.VOLKSWAGEN_POLO_MK6 in MQB_A0_CARS
assert CAR.VOLKSWAGEN_TCROSS_MK1 in MQB_A0_CARS
assert CAR.SKODA_FABIA_MK4 in MQB_A0_CARS
assert CAR.SKODA_KAMIQ_MK1 in MQB_A0_CARS
assert CarController._is_mqb_a0_car(CAR.VOLKSWAGEN_POLO_MK6)
assert CarController._is_mqb_a0_car(CAR.VOLKSWAGEN_TCROSS_MK1)
assert CarController._is_mqb_a0_car(CAR.SKODA_FABIA_MK4)
assert CarController._is_mqb_a0_car(CAR.SKODA_KAMIQ_MK1)
assert not CarController._is_mqb_a0_car(CAR.VOLKSWAGEN_GOLF_MK7)
def test_mqb_steering_torque_scale_only_changes_when_toggle_enabled():
controller = object.__new__(CarController)
controller.CCS = mqbcan
assert controller._get_mqb_steering_torque_scale(0.4, False) == 1.0
assert controller._get_mqb_steering_torque_scale(0.4, True) == 0.8
assert controller._get_mqb_steering_torque_scale(4.0, True) == 1.0
controller.CCS = pqcan
assert controller._get_mqb_steering_torque_scale(0.4, True) == 1.0
def test_mqb_a0_resume_spam_requires_toggle_platform_and_window():
controller = object.__new__(CarController)
controller.CCS = mqbcan
controller.is_mqb_a0 = True
controller.frame = 10
cs = SimpleNamespace(out=SimpleNamespace(standstill=True))
assert controller._should_spam_mqb_a0_resume(cs, True)
controller.frame = 20
assert not controller._should_spam_mqb_a0_resume(cs, True)
controller.frame = 10
controller.is_mqb_a0 = False
assert not controller._should_spam_mqb_a0_resume(cs, True)
controller.is_mqb_a0 = True
assert not controller._should_spam_mqb_a0_resume(cs, False)
@@ -1,186 +0,0 @@
"""
Regression guard for the class of bug fixed in carstate.py's Diagnose_1 (cluster clock,
removed) and EPB_1 (stop-and-go hold, now gated on PQ_SNG_ECD): reading a CAN message via
`some_cp.vl["MsgName"]["Signal"]` without declaring it in get_can_parsers_pq() lazily adds
it via VLDict.__getitem__ -> CANParser._add_message(key, freq=None), which is NOT the same
as ignore-alive (that's what math.nan is for -- see
iqdbc/can/tests/test_packer_parser.py::test_lazy_add_not_ignore_alive). An undeclared
message defaults to "assume ~1Hz, must be seen within ~10s", so if the real car never sends
it, CarState.canValid gets stuck False forever.
Rather than fuzzing every VolkswagenFlagsIQ combination (most are unreachable through
interface.py's real detection logic), each fixture below is the CarParams captured from a
real konn3kt route for a real car of that variant. We run CarState.update() once (no CAN
data needs to be fed -- .vl[...] lazily adds and returns default-zero values regardless of
whether the parser has ever seen a real frame) while recording every message name accessed,
then assert that set is a subset of what that real car actually transmits (per its captured
CAN fingerprint). A message accessed but not in the real fingerprint is exactly the bug
class this guards against.
"""
from iqdbc.can.parser import VLDict
from iqdbc.car import structs
from iqdbc.car.volkswagen.carstate import CarState
from iqdbc.car.volkswagen.values import CAR, Bus
class _RecordingVLDict(VLDict):
"""Records every message name read via .vl[...], including ones lazily added for
messages never declared in get_can_parsers_pq's message list. Applied by reclassing
a live CANParser.vl instance in place (rather than replacing it with a fresh object)
so any messages already registered at CANParser construction time, and the parser
back-reference _add_message needs, are preserved."""
accessed: set[str]
def __getitem__(self, key):
if isinstance(key, str):
self.accessed.add(key)
return super().__getitem__(key)
def _make_car_params(car_fingerprint, flags, network_location, transmission_type, enable_bsm, pcm_cruise):
CP = structs.CarParams.new_message()
CP.carFingerprint = car_fingerprint
CP.flags = flags
CP.radarUnavailable = True
CP.networkLocation = network_location
CP.transmissionType = transmission_type
CP.enableBsm = enable_bsm
CP.pcmCruise = pcm_cruise
CP.minSteerSpeed = 0.0
CP_IQ = structs.IQCarParams()
return CP, CP_IQ
# Real per-variant CarParams captured from konn3kt routes (not hand-derived), so each
# fixture reflects an actual car rather than a guess at which flag combos are reachable.
# known_bus1/known_bus2 are the exact message names present in that car's real CAN
# fingerprint. Bus.pt and Bus.aux both listen on physical bus 1 for PQ (CanBus.powertrain
# == CanBus.aux == 1); Bus.cam listens on physical bus 2 (CanBus.cam == 2) -- see CanBus in
# iqdbc/car/volkswagen/values.py. UNK_* (unrecognized DBC addresses) are dropped.
FIXTURES = [
dict(
name="jetta_mk6_base",
route="a0d99b85ff06857b|0000003a--135c88414f",
car_fingerprint=CAR.VOLKSWAGEN_JETTA_MK6,
flags=2,
network_location="gateway",
transmission_type="automatic",
enable_bsm=False,
pcm_cruise=True,
known_bus1={
'ACC_GRA_Anzeige', 'ACC_System', 'AWV', 'Airbag_1', 'Airbag_2', 'BSG_Last', 'Bremse_1',
'Bremse_10', 'Bremse_11', 'Bremse_2', 'Bremse_3', 'Bremse_4', 'Bremse_5', 'Bremse_8',
'Bremse_9', 'Diagnose_1', 'Einheiten_1', 'GRA_Neu', 'Gate_Komf_1', 'Gate_Komf_2',
'Getriebe_1', 'Getriebe_2', 'Getriebe_4', 'Ident', 'Klima_1', 'Kombi_1', 'Kombi_2',
'Kombi_3', 'Lenkhilfe_1', 'Lenkhilfe_2', 'Lenkhilfe_3', 'Lenkwinkel_1', 'Motor_1',
'Motor_10', 'Motor_12', 'Motor_2', 'Motor_3', 'Motor_5', 'Motor_6', 'Motor_7', 'Motor_8',
'Motor_Bremse', 'Motor_Flexia', 'Soll_Verbauliste_neu', 'Systeminfo_1', 'Waehlhebel_1',
},
known_bus2={
'ACC_GRA_Anzeige', 'ACC_System', 'AWV', 'Airbag_1', 'BSG_Last', 'Bremse_1', 'Bremse_10',
'Bremse_11', 'Bremse_2', 'Bremse_3', 'Bremse_5', 'Bremse_8', 'Diagnose_1', 'Einheiten_1',
'GRA_Neu', 'Gate_Komf_1', 'Gate_Komf_2', 'Getriebe_1', 'Ident', 'Kombi_1', 'Kombi_2',
'Kombi_3', 'Lenkhilfe_2', 'Lenkhilfe_3', 'Lenkwinkel_1', 'Motor_1', 'Motor_10', 'Motor_2',
'Motor_3', 'Motor_5', 'Motor_Flexia', 'Soll_Verbauliste_neu', 'Systeminfo_1',
},
),
dict(
name="passat_nms_sng_ecd",
route="0f53129ed44f6920|00000031--0c1aea511e",
car_fingerprint=CAR.VOLKSWAGEN_PASSAT_NMS,
flags=524418, # PQ | IQ_LVBS_ALC_MODULE | IQ_PQ_SNG_ECD
network_location="gateway",
transmission_type="automatic",
enable_bsm=False,
pcm_cruise=False,
known_bus1={
'ACC_GRA_Anzeige', 'ACC_System', 'AWV', 'Airbag_1', 'Airbag_2', 'BSG_Last', 'Bremse_1',
'Bremse_10', 'Bremse_11', 'Bremse_2', 'Bremse_3', 'Bremse_4', 'Bremse_5', 'Bremse_8',
'Bremse_9', 'Diagnose_1', 'EPB_1', 'EPB_2', 'Einheiten_1', 'GRA_Neu', 'Gate_Komf_1',
'Gate_Komf_2', 'Getriebe_1', 'Getriebe_2', 'Ident', 'Klima_1', 'Kombi_1', 'Kombi_2',
'Kombi_3', 'Lenkhilfe_1', 'Lenkhilfe_2', 'Lenkhilfe_3', 'Lenkwinkel_1', 'Motor_1',
'Motor_10', 'Motor_12', 'Motor_13', 'Motor_2', 'Motor_3', 'Motor_5', 'Motor_6', 'Motor_7',
'Motor_8', 'Motor_Bremse', 'Motor_Flexia', 'Soll_Verbauliste_neu', 'Systeminfo_1',
},
known_bus2={
'ACC_GRA_Anzeige', 'ACC_System', 'AWV', 'Airbag_1', 'BSG_Last', 'Bremse_1', 'Bremse_10',
'Bremse_11', 'Bremse_2', 'Bremse_3', 'Bremse_5', 'Bremse_8', 'Diagnose_1', 'EPB_1',
'Einheiten_1', 'GRA_Neu', 'Gate_Komf_1', 'Gate_Komf_2', 'Getriebe_1', 'Ident', 'Kombi_1',
'Kombi_2', 'Kombi_3', 'Lenkhilfe_2', 'Lenkhilfe_3', 'Lenkwinkel_1', 'Motor_1', 'Motor_10',
'Motor_2', 'Motor_3', 'Motor_5', 'Motor_Flexia', 'Soll_Verbauliste_neu', 'Systeminfo_1',
},
),
dict(
name="passat_b7_acc_fts_epb",
route="20e3cd4f0d5f39d1|0000008e--274d762bac",
car_fingerprint=CAR.VOLKSWAGEN_PASSAT_B7,
flags=262146, # PQ | IQ_PQ_ACC_FTS_EPB
network_location="gateway",
transmission_type="automatic",
enable_bsm=True,
pcm_cruise=False,
known_bus1={
'ACC_GRA_Anzeige', 'ACC_System', 'AWV', 'Airbag_1', 'Airbag_2', 'BSG_Last', 'Bremse_1',
'Bremse_10', 'Bremse_11', 'Bremse_2', 'Bremse_3', 'Bremse_4', 'Bremse_5', 'Bremse_8',
'Bremse_9', 'Daempfer_1', 'Diagnose_1', 'EPB_1', 'Einheiten_1', 'GRA_Neu', 'Gate_Komf_1',
'Gate_Komf_2', 'Getriebe_1', 'Getriebe_2', 'Getriebe_4', 'HCA_1', 'Ident', 'Klima_1',
'Kombi_1', 'Kombi_2', 'Kombi_3', 'Lenkhilfe_1', 'Lenkhilfe_2', 'Lenkhilfe_3',
'Lenkwinkel_1', 'Motor_1', 'Motor_10', 'Motor_12', 'Motor_2', 'Motor_3', 'Motor_5',
'Motor_6', 'Motor_7', 'Motor_8', 'Motor_Bremse', 'Motor_Flexia', 'Parkhilfe_01',
'Soll_Verbauliste_neu', 'Systeminfo_1', 'Waehlhebel_1',
},
known_bus2={
'ACC_GRA_Anzeige', 'ACC_System', 'AWV', 'Airbag_1', 'BSG_Last', 'Bremse_1', 'Bremse_10',
'Bremse_11', 'Bremse_2', 'Bremse_3', 'Bremse_5', 'Bremse_8', 'Diagnose_1', 'EPB_1',
'Einheiten_1', 'GRA_Neu', 'Gate_Komf_1', 'Gate_Komf_2', 'Getriebe_1', 'HCA_1', 'Ident',
'Kombi_1', 'Kombi_2', 'Kombi_3', 'LDW_Status', 'Lenkhilfe_2', 'Lenkhilfe_3',
'Lenkwinkel_1', 'Motor_1', 'Motor_10', 'Motor_2', 'Motor_3', 'Motor_5', 'Motor_Flexia',
'Parkhilfe_01', 'RDK_Status', 'SWA_1', 'Soll_Verbauliste_neu', 'Systeminfo_1',
},
),
]
class TestVolkswagenPqCanValid:
def test_only_reads_messages_the_real_car_sends(self, subtests):
for fixture in FIXTURES:
with subtests.test(car=fixture["name"]):
CP, CP_IQ = _make_car_params(
fixture["car_fingerprint"], fixture["flags"], fixture["network_location"],
fixture["transmission_type"], fixture["enable_bsm"], fixture["pcm_cruise"],
)
CS = CarState(CP, CP_IQ)
can_parsers = CS.get_can_parsers(CP, CP_IQ)
recorders = {}
for bus, parser in can_parsers.items():
if parser is None:
continue
parser.vl.__class__ = _RecordingVLDict
parser.vl.accessed = set()
recorders[bus] = parser.vl
# no CAN data is fed: .vl[...] lazily adds and returns default-zero values
# regardless of whether the parser has ever seen a real frame, so this alone
# is enough to harvest every message name update_pq() touches for this variant
CS.update(can_parsers)
accessed_bus1 = recorders[Bus.pt].accessed | recorders[Bus.aux].accessed
accessed_bus2 = recorders[Bus.cam].accessed
extra_bus1 = accessed_bus1 - fixture["known_bus1"]
extra_bus2 = accessed_bus2 - fixture["known_bus2"]
assert not extra_bus1, (
f"{fixture['name']}: code reads {sorted(extra_bus1)} on bus 1 (pt/aux) but the "
f"real car (route {fixture['route']}) never transmits it -- this needs to be "
f"gated behind whatever flag makes it optional, or declared with math.nan if it's "
f"genuinely on-demand/rare, not read unconditionally"
)
assert not extra_bus2, (
f"{fixture['name']}: code reads {sorted(extra_bus2)} on bus 2 (cam) but the "
f"real car (route {fixture['route']}) never transmits it -- this needs to be "
f"gated behind whatever flag makes it optional, or declared with math.nan if it's "
f"genuinely on-demand/rare, not read unconditionally"
)
@@ -1,22 +0,0 @@
import pytest
from iqdbc.car.volkswagen.values import (
CAR, PASSAT_B7_STOP_ACCEL, PASSAT_B7_STOPPING_SPEED, PQ_STOPPING_SPEED,
VolkswagenFlags, apply_pq_stopping_accel, get_longitudinal_stopping_speed_override,
)
@pytest.mark.parametrize("candidate, flags, expected", [
(CAR.VOLKSWAGEN_PASSAT_B7, VolkswagenFlags.PQ, PASSAT_B7_STOPPING_SPEED),
(CAR.VOLKSWAGEN_JETTA_MK6, VolkswagenFlags.PQ, PQ_STOPPING_SPEED),
(CAR.VOLKSWAGEN_GOLF_MK7, 0, 0.0),
(CAR.VOLKSWAGEN_ID4_MK1, VolkswagenFlags.MEB, 0.0),
])
def test_stopping_speed_override(candidate, flags, expected):
assert get_longitudinal_stopping_speed_override(candidate, flags) == expected
def test_passat_b7_stop_accel_is_exact():
assert apply_pq_stopping_accel(CAR.VOLKSWAGEN_PASSAT_B7, -0.2, True) == PASSAT_B7_STOP_ACCEL == -0.55
assert apply_pq_stopping_accel(CAR.VOLKSWAGEN_PASSAT_B7, -0.2, False) == -0.2
assert apply_pq_stopping_accel(CAR.VOLKSWAGEN_JETTA_MK6, -0.2, True) == -0.2
@@ -43,10 +43,6 @@ class TestVolkswagenPlatformConfigs:
if len(shared_chassis_codes) == 0:
continue
# A shared chassis code is unambiguous when the VIN WMI separates the candidates.
if platform.config.wmis.isdisjoint(comp.config.wmis):
continue
platform_model_years = getattr(platform.config, "model_years", set())
comp_model_years = getattr(comp.config, "model_years", set())
if platform_model_years and comp_model_years and platform_model_years.isdisjoint(comp_model_years):
@@ -55,13 +51,10 @@ class TestVolkswagenPlatformConfigs:
assert set() == shared_chassis_codes, f"Shared chassis codes: {comp}"
def test_custom_fuzzy_fingerprinting(self, subtests):
all_radar_fw = list({fw for ecus in FW_VERSIONS.values() for fw in ecus.get((Ecu.fwdRadar, 0x757, None), [])})
all_radar_fw = list({fw for ecus in FW_VERSIONS.values() for fw in ecus[Ecu.fwdRadar, 0x757, None]})
for platform in CAR:
with subtests.test(platform=platform.name):
# Fuzzy matching keys off the radar ECU (CHECK_FUZZY_ECUS), so a platform that
# declares no radar ECU can never be VIN-matched and should never be expected to match.
platform_has_radar = (Ecu.fwdRadar, 0x757, None) in FW_VERSIONS.get(platform, {})
for wmi in WMI:
for chassis_code in platform.config.chassis_codes | {"00"}:
platform_model_years = getattr(platform.config, "model_years", set())
@@ -77,7 +70,7 @@ class TestVolkswagenPlatformConfigs:
for radar_fw in random.sample(all_radar_fw, 5) + [b'\xf1\x875Q0907572G \xf1\x890571', b'\xf1\x877H9907572AA\xf1\x890396']:
model_year_match = len(platform_model_years) == 0 or model_year in platform_model_years
should_match = ((wmi in platform.config.wmis and chassis_code in platform.config.chassis_codes and model_year_match) and
radar_fw in all_radar_fw and platform_has_radar)
radar_fw in all_radar_fw)
live_fws = {(0x757, None): [radar_fw]}
matches = FW_QUERY_CONFIG.match_fw_to_car_fuzzy(live_fws, vin, FW_VERSIONS)
@@ -113,17 +106,3 @@ def test_button_enable_recovers_once_cruise_fault_clears():
)]
assert state.update_button_enable(button_events)
def test_pq_hca_ready_does_not_complete_eps_initialization():
state = object.__new__(CarState)
state.eps_init_complete = False
state.frame = 0
assert state.update_hca_state("READY", ready_confirms_init=False) == (True, False)
state.frame = 317
assert state.update_hca_state("FAULT", ready_confirms_init=False) == (True, False)
state.frame = 1001
assert state.update_hca_state("FAULT", ready_confirms_init=False) == (False, True)
+13 -61
View File
@@ -35,10 +35,6 @@ class CanBus(CanBusBase):
# NetworkLocation.gateway: powertrain CAN
return 1
@property
def powertrain(self) -> int:
return 1
@property
def main(self) -> int:
return 1
@@ -53,11 +49,17 @@ class CanBus(CanBusBase):
# ADAS / Extended CAN, side of the relay with the ACC radar
return 2
@property
def eps(self) -> int:
return 4
@property
def car(self) -> int:
return 6
# Extra Tolerances For Road Variance
AVERAGE_ROAD_ROLL = 0.06
PQ_STOPPING_SPEED = 1.5 * CV.KPH_TO_MS
PASSAT_B7_STOPPING_SPEED = 0.55 * CV.KPH_TO_MS
PASSAT_B7_STOP_ACCEL = -0.55
class CarControllerParams:
@@ -82,8 +84,6 @@ class CarControllerParams:
AEB_CONTROL_STEP = 2 # ACC_10 frequency 50Hz
AEB_HUD_STEP = 20 # ACC_15 frequency 5Hz
VW_LOW_SPEED_STATE_SPEED = 0.5 # m/s; below this, always force starting or stopping in MQB legacy long
SNG_HANDOFF_SPEED = 5.0 * CV.KPH_TO_MS
SNG_HOLD_DECEL_MAX = -0.1
# Documented lateral limits: 3.00 Nm max, rate of change 5.00 Nm/sec.
# MQB vs PQ maximums are shared, but rate-of-change limited differently
@@ -99,8 +99,7 @@ class CarControllerParams:
STEER_LOW_TORQUE = int(STEER_MAX * 0.20) # Steer timer mitigation performed when torque output under 20%
STEER_TIME_LOW_TORQUE = 0.5 # Wait for this duration of STEER_LOW_TORQUE to begin mitigation
STEER_TIME_STUCK_TORQUE = 1.9 # EPS limits same torque to 6 seconds, reset timer 3x within that period
STEER_TIME_RESET = 1.1 # Duration of HCA disable needed for effective EPS timer reset'
IQ_PQ_UNAVAILABLE_HUD_FRAMES = 25
STEER_TIME_RESET = 1.1 # Duration of HCA disable needed for effective EPS timer reset
DEFAULT_MIN_STEER_SPEED = 0.4 # m/s, newer EPS racks fault below this speed, don't show a low speed alert
@@ -116,8 +115,8 @@ class CarControllerParams:
self.LDW_STEP = 5 # LDW_1 message frequency 20Hz
self.ACC_HUD_STEP = 4 # ACC_GRA_Anzeige frequency 25Hz
self.STEER_DRIVER_ALLOWANCE = 80 # Driver intervention threshold 0.8 Nm
self.STEER_DELTA_UP = 150 # Max HCA reached in 0.60s (STEER_MAX / (50Hz * 0.60))
self.STEER_DELTA_DOWN = 300 # Min HCA reached in 0.60s (STEER_MAX / (50Hz * 0.60))
self.STEER_DELTA_UP = 10 # Max HCA reached in 0.60s (STEER_MAX / (50Hz * 0.60))
self.STEER_DELTA_DOWN = 10 # Min HCA reached in 0.60s (STEER_MAX / (50Hz * 0.60))
if CP.transmissionType == TransmissionType.automatic:
self.shifter_values = can_define.dv["Getriebe_1"]["GE1_Wahl_Pos"]
@@ -285,10 +284,6 @@ class VolkswagenSafetyFlags(IntFlag):
ALLOW_LONG_ACCEL_WITH_GAS_PRESSED = 8
DISABLE_RADAR = 16
PQ_ALC_MODULE = 32
PQ_LOWLINE = 64
PQ_NO_CAM_BUS = 128
PQ_ACC_FTS_EPB = 256
PQ_SNG_ECD = 512
class VolkswagenFlags(IntFlag):
@@ -316,10 +311,6 @@ class VolkswagenFlagsIQ(IntFlag):
IQ_CC_ONLY = 1 << 5 # CC only mode with radar (has AEB)
IQ_CC_ONLY_NO_RADAR = 1 << 6 # CC only mode without radar
IQ_LVBS_ALC_MODULE = 1 << 7 # IQ.Lvbs VW ALC hardware module present / intended active path
IQ_PQ_LOWLINE = 1 << 17 # Non-ECAN lateral-only PQ: bus 0 dead, TX on bus 1 (ptCAN)
IQ_PQ_ACC_FTS_EPB = 1 << 18 # B7 TRW450: ACC FtS + EPB hold, Motor_1 resume spoof on bus 1
IQ_PQ_SNG_ECD = 1 << 19
IQ_PQ_TIMEBOMB = 1 << 20
RADAR_DISABLE_STATE = {"error": False}
@@ -342,7 +333,6 @@ class VolkswagenMQBPlatformConfig(PlatformConfig):
# on camera-integrated cars, as we lose too many ECUs to reliably identify the vehicle
chassis_codes: set[str] = field(default_factory=set)
wmis: set[WMI] = field(default_factory=set)
model_years: set[str] = field(default_factory=set)
@dataclass
@@ -552,35 +542,24 @@ class CAR(Platforms):
VolkswagenCarSpecs(mass=1551, wheelbase=2.79),
chassis_codes={"3C", "3G"},
wmis={WMI.VOLKSWAGEN_EUROPE_CAR},
model_years={"F", "G", "H", "J", "K", "L", "M", "N"}, # 2015-2022
)
VOLKSWAGEN_PASSAT_MK7 = VolkswagenPQPlatformConfig(
[VWCarDocs("Volkswagen Passat 2.0 TDI 2014")],
VolkswagenCarSpecs(mass=1836, wheelbase=2.70, steerRatio=13.0, minSteerSpeed=31 * CV.KPH_TO_MS),
chassis_codes={"3C"},
wmis={WMI.VOLKSWAGEN_EUROPE_CAR},
model_years={"E"}, # 2014
)
VOLKSWAGEN_PASSAT_NMS = VolkswagenPQPlatformConfig(
[VWCarDocs("Volkswagen Passat NMS 2015-17")],
VolkswagenCarSpecs(mass=1503, wheelbase=2.80),
chassis_codes={"A3"},
wmis={WMI.VOLKSWAGEN_USA_CAR},
# NMS and NMS+ share chassis code A3; disambiguate by model year
model_years={"F", "G", "H"}, # 2015-2017
)
VOLKSWAGEN_PASSAT_NMS_PLUS = VolkswagenPQPlatformConfig(
[VWCarDocs("Volkswagen Passat NMS 2018-22")],
VolkswagenCarSpecs(mass=1503, wheelbase=2.80, minEnableSpeed=20 * CV.KPH_TO_MS),
chassis_codes={"A3"},
wmis={WMI.VOLKSWAGEN_USA_CAR},
model_years={"J", "K", "L", "M", "N"}, # 2018-2022
)
VOLKSWAGEN_PASSAT_B7 = VolkswagenPQPlatformConfig(
[VWCarDocs("Volkswagen Passat B7 2008-2011")],
VolkswagenCarSpecs(mass=1503, wheelbase=2.712, steerRatio=16.4, minSteerSpeed=0),
chassis_codes={"A3"},
wmis={WMI.VOLKSWAGEN_USA_CAR},
)
VOLKSWAGEN_POLO_MK6 = VolkswagenMQBPlatformConfig(
[
@@ -594,19 +573,12 @@ class CAR(Platforms):
VOLKSWAGEN_SHARAN_MK2 = VolkswagenPQPlatformConfig(
[
VWCarDocs("Volkswagen Sharan 2018-22"),
VWCarDocs("SEAT Alhambra 2018-20"),
],
VolkswagenCarSpecs(mass=1639, wheelbase=2.92),
chassis_codes={"7N"},
wmis={WMI.VOLKSWAGEN_EUROPE_CAR},
)
SEAT_ALHAMBRA_MK1 = VolkswagenPQPlatformConfig(
[
VWCarDocs("SEAT Alhambra 2018-20"),
],
VolkswagenCarSpecs(mass=1639, wheelbase=2.92, minSteerSpeed=50 * CV.KPH_TO_MS),
chassis_codes={"7N"},
wmis={WMI.SEAT},
)
VOLKSWAGEN_TAOS_MK1 = VolkswagenMQBPlatformConfig(
[VWCarDocs("Volkswagen Taos 2022-24")],
VolkswagenCarSpecs(mass=1498, wheelbase=2.69),
@@ -908,24 +880,4 @@ FW_QUERY_CONFIG = FwQueryConfig(
match_fw_to_car_fuzzy=match_fw_to_car_fuzzy,
)
MQB_A0_CARS = {
CAR.VOLKSWAGEN_POLO_MK6,
CAR.VOLKSWAGEN_TCROSS_MK1,
CAR.SKODA_FABIA_MK4,
CAR.SKODA_KAMIQ_MK1,
}
def get_longitudinal_stopping_speed_override(candidate: CAR, flags: int) -> float:
if candidate == CAR.VOLKSWAGEN_PASSAT_B7:
return PASSAT_B7_STOPPING_SPEED
if flags & VolkswagenFlags.PQ:
return PQ_STOPPING_SPEED
return 0.0
def apply_pq_stopping_accel(candidate: CAR, accel: float, stopping: bool) -> float:
return PASSAT_B7_STOP_ACCEL if candidate == CAR.VOLKSWAGEN_PASSAT_B7 and stopping else accel
DBC = CAR.create_dbc_map()
+1 -8
View File
@@ -3,13 +3,6 @@ from pathlib import Path
env = Environment(ENV=os.environ)
# short colored build output, if the top-level pretty tool is present (main-repo build)
_pretty = Dir('#site_scons/site_tools').File('pretty.py')
if _pretty.exists():
env.Tool('pretty', toolpath=[_pretty.dir.abspath])
if not hasattr(env, 'PrettyAction'):
env.AddMethod(lambda e, cmd, label, **kw: Action(cmd), 'PrettyAction')
generator = File("generator/generator.py")
source_files = [
@@ -34,5 +27,5 @@ output_files += [
generated = env.Command(
target=list(set(output_files)),
source=[generator] + source_files,
action=env.PrettyAction("python3 ${SOURCES[0]}", 'GEN'),
action="python3 ${SOURCES[0]}",
)
+2 -2
View File
@@ -204,7 +204,7 @@ BO_ 605 DAS_road: 6 XXX
BO_ 1160 DAS_steeringControl: 4 PARTY
SG_ DAS_steeringControlChecksum : 24|8@1+ (1,0) [0|255] "" aps
SG_ DAS_steeringControlCounter : 16|4@1+ (1,0) [0|15] "" aps
SG_ DAS_steeringControlType : 23|3@0+ (1,0) [0|7] "" aps
SG_ DAS_steeringControlType : 23|2@0+ (1,0) [0|3] "" aps
SG_ DAS_steeringAngleRequest : 6|15@0+ (0.1,-1638.35) [-1638.35|1638.35] "deg" aps
SG_ DAS_steeringHapticRequest : 7|1@0+ (1,0) [0|1] "" aps
@@ -426,7 +426,7 @@ VAL_ 599 DI_uiSpeedUnits 0 "DI_SPEED_MPH" 1 "DI_SPEED_KPH" ;
VAL_ 599 DI_uiSpeed 255 "DI_UI_SPEED_SNA" ;
VAL_ 599 DI_vehicleSpeed 4095 "SNA" ;
VAL_ 637 APS_eacAllow 0 "INHIBIT" 1 "ALLOW" 2 "RESERVED" 3 "SNA";
VAL_ 1160 DAS_steeringControlType 2 "LANE_KEEP_ASSIST" 0 "NONE" 1 "ANGLE_CONTROL" 3 "EMERGENCY_LANE_KEEP" 4 "FSD" ;
VAL_ 1160 DAS_steeringControlType 2 "LANE_KEEP_ASSIST" 0 "NONE" 1 "ANGLE_CONTROL" 3 "EMERGENCY_LANE_KEEP" ;
VAL_ 1160 DAS_steeringAngleRequest 16384 "ZERO_ANGLE" ;
VAL_ 297 SCCM_steeringAngleValidity 3 "SNA" 2 "INIT" 0 "INVALID" 1 "VALID" ;
VAL_ 297 SCCM_steeringAngleSensorStatus 0 "OK" 1 "INIT" 2 "ERROR" 3 "ERROR_INIT" ;
+6 -20
View File
@@ -197,24 +197,15 @@ BO_ 1001 DAS_bodyControls: 8 VEH
SG_ DAS_headlightRequest : 0|2@1+ (1,0) [0|3] "" aps
SG_ DAS_hazardLightRequest : 2|2@1+ (1,0) [0|3] "" aps
SG_ DAS_wiperSpeed : 4|4@1+ (1,0) [0|15] "" aps
SG_ DAS_turnIndicatorRequest : 8|3@1+ (1,0) [0|4] "" aps
SG_ DAS_highLowBeamDecision : 11|2@1+ (1,0) [0|3] "" aps
SG_ DAS_heaterRequest : 13|2@1+ (1,0) [0|2] "" aps
SG_ DAS_highLowBeamOffReason : 15|3@1+ (1,0) [0|5] "" aps
SG_ DAS_turnIndicatorRequestReason : 18|4@1+ (1,0) [0|12] "" aps
SG_ DAS_dynamicBrakeLightRequest : 22|1@1+ (1,0) [0|1] "" aps
SG_ DAS_radarHeaterRequest : 23|1@1+ (1,0) [0|1] "" aps
SG_ DAS_ahlbOverride : 24|1@1+ (1,0) [0|1] "" aps
SG_ DAS_mirrorFoldRequest : 25|2@1+ (1,0) [0|3] "" aps
SG_ DAS_turnIndicatorRequest : 8|2@1+ (1,0) [0|3] "" aps
SG_ DAS_highLowBeamDecision : 10|2@1+ (1,0) [0|3] "" aps
SG_ DAS_heaterRequest : 12|2@1+ (1,0) [0|2] "" aps
SG_ DAS_turnIndicatorRequestReason : 17|4@1+ (1,0) [0|8] "" aps
SG_ DAS_autopilotActive : 24|1@1+ (1,0) [0|1] "" XXX
SG_ DAS_accActive : 29|1@1+ (1,0) [0|1] "" XXX
SG_ DAS_bodyControlsCounter : 52|4@1+ (1,0) [0|15] "" aps
SG_ DAS_bodyControlsChecksum : 56|8@1+ (1,0) [0|255] "" aps
BO_ 994 VCLEFT_lightStatus: 7 VEH
SG_ VCLEFT_turnSignalStatus : 4|2@1+ (1,0) [0|3] "" XXX
BO_ 995 VCRIGHT_lightStatus: 2 VEH
SG_ VCRIGHT_turnSignalStatus : 4|2@1+ (1,0) [0|3] "" XXX
BO_ 1013 ID3F5VCFRONT_lighting: 8 VEH
SG_ VCFRONT_lowBeamsCalibrated : 62|1@1+ (1,0) [0|1] "" Receiver
SG_ VCFRONT_lowBeamsOnForDRL : 61|1@1+ (1,0) [0|1] "" Receiver
@@ -397,10 +388,5 @@ BO_ 826 ID33AUI_rangeSOC: 8 VehicleBus
BO_ 306 ID132HVBattAmpVolt: 8 VehicleBus
SG_ BattVoltage132 : 0|16@1+ (0.01,0) [0|655.35] "V" Receiver
BO_ 950 ID3B6UI_odometer: 8 VehicleBus
SG_ UI_odometer : 0|32@1+ (0.001,0) [0|4294967.295] "km" Receiver
SG_ UI_odometerCounter : 52|4@1+ (1,0) [0|15] "" Receiver
SG_ UI_odometerChecksum : 56|8@1+ (1,0) [0|255] "" Receiver
BO_ 658 ID292BMS_SOC: 8 VehicleBus
SG_ SOCUI292 : 10|10@1+ (0.1,0) [0|102.3] "%" Receiver
+15 -21
View File
@@ -541,21 +541,21 @@ BO_ 1437 mRemotestart_FFB: 8 Vector__XXX
SG_ RSF_Tastencode_2 : 8|8@1+ (1,0) [1|255] "" Vector__XXX
SG_ RSF_Tastencode_Maske : 16|8@1+ (1,0) [1|255] "" Vector__XXX
BO_ 640 Motor_1: 8 Motor
SG_ MO1_Wunschmo : 56|8@1+ (0.39,0) [0|99.06] "%" XXX
SG_ MO1_Verlustmo : 48|8@1+ (0.39,0) [0|99.06] "%" XXX
SG_ MO1_Pedalwert : 40|8@1+ (0.4,0) [0|101.6] "%" XXX
SG_ MO1_Mo_o_ex : 32|8@1+ (0.39,0) [0|99.06] "%" XXX
SG_ MO1_Drehzahl : 16|16@1+ (0.25,0) [0|16256] "1/min" XXX
SG_ MO1_Mo_m_ex : 8|8@1+ (0.39,0) [0|99.06] "%" XXX
SG_ MO1_Sta_MotMo : 7|1@1+ (1,0) [0|1] "" XXX
SG_ MO1_Sta_Getr : 6|1@1+ (1,0) [0|1] "" XXX
SG_ MO1_Sta_Bremse : 5|1@1+ (1,0) [0|1] "" XXX
SG_ MO1_TiOut_Br : 4|1@1+ (1,0) [0|1] "" XXX
SG_ MO1_Kup_schalt : 3|1@1+ (1,0) [0|1] "" XXX
SG_ MO1_Kickdown : 2|1@1+ (1,0) [0|1] "" XXX
SG_ MO1_Sta_Pedal : 1|1@1+ (1,0) [0|1] "" XXX
SG_ MO1_Leergas : 0|1@1+ (1,0) [0|1] "" XXX
BO_ 640 Motor_1: 8 XXX
SG_ Fahrerwunschmoment : 56|8@1+ (0.39,0) [0|99] "MDI" XXX
SG_ mechanisches_Motor_Verlustmomen : 48|8@1+ (0.39,0) [0|99] "MDI" XXX
SG_ Fahrpedalwert_oder_Drosselklapp : 40|8@1+ (0.4,0) [0|101.6] "%" XXX
SG_ inneres_Motor_Moment_ohne_exter : 32|8@1+ (0.39,0) [0|99] "MDI" XXX
SG_ Motordrehzahl : 16|16@1+ (0.25,0) [0|16256] "U/min" XXX
SG_ inneres_Motor_Moment : 8|8@1+ (0.39,0) [0|99] "MDI" XXX
SG_ Momentenangaben_ungenau : 7|1@1+ (1,0) [0|0] "" XXX
SG_ Fehlerstatus_Getriebe_Momentene : 6|1@1+ (1,0) [0|0] "" XXX
SG_ Fehlerstatus_Brems_Momenteneing : 5|1@1+ (1,0) [0|0] "" XXX
SG_ Time_Out_Bremsen_Botschaft : 4|1@1+ (1,0) [0|0] "" XXX
SG_ Kupplungsschalter : 3|1@1+ (1,0) [0|0] "" XXX
SG_ Kickdownschalter : 2|1@1+ (1,0) [0|0] "" XXX
SG_ Fahrpedalwert_ungenau__Motor_1_ M : 1|1@1+ (1,0) [0|0] "" XXX
SG_ Leergasinformation : 0|1@1+ (1,0) [0|0] "" XXX
BO_ 262 Master_3: 8 XXX
SG_ Frei_Master_3_1 : 56|8@1+ (1,0) [0|0] "" XXX
@@ -1353,12 +1353,6 @@ BO_ 982 APD_1: 8 XXX
SG_ APD_AngleOffSet : 42|11@1+ (0.01,-10) [-10|10.47] "deg" XXX
SG_ APD_GripFactor : 53|11@1+ (0.001,0) [0|2.047] "" XXX
BO_ 983 SNG_1: 8 XXX
SG_ CHECKSUM : 0|8@1+ (1,0) [0|255] "" XXX
SG_ COUNTER : 8|4@1+ (1,0) [0|15] "" XXX
SG_ SNG_HandoffActive : 12|1@1+ (1,0) [0|1] "" XXX
SG_ SNG_DecelReq : 16|8@1+ (0.048,-7.968) [-7.968|4.224] "Unit_MeterPerSeconSquar" XXX
BO_ 210 HCA_1: 5 XXX
SG_ CHECKSUM : 0|8@1+ (1,0) [0|15] "" XXX
SG_ COUNTER : 8|4@1+ (1,0) [0|15] "" XXX
File diff suppressed because it is too large Load Diff
+7 -1
View File
@@ -1,3 +1,7 @@
"""
Copyright © IQ.Lvbs, apart of Project Teal Lvbs, All Rights Reserved, licensed under https://konn3kt.com/tos
"""
from enum import StrEnum
from collections import namedtuple
@@ -26,6 +30,7 @@ class AolCarController:
self.lkas_icon = 0
self.lfa_icon = 0
# display LFA "white_wheel" and LKAS "White car + lanes" when not CC.latActive
def aol_status_update(self, CC: structs.CarControl, CC_IQ: structs.IQCarControl, frame: int) -> AolDataIQ:
enable_aol = CC_IQ.aol.available
@@ -50,6 +55,7 @@ class AolCarController:
else:
lkas_icon = 2 if enabled else 1
# Override common signals for KIA_OPTIMA_G4 and KIA_OPTIMA_G4_FL
if CP.carFingerprint in (CAR.KIA_OPTIMA_G4, CAR.KIA_OPTIMA_G4_FL, CAR.HYUNDAI_KONA_NON_SCC):
lkas_icon = 3 if (self.aol.lat_active if self.aol.enable_aol else enabled) else 1
@@ -79,7 +85,7 @@ class AolCarState(AolCarStateBase):
pass
def get_main_cruise(self, ret: structs.CarState) -> bool:
if self.CP_IQ.flags & HyundaiFlagsIQ.MAIN_BTN_LONG_TOGGLE:
if self.CP_IQ.flags & HyundaiFlagsIQ.LONGITUDINAL_MAIN_CRUISE_TOGGLEABLE:
if any(be.type == ButtonType.mainCruise and be.pressed for be in ret.buttonEvents):
self.main_cruise_enabled = not self.main_cruise_enabled
else:
@@ -1,3 +1,7 @@
"""
Copyright © IQ.Lvbs, apart of Project Teal Lvbs, All Rights Reserved, licensed under https://konn3kt.com/tos
"""
from enum import StrEnum
from iqdbc.car import Bus, structs
@@ -1,3 +1,7 @@
"""
Copyright © IQ.Lvbs, apart of Project Teal Lvbs, All Rights Reserved, licensed under https://konn3kt.com/tos
"""
from iqdbc.car import uds
from iqdbc.car.carlog import carlog
from iqdbc.car.isotp_parallel_query import IsoTpParallelQuery
@@ -19,13 +19,30 @@ class EnhancedSmartCruiseControl:
return ESCC_MSG
def update_car_state(self, car_state):
"""
This method is invoked by the CarController to update the car state on the ESCC object.
The updated state is then used to update SCC12 with the current car state values received through ESCC.
:param car_state:
:return:
"""
self.car_state = car_state
def update_scc12(self, values):
"""
Update SCC12 with the current car state values received through ESCC.
These values are sourced directly from the car's SCC radar and provide a more reliable source for AEB and FCA alerts.
:param values: SCC12 to be sent in dictionary form before being packed
:return: Nothing. SCC12 is updated in place.
"""
values["AEB_CmdAct"] = self.car_state.escc_cmd_act
values["CF_VSM_Warn"] = self.car_state.escc_aeb_warning
values["CF_VSM_DecCmdAct"] = self.car_state.escc_aeb_dec_cmd_act
values["CR_VSM_DecCmd"] = self.car_state.escc_aeb_dec_cmd
# TODO-IQ: we should read it from the car's settings and use that value.
# It may not be ideal to set this here directly.
# Observed flickering on the dashboard settings switching between "deactivated" and "active assistance" when sending AEB_Status = 1.
# These values could differ from the user's configuration from the car's settings.
# This indicates that SCC12 likely displays it on the dashboard, and another FCA message may also cause it to appear.
values["AEB_Status"] = 2 # AEB enabled
@@ -13,6 +13,19 @@ FW_VERSIONS_EXT = {
b'\xf1\x00CDH LKAS AT EUR LHD 1.00 1.01 99211-CR700 931',
],
},
# TODO-IQ: HYUNDAI_KONA_EV_NON_SCC has the same FW versions as HYUNDAI_KONA_EV, in the future we may
# allow similar FW versions across different platforms
# CAR.HYUNDAI_KONA_EV_NON_SCC: {
# (Ecu.abs, 0x7d1, None): [
# b'\xf1\x00OS IEB \x02 212 \x11\x13 58520-K4000',
# ],
# (Ecu.eps, 0x7d4, None): [
# b'\xf1\x00OS MDPS C 1.00 1.04 56310K4000\x00 4OEDC104',
# ],
# (Ecu.fwdCamera, 0x7c4, None): [
# b'\xf1\x00OSE LKAS AT USA LHD 1.00 1.00 95740-K4100 W40',
# ],
# },
CAR.GENESIS_G70_2021_NON_SCC: {
(Ecu.eps, 0x7d4, None): [
b'\xf1\x00IK MDPS R 1.00 1.08 57700-G9200 4I2CL108',
@@ -25,6 +38,9 @@ FW_VERSIONS_EXT = {
],
},
CAR.HYUNDAI_KONA_NON_SCC: {
# (Ecu.abs, 0x7d1, None): [
# b'\xf1\x816V5RAJ00040.ELF\xf1\x00\x00\x00\x00\x00\x00\x00',
# ],
(Ecu.eps, 0x7d4, None): [
b'\xf1\x00OS MDPS C 1.00 1.05 56310J9030\x00 4OSDC105',
b'\xf1\x00OS MDPS C 1.00 1.04 56310J9030\x00 4OSDC104',
@@ -44,6 +60,12 @@ FW_VERSIONS_EXT = {
(Ecu.fwdCamera, 0x7C4, None): [
b'\xf1\x00BD LKAS AT USA LHD 1.00 1.02 95740-M6000 J31',
],
# (Ecu.abs, 0x7d1, None): [
# b'\xf1\x816VFRAF00018.ELF\xf1\x00\x00\x00\x00\x00\x00\x00',
# ],
# (Ecu.transmission, 0x7e1, None): [
# b'\xf1\x87CXJQAM4966515JB0x\xa9\x98\x9b\x99fff\x98feg\x88\x88w\x88Ff\x8f\xff{\xff\xff\xff\xa8\xf6\xf1\x816V2C1051\x00\x00\xf1\x006V2B0_C2\x00\x006V2C1051\x00\x00CBD0N20NS8q\xc1&\xd2', # noqa: E501
# ],
},
CAR.KIA_FORTE_2021_NON_SCC: {
(Ecu.eps, 0x7D4, None): [
@@ -52,6 +74,12 @@ FW_VERSIONS_EXT = {
(Ecu.fwdCamera, 0x7C4, None): [
b'\xf1\x00BD LKAS AT USA LHD 1.00 1.04 95740-M6000 J33',
],
# (Ecu.abs, 0x7d1, None): [
# b'\xf1\x816VFRAL00010.ELF\xf1\x00\x00\x00\x00\x00\x00\x00',
# ],
# (Ecu.transmission, 0x7e1, None): [
# b'\xf1\x87CXLQAM0906975JB0\x89\x88\xa6\x8aVfug\xba\x87\x94yffuxgfo\xff\x8b\xff\xff\xff\x91\x82\xf1\x816V2C1051\x00\x00\xf1\x006V2B0_C2\x00\x006V2C1051\x00\x00CBD0N20NS8q\xc1&\xd2', # noqa: E501
# ],
},
CAR.KIA_SELTOS_2023_NON_SCC: {
(Ecu.abs, 0x7d1, None): [
@@ -73,6 +101,7 @@ FW_VERSIONS_EXT = {
},
CAR.HYUNDAI_ELANTRA_2022_NON_SCC: {
(Ecu.eps, 0x7d4, None): [
# b'\xf1\x8756310AA030\x00\xf1\x00CN7 MDPS C 1.00 1.06 56310AA030\x00 4CNDC106',
b'\xf1\x00CN7 MDPS R 1.00 1.04 57700-IB000 4CNNP104',
],
(Ecu.fwdCamera, 0x7c4, None): [
@@ -80,9 +109,11 @@ FW_VERSIONS_EXT = {
b'\xf1\x00CN7 MFC AT USA LHD 1.00 1.00 99210-IB000 210531',
],
(Ecu.abs, 0x7d1, None): [
# b'\xf1\x8758910-AB500\xf1\x00CN ESC \t 100 \x06\x01 58910-AB500',
b'\xf1\x00CN ESC \t 100!\x05\x01 58910-IB000',
],
(Ecu.transmission, 0x7e1, None): [
# b'\xf1\x87CXNQEM4091445JB3g\x98\x98\x89\x99\x87gv\x89wuwgwv\x89hD_\xffx\xff\xff\xff\x86\xeb\xf1\x89HT6VA640A1\xf1\x82CCN0N20NS5\x00\x00\x00\x00\x00\x00', # noqa: E501
b'\xf1\x00T02601BL T02900A1 WCN7T20XXX900NS4\xf7\xccz\xf6',
],
},
@@ -1,3 +1,6 @@
"""
Copyright © IQ.Lvbs, apart of Project Teal Lvbs, All Rights Reserved, licensed under https://konn3kt.com/tos
"""
from abc import ABC, abstractmethod
from iqdbc.car import structs
from iqdbc.car.hyundai.values import HyundaiFlags
@@ -0,0 +1,3 @@
"""
Copyright © IQ.Lvbs, apart of Project Teal Lvbs, All Rights Reserved, licensed under https://konn3kt.com/tos
"""
@@ -1,3 +1,7 @@
"""
Copyright © IQ.Lvbs, apart of Project Teal Lvbs, All Rights Reserved, licensed under https://konn3kt.com/tos
"""
from dataclasses import dataclass, field
from iqdbc.car.hyundai.values import CAR
@@ -5,6 +9,9 @@ from iqdbc.car.hyundai.values import CAR
@dataclass
class CarTuningConfig:
v_ego_stopping: float = 0.25
v_ego_starting: float = 0.10
stopping_decel_rate: float = 0.40
lookahead_jerk_bp: list[float] = field(default_factory=lambda: [5., 20.])
lookahead_jerk_upper_v: list[float] = field(default_factory=lambda: [0.25, 0.5])
lookahead_jerk_lower_v: list[float] = field(default_factory=lambda: [0.15, 0.3])
@@ -15,15 +22,22 @@ class CarTuningConfig:
# Default configurations for different car types
TUNING_CONFIGS = {
"CANFD": CarTuningConfig(
v_ego_stopping=0.365,
lookahead_jerk_bp=[2., 5., 20.],
lookahead_jerk_upper_v=[0.25, 0.5, 1.0],
lookahead_jerk_lower_v=[0.05, 0.10, 0.325],
),
"EV": CarTuningConfig(
stopping_decel_rate=0.45,
v_ego_stopping=0.35,
lookahead_jerk_upper_v=[0.3, 0.7],
lookahead_jerk_lower_v=[0.2, 0.4],
),
"HYBRID": CarTuningConfig(),
"HYBRID": CarTuningConfig(
v_ego_starting=0.15,
stopping_decel_rate=0.45,
v_ego_stopping=0.4,
),
"DEFAULT": CarTuningConfig(
lookahead_jerk_bp=[2., 5., 20.],
lookahead_jerk_upper_v=[0.25, 0.5, 1.0],
@@ -34,11 +48,13 @@ TUNING_CONFIGS = {
# Car-specific configs
CAR_SPECIFIC_CONFIGS = {
CAR.KIA_NIRO_EV: CarTuningConfig(
stopping_decel_rate=0.3,
lookahead_jerk_upper_v=[0.3, 1.0],
lookahead_jerk_lower_v=[0.2, 0.4],
jerk_limits=2.5,
),
CAR.KIA_NIRO_PHEV_2022: CarTuningConfig(
stopping_decel_rate=0.3,
lookahead_jerk_upper_v=[0.3, 1.0],
lookahead_jerk_lower_v=[0.15, 0.3],
jerk_limits=4.0,
@@ -1,3 +1,7 @@
"""
Copyright © IQ.Lvbs, apart of Project Teal Lvbs, All Rights Reserved, licensed under https://konn3kt.com/tos
"""
import numpy as np
from dataclasses import dataclass
@@ -1,3 +1,7 @@
"""
Copyright © IQ.Lvbs, apart of Project Teal Lvbs, All Rights Reserved, licensed under https://konn3kt.com/tos
"""
import numpy as np
from iqdbc.car import structs, DT_CTRL, rate_limit
@@ -33,6 +37,10 @@ def get_car_config(CP: structs.CarParams) -> CarTuningConfig:
def get_longitudinal_tune(CP: structs.CarParams) -> None:
config = get_car_config(CP)
CP.vEgoStopping = config.v_ego_stopping
CP.vEgoStarting = config.v_ego_starting
CP.stoppingDecelRate = config.stopping_decel_rate
CP.startingState = False
CP.longitudinalActuatorDelay = config.longitudinal_actuator_delay
@@ -73,9 +73,9 @@ class RadarInterfaceExt(EsccRadarInterfaceBase):
if valid:
self.pts[ii].measured = True
self.pts[ii].dRel = msg['ACC_ObjDist']
self.pts[ii].yRel = float('nan')
self.pts[ii].yRel = float('nan') # FIXME-IQ: Only some cars have lateral position from SCC
self.pts[ii].vRel = msg['ACC_ObjRelSpd']
self.pts[ii].aRel = float('nan')
self.pts[ii].aRel = float('nan') # TODO-IQ: calculate from ACC_ObjRelSpd and with timestep 50Hz (needs to modify in interfaces.py)
self.pts[ii].yvRel = float('nan')
else:
@@ -0,0 +1,3 @@
"""
Copyright © IQ.Lvbs, apart of Project Teal Lvbs, All Rights Reserved, licensed under https://konn3kt.com/tos
"""
@@ -0,0 +1,61 @@
import pytest
from hypothesis import given, strategies as st, settings, HealthCheck
from iqdbc.iqpilot.car.hyundai.escc import EnhancedSmartCruiseControl, ESCC_MSG
from iqdbc.car.hyundai.carstate import CarState
from iqdbc.car import structs
from iqdbc.iqpilot.car.hyundai.values import HyundaiFlagsIQ
@pytest.fixture
def car_params():
params = structs.CarParams()
params.carFingerprint = "HYUNDAI_SONATA"
return params
@pytest.fixture
def car_params_iq():
params = structs.IQCarParams()
params.flags = HyundaiFlagsIQ.ENHANCED_SCC
return params
@pytest.fixture
def escc(car_params, car_params_iq):
return EnhancedSmartCruiseControl(car_params, car_params_iq)
class TestEscc:
def test_escc_msg_id(self, escc):
assert escc.trigger_msg == ESCC_MSG
@settings(suppress_health_check=[HealthCheck.function_scoped_fixture])
@given(st.integers(min_value=0, max_value=255))
def test_enabled_flag(self, car_params, car_params_iq, value):
car_params_iq.flags = value
escc = EnhancedSmartCruiseControl(car_params, car_params_iq)
assert escc.enabled == (value & HyundaiFlagsIQ.ENHANCED_SCC)
def test_update_car_state(self, escc, car_params, car_params_iq):
car_state = CarState(car_params, car_params_iq)
car_state.escc_cmd_act = 1
car_state.escc_aeb_warning = 1
car_state.escc_aeb_dec_cmd_act = 1
car_state.escc_aeb_dec_cmd = 1
escc.update_car_state(car_state)
assert escc.car_state == car_state
def test_update_scc12(self, escc, car_params, car_params_iq):
car_state = CarState(car_params, car_params_iq)
car_state.escc_cmd_act = 1
car_state.escc_aeb_warning = 1
car_state.escc_aeb_dec_cmd_act = 1
car_state.escc_aeb_dec_cmd = 1
escc.update_car_state(car_state)
scc12_message = {}
escc.update_scc12(scc12_message)
assert scc12_message["AEB_CmdAct"] == 1
assert scc12_message["CF_VSM_Warn"] == 1
assert scc12_message["CF_VSM_DecCmdAct"] == 1
assert scc12_message["CR_VSM_DecCmd"] == 1
assert scc12_message["AEB_Status"] == 2
@@ -0,0 +1,77 @@
from enum import IntFlag
from iqdbc.iqpilot.car.hyundai.lead_data_ext import LeadDataCarController, CanLeadData, CanFdLeadData
from iqdbc.car import structs
from iqdbc.car.hyundai.values import HyundaiFlags
def make_carparams(flags: IntFlag = HyundaiFlags.LEGACY):
cp = structs.CarParams()
cp.carFingerprint = "HYUNDAI_SONATA"
cp.flags = flags.value
return cp
def make_iq_carcontrol(leadDistance=10.0, leadRelSpeed=0.0, leadVisible=True):
c = structs.IQCarControl()
c.leadOne.dRel = leadDistance
c.leadOne.vRel = leadRelSpeed
c.leadOne.status = leadVisible
return c
class TestLeadDataCarController:
def test_update_object_gap(self):
ctrl = LeadDataCarController(make_carparams())
# Initial value should be 0
assert ctrl.object_gap == 0
# Set to 15 (should become 2 after hysteresis)
for _ in range(ctrl.LEAD_HYSTERESIS_FRAMES):
ctrl._update_object_gap(15)
assert ctrl.object_gap == 2
# Set to 22 (should become 3 after hysteresis)
for _ in range(ctrl.LEAD_HYSTERESIS_FRAMES):
ctrl._update_object_gap(22)
assert ctrl.object_gap == 3
# Set to 0 (should become 0 after hysteresis)
for _ in range(ctrl.LEAD_HYSTERESIS_FRAMES):
ctrl._update_object_gap(0)
assert ctrl.object_gap == 0
def test_update_lead_visible_hysteresis(self):
ctrl = LeadDataCarController(make_carparams())
ctrl._update_lead_visible_hysteresis(True)
assert isinstance(ctrl.lead_visible, bool)
ctrl._update_lead_visible_hysteresis(False)
assert isinstance(ctrl.lead_visible, bool)
def test_update(self):
ctrl = LeadDataCarController(make_carparams())
iq_control = make_iq_carcontrol(leadDistance=25, leadRelSpeed=-0.5, leadVisible=True)
ctrl.update(iq_control)
assert ctrl.lead_distance == 25
assert ctrl.lead_rel_speed == -0.5
assert isinstance(ctrl.lead_visible, bool)
def test_lead_data_can(self):
ctrl = LeadDataCarController(make_carparams())
ctrl.object_gap = 1
ctrl.lead_distance = 10
ctrl.lead_rel_speed = -0.3
ctrl.lead_visible = True
ld = ctrl.lead_data
assert isinstance(ld, CanLeadData)
assert ld.object_rel_gap == 2
def test_lead_data_canfd(self):
ctrl = LeadDataCarController(make_carparams(HyundaiFlags.CANFD))
ctrl.object_gap = 1
ctrl.lead_distance = 10
ctrl.lead_rel_speed = 1.0
ctrl.lead_visible = True
ld = ctrl.lead_data
assert isinstance(ld, CanFdLeadData)
assert ld.object_rel_gap == 1
@@ -0,0 +1,119 @@
from parameterized import parameterized
from iqdbc.car import CanData
from iqdbc.car.car_helpers import interfaces
from iqdbc.car.hyundai.values import CAR, HyundaiFlags
from iqdbc.iqpilot.car.hyundai.escc import ESCC_MSG
ESCC_CARS = [
(CAR.HYUNDAI_ELANTRA_2021, ESCC_MSG),
]
CAMERA_SCC_CARS = [
(CAR.HYUNDAI_KONA_EV_2022, 0, 0x420, "SCC11"),
(CAR.HYUNDAI_IONIQ_5, HyundaiFlags.CANFD_CAMERA_SCC.value, 0x1A0, "SCC_CONTROL"),
]
STANDARD_RADAR_CARS = [
(CAR.HYUNDAI_ELANTRA_2021, 0),
(CAR.HYUNDAI_SANTA_FE, 0),
]
class TestRadarInterfaceExt:
@staticmethod
def _setup_platform(car_name, additional_flags=0, escc_msg=None):
"""Set up the platform with specific parameters"""
CarInterface = interfaces[car_name]
CP = CarInterface.get_non_essential_params(car_name)
CP.flags |= additional_flags
CP_IQ = CarInterface.get_non_essential_params_iq(CP, car_name)
CI = CarInterface(CP, CP_IQ)
RD = CI.RadarInterface(CP, CP_IQ)
if escc_msg is not None and hasattr(RD, 'use_escc'):
try:
RD.use_escc = True
except AttributeError:
object.__setattr__(RD, 'use_escc', True)
return RD, CP, CP_IQ
@parameterized.expand(ESCC_CARS)
def test_escc_radar_interface(self, car_name, escc_msg):
"""Test radar interface for ESCC-enabled cars"""
RD, CP, CP_IQ = self._setup_platform(car_name, escc_msg=escc_msg)
# Assert that ESCC features are present
if hasattr(RD, 'use_escc'):
assert RD.use_escc, "ESCC car should have use_escc=True"
if hasattr(RD, 'use_radar_interface_ext'):
assert RD.use_radar_interface_ext, "ESCC car should use radar interface ext"
# Run radar interface once
RD.update([])
# Test radar fault
if not CP.radarUnavailable and RD.rcp is not None:
cans = [(0, [CanData(0, b'', 0) for _ in range(5)])]
rr = RD.update(cans)
assert rr is None or len(rr.errors) > 0
@parameterized.expand(CAMERA_SCC_CARS)
def test_camera_scc_radar_interface(self, car_name, flags, expected_trigger, msg_src):
"""Test radar interface for Camera SCC cars"""
RD, CP, CP_IQ = self._setup_platform(car_name, additional_flags=flags)
# Assert Camera SCC flag is set appropriately
if flags & HyundaiFlags.CAMERA_SCC:
assert CP.flags & HyundaiFlags.CAMERA_SCC, "Car should have CAMERA_SCC flag"
if flags & HyundaiFlags.CANFD_CAMERA_SCC:
assert CP.flags & HyundaiFlags.CANFD_CAMERA_SCC, "Car should have CANFD_CAMERA_SCC flag"
# Check if using radar interface ext
if hasattr(RD, 'use_radar_interface_ext'):
assert RD.use_radar_interface_ext, "Camera SCC car should use radar interface ext"
# Verify trigger message
if hasattr(RD, 'trigger_msg'):
assert RD.trigger_msg == expected_trigger, f"Expected trigger_msg {expected_trigger}, got {RD.trigger_msg}"
# Run radar interface once
RD.update([])
# Test radar fault
if not CP.radarUnavailable and RD.rcp is not None:
cans = [(0, [CanData(0, b'', 0) for _ in range(5)])]
rr = RD.update(cans)
assert rr is None or len(rr.errors) > 0
@parameterized.expand(STANDARD_RADAR_CARS)
def test_standard_radar_interface(self, car_name, flags):
"""Test radar interface for standard radar cars"""
RD, CP, CP_IQ = self._setup_platform(car_name, additional_flags=flags)
# Standard cars should not use radar interface ext
if hasattr(RD, 'use_radar_interface_ext'):
assert not RD.use_radar_interface_ext, "Standard car should not use radar interface ext"
# Run radar interface once
RD.update([])
# For standard radar, test the _update method directly if available
if not CP.radarUnavailable and RD.rcp is not None and \
hasattr(RD, '_update') and hasattr(RD, 'trigger_msg'):
# Setup for _update test if needed
if hasattr(RD, 'updated_messages'):
RD.updated_messages = {RD.trigger_msg}
RD._update(RD.updated_messages)
# Test radar fault
if not CP.radarUnavailable and RD.rcp is not None:
cans = [(0, [CanData(0, b'', 0) for _ in range(5)])]
rr = RD.update(cans)
assert rr is None or len(rr.errors) > 0
@@ -0,0 +1,146 @@
"""
Copyright © IQ.Lvbs, apart of Project Teal Lvbs, All Rights Reserved, licensed under https://konn3kt.com/tos
"""
import unittest
import numpy as np
from unittest.mock import Mock
from iqdbc.iqpilot.car.hyundai.longitudinal.controller import LongitudinalController, LongitudinalState
from iqdbc.iqpilot.car.hyundai.values import HyundaiFlagsIQ
from iqdbc.car import DT_CTRL, structs
from iqdbc.car.interfaces import CarStateBase
from iqdbc.car.hyundai.values import HyundaiFlags
LongCtrlState = structs.CarControl.Actuators.LongControlState
class TestLongitudinalTuningController(unittest.TestCase):
def setUp(self):
self.mock_CP = Mock(carFingerprint="KIA_NIRO_EV", flags=0)
self.mock_CP.radarUnavailable = False # ensure tuning branch
self.mock_CP_IQ = Mock(flags=0)
self.controller = LongitudinalController(self.mock_CP, self.mock_CP_IQ)
def test_init(self):
"""Test controller initialization"""
self.assertIsInstance(self.controller.tuning, LongitudinalState)
self.assertEqual(self.controller.desired_accel, 0.0)
self.assertEqual(self.controller.actual_accel, 0.0)
self.assertEqual(self.controller.jerk_upper, 0.0)
self.assertEqual(self.controller.jerk_lower, 0.0)
self.assertEqual(self.controller.comfort_band_upper, 0.0)
self.assertEqual(self.controller.comfort_band_lower, 0.0)
def test_make_jerk_flag_off(self):
"""Test when LONG_TUNING_DYNAMIC flag is off"""
mock_CC, mock_CS = Mock(spec=structs.CarControl), Mock(spec=CarStateBase)
mock_CS.out = Mock()
mock_CS.out.vEgo = 0.0
mock_CS.out.aEgo = 0.0
mock_CS.aBasis = 0.0
# Test with PID state
self.controller.calculate_jerk(mock_CC, mock_CS, LongCtrlState.pid)
print(f"[PID state] jerk_upper={self.controller.jerk_upper:.2f}, jerk_lower={self.controller.jerk_lower:.2f}")
self.assertEqual(self.controller.jerk_upper, 3.0)
self.assertEqual(self.controller.jerk_lower, 5.0)
# Test with non-PID state
self.controller.calculate_jerk(mock_CC, mock_CS, LongCtrlState.stopping)
print(f"[Non-PID state] jerk_upper={self.controller.jerk_upper:.2f}, jerk_lower={self.controller.jerk_lower:.2f}")
self.assertEqual(self.controller.jerk_upper, 1.0)
self.assertEqual(self.controller.jerk_lower, 5.0)
def test_make_jerk_flag_on(self):
"""Only verify that limits update when flags are on."""
self.controller.CP_IQ.flags = HyundaiFlagsIQ.LONG_TUNING_DYNAMIC
self.controller.CP.flags = HyundaiFlags.CANFD
mock_CC = Mock()
mock_CC.actuators = Mock(accel=1.0)
mock_CC.longActive = True
self.controller.stopping = False
mock_CS = Mock()
mock_CS.out = Mock(aEgo=0.8, vEgo=3.0)
mock_CS.aBasis = 0.8
self.controller.calculate_jerk(mock_CC, mock_CS, LongCtrlState.pid)
print(f"[FlagOn] jerk_upper={self.controller.jerk_upper:.3f}, jerk_lower={self.controller.jerk_lower:.3f}")
self.assertGreater(self.controller.jerk_upper, 0.0)
self.assertGreater(self.controller.jerk_lower, 0.0)
def test_a_value_jerk_scaling(self):
"""Test a_value jerk scaling under tuning branch."""
self.controller.CP_IQ.flags = HyundaiFlagsIQ.LONG_TUNING_DYNAMIC
self.controller.CP.radarUnavailable = False
mock_CC = Mock()
mock_CC.actuators = Mock(accel=1.0)
mock_CC.longActive = True
print("[a_value] starting accel_last:", self.controller.tuning.accel_last)
# first pass: limit to jerk_upper * DT_CTRL * 2 = 0.1
self.controller.jerk_upper = 0.1 / (DT_CTRL * 2)
self.controller.accel_cmd = 1.0 # ensure accel_cmd is set
self.controller.calculate_accel(mock_CC)
print(f"[a_value] pass1 actual_accel={self.controller.actual_accel:.5f}")
self.assertAlmostEqual(self.controller.actual_accel, 0.1, places=5)
# second pass: limit increment by new jerk_upper
mock_CC.actuators.accel = 0.7
self.controller.jerk_upper = 0.2 / (DT_CTRL * 2)
self.controller.accel_cmd = 0.7 # update accel_cmd
self.controller.calculate_accel(mock_CC)
print(f"[a_value] pass2 actual_accel={self.controller.actual_accel:.5f}")
self.assertAlmostEqual(self.controller.actual_accel, 0.3, places=5)
def test_make_jerk_realistic_profile(self):
"""Test make_jerk with realistic velocity and acceleration profile"""
np.random.seed(42)
num_points = 30
segments = [
np.random.uniform(0.3, 0.8, num_points//4),
np.random.uniform(0.8, 1.6, num_points//4),
np.random.uniform(-0.2, 0.2, num_points//4),
np.random.uniform(-1.2, -0.5, num_points//8),
np.random.uniform(-2.2, -1.2, num_points//8)
]
accels = np.concatenate(segments)[:num_points]
vels = np.zeros_like(accels)
vels[0] = 5.0
for i in range(1, len(accels)):
vels[i] = max(0.0, min(30.0, vels[i-1] + accels[i-1] * (DT_CTRL*2)))
mock_CC, mock_CS = Mock(), Mock()
mock_CC.actuators, mock_CS.out = Mock(), Mock()
mock_CC.longActive = True
self.controller.stopping = False
# Test with LONG_TUNING_DYNAMIC only
self.controller.CP_IQ.flags = HyundaiFlagsIQ.LONG_TUNING_DYNAMIC
for v, a in zip(vels, accels, strict=True):
mock_CS.out.vEgo = float(v)
mock_CS.out.aEgo = float(a)
mock_CS.aBasis = float(a)
mock_CC.actuators.accel = float(a)
self.controller.calculate_jerk(mock_CC, mock_CS, LongCtrlState.pid)
print(f"[realistic][LONG_TUNING_DYNAMIC] v={v:.2f}, a={a:.2f}, jerk_upper={self.controller.jerk_upper:.2f}, jerk_lower={self.controller.jerk_lower:.2f}")
self.assertGreater(self.controller.jerk_upper, 0.0)
# Reset controller before next test
self.controller.tuning = LongitudinalState()
self.controller.jerk_upper = 0.5
self.controller.jerk_lower = 0.5
# Test with LONG_TUNING_DYNAMIC and LONG_TUNING_PREDICTIVE
self.controller.CP_IQ.flags = HyundaiFlagsIQ.LONG_TUNING_DYNAMIC | HyundaiFlagsIQ.LONG_TUNING_PREDICTIVE
for v, a in zip(vels, accels, strict=True):
mock_CS.out.vEgo = float(v)
mock_CS.out.aEgo = float(a)
mock_CS.aBasis = float(a)
mock_CC.actuators.accel = float(a)
self.controller.calculate_jerk(mock_CC, mock_CS, LongCtrlState.pid)
print(f"[realistic][LONG_TUNING_PREDICTIVE] v={v:.2f}, a={a:.2f}, " +
f"jerk_upper={self.controller.jerk_upper:.2f}, jerk_lower={self.controller.jerk_lower:.2f}")
self.assertGreater(self.controller.jerk_upper, 0.0)
if __name__ == "__main__":
unittest.main()
+16 -9
View File
@@ -1,25 +1,32 @@
"""
Copyright © IQ.Lvbs, apart of Project Teal Lvbs, All Rights Reserved, licensed under https://konn3kt.com/tos
"""
from enum import IntFlag
class HyundaiSafetyFlagsIQ:
DEFAULT = 0
ESCC = 16
MAIN_BTN_LONG_TOGGLE = 32
HAS_LDA_BUTTON = 64
NON_SCC = 128
ESCC = 1
LONG_MAIN_CRUISE_TOGGLEABLE = 2
HAS_LDA_BUTTON = 4
NON_SCC = 8
class HyundaiFlagsIQ(IntFlag):
"""
Flags for Hyundai specific quirks within iqpilot.
"""
ENHANCED_SCC = 1
HAS_LFA_BUTTON = 2
MAIN_BTN_LONG_TOGGLE = 2 ** 2
HAS_LFA_BUTTON = 2 # Deprecated in favor of HyundaiFlags.HAS_LDA_BUTTON
LONGITUDINAL_MAIN_CRUISE_TOGGLEABLE = 2 ** 2
ENABLE_RADAR_TRACKS_DEPRECATED = 2 ** 3
LONG_TUNING_DYNAMIC = 2 ** 4
LONG_TUNING_PREDICTIVE = 2 ** 5
NON_SCC = 2 ** 6
NON_SCC_RADAR_FCA = 2 ** 7
NON_SCC_NO_FCA = 2 ** 8
SPEED_LIMIT_AVAILABLE = 2 ** 9
NON_SCC_RADAR_FCA = 2 ** 7 # most with FCA come from the camera
NON_SCC_NO_FCA = 2 ** 8 # not all have FCA
SPEED_LIMIT_AVAILABLE = 2 ** 9 # platforms with speed limit data available
HAS_LKAS12 = 2 ** 10
@@ -89,8 +89,6 @@ def setup_interfaces(CI, CP: structs.CarParams, CP_IQ: structs.IQCarParams,
def _initialize_custom_longitudinal_tuning(CI, CP: structs.CarParams, CP_IQ: structs.IQCarParams,
params_dict: dict[str, str]) -> None:
# HKG longitudinal tuning lineage in this port traces to Jason Wen, James
# Vecellio-Grant, and carrotpilot; keep that attribution with this path.
# Hyundai Custom Longitudinal Tuning
if CP.brand == 'hyundai':
hyundai_longitudinal_tuning = int(params_dict.get("HyundaiLongitudinalTuning", 0))
+10 -3
View File
@@ -1,9 +1,14 @@
import re
import json
import os
import unicodedata
from iqdbc.car.common.basedir import BASEDIR
from iqdbc.car.docs import get_all_footnotes, get_params_for_docs
from iqdbc.car.values import PLATFORMS
CAR_LIST_JSON_OUT = os.path.join(BASEDIR, "../", "iqpilot", "car", "car_list.json")
def get_car_list() -> dict[str, dict[str, list[str] | str]]:
collected_footnote = get_all_footnotes()
@@ -57,6 +62,8 @@ def build_sorted_car_list(platforms, footnotes) -> dict[str, dict[str, list[str]
if __name__ == "__main__":
# get_car_list() is the raw platform source; the shipped catalog is generated
# (and encoded to its on-disk envelope) by the main-repo entry point:
print("run: python -m openpilot.iqpilot.selfdrive.car.vehicle_catalog")
platform_list = get_car_list()
with open(CAR_LIST_JSON_OUT, "w") as json_file:
json.dump(platform_list, json_file, indent=2, ensure_ascii=False)
print(f"Generated and written to {CAR_LIST_JSON_OUT}")
@@ -18,18 +18,9 @@ class CarStateExt:
self.CP_IQ = CP_IQ
self.infotainment_3_finger_press = 0
self.vehicle_bus_available = bool(CP_IQ.flags & TeslaFlagsIQ.HAS_VEHICLE_BUS)
def update(self, ret: structs.CarState, ret_iq: structs.IQCarState, can_parsers: dict[StrEnum, CANParser]) -> None:
if Bus.adas in can_parsers:
cp_adas = can_parsers[Bus.adas]
odometer_km = float(cp_adas.vl["ID3B6UI_odometer"].get("UI_odometer", 0.0))
if 0.0 < odometer_km < 4294967.296:
self.vehicle_bus_available = True
ret.odometer = odometer_km
if self.vehicle_bus_available and Bus.adas in can_parsers:
if self.CP_IQ.flags & TeslaFlagsIQ.HAS_VEHICLE_BUS:
cp_adas = can_parsers[Bus.adas]
prev_infotainment_3_finger_press = self.infotainment_3_finger_press
@@ -75,9 +66,7 @@ class CarStateExt:
def get_parser(CP: structs.CarParams, CP_IQ: structs.IQCarParams) -> dict[StrEnum, CANParser]:
messages = {}
if Bus.adas in DBC[CP.carFingerprint]:
# Parse the absolute odometer even if the initial fingerprint missed a
# slow vehicle-bus marker. Runtime data latches support safely.
if CP_IQ.flags & TeslaFlagsIQ.HAS_VEHICLE_BUS:
messages[Bus.adas] = CANParser(DBC[CP.carFingerprint][Bus.adas], [], CANBUS.vehicle)
return messages
@@ -1,25 +1,12 @@
import json
import os
from iqdbc.car.common.basedir import BASEDIR
from iqdbc.iqpilot.car.platform_list import get_car_list
CATALOG_JSON = os.path.join(BASEDIR, "..", "..", "iqpilot", "selfdrive", "car", "vehicle_catalog.json")
_KEY_TO_ATTR = {"id": "platform", "mk": "make", "grp": "brand", "mdl": "model", "yrs": "year", "req": "package"}
def _decode(envelope) -> dict:
out = {}
for record in (envelope.get("vehicles") or {}).values():
out[record.get("label", "")] = {attr: record.get(key) for key, attr in _KEY_TO_ATTR.items()}
return out
from iqdbc.iqpilot.car.platform_list import get_car_list, CAR_LIST_JSON_OUT
class TestCarList:
def test_generator(self):
generated = get_car_list()
with open(CATALOG_JSON) as f:
shipped = _decode(json.load(f))
generated_car_list = json.dumps(get_car_list(), indent=2, ensure_ascii=False)
with open(CAR_LIST_JSON_OUT) as f:
current_car_list = f.read()
assert shipped == generated, "Run: python -m openpilot.iqpilot.selfdrive.car.vehicle_catalog"
assert generated_car_list == current_car_list, "Run iqdbc/iqpilot/car/platform_list.py to update the car list"
+3 -3
View File
@@ -6,7 +6,7 @@ static const float ISO_LATERAL_ACCEL = 3.0; // m/s^2
static const float EARTH_G = 9.81;
static const float AVERAGE_ROAD_ROLL = 0.06; // ~3.4 degrees, 6% superelevation
static const float MAX_LATERAL_ACCEL = ISO_LATERAL_ACCEL + (EARTH_G * AVERAGE_ROAD_ROLL); // ~5.6 m/s^2
static const float MAX_LATERAL_ACCEL = ISO_LATERAL_ACCEL + (EARTH_G * AVERAGE_ROAD_ROLL); // ~3.6 m/s^2
bool is_lat_active(void) {
return controls_allowed || aol_is_lateral_control_allowed_by_aol();
@@ -233,7 +233,7 @@ bool steer_angle_cmd_checks(int desired_angle, bool steer_control_enabled, const
if (limits.angle_is_curvature) {
// Limit to average banked road since safety doesn't have the roll
static const float MAX_LATERAL_ACCEL = ISO_LATERAL_ACCEL - (EARTH_G * AVERAGE_ROAD_ROLL); // ~4.4 m/s^2
static const float MAX_LATERAL_ACCEL = ISO_LATERAL_ACCEL - (EARTH_G * AVERAGE_ROAD_ROLL); // ~2.4 m/s^2
// Allow small tolerance by using minimum speed and rounding curvature up
const float speed_lower = SAFETY_MAX(vehicle_speed.min / VEHICLE_SPEED_FACTOR, 1.0);
@@ -301,7 +301,7 @@ bool steer_angle_cmd_checks_vm(int desired_angle, bool steer_control_enabled, co
// TODO: remove the inaccurate breakpoint angle limiting function above and always use this one
// Highway curves are rolled in the direction of the turn, add tolerance to compensate
static const float MAX_LATERAL_ACCEL = ISO_LATERAL_ACCEL + (EARTH_G * AVERAGE_ROAD_ROLL); // ~5.6 m/s^2
static const float MAX_LATERAL_ACCEL = ISO_LATERAL_ACCEL + (EARTH_G * AVERAGE_ROAD_ROLL); // ~3.6 m/s^2
// Lower than ISO 11270 lateral jerk limit, which is 5.0 m/s^3
static const float MAX_LATERAL_JERK = 3.0 + (EARTH_G * AVERAGE_ROAD_ROLL); // ~3.6 m/s^3
@@ -18,10 +18,10 @@ enum {
};
enum {
HYUNDAI_PARAM_IQ_ESCC = 16,
HYUNDAI_PARAM_IQ_MAIN_BTN_LONG_TOGGLE = 32,
HYUNDAI_PARAM_IQ_HAS_LDA_BUTTON = 64,
HYUNDAI_PARAM_IQ_NON_SCC = 128,
HYUNDAI_PARAM_IQ_ESCC = 1,
HYUNDAI_PARAM_IQ_LONGITUDINAL_MAIN_CRUISE_TOGGLEABLE = 2,
HYUNDAI_PARAM_IQ_HAS_LDA_BUTTON = 4,
HYUNDAI_PARAM_IQ_NON_SCC = 8,
};
// common state
@@ -53,8 +53,8 @@ bool hyundai_alt_limits_2 = false;
extern bool hyundai_escc;
bool hyundai_escc = false;
extern bool hyundai_main_btn_long_toggle;
bool hyundai_main_btn_long_toggle = false;
extern bool hyundai_longitudinal_main_cruise_toggleable;
bool hyundai_longitudinal_main_cruise_toggleable = false;
extern bool hyundai_has_lda_button;
bool hyundai_has_lda_button = false;
@@ -87,7 +87,7 @@ void hyundai_common_init(uint16_t param) {
hyundai_alt_limits_2 = GET_FLAG(param, HYUNDAI_PARAM_ALT_LIMITS_2);
hyundai_escc = GET_FLAG(current_safety_param_iq, HYUNDAI_PARAM_IQ_ESCC);
hyundai_main_btn_long_toggle = GET_FLAG(current_safety_param_iq, HYUNDAI_PARAM_IQ_MAIN_BTN_LONG_TOGGLE);
hyundai_longitudinal_main_cruise_toggleable = GET_FLAG(current_safety_param_iq, HYUNDAI_PARAM_IQ_LONGITUDINAL_MAIN_CRUISE_TOGGLEABLE);
hyundai_has_lda_button = GET_FLAG(current_safety_param_iq, HYUNDAI_PARAM_IQ_HAS_LDA_BUTTON);
hyundai_non_scc = GET_FLAG(current_safety_param_iq, HYUNDAI_PARAM_IQ_NON_SCC);
@@ -144,7 +144,7 @@ void hyundai_common_cruise_buttons_check(const int cruise_button, const bool mai
}
// toggle main cruise state on rising edge of main cruise button
if (main_button && !main_button_prev && hyundai_main_btn_long_toggle) {
if (main_button && !main_button_prev && hyundai_longitudinal_main_cruise_toggleable) {
acc_main_on = !acc_main_on;
}
+26 -44
View File
@@ -17,7 +17,7 @@
{.msg = {{0x3DF, 1, 8, 2U, .ignore_checksum = true, .ignore_counter = true, .ignore_quality_flag = true}, { 0 }, { 0 }}}, /* UI_status2 */ \
static bool tesla_longitudinal = false;
static bool tesla_legacy_das_steering = false;
static bool tesla_fsd_14 = false;
static bool tesla_stock_aeb = false;
// Only rising edges while controls are not allowed are considered for these systems:
@@ -50,9 +50,6 @@ static uint8_t tesla_get_counter(const CANPacket_t *msg) {
} else if (msg->addr == 0x370U) {
// Signal: EPAS3S_sysStatusCounter
cnt = msg->data[6] & 0x0FU;
} else if (msg->addr == 0x3E9U) {
// Signal: DAS_bodyControlsCounter
cnt = msg->data[6] >> 4;
} else {
}
return cnt;
@@ -60,8 +57,8 @@ static uint8_t tesla_get_counter(const CANPacket_t *msg) {
static int _tesla_get_checksum_byte(const int addr) {
int checksum_byte = -1;
if ((addr == 0x370) || (addr == 0x2b9) || (addr == 0x155) || (addr == 0x3E9)) {
// Signal: EPAS3S_sysStatusChecksum, DAS_controlChecksum, ESP_wheelRotationChecksum, DAS_bodyControlsChecksum
if ((addr == 0x370) || (addr == 0x2b9) || (addr == 0x155)) {
// Signal: EPAS3S_sysStatusChecksum, DAS_controlChecksum, ESP_wheelRotationChecksum
checksum_byte = 7;
} else if (addr == 0x488) {
// Signal: DAS_steeringControlChecksum
@@ -112,9 +109,18 @@ static bool tesla_get_quality_flag_valid(const CANPacket_t *msg) {
return valid;
}
static int tesla_get_steer_ctrl_type(const uint8_t byte2) {
// Older Tesla firmware used a 2-bit field (now 3-bit) for DAS_steeringControlType
return tesla_legacy_das_steering ? (byte2 >> 6) : ((byte2 >> 5) & 0x07U);
static int tesla_get_steer_ctrl_type(const int ctrl_type) {
// Returns ANGLE_CONTROL-equivalent control type for FSD 14
int steer_ctrl_type = ctrl_type;
if (tesla_fsd_14) {
if (ctrl_type == 1) {
steer_ctrl_type = 2;
} else if (ctrl_type == 2) {
steer_ctrl_type = 1;
} else {
}
}
return steer_ctrl_type;
}
static void tesla_rx_hook(const CANPacket_t *msg) {
@@ -257,17 +263,19 @@ static bool tesla_tx_hook(const CANPacket_t *msg) {
// We use 1/10 deg as a unit here
int raw_angle_can = ((msg->data[0] & 0x7FU) << 8) | msg->data[1];
int desired_angle = raw_angle_can - 16384;
int steer_control_type = tesla_get_steer_ctrl_type(msg->data[2]);
bool steer_control_enabled = (steer_control_type == 1) || // ANGLE_CONTROL
(steer_control_type == 2); // LANE_KEEP_ASSIST
int steer_control_type = msg->data[2] >> 6;
const int angle_ctrl_type = tesla_get_steer_ctrl_type(1);
const int lkas_ctrl_type = tesla_get_steer_ctrl_type(2);
bool steer_control_enabled = (steer_control_type == angle_ctrl_type) || // ANGLE_CONTROL
(steer_control_type == lkas_ctrl_type); // LANE_KEEP_ASSIST
if (steer_angle_cmd_checks_vm(desired_angle, steer_control_enabled, TESLA_STEERING_LIMITS, TESLA_STEERING_PARAMS)) {
violation = true;
}
bool valid_steer_control_type = (steer_control_type == 0) || // NONE
(steer_control_type == 1) || // ANGLE_CONTROL
(steer_control_type == 2); // LANE_KEEP_ASSIST
bool valid_steer_control_type = (steer_control_type == 0) || // NONE
(steer_control_type == angle_ctrl_type) || // ANGLE_CONTROL
(steer_control_type == lkas_ctrl_type); // LANE_KEEP_ASSIST
if (!valid_steer_control_type) {
violation = true;
}
@@ -317,11 +325,6 @@ static bool tesla_tx_hook(const CANPacket_t *msg) {
}
}
// DAS_bodyControls (blinker MITM on vehicle bus) is body control only, not motion
// actuation. openpilot copies the stock frame verbatim and only flips the turn-indicator
// bits, so we don't value-check it here — rejecting a frame would break the counter
// sequence the body controller validates. The TX whitelist still gates the address/bus.
if (violation) {
tx = false;
}
@@ -348,11 +351,9 @@ static bool tesla_fwd_hook(int bus_num, int addr) {
if (tesla_longitudinal && (addr == 0x2b9) && !tesla_stock_aeb) {
block_msg = true;
}
}
}
return block_msg;
}
@@ -370,23 +371,8 @@ static safety_config tesla_init(uint16_t param) {
{0x27D, 0, 3, .check_relay = true, .disable_static_blocking = true}, // APS_eacMonitor
};
// With vehicle bus harness: adds DAS_bodyControls on bus 1 for blinker control
static const CanMsg TESLA_VEHICLE_BUS_TX_MSGS[] = {
{0x488, 0, 4, .check_relay = true, .disable_static_blocking = true}, // DAS_steeringControl
{0x2b9, 0, 8, .check_relay = false}, // DAS_control (for cancel)
{0x27D, 0, 3, .check_relay = true, .disable_static_blocking = true}, // APS_eacMonitor
{0x3E9, 1, 8, .check_relay = false}, // DAS_bodyControls (blinker)
};
static const CanMsg TESLA_VEHICLE_BUS_LONG_TX_MSGS[] = {
{0x488, 0, 4, .check_relay = true, .disable_static_blocking = true}, // DAS_steeringControl
{0x2b9, 0, 8, .check_relay = true, .disable_static_blocking = true}, // DAS_control
{0x27D, 0, 3, .check_relay = true, .disable_static_blocking = true}, // APS_eacMonitor
{0x3E9, 1, 8, .check_relay = false}, // DAS_bodyControls (blinker)
};
const uint16_t TESLA_FLAG_LEGACY_DAS_STEERING = 2;
tesla_legacy_das_steering = GET_FLAG(param, TESLA_FLAG_LEGACY_DAS_STEERING);
const uint16_t TESLA_FLAG_FSD_14 = 2;
tesla_fsd_14 = GET_FLAG(param, TESLA_FLAG_FSD_14);
#ifdef ALLOW_DEBUG
const uint16_t TESLA_FLAG_LONGITUDINAL_CONTROL = 1;
@@ -415,11 +401,7 @@ static safety_config tesla_init(uint16_t param) {
};
safety_config ret;
if (tesla_has_vehicle_bus && tesla_longitudinal) {
SET_TX_MSGS(TESLA_VEHICLE_BUS_LONG_TX_MSGS, ret);
} else if (tesla_has_vehicle_bus) {
SET_TX_MSGS(TESLA_VEHICLE_BUS_TX_MSGS, ret);
} else if (tesla_longitudinal) {
if (tesla_longitudinal) {
SET_TX_MSGS(TESLA_M3_Y_LONG_TX_MSGS, ret);
} else {
SET_TX_MSGS(TESLA_M3_Y_TX_MSGS, ret);
+3 -7
View File
@@ -375,15 +375,11 @@ static bool toyota_tx_hook(const CANPacket_t *msg) {
}
}
// UDS: Only tester present and door lock/unlock allowed on diagnostics address
// UDS: Only tester present ("\x0F\x02\x3E\x00\x00\x00\x00\x00") allowed on diagnostics address
if (msg->addr == 0x750U) {
// this address is sub-addressed. only allow tester present to radar (0xF)
bool valid_tester_present = (GET_BYTES(msg, 0, 4) == 0x003E020FU) && (GET_BYTES(msg, 4, 4) == 0x0U);
// BCM door lock/unlock routine (0x40=BCM sub-addr, 0x05=len, 0x30 0x11=routine ID)
// Byte 5: 0x80=lock, 0x40=unlock. Only these two values allowed.
bool valid_door_lock = (GET_BYTES(msg, 0, 4) == 0x11300540U) &&
((GET_BYTES(msg, 5, 1) == 0x80U) || (GET_BYTES(msg, 5, 1) == 0x40U));
if (!valid_tester_present && !valid_door_lock) {
bool invalid_uds_msg = (GET_BYTES(msg, 0, 4) != 0x003E020FU) || (GET_BYTES(msg, 4, 4) != 0x0U);
if (invalid_uds_msg) {
tx = 0;
}
}
@@ -10,14 +10,6 @@ extern const uint16_t FLAG_VOLKSWAGEN_ALLOW_LONG_ACCEL_WITH_GAS_PRESSED;
const uint16_t FLAG_VOLKSWAGEN_ALLOW_LONG_ACCEL_WITH_GAS_PRESSED = 8;
extern const uint16_t FLAG_VOLKSWAGEN_PQ_ALC_MODULE;
const uint16_t FLAG_VOLKSWAGEN_PQ_ALC_MODULE = 32;
extern const uint16_t FLAG_VOLKSWAGEN_PQ_LOWLINE;
const uint16_t FLAG_VOLKSWAGEN_PQ_LOWLINE = 64;
extern const uint16_t FLAG_VOLKSWAGEN_PQ_NO_CAM_BUS;
const uint16_t FLAG_VOLKSWAGEN_PQ_NO_CAM_BUS = 128;
extern const uint16_t FLAG_VOLKSWAGEN_PQ_ACC_FTS_EPB;
const uint16_t FLAG_VOLKSWAGEN_PQ_ACC_FTS_EPB = 256;
extern const uint16_t FLAG_VOLKSWAGEN_PQ_SNG_ECD;
const uint16_t FLAG_VOLKSWAGEN_PQ_SNG_ECD = 512;
static uint8_t volkswagen_crc8_lut_8h2f[256]; // Static lookup table for CRC8 poly 0x2F, aka 8H2F/AUTOSAR
@@ -63,9 +55,6 @@ float vw_iq_measured_angle_deg = 0.0f;
extern bool vw_iq_aol_active;
bool vw_iq_aol_active = false;
extern bool vw_iq_no_cam;
bool vw_iq_no_cam = false;
extern float vw_iq_angle_offset_deg;
float vw_iq_angle_offset_deg = 0.0f;
@@ -114,7 +103,6 @@ static void volkswagen_common_init(void) {
vw_iq_apd_wheelbase = 0.0f;
vw_iq_apd_params_valid = false;
vw_iq_aol_active = false;
vw_iq_no_cam = false;
vw_iq_angle_offset_deg = 0.0f;
vw_iq_alc_desired_angle_deg = 0.0f;
vw_iq_alc_active = false;
@@ -226,7 +214,7 @@ static void volkswagen_iq_send_debug_la(uint32_t debug_addr, uint8_t bus) {
float speed_kmh = ((float)(vehicle_speed.min) / VEHICLE_SPEED_FACTOR) * 3.6f;
uint16_t spd_raw = (uint16_t)(speed_kmh * 100.0f);
int16_t ang_raw = (int16_t)(vw_iq_measured_angle_deg * 100.0f);
uint8_t flags = (vw_iq_apd_params_valid ? 0x01U : 0x00U) | (vw_iq_aol_active ? 0x02U : 0x00U) | (vw_iq_no_cam ? 0x04U : 0x00U);
uint8_t flags = (vw_iq_apd_params_valid ? 0x01U : 0x00U);
msg.data[0] = (uint8_t)(la_raw & 0xFFU);
msg.data[1] = (uint8_t)((la_raw >> 8) & 0xFFU);
+8 -10
View File
@@ -236,16 +236,6 @@ static void volkswagen_meb_rx_hook(const CANPacket_t *msg) {
}
}
// Lateral limits for curvature-based steering (HCA_03)
// max_power matches 50% (~125/255) of the byte range, safely above Python's STEERING_POWER_MAX of 90
static const CurvatureSteeringLimits VOLKSWAGEN_MEB_STEERING_LIMITS = {
.max_curvature = 29105, // 0.195 rad/m
.curvature_to_can = 149253.7313f, // 1 / 6.7e-6 rad/m to CAN units
.send_rate = 0.02f,
.inactive_curvature_is_zero = true,
.max_power = 125, // ~50% of byte range; Python STEERING_POWER_MAX is 90
};
static bool volkswagen_meb_tx_hook(const CANPacket_t *msg) {
const LongitudinalLimits VOLKSWAGEN_MEB_LONG_LIMITS = {
.max_accel = 2000,
@@ -253,6 +243,14 @@ static bool volkswagen_meb_tx_hook(const CANPacket_t *msg) {
.inactive_accel = 3010,
};
const CurvatureSteeringLimits VOLKSWAGEN_MEB_STEERING_LIMITS = {
.max_curvature = 32767, // TEST: 15-bit max, no curvature ceiling
.curvature_to_can = 149253.7313f,
.send_rate = 0.02f,
.inactive_curvature_is_zero = true,
.max_power = 65535, // TEST: no power ceiling
};
bool tx = true;
if (msg->addr == MSG_HCA_03) {
+13 -59
View File
@@ -6,7 +6,6 @@
#define MSG_LENKHILFE_3 0x0D0U // RX from EPS, for steering angle and driver steering torque
#define MSG_HCA_1 0x0D2U // TX by OP, Heading Control Assist steering torque
#define MSG_BREMSE_1 0x1A0U // RX from ABS, for ego speed
#define MSG_MOTOR_3 0x380U // RX from ECU
#define MSG_MOTOR_2 0x288U // RX from ECU, for CC state and brake switch state
#define MSG_ACC_SYSTEM 0x368U // TX by OP, longitudinal acceleration controls
#define MSG_MOTOR_3 0x380U // RX from ECU, for driver throttle input
@@ -16,16 +15,10 @@
#define MSG_LDW_1 0x5BEU // TX by OP, Lane line recognition and text alerts
#define MSG_BLINKMODI_02 0x0AAU // TX by OP, Blinker control
#define MSG_APD_1 0x3D6U // TX by OP, CarParams
#define MSG_SNG_1 0x3D7U // TX by OP
#define MSG_PQ_SAFETY_1 0x6A0U // RX by OP
#define MSG_PQ_DEBUG_LA 0x6A1U // TX by panda, internal safety state debug
#define MSG_IQ 0x6A1U // TX by OP
static bool volkswagen_pq_alc_module_present = false;
static bool volkswagen_pq_acc_tsk_ready = false;
static bool volkswagen_pq_lowline = false;
static bool volkswagen_pq_acc_fts_epb = false;
static bool volkswagen_pq_sng_ecd = false;
static uint32_t volkswagen_pq_get_checksum(const CANPacket_t *msg) {
return (uint32_t)msg->data[(msg->addr == MSG_MOTOR_5) ? 7 : 0];
@@ -64,39 +57,26 @@ static safety_config volkswagen_pq_init(uint16_t param) {
static const CanMsg VOLKSWAGEN_PQ_STOCK_TX_MSGS[] = {{MSG_HCA_1, 0, 5, .check_relay = true}, {MSG_LDW_1, 0, 8, .check_relay = true},
{MSG_GRA_NEU, 0, 4, .check_relay = false}, {MSG_GRA_NEU, 1, 4, .check_relay = false},
{MSG_GRA_NEU, 2, 4, .check_relay = false}, {MSG_BLINKMODI_02, 0, 8, .check_relay = false},
{MSG_APD_1, 1, 8, .check_relay = false}, {MSG_IQ, 1, 8, .check_relay = false}};
// Lowline (non-ECAN) lateral-only cars: ptCAN (bus 1) is the only active bus, no J533 gateway.
// HCA_1 and lateral messages go directly on bus 1 to the EPS. GRA_Neu bus 0 dropped (dead).
static const CanMsg VOLKSWAGEN_PQ_STOCK_TX_MSGS_BUS1[] = {{MSG_HCA_1, 1, 5, .check_relay = true}, {MSG_LDW_1, 1, 8, .check_relay = true},
{MSG_GRA_NEU, 1, 4, .check_relay = false}, {MSG_GRA_NEU, 2, 4, .check_relay = false},
{MSG_BLINKMODI_02, 1, 8, .check_relay = false},
{MSG_APD_1, 1, 8, .check_relay = false}, {MSG_IQ, 1, 8, .check_relay = false}};
{MSG_APD_1, 1, 8, .check_relay = false}};
static const CanMsg VOLKSWAGEN_PQ_LONG_TX_MSGS[] = {{MSG_HCA_1, 0, 5, .check_relay = true}, {MSG_LDW_1, 0, 8, .check_relay = true},
{MSG_ACC_SYSTEM, 0, 8, .check_relay = true}, {MSG_ACC_GRA_ANZEIGE, 0, 8, .check_relay = true},
{MSG_GRA_NEU, 1, 4, .check_relay = false}, {MSG_GRA_NEU, 2, 4, .check_relay = true},
{MSG_BLINKMODI_02, 0, 8, .check_relay = false}, {MSG_MOTOR_2, 2, 8, .check_relay = true},
{MSG_MOTOR_5, 2, 8, .check_relay = true}, {MSG_MOTOR_3, 1, 8, .check_relay = false},
{MSG_APD_1, 1, 8, .check_relay = false}, {MSG_IQ, 1, 8, .check_relay = false},
{MSG_SNG_1, 1, 8, .check_relay = false}};
{MSG_APD_1, 1, 8, .check_relay = false}};
static RxCheck volkswagen_pq_rx_checks[] = {
{.msg = {{MSG_LENKHILFE_3, 1, 6, 100U, .max_counter = 15U, .ignore_quality_flag = true}, { 0 }, { 0 }}},
{.msg = {{MSG_BREMSE_1, 1, 8, 100U, .ignore_checksum = true, .ignore_counter = true, .ignore_quality_flag = true}, { 0 }, { 0 }}},
{.msg = {{MSG_MOTOR_2, 1, 8, 50U, .ignore_checksum = true, .ignore_counter = true, .ignore_quality_flag = true}, { 0 }, { 0 }}},
{.msg = {{MSG_MOTOR_3, 1, 8, 100U, .ignore_checksum = true, .ignore_counter = true, .ignore_quality_flag = true}, { 0 }, { 0 }}},
{.msg = {{MSG_MOTOR_5, 1, 8, 50U, .ignore_counter = true, .ignore_quality_flag = true}, { 0 }, { 0 }}},
{.msg = {{MSG_GRA_NEU, 1, 4, 30U, .max_counter = 15U, .ignore_quality_flag = true}, { 0 }, { 0 }}},
{.msg = {{MSG_LENKHILFE_3, 0, 6, 100U, .max_counter = 15U, .ignore_quality_flag = true}, { 0 }, { 0 }}},
{.msg = {{MSG_BREMSE_1, 0, 8, 100U, .ignore_checksum = true, .ignore_counter = true, .ignore_quality_flag = true}, { 0 }, { 0 }}},
{.msg = {{MSG_MOTOR_2, 0, 8, 50U, .ignore_checksum = true, .ignore_counter = true, .ignore_quality_flag = true}, { 0 }, { 0 }}},
{.msg = {{MSG_MOTOR_3, 0, 8, 100U, .ignore_checksum = true, .ignore_counter = true, .ignore_quality_flag = true}, { 0 }, { 0 }}},
{.msg = {{MSG_MOTOR_5, 0, 8, 50U, .ignore_counter = true, .ignore_quality_flag = true}, { 0 }, { 0 }}},
{.msg = {{MSG_GRA_NEU, 0, 4, 30U, .max_counter = 15U, .ignore_quality_flag = true}, { 0 }, { 0 }}},
{.msg = {{MSG_PQ_SAFETY_1, 1, 8, 50U, .ignore_checksum = true, .ignore_counter = true, .ignore_quality_flag = true}, { 0 }, { 0 }}},
};
volkswagen_common_init();
volkswagen_pq_alc_module_present = GET_FLAG(param, FLAG_VOLKSWAGEN_PQ_ALC_MODULE);
volkswagen_pq_lowline = GET_FLAG(param, FLAG_VOLKSWAGEN_PQ_LOWLINE);
vw_iq_no_cam = GET_FLAG(param, FLAG_VOLKSWAGEN_PQ_NO_CAM_BUS);
volkswagen_pq_acc_fts_epb = GET_FLAG(param, FLAG_VOLKSWAGEN_PQ_ACC_FTS_EPB);
volkswagen_pq_sng_ecd = GET_FLAG(param, FLAG_VOLKSWAGEN_PQ_SNG_ECD);
volkswagen_pq_acc_tsk_ready = false;
#ifdef ALLOW_DEBUG
@@ -106,8 +86,7 @@ static safety_config volkswagen_pq_init(uint16_t param) {
SAFETY_UNUSED(param);
#endif
safety_config ret = volkswagen_longitudinal ? BUILD_SAFETY_CFG(volkswagen_pq_rx_checks, VOLKSWAGEN_PQ_LONG_TX_MSGS) : \
volkswagen_pq_lowline ? BUILD_SAFETY_CFG(volkswagen_pq_rx_checks, VOLKSWAGEN_PQ_STOCK_TX_MSGS_BUS1) : \
BUILD_SAFETY_CFG(volkswagen_pq_rx_checks, VOLKSWAGEN_PQ_STOCK_TX_MSGS);
BUILD_SAFETY_CFG(volkswagen_pq_rx_checks, VOLKSWAGEN_PQ_STOCK_TX_MSGS);
if (!volkswagen_pq_alc_module_present) {
ret.rx_checks_len -= 1;
}
@@ -115,9 +94,7 @@ static safety_config volkswagen_pq_init(uint16_t param) {
}
static void volkswagen_pq_rx_hook(const CANPacket_t *msg) {
// All PQ RX processing is on bus 1 (ptCAN). Messages exist on both bus 0 and bus 1 for ECAN
// gateway cars; on lowline non-ECAN cars bus 1 is the only active bus.
if (msg->bus == 1U) {
if (msg->bus == 0U) {
// Update in-motion state from speed value.
// Signal: Bremse_1.BR1_Rad_kmh
if (msg->addr == MSG_BREMSE_1) {
@@ -142,14 +119,9 @@ static void volkswagen_pq_rx_hook(const CANPacket_t *msg) {
vw_iq_measured_angle_deg = angle_sign ? -angle_deg : angle_deg;
}
// acc_main_on tracked unconditionally so main-switch disengagement works for both long
// and lateral-only (pcmCruise) configurations.
if (msg->addr == MSG_MOTOR_5) {
acc_main_on = GET_BIT(msg, 50U);
}
if (volkswagen_longitudinal) {
if (msg->addr == MSG_MOTOR_5) {
acc_main_on = GET_BIT(msg, 50U);
if (!acc_main_on && !volkswagen_pq_acc_tsk_ready) {
controls_allowed = false;
}
@@ -189,7 +161,8 @@ static void volkswagen_pq_rx_hook(const CANPacket_t *msg) {
if (msg->addr == MSG_MOTOR_2) {
brake_pressed = (msg->data[2] & 0x1U);
}
}
if (msg->bus == 1U) {
if (volkswagen_pq_alc_module_present && (msg->addr == MSG_PQ_SAFETY_1)) {
const uint16_t desired_angle_raw = (uint16_t)msg->data[6] | (((uint16_t)msg->data[7] & 0x7FU) << 8);
const bool desired_angle_sign = (msg->data[7] & 0x80U) != 0U;
@@ -207,7 +180,6 @@ static bool volkswagen_pq_tx_hook(const CANPacket_t *msg) {
}
if (msg->addr == MSG_HCA_1) {
volkswagen_iq_send_debug_la(MSG_PQ_DEBUG_LA, 1U);
const uint8_t hca_status = (msg->data[1] >> 4) & 0x0FU;
if (volkswagen_pq_alc_module_present && (hca_status == 8U)) {
@@ -242,31 +214,13 @@ static bool volkswagen_pq_tx_hook(const CANPacket_t *msg) {
}
}
if (msg->addr == MSG_MOTOR_3) {
if (!volkswagen_pq_acc_fts_epb) {
tx = false;
}
}
if (msg->addr == MSG_SNG_1) {
if (!volkswagen_pq_sng_ecd) {
tx = false;
}
}
return tx;
}
static bool volkswagen_pq_fwd_hook(int bus_num, int addr) {
SAFETY_UNUSED(addr);
return vw_iq_no_cam && (bus_num == 0);
}
const safety_hooks volkswagen_pq_hooks = {
.init = volkswagen_pq_init,
.rx = volkswagen_pq_rx_hook,
.tx = volkswagen_pq_tx_hook,
.fwd = volkswagen_pq_fwd_hook,
.get_counter = volkswagen_pq_get_counter,
.get_checksum = volkswagen_pq_get_checksum,
.compute_checksum = volkswagen_pq_compute_checksum,
@@ -150,7 +150,7 @@ class HyundaiLongitudinalBase(common.LongitudinalAccelSafetyTest):
with self.subTest("enable_aol", aol_enabled=enable_aol):
for main_cruise_toggleable in (True, False):
with self.subTest("main_cruise_toggleable", main_cruise_toggleable=main_cruise_toggleable):
main_cruise_toggleable_flag = HyundaiSafetyFlagsIQ.MAIN_BTN_LONG_TOGGLE if main_cruise_toggleable else 0
main_cruise_toggleable_flag = HyundaiSafetyFlagsIQ.LONG_MAIN_CRUISE_TOGGLEABLE if main_cruise_toggleable else 0
self.safety.set_current_safety_param_iq(default_safety_param_iq | main_cruise_toggleable_flag)
self.safety.set_safety_hooks(default_safety_mode, default_safety_param)
@@ -182,7 +182,7 @@ class HyundaiLongitudinalBase(common.LongitudinalAccelSafetyTest):
for enable_aol in (True, False):
with self.subTest("enable_aol", aol_enabled=enable_aol):
main_cruise_toggleable_flag = HyundaiSafetyFlagsIQ.MAIN_BTN_LONG_TOGGLE
main_cruise_toggleable_flag = HyundaiSafetyFlagsIQ.LONG_MAIN_CRUISE_TOGGLEABLE
self.safety.set_current_safety_param_iq(default_safety_param_iq | main_cruise_toggleable_flag)
self.safety.set_safety_hooks(default_safety_mode, default_safety_param)
@@ -215,7 +215,7 @@ class HyundaiLongitudinalBase(common.LongitudinalAccelSafetyTest):
for enable_aol in (True, False):
with self.subTest("enable_aol", aol_enabled=enable_aol):
main_cruise_toggleable_flag = HyundaiSafetyFlagsIQ.MAIN_BTN_LONG_TOGGLE
main_cruise_toggleable_flag = HyundaiSafetyFlagsIQ.LONG_MAIN_CRUISE_TOGGLEABLE
self.safety.set_current_safety_param_iq(default_safety_param_iq | main_cruise_toggleable_flag)
self.safety.set_safety_hooks(default_safety_mode, default_safety_param)
@@ -256,7 +256,7 @@ class HyundaiLongitudinalBase(common.LongitudinalAccelSafetyTest):
"""Test that mismatch counter resets when states resync"""
for enable_aol in (True, False):
with self.subTest("enable_aol", aol_enabled=enable_aol):
main_cruise_toggleable_flag = HyundaiSafetyFlagsIQ.MAIN_BTN_LONG_TOGGLE
main_cruise_toggleable_flag = HyundaiSafetyFlagsIQ.LONG_MAIN_CRUISE_TOGGLEABLE
self.safety.set_current_safety_param_iq(default_safety_param_iq | main_cruise_toggleable_flag)
self.safety.set_safety_hooks(default_safety_mode, default_safety_param)
@@ -25,11 +25,6 @@ env = Environment(
tools=["default", "compilation_db"],
)
# short colored build output, if the top-level pretty tool is present (main-repo build)
_pretty = Dir('#site_scons/site_tools').File('pretty.py')
if _pretty.exists():
env.Tool('pretty', toolpath=[_pretty.dir.abspath])
# The Mull plugin injects mutations that are dormant unless run with mull-runner
if system == "Darwin":
mull_plugin = Dir('#').abspath + '/.mull/lib/mull-ir-frontend-18'
+1 -1
View File
@@ -22,7 +22,7 @@ MSG_LateralMotionControl = 0x3D3 # TX by OP, Lateral Control message
MSG_LateralMotionControl2 = 0x3D6 # TX by OP, alternate Lateral Control message
MSG_IPMA_Data = 0x3D8 # TX by OP, IPMA and LKAS user interface
SAFETY_ISO_LATERAL_ACCEL = 5.0
SAFETY_ISO_LATERAL_ACCEL = 3.0
EARTH_G = 9.81
AVERAGE_ROAD_ROLL = 0.06
MAX_LATERAL_ACCEL = SAFETY_ISO_LATERAL_ACCEL - (EARTH_G * AVERAGE_ROAD_ROLL)
+11 -13
View File
@@ -11,7 +11,8 @@ except ImportError:
pytest.skip("requires openpilot dependencies", allow_module_level=True)
from iqdbc.car.lateral import get_max_angle_delta_vm, get_max_angle_vm
from iqdbc.car.tesla.values import CarControllerParams, TeslaSafetyFlags, CANBUS
from iqdbc.car.tesla.teslacan import get_steer_ctrl_type
from iqdbc.car.tesla.values import CarControllerParams, TeslaSafetyFlags, TeslaFlags, CANBUS
from iqdbc.car.structs import CarParams
from iqdbc.car.vehicle_model import VehicleModel
from iqdbc.can import CANDefine
@@ -22,7 +23,6 @@ from iqdbc.safety.tests.common import CANPackerSafety, MAX_SPEED_DELTA, MAX_WRON
MSG_DAS_steeringControl = 0x488
MSG_APS_eacMonitor = 0x27d
MSG_DAS_Control = 0x2b9
MSG_DAS_bodyControls = 0x3E9
def round_angle(apply_angle, can_offset=0):
@@ -34,7 +34,6 @@ def round_angle(apply_angle, can_offset=0):
class TestTeslaSafetyBase(common.CarSafetyTest, common.AngleSteeringSafetyTest, common.LongitudinalAccelSafetyTest):
SAFETY_PARAM = 0
STEER_TYPE_SHIFT = 0 # legacy firmware uses a 2-bit field, one bit up from the 3-bit signal
RELAY_MALFUNCTION_ADDRS = {0: (MSG_DAS_steeringControl, MSG_APS_eacMonitor)}
FWD_BLACKLISTED_ADDRS = {2: [MSG_DAS_steeringControl, MSG_APS_eacMonitor]}
@@ -84,7 +83,11 @@ class TestTeslaSafetyBase(common.CarSafetyTest, common.AngleSteeringSafetyTest,
self.safety.init_tests()
def _angle_cmd_msg(self, angle: float, state: bool | int, increment_timer: bool = True, bus: int = 0):
values = {"DAS_steeringAngleRequest": angle, "DAS_steeringControlType": int(state) << self.STEER_TYPE_SHIFT}
# If FSD 14, translate steer control type to new flipped definition
if self.safety.get_current_safety_param() & TeslaSafetyFlags.FSD_14:
state = get_steer_ctrl_type(TeslaFlags.FSD_14, int(state))
values = {"DAS_steeringAngleRequest": angle, "DAS_steeringControlType": state}
if increment_timer:
self.safety.set_timer(self.cnt_angle_cmd * int(1e6 / self.LATERAL_FREQUENCY))
self.__class__.cnt_angle_cmd += 1
@@ -404,9 +407,8 @@ class TestTeslaStockSafety(TestTeslaSafetyBase):
self.assertFalse(self._tx(no_aeb_msg))
class TestTeslaLegacyDasSteeringStockSafety(TestTeslaStockSafety):
SAFETY_PARAM = TeslaSafetyFlags.LEGACY_DAS_STEERING
STEER_TYPE_SHIFT = 1
class TestTeslaFSD14StockSafety(TestTeslaStockSafety):
SAFETY_PARAM = TeslaSafetyFlags.FSD_14
class TestTeslaLongitudinalSafety(TestTeslaSafetyBase):
@@ -457,18 +459,14 @@ class TestTeslaLongitudinalSafety(TestTeslaSafetyBase):
self.assertFalse(self._tx(self._long_control_msg(set_speed=0, accel_limits=(-0.1, -0.1))))
class TestTeslaLegacyDasSteeringLongitudinalSafety(TestTeslaLongitudinalSafety):
SAFETY_PARAM = TeslaSafetyFlags.LONG_CONTROL | TeslaSafetyFlags.LEGACY_DAS_STEERING
STEER_TYPE_SHIFT = 1
class TestTeslaFSD14LongitudinalSafety(TestTeslaLongitudinalSafety):
SAFETY_PARAM = TeslaSafetyFlags.LONG_CONTROL | TeslaSafetyFlags.FSD_14
class TestTeslaVehicleBusSafety(TestTeslaSafetyBase):
LONGITUDINAL = False
# With the vehicle bus harness, DAS_bodyControls is also TX'd on bus 1 (blinker MITM)
TX_MSGS = [*TestTeslaSafetyBase.TX_MSGS, [MSG_DAS_bodyControls, 1]]
def setUp(self):
super().setUp()
self.safety = libsafety_py.libsafety
@@ -20,7 +20,6 @@ MSG_ACC_GRA_ANZEIGE = 0x56A # TX by OP, ACC HUD
MSG_LDW_1 = 0x5BE # TX by OP, Lane line recognition and text alerts
MSG_BLINKMODI_02 = 0x0AA # TX by OP, turn signal control
MSG_APD_1 = 0x3D6 # TX by OP, CarParams
MSG_IQ = 0x6A1 # TX by OP
class TestVolkswagenPqSafetyBase(common.CarSafetyTest):
@@ -44,7 +43,7 @@ class TestVolkswagenPqSafetyBase(common.CarSafetyTest):
# Ego speed (Bremse_1)
def _speed_msg(self, speed):
values = {"BR1_Rad_kmh": speed}
return self.packer.make_can_msg_safety("Bremse_1", 1, values)
return self.packer.make_can_msg_safety("Bremse_1", 0, values)
# Brake light switch (shared message Motor_2)
def _user_brake_msg(self, brake):
@@ -64,7 +63,7 @@ class TestVolkswagenPqSafetyBase(common.CarSafetyTest):
# Driver steering input torque
def _torque_driver_msg(self, torque):
values = {"LH3_LM": abs(torque), "LH3_LMSign": torque < 0}
return self.packer.make_can_msg_safety("Lenkhilfe_3", 1, values)
return self.packer.make_can_msg_safety("Lenkhilfe_3", 0, values)
# openpilot steering output torque
def _torque_cmd_msg(self, torque, steer_req=1, hca_status=7):
@@ -77,17 +76,17 @@ class TestVolkswagenPqSafetyBase(common.CarSafetyTest):
values = {"MO2_BLS": brake_pressed,
"MO2_Sta_GRA": cruise_engaged,
"MO2_Status_TSK": tsk_status}
return self.packer.make_can_msg_safety("Motor_2", 1, values)
return self.packer.make_can_msg_safety("Motor_2", 0, values)
# ACC main switch status
def _motor_5_msg(self, main_switch=False):
values = {"MO5_GRA_Hauptsch": main_switch}
return self.packer.make_can_msg_safety("Motor_5", 1, values)
return self.packer.make_can_msg_safety("Motor_5", 0, values)
# Driver throttle input (Motor_3)
def _user_gas_msg(self, gas):
values = {"MO3_Pedalwert": gas}
return self.packer.make_can_msg_safety("Motor_3", 1, values)
return self.packer.make_can_msg_safety("Motor_3", 0, values)
# Cruise control buttons (GRA_Neu)
def _button_msg(self, _set=False, resume=False, cancel=False, bus=2):
@@ -117,7 +116,7 @@ class TestVolkswagenPqSafetyBase(common.CarSafetyTest):
class TestVolkswagenPqStockSafety(TestVolkswagenPqSafetyBase):
# Transmit of GRA_Neu is allowed on bus 0/1/2 to keep compatibility with gateway and camera integration
TX_MSGS = [[MSG_HCA_1, 0], [MSG_GRA_NEU, 0], [MSG_GRA_NEU, 1], [MSG_GRA_NEU, 2], [MSG_LDW_1, 0], [MSG_BLINKMODI_02, 0], [MSG_APD_1, 1], [MSG_IQ, 1]]
TX_MSGS = [[MSG_HCA_1, 0], [MSG_GRA_NEU, 0], [MSG_GRA_NEU, 1], [MSG_GRA_NEU, 2], [MSG_LDW_1, 0], [MSG_BLINKMODI_02, 0], [MSG_APD_1, 1]]
FWD_BLACKLISTED_ADDRS = {2: [MSG_HCA_1, MSG_LDW_1]}
def setUp(self):
@@ -140,11 +139,11 @@ class TestVolkswagenPqLongSafety(TestVolkswagenPqSafetyBase, common.Longitudinal
tsk_status = True
TX_MSGS = [[MSG_HCA_1, 0], [MSG_LDW_1, 0], [MSG_ACC_SYSTEM, 0], [MSG_ACC_GRA_ANZEIGE, 0],
[MSG_GRA_NEU, 1], [MSG_GRA_NEU, 2], [MSG_BLINKMODI_02, 0], [MSG_MOTOR_2, 2], [MSG_MOTOR_5, 2], [MSG_APD_1, 1], [MSG_IQ, 1]]
FWD_BLACKLISTED_ADDRS = {0: [MSG_MOTOR_2, MSG_MOTOR_5, MSG_GRA_NEU],
[MSG_GRA_NEU, 1], [MSG_GRA_NEU, 2], [MSG_BLINKMODI_02, 0], [MSG_MOTOR_2, 2], [MSG_APD_1, 1]]
FWD_BLACKLISTED_ADDRS = {0: [MSG_MOTOR_2, MSG_GRA_NEU],
2: [MSG_HCA_1, MSG_LDW_1, MSG_ACC_SYSTEM, MSG_ACC_GRA_ANZEIGE]}
RELAY_MALFUNCTION_ADDRS = {0: (MSG_HCA_1, MSG_LDW_1, MSG_ACC_SYSTEM, MSG_ACC_GRA_ANZEIGE),
2: (MSG_MOTOR_2, MSG_GRA_NEU, MSG_MOTOR_5)}
2: (MSG_MOTOR_2, MSG_GRA_NEU)}
INACTIVE_ACCEL = 3.01
def setUp(self):
@@ -169,20 +168,20 @@ class TestVolkswagenPqLongSafety(TestVolkswagenPqSafetyBase, common.Longitudinal
# ACC main switch must be on, engage on falling edge
self.safety.set_controls_allowed(0)
self._rx(self._motor_5_msg(main_switch=False))
self._rx(self._button_msg(_set=(button == "set"), resume=(button == "resume"), bus=1))
self._rx(self._button_msg(bus=1))
self._rx(self._button_msg(_set=(button == "set"), resume=(button == "resume"), bus=0))
self._rx(self._button_msg(bus=0))
self.assertFalse(self.safety.get_controls_allowed(), f"controls allowed on {button} with main switch off")
self._rx(self._motor_5_msg(main_switch=True))
self._rx(self._button_msg(_set=(button == "set"), resume=(button == "resume"), bus=1))
self._rx(self._button_msg(_set=(button == "set"), resume=(button == "resume"), bus=0))
self.assertFalse(self.safety.get_controls_allowed(), f"controls allowed on {button} rising edge")
self._rx(self._button_msg(bus=1))
self._rx(self._button_msg(bus=0))
self.assertTrue(self.safety.get_controls_allowed(), f"controls not allowed on {button} falling edge")
def test_cancel_button(self):
# Disable on rising edge of cancel button
self._rx(self._motor_5_msg(main_switch=True))
self.safety.set_controls_allowed(1)
self._rx(self._button_msg(cancel=True, bus=1))
self._rx(self._button_msg(cancel=True, bus=0))
self.assertFalse(self.safety.get_controls_allowed(), "controls allowed after cancel")
def test_main_switch(self):
@@ -224,8 +223,8 @@ class TestVolkswagenPqLongSafety(TestVolkswagenPqSafetyBase, common.Longitudinal
self.safety.set_controls_allowed(0)
self._rx(self._motor_5_msg(main_switch=False))
self._rx(self._motor_2_msg(tsk_status=True))
self._rx(self._button_msg(_set=(button == "set"), resume=(button == "resume"), bus=1))
self._rx(self._button_msg(bus=1))
self._rx(self._button_msg(_set=(button == "set"), resume=(button == "resume"), bus=0))
self._rx(self._button_msg(bus=0))
self.assertTrue(self.safety.get_controls_allowed(), f"controls not allowed on {button} falling edge with TSK ready")
def test_torque_cmd_enable_variants(self):
@@ -251,42 +250,5 @@ class TestVolkswagenPqLongSafety(TestVolkswagenPqSafetyBase, common.Longitudinal
self.safety.set_controls_allowed(True)
self.assertTrue(self._tx(self._accel_msg(0.5)))
class TestVolkswagenPqLowlineSafety(TestVolkswagenPqSafetyBase):
"""Non-ECAN lateral-only PQ cars: bus 0 dead, TX on bus 1 (ptCAN) directly to EPS."""
TX_MSGS = [[MSG_HCA_1, 1], [MSG_GRA_NEU, 1], [MSG_GRA_NEU, 2], [MSG_LDW_1, 1], [MSG_BLINKMODI_02, 1], [MSG_APD_1, 1], [MSG_IQ, 1]]
FWD_BUS_LOOKUP = {2: 0}
FWD_BLACKLISTED_ADDRS = {}
RELAY_MALFUNCTION_ADDRS = {1: (MSG_HCA_1, MSG_LDW_1)}
def setUp(self):
self.packer = CANPackerSafety("vw_pq")
self.safety = libsafety_py.libsafety
self.safety.set_safety_hooks(CarParams.SafetyModel.volkswagenPq, VolkswagenSafetyFlags.PQ_LOWLINE | VolkswagenSafetyFlags.PQ_NO_CAM_BUS)
self.safety.init_tests()
def _torque_cmd_msg(self, torque, steer_req=1, hca_status=7):
values = {"LM_Offset": abs(torque), "LM_OffSign": torque < 0, "HCA_Status": hca_status if steer_req else 3}
return self.packer.make_can_msg_safety("HCA_1", 1, values)
def test_spam_cancel_safety_check(self):
self.safety.set_controls_allowed(0)
self.assertTrue(self._tx(self._button_msg(cancel=True)))
self.assertFalse(self._tx(self._button_msg(resume=True)))
self.assertFalse(self._tx(self._button_msg(_set=True)))
self.safety.set_controls_allowed(1)
self.assertTrue(self._tx(self._button_msg(resume=True)))
class TestVolkswagenPqNoCamSafety(TestVolkswagenPqStockSafety):
FWD_BUS_LOOKUP = {2: 0}
def setUp(self):
self.packer = CANPackerSafety("vw_pq")
self.safety = libsafety_py.libsafety
self.safety.set_safety_hooks(CarParams.SafetyModel.volkswagenPq, VolkswagenSafetyFlags.PQ_NO_CAM_BUS)
self.safety.init_tests()
if __name__ == "__main__":
unittest.main()
+30
View File
@@ -0,0 +1,30 @@
output:
- meta # Print lefthook version
- summary # Print summary block (successful and failed steps)
- empty_summary # Print summary heading when there are no steps to run
- success # Print successful steps
- failure # Print failed steps printing
- execution # Print any execution logs
#- execution_out # Print execution output
#- execution_info # Print `EXECUTE > ...` logging
- skips # Print "skip" (i.e. no files matched)
test:
parallel: true
commands:
# *** static analysis
ruff:
run: ruff check .
ty:
run: ty check
codespell:
run: codespell {files} -L tge,stdio -S *.dbc --ignore-words=.codespellignore
files: git ls-tree -r HEAD --name-only
cpplint:
run: cpplint --exclude=iqdbc/safety/tests/misra/cppcheck/ --exclude=iqdbc/can/*_pyx.cpp --recursive --quiet --counting=detailed --linelength=240 --filter=-build,-legal,-readability,-runtime,-whitespace,+build/include_subdir,+build/forward_decl,+build/include_what_you_use,+build/deprecated,+whitespace/comma,+whitespace/line_length,+whitespace/empty_if_body,+whitespace/empty_loop_body,+whitespace/empty_conditional_body,+whitespace/forcolon,+whitespace/parens,+whitespace/semicolon,+whitespace/tab,+readability/braces iqdbc/
misra:
run: iqdbc/safety/tests/misra/test_misra.sh
# *** tests ***
pytest:
run: pytest -n8
+2 -2
View File
@@ -3,11 +3,11 @@ name = "iqdbc"
version = "0.2.1"
description = "CAN bus databases and tools"
license = "MIT"
authors = [{ name = "IQ.Lvbs", email = "team@iqlvbs.com" }]
authors = [{ name = "Vehicle Researcher", email = "user@comma.ai" }]
readme = "README.md"
requires-python = ">=3.11,<3.13" # pycapnp doesn't work with 3.13
urls = { "homepage" = "https://git.konn3kt.com/teal/iqdbc" }
urls = { "homepage" = "https://gitlvb.teallvbs.xyz/teal/iqdbc" }
dependencies = [
"scons",
+7 -13
View File
@@ -20,6 +20,9 @@ else:
if os.getenv("DEBUG"):
common_flags += ["-DDEBUG"]
def objcopy(source, target, env, for_signature):
return '$OBJCOPY -O binary %s %s' % (source[0], target[0])
def get_version(builder, build_type):
try:
git = subprocess.check_output(["git", "rev-parse", "--short=8", "HEAD"], encoding='utf8').strip()
@@ -89,20 +92,12 @@ def build_project(project_name, project, main, extra_flags):
LINKFLAGS=flags,
CPPPATH=[Dir("./"), "./board/stm32f4/inc", "./board/stm32h7/inc", iqdbc.INCLUDE_PATH],
ASCOM="$AS $ASFLAGS -o $TARGET -c $SOURCES",
BUILDERS={
'Objcopy': Builder(generator=objcopy, suffix='.bin', src_suffix='.elf')
},
tools=["default", "compilation_db"],
)
# short colored build output shared with the top-level build (if present)
_pretty = Dir('#site_scons/site_tools').File('pretty.py')
if _pretty.exists():
env.Tool('pretty', toolpath=[_pretty.dir.abspath])
if not hasattr(env, 'PrettyAction'):
env.AddMethod(lambda e, cmd, label, **kw: Action(cmd), 'PrettyAction')
env.Append(BUILDERS={
'Objcopy': Builder(action=env.PrettyAction('$OBJCOPY -O binary $SOURCE $TARGET', 'OBJCOPY'),
suffix='.bin', src_suffix='.elf')
})
startup = env.Object(project["STARTUP_FILE"])
# Build bootstub
@@ -123,8 +118,7 @@ def build_project(project_name, project, main, extra_flags):
], LINKFLAGS=[f"-Wl,--section-start,.isr_vector={project['APP_START_ADDRESS']}"] + flags)
main_bin = env.Objcopy(f"{project_dir}/main.bin", main_elf)
sign_py = File(f"./crypto/sign.py").srcnode().relpath
env.Command(f"./board/obj/{project_name}.bin.signed", main_bin,
env.PrettyAction(f"SETLEN=1 {sign_py} $SOURCE $TARGET {cert_fn}", 'SIGN', logfile='/dev/null'))
env.Command(f"./board/obj/{project_name}.bin.signed", main_bin, f"SETLEN=1 {sign_py} $SOURCE $TARGET {cert_fn}")
+15 -11
View File
@@ -138,11 +138,9 @@ bus_config_t bus_config[PANDA_CAN_CNT] = {
void can_init_all(void) {
for (uint8_t i=0U; i < PANDA_CAN_CNT; i++) {
bus_config[i].canfd_enabled = false;
// NOTE: do NOT reset can_data_speed here. Matching stock panda, can_init_all() only clears
// canfd_enabled (re-discovered on RX via canfd_auto). The old "#ifndef CANFD: can_data_speed = 0U"
// fired on every board because CANFD is never defined, zeroing the FD data-phase bitrate on every
// set_safety_model() -> CAN-FD frames then failed with form/stuff errors and never re-enabled,
// breaking all CAN-FD cars (e.g. Kia EV6) after fingerprinting. can_data_speed is only used by fdcan.
// Preserve can_data_speed (default 20000U = 2Mbps) through safety model changes.
// Auto-detection in can_rx() sets canfd_enabled=true when the first FD frame arrives;
// keeping data speed nonzero lets the FDCAN peripheral handle FD frames immediately.
can_clear(can_queues[i]);
(void)can_init(i);
}
@@ -171,7 +169,6 @@ void ignition_can_hook(CANPacket_t *msg) {
static int tesla_gear = TESLA_DI_GEAR_P;
static int toyota_gear = TOYOTA_GEAR_P;
static int toyota_hybrid_gear = TOYOTA_HYBRID_GEAR_P;
static bool toyota_hybrid_gear_seen = false;
// GM exception
if ((msg->addr == 0x1F1U) && (len == 8)) {
@@ -221,10 +218,8 @@ void ignition_can_hook(CANPacket_t *msg) {
ignition_can_cnt = 0U;
}
// Toyota/Lexus exception. SecOC hybrids (e.g. Sienna 4th gen) report gear on
// GEAR_PACKET_HYBRID (0x127); their GEAR_PACKET (0x3BC) does not read Park, so once
// the hybrid gear packet is seen, don't let 0x3BC override it (Park -> ignition off).
if ((msg->addr == 0x3BCU) && (len == 8) && !toyota_hybrid_gear_seen) {
// Toyota/Lexus exception
if ((msg->addr == 0x3BCU) && (len == 8)) {
int gear = msg->data[1] & 0x3FU;
if ((gear == 0) || (gear == 1) || (gear == 8) || (gear == 16) || (gear == 32)) {
toyota_gear = gear;
@@ -236,7 +231,6 @@ void ignition_can_hook(CANPacket_t *msg) {
if ((msg->addr == 0x127U) && (len == 8)) {
int gear = (msg->data[5] >> 4U) & 0xFU;
if (gear <= 4) {
toyota_hybrid_gear_seen = true;
toyota_hybrid_gear = gear;
ignition_can = toyota_hybrid_gear != TOYOTA_HYBRID_GEAR_P;
ignition_can_cnt = 0U;
@@ -245,6 +239,16 @@ void ignition_can_hook(CANPacket_t *msg) {
}
// Volkswagen MEB / MQBevo exception (Klemmen_Status_01)
// On gateway harness cars this message is on bus 1 (powertrain CAN), not bus 0.
if ((msg->bus == 0U) || (msg->bus == 1U)) {
int len = GET_LEN(msg);
if ((msg->addr == 0x3C0U) && (len == 4)) {
ignition_can = ((msg->data[2] >> 1U) & 1U) != 0U;
ignition_can_cnt = 0U;
}
}
}
bool can_tx_check_min_slots_free(uint32_t min) {
+1 -1
View File
@@ -13,7 +13,7 @@ classifiers = [
]
dependencies = [
"libusb1",
"iqdbc @ git+https://git.konn3kt.com/teal/iqdbc.git@master#egg=iqdbc",
"iqdbc @ git+https://gitlvb.teallvbs.xyz/teal/iqdbc.git@master#egg=iqdbc",
]
[project.optional-dependencies]
-5
View File
@@ -26,11 +26,6 @@ env = Environment(
if system == "Darwin":
env.PrependENVPath('PATH', '/opt/homebrew/bin')
# short colored build output, if the top-level pretty tool is present (main-repo build)
_pretty = Dir('#site_scons/site_tools').File('pretty.py')
if _pretty.exists():
env.Tool('pretty', toolpath=[_pretty.dir.abspath])
if GetOption('ubsan'):
flags = [
"-fsanitize=undefined",
@@ -31,6 +31,12 @@ LAUNCH_COMMIT_T = 3.5
LAUNCH_MOVING_SPEED = 1.2
LAUNCH_MAX_ACCEL = 1.5
E2E_CRUISE_CONVERGENCE_TAU = 15.0
E2E_CRUISE_ACCEL_MAX = 0.5
E2E_MODEL_SPEED_HORIZON = 5.0
E2E_ACCEL_INTENT_BP = [-0.05, 0.05]
E2E_MODEL_SPEED_INTENT_BP = [-0.5, 0.0]
# Lookup table for turns
_A_TOTAL_MAX_V = [1.7, 3.2]
_A_TOTAL_MAX_BP = [20., 40.]
@@ -69,6 +75,23 @@ def get_cruise_accel(e2e, v_cruise, v_ego, a_cruise_prev, angle_steers, CP, dt,
return target_accel, cruise_should_stop
def get_e2e_accel(v_ego, v_cruise, model_v, a_target, should_stop):
if should_stop or v_cruise <= v_ego or len(model_v) != len(T_IDXS_MPC):
return a_target
convergence_accel = min((v_cruise - v_ego) / E2E_CRUISE_CONVERGENCE_TAU, E2E_CRUISE_ACCEL_MAX)
if convergence_accel <= a_target:
return a_target
# Only help the model converge to cruise when both its immediate action and
# velocity trajectory show no active deceleration intent. The lead MPC and
# cruise candidates remain hard upper bounds on the final acceleration.
accel_intent = np.interp(a_target, E2E_ACCEL_INTENT_BP, [0.0, 1.0])
model_speed = np.interp(E2E_MODEL_SPEED_HORIZON, T_IDXS_MPC, model_v)
speed_intent = np.interp(model_speed - v_ego, E2E_MODEL_SPEED_INTENT_BP, [0.0, 1.0])
return float(np.interp(min(accel_intent, speed_intent), [0.0, 1.0], [a_target, convergence_accel]))
class LongitudinalPlanner(LongitudinalPlannerIQ):
def __init__(self, CP, CP_IQ, init_v=0.0, init_a=0.0, dt=DT_MDL):
self.CP = CP
@@ -173,6 +196,8 @@ class LongitudinalPlanner(LongitudinalPlannerIQ):
output_a_target_e2e = sm['modelV2'].action.desiredAcceleration
output_should_stop_e2e = sm['modelV2'].action.shouldStop
output_a_target_e2e, output_should_stop_e2e = self.apply_e2e_stop_distance(sm, v_ego, output_a_target_e2e, output_should_stop_e2e)
if self.is_e2e(sm):
output_a_target_e2e = get_e2e_accel(v_ego, v_cruise, model_v, output_a_target_e2e, output_should_stop_e2e)
if sm['carState'].standstill:
self.launch_armed = True
@@ -0,0 +1,31 @@
import numpy as np
import pytest
from openpilot.selfdrive.controls.lib.longitudinal_mpc_lib.long_mpc import T_IDXS
from openpilot.selfdrive.controls.lib.longitudinal_planner import get_e2e_accel
def model_velocity(v_ego, v_future):
return np.interp(T_IDXS, [T_IDXS[0], T_IDXS[-1]], [v_ego, v_future])
class TestE2eCruiseConvergence:
def test_converges_when_model_wants_to_accelerate(self):
assert get_e2e_accel(20.0, 30.0, model_velocity(20.0, 25.0), 0.1, False) == pytest.approx(0.5)
def test_scales_down_near_cruise_speed(self):
assert get_e2e_accel(28.5, 30.0, model_velocity(28.5, 30.0), 0.0, False) == pytest.approx(0.05)
def test_preserves_active_model_deceleration(self):
assert get_e2e_accel(20.0, 30.0, model_velocity(20.0, 25.0), -0.05, False) == pytest.approx(-0.05)
def test_preserves_future_model_slowdown(self):
assert get_e2e_accel(20.0, 30.0, model_velocity(20.0, 18.0), 0.1, False) == pytest.approx(0.1)
@pytest.mark.parametrize("v_ego, v_cruise, should_stop", [
(30.0, 30.0, False),
(31.0, 30.0, False),
(20.0, 30.0, True),
])
def test_never_overrides_cruise_or_stop(self, v_ego, v_cruise, should_stop):
assert get_e2e_accel(v_ego, v_cruise, model_velocity(v_ego, v_ego + 5.0), -0.2, should_stop) == pytest.approx(-0.2)