mirror of
https://gitlvb.teallvbs.xyz/IQ.Lvbs/IQ.Pilot.git
synced 2026-07-23 20:42:11 +08:00
IQ.Pilot Release Commit @ 3807439
This commit is contained in:
@@ -0,0 +1 @@
|
||||
Wen
|
||||
@@ -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:
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
include iqdbc/car/car.capnp
|
||||
include iqdbc/car/include/c++.capnp
|
||||
recursive-include iqdbc/safety *.h
|
||||
@@ -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`.
|
||||
@@ -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.
|
||||
Executable
+117
@@ -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)
|
||||
Executable
+60
@@ -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()
|
||||
@@ -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)
|
||||
|
||||
@@ -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.
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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
|
||||
@@ -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.]
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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():
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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)
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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]}",
|
||||
)
|
||||
|
||||
@@ -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" ;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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()
|
||||
@@ -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))
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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,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()
|
||||
|
||||
@@ -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
|
||||
@@ -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
@@ -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}")
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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)
|
||||
Reference in New Issue
Block a user