mirror of
https://github.com/firestar5683/StarPilot.git
synced 2026-09-08 17:13:45 +08:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 6170955afc |
@@ -81,7 +81,6 @@ selfdrive/modeld/models/*.pkl
|
||||
# openpilot log files
|
||||
*.bz2
|
||||
*.zst
|
||||
!selfdrive/modeld/firmware/amdgpu/*.zst
|
||||
|
||||
build/
|
||||
|
||||
|
||||
-162
@@ -1,162 +0,0 @@
|
||||
# Credits and Code Provenance
|
||||
|
||||
StarPilot stands on work by comma.ai, FrogPilot, and many other open-source contributors. This
|
||||
document records provenance that is not adequately represented by StarPilot's historical commit
|
||||
authorship. It is not an assertion that an upstream contributor endorses, maintains, or is
|
||||
responsible for StarPilot's adaptation.
|
||||
|
||||
## Ford support adapted from BluePilot
|
||||
|
||||
StarPilot commit [`3f6ccd104e826643887930b41ad3b4086b833d32`](https://github.com/firestar5683/StarPilot/commit/3f6ccd104e826643887930b41ad3b4086b833d32)
|
||||
introduced a substantial adaptation of Ford work from BluePilot's `bp-7.0` branch. That commit's
|
||||
message thanked the project, but it did not record a source revision or preserve the upstream
|
||||
authors in the code. This file corrects that provenance gap without rewriting published history.
|
||||
|
||||
The exact checkout used for the original port was not recorded and therefore cannot now be proven.
|
||||
At the time of the import,
|
||||
[`3a838bba6d3d280592c2fd0496b3378977ae25f1`](https://github.com/BluePilotDev/bluepilot/commit/3a838bba6d3d280592c2fd0496b3378977ae25f1),
|
||||
was the tip of the public `bp-7.0` line. Some imported platform lines instead trace to the
|
||||
contemporaneous development line at
|
||||
[`59e3f2f16a38ff0c16d173b0ccddded23aaa1cd8`](https://github.com/BluePilotDev/bluepilot/commit/59e3f2f16a38ff0c16d173b0ccddded23aaa1cd8).
|
||||
Those histories were merged the next day as
|
||||
[`e1d051d7ba270261b4455068bd68f1a58db15a4a`](https://github.com/BluePilotDev/bluepilot/commit/e1d051d7ba270261b4455068bd68f1a58db15a4a),
|
||||
which is the complete `bp-7.0` snapshot used for this audit. This reconstruction is deliberately
|
||||
recorded as a range rather than pretending that the missing original source SHA can be recovered.
|
||||
|
||||
### Upstream authors and work
|
||||
|
||||
- **Alan Polk (`alan-polk`)** is the principal upstream author of the Ford curvature controller,
|
||||
angle-primary controller, manual-turn behavior, controller integration, and related panda safety
|
||||
work. Important lineage commits include
|
||||
[`db2bdff05`](https://github.com/BluePilotDev/bluepilot/commit/db2bdff05df103d71df62f45c2a3cb5211aba6e6),
|
||||
[`d0aac605f`](https://github.com/BluePilotDev/bluepilot/commit/d0aac605f99d37e9da205e419f7989c1e9eaa386),
|
||||
[`8f8d6d15f`](https://github.com/BluePilotDev/bluepilot/commit/8f8d6d15f0a590f42b78de964ffb0d0af7f5d63d), and
|
||||
[`97867c1eb`](https://github.com/BluePilotDev/bluepilot/commit/97867c1eb57b7472f6fc3de62f0fef576e5a5497).
|
||||
- **John Christman** contributed `bp-7.0` lateral integration, platform data, and the upstream
|
||||
anti-stall work referenced by StarPilot's recovery logic, including
|
||||
[`ec0ab181c`](https://github.com/BluePilotDev/bluepilot/commit/ec0ab181c344dccaae053e763bc6ce269551a2d8) and
|
||||
[`9012f7666`](https://github.com/BluePilotDev/bluepilot/commit/9012f76666a5c90764fcaec40832b9a607488c27),
|
||||
with additional vehicle data in
|
||||
[`f0c6bf51f`](https://github.com/BluePilotDev/bluepilot/commit/f0c6bf51f19bb7b74226ead622cabef56e602125).
|
||||
- **Jacob Neulight** contributed measured shadow-curvature and steering-pinion curvature/safety
|
||||
work, including
|
||||
[`699c17d9f`](https://github.com/BluePilotDev/bluepilot/commit/699c17d9fde62c690eed9d68eed7d731744d5137) and
|
||||
[`26030f3cb`](https://github.com/BluePilotDev/bluepilot/commit/26030f3cb56a169ab0cf2b5a5ad15fa9af391b10).
|
||||
- **Nathan Ingraham** contributed the separate high-speed damping adjustment in
|
||||
[`1db52bc79`](https://github.com/BluePilotDev/bluepilot/commit/1db52bc79e607ddb00214254dd4d732615e27fe4) and
|
||||
[`3610e3f18`](https://github.com/BluePilotDev/bluepilot/commit/3610e3f18a0ad37d793d7ada61dbc739ab1077a3).
|
||||
- **Praeuner** contributed the damping-range update in
|
||||
[`b600a8fdb`](https://github.com/BluePilotDev/bluepilot/commit/b600a8fdb985a2219fad4006ce9444b7071b52da).
|
||||
- **tonesto7** contributed to the Ford integration and CAN-message history represented in the
|
||||
imported branch, including
|
||||
[`7f9212f7f`](https://github.com/BluePilotDev/bluepilot/commit/7f9212f7f8dc22c4a0a22443158554f4d3652a3c).
|
||||
- **Haibin Wen and sunnypilot contributors** are named in file-level notices on upstream Ford
|
||||
extension files that informed StarPilot's vehicle-state and lateral-limit integration. Their
|
||||
published copyright and license notices are retained in [THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md).
|
||||
- **Dirk Petersen, Alex Troxel, Kacer Aleks, and other BluePilot contributors** supplied Ford
|
||||
fingerprints, VIN/platform data, tests, and integration changes that are represented in the
|
||||
imported vehicle-support set. Examples include
|
||||
[`3706b5645`](https://github.com/BluePilotDev/bluepilot/commit/3706b5645c270ad83cfecc3e92618924f0963166),
|
||||
[`694bdbb7a`](https://github.com/BluePilotDev/bluepilot/commit/694bdbb7a9cc70af04d0118e891df96e47ded7c7), and
|
||||
[`a02c06ef3`](https://github.com/BluePilotDev/bluepilot/commit/a02c06ef37acff518a511ba0a88a4dc65f506353).
|
||||
|
||||
This list identifies contributors whose work was found during the repository and blame audit. The
|
||||
BluePilot repository and its Git history remain the authoritative record and may identify further
|
||||
contributors.
|
||||
|
||||
### Local-to-upstream map
|
||||
|
||||
| StarPilot area | Upstream lineage | What StarPilot changed |
|
||||
| --- | --- | --- |
|
||||
| `starpilot/car/ford/lateral.py` | `human_turn.py`, `lateral_curv_ext.py`, and `values_ext.py` at the reference snapshot above; earlier StarPilot revisions also adapted `lateral_angle_ext.py` | Consolidated the runtime implementation on the extended-curvature strategy, integrated StarPilot Params, and added live-delay curvature lookahead and local tuning behavior. |
|
||||
| `starpilot/car/ford/fordcan.py` | `fordcan_ext.py` and the extended-lateral protocol, especially `8f8d6d15f` | Reduced the extension to the curvature CAN constructors used by StarPilot and adapted it to the local controller interface. |
|
||||
| `opendbc_repo/opendbc/car/ford/` | Ford controller/state/interface/radar/platform changes in the `bp-7.0` snapshot | Integrated the changes directly into StarPilot's opendbc layout instead of retaining sunnypilot mixins; subsequent fixes and behavior differ by file. |
|
||||
| `opendbc_repo/opendbc/safety/modes/ford.h` and Ford safety tests | BluePilot panda enforcement for four-signal curvature and angle-primary control, especially `8f8d6d15f`, plus shadow-curvature work | Retained the extended-curvature checks, removed runtime path-angle selection, and continued adding local regression coverage. |
|
||||
| Ford Params and settings surfaces | BluePilot's curvature tuning concepts | Renamed and implemented in StarPilot's native Params/Galaxy architecture; no BluePilot or sunnypilot UI classes were retained. |
|
||||
|
||||
The local code has materially diverged, but the first four rows remain derivative in design and in
|
||||
parts of their implementation. Future ports should cite the exact upstream commit in the importing
|
||||
commit and at the relevant source boundary; when history can be retained cleanly, use a merge,
|
||||
subtree, or cherry-pick with origin metadata rather than a single squashed attribution.
|
||||
|
||||
## Hyundai, Kia, and Genesis support adapted from sunnypilot
|
||||
|
||||
Portions of StarPilot's HKG angle steering, CAN integration, panda safety enforcement, safety tests,
|
||||
firmware fingerprints, and cruise-button management are adapted from sunnypilot. Some upstream HKG
|
||||
authorship is retained in StarPilot's Git history, and StarPilot commit
|
||||
[`e33305151`](https://github.com/firestar5683/StarPilot/commit/e33305151ba852ea3350aa9f7a12d1c8bd137c43)
|
||||
identified its ICBM/CSLC work as a sunnypilot port. Other substantial imports were committed locally
|
||||
without recording an exact source revision, so this section documents the reconstructed lineage.
|
||||
|
||||
The exact checkout used for each historical import cannot now be proven. The reference snapshots
|
||||
used for this audit are:
|
||||
|
||||
- [`sunnypilot/sunnypilot` `hkg-angle-steering-2025`](https://github.com/sunnypilot/sunnypilot/tree/hkg-angle-steering-2025)
|
||||
at [`cfb38312d`](https://github.com/sunnypilot/sunnypilot/commit/cfb38312db33779f4727c983d372474a56ccb5d8),
|
||||
whose opendbc submodule points to the next snapshot.
|
||||
- [`sunnypilot/opendbc` `hkg-angle-steering-2025`](https://github.com/sunnypilot/opendbc/tree/hkg-angle-steering-2025)
|
||||
at [`cc4b08625`](https://github.com/sunnypilot/opendbc/commit/cc4b08625a98e94b318cab15e45e05dad58042bd).
|
||||
- [`sunnypilot/opendbc` `master`](https://github.com/sunnypilot/opendbc)
|
||||
at [`f95f996f5`](https://github.com/sunnypilot/opendbc/commit/f95f996f5917dcbbf2e32fe51b606a24cf836af6),
|
||||
used to audit later HKG fingerprints and extension history.
|
||||
|
||||
### Upstream authors and work
|
||||
|
||||
- **Haibin (Jason) Wen** contributed the original Kia EV9 HDA2/LFA2 angle-steering port, Hyundai
|
||||
angle integration and signals, non-SCC platform support, and Intelligent Cruise Button Management.
|
||||
Important lineage commits include
|
||||
[`abe78de2c`](https://github.com/sunnypilot/opendbc/commit/abe78de2c2f9d8774d343c35c181d27e9d944392),
|
||||
[`333de6f1a`](https://github.com/sunnypilot/opendbc/commit/333de6f1a2097a27709a652d5862bad05d83ed1f),
|
||||
[`559a37426`](https://github.com/sunnypilot/opendbc/commit/559a37426976171ceb56c541bec9362abd8b8bd2), and
|
||||
[`862828ad6`](https://github.com/sunnypilot/opendbc/commit/862828ad6f870fb23dd8670fe2a18dc19313217b).
|
||||
- **Shane Smiskol** contributed foundational angle-command limiting, driver-override behavior, and
|
||||
EPS-fault avoidance, including
|
||||
[`228a397a3`](https://github.com/sunnypilot/opendbc/commit/228a397a37618de1ecbaf06b70efe3e0e0a8eec6) and
|
||||
[`42d84ff6c`](https://github.com/sunnypilot/opendbc/commit/42d84ff6ca3d48529b195395d652ad777ad397ca).
|
||||
- **DevTekVE** contributed substantial angle-controller integration, tuning, platform support, panda
|
||||
safety logic, and tests, including
|
||||
[`c77c7ec2e`](https://github.com/sunnypilot/opendbc/commit/c77c7ec2e39959b65c42e2d70aa943fccd606361),
|
||||
[`7ded99dba`](https://github.com/sunnypilot/opendbc/commit/7ded99dba145c56754e8bbe47217eb174ec03396),
|
||||
[`3819ca7f0`](https://github.com/sunnypilot/opendbc/commit/3819ca7f0d2576771924bd60f47d3e8676b5d583), and
|
||||
[`8d134e98f`](https://github.com/sunnypilot/opendbc/commit/8d134e98f3151a1aec354f93e81c3a4269788991).
|
||||
- **Nicholas Evans, dany7915, janpoo6427, Tinkerpet, royjr, Taylor Hoshino, Intelli, Joshua Mack,
|
||||
Mark McCallister, Discountchubbs, and other sunnypilot contributors** supplied vehicle ports,
|
||||
fingerprints, firmware data, safety-limit updates, and related integration represented in the
|
||||
adapted HKG support.
|
||||
|
||||
This list identifies contributors found during the repository, commit, and blame audit. The
|
||||
sunnypilot repositories and their Git histories remain the authoritative record and may identify
|
||||
additional contributors.
|
||||
|
||||
### Local-to-upstream map
|
||||
|
||||
| StarPilot area | Upstream lineage | What StarPilot changed |
|
||||
| --- | --- | --- |
|
||||
| `opendbc_repo/opendbc/car/hyundai/carcontroller.py` | Angle controller and driver-override work in `sunnypilot/opendbc` `hkg-angle-steering-2025` | Integrated the controller into StarPilot's opendbc layout and added substantial local longitudinal, smoothing, recovery, and platform behavior. |
|
||||
| `opendbc_repo/opendbc/car/hyundai/hyundaicanfd.py`, `interface.py`, and `values.py` | Angle commands, signal selection, safety flags, limits, and platform integration from the angle branch | Combined later upstream changes with StarPilot flags, Params, platform tuning, and local CAN/CAN-FD behavior. |
|
||||
| `opendbc_repo/opendbc/safety/modes/hyundai_canfd.h` and its tests | Angle-command safety enforcement and regression tests from the angle branch | Extended and reorganized the safety mode and tests for StarPilot's current supported modes. |
|
||||
| `opendbc_repo/opendbc/car/hyundai/fingerprints.py` and HKG platform data | sunnypilot HKG extension/fingerprint history on `master` plus angle-branch vehicle ports | Flattened extension data into the local opendbc tree and continued adding and updating platforms. |
|
||||
| HKG cruise-button management and settings integration | sunnypilot ICBM/CSLC concepts and implementation history, including `862828ad6` and `2277e3d49` | Adapted the feature to StarPilot/FrogPilot controls and settings; later revisions changed or removed portions of the original integration. |
|
||||
|
||||
The current implementation is not a wholesale copy of either reference snapshot and has diverged
|
||||
substantially. Its HKG angle-control and supporting safety architecture nevertheless remain
|
||||
derivative in design and in identifiable portions of the implementation. The applicable upstream
|
||||
notices are preserved in [THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md).
|
||||
|
||||
## Policy for future third-party ports
|
||||
|
||||
Before publishing a third-party port:
|
||||
|
||||
1. Read every repository-level and file-level license that may cover the source. Stop and ask the
|
||||
rightsholder when the terms or their scope conflict.
|
||||
2. Record the repository URL, exact commit SHA, source paths/symbols, authors found in the relevant
|
||||
history, and the local destination in the importing commit and this file.
|
||||
3. Preserve required copyright and license notices verbatim. Reorganization, translation, and
|
||||
AI-assisted rewriting do not remove source provenance.
|
||||
4. Retain authorship/history with a merge, subtree, or `cherry-pick -x` when practical. For a true
|
||||
adaptation, keep the local adapter as commit author and use an `Adapted-from:` trailer and source
|
||||
comments; do not add `Co-authored-by:` for a person without their agreement.
|
||||
5. Describe material local deviations and make clear that upstream contributors do not support or
|
||||
endorse the downstream version.
|
||||
|
||||
See [THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md) for licensing notices.
|
||||
@@ -1,5 +1,4 @@
|
||||
Copyright (c) 2018, Comma.ai, Inc.
|
||||
Copyright (c) 2026, firestar5683 and StarPilot 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:
|
||||
|
||||
|
||||
@@ -21,20 +21,6 @@ but [has expanded to offer Quality-Of-Life improvements for all](#features)!
|
||||
StarPilot is built off of [FrogPilot](https://github.com/FrogAi/FrogPilot)
|
||||
and supports the major features FrogPilot offers.
|
||||
|
||||
Ford-specific lateral-control and vehicle-support work includes substantial adaptations from
|
||||
[BluePilot](https://github.com/BluePilotDev/bluepilot/tree/bp-7.0), principally developed by
|
||||
[Alan Polk](https://github.com/alan-polk) with additional BluePilot contributors. StarPilot's
|
||||
implementation has since diverged, but that does not erase its lineage. See [CREDITS.md](CREDITS.md)
|
||||
for the code-level provenance and [THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md) for applicable
|
||||
upstream notices and terms. BluePilot and its contributors do not maintain or endorse StarPilot;
|
||||
please direct support requests for this adaptation to the StarPilot project.
|
||||
|
||||
Hyundai, Kia, and Genesis angle steering and related vehicle support include substantial adaptations
|
||||
from [sunnypilot](https://github.com/sunnypilot/sunnypilot/tree/hkg-angle-steering-2025) and its
|
||||
[opendbc angle-steering branch](https://github.com/sunnypilot/opendbc/tree/hkg-angle-steering-2025).
|
||||
StarPilot's implementation has diverged significantly; the upstream contributors do not maintain
|
||||
this adaptation. Detailed code lineage is recorded in [CREDITS.md](CREDITS.md#hyundai-kia-and-genesis-support-adapted-from-sunnypilot).
|
||||
|
||||
StarPilot has a vibrant, welcoming community [discord](https://firestar.link/discord).
|
||||
Stop by to chat or ask questions!
|
||||
|
||||
@@ -91,9 +77,4 @@ Uses your comma's sysroot/toolchain
|
||||
* Custom long maneuver tests, specifically designed for regen-only vehicles
|
||||
|
||||
## Third-Party Notices
|
||||
|
||||
* Portions of this software include modified versions of the Material Design Icons provided by Google under the Apache License 2.0. A copy of the license is included in the `LICENSE-MDI` file.
|
||||
* Ford support includes software adapted from BluePilot's `bp-7.0` branch. The source repository contains both a standard MIT notice and a separate custom SUNNYPILOT LLC notice; StarPilot preserves both in [THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md).
|
||||
* Hyundai, Kia, and Genesis support includes software adapted directly from sunnypilot's HKG angle-steering branch and sunnypilot/opendbc. StarPilot preserves the applicable notices in [THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md).
|
||||
|
||||
> This project uses software from Haibin Wen and SUNNYPILOT LLC and is licensed under a custom license requiring permission for use.
|
||||
|
||||
@@ -1,125 +0,0 @@
|
||||
# Third-Party Notices
|
||||
|
||||
This file supplements StarPilot's root `LICENSE`. StarPilot-original contributions are offered
|
||||
under that MIT license. Third-party material remains subject to its original terms; inclusion here
|
||||
does not relicense it or imply endorsement by an upstream project or contributor.
|
||||
|
||||
## BluePilot `bp-7.0` Ford support
|
||||
|
||||
Portions of StarPilot's Ford lateral control, CAN integration, panda safety logic, vehicle data,
|
||||
and related tests are adapted from the public BluePilot repository:
|
||||
|
||||
- Source: <https://github.com/BluePilotDev/bluepilot/tree/bp-7.0>
|
||||
- Audited `bp-7.0` snapshot: [`e1d051d7ba270261b4455068bd68f1a58db15a4a`](https://github.com/BluePilotDev/bluepilot/commit/e1d051d7ba270261b4455068bd68f1a58db15a4a)
|
||||
- Historical reconstruction: [CREDITS.md](CREDITS.md#ford-support-adapted-from-bluepilot)
|
||||
- Provenance and contributors: [CREDITS.md](CREDITS.md)
|
||||
|
||||
## sunnypilot Hyundai, Kia, and Genesis support
|
||||
|
||||
Portions of StarPilot's HKG angle steering, CAN integration, panda safety enforcement and tests,
|
||||
firmware fingerprints, platform data, and cruise-button management are adapted directly from the
|
||||
public sunnypilot repositories:
|
||||
|
||||
- Source: <https://github.com/sunnypilot/sunnypilot/tree/hkg-angle-steering-2025>
|
||||
- Audited superproject snapshot: [`cfb38312db33779f4727c983d372474a56ccb5d8`](https://github.com/sunnypilot/sunnypilot/commit/cfb38312db33779f4727c983d372474a56ccb5d8)
|
||||
- Source: <https://github.com/sunnypilot/opendbc/tree/hkg-angle-steering-2025>
|
||||
- Audited angle-branch snapshot: [`cc4b08625a98e94b318cab15e45e05dad58042bd`](https://github.com/sunnypilot/opendbc/commit/cc4b08625a98e94b318cab15e45e05dad58042bd)
|
||||
- Later HKG history was audited against `sunnypilot/opendbc` `master` at
|
||||
[`f95f996f5917dcbbf2e32fe51b606a24cf836af6`](https://github.com/sunnypilot/opendbc/commit/f95f996f5917dcbbf2e32fe51b606a24cf836af6).
|
||||
- Historical reconstruction and contributors: [CREDITS.md](CREDITS.md#hyundai-kia-and-genesis-support-adapted-from-sunnypilot)
|
||||
|
||||
## Applicable upstream license notices
|
||||
|
||||
The upstream repositories and branches identified above publish both `LICENSE` and `LICENSE.md`.
|
||||
Their READMEs point to `LICENSE` for openpilot licensing, while some extension files point to
|
||||
`LICENSE.md`. Because the scope of those two notices is not unambiguous, StarPilot preserves both
|
||||
and treats the more restrictive notice conservatively for upstream-derived material. This is a
|
||||
record of the published notices, not a legal conclusion about their scope.
|
||||
|
||||
### Upstream `LICENSE` notice (MIT)
|
||||
|
||||
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.
|
||||
|
||||
### Upstream `opendbc` `LICENSE` notice (MIT)
|
||||
|
||||
Copyright (c) 2020, Comma.ai, Inc.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
|
||||
associated documentation files (the "Software"), to deal in the Software without restriction,
|
||||
including without limitation the rights to use, copy, modify, merge, publish, distribute,
|
||||
sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or
|
||||
substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
|
||||
NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT
|
||||
OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
### Upstream extension file notice
|
||||
|
||||
Several upstream Ford and HKG extension files carry this file-level notice:
|
||||
|
||||
> Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
|
||||
>
|
||||
> This file is part of sunnypilot and is licensed under the MIT License.
|
||||
> See the LICENSE.md file in the root directory for more details.
|
||||
|
||||
StarPilot's Ford and HKG integrations were informed by extension files carrying this notice. The
|
||||
reference to “MIT License” conflicts with the nonstandard restrictions in the referenced upstream
|
||||
`LICENSE.md`; both texts are retained here rather than silently choosing between them.
|
||||
|
||||
### Upstream `LICENSE.md` notice (published as “Custom MIT License”)
|
||||
|
||||
The following notice is reproduced verbatim from the reference branch:
|
||||
|
||||
> # Custom MIT License
|
||||
>
|
||||
> Copyright (c) 2024, Haibin Wen, SUNNYPILOT LLC
|
||||
>
|
||||
> Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to view and modify the Software, subject to the following conditions:
|
||||
>
|
||||
> 1. **Permission Required**: Permission Required for Commercial, For-Profit, or Closed Source Use: Use of the Software, in whole or in part, for any commercial purposes, for-profit projects, or in closed source projects requires explicit written permission from the original author(s).
|
||||
>
|
||||
> 2. **Redistribution**: Any redistribution of the Software, modified or unmodified, must retain this license notice and the following acknowledgment:
|
||||
> "This software is licensed under a custom license requiring permission for use."
|
||||
>
|
||||
> 3. **Visibility**: Any project that uses the Software must visibly mention the following acknowledgment:
|
||||
> "This project uses software from Haibin Wen and SUNNYPILOT LLC and is licensed under a custom license requiring permission for use."
|
||||
>
|
||||
> 4. **No Warranty**: THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
>
|
||||
> Contact sunnypilot Support <support@sunnypilot.ai> for permission requests.
|
||||
>
|
||||
> ---
|
||||
>
|
||||
> Haibin Wen, SUNNYPILOT LLC
|
||||
|
||||
Required upstream acknowledgments:
|
||||
|
||||
> This software is licensed under a custom license requiring permission for use.
|
||||
>
|
||||
> This project uses software from Haibin Wen and SUNNYPILOT LLC and is licensed under a custom license requiring permission for use.
|
||||
|
||||
For commercial, for-profit, or closed-source use, consult the upstream notice and obtain any
|
||||
permission it requires. The upstream repository's simultaneous publication of two differently
|
||||
scoped notices should be clarified with the relevant copyright holders before relying on one to
|
||||
the exclusion of the other.
|
||||
Binary file not shown.
@@ -774,7 +774,6 @@ struct ChestnutState {
|
||||
pcieLtssm @7 :UInt8;
|
||||
supplyVoltage @8 :UInt16; # mV
|
||||
supplyCurrent @9 :Int16; # mA
|
||||
supplyFault @10 :Bool;
|
||||
}
|
||||
|
||||
struct RadarState @0x9a185389d6fdd05f {
|
||||
|
||||
+2
-19
@@ -1,25 +1,8 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from cereal import car
|
||||
from openpilot.common.params import Params
|
||||
|
||||
|
||||
def gm_car_params_present(params: Params, CP: car.CarParams | None = None) -> bool:
|
||||
if CP is not None and getattr(CP, "brand", None):
|
||||
return CP.brand == "gm"
|
||||
try:
|
||||
raw_car_params = params.get("CarParams")
|
||||
if raw_car_params is None:
|
||||
return False
|
||||
with car.CarParams.from_bytes(raw_car_params) as parsed_cp:
|
||||
return parsed_cp.brand == "gm"
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
|
||||
def get_gps_location_service(params: Params, CP: car.CarParams | None = None) -> str:
|
||||
# GM arbitrates device/PPS/OnStar through gpsLocationExternal.
|
||||
if gm_car_params_present(params, CP) or params.get_bool("UbloxAvailable") or params.get_bool("CarGpsAvailable"):
|
||||
def get_gps_location_service(params: Params) -> str:
|
||||
if params.get_bool("UbloxAvailable") or params.get_bool("CarGpsAvailable"):
|
||||
return "gpsLocationExternal"
|
||||
else:
|
||||
return "gpsLocation"
|
||||
|
||||
Binary file not shown.
+6
-15
@@ -113,12 +113,6 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
|
||||
{"ObdMultiplexingChanged", {CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION, BOOL}},
|
||||
{"ObdMultiplexingEnabled", {CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION, BOOL}},
|
||||
{"Offroad_CarUnrecognized", {CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION, JSON}},
|
||||
{"Offroad_ChestnutNotDetected", {CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION, JSON}},
|
||||
{"Offroad_ChestnutOverheated", {CLEAR_ON_MANAGER_START, JSON}},
|
||||
{"Offroad_ChestnutPcieUnavailable", {CLEAR_ON_MANAGER_START, JSON}},
|
||||
{"Offroad_ChestnutUncompiled", {CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION, JSON}},
|
||||
{"Offroad_ChestnutUpdateFailed", {CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION, JSON}},
|
||||
{"Offroad_ChestnutUsbSlow", {CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION, JSON}},
|
||||
{"Offroad_ConnectivityNeeded", {CLEAR_ON_MANAGER_START, JSON}},
|
||||
{"Offroad_ConnectivityNeededPrompt", {CLEAR_ON_MANAGER_START, JSON}},
|
||||
{"Offroad_ExcessiveActuation", {PERSISTENT, JSON}},
|
||||
@@ -316,7 +310,6 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
|
||||
{"DeveloperSidebarMetric7", {PERSISTENT, INT, "7", "0", 3}},
|
||||
{"DeveloperUI", {PERSISTENT, BOOL, "0", "0", 3}},
|
||||
{"GalaxyDeveloperMode", {PERSISTENT | DONT_LOG, BOOL, "0", "0", 0, SETTINGS_SIMPLE}},
|
||||
{"GalaxyMobileDefault", {PERSISTENT | DONT_LOG, BOOL, "0", "0", 0, SETTINGS_ADVANCED}},
|
||||
{"DeveloperWidgets", {PERSISTENT, BOOL, "1", "0", 3}},
|
||||
{"DeviceManagement", {PERSISTENT, BOOL, "1", "0", 1, SETTINGS_SIMPLE}},
|
||||
{"DeviceShutdown", {PERSISTENT, INT, "6", "6", 1, SETTINGS_SIMPLE}},
|
||||
@@ -337,12 +330,6 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
|
||||
{"DownloadAllModels", {CLEAR_ON_MANAGER_START, BOOL, "0", "0"}},
|
||||
{"DownloadMaps", {CLEAR_ON_MANAGER_START, BOOL, "0", "0"}},
|
||||
{"DriverCamera", {PERSISTENT, BOOL, "0", "0", 1, SETTINGS_SIMPLE}},
|
||||
{"ActiveBigModel", {PERSISTENT, STRING}},
|
||||
{"ActiveBigModelName", {PERSISTENT, STRING}},
|
||||
{"ActiveBigModelVersion", {PERSISTENT, STRING}},
|
||||
{"ActiveSmallModel", {PERSISTENT, STRING}},
|
||||
{"ActiveSmallModelName", {PERSISTENT, STRING}},
|
||||
{"ActiveSmallModelVersion", {PERSISTENT, STRING}},
|
||||
{"Model", {PERSISTENT, STRING, "rdf43", "rdf43", 1}},
|
||||
{"ModelVersion", {PERSISTENT, STRING, "v15", "v15", 1}},
|
||||
{"DrivingModel", {PERSISTENT, STRING, "rdf43", "rdf43", 1}},
|
||||
@@ -385,13 +372,17 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
|
||||
{"ForceStandstill", {PERSISTENT, BOOL, "0", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"FordLKASButtonControlMigrated", {PERSISTENT, BOOL, "0", "0"}},
|
||||
{"ForceTorqueController", {PERSISTENT, BOOL, "0", "0", 3}},
|
||||
// These Ford curvature tuning concepts descend from BluePilot bp-7.0. StarPilot's key names and
|
||||
// settings integration are local; see /CREDITS.md and /THIRD_PARTY_NOTICES.md for provenance.
|
||||
{"FordAngleBlend", {PERSISTENT, FLOAT, "0.5", "0.5", 2}},
|
||||
{"FordAngleHighSpeedDamping", {PERSISTENT, FLOAT, "1.0", "1.0", 2}},
|
||||
{"FordAngleHighSpeedFactor", {PERSISTENT, FLOAT, "1.0", "1.0", 2}},
|
||||
{"FordAngleLaneChangeFactor", {PERSISTENT, FLOAT, "1.0", "1.0", 2}},
|
||||
{"FordAngleLowSpeedFactor", {PERSISTENT, FLOAT, "1.0", "1.0", 2}},
|
||||
{"FordCurvatureBlendHigh", {PERSISTENT, FLOAT, "0.4", "0.4", 2}},
|
||||
{"FordCurvatureBlendLow", {PERSISTENT, FLOAT, "0.4", "0.4", 2}},
|
||||
{"FordCurvatureLaneChangeFactor", {PERSISTENT, FLOAT, "0.85", "0.85", 2}},
|
||||
{"FordHandsFreeCluster", {PERSISTENT, BOOL, "0", "0", 2}},
|
||||
{"FordHumanTurnDetection", {PERSISTENT, BOOL, "1", "1", 2}},
|
||||
{"FordLateralMode", {PERSISTENT, INT, "1", "1", 2}},
|
||||
{"FLMActiveOverrides", {PERSISTENT, JSON, "{}", "{}", 2}},
|
||||
{"FLMActiveProfileId", {PERSISTENT, STRING, "", "", 2}},
|
||||
{"FLMSubmittedTune", {CLEAR_ON_MANAGER_START, JSON, "{}", "{}"}},
|
||||
|
||||
Binary file not shown.
+1
-1
@@ -21,7 +21,7 @@ fi
|
||||
export QCOM_PRIORITY=12
|
||||
|
||||
if [ -z "$AGNOS_VERSION" ]; then
|
||||
export AGNOS_VERSION="19.6.20"
|
||||
export AGNOS_VERSION="19.6.19"
|
||||
fi
|
||||
|
||||
if [ -z "$AGNOS_ACCEPTED_VERSIONS" ]; then
|
||||
|
||||
@@ -10,7 +10,6 @@ from opendbc.car.carlog import carlog
|
||||
from opendbc.car.structs import CarParams, CarParamsT
|
||||
from opendbc.car.fingerprints import eliminate_incompatible_cars, all_legacy_fingerprint_cars
|
||||
from opendbc.car.fw_versions import ObdCallback, get_fw_versions_ordered, get_present_ecus, match_fw_to_car
|
||||
from opendbc.car.hyundai.values import kia_ray_ev_vin
|
||||
from opendbc.car.mock.values import CAR as MOCK
|
||||
from opendbc.car.toyota.values import ToyotaSafetyFlags
|
||||
from opendbc.car.values import BRANDS
|
||||
@@ -247,13 +246,8 @@ def fingerprint(can_recv: CanRecvCallable, can_send: CanSendCallable, set_obd_mu
|
||||
set_obd_multiplexing(True)
|
||||
# VIN query only reliably works through OBDII
|
||||
vin_rx_addr, vin_rx_bus, vin = get_vin(can_recv, can_send, (0, 1))
|
||||
skip_fw_buses = {1} if kia_ray_ev_vin(vin) else set()
|
||||
if skip_fw_buses:
|
||||
carlog.warning("Kia Ray EV: skipping CAN1 firmware queries")
|
||||
ecu_rx_addrs = get_present_ecus(can_recv, can_send, set_obd_multiplexing,
|
||||
num_pandas=num_pandas, skip_buses=skip_fw_buses)
|
||||
car_fw = get_fw_versions_ordered(can_recv, can_send, set_obd_multiplexing, vin, ecu_rx_addrs,
|
||||
num_pandas=num_pandas, skip_buses=skip_fw_buses)
|
||||
ecu_rx_addrs = get_present_ecus(can_recv, can_send, set_obd_multiplexing, num_pandas=num_pandas)
|
||||
car_fw = get_fw_versions_ordered(can_recv, can_send, set_obd_multiplexing, vin, ecu_rx_addrs, num_pandas=num_pandas)
|
||||
cached = False
|
||||
|
||||
exact_fw_match, fw_candidates = match_fw_to_car(car_fw, vin)
|
||||
|
||||
@@ -1,15 +1,13 @@
|
||||
import math
|
||||
import numpy as np
|
||||
from opendbc.can import CANPacker
|
||||
from opendbc.car import ACCELERATION_DUE_TO_GRAVITY, Bus, DT_CTRL, structs
|
||||
from opendbc.car import ACCELERATION_DUE_TO_GRAVITY, Bus, DT_CTRL, apply_hysteresis, structs
|
||||
from opendbc.car.lateral import ISO_LATERAL_ACCEL, apply_std_steer_angle_limits
|
||||
from opendbc.car.ford import fordcan
|
||||
from opendbc.car.ford.values import CarControllerParams, FordFlags
|
||||
from opendbc.car.ford.values import CarControllerParams, FordFlags, CAR
|
||||
from opendbc.car.interfaces import CarControllerBase, V_CRUISE_MAX
|
||||
# This Ford extension boundary substantially adapts BluePilot bp-7.0 work. See the root CREDITS.md
|
||||
# (including Alan Polk's d0aac605f and db2bdff05) and THIRD_PARTY_NOTICES.md.
|
||||
from openpilot.starpilot.car.ford import fordcan as starpilot_fordcan
|
||||
from openpilot.starpilot.car.ford.lateral import FordLateralController, FordLateralResult
|
||||
from openpilot.starpilot.car.ford.lateral import FordLateralController, FordLateralMode, FordLateralResult
|
||||
|
||||
LongCtrlState = structs.CarControl.Actuators.LongControlState
|
||||
VisualAlert = structs.CarControl.HUDControl.VisualAlert
|
||||
@@ -45,6 +43,22 @@ def apply_ford_angle(desired_angle_deg: float, current_angle_deg: float) -> floa
|
||||
return float(np.clip(relative_angle, -5.8, 5.8))
|
||||
|
||||
|
||||
def anti_overshoot(apply_curvature, apply_curvature_last, v_ego):
|
||||
diff = 0.1
|
||||
tau = 5 # 5s smooths over the overshoot
|
||||
dt = DT_CTRL * CarControllerParams.STEER_STEP
|
||||
alpha = 1 - np.exp(-dt / tau)
|
||||
|
||||
lataccel = apply_curvature * (v_ego ** 2)
|
||||
last_lataccel = apply_curvature_last * (v_ego ** 2)
|
||||
last_lataccel = apply_hysteresis(lataccel, last_lataccel, diff)
|
||||
last_lataccel = alpha * lataccel + (1 - alpha) * last_lataccel
|
||||
|
||||
output_curvature = last_lataccel / (max(v_ego, 1) ** 2)
|
||||
|
||||
return float(np.interp(v_ego, [5, 10], [apply_curvature, output_curvature]))
|
||||
|
||||
|
||||
def apply_ford_curvature_limits(apply_curvature, apply_curvature_last, current_curvature, v_ego_raw, steering_angle, lat_active, CP):
|
||||
# No blending at low speed due to lack of torque wind-up and inaccurate current curvature
|
||||
if v_ego_raw > 9:
|
||||
@@ -79,6 +93,7 @@ class CarController(CarControllerBase):
|
||||
|
||||
self.apply_curvature_last = 0
|
||||
self.apply_angle_last = 0
|
||||
self.anti_overshoot_curvature_last = 0
|
||||
self.accel = 0.0
|
||||
self.gas = 0.0
|
||||
self.brake_request = False
|
||||
@@ -88,7 +103,8 @@ class CarController(CarControllerBase):
|
||||
self.lead_distance_bars_last = None
|
||||
self.distance_bar_frame = 0
|
||||
self.ford_lateral = None if CP.flags & FordFlags.LKA_STEERING else FordLateralController(CP)
|
||||
self.ford_extended_lateral_announced = False
|
||||
self.ford_shadow_curvature = 0.0
|
||||
self.ford_lateral_announced_mode = FordLateralMode.native
|
||||
self.stock_cruise_button = FordStockCruiseButton()
|
||||
|
||||
def update(self, CC, CS, now_nanos, starpilot_toggles):
|
||||
@@ -155,26 +171,70 @@ class CarController(CarControllerBase):
|
||||
can_sends.append(fordcan.create_lka_msg(self.packer, self.CAN, active=lka_active, apply_angle=self.apply_angle_last,
|
||||
direction=direction, ramp_type=ramp_type, curvature=-self.apply_curvature_last))
|
||||
else:
|
||||
if (self.frame % CarControllerParams.STEER_STEP) == 0:
|
||||
lateral = self.ford_lateral.update(CC, CS, actuators) \
|
||||
if self.ford_extended_lateral_announced else FordLateralResult()
|
||||
lateral_mode = self.ford_lateral.mode
|
||||
lateral_mode_ready = lateral_mode == self.ford_lateral_announced_mode
|
||||
|
||||
# Keep the original Ford path available without changing its command behavior.
|
||||
if lateral_mode == FordLateralMode.native:
|
||||
if (self.frame % CarControllerParams.STEER_STEP) == 0:
|
||||
if not lateral_mode_ready:
|
||||
self.apply_curvature_last = 0.0
|
||||
apply_curvature = 0.0
|
||||
elif self.CP.carFingerprint in (CAR.FORD_BRONCO_SPORT_MK1, CAR.FORD_F_150_MK14):
|
||||
self.anti_overshoot_curvature_last = anti_overshoot(
|
||||
actuators.curvature, self.anti_overshoot_curvature_last, CS.out.vEgoRaw)
|
||||
apply_curvature = self.anti_overshoot_curvature_last
|
||||
else:
|
||||
apply_curvature = actuators.curvature
|
||||
|
||||
current_curvature = -CS.out.yawRate / max(CS.out.vEgoRaw, 0.1)
|
||||
self.apply_curvature_last = apply_ford_curvature_limits(
|
||||
apply_curvature, self.apply_curvature_last, current_curvature,
|
||||
CS.out.vEgoRaw, 0., CC.latActive and lateral_mode_ready, self.CP)
|
||||
|
||||
if self.CP.flags & FordFlags.CANFD:
|
||||
mode = 1 if CC.latActive and lateral_mode_ready else 0
|
||||
counter = (self.frame // CarControllerParams.STEER_STEP) % 0x10
|
||||
can_sends.append(fordcan.create_lat_ctl2_msg(
|
||||
self.packer, self.CAN, mode, 0., 0., -self.apply_curvature_last, 0., counter))
|
||||
else:
|
||||
can_sends.append(fordcan.create_lat_ctl_msg(
|
||||
self.packer, self.CAN, CC.latActive and lateral_mode_ready, 0., 0., -self.apply_curvature_last, 0.))
|
||||
elif (self.frame % CarControllerParams.STEER_STEP) == 0:
|
||||
if not lateral_mode_ready:
|
||||
lateral = FordLateralResult(shadow_curvature=self.ford_lateral._current_curvature(CS))
|
||||
elif lateral_mode == FordLateralMode.angle:
|
||||
lateral = self.ford_lateral.update_angle(CC, CS, actuators)
|
||||
else:
|
||||
lateral = self.ford_lateral.update_curvature(CC, CS, actuators)
|
||||
|
||||
self.apply_curvature_last = lateral.curvature
|
||||
self.ford_shadow_curvature = lateral.shadow_curvature
|
||||
if self.CP.flags & FordFlags.CANFD:
|
||||
counter = (self.frame // CarControllerParams.STEER_STEP) % 0x10
|
||||
can_sends.append(starpilot_fordcan.create_lat_ctl2_msg(
|
||||
self.packer, self.CAN, 1 if lateral.active else 0,
|
||||
lateral.ramp_type, lateral.precision_type,
|
||||
-lateral.path_offset, -lateral.path_angle,
|
||||
-lateral.curvature, -lateral.curvature_rate, counter))
|
||||
else:
|
||||
can_sends.append(starpilot_fordcan.create_lat_ctl_msg(
|
||||
self.packer, self.CAN, lateral.active,
|
||||
lateral.ramp_type, lateral.precision_type,
|
||||
-lateral.path_offset, -lateral.path_angle,
|
||||
-lateral.curvature, -lateral.curvature_rate))
|
||||
|
||||
if (self.frame % CarControllerParams.LKA_STEP) == 0:
|
||||
can_sends.append(starpilot_fordcan.create_lka_msg(self.packer, self.CAN))
|
||||
self.ford_extended_lateral_announced = True
|
||||
if lateral_mode == FordLateralMode.native:
|
||||
can_sends.append(fordcan.create_lka_msg(self.packer, self.CAN))
|
||||
else:
|
||||
angle_mode = lateral_mode == FordLateralMode.angle
|
||||
shadow_curvature = -self.ford_lateral._current_curvature(CS)
|
||||
if angle_mode:
|
||||
shadow_curvature = -self.ford_shadow_curvature
|
||||
can_sends.append(starpilot_fordcan.create_lka_msg(
|
||||
self.packer, self.CAN, angle_mode=angle_mode, shadow_curvature=shadow_curvature))
|
||||
self.ford_lateral_announced_mode = lateral_mode
|
||||
|
||||
### longitudinal control ###
|
||||
# send acc msg at 50Hz
|
||||
@@ -232,7 +292,8 @@ class CarController(CarControllerBase):
|
||||
show_distance_bars = self.frame - self.distance_bar_frame < 400
|
||||
hands_free_cluster = bool(
|
||||
self.ford_lateral is not None
|
||||
and self.ford_extended_lateral_announced
|
||||
and self.ford_lateral.mode != FordLateralMode.native
|
||||
and self.ford_lateral.mode == self.ford_lateral_announced_mode
|
||||
and self.ford_lateral.hands_free_cluster_enabled)
|
||||
can_sends.append(fordcan.create_acc_ui_msg(self.packer, self.CAN, self.CP, main_on, CC.latActive,
|
||||
fcw_alert, CS.out.cruiseState.standstill, show_distance_bars,
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
# Ford-specific additions first imported in StarPilot 3f6ccd104e substantially adapt BluePilot
|
||||
# bp-7.0 vehicle-state work, including a sunnypilot extension with the Haibin Wen/contributors
|
||||
# notice retained in THIRD_PARTY_NOTICES.md. See the repository root CREDITS.md for provenance.
|
||||
from cereal import custom
|
||||
from opendbc.can import CANDefine, CANParser
|
||||
from opendbc.car import Bus, create_button_events, structs
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
""" AUTO-FORMATTED USING opendbc/car/debug/format_fingerprints.py, EDIT STRUCTURE THERE."""
|
||||
# Some Ford firmware entries first imported in StarPilot 3f6ccd104e came from BluePilot bp-7.0
|
||||
# contributors. The exact authors and source revisions are recorded in the root CREDITS.md.
|
||||
from opendbc.car.structs import CarParams
|
||||
from opendbc.car.ford.values import CAR
|
||||
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
# Ford-specific additions first imported in StarPilot 3f6ccd104e substantially adapt BluePilot
|
||||
# bp-7.0 interface work. See the repository root CREDITS.md and THIRD_PARTY_NOTICES.md.
|
||||
import numpy as np
|
||||
from opendbc.car import Bus, get_safety_config, structs
|
||||
from opendbc.car.carlog import carlog
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
# Ford-specific additions first imported in StarPilot 3f6ccd104e substantially adapt BluePilot
|
||||
# bp-7.0 radar work. See the repository root CREDITS.md and THIRD_PARTY_NOTICES.md.
|
||||
import numpy as np
|
||||
from collections import deque
|
||||
from typing import cast
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
# Ford platform data and limits first imported in StarPilot 3f6ccd104e substantially adapt
|
||||
# BluePilot bp-7.0 contributor work. See the repository root CREDITS.md and THIRD_PARTY_NOTICES.md.
|
||||
import copy
|
||||
import re
|
||||
from dataclasses import dataclass, field, replace
|
||||
|
||||
@@ -170,9 +170,7 @@ def match_fw_to_car(fw_versions: list[CarParams.CarFw], vin: str, allow_exact: b
|
||||
return True, set()
|
||||
|
||||
|
||||
def get_present_ecus(can_recv: CanRecvCallable, can_send: CanSendCallable, set_obd_multiplexing: ObdCallback,
|
||||
num_pandas: int = 1, skip_buses: set[int] | None = None) -> set[EcuAddrBusType]:
|
||||
skip_buses = skip_buses or set()
|
||||
def get_present_ecus(can_recv: CanRecvCallable, can_send: CanSendCallable, set_obd_multiplexing: ObdCallback, num_pandas: int = 1) -> set[EcuAddrBusType]:
|
||||
# queries are split by OBD multiplexing mode
|
||||
queries: dict[bool, list[list[EcuAddrBusType]]] = {True: [], False: []}
|
||||
parallel_queries: dict[bool, list[EcuAddrBusType]] = {True: [], False: []}
|
||||
@@ -180,7 +178,7 @@ def get_present_ecus(can_recv: CanRecvCallable, can_send: CanSendCallable, set_o
|
||||
|
||||
for brand, config, r in REQUESTS:
|
||||
# Skip query if no panda available
|
||||
if r.bus > num_pandas * 4 - 1 or r.bus in skip_buses:
|
||||
if r.bus > num_pandas * 4 - 1:
|
||||
continue
|
||||
|
||||
for ecu_type, addr, sub_addr in config.get_all_ecus(VERSIONS[brand]):
|
||||
@@ -237,8 +235,7 @@ def get_brand_ecu_matches(ecu_rx_addrs: set[EcuAddrBusType]) -> dict[str, list[b
|
||||
|
||||
|
||||
def get_fw_versions_ordered(can_recv: CanRecvCallable, can_send: CanSendCallable, set_obd_multiplexing: ObdCallback, vin: str,
|
||||
ecu_rx_addrs: set[EcuAddrBusType], timeout: float = 0.1, num_pandas: int = 1,
|
||||
progress: bool = False, skip_buses: set[int] | None = None) -> list[CarParams.CarFw]:
|
||||
ecu_rx_addrs: set[EcuAddrBusType], timeout: float = 0.1, num_pandas: int = 1, progress: bool = False) -> list[CarParams.CarFw]:
|
||||
"""Queries for FW versions ordering brands by likelihood, breaks when exact match is found"""
|
||||
|
||||
all_car_fw = []
|
||||
@@ -251,8 +248,7 @@ def get_fw_versions_ordered(can_recv: CanRecvCallable, can_send: CanSendCallable
|
||||
if True not in brand_matches[brand]:
|
||||
continue
|
||||
|
||||
car_fw = get_fw_versions(can_recv, can_send, set_obd_multiplexing, query_brand=brand, timeout=timeout,
|
||||
num_pandas=num_pandas, progress=progress, skip_buses=skip_buses)
|
||||
car_fw = get_fw_versions(can_recv, can_send, set_obd_multiplexing, query_brand=brand, timeout=timeout, num_pandas=num_pandas, progress=progress)
|
||||
all_car_fw.extend(car_fw)
|
||||
|
||||
# If there is a match using this brand's FW alone, finish querying early
|
||||
@@ -264,9 +260,7 @@ def get_fw_versions_ordered(can_recv: CanRecvCallable, can_send: CanSendCallable
|
||||
|
||||
|
||||
def get_fw_versions(can_recv: CanRecvCallable, can_send: CanSendCallable, set_obd_multiplexing: ObdCallback, query_brand: str = None,
|
||||
extra: OfflineFwVersions = None, timeout: float = 0.1, num_pandas: int = 1, progress: bool = False,
|
||||
skip_buses: set[int] | None = None) -> list[CarParams.CarFw]:
|
||||
skip_buses = skip_buses or set()
|
||||
extra: OfflineFwVersions = None, timeout: float = 0.1, num_pandas: int = 1, progress: bool = False) -> list[CarParams.CarFw]:
|
||||
versions = VERSIONS.copy()
|
||||
|
||||
if query_brand is not None:
|
||||
@@ -304,7 +298,7 @@ def get_fw_versions(can_recv: CanRecvCallable, can_send: CanSendCallable, set_ob
|
||||
for addr_chunk in chunks(addr_group):
|
||||
for brand, config, r in requests:
|
||||
# Skip query if no panda available
|
||||
if r.bus > num_pandas * 4 - 1 or r.bus in skip_buses:
|
||||
if r.bus > num_pandas * 4 - 1:
|
||||
continue
|
||||
|
||||
# Toggle OBD multiplexing for each request
|
||||
|
||||
@@ -852,7 +852,6 @@ class CarController(CarControllerBase):
|
||||
CAR.CHEVROLET_VOLT_CC,
|
||||
CAR.CHEVROLET_MALIBU_CC,
|
||||
CAR.CHEVROLET_MALIBU_HYBRID_CC,
|
||||
CAR.BUICK_LACROSSE,
|
||||
}
|
||||
|
||||
if (self.CP.enableGasInterceptorDEPRECATED and self.CP.carFingerprint in CC_REGEN_PADDLE_CAR and
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
import copy
|
||||
import math
|
||||
from datetime import UTC, datetime, timedelta
|
||||
from collections.abc import Mapping
|
||||
from cereal import custom
|
||||
from opendbc.can import CANDefine, CANParser
|
||||
from opendbc.car import Bus, create_button_events, structs
|
||||
@@ -10,10 +7,12 @@ from opendbc.car.common.conversions import Conversions as CV
|
||||
from opendbc.car.gps import get_car_gps_config
|
||||
from opendbc.car.interfaces import CarStateBase
|
||||
from opendbc.car.gm.values import (
|
||||
ALT_ACCS,
|
||||
ASCM_INT,
|
||||
CAMERA_ACC_CAR,
|
||||
CAR,
|
||||
CC_ONLY_CAR,
|
||||
CC_REGEN_PADDLE_CAR,
|
||||
DBC,
|
||||
AccState,
|
||||
CanBus,
|
||||
@@ -38,112 +37,6 @@ BUTTONS_DICT = {CruiseButtons.RES_ACCEL: ButtonType.accelCruise, CruiseButtons.D
|
||||
HARD_BUTTONS_DICT = {CruiseButtons.RES_ACCEL: ButtonType.accelCruise, CruiseButtons.DECEL_SET: ButtonType.decelCruise}
|
||||
NORMAL_CRUISE_BUTTONS = (CruiseButtons.RES_ACCEL, CruiseButtons.DECEL_SET)
|
||||
|
||||
# Optional ~10 Hz CT6 PPS GPS messages on the powertrain bus.
|
||||
PPS_GPS_MESSAGES = (
|
||||
"PPS_ElevHdSpd_FO",
|
||||
"PPS_PosLat_FO",
|
||||
"PPS_PosLong_FO",
|
||||
"PPS_Time_FO",
|
||||
"PPS_QualMetrics_FO",
|
||||
)
|
||||
# PPS_SigAcqTime_FO is omitted: its validity bit stays 1 even during valid fixes.
|
||||
|
||||
|
||||
def pps_checksum_ok(data: bytes) -> bool:
|
||||
"""Validate the 11-bit checksum used by the observed PPS frames."""
|
||||
if len(data) < 2:
|
||||
return False
|
||||
received = ((data[-2] & 0x07) << 8) | data[-1]
|
||||
expected = sum(data[:-2]) + (data[-2] >> 3) + 0x4C
|
||||
return (expected & 0x7FF) == received
|
||||
|
||||
|
||||
def decode_gm_pps_gps(values: Mapping[str, Mapping[str, float]], raw: Mapping[str, bytes],
|
||||
timestamp_nanos: int) -> dict | None:
|
||||
"""Decode one coherent PPS bundle into the existing car-GPS sample shape."""
|
||||
if any(not pps_checksum_ok(raw.get(name, b"")) for name in PPS_GPS_MESSAGES):
|
||||
return None
|
||||
|
||||
try:
|
||||
pos_lat = values["PPS_PosLat_FO"]
|
||||
pos_long = values["PPS_PosLong_FO"]
|
||||
timestamp_values = values["PPS_Time_FO"]
|
||||
quality = values["PPS_QualMetrics_FO"]
|
||||
if (int(pos_lat.get("PPSLatV", 1)) != 0 or
|
||||
int(pos_long.get("PPSLongV", 1)) != 0 or
|
||||
int(quality.get("PPS2DAbsPosErrEstmtV", 1)) != 0 or
|
||||
int(quality.get("PPSMdV", 1)) != 0 or
|
||||
int(quality.get("PPSPstnDilPrcsV", 1)) != 0 or
|
||||
int(timestamp_values.get("PPSTmdayV", 1)) != 0 or
|
||||
int(timestamp_values.get("PPSCldrDayV", 1)) != 0 or
|
||||
int(timestamp_values.get("PPSCldrYrV", 1)) != 0):
|
||||
return None
|
||||
# Reject mode 6 (dead reckoning only without GNSS).
|
||||
if int(quality["PPSMd"]) == 6:
|
||||
return None
|
||||
|
||||
latitude = float(pos_lat["PPSLat"]) / 3_600_000.0
|
||||
longitude = float(pos_long["PPSLong"]) / 3_600_000.0
|
||||
if not (math.isfinite(latitude) and math.isfinite(longitude) and
|
||||
-90.0 <= latitude <= 90.0 and -180.0 <= longitude <= 180.0 and
|
||||
(latitude != 0.0 or longitude != 0.0)):
|
||||
return None
|
||||
|
||||
year = int(timestamp_values["PPSCldrYr"])
|
||||
day_of_year = int(timestamp_values["PPSCldrDay"])
|
||||
millis_of_day = int(timestamp_values["PPSTmday"])
|
||||
if not 2014 <= year <= 2141 or day_of_year < 1 or not 0 <= millis_of_day < 86_400_000:
|
||||
return None
|
||||
timestamp = datetime(year, 1, 1, tzinfo=UTC) + timedelta(days=day_of_year - 1, milliseconds=millis_of_day)
|
||||
if timestamp.year != year:
|
||||
return None
|
||||
|
||||
elev = values["PPS_ElevHdSpd_FO"]
|
||||
speed = float(elev["PPSVel"]) * CV.KPH_TO_MS
|
||||
if int(elev.get("PPSVelV", 1)) != 0 or not math.isfinite(speed) or not 0.0 <= speed <= 200.0:
|
||||
speed = 0.0
|
||||
heading = float(elev["PPSHedng"])
|
||||
if (int(elev.get("PPSHedngV", 1)) != 0 or
|
||||
not math.isfinite(heading) or not 0.0 <= heading < 360.0):
|
||||
heading = 0.0
|
||||
|
||||
altitude = float(elev["PPSElvtn"]) / 100.0
|
||||
if int(elev.get("PPSElvtnV", 1)) != 0 or not math.isfinite(altitude):
|
||||
altitude = 0.0
|
||||
|
||||
horizontal_accuracy = float(quality["PPS2DAbsPosErrEstmt"])
|
||||
if not math.isfinite(horizontal_accuracy) or horizontal_accuracy < 0.0:
|
||||
horizontal_accuracy = 0.0
|
||||
|
||||
vertical_accuracy = float(quality["PPS3DAbsPosErrEstmt"])
|
||||
if int(quality.get("PPS3DAbsPosErrEstmtV", 1)) != 0 or not math.isfinite(vertical_accuracy) or vertical_accuracy < 0.0:
|
||||
vertical_accuracy = 0.0
|
||||
|
||||
bearing_accuracy = float(quality["PPSAbsHdngErrEstmt"])
|
||||
if int(quality.get("PPSAbsHdngErrEstmtV", 1)) != 0 or not math.isfinite(bearing_accuracy) or bearing_accuracy < 0.0:
|
||||
bearing_accuracy = 180.0
|
||||
except (KeyError, TypeError, ValueError, OverflowError, AttributeError):
|
||||
return None
|
||||
|
||||
heading_rad = math.radians(heading)
|
||||
return {
|
||||
"timestamp_nanos": timestamp_nanos,
|
||||
"latitude": latitude,
|
||||
"longitude": longitude,
|
||||
"altitude": altitude,
|
||||
"speed": speed,
|
||||
"bearingDeg": heading,
|
||||
"horizontalAccuracy": horizontal_accuracy,
|
||||
"unixTimestampMillis": round(timestamp.timestamp() * 1000),
|
||||
"verticalAccuracy": vertical_accuracy,
|
||||
"bearingAccuracyDeg": bearing_accuracy,
|
||||
# Velocity error units are undocumented in DBC; omit conversion.
|
||||
"speedAccuracy": 0.0,
|
||||
"hasFix": True,
|
||||
"satelliteCount": 0,
|
||||
"vNED": [speed * math.cos(heading_rad), speed * math.sin(heading_rad), 0.0],
|
||||
}
|
||||
|
||||
|
||||
def get_hard_cruise_buttons(steering_button_msg: dict) -> int:
|
||||
return steering_button_msg.get("ACCButtonsHard", CruiseButtons.INIT)
|
||||
@@ -215,16 +108,9 @@ class CarState(CarStateBase):
|
||||
self.car_gps_config = get_car_gps_config(CP)
|
||||
self.car_gps_supported = self.car_gps_config is not None
|
||||
self.car_gps = None
|
||||
self.onstar_gps = None
|
||||
self._car_gps_timestamp_nanos = 0
|
||||
self._prev_gps_lat = None
|
||||
self._prev_gps_lon = None
|
||||
self._last_gps_bearing = None
|
||||
|
||||
self.pps_gps = None
|
||||
self._pps_gps_timestamp_nanos = 0
|
||||
|
||||
def _update_car_gps(self, cp, v_ego: float = 0.0) -> None:
|
||||
def _update_car_gps(self, cp) -> None:
|
||||
if self.car_gps_config is None:
|
||||
return
|
||||
|
||||
@@ -239,66 +125,12 @@ class CarState(CarStateBase):
|
||||
gps = self.car_gps_config.decoder(*(cp.vl[name] for name in self.car_gps_config.messages))
|
||||
if gps is not None:
|
||||
gps["timestamp_nanos"] = timestamp_nanos
|
||||
if gps["hasFix"]:
|
||||
lat, lon = gps["latitude"], gps["longitude"]
|
||||
if self._prev_gps_lat is not None and (lat, lon) != (self._prev_gps_lat, self._prev_gps_lon):
|
||||
d_lat = (lat - self._prev_gps_lat) * 111139.0
|
||||
d_lon = (lon - self._prev_gps_lon) * 111139.0 * math.cos(math.radians(lat))
|
||||
if math.hypot(d_lat, d_lon) > 1.5 and v_ego > 1.0 and not self.moving_backward:
|
||||
self._last_gps_bearing = math.degrees(math.atan2(d_lon, d_lat)) % 360.0
|
||||
|
||||
self._prev_gps_lat, self._prev_gps_lon = lat, lon
|
||||
|
||||
bearing = self._last_gps_bearing if self._last_gps_bearing is not None else 0.0
|
||||
gps["speed"] = max(0.0, v_ego)
|
||||
gps["bearingDeg"] = bearing
|
||||
gps["bearingAccuracyDeg"] = 5.0 if (v_ego > 1.0 and self._last_gps_bearing is not None) else 180.0
|
||||
heading_rad = math.radians(bearing)
|
||||
gps["vNED"] = [v_ego * math.cos(heading_rad), v_ego * math.sin(heading_rad), 0.0]
|
||||
else:
|
||||
self._prev_gps_lat = self._prev_gps_lon = None
|
||||
|
||||
self.onstar_gps = gps
|
||||
self.car_gps = gps
|
||||
self._car_gps_timestamp_nanos = timestamp_nanos
|
||||
|
||||
def _update_pps_gps(self, cp) -> None:
|
||||
"""Decode a complete, checksum-valid PPS burst when one is available."""
|
||||
timestamps = [max(cp.ts_nanos[name].values(), default=0) for name in PPS_GPS_MESSAGES]
|
||||
if not all(timestamps):
|
||||
return
|
||||
|
||||
timestamp_nanos = max(timestamps)
|
||||
if timestamp_nanos <= self._pps_gps_timestamp_nanos:
|
||||
return
|
||||
if timestamp_nanos - min(timestamps) > 100_000_000:
|
||||
return
|
||||
|
||||
vl = cp.vl
|
||||
try:
|
||||
first_id = int(vl["PPS_ElevHdSpd_FO"]["PPSElvHedngSpdBrstID"])
|
||||
if not (first_id == int(vl["PPS_PosLat_FO"]["PPSLatBrstID"]) ==
|
||||
int(vl["PPS_PosLong_FO"]["PPSLongBrstID"]) ==
|
||||
int(vl["PPS_Time_FO"]["PPSTmBrstID"]) ==
|
||||
int(vl["PPS_QualMetrics_FO"]["PPSPosQltyMtcBrstID"])):
|
||||
return
|
||||
except (KeyError, ValueError, TypeError, OverflowError):
|
||||
return
|
||||
|
||||
values = {name: cp.vl[name] for name in PPS_GPS_MESSAGES}
|
||||
raw = {name: cp.vl_raw[name] for name in PPS_GPS_MESSAGES}
|
||||
self.pps_gps = decode_gm_pps_gps(values, raw, timestamp_nanos)
|
||||
self._pps_gps_timestamp_nanos = timestamp_nanos
|
||||
|
||||
def get_car_gps(self) -> dict | None:
|
||||
def get_car_gps(self):
|
||||
return self.car_gps
|
||||
|
||||
def get_car_gps_sources(self) -> dict[str, dict | None]:
|
||||
return {
|
||||
"pps": self.pps_gps,
|
||||
"onstar": self.onstar_gps,
|
||||
}
|
||||
|
||||
def update_button_enable(self, buttonEvents: list[structs.CarState.ButtonEvent]):
|
||||
if not self.CP.pcmCruise:
|
||||
for b in buttonEvents:
|
||||
@@ -313,6 +145,8 @@ class CarState(CarStateBase):
|
||||
cam_cp = can_parsers[Bus.cam]
|
||||
loopback_cp = can_parsers[Bus.loopback]
|
||||
|
||||
self._update_car_gps(pt_cp)
|
||||
|
||||
ret = structs.CarState()
|
||||
|
||||
volt_like = {
|
||||
@@ -383,11 +217,6 @@ class CarState(CarStateBase):
|
||||
ret.standstill = abs(pt_cp.vl["EBCMWheelSpdRear"]["RLWheelSpd"]) <= STANDSTILL_THRESHOLD and \
|
||||
abs(pt_cp.vl["EBCMWheelSpdRear"]["RRWheelSpd"]) <= STANDSTILL_THRESHOLD
|
||||
|
||||
self._update_car_gps(pt_cp, ret.vEgo)
|
||||
pps_cp = can_parsers.get(Bus.adas)
|
||||
if pps_cp is not None:
|
||||
self._update_pps_gps(pps_cp)
|
||||
|
||||
if pt_cp.vl["ECMPRDNL2"]["ManualMode"] == 1:
|
||||
ret.gearShifter = self.parse_gear_shifter("T")
|
||||
else:
|
||||
@@ -736,16 +565,8 @@ class CarState(CarStateBase):
|
||||
("ASCMLKASteeringCmd", 0),
|
||||
]
|
||||
|
||||
parsers = {
|
||||
return {
|
||||
Bus.pt: CANParser(DBC[CP.carFingerprint][Bus.pt], pt_messages, CanBus.POWERTRAIN),
|
||||
Bus.cam: CANParser(DBC[CP.carFingerprint][Bus.pt], cam_messages, CanBus.CAMERA),
|
||||
Bus.loopback: CANParser(DBC[CP.carFingerprint][Bus.pt], loopback_messages, CanBus.LOOPBACK),
|
||||
}
|
||||
if getattr(CP, "brand", None) == "gm":
|
||||
# Optional CT6 PPS parser on Bus.adas; non-PPS vehicles remain CAN-valid.
|
||||
parsers[Bus.adas] = CANParser(
|
||||
"cadillac_ct6_object",
|
||||
[(name, 0) for name in PPS_GPS_MESSAGES],
|
||||
CanBus.POWERTRAIN,
|
||||
)
|
||||
return parsers
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import pytest
|
||||
import numpy as np
|
||||
from datetime import UTC, datetime
|
||||
from types import SimpleNamespace
|
||||
from parameterized import parameterized
|
||||
|
||||
@@ -9,14 +8,7 @@ from opendbc.can import CANPacker, CANParser
|
||||
from opendbc.car import Bus, DT_CTRL, structs
|
||||
from opendbc.car.car_helpers import interfaces
|
||||
from opendbc.car.gm import gmcan
|
||||
from opendbc.car.gm.carstate import (
|
||||
CarState as GMCarState,
|
||||
PPS_GPS_MESSAGES,
|
||||
decode_gm_pps_gps,
|
||||
get_hard_cruise_buttons,
|
||||
pps_checksum_ok,
|
||||
update_auto_hold_drive_timers,
|
||||
)
|
||||
from opendbc.car.gm.carstate import CarState as GMCarState, get_hard_cruise_buttons, update_auto_hold_drive_timers
|
||||
from opendbc.car.gm.carcontroller import (
|
||||
VisualAlert,
|
||||
get_acc_dashboard_always_one,
|
||||
@@ -96,247 +88,6 @@ class TestBoltGps:
|
||||
assert gps["latitude"] == 0.0
|
||||
assert gps["longitude"] == 0.0
|
||||
|
||||
def test_bolt_gps_accuracy_metrics(self):
|
||||
gps = parse_chevrolet_bolt_can_gps({"GPSLatitude": 145292743.0, "GPSLongitude": -267520892.0})
|
||||
assert gps is not None
|
||||
assert gps["horizontalAccuracy"] == 6.0
|
||||
assert gps["verticalAccuracy"] == 10.0
|
||||
assert gps["speedAccuracy"] == 0.5
|
||||
|
||||
|
||||
class TestPpsGps:
|
||||
_frames = [
|
||||
(0x260, bytes.fromhex("10ddac000d831277"), 0),
|
||||
(0x261, bytes.fromhex("08386fce09ca"), 0),
|
||||
(0x262, bytes.fromhex("6d98820341de"), 0),
|
||||
(0x264, bytes.fromhex("0018f90578b5eaac"), 0),
|
||||
(0x265, bytes.fromhex("1a0000800a0258fd"), 0),
|
||||
]
|
||||
|
||||
def test_observed_bundle_checksum_and_conversion(self):
|
||||
parser = CANParser("cadillac_ct6_object", [(name, 0) for name in PPS_GPS_MESSAGES], 0)
|
||||
parser.update([(1_000_000_000, self._frames)])
|
||||
|
||||
assert all(pps_checksum_ok(parser.vl_raw[name]) for name in PPS_GPS_MESSAGES)
|
||||
gps = decode_gm_pps_gps(
|
||||
{name: parser.vl[name] for name in PPS_GPS_MESSAGES},
|
||||
{name: parser.vl_raw[name] for name in PPS_GPS_MESSAGES},
|
||||
1_000_000_000,
|
||||
)
|
||||
assert gps is not None
|
||||
assert gps["hasFix"]
|
||||
assert gps["latitude"] == pytest.approx(38.3101, abs=1e-4)
|
||||
assert gps["longitude"] == pytest.approx(-85.7701, abs=1e-4)
|
||||
assert gps["altitude"] == pytest.approx(106.9)
|
||||
assert gps["bearingDeg"] == pytest.approx(56.748)
|
||||
assert gps["horizontalAccuracy"] == pytest.approx(1.0)
|
||||
assert gps["unixTimestampMillis"] == 1788655668655
|
||||
|
||||
@pytest.fixture
|
||||
def bundle(self):
|
||||
parser = CANParser("cadillac_ct6_object", [(name, 0) for name in PPS_GPS_MESSAGES], 0)
|
||||
parser.update([(1_000_000_000, self._frames)])
|
||||
return ({name: dict(parser.vl[name]) for name in PPS_GPS_MESSAGES},
|
||||
{name: parser.vl_raw[name] for name in PPS_GPS_MESSAGES})
|
||||
|
||||
@pytest.mark.parametrize("year,day,date", [
|
||||
(2025, 1, "2025-01-01"), (2025, 365, "2025-12-31"), (2025, 366, None),
|
||||
(2024, 366, "2024-12-31"), (2024, 367, None), (2025, 0, None),
|
||||
])
|
||||
def test_one_based_day_of_year(self, bundle, year, day, date):
|
||||
values, raw = bundle
|
||||
values["PPS_Time_FO"].update(PPSCldrYr=year, PPSCldrDay=day, PPSTmday=1234)
|
||||
gps = decode_gm_pps_gps(values, raw, 1_000_000_000)
|
||||
if date is None:
|
||||
assert gps is None
|
||||
else:
|
||||
expected = int(datetime.fromisoformat(date).replace(tzinfo=UTC).timestamp() * 1000) + 1234
|
||||
assert gps["unixTimestampMillis"] == expected
|
||||
|
||||
@pytest.mark.parametrize("bad_data", [b"", b"\x01"])
|
||||
def test_checksum_short_input(self, bad_data):
|
||||
assert not pps_checksum_ok(bad_data)
|
||||
|
||||
@pytest.mark.parametrize("lat,lon,valid", [(0.0, 10.0 * 3_600_000, True), (10.0 * 3_600_000, 0.0, True), (0.0, 0.0, False)])
|
||||
def test_coordinate_axes(self, bundle, lat, lon, valid):
|
||||
values, raw = bundle
|
||||
values["PPS_PosLat_FO"]["PPSLat"] = lat
|
||||
values["PPS_PosLong_FO"]["PPSLong"] = lon
|
||||
gps = decode_gm_pps_gps(values, raw, 1_000_000_000)
|
||||
if valid:
|
||||
assert gps is not None
|
||||
assert gps["hasFix"]
|
||||
else:
|
||||
assert gps is None
|
||||
|
||||
def test_get_car_gps_sources_shape(self):
|
||||
cs = GMCarState.__new__(GMCarState)
|
||||
cs.pps_gps = {"hasFix": True}
|
||||
cs.onstar_gps = None
|
||||
sources = cs.get_car_gps_sources()
|
||||
assert sources == {"pps": {"hasFix": True}, "onstar": None}
|
||||
|
||||
@pytest.mark.parametrize("message,signal,value", [
|
||||
("PPS_PosLat_FO", "PPSLatV", 1),
|
||||
("PPS_PosLong_FO", "PPSLongV", 1),
|
||||
("PPS_QualMetrics_FO", "PPS2DAbsPosErrEstmtV", 1),
|
||||
("PPS_PosLat_FO", "PPSLat", float("nan")),
|
||||
("PPS_PosLong_FO", "PPSLong", 181 * 3_600_000),
|
||||
("PPS_QualMetrics_FO", "PPSMd", 6),
|
||||
("PPS_Time_FO", "PPSTmdayV", 1),
|
||||
])
|
||||
def test_unusable_position_rejected(self, bundle, message, signal, value):
|
||||
values, raw = bundle
|
||||
values[message][signal] = value
|
||||
assert decode_gm_pps_gps(values, raw, 1_000_000_000) is None
|
||||
|
||||
@pytest.mark.parametrize("invalidity", ["checksum", "position-validity"])
|
||||
def test_burst_cache_and_explicit_invalidation(self, invalidity):
|
||||
parser = CANParser("cadillac_ct6_object", [(name, 0) for name in PPS_GPS_MESSAGES], 0)
|
||||
cs = GMCarState.__new__(GMCarState)
|
||||
cs.pps_gps = None
|
||||
cs._pps_gps_timestamp_nanos = 0
|
||||
parser.update([(1_000_000_000, self._frames[:-1])])
|
||||
cs._update_pps_gps(parser)
|
||||
assert cs.pps_gps is None # Incomplete startup burst.
|
||||
parser.update([(1_000_000_000, self._frames[-1:])])
|
||||
cs._update_pps_gps(parser)
|
||||
good = cs.pps_gps
|
||||
assert good is not None
|
||||
# No complete new burst: keep its original timestamp for freshness.
|
||||
parser.update([(2_000_000_000, self._frames[:1])])
|
||||
cs._update_pps_gps(parser)
|
||||
assert cs.pps_gps is good
|
||||
parser.update([(2_100_000_000, self._frames)])
|
||||
parser.vl["PPS_PosLat_FO"]["PPSLatBrstID"] = int(parser.vl["PPS_PosLat_FO"]["PPSLatBrstID"]) ^ 1
|
||||
cs._update_pps_gps(parser)
|
||||
assert cs.pps_gps is good # A mismatched burst must not refresh the fix.
|
||||
bad_frames = ([(addr, data[:-1] + bytes([data[-1] ^ 1]), bus) for addr, data, bus in self._frames]
|
||||
if invalidity == "checksum" else self._frames)
|
||||
parser.update([(3_000_000_000, bad_frames)])
|
||||
if invalidity == "position-validity":
|
||||
parser.vl["PPS_PosLat_FO"]["PPSLatV"] = 1
|
||||
cs._update_pps_gps(parser)
|
||||
assert cs.pps_gps is None
|
||||
parser.update([(4_000_000_000, self._frames)])
|
||||
cs._update_pps_gps(parser)
|
||||
assert cs.pps_gps is not None
|
||||
|
||||
@pytest.mark.parametrize("speed_bad,heading_bad,elevation_bad,vertical_bad,bearing_bad", [
|
||||
(0, 0, 0, 0, 0), (1, 0, 0, 0, 0), (0, 1, 0, 0, 0), (0, 0, 1, 0, 0),
|
||||
(0, 0, 0, 1, 0), (0, 0, 0, 0, 1), (1, 1, 1, 1, 1),
|
||||
], ids=["valid", "speed", "heading", "elevation", "vertical-accuracy", "bearing-accuracy", "all-invalid"])
|
||||
def test_optional_field_fallbacks(self, bundle, speed_bad, heading_bad, elevation_bad, vertical_bad, bearing_bad):
|
||||
values, raw = bundle
|
||||
values["PPS_ElevHdSpd_FO"].update(PPSVel=36, PPSVelV=speed_bad, PPSHedng=90, PPSHedngV=heading_bad,
|
||||
PPSElvtn=12345, PPSElvtnV=elevation_bad)
|
||||
values["PPS_QualMetrics_FO"].update(PPS2DAbsPosErrEstmt=3.2, PPS3DAbsPosErrEstmt=4.5, PPS3DAbsPosErrEstmtV=vertical_bad,
|
||||
PPSAbsHdngErrEstmt=6.0, PPSAbsHdngErrEstmtV=bearing_bad)
|
||||
gps = decode_gm_pps_gps(values, raw, 1_000_000_000)
|
||||
assert gps is not None
|
||||
assert gps["hasFix"]
|
||||
assert gps["speed"] == pytest.approx(0.0 if speed_bad else 10.0)
|
||||
assert gps["bearingDeg"] == (0 if heading_bad else 90)
|
||||
assert gps["altitude"] == pytest.approx(0.0 if elevation_bad else 123.45)
|
||||
assert gps["vNED"] == pytest.approx([gps["speed"], 0, 0] if heading_bad else [0, gps["speed"], 0])
|
||||
assert gps["horizontalAccuracy"] == pytest.approx(3.2)
|
||||
assert gps["verticalAccuracy"] == pytest.approx(0.0 if vertical_bad else 4.5)
|
||||
assert gps["bearingAccuracyDeg"] == pytest.approx(180.0 if bearing_bad else 6.0)
|
||||
|
||||
def test_bolt_gps_heading_and_speed_derivation(self):
|
||||
cp = SimpleNamespace(
|
||||
brand="gm",
|
||||
carFingerprint=CAR.CHEVROLET_BOLT_CC_2018_2021,
|
||||
flags=0,
|
||||
networkLocation=structs.CarParams.NetworkLocation.gateway,
|
||||
transmissionType=structs.CarParams.TransmissionType.direct,
|
||||
enableBsm=False,
|
||||
enableGasInterceptorDEPRECATED=False,
|
||||
pcmCruise=False,
|
||||
)
|
||||
fpcp = custom.StarPilotCarParams.new_message()
|
||||
cs = GMCarState(cp, fpcp)
|
||||
|
||||
# First position (stationary)
|
||||
mock_cp = SimpleNamespace(
|
||||
ts_nanos={"TCICOnStarGPSPosition": {"GPSLatitude": 1_000_000}},
|
||||
vl={"TCICOnStarGPSPosition": {"GPSLatitude": 145292743.0, "GPSLongitude": -267520892.0}},
|
||||
)
|
||||
cs._update_car_gps(mock_cp, v_ego=0.0)
|
||||
gps = cs.get_car_gps()
|
||||
assert gps is not None
|
||||
assert gps["speed"] == 0.0
|
||||
assert gps["bearingDeg"] == 0.0
|
||||
assert gps["bearingAccuracyDeg"] == 180.0
|
||||
|
||||
# Move East at 15 m/s
|
||||
mock_cp.ts_nanos["TCICOnStarGPSPosition"]["GPSLatitude"] = 2_000_000_000
|
||||
mock_cp.vl["TCICOnStarGPSPosition"]["GPSLongitude"] = -267520892.0 + 1000.0 # Eastward shift
|
||||
cs._update_car_gps(mock_cp, v_ego=15.0)
|
||||
gps = cs.get_car_gps()
|
||||
assert gps is not None
|
||||
assert gps["speed"] == 15.0
|
||||
assert gps["bearingDeg"] == pytest.approx(90.0, abs=1.0)
|
||||
assert gps["bearingAccuracyDeg"] == 5.0
|
||||
assert gps["vNED"][1] > 0.0 # East velocity positive
|
||||
|
||||
# Stop moving (v_ego=0.0): heading should be retained, not reset to 0
|
||||
mock_cp.ts_nanos["TCICOnStarGPSPosition"]["GPSLatitude"] = 3_000_000_000
|
||||
cs._update_car_gps(mock_cp, v_ego=0.0)
|
||||
gps = cs.get_car_gps()
|
||||
assert gps is not None
|
||||
assert gps["speed"] == 0.0
|
||||
assert gps["bearingDeg"] == pytest.approx(90.0, abs=1.0)
|
||||
|
||||
# Reversing: coordinate changes while moving backward should not flip heading
|
||||
cs.moving_backward = True
|
||||
mock_cp.ts_nanos["TCICOnStarGPSPosition"]["GPSLatitude"] = 4_000_000_000
|
||||
mock_cp.vl["TCICOnStarGPSPosition"]["GPSLongitude"] = -267520892.0 - 1000.0 # Westward shift
|
||||
cs._update_car_gps(mock_cp, v_ego=3.0)
|
||||
gps = cs.get_car_gps()
|
||||
assert gps is not None
|
||||
assert gps["bearingDeg"] == pytest.approx(90.0, abs=1.0)
|
||||
|
||||
# Drive True North: verify bearing is 0.0 deg and accuracy is 5.0 deg (not degraded to 180.0)
|
||||
cs.moving_backward = False
|
||||
mock_cp.ts_nanos["TCICOnStarGPSPosition"]["GPSLatitude"] = 5_000_000_000
|
||||
mock_cp.vl["TCICOnStarGPSPosition"]["GPSLatitude"] = 145292743.0 + 1000.0 # Northward shift
|
||||
cs._update_car_gps(mock_cp, v_ego=12.0)
|
||||
gps = cs.get_car_gps()
|
||||
assert gps is not None
|
||||
assert gps["bearingDeg"] == pytest.approx(0.0, abs=1.0)
|
||||
assert gps["bearingAccuracyDeg"] == 5.0
|
||||
|
||||
# Tunnel / fix loss: invalid coordinates cause hasFix=False and clear previous coordinates
|
||||
mock_cp.ts_nanos["TCICOnStarGPSPosition"]["GPSLatitude"] = 6_000_000_000
|
||||
mock_cp.vl["TCICOnStarGPSPosition"]["GPSLatitude"] = 0.0
|
||||
mock_cp.vl["TCICOnStarGPSPosition"]["GPSLongitude"] = 0.0
|
||||
cs._update_car_gps(mock_cp, v_ego=20.0)
|
||||
gps = cs.get_car_gps()
|
||||
assert gps is not None
|
||||
assert not gps["hasFix"]
|
||||
assert cs._prev_gps_lat is None and cs._prev_gps_lon is None
|
||||
|
||||
# Tunnel exit: GPS fix re-acquired 5 km away heading South
|
||||
# The first sample after fix loss sets initial coordinates without calculating a phantom jump vector
|
||||
mock_cp.ts_nanos["TCICOnStarGPSPosition"]["GPSLatitude"] = 7_000_000_000
|
||||
mock_cp.vl["TCICOnStarGPSPosition"]["GPSLatitude"] = 145292743.0 - 50000.0
|
||||
mock_cp.vl["TCICOnStarGPSPosition"]["GPSLongitude"] = -267520892.0
|
||||
cs._update_car_gps(mock_cp, v_ego=20.0)
|
||||
gps = cs.get_car_gps()
|
||||
assert gps is not None
|
||||
assert gps["hasFix"]
|
||||
assert gps["bearingDeg"] == pytest.approx(0.0, abs=1.0)
|
||||
assert cs._prev_gps_lat is not None
|
||||
|
||||
# Second sample: moving Southward -> bearing smoothly updates to 180 deg
|
||||
mock_cp.ts_nanos["TCICOnStarGPSPosition"]["GPSLatitude"] = 8_000_000_000
|
||||
mock_cp.vl["TCICOnStarGPSPosition"]["GPSLatitude"] = 145292743.0 - 51000.0
|
||||
cs._update_car_gps(mock_cp, v_ego=20.0)
|
||||
gps = cs.get_car_gps()
|
||||
assert gps is not None
|
||||
assert gps["bearingDeg"] == pytest.approx(180.0, abs=1.0)
|
||||
|
||||
@parameterized.expand(CHEVROLET_BOLT_GPS_CARS)
|
||||
def test_gps_message_is_added_to_powertrain_parser(self, car_model):
|
||||
cp = SimpleNamespace(
|
||||
|
||||
@@ -6,10 +6,8 @@ from collections.abc import Callable, Mapping
|
||||
from typing import Any
|
||||
|
||||
from opendbc.car.common.conversions import Conversions as CV
|
||||
from opendbc.can.dbc import DBC as DBC_FILE
|
||||
from opendbc.car import Bus
|
||||
from opendbc.car.ford.values import CAR as FORD_CAR
|
||||
from opendbc.car.gm.values import CAR as GM_CAR, DBC as GM_DBC
|
||||
from opendbc.car.gm.values import CAR as GM_CAR
|
||||
|
||||
|
||||
CarGpsSample = dict[str, Any]
|
||||
@@ -115,11 +113,11 @@ def parse_chevrolet_bolt_can_gps(position: Mapping[str, float]) -> CarGpsSample
|
||||
"altitude": 0.0,
|
||||
"speed": 0.0,
|
||||
"bearingDeg": 0.0,
|
||||
"horizontalAccuracy": 6.0,
|
||||
"horizontalAccuracy": 100.0,
|
||||
"unixTimestampMillis": int(datetime.now(UTC).timestamp() * 1000),
|
||||
"verticalAccuracy": 10.0,
|
||||
"verticalAccuracy": 100.0,
|
||||
"bearingAccuracyDeg": 180.0,
|
||||
"speedAccuracy": 0.5,
|
||||
"speedAccuracy": 100.0,
|
||||
"hasFix": coordinates_valid,
|
||||
"satelliteCount": 0,
|
||||
"vNED": [0.0, 0.0, 0.0],
|
||||
@@ -160,25 +158,8 @@ CAR_GPS_CONFIGS: dict[str, CarGpsConfig] = {
|
||||
|
||||
|
||||
def get_car_gps_config(CP) -> CarGpsConfig | None:
|
||||
cp_brand = getattr(CP, "brand", None)
|
||||
config = CAR_GPS_CONFIGS.get(CP.carFingerprint)
|
||||
if config is not None and config.brand == cp_brand:
|
||||
return config
|
||||
|
||||
# Enable OnStar GPS for GM cars whose powertrain DBC defines it.
|
||||
if cp_brand == "gm":
|
||||
try:
|
||||
dbc_name = GM_DBC[CP.carFingerprint][Bus.pt]
|
||||
if "TCICOnStarGPSPosition" in DBC_FILE(dbc_name).name_to_msg:
|
||||
return CarGpsConfig(
|
||||
brand="gm",
|
||||
messages=CHEVROLET_BOLT_GPS_MESSAGES,
|
||||
decoder=parse_chevrolet_bolt_can_gps,
|
||||
)
|
||||
except (KeyError, OSError, TypeError, RuntimeError):
|
||||
pass
|
||||
|
||||
return None
|
||||
return config if config is not None and config.brand == CP.brand else None
|
||||
|
||||
|
||||
def car_gps_available(CP) -> bool:
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
from dataclasses import dataclass
|
||||
|
||||
# Provenance: portions of HKG angle control are adapted from sunnypilot/opendbc's
|
||||
# hkg-angle-steering-2025 branch at cc4b08625. See CREDITS.md and THIRD_PARTY_NOTICES.md.
|
||||
import numpy as np
|
||||
from opendbc.can import CANPacker
|
||||
from opendbc.car import Bus, DT_CTRL, make_tester_present_msg, rate_limit, structs
|
||||
@@ -10,7 +8,7 @@ from opendbc.car.lateral import apply_driver_steer_torque_limits, apply_steer_an
|
||||
from opendbc.car.common.conversions import Conversions as CV
|
||||
from opendbc.car.hyundai import hyundaicanfd, hyundaican
|
||||
from opendbc.car.hyundai.hyundaicanfd import CanBus
|
||||
from opendbc.car.hyundai.values import HyundaiFlags, HyundaiSafetyFlags, HyundaiStarPilotFlags, Buttons, CarControllerParams, CAR, CANFD_ANGLE_LONGITUDINAL_CAR, \
|
||||
from opendbc.car.hyundai.values import HyundaiFlags, HyundaiStarPilotFlags, Buttons, CarControllerParams, CAR, CANFD_ANGLE_LONGITUDINAL_CAR, \
|
||||
CANFD_RADAR_LIVE_LONGITUDINAL_CAR, CANFD_ALT_BUTTONS_RESUME_CAR, kia_ev6_gt_line_longitudinal_tuning, \
|
||||
KIA_EV6_GT_LINE_LONG_TUNING_TESTING_GROUND_ID
|
||||
from opendbc.car.interfaces import CarControllerBase
|
||||
@@ -477,11 +475,6 @@ class CarController(CarControllerBase):
|
||||
self._dash_lat_disengage_init = False
|
||||
self._dash_prev_lat_active = False
|
||||
self._ray_lkas11_active = False
|
||||
self._ray_lfa_8byte = CP.carFingerprint == CAR.KIA_RAY_EV and bool(
|
||||
getattr(CP, "safetyConfigs", None) and
|
||||
CP.safetyConfigs[-1].safetyParam & HyundaiSafetyFlags.CAN_REFRESH_MSGS
|
||||
)
|
||||
self._ray_lfa_packer = CANPacker("hyundai_kia_ray_lfa") if self._ray_lfa_8byte else None
|
||||
|
||||
def _update_dash_icon_state(self, CC):
|
||||
if CC.latActive:
|
||||
@@ -755,7 +748,6 @@ class CarController(CarControllerBase):
|
||||
can_sends = []
|
||||
can_canfd_blended = bool(self.CP.flags & HyundaiFlags.CAN_CANFD_BLENDED)
|
||||
blended_hda2 = can_canfd_blended and bool(self.CP.flags & HyundaiFlags.CANFD_LKA_STEERING)
|
||||
longitudinal_active = bool(self.long_active_ecu and getattr(CC, "longActive", False))
|
||||
|
||||
# HUD messages
|
||||
sys_warning, sys_state, left_lane_warning, right_lane_warning = process_hud_alert(CC.enabled, self.car_fingerprint,
|
||||
@@ -764,7 +756,6 @@ class CarController(CarControllerBase):
|
||||
if blended_hda2:
|
||||
can_sends.extend(hyundaicanfd.create_steering_messages(
|
||||
self.packer, self.CP, self.CAN, CC.enabled, apply_steer_req, apply_torque, 0.0,
|
||||
longitudinal_active=longitudinal_active,
|
||||
))
|
||||
if self.long_active_ecu:
|
||||
can_sends.extend(hyundaican.create_lkas11_can_canfd_blended(
|
||||
@@ -839,10 +830,7 @@ class CarController(CarControllerBase):
|
||||
|
||||
# 20 Hz LFA MFA message
|
||||
if self.frame % 5 == 0 and (self.CP.flags & HyundaiFlags.SEND_LFA.value or (self.long_active_ecu and blended_hda2)):
|
||||
if self._ray_lfa_8byte:
|
||||
can_sends.append(hyundaican.create_ray_lfahda_mfc(self._ray_lfa_packer, CC.latActive, lfa_icon))
|
||||
else:
|
||||
can_sends.append(hyundaican.create_lfahda_mfc(self.packer, CC.enabled, self.frame, self.CP, lfa_icon))
|
||||
can_sends.append(hyundaican.create_lfahda_mfc(self.packer, CC.enabled, self.frame, self.CP, lfa_icon))
|
||||
|
||||
# 5 Hz ACC options
|
||||
if self.frame % 20 == 0 and self.long_active_ecu and not can_canfd_blended:
|
||||
@@ -859,9 +847,7 @@ class CarController(CarControllerBase):
|
||||
can_sends = []
|
||||
|
||||
lka_steering = self.CP.flags & HyundaiFlags.CANFD_LKA_STEERING
|
||||
longitudinal_active = bool(self.long_active_ecu and getattr(CC, "longActive", False))
|
||||
lfa_longitudinal_active = longitudinal_active if self.CP.carFingerprint == CAR.GENESIS_GV70_ELECTRIFIED_1ST_GEN else self.CP.openpilotLongitudinalControl
|
||||
lka_steering_long = lka_steering and lfa_longitudinal_active
|
||||
lka_steering_long = lka_steering and self.long_active_ecu
|
||||
ccnc_non_hda2 = self.CP.flags & HyundaiFlags.CCNC and not lka_steering
|
||||
use_egmp_dynamic_long_tuning = egmp_dynamic_longitudinal_tuning(self.CP) and self.long_active_ecu and \
|
||||
CC.actuators.longControlState in (LongCtrlState.starting, LongCtrlState.pid, LongCtrlState.stopping)
|
||||
@@ -890,7 +876,7 @@ class CarController(CarControllerBase):
|
||||
if angle_lkas_alt:
|
||||
steering_msg_active = bool(steering_msg_active and drive_gear)
|
||||
angle_lkas_alt_standstill_handoff = bool(getattr(CS.out, "standstill", False) and not CC.latActive)
|
||||
forward_stock_lkas = self.CP.carFingerprint in CANFD_ANGLE_LONGITUDINAL_CAR and angle_lkas_alt and (
|
||||
forward_stock_lkas = angle_lkas_alt and (
|
||||
angle_lkas_alt_standstill_handoff or not (drive_gear and (CC.latActive or CC.enabled))
|
||||
)
|
||||
preserve_stock_lfa_status = preserve_stock_canfd_lfa_status(self.CP.carFingerprint)
|
||||
@@ -899,8 +885,7 @@ class CarController(CarControllerBase):
|
||||
steering_msg_active, apply_torque, apply_angle,
|
||||
CS.stock_lfa_msg if preserve_stock_lfa_status else None,
|
||||
CS.stock_lkas_msg if preserve_stock_lkas else None,
|
||||
lka_icon=lka_icon,
|
||||
longitudinal_active=lfa_longitudinal_active))
|
||||
lka_icon=lka_icon))
|
||||
direct_steering_active = ccnc_angle_long and drive_gear and CC.latActive and self.direct_angle_request_allowed and not CS.angle_steering_fault
|
||||
inactive_steering_angle = float(np.clip(CS.angle_steering_angle,
|
||||
-self.params.ANGLE_LIMITS.STEER_ANGLE_MAX,
|
||||
|
||||
@@ -2,8 +2,6 @@ from collections import deque
|
||||
import copy
|
||||
import math
|
||||
|
||||
# Provenance: portions of HKG angle-state integration are adapted from sunnypilot/opendbc's
|
||||
# hkg-angle-steering-2025 branch at cc4b08625. See CREDITS.md and THIRD_PARTY_NOTICES.md.
|
||||
from cereal import custom
|
||||
from opendbc.can import CANDefine, CANParser
|
||||
from opendbc.car import Bus, create_button_events, structs
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
""" AUTO-FORMATTED USING opendbc/car/debug/format_fingerprints.py, EDIT STRUCTURE THERE."""
|
||||
# Provenance: portions of HKG firmware data are adapted from sunnypilot/opendbc master at
|
||||
# f95f996f5 and its hkg-angle-steering-2025 branch at cc4b08625. See CREDITS.md.
|
||||
from opendbc.car.structs import CarParams
|
||||
from opendbc.car.hyundai.values import CAR
|
||||
|
||||
|
||||
@@ -203,17 +203,6 @@ def create_lfahda_mfc(packer, enabled, frame=None, CP=None, lfa_icon=None):
|
||||
return packer.make_can_msg("LFAHDA_MFC", bus, values)
|
||||
|
||||
|
||||
def create_ray_lfahda_mfc(packer, lat_active, lfa_icon):
|
||||
values = {
|
||||
"HDA_USM": 2,
|
||||
"HDA_Icon_State": 2 if lfa_icon else 0,
|
||||
"HDA_VSetReq": 0,
|
||||
"HDA_Icon_Wheel": int(lat_active),
|
||||
"LFA_Icon_State": lfa_icon,
|
||||
}
|
||||
return packer.make_can_msg("LFAHDA_MFC", 0, values)
|
||||
|
||||
|
||||
def create_acc_commands_can_canfd_blended(packer, enabled, accel, upper_jerk, idx, hud_control, set_speed,
|
||||
stopping, long_override, use_fca, CP):
|
||||
commands = []
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
import copy
|
||||
# Provenance: portions of HKG angle-command construction are adapted from sunnypilot/opendbc's
|
||||
# hkg-angle-steering-2025 branch at cc4b08625. See CREDITS.md and THIRD_PARTY_NOTICES.md.
|
||||
import numpy as np
|
||||
from opendbc.car import CanBusBase, CanData
|
||||
from opendbc.car.common.conversions import Conversions as CV
|
||||
@@ -99,12 +97,9 @@ def create_angle_adas_cmd(packer, CAN, apply_angle: float, lat_active: bool, tor
|
||||
|
||||
|
||||
def create_steering_messages(packer, CP, CAN, enabled, lat_active, apply_torque, apply_angle,
|
||||
lfa_base_values=None, lkas_base_values=None, lka_icon=None,
|
||||
longitudinal_active=None):
|
||||
lfa_base_values=None, lkas_base_values=None, lka_icon=None):
|
||||
if lka_icon is None:
|
||||
lka_icon = 2 if enabled else 1
|
||||
if longitudinal_active is None:
|
||||
longitudinal_active = CP.openpilotLongitudinalControl
|
||||
|
||||
angle_lkas_alt = CP.flags & HyundaiFlags.CANFD_ANGLE_STEERING and CP.flags & HyundaiFlags.CANFD_LKA_STEERING_ALT
|
||||
|
||||
@@ -198,7 +193,7 @@ def create_steering_messages(packer, CP, CAN, enabled, lat_active, apply_torque,
|
||||
ret = []
|
||||
if CP.flags & HyundaiFlags.CANFD_LKA_STEERING:
|
||||
lkas_msg = "LKAS_ALT" if CP.flags & HyundaiFlags.CANFD_LKA_STEERING_ALT else "LKAS"
|
||||
if longitudinal_active and not CP.flags & HyundaiFlags.CAN_CANFD_BLENDED:
|
||||
if CP.openpilotLongitudinalControl and not CP.flags & HyundaiFlags.CAN_CANFD_BLENDED:
|
||||
ret.append(packer.make_can_msg("LFA", CAN.ECAN, lfa_values))
|
||||
ret.append(packer.make_can_msg(lkas_msg, CAN.ACAN, lkas_values))
|
||||
else:
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
import time
|
||||
# Provenance: portions of HKG angle integration are adapted from sunnypilot/opendbc's
|
||||
# hkg-angle-steering-2025 branch at cc4b08625. See CREDITS.md and THIRD_PARTY_NOTICES.md.
|
||||
from opendbc.car import get_safety_config, structs, uds
|
||||
from opendbc.car.hyundai.hyundaicanfd import CanBus
|
||||
from opendbc.car.hyundai.values import HyundaiFlags, CAR, CarControllerParams, \
|
||||
@@ -211,7 +209,9 @@ class CarInterface(CarInterfaceBase):
|
||||
ret.enableBsm = 0x58b in fingerprint[CAN.ECAN]
|
||||
|
||||
# Send LFA message on cars with HDA
|
||||
if 0x485 in fingerprint[CAN.CAM]:
|
||||
if 0x485 in fingerprint[CAN.CAM] and (
|
||||
candidate != CAR.KIA_RAY_EV or fingerprint[CAN.CAM][0x485] == 4
|
||||
):
|
||||
ret.flags |= HyundaiFlags.SEND_LFA.value
|
||||
|
||||
# These cars use the FCA11 message for the AEB and FCW signals, all others use SCC12
|
||||
@@ -226,9 +226,6 @@ class CarInterface(CarInterfaceBase):
|
||||
else:
|
||||
ret.safetyConfigs = [get_safety_config(structs.CarParams.SafetyModel.hyundai, 0)]
|
||||
|
||||
if candidate == CAR.KIA_RAY_EV and fingerprint[CAN.CAM].get(0x485) == 8:
|
||||
ret.safetyConfigs[-1].safetyParam |= HyundaiSafetyFlags.CAN_REFRESH_MSGS.value
|
||||
|
||||
if ret.flags & HyundaiFlags.CAMERA_SCC:
|
||||
ret.safetyConfigs[0].safetyParam |= HyundaiSafetyFlags.CAMERA_SCC.value
|
||||
if candidate in (CAR.HYUNDAI_ELANTRA_2024, CAR.HYUNDAI_ELANTRA_HEV_2024):
|
||||
|
||||
@@ -901,21 +901,7 @@ class TestHyundaiFingerprint:
|
||||
fingerprint[2][0x485] = 8
|
||||
CP = CarInterface.get_params(CAR.KIA_RAY_EV, fingerprint, [], False, False, False, None)
|
||||
|
||||
assert CP.flags & HyundaiFlags.SEND_LFA
|
||||
assert CP.safetyConfigs[-1].safetyParam & HyundaiSafetyFlags.CAN_REFRESH_MSGS
|
||||
|
||||
def test_ray_ev_uses_carrot_eight_byte_lfa_frame(self):
|
||||
fingerprint = gen_empty_fingerprint()
|
||||
fingerprint[2][0x485] = 8
|
||||
CP = CarInterface.get_params(CAR.KIA_RAY_EV, fingerprint, [], False, False, False, None)
|
||||
controller = CarController(DBC[CP.carFingerprint], CP)
|
||||
|
||||
msg = hyundaican.create_ray_lfahda_mfc(controller._ray_lfa_packer, True, 2)
|
||||
assert msg[0] == 0x485
|
||||
assert len(msg[1]) == 8
|
||||
assert msg[1][0] & 0x03 == 2
|
||||
assert msg[1][2] & 0x10 == 0x10
|
||||
assert msg[1][3] & 0x03 == 2
|
||||
assert not (CP.flags & HyundaiFlags.SEND_LFA)
|
||||
|
||||
def test_non_ray_legacy_platform_keeps_53e_lkas12_detection(self):
|
||||
fingerprint = gen_empty_fingerprint()
|
||||
@@ -1052,12 +1038,9 @@ class TestHyundaiFingerprint:
|
||||
ret = update(0, 3)
|
||||
assert any(be.type == ButtonType.lkas and not be.pressed for be in ret.buttonEvents)
|
||||
|
||||
ret = update(1, 4)
|
||||
ret = update(2, 4)
|
||||
assert any(be.type == ButtonType.lkas and be.pressed for be in ret.buttonEvents)
|
||||
|
||||
raw_button_msg = packer.make_can_msg("BCM_PO_11", 0, {"RAY_LKAS_BTN": 1})
|
||||
assert raw_button_msg[1][0] == 0x10
|
||||
|
||||
def test_non_ray_does_not_use_ray_lkas_signal(self):
|
||||
CP = CarInterface.get_params(CAR.KIA_FORTE_2021_NON_SCC, gen_empty_fingerprint(), [], False, False, False, None)
|
||||
car_state = CarState(CP, CarInterface.get_starpilot_params(CAR.KIA_FORTE_2021_NON_SCC,
|
||||
@@ -2537,45 +2520,6 @@ class TestHyundaiFingerprint:
|
||||
assert lfa_parser.can_valid
|
||||
assert lfa_parser.vl["LFA"]["DAMP_FACTOR"] == 100
|
||||
|
||||
controller.long_active_ecu = True
|
||||
cc.longActive = False
|
||||
inactive_msgs = controller.create_canfd_msgs(0, True, 0.44, 0.0, 0.0, 0.0, False,
|
||||
cc.hudControl, cs, cc, get_test_toggles(), lka_icon=2, lfa_icon=2)
|
||||
steering_names = [(controller.packer.dbc.addr_to_msg[addr].name, bus) for addr, _, bus in inactive_msgs
|
||||
if controller.packer.dbc.addr_to_msg[addr].name in ("LFA", "LKAS")]
|
||||
assert steering_names == [("LKAS", can_bus.ACAN)]
|
||||
|
||||
controller.frame = 1
|
||||
cc.longActive = True
|
||||
active_msgs = controller.create_canfd_msgs(0, True, 0.44, 0.0, 0.0, 0.0, False,
|
||||
cc.hudControl, cs, cc, get_test_toggles(), lka_icon=2, lfa_icon=2)
|
||||
steering_names = [(controller.packer.dbc.addr_to_msg[addr].name, bus) for addr, _, bus in active_msgs
|
||||
if controller.packer.dbc.addr_to_msg[addr].name in ("LFA", "LKAS")]
|
||||
assert steering_names == [("LFA", can_bus.ECAN), ("LKAS", can_bus.ACAN)]
|
||||
|
||||
def test_ioniq_6_keeps_lfa_status_when_longitudinal_is_inactive(self):
|
||||
CP = CarParams.new_message()
|
||||
CP.carFingerprint = CAR.HYUNDAI_IONIQ_6
|
||||
CP.flags = int(HyundaiFlags.CANFD | HyundaiFlags.EV | HyundaiFlags.CANFD_LKA_STEERING)
|
||||
CP.openpilotLongitudinalControl = True
|
||||
|
||||
controller = CarController(DBC[CP.carFingerprint], CP)
|
||||
controller.frame = 1
|
||||
controller.long_active_ecu = False
|
||||
cc = SimpleNamespace(
|
||||
enabled=False, latActive=False, longActive=False,
|
||||
actuators=SimpleNamespace(longControlState=LongCtrlState.off),
|
||||
leftBlinker=False, rightBlinker=False, hudControl=SimpleNamespace(),
|
||||
)
|
||||
cs = SimpleNamespace(
|
||||
stock_lfa_msg=None, stock_lkas_msg=None,
|
||||
out=SimpleNamespace(gearShifter=structs.CarState.GearShifter.park),
|
||||
)
|
||||
|
||||
msgs = controller.create_canfd_msgs(0, False, 0.0, 0.0, 0.0, 0.0, False,
|
||||
cc.hudControl, cs, cc, get_test_toggles(), lka_icon=1, lfa_icon=1)
|
||||
assert any(addr == 0x12A for addr, _, _ in msgs)
|
||||
|
||||
def test_gv70_electrified_longitudinal_uses_hda2_scc_contract(self):
|
||||
CP = CarParams.new_message()
|
||||
CP.carFingerprint = CAR.GENESIS_GV70_ELECTRIFIED_1ST_GEN
|
||||
@@ -2707,70 +2651,6 @@ class TestHyundaiFingerprint:
|
||||
get_test_toggles(), lka_icon=1, lfa_icon=1)
|
||||
assert len([msg for msg in msgs if msg[0] == 0x110]) == expected_lkas_msgs
|
||||
|
||||
@pytest.mark.parametrize("standstill", [False, True])
|
||||
def test_sportage_angle_lkas_alt_keeps_inactive_status_in_drive(self, standstill):
|
||||
CP = CarParams.new_message()
|
||||
CP.carFingerprint = CAR.KIA_SPORTAGE_HEV_2026
|
||||
CP.flags = int(HyundaiFlags.CANFD | HyundaiFlags.HYBRID | HyundaiFlags.CANFD_ANGLE_STEERING |
|
||||
HyundaiFlags.CANFD_LKA_STEERING | HyundaiFlags.CANFD_LKA_STEERING_ALT)
|
||||
CP.openpilotLongitudinalControl = False
|
||||
|
||||
controller = CarController(DBC[CP.carFingerprint], CP)
|
||||
can_bus = CanBus(CP)
|
||||
parser = CANParser(DBC[CP.carFingerprint][Bus.pt], [("LKAS_ALT", 0)], can_bus.ACAN)
|
||||
stock_lkas = {
|
||||
"CHECKSUM": 1234,
|
||||
"COUNTER": 42,
|
||||
"LKA_OptUsmSta": 2,
|
||||
"LKA_MODE": 2,
|
||||
"LKA_RcgSta": 3,
|
||||
"LKA_AVAILABLE": 3,
|
||||
"LKA_LHLnWrnSta": 3,
|
||||
"LKA_RHLnWrnSta": 3,
|
||||
"LKA_WARNING": 1,
|
||||
"LKA_HndsoffSnd": 1,
|
||||
"LKA_StrSnd": 1,
|
||||
"LKA_SysIndReq": 4,
|
||||
"LKA_ICON": 2,
|
||||
"FCA_SYSWARN": 1,
|
||||
"StrTqReqVal": 17,
|
||||
"TORQUE_REQUEST": 17,
|
||||
"ActToiSta": 3,
|
||||
"STEER_REQ": 1,
|
||||
"ToiFltSta": 3,
|
||||
"LFA_BUTTON": 1,
|
||||
"LKA_SysWrn": 15,
|
||||
"LKA_ASSIST": 1,
|
||||
"Damping_Gain": 0,
|
||||
"STEER_MODE": 5,
|
||||
"NEW_SIGNAL_2": 0,
|
||||
"LKAS_ANGLE_ACTIVE": 2,
|
||||
"LKA_UsmMod": 3,
|
||||
"HAS_LANE_SAFETY": 1,
|
||||
"ADAS_StrAnglReqVal": 12.3,
|
||||
"ADAS_ACIAnglTqRedcGainVal": 0.42,
|
||||
"DAMP_FACTOR": 0,
|
||||
}
|
||||
cc = SimpleNamespace(enabled=False, latActive=False,
|
||||
actuators=SimpleNamespace(longControlState=LongCtrlState.off),
|
||||
leftBlinker=False, rightBlinker=False, hudControl=SimpleNamespace())
|
||||
cs = SimpleNamespace(stock_lfa_msg=None, stock_lkas_msg=stock_lkas,
|
||||
out=SimpleNamespace(standstill=standstill, steeringAngleDeg=0.0,
|
||||
gearShifter=structs.CarState.GearShifter.drive))
|
||||
|
||||
msgs = controller.create_canfd_msgs(0, False, 0.0, 0.0, 0.0, 0.0, False, cc.hudControl, cs, cc,
|
||||
get_test_toggles(), lka_icon=1, lfa_icon=1)
|
||||
lkas_msgs = [msg for msg in msgs if msg[0] == 0x110]
|
||||
assert len(lkas_msgs) == 1
|
||||
|
||||
parser.update([(1, lkas_msgs)])
|
||||
assert parser.can_valid
|
||||
assert parser.vl["LKAS_ALT"]["LKA_StrSnd"] == 2
|
||||
assert parser.vl["LKAS_ALT"]["LKA_SysIndReq"] == 1
|
||||
assert parser.vl["LKAS_ALT"]["LKA_RcgSta"] == 0
|
||||
assert parser.vl["LKAS_ALT"]["LKA_AVAILABLE"] == 0
|
||||
assert parser.vl["LKAS_ALT"]["LKAS_ANGLE_ACTIVE"] == 1
|
||||
|
||||
def test_ev9_inactive_angle_steering_does_not_suppress_stock_lfa(self):
|
||||
CP = CarParams.new_message()
|
||||
CP.carFingerprint = CAR.KIA_EV9
|
||||
|
||||
@@ -2,8 +2,6 @@ import re
|
||||
from dataclasses import dataclass, field
|
||||
from enum import IntFlag
|
||||
|
||||
# Provenance: portions of HKG angle limits, flags, and platform data are adapted from
|
||||
# sunnypilot/opendbc's hkg-angle-steering-2025 branch at cc4b08625. See CREDITS.md.
|
||||
from opendbc.car import ACCELERATION_DUE_TO_GRAVITY, Bus, CarSpecs, DbcDict, PlatformConfig, Platforms, uds
|
||||
from opendbc.car.lateral import AngleSteeringLimits, ISO_LATERAL_ACCEL
|
||||
from opendbc.car.common.conversions import Conversions as CV
|
||||
@@ -1000,10 +998,6 @@ KIA_EV6_GT_LINE_LONG_TUNING_VDS_PREFIXES = frozenset({
|
||||
})
|
||||
KIA_EV6_GT_LINE_LONG_TUNING_TESTING_GROUND_ID = "5"
|
||||
|
||||
KIA_RAY_EV_VIN_VDS_PREFIXES = frozenset({
|
||||
"CG81A",
|
||||
})
|
||||
|
||||
|
||||
ALT_BUS_LDA_BUTTON_CARS = frozenset()
|
||||
ALT_BUS_LDA_BUTTON_SWL_STAT_CARS = frozenset()
|
||||
@@ -1018,10 +1012,6 @@ def kia_ev6_gt_line_longitudinal_tuning(car_fingerprint, vin: str, testing_groun
|
||||
return car_fingerprint == CAR.KIA_EV6 and (vin_match or testing_ground_active)
|
||||
|
||||
|
||||
def kia_ray_ev_vin(vin: str) -> bool:
|
||||
return isinstance(vin, str) and len(vin) == 17 and vin[3:8] in KIA_RAY_EV_VIN_VDS_PREFIXES
|
||||
|
||||
|
||||
def get_platform_codes(fw_versions: list[bytes]) -> set[tuple[bytes, bytes | None]]:
|
||||
# Returns unique, platform-specific identification codes for a set of versions
|
||||
codes = set() # (code-Optional[part], date)
|
||||
|
||||
@@ -11,7 +11,7 @@ from opendbc.car.interfaces import CarControllerBase
|
||||
from opendbc.car.toyota import toyotacan
|
||||
from opendbc.car.toyota.values import CAR, MIN_ACC_SPEED, NO_STOP_TIMER_CAR, PEDAL_TRANSITION, TSS2_CAR, \
|
||||
CarControllerParams, ToyotaFlags, \
|
||||
UNSUPPORTED_DSU_CAR, LEGACY_PRIUS_CAR, TOYOTA_AUTO_HOLD_CARS
|
||||
UNSUPPORTED_DSU_CAR, LEGACY_PRIUS_CAR, RADAR_ACC_CAR, SECOC_CAR
|
||||
from opendbc.can import CANPacker
|
||||
|
||||
Ecu = structs.CarParams.Ecu
|
||||
@@ -37,9 +37,6 @@ TOYOTA_COAST_BRAKE_DISABLE_ACCEL = -0.06 # m/s^2
|
||||
TOYOTA_NO_LEAD_COAST_BRAKE_ACCEL = -0.30 # m/s^2
|
||||
TOYOTA_INTERCEPTOR_COMFORT_TARGET_ACCEL = 2.0 # m/s^2
|
||||
TOYOTA_NO_LEAD_CRUISE_SIGN_FLIP_MIN_SET_SPEED_ERROR = 0.35 # m/s
|
||||
TOYOTA_RAV4_LAUNCH_PEDAL_BLEND_SPEED = 5.0 # m/s
|
||||
TOYOTA_RAV4_LAUNCH_PEDAL_SCALE = 0.11
|
||||
TOYOTA_RAV4_LOW_SPEED_PEDAL_SCALE = 0.23
|
||||
|
||||
# LKA limits
|
||||
# EPS faults if you apply torque while the steering rate is above 100 deg/s for too long
|
||||
@@ -52,6 +49,8 @@ MAX_USER_TORQUE = 500
|
||||
PARK = structs.CarState.GearShifter.park
|
||||
REVERSE = structs.CarState.GearShifter.reverse
|
||||
|
||||
TOYOTA_AUTO_HOLD_CARS = TSS2_CAR - RADAR_ACC_CAR - SECOC_CAR
|
||||
|
||||
# Lock / unlock door commands - Credit goes to AlexandreSato!
|
||||
LOCK_CMD = b"\x40\x05\x30\x11\x00\x80\x00\x00"
|
||||
UNLOCK_CMD = b"\x40\x05\x30\x11\x00\x40\x00\x00"
|
||||
@@ -83,14 +82,6 @@ def supports_toyota_auto_hold(CP, auto_hold_enabled: bool) -> bool:
|
||||
)
|
||||
|
||||
|
||||
def get_rav4_interceptor_pedal_scale(v_ego: float) -> float:
|
||||
return float(np.interp(
|
||||
max(float(v_ego), 0.0),
|
||||
[0.0, TOYOTA_RAV4_LAUNCH_PEDAL_BLEND_SPEED, MIN_ACC_SPEED, MIN_ACC_SPEED + PEDAL_TRANSITION],
|
||||
[TOYOTA_RAV4_LAUNCH_PEDAL_SCALE, TOYOTA_RAV4_LOW_SPEED_PEDAL_SCALE, 0.3, 0.0],
|
||||
))
|
||||
|
||||
|
||||
def get_long_tune(CP, params):
|
||||
kiBP = [2., 5.]
|
||||
kiV = [0.5, 0.25]
|
||||
@@ -274,7 +265,7 @@ class CarController(CarControllerBase):
|
||||
|
||||
max_interceptor_gas = 0.5
|
||||
if self.CP.carFingerprint == CAR.TOYOTA_RAV4:
|
||||
pedal_scale = get_rav4_interceptor_pedal_scale(CS.out.vEgo)
|
||||
pedal_scale = float(np.interp(CS.out.vEgo, [0.0, MIN_ACC_SPEED, MIN_ACC_SPEED + PEDAL_TRANSITION], [0.15, 0.3, 0.0]))
|
||||
elif self.CP.carFingerprint in (CAR.TOYOTA_COROLLA, CAR.TOYOTA_MATRIX_RETROFIT):
|
||||
pedal_scale = float(np.interp(CS.out.vEgo, [0.0, MIN_ACC_SPEED, MIN_ACC_SPEED + PEDAL_TRANSITION], [0.3, 0.4, 0.0]))
|
||||
else:
|
||||
|
||||
@@ -314,9 +314,6 @@ class CarState(CarStateBase):
|
||||
if CP.carFingerprint in DISTANCE_BUTTON_CAR:
|
||||
pt_messages.append(("PCM_CRUISE_4", 1))
|
||||
|
||||
if CP.flags & ToyotaFlags.AUTO_BRAKE_HOLD.value:
|
||||
cam_messages.append(("PRE_COLLISION_2", 50))
|
||||
|
||||
return {
|
||||
Bus.pt: CANParser(DBC[CP.carFingerprint][Bus.pt], pt_messages, 0),
|
||||
Bus.cam: CANParser(DBC[CP.carFingerprint][Bus.pt], cam_messages, 2),
|
||||
|
||||
@@ -2,9 +2,9 @@ from opendbc.car import Bus, structs, get_safety_config, uds
|
||||
from opendbc.car.toyota.carstate import CarState
|
||||
from opendbc.car.toyota.carcontroller import CarController
|
||||
from opendbc.car.toyota.radar_interface import RadarInterface
|
||||
from opendbc.car.toyota.values import Ecu, CAR, DBC, ToyotaFlags, CarControllerParams, TSS2_CAR, RADAR_ACC_CAR, NO_DSU_CAR, \
|
||||
from opendbc.car.toyota.values import Ecu, CAR, DBC, ToyotaFlags, CarControllerParams, TSS2_CAR, RADAR_ACC_CAR, SECOC_CAR, NO_DSU_CAR, \
|
||||
MIN_ACC_SPEED, EPS_SCALE, NO_STOP_TIMER_CAR, ANGLE_CONTROL_CAR, \
|
||||
ToyotaSafetyFlags, LEGACY_PRIUS_CAR, TOYOTA_AUTO_HOLD_CARS
|
||||
ToyotaSafetyFlags, LEGACY_PRIUS_CAR
|
||||
from opendbc.car.disable_ecu import disable_ecu
|
||||
from opendbc.car.interfaces import CarInterfaceBase
|
||||
from opendbc.safety import ALTERNATIVE_EXPERIENCE
|
||||
@@ -164,7 +164,7 @@ class CarInterface(CarInterfaceBase):
|
||||
ret.safetyConfigs[0].safetyParam |= ToyotaSafetyFlags.GAS_INTERCEPTOR.value
|
||||
|
||||
toyota_auto_hold = Params(return_defaults=True).get_bool("ToyotaAutoHold")
|
||||
if toyota_auto_hold and candidate in TOYOTA_AUTO_HOLD_CARS:
|
||||
if toyota_auto_hold and candidate in (TSS2_CAR - RADAR_ACC_CAR - SECOC_CAR):
|
||||
ret.alternativeExperience |= ALTERNATIVE_EXPERIENCE.ALLOW_AEB
|
||||
ret.flags |= ToyotaFlags.AUTO_BRAKE_HOLD.value
|
||||
|
||||
|
||||
@@ -10,7 +10,6 @@ from opendbc.car.fw_versions import build_fw_dict, match_fw_to_car
|
||||
from opendbc.car.toyota import toyotacan
|
||||
from opendbc.car.toyota.carcontroller import CarController, get_camry_hybrid_feedforward, get_long_tune, get_prius_feedforward, \
|
||||
get_prius_positive_feedforward_scale, \
|
||||
get_rav4_interceptor_pedal_scale, \
|
||||
limit_interceptor_pcm_accel, \
|
||||
limit_interceptor_stopping_accel, limit_no_lead_cruise_sign_flip, \
|
||||
limit_prius_stopping_accel, should_bypass_toyota_long_pid, supports_toyota_auto_hold, \
|
||||
@@ -19,10 +18,9 @@ from opendbc.car.toyota.carstate import CarState, LKAS_BUTTON_CAR, calculate_int
|
||||
from opendbc.car.toyota.fingerprints import FW_VERSIONS
|
||||
from opendbc.car.toyota.interface import CarInterface
|
||||
from opendbc.car.toyota.radar_interface import RadarInterface, TSSP_RADAR_EGO_SPEED_SCALE
|
||||
from opendbc.car.toyota.values import CAR, DBC, MIN_ACC_SPEED, TSS2_CAR, ANGLE_CONTROL_CAR, RADAR_ACC_CAR, SECOC_CAR, \
|
||||
from opendbc.car.toyota.values import CAR, DBC, TSS2_CAR, ANGLE_CONTROL_CAR, RADAR_ACC_CAR, SECOC_CAR, \
|
||||
FW_QUERY_CONFIG, PLATFORM_CODE_ECUS, FUZZY_EXCLUDED_PLATFORMS, \
|
||||
ToyotaFlags, ToyotaSafetyFlags, ToyotaStarPilotFlags, TOYOTA_AUTO_HOLD_CARS, \
|
||||
get_platform_codes
|
||||
ToyotaFlags, ToyotaSafetyFlags, ToyotaStarPilotFlags, get_platform_codes
|
||||
from opendbc.safety import ALTERNATIVE_EXPERIENCE
|
||||
from openpilot.common.params import Params
|
||||
|
||||
@@ -189,13 +187,12 @@ class TestToyotaInterfaces:
|
||||
if car_model in TSS2_CAR and car_model not in SECOC_CAR:
|
||||
assert dbc[Bus.pt] == "toyota_nodsu_pt_generated"
|
||||
|
||||
@pytest.mark.parametrize("candidate", [CAR.TOYOTA_CAMRY_TSS2, CAR.TOYOTA_RAV4, CAR.TOYOTA_RAV4H])
|
||||
def test_auto_hold_sets_flag_on_supported_toyota(self, candidate):
|
||||
def test_auto_hold_sets_flag_on_supported_tss2(self):
|
||||
params = Params()
|
||||
try:
|
||||
params.put_bool("ToyotaAutoHold", True)
|
||||
car_params = CarInterface.get_params(
|
||||
candidate,
|
||||
CAR.TOYOTA_CAMRY_TSS2,
|
||||
{bus: {} for bus in range(8)},
|
||||
[],
|
||||
alpha_long=False,
|
||||
@@ -209,17 +206,11 @@ class TestToyotaInterfaces:
|
||||
assert car_params.flags & ToyotaFlags.AUTO_BRAKE_HOLD.value
|
||||
assert car_params.alternativeExperience & ALTERNATIVE_EXPERIENCE.ALLOW_AEB
|
||||
|
||||
can_parsers = CarState.get_can_parsers(car_params)
|
||||
car_state = CarState(car_params, SimpleNamespace(flags=0))
|
||||
car_state.update(can_parsers, SimpleNamespace(cluster_offset=1.0))
|
||||
assert "PRE_COLLISION_2" in can_parsers[Bus.cam].vl
|
||||
|
||||
@pytest.mark.parametrize("candidate", [CAR.TOYOTA_CAMRY_TSS2, CAR.TOYOTA_RAV4, CAR.TOYOTA_RAV4H])
|
||||
def test_auto_hold_is_disabled_by_default(self, candidate):
|
||||
def test_auto_hold_is_disabled_by_default(self):
|
||||
params = Params()
|
||||
params.remove("ToyotaAutoHold")
|
||||
car_params = CarInterface.get_params(
|
||||
candidate,
|
||||
CAR.TOYOTA_CAMRY_TSS2,
|
||||
{bus: {} for bus in range(8)},
|
||||
[],
|
||||
alpha_long=False,
|
||||
@@ -783,14 +774,6 @@ class TestToyotaCarController:
|
||||
)
|
||||
|
||||
assert supports_toyota_auto_hold(CP, True)
|
||||
assert supports_toyota_auto_hold(SimpleNamespace(
|
||||
carFingerprint=CAR.TOYOTA_RAV4,
|
||||
flags=ToyotaFlags.AUTO_BRAKE_HOLD.value,
|
||||
), True)
|
||||
assert supports_toyota_auto_hold(SimpleNamespace(
|
||||
carFingerprint=CAR.TOYOTA_RAV4H,
|
||||
flags=ToyotaFlags.AUTO_BRAKE_HOLD.value,
|
||||
), True)
|
||||
assert not supports_toyota_auto_hold(CP, False)
|
||||
assert not supports_toyota_auto_hold(SimpleNamespace(
|
||||
carFingerprint=CAR.TOYOTA_CAMRY_TSS2,
|
||||
@@ -801,8 +784,6 @@ class TestToyotaCarController:
|
||||
flags=ToyotaFlags.AUTO_BRAKE_HOLD.value,
|
||||
), True)
|
||||
|
||||
assert TOYOTA_AUTO_HOLD_CARS >= {CAR.TOYOTA_RAV4, CAR.TOYOTA_RAV4H}
|
||||
|
||||
def test_toyota_auto_hold_latches_after_brake_press_until_gas(self):
|
||||
controller = self._make_controller()
|
||||
controller.packer = CANPacker(DBC[CAR.TOYOTA_CAMRY_TSS2][Bus.pt])
|
||||
@@ -1042,27 +1023,6 @@ class TestToyotaCarController:
|
||||
|
||||
assert 0.0 < gas_cmd <= 0.5
|
||||
|
||||
def test_rav4_interceptor_launch_mapping_is_softer_than_generic_mapping(self):
|
||||
controller = self._make_controller()
|
||||
controller.CP.enableGasInterceptorDEPRECATED = True
|
||||
controller.CP.carFingerprint = CAR.TOYOTA_RAV4
|
||||
controller.accel = 1.5
|
||||
|
||||
rav4_gas = controller._compute_interceptor_gas_cmd(
|
||||
SimpleNamespace(longActive=True),
|
||||
SimpleNamespace(out=SimpleNamespace(standstill=False, vEgo=1.0)),
|
||||
)
|
||||
|
||||
controller.CP.carFingerprint = CAR.TOYOTA_AVALON_2019
|
||||
generic_gas = controller._compute_interceptor_gas_cmd(
|
||||
SimpleNamespace(longActive=True),
|
||||
SimpleNamespace(out=SimpleNamespace(standstill=False, vEgo=1.0)),
|
||||
)
|
||||
|
||||
assert rav4_gas < generic_gas
|
||||
assert get_rav4_interceptor_pedal_scale(5.0) == pytest.approx(0.23)
|
||||
assert get_rav4_interceptor_pedal_scale(MIN_ACC_SPEED) == pytest.approx(0.3)
|
||||
|
||||
def test_interceptor_corolla_scales_with_accel_request_when_pedal_enables_sng(self):
|
||||
controller = self._make_controller()
|
||||
controller.CP.enableGasInterceptorDEPRECATED = True
|
||||
|
||||
@@ -624,11 +624,6 @@ ANGLE_CONTROL_CAR = CAR.with_flags(ToyotaFlags.ANGLE_CONTROL)
|
||||
|
||||
SECOC_CAR = CAR.with_flags(ToyotaFlags.SECOC)
|
||||
|
||||
TOYOTA_AUTO_HOLD_CARS = (TSS2_CAR - RADAR_ACC_CAR - SECOC_CAR) | {
|
||||
CAR.TOYOTA_RAV4,
|
||||
CAR.TOYOTA_RAV4H,
|
||||
}
|
||||
|
||||
# no resume button press required
|
||||
NO_STOP_TIMER_CAR = CAR.with_flags(ToyotaFlags.NO_STOP_TIMER)
|
||||
|
||||
|
||||
@@ -1497,7 +1497,7 @@ BO_ 913 BCM_PO_11: 8 Vector__XXX
|
||||
SG_ BCM_Door_Dri_Status : 5|1@0+ (1,0) [0|1] "" PT_ESC_ABS
|
||||
SG_ BCM_Shift_R_MT_SW_Status : 39|2@0+ (1,0) [0|3] "" PT_ESC_ABS
|
||||
SG_ LDA_BTN : 4|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ RAY_LKAS_BTN : 4|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ RAY_LKAS_BTN : 0|2@1+ (1,0) [0|3] "" XXX
|
||||
|
||||
BO_ 1426 LABEL11: 8 XXX
|
||||
SG_ CC_React : 34|1@1+ (1,0) [0|1] "" XXX
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
VERSION ""
|
||||
|
||||
NS_ :
|
||||
NS_DESC_
|
||||
CM_
|
||||
BA_DEF_
|
||||
BA_
|
||||
VAL_
|
||||
|
||||
BS_:
|
||||
|
||||
BU_: XXX
|
||||
|
||||
BO_ 1157 LFAHDA_MFC: 8 XXX
|
||||
SG_ HDA_USM : 0|2@1+ (1,0) [0|3] "" XXX
|
||||
SG_ HDA_Active : 2|1@1+ (1,0) [0|1] "" XXX
|
||||
SG_ HDA_Icon_State : 3|2@1+ (1,0) [0|3] "" XXX
|
||||
SG_ HDA_Chime : 7|1@1+ (1,0) [0|1] "" XXX
|
||||
SG_ HDA_VSetReq : 8|8@1+ (1,0) [0|255] "km/h" XXX
|
||||
SG_ LFA_SysWarning : 16|3@1+ (1,0) [0|7] "" XXX
|
||||
SG_ HDA_Icon_Wheel : 20|1@1+ (1,0) [0|1] "" XXX
|
||||
SG_ HDA_LdwSysState : 21|2@1+ (1,0) [0|3] "" XXX
|
||||
SG_ LFA_Icon_State : 24|2@1+ (1,0) [0|3] "" XXX
|
||||
SG_ LFA_USM : 27|2@1+ (1,0) [0|3] "" XXX
|
||||
SG_ HDA_SysWarning : 29|2@1+ (1,0) [0|3] "" XXX
|
||||
@@ -2,12 +2,6 @@
|
||||
|
||||
#include "opendbc/safety/declarations.h"
|
||||
|
||||
// StarPilot's extended Ford curvature enforcement below is substantially adapted from
|
||||
// BluePilot bp-7.0 panda work, principally Alan Polk's 8f8d6d15f0a590f42b78de964ffb0d0af7f5d63d
|
||||
// See /CREDITS.md and /THIRD_PARTY_NOTICES.md. This comment does not attribute the surrounding
|
||||
// upstream openpilot code.
|
||||
|
||||
|
||||
// Safety-relevant CAN messages for Ford vehicles.
|
||||
#define FORD_EngBrakeData 0x165U // RX from PCM, for driver brake pedal and cruise state
|
||||
#define FORD_EngVehicleSpThrottle 0x204U // RX from PCM, for driver throttle input
|
||||
@@ -94,8 +88,10 @@ static bool ford_get_quality_flag_valid(const CANPacket_t *msg) {
|
||||
|
||||
static bool ford_lka_steering = false;
|
||||
static bool ford_extended_lateral = false;
|
||||
static bool ford_angle_mode = false;
|
||||
static bool ford_longitudinal = false;
|
||||
static bool ford_cancel_resume_button = false;
|
||||
static int16_t ford_shadow_curvature = 0;
|
||||
|
||||
// Curvature rate limits
|
||||
#define FORD_LIMITS(limit_lateral_acceleration) { \
|
||||
@@ -142,6 +138,38 @@ static const AngleSteeringLimits FORD_STEERING_LIMITS = FORD_LIMITS(false);
|
||||
|
||||
static const AngleSteeringLimits FORD_EXTENDED_STEERING_LIMITS = FORD_EXTENDED_LIMITS(false);
|
||||
|
||||
static int ford_desired_path_angle_last = 0;
|
||||
|
||||
static bool ford_path_angle_checks(int desired_path_angle, bool steer_control_enabled) {
|
||||
bool violation = false;
|
||||
if (steer_control_enabled) {
|
||||
float speed = ((float)vehicle_speed.min / VEHICLE_SPEED_FACTOR) - 1.0;
|
||||
const struct lookup_t path_angle_rate = {
|
||||
.x = {10., 15., 25.},
|
||||
.y = {0.0561, 0.04335, 0.00918},
|
||||
};
|
||||
int max_delta = (safety_interpolate(path_angle_rate, speed) * 2000.0) + 1.0;
|
||||
violation |= safety_max_limit_check(desired_path_angle,
|
||||
ford_desired_path_angle_last + max_delta,
|
||||
ford_desired_path_angle_last - max_delta);
|
||||
} else {
|
||||
violation |= desired_path_angle != 0;
|
||||
}
|
||||
ford_desired_path_angle_last = violation ? 0 : desired_path_angle;
|
||||
return violation;
|
||||
}
|
||||
|
||||
static bool ford_shadow_curvature_check(int desired_curvature, bool steer_control_enabled,
|
||||
const AngleSteeringLimits limits) {
|
||||
if (steer_control_enabled && limits.enforce_angle_error &&
|
||||
((vehicle_speed.values[0] / VEHICLE_SPEED_FACTOR) > limits.angle_error_min_speed)) {
|
||||
int lowest_allowed = angle_meas.min - limits.max_angle_error - 1;
|
||||
int highest_allowed = angle_meas.max + limits.max_angle_error + 1;
|
||||
return safety_max_limit_check(desired_curvature, highest_allowed, lowest_allowed);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static void ford_rx_hook(const CANPacket_t *msg) {
|
||||
if (msg->bus == FORD_MAIN_BUS) {
|
||||
// Update in motion state from standstill signal
|
||||
@@ -272,8 +300,10 @@ static bool ford_tx_hook(const CANPacket_t *msg) {
|
||||
}
|
||||
|
||||
if (!ford_lka_steering) {
|
||||
ford_angle_mode = (msg->data[4] & 0x1U) != 0U;
|
||||
ford_extended_lateral = (msg->data[4] & 0x2U) != 0U;
|
||||
if ((msg->data[4] & 0x1U) != 0U) {
|
||||
ford_shadow_curvature = (int16_t)((msg->data[5] << 8) | msg->data[6]);
|
||||
if (ford_angle_mode && !ford_extended_lateral) {
|
||||
tx = false;
|
||||
}
|
||||
}
|
||||
@@ -297,9 +327,20 @@ static bool ford_tx_hook(const CANPacket_t *msg) {
|
||||
if (ford_extended_lateral) {
|
||||
violation |= desired_path_offset != 0;
|
||||
violation |= (desired_curvature_rate < -4096) || (desired_curvature_rate > 4095);
|
||||
violation |= desired_path_angle != 0;
|
||||
violation |= steer_angle_cmd_checks(desired_curvature, steer_control_enabled,
|
||||
FORD_EXTENDED_STEERING_LIMITS);
|
||||
violation |= ford_path_angle_checks(desired_path_angle, steer_control_enabled);
|
||||
if (ford_angle_mode) {
|
||||
violation |= (desired_path_angle < -1000) || (desired_path_angle > 1047);
|
||||
violation |= desired_curvature != 0;
|
||||
violation |= steer_control_enabled && !(aol_allowed || controls_allowed);
|
||||
int shadow_curvature_can = ROUND((float)ford_shadow_curvature * 0.05);
|
||||
violation |= ford_shadow_curvature_check(shadow_curvature_can, steer_control_enabled,
|
||||
FORD_EXTENDED_STEERING_LIMITS);
|
||||
desired_angle_last = 0;
|
||||
} else {
|
||||
violation |= desired_path_angle != 0;
|
||||
violation |= steer_angle_cmd_checks(desired_curvature, steer_control_enabled,
|
||||
FORD_EXTENDED_STEERING_LIMITS);
|
||||
}
|
||||
if (!steer_control_enabled) {
|
||||
violation |= (desired_curvature != 0) || (desired_curvature_rate != 0);
|
||||
}
|
||||
@@ -336,9 +377,20 @@ static bool ford_tx_hook(const CANPacket_t *msg) {
|
||||
if (ford_extended_lateral) {
|
||||
violation |= desired_path_offset != 0;
|
||||
violation |= (desired_curvature_rate < -1024) || (desired_curvature_rate > 1023);
|
||||
violation |= desired_path_angle != 0;
|
||||
violation |= steer_angle_cmd_checks(desired_curvature, steer_control_enabled,
|
||||
FORD_CANFD_EXTENDED_STEERING_LIMITS);
|
||||
violation |= ford_path_angle_checks(desired_path_angle, steer_control_enabled);
|
||||
if (ford_angle_mode) {
|
||||
violation |= (desired_path_angle < -1000) || (desired_path_angle > 1047);
|
||||
violation |= desired_curvature != 0;
|
||||
violation |= steer_control_enabled && !(aol_allowed || controls_allowed);
|
||||
int shadow_curvature_can = ROUND((float)ford_shadow_curvature * 0.05);
|
||||
violation |= ford_shadow_curvature_check(shadow_curvature_can, steer_control_enabled,
|
||||
FORD_CANFD_EXTENDED_STEERING_LIMITS);
|
||||
desired_angle_last = 0;
|
||||
} else {
|
||||
violation |= desired_path_angle != 0;
|
||||
violation |= steer_angle_cmd_checks(desired_curvature, steer_control_enabled,
|
||||
FORD_CANFD_EXTENDED_STEERING_LIMITS);
|
||||
}
|
||||
if (!steer_control_enabled) {
|
||||
violation |= (desired_curvature != 0) || (desired_curvature_rate != 0);
|
||||
}
|
||||
@@ -409,7 +461,10 @@ static safety_config ford_init(uint16_t param) {
|
||||
const bool ford_canfd = GET_FLAG(param, FORD_PARAM_CANFD);
|
||||
ford_lka_steering = GET_FLAG(param, FORD_PARAM_LKA_STEERING);
|
||||
ford_extended_lateral = false;
|
||||
ford_angle_mode = false;
|
||||
ford_cancel_resume_button = false;
|
||||
ford_shadow_curvature = 0;
|
||||
ford_desired_path_angle_last = 0;
|
||||
|
||||
ford_longitudinal = false;
|
||||
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
// Provenance: portions of HKG angle-command safety are adapted from sunnypilot/opendbc's
|
||||
// hkg-angle-steering-2025 branch at cc4b08625. See CREDITS.md and THIRD_PARTY_NOTICES.md.
|
||||
#include "opendbc/safety/declarations.h"
|
||||
#include "opendbc/safety/modes/hyundai_common.h"
|
||||
|
||||
|
||||
@@ -224,16 +224,10 @@ static void tesla_preap_rx_hook(const CANPacket_t *msg) {
|
||||
}
|
||||
|
||||
if (msg->addr == 0x368U) {
|
||||
const int cruise_state = (msg->data[1] >> 4) & 0x0FU;
|
||||
const int cruise_state = (msg->data[1] >> 4) & 0x07U;
|
||||
if (cruise_state == 3) {
|
||||
vehicle_moving = false;
|
||||
}
|
||||
acc_main_on = (cruise_state == 1) ||
|
||||
(cruise_state == 2) ||
|
||||
(cruise_state == 3) ||
|
||||
(cruise_state == 4) ||
|
||||
(cruise_state == 6) ||
|
||||
(cruise_state == 7);
|
||||
}
|
||||
|
||||
if (msg->addr == 0x118U) {
|
||||
|
||||
@@ -170,11 +170,6 @@ class TestFordSafetyBase(common.CarSafetyTest):
|
||||
}
|
||||
return self.packer.make_can_msg_safety("Lane_Assist_Data1", 0, values)
|
||||
|
||||
def _extended_lka_msg(self, angle_mode=False):
|
||||
msg = self._lkas_command_msg(0)
|
||||
msg[0].data[4] |= 0x2 | int(angle_mode)
|
||||
return msg
|
||||
|
||||
# LCA command
|
||||
def _lat_ctl_msg(self, enabled: bool, path_offset: float, path_angle: float, curvature: float, curvature_rate: float):
|
||||
if self.STEER_MESSAGE == MSG_LateralMotionControl:
|
||||
@@ -381,25 +376,6 @@ class TestFordSafetyBase(common.CarSafetyTest):
|
||||
should_tx |= self.LKA_STEERING and controls_allowed and action in (2, 4)
|
||||
self.assertEqual(should_tx, self._tx(self._lkas_command_msg(action)))
|
||||
|
||||
def test_extended_angle_mode_rejected(self):
|
||||
if self.LKA_STEERING:
|
||||
return
|
||||
|
||||
self.assertTrue(self._tx(self._extended_lka_msg()))
|
||||
self.assertFalse(self._tx(self._extended_lka_msg(angle_mode=True)))
|
||||
|
||||
def test_extended_curvature_signals(self):
|
||||
if self.LKA_STEERING:
|
||||
return
|
||||
|
||||
speed = 15.0
|
||||
self.safety.set_controls_allowed(True)
|
||||
self._reset_curvature_measurement(0.0, speed)
|
||||
self.assertTrue(self._tx(self._extended_lka_msg()))
|
||||
self.assertTrue(self._tx(self._lat_ctl_msg(True, 0.0, 0.0, 0.001, 0.0005)))
|
||||
self.assertFalse(self._tx(self._lat_ctl_msg(True, 0.1, 0.0, 0.001, 0.0005)))
|
||||
self.assertFalse(self._tx(self._lat_ctl_msg(True, 0.0, 0.02, 0.001, 0.0005)))
|
||||
|
||||
def test_acc_buttons(self):
|
||||
for allowed in (0, 1):
|
||||
self.safety.set_controls_allowed(allowed)
|
||||
@@ -467,6 +443,39 @@ class TestFordCANFDStockSafety(TestFordSafetyBase):
|
||||
self.safety.set_safety_hooks(CarParams.SafetyModel.ford, FordSafetyFlags.CANFD)
|
||||
self.safety.init_tests()
|
||||
|
||||
def _extended_lka_msg(self, angle_mode=False, shadow_curvature=0.0):
|
||||
msg = self._lkas_command_msg(0)
|
||||
raw_shadow = int(round(shadow_curvature / 1e-6)) & 0xFFFF
|
||||
msg[0].data[4] |= 0x2 | int(angle_mode)
|
||||
msg[0].data[5] = raw_shadow >> 8
|
||||
msg[0].data[6] = raw_shadow & 0xFF
|
||||
return msg
|
||||
|
||||
def test_extended_curvature_signals(self):
|
||||
speed = 15.0
|
||||
self.safety.set_controls_allowed(True)
|
||||
self._reset_curvature_measurement(0.0, speed)
|
||||
self.assertTrue(self._tx(self._extended_lka_msg()))
|
||||
self.assertTrue(self._tx(self._lat_ctl_msg(True, 0.0, 0.0, 0.001, 0.0005)))
|
||||
|
||||
self.assertTrue(self._tx(self._extended_lka_msg()))
|
||||
self.assertFalse(self._tx(self._lat_ctl_msg(True, 0.1, 0.0, 0.001, 0.0005)))
|
||||
|
||||
def test_extended_angle_signals(self):
|
||||
speed = 15.0
|
||||
curvature = 0.005
|
||||
self.safety.set_controls_allowed(True)
|
||||
self._reset_curvature_measurement(curvature, speed)
|
||||
self.assertTrue(self._tx(self._extended_lka_msg(angle_mode=True, shadow_curvature=curvature)))
|
||||
self.assertTrue(self._tx(self._lat_ctl_msg(True, 0.0, 0.02, 0.0, 0.0)))
|
||||
|
||||
self.assertTrue(self._tx(self._extended_lka_msg(angle_mode=True, shadow_curvature=curvature)))
|
||||
self.assertFalse(self._tx(self._lat_ctl_msg(True, 0.0, 0.2, 0.0, 0.0)))
|
||||
|
||||
self.assertTrue(self._tx(self._extended_lka_msg(angle_mode=True, shadow_curvature=-curvature)))
|
||||
self.assertFalse(self._tx(self._lat_ctl_msg(True, 0.0, 0.01, 0.0, 0.0)))
|
||||
|
||||
|
||||
class TestFordStockSafety(TestFordSafetyBase):
|
||||
STEER_MESSAGE = MSG_LateralMotionControl
|
||||
STOCK_LONGITUDINAL = True
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
#!/usr/bin/env python3
|
||||
# Provenance: portions of HKG angle-safety tests are adapted from sunnypilot/opendbc's
|
||||
# hkg-angle-steering-2025 branch at cc4b08625. See CREDITS.md and THIRD_PARTY_NOTICES.md.
|
||||
from parameterized import parameterized_class
|
||||
import unittest
|
||||
import numpy as np
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
import unittest
|
||||
|
||||
from opendbc.car.tesla.preap.interface import SAFETY_TESLA_PREAP
|
||||
from opendbc.safety import ALTERNATIVE_EXPERIENCE
|
||||
from opendbc.safety.tests.libsafety import libsafety_py
|
||||
import opendbc.safety.tests.common as common
|
||||
|
||||
@@ -37,12 +36,6 @@ class TestTeslaPreAPSafety(common.SafetyTestBase):
|
||||
dat[0] = lever & 0x3F
|
||||
return common.make_msg(0, 0x45, 8, dat)
|
||||
|
||||
@staticmethod
|
||||
def _di_state_msg(cruise_state: int):
|
||||
dat = bytearray(8)
|
||||
dat[1] = (cruise_state & 0x0F) << 4
|
||||
return common.make_msg(0, 0x368, 8, dat)
|
||||
|
||||
@staticmethod
|
||||
def _steering_status_msg(hands_on_level: int = 0, eac_status: int = 1, eac_error_code: int = 0, angle_tenths: int = 0):
|
||||
raw_angle = angle_tenths + 8192
|
||||
@@ -130,30 +123,6 @@ class TestTeslaPreAPSafety(common.SafetyTestBase):
|
||||
self.assertTrue(self._tx(self._epas_control_msg(1)))
|
||||
self.assertFalse(self._tx(self._epas_control_msg(2)))
|
||||
|
||||
def test_always_on_lateral(self):
|
||||
ENABLED, OFF = 2, 0
|
||||
self.safety.set_controls_allowed(False)
|
||||
|
||||
self.safety.set_alternative_experience(0)
|
||||
self.assertTrue(self._rx(self._di_state_msg(ENABLED)))
|
||||
self.assertFalse(self._tx(self._steer_cmd_msg(0, 1)))
|
||||
|
||||
self.safety.set_alternative_experience(ALTERNATIVE_EXPERIENCE.ALWAYS_ON_LATERAL)
|
||||
self.assertTrue(self._rx(self._di_state_msg(OFF)))
|
||||
self.assertFalse(self.safety.get_controls_allowed())
|
||||
self.assertFalse(self._tx(self._steer_cmd_msg(0, 1)))
|
||||
|
||||
self.assertTrue(self._rx(self._di_state_msg(ENABLED)))
|
||||
self.assertFalse(self.safety.get_controls_allowed())
|
||||
self.assertTrue(self._tx(self._steer_cmd_msg(0, 1)))
|
||||
|
||||
self.assertTrue(self._rx(self._di_state_msg(OFF)))
|
||||
self.assertFalse(self._tx(self._steer_cmd_msg(0, 1)))
|
||||
|
||||
self.assertTrue(self._rx(self._di_state_msg(9)))
|
||||
self.assertFalse(self.safety.get_aol_allowed())
|
||||
self.assertFalse(self._tx(self._steer_cmd_msg(0, 1)))
|
||||
|
||||
def test_aeb_is_blocked(self):
|
||||
self.safety.set_controls_allowed(True)
|
||||
self.assertTrue(self._tx(self._long_msg(0)))
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,2 +1,2 @@
|
||||
extern const uint8_t gitversion[19];
|
||||
const uint8_t gitversion[19] = "DEV-c03d06b4-DEBUG";
|
||||
const uint8_t gitversion[19] = "DEV-cec1a0fb-DEBUG";
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user