mirror of
https://github.com/firestar5683/StarPilot.git
synced 2026-08-24 01:33:46 +08:00
Compare commits
333 Commits
plexy
...
testharnessbox
| Author | SHA1 | Date | |
|---|---|---|---|
| 44e5d0cfb9 | |||
| 0cddc6308a | |||
| 56a0c4e0d9 | |||
| 666b9d35d6 | |||
| 2ef8f8ebef | |||
| b88c886592 | |||
| e742482175 | |||
| 8c63451137 | |||
| a30a59331d | |||
| 95d074eb92 | |||
| 103c44bfd8 | |||
| bfcd43452e | |||
| 0c5cf54d57 | |||
| 3f4f2a1813 | |||
| a94c3b72d3 | |||
| c848b5f1b4 | |||
| 55f110351e | |||
| 58e7f84627 | |||
| db769226b8 | |||
| bfcab984e2 | |||
| b33a8dea15 | |||
| 5e00550c6a | |||
| 329b41a84b | |||
| 5e74861311 | |||
| 70229486bb | |||
| 813507267c | |||
| f55402e033 | |||
| f3cf4d6969 | |||
| da88d20b1e | |||
| b17bba7e93 | |||
| c99a999f1a | |||
| c5c5253de5 | |||
| 1f8652d809 | |||
| 8846b907d7 | |||
| 3d6c55962b | |||
| 0b0f18399c | |||
| 5856e77f3b | |||
| c80d9ffb99 | |||
| 1f84395539 | |||
| f3a7620188 | |||
| 180b0c6768 | |||
| 55a93013c7 | |||
| 982e742322 | |||
| 3676191fc8 | |||
| 5b19d38d88 | |||
| 32c088ac00 | |||
| 123adc7edc | |||
| dbecce602c | |||
| 2b10dc21b0 | |||
| 51701ecffc | |||
| 601b8855f6 | |||
| f56bfb7d4b | |||
| 3f8d368b87 | |||
| 70ccc64831 | |||
| 2f5aef1fb8 | |||
| 3581a9ecf0 | |||
| ed06aa8920 | |||
| 5bb401772a | |||
| 5c30a69e10 | |||
| be585f72a3 | |||
| 309665f63e | |||
| 9d8a055a04 | |||
| 45a1d3fa8e | |||
| 473e3efae9 | |||
| e2607ea6e8 | |||
| 75dcc0772e | |||
| 38ad334e12 | |||
| 5fcc1bc196 | |||
| 5bd1977c89 | |||
| 3eb5db5b31 | |||
| aa84176c7d | |||
| f30e540690 | |||
| 1729d3cda3 | |||
| c7c10822fb | |||
| 6b39e4103e | |||
| 5a90937c52 | |||
| 69e7ed3eae | |||
| 01bcb40c50 | |||
| 3ecb73dc8a | |||
| 06c00b0895 | |||
| 3f8b47aa91 | |||
| dc82e51ef6 | |||
| 2516d13bc5 | |||
| 2ffb286569 | |||
| af84c8802b | |||
| d7e43b7ecc | |||
| 471a33d64d | |||
| 334481c78d | |||
| c2bb8f9c56 | |||
| c650edd193 | |||
| 3e03917edc | |||
| 4d0f65a6d1 | |||
| 02717ebe73 | |||
| a67c8f98b7 | |||
| 21589861fd | |||
| 0ce8161cbf | |||
| 013ecbed97 | |||
| 4370e9c7a3 | |||
| a5dedeb164 | |||
| 7b557144b3 | |||
| 4747d26cf6 | |||
| 3c3969b09d | |||
| 8bd080b3e0 | |||
| 5a044781fc | |||
| 98480a3018 | |||
| 5c412cea96 | |||
| ee3d33526b | |||
| 25633be976 | |||
| cd20d94c7c | |||
| 90fbb74f93 | |||
| 867159198a | |||
| 6c4ae9749d | |||
| 4254090cf4 | |||
| 006319deff | |||
| 9872c5fb4b | |||
| 2bc5247dc0 | |||
| 2ca5f36590 | |||
| c1e3525929 | |||
| ef44741760 | |||
| eebfd509d7 | |||
| 61ca62df5f | |||
| ba18142718 | |||
| 1714c516ab | |||
| 1b2b13881c | |||
| 7dc26d1a68 | |||
| fbba97f9a6 | |||
| e969a96c17 | |||
| 930ef6e646 | |||
| b0bba1917f | |||
| 1fbd6ecdfa | |||
| 888a674675 | |||
| 510e2c2225 | |||
| ffd8f6404b | |||
| e0259e191c | |||
| 245a5eaaeb | |||
| 1fb31b2e12 | |||
| ea64cc4de3 | |||
| b038b102d8 | |||
| 5e0a1bbec3 | |||
| 5f3d71798d | |||
| c1804b1fd8 | |||
| 86ce0b03ee | |||
| e5ac0c511d | |||
| 8c0e8375f4 | |||
| 357d7e545f | |||
| d2bd12e9a1 | |||
| 300ad3d49c | |||
| be9ab0a7e6 | |||
| c71a9aed6e | |||
| ecf6161f9c | |||
| 78d5f71fed | |||
| c1d3e4540b | |||
| 2e7036ab07 | |||
| cfff7d7cb7 | |||
| 1f68e0deaf | |||
| c79e2b3ad7 | |||
| 49aad9cce3 | |||
| 8ec27d25c5 | |||
| cd9115b0b2 | |||
| 7918dab915 | |||
| 0913a22dfe | |||
| e36a9c6c61 | |||
| a98bf277c8 | |||
| c5b12e0a16 | |||
| e421eb271e | |||
| 9b97124f80 | |||
| 1a39c51617 | |||
| 2f74f93308 | |||
| 4747792c37 | |||
| 5e0447df1e | |||
| d9676ca194 | |||
| 026a2eb528 | |||
| e49cfe44b9 | |||
| 2e113835a7 | |||
| 5f51e6704d | |||
| 806a210467 | |||
| 1ddbc24a14 | |||
| aed0b7fdb9 | |||
| 7837a3c1aa | |||
| 7e4818916b | |||
| 32185c0c2e | |||
| 90b05a1d18 | |||
| 37882e86d5 | |||
| 1fd3f2cb9d | |||
| 3f3a63a14b | |||
| 4abda00404 | |||
| b2ded79155 | |||
| 7ea096686d | |||
| 847333d536 | |||
| f132bd7b82 | |||
| d7ec15bddb | |||
| 41fef69a57 | |||
| 14cde4a64c | |||
| abd2721a15 | |||
| 3deb8fbb84 | |||
| c49b4ec228 | |||
| 8125fd789a | |||
| 6764db8b3b | |||
| 24af3405f2 | |||
| 6626202ff1 | |||
| 261b139bcc | |||
| da5a028057 | |||
| b9ab07d7cd | |||
| a9d57b521e | |||
| 7f03e2880c | |||
| 6923c35e7d | |||
| 48ea42c026 | |||
| 5bc24ff148 | |||
| 8ddf2b4757 | |||
| 863273edbc | |||
| b8a84d6196 | |||
| 997c1be7b6 | |||
| 471981f732 | |||
| 989b69ee92 | |||
| 650b79941f | |||
| f7d79c613e | |||
| 37c82cc2d4 | |||
| 1c38b731bf | |||
| 5312ce3082 | |||
| 3c27a6a273 | |||
| d4f73f56c4 | |||
| 5584535842 | |||
| 5607d3d01a | |||
| 5ace618860 | |||
| fc4a53c02e | |||
| 1a016da5fa | |||
| 0f5ab2f6f6 | |||
| be88d86b21 | |||
| d98d1c3432 | |||
| 873275374d | |||
| 712aa081bc | |||
| 0c39558afb | |||
| 762146babc | |||
| 708af332de | |||
| c612f7a102 | |||
| 943a060db2 | |||
| b77341c804 | |||
| f69599fabb | |||
| 617aa0a5db | |||
| 86f5102158 | |||
| 900bb97459 | |||
| 86f99b9e1b | |||
| e57d75a3d7 | |||
| 375a73891c | |||
| ae4c155c6e | |||
| eedcfe3c56 | |||
| 345747a042 | |||
| 26e25bf9a8 | |||
| 8b12ed459f | |||
| 0041a79823 | |||
| b820a3214f | |||
| f93a4f5adb | |||
| 4503811202 | |||
| 8ec92d12fd | |||
| edc9a9ce60 | |||
| f58b6fc6c7 | |||
| d5b8ab3262 | |||
| 609ba6cf63 | |||
| b9c5a78d16 | |||
| a919998872 | |||
| f7fdd7634e | |||
| feb9d49c93 | |||
| d0c19745c3 | |||
| 3adc8592da | |||
| dcaec4f160 | |||
| b5340a3f3d | |||
| b3a2f141d6 | |||
| 86db8e39c6 | |||
| 749b0d6d79 | |||
| 0c7d259e7f | |||
| f9d6c0d648 | |||
| 3e82e773a9 | |||
| 04dbd8d0fd | |||
| 60d6cc4476 | |||
| 91a6df08b0 | |||
| 40aff87f88 | |||
| e19f7d29b4 | |||
| f27069f2d2 | |||
| aac395ccd2 | |||
| 422d92f0ea | |||
| d547e3632d | |||
| ef57854b81 | |||
| d7a29e6d81 | |||
| a68bc7be2e | |||
| 2ed89e4bb7 | |||
| 145f9d2b19 | |||
| 94c71b8b20 | |||
| 52071db172 | |||
| 66d2a63bcf | |||
| 53116c29c8 | |||
| d8fccdb01c | |||
| be77164d8d | |||
| 0889b3b0c2 | |||
| 67c88675da | |||
| 65a9fcf3ec | |||
| cb56102da7 | |||
| 2e28374fc9 | |||
| 9598e47162 | |||
| 40372c63ee | |||
| e24520254b | |||
| 43a5c09412 | |||
| 426dbd1718 | |||
| 8bae6a1063 | |||
| 8f7b28deed | |||
| 4026b6af9b | |||
| a5dae4669c | |||
| 6b1e11c314 | |||
| e2681b17a4 | |||
| da61e79f2e | |||
| 31fefd8eb8 | |||
| bcf78fec02 | |||
| 71528fd547 | |||
| 962e0d740a | |||
| 1262a2d8f3 | |||
| a8ba10aac6 | |||
| ff67d2eedd | |||
| a2c39aa2ed | |||
| a1a781bcf6 | |||
| 6d0559c6d9 | |||
| 330a811114 | |||
| ed695fab1b | |||
| a2d51807c7 | |||
| df0b6c3e2b | |||
| 636e0d0585 | |||
| 124daf1f51 | |||
| e8d0d7bc3a | |||
| f074dcc3b5 | |||
| 2eb4dfbdc9 | |||
| 2e4c3f43ee | |||
| be87ecec3e | |||
| 317be3b383 | |||
| ca5b641b96 | |||
| 031ad89851 |
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"permissions": {
|
||||
"allow": [
|
||||
"Bash(python3 -c \"import json; json.load\\(open\\(r'c:\\\\Users\\\\user\\\\Documents\\\\GitHub\\\\openpilot\\\\starpilot\\\\system\\\\the_pond\\\\assets\\\\components\\\\tools\\\\device_settings_layout.json'\\)\\)\")",
|
||||
"Bash(python3 -m py_compile \"c:\\\\Users\\\\user\\\\Documents\\\\GitHub\\\\openpilot\\\\starpilot\\\\controls\\\\lib\\\\starpilot_vcruise.py\")",
|
||||
"Bash(python3 -m py_compile \"c:\\\\Users\\\\user\\\\Documents\\\\GitHub\\\\openpilot\\\\selfdrive\\\\ui\\\\layouts\\\\settings\\\\starpilot\\\\longitudinal.py\")"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
name: "PR review"
|
||||
on:
|
||||
pull_request_target:
|
||||
types: [opened, reopened, synchronize, edited]
|
||||
|
||||
jobs:
|
||||
labeler:
|
||||
name: review
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: false
|
||||
|
||||
# Label PRs
|
||||
- uses: actions/labeler@v5.0.0
|
||||
with:
|
||||
dot: true
|
||||
configuration-path: .github/labeler.yaml
|
||||
|
||||
# Check PR target branch
|
||||
- name: check branch
|
||||
uses: Vankka/pr-target-branch-action@def32ec9d93514138d6ac0132ee62e120a72aed5
|
||||
if: github.repository == 'commaai/openpilot'
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
target: /^(?!master$).*/
|
||||
exclude: /commaai:.*/
|
||||
change-to: ${{ github.base_ref }}
|
||||
already-exists-action: close_this
|
||||
already-exists-comment: "Your PR should be made against the `master` branch"
|
||||
|
||||
# Welcome comment
|
||||
- name: "First timers PR"
|
||||
uses: actions/first-interaction@v1
|
||||
if: github.event.pull_request.head.repo.full_name != 'commaai/openpilot'
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
pr-message: |
|
||||
<!-- _(run_id **${{ github.run_id }}**)_ -->
|
||||
Thanks for contributing to openpilot! In order for us to review your PR as quickly as possible, check the following:
|
||||
* Convert your PR to a draft unless it's ready to review
|
||||
* Read the [contributing docs](https://github.com/commaai/openpilot/blob/master/docs/CONTRIBUTING.md)
|
||||
* Before marking as "ready for review", ensure:
|
||||
* the goal is clearly stated in the description
|
||||
* all the tests are passing
|
||||
* the change is [something we merge](https://github.com/commaai/openpilot/blob/master/docs/CONTRIBUTING.md#what-gets-merged)
|
||||
* include a route or your device' dongle ID if relevant
|
||||
@@ -1,37 +0,0 @@
|
||||
name: badges
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 * * * *'
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
BASE_IMAGE: openpilot-base
|
||||
DOCKER_REGISTRY: ghcr.io/commaai
|
||||
RUN: docker run --shm-size 2G -v $PWD:/tmp/openpilot -w /tmp/openpilot -e PYTHONPATH=/tmp/openpilot -e NUM_JOBS -e JOB_ID -e GITHUB_ACTION -e GITHUB_REF -e GITHUB_HEAD_REF -e GITHUB_SHA -e GITHUB_REPOSITORY -e GITHUB_RUN_ID -v $GITHUB_WORKSPACE/.ci_cache/scons_cache:/tmp/scons_cache -v $GITHUB_WORKSPACE/.ci_cache/comma_download_cache:/tmp/comma_download_cache -v $GITHUB_WORKSPACE/.ci_cache/openpilot_cache:/tmp/openpilot_cache $DOCKER_REGISTRY/$BASE_IMAGE:latest /bin/bash -c
|
||||
|
||||
jobs:
|
||||
badges:
|
||||
name: create badges
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository == 'commaai/openpilot'
|
||||
permissions:
|
||||
contents: write
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
- uses: ./.github/workflows/setup-with-retry
|
||||
- name: Push badges
|
||||
run: |
|
||||
${{ env.RUN }} "python3 selfdrive/ui/translations/create_badges.py"
|
||||
|
||||
rm .gitattributes
|
||||
|
||||
git checkout --orphan badges
|
||||
git rm -rf --cached .
|
||||
git config user.email "badge-researcher@comma.ai"
|
||||
git config user.name "Badge Researcher"
|
||||
|
||||
git add translation_badge.svg
|
||||
git commit -m "Add/Update badges"
|
||||
git push -f origin HEAD
|
||||
@@ -1,101 +0,0 @@
|
||||
name: weekly CI test report
|
||||
on:
|
||||
schedule:
|
||||
- cron: '37 9 * * 1' # 9:37AM UTC -> 2:37AM PST every monday
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
ci_runs:
|
||||
description: 'The amount of runs to trigger in CI test report'
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
env:
|
||||
CI_RUNS: ${{ github.event.inputs.ci_runs || '50' }}
|
||||
|
||||
jobs:
|
||||
setup:
|
||||
if: github.repository == 'commaai/openpilot'
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
ci_runs: ${{ steps.ci_runs_setup.outputs.matrix }}
|
||||
steps:
|
||||
- id: ci_runs_setup
|
||||
name: CI_RUNS=${{ env.CI_RUNS }}
|
||||
run: |
|
||||
matrix=$(python3 -c "import json; print(json.dumps({ 'run_number' : list(range(${{ env.CI_RUNS }})) }))")
|
||||
echo "matrix=$matrix" >> $GITHUB_OUTPUT
|
||||
|
||||
ci_matrix_run:
|
||||
needs: [ setup ]
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix: ${{fromJSON(needs.setup.outputs.ci_runs)}}
|
||||
uses: commaai/openpilot/.github/workflows/ci_weekly_run.yaml@master
|
||||
with:
|
||||
run_number: ${{ matrix.run_number }}
|
||||
|
||||
report:
|
||||
needs: [ci_matrix_run]
|
||||
runs-on: ubuntu-latest
|
||||
if: always() && github.repository == 'commaai/openpilot'
|
||||
steps:
|
||||
- name: Get job results
|
||||
uses: actions/github-script@v7
|
||||
id: get-job-results
|
||||
with:
|
||||
script: |
|
||||
const jobs = await github
|
||||
.paginate("GET /repos/{owner}/{repo}/actions/runs/{run_id}/attempts/{attempt}/jobs", {
|
||||
owner: "commaai",
|
||||
repo: "${{ github.event.repository.name }}",
|
||||
run_id: "${{ github.run_id }}",
|
||||
attempt: "${{ github.run_attempt }}",
|
||||
})
|
||||
var report = {}
|
||||
jobs.slice(1, jobs.length-1).forEach(job => {
|
||||
if (job.conclusion === "skipped") return;
|
||||
const jobName = job.name.split(" / ")[2];
|
||||
const runRegex = /\((.*?)\)/;
|
||||
const run = job.name.match(runRegex)[1];
|
||||
report[jobName] = report[jobName] || { successes: [], failures: [], canceled: [] };
|
||||
switch (job.conclusion) {
|
||||
case "success":
|
||||
report[jobName].successes.push({ "run_number": run, "link": job.html_url}); break;
|
||||
case "failure":
|
||||
report[jobName].failures.push({ "run_number": run, "link": job.html_url }); break;
|
||||
case "canceled":
|
||||
report[jobName].canceled.push({ "run_number": run, "link": job.html_url }); break;
|
||||
}
|
||||
});
|
||||
return JSON.stringify({"jobs": report});
|
||||
|
||||
- name: Add job results to summary
|
||||
env:
|
||||
JOB_RESULTS: ${{ fromJSON(steps.get-job-results.outputs.result) }}
|
||||
run: |
|
||||
cat <<EOF >> template.html
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th>Job</th>
|
||||
<th>✅ Passing</th>
|
||||
<th>❌ Failure Details</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for key in jobs.keys() %}<tr>
|
||||
<td>{% for i in range(5) %}{% if i+1 <= (5 * jobs[key]["successes"]|length // ${{ env.CI_RUNS }}) %}🟩{% else %}🟥{% endif %}{% endfor%}</td>
|
||||
<td>{{ key }}</td>
|
||||
<td>{{ 100 * jobs[key]["successes"]|length // ${{ env.CI_RUNS }} }}%</td>
|
||||
<td>{% if jobs[key]["failures"]|length > 0 %}<details>{% for failure in jobs[key]["failures"] %}<a href="{{ failure['link'] }}">Log for run #{{ failure['run_number'] }}</a><br>{% endfor %}</details>{% else %}{% endif %}</td>
|
||||
</td>
|
||||
</tr>{% endfor %}
|
||||
</table>
|
||||
EOF
|
||||
|
||||
pip install jinja2-cli
|
||||
echo $JOB_RESULTS | jinja2 template.html > report.html
|
||||
echo "# CI Test Report - ${{ env.CI_RUNS }} Runs" >> $GITHUB_STEP_SUMMARY
|
||||
cat report.html >> $GITHUB_STEP_SUMMARY
|
||||
@@ -1,17 +0,0 @@
|
||||
name: weekly CI test run
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
run_number:
|
||||
required: true
|
||||
type: string
|
||||
|
||||
concurrency:
|
||||
group: ci-run-${{ inputs.run_number }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
tests:
|
||||
uses: commaai/openpilot/.github/workflows/tests.yaml@master
|
||||
with:
|
||||
run_number: ${{ inputs.run_number }}
|
||||
@@ -0,0 +1,211 @@
|
||||
name: Compile Bluescreens
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
target_ref:
|
||||
description: "Branch to build and publish back to"
|
||||
type: string
|
||||
default: "bluescreensonly2"
|
||||
required: true
|
||||
jobs:
|
||||
description: "Optional parallel job count passed to ./build"
|
||||
type: string
|
||||
default: "12"
|
||||
required: false
|
||||
enable_scons_cache:
|
||||
description: "Enable SCons cache for faster incremental builds"
|
||||
type: boolean
|
||||
default: true
|
||||
required: false
|
||||
build_all:
|
||||
description: "Build everything"
|
||||
type: boolean
|
||||
default: true
|
||||
required: false
|
||||
build_ui:
|
||||
description: "Build UI (selfdrive/ui)"
|
||||
type: boolean
|
||||
default: true
|
||||
required: false
|
||||
build_params:
|
||||
description: "Build params Python extension"
|
||||
type: boolean
|
||||
default: false
|
||||
required: false
|
||||
build_cereal:
|
||||
description: "Build cereal messaging bridge"
|
||||
type: boolean
|
||||
default: false
|
||||
required: false
|
||||
build_panda:
|
||||
description: "Build panda firmware outputs"
|
||||
type: boolean
|
||||
default: true
|
||||
required: false
|
||||
build_modeld:
|
||||
description: "Build modeld Python extension"
|
||||
type: boolean
|
||||
default: false
|
||||
required: false
|
||||
build_controls:
|
||||
description: "Build controls solvers + locationd generated models"
|
||||
type: boolean
|
||||
default: false
|
||||
required: false
|
||||
|
||||
concurrency:
|
||||
group: compile-bluescreens-${{ inputs.target_ref }}
|
||||
cancel-in-progress: false
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
env:
|
||||
GIT_EMAIL: "168790843+firestar5683@users.noreply.github.com"
|
||||
GIT_NAME: "StarPilot Build Bot"
|
||||
TARGET_REF: ${{ inputs.target_ref }}
|
||||
|
||||
jobs:
|
||||
build_and_push:
|
||||
runs-on: [self-hosted, truenas, starpilot-build]
|
||||
timeout-minutes: 720
|
||||
steps:
|
||||
- name: Validate target branch
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
if [[ -z "${TARGET_REF}" ]]; then
|
||||
echo "target_ref is required." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ "${TARGET_REF}" == refs/heads/* ]]; then
|
||||
echo "BUILD_BRANCH=${TARGET_REF#refs/heads/}" >> "$GITHUB_ENV"
|
||||
else
|
||||
echo "BUILD_BRANCH=${TARGET_REF}" >> "$GITHUB_ENV"
|
||||
fi
|
||||
|
||||
- name: Checkout target branch
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ env.BUILD_BRANCH }}
|
||||
fetch-depth: 0
|
||||
submodules: recursive
|
||||
|
||||
- name: Configure git identity
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
git config user.name "$GIT_NAME"
|
||||
git config user.email "$GIT_EMAIL"
|
||||
git config http.postBuffer 104857600
|
||||
|
||||
- name: Ensure branch exists on origin
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
git ls-remote --exit-code --heads origin "refs/heads/${BUILD_BRANCH}" >/dev/null
|
||||
|
||||
- name: Link persistent sysroot
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
ls -ld /runner /runner/sysroots /runner/sysroots/default || true
|
||||
ls -ld /runner/sysroots/default/usr /runner/sysroots/default/usr/local /runner/sysroots/default/usr/local/lib || true
|
||||
rm -rf .comma_sysroot
|
||||
ln -s /runner/sysroots/default .comma_sysroot
|
||||
ls -ld .comma_sysroot .comma_sysroot/usr .comma_sysroot/usr/local .comma_sysroot/usr/local/lib
|
||||
|
||||
- name: Run Bluescreens build
|
||||
shell: bash
|
||||
env:
|
||||
SP_ENABLE_SCONS_CACHE: ${{ inputs.enable_scons_cache && '1' || '0' }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
RUNNER_HOST_ROOT="/mnt/Apparition/My_App_Data/starpilot-runner"
|
||||
if [[ "${GITHUB_WORKSPACE}" != /runner/_work/* ]]; then
|
||||
echo "Unexpected GITHUB_WORKSPACE: ${GITHUB_WORKSPACE}" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
WORKSPACE_REL="${GITHUB_WORKSPACE#/runner}"
|
||||
export COMMA_HOST_ROOT_DIR="${RUNNER_HOST_ROOT}${WORKSPACE_REL}"
|
||||
export COMMA_HOST_SYSROOT_DIR="${RUNNER_HOST_ROOT}/sysroots/default"
|
||||
export COMMA_HOST_CACHE_DIR="${RUNNER_HOST_ROOT}/cache/work"
|
||||
export COMMA_HOST_VENV_DIR="${RUNNER_HOST_ROOT}/cache/venv-linux-arm64"
|
||||
export COMMA_SYSROOT_DIR="/runner/sysroots/default"
|
||||
|
||||
echo "GITHUB_WORKSPACE=${GITHUB_WORKSPACE}"
|
||||
echo "COMMA_HOST_ROOT_DIR=${COMMA_HOST_ROOT_DIR}"
|
||||
echo "COMMA_HOST_SYSROOT_DIR=${COMMA_HOST_SYSROOT_DIR}"
|
||||
echo "COMMA_HOST_CACHE_DIR=${COMMA_HOST_CACHE_DIR}"
|
||||
echo "COMMA_HOST_VENV_DIR=${COMMA_HOST_VENV_DIR}"
|
||||
|
||||
JOBS="${{ inputs.jobs }}"
|
||||
|
||||
if [[ "${{ inputs.build_all }}" == "true" ]]; then
|
||||
if [[ -n "${JOBS}" ]]; then
|
||||
./build "${JOBS}"
|
||||
else
|
||||
./build
|
||||
fi
|
||||
exit 0
|
||||
fi
|
||||
|
||||
TARGETS=()
|
||||
[[ "${{ inputs.build_ui }}" == "true" ]] && TARGETS+=("selfdrive/ui/ui")
|
||||
[[ "${{ inputs.build_params }}" == "true" ]] && TARGETS+=("common/params_pyx.so")
|
||||
[[ "${{ inputs.build_cereal }}" == "true" ]] && TARGETS+=("cereal/messaging/bridge")
|
||||
if [[ "${{ inputs.build_panda }}" == "true" ]]; then
|
||||
TARGETS+=(
|
||||
"panda/board/obj/panda.bin.signed"
|
||||
"panda/board/obj/panda_h7.bin.signed"
|
||||
"panda/board/obj/panda_remote.bin.signed"
|
||||
"panda/board/obj/panda_h7_remote.bin.signed"
|
||||
"panda/board/obj/panda_jungle_h7.bin.signed"
|
||||
"panda/board/obj/body_h7.bin.signed"
|
||||
)
|
||||
fi
|
||||
[[ "${{ inputs.build_modeld }}" == "true" ]] && TARGETS+=("selfdrive/modeld/models/commonmodel_pyx.so")
|
||||
if [[ "${{ inputs.build_controls }}" == "true" ]]; then
|
||||
TARGETS+=(
|
||||
"selfdrive/controls/lib/lateral_mpc_lib/c_generated_code/libacados_ocp_solver_lat.so"
|
||||
"selfdrive/controls/lib/longitudinal_mpc_lib/c_generated_code/libacados_ocp_solver_long.so"
|
||||
"selfdrive/locationd/models/generated/car.cpp"
|
||||
"selfdrive/locationd/models/generated/pose.cpp"
|
||||
)
|
||||
fi
|
||||
|
||||
if [[ ${#TARGETS[@]} -eq 0 ]]; then
|
||||
echo "No partial targets selected; defaulting to UI + panda"
|
||||
TARGETS=("selfdrive/ui/ui" "panda/board/obj/panda.bin.signed")
|
||||
fi
|
||||
|
||||
echo "Running partial build for targets: ${TARGETS[*]}"
|
||||
if [[ -n "${JOBS}" ]]; then
|
||||
./build "${JOBS}" "${TARGETS[@]}"
|
||||
else
|
||||
./build "$(nproc)" "${TARGETS[@]}"
|
||||
fi
|
||||
|
||||
- name: Commit build output
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
if [[ -z "$(git status --porcelain --untracked-files=no)" ]]; then
|
||||
echo "No build output changes detected."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
git add -u
|
||||
git commit -m "build"
|
||||
|
||||
- name: Push build commit
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
git push origin "HEAD:refs/heads/${BUILD_BRANCH}"
|
||||
@@ -3,237 +3,113 @@ name: Compile StarPilot
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
not_vetted:
|
||||
description: "This branch is not vetted"
|
||||
type: boolean
|
||||
default: false
|
||||
required: false
|
||||
publish_custom_branch:
|
||||
description: "Push to custom branch:"
|
||||
target_ref:
|
||||
description: "Branch to build and publish back to"
|
||||
type: string
|
||||
default: ""
|
||||
required: false
|
||||
publish_starpilot:
|
||||
description: "Push to StarPilot"
|
||||
type: boolean
|
||||
default: false
|
||||
required: false
|
||||
publish_staging:
|
||||
description: "Push to StarPilot-Staging"
|
||||
type: boolean
|
||||
default: false
|
||||
required: false
|
||||
publish_testing:
|
||||
description: "Push to StarPilot-Testing"
|
||||
type: boolean
|
||||
default: false
|
||||
required: false
|
||||
runner:
|
||||
description: "Select runner"
|
||||
type: choice
|
||||
options:
|
||||
- c3
|
||||
- c3x
|
||||
default: "c3"
|
||||
default: "master"
|
||||
required: true
|
||||
update_translations:
|
||||
description: "Update missing/outdated translations"
|
||||
type: boolean
|
||||
default: false
|
||||
required: false
|
||||
vet_existing_translations:
|
||||
description: "Vet existing translations"
|
||||
type: boolean
|
||||
default: false
|
||||
jobs:
|
||||
description: "Optional parallel job count passed to ./build"
|
||||
type: string
|
||||
default: "12"
|
||||
required: false
|
||||
|
||||
concurrency:
|
||||
group: compile-starpilot-${{ inputs.target_ref }}
|
||||
cancel-in-progress: false
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
env:
|
||||
BASE_DIR: ${{ github.workspace }}
|
||||
BUILD_DIR: "/data/openpilot"
|
||||
CUSTOM_BRANCH: ${{ inputs.publish_custom_branch }}
|
||||
GIT_EMAIL: "91348155+FrogAi@users.noreply.github.com"
|
||||
GIT_NAME: "James"
|
||||
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
|
||||
GIT_EMAIL: "168790843+firestar5683@users.noreply.github.com"
|
||||
GIT_NAME: "StarPilot Build Bot"
|
||||
TARGET_REF: ${{ inputs.target_ref }}
|
||||
|
||||
jobs:
|
||||
get_branch:
|
||||
runs-on: [self-hosted, "${{ inputs.runner }}"]
|
||||
outputs:
|
||||
branch: ${{ steps.get_branch.outputs.branch }}
|
||||
python_version: ${{ steps.get_python_version.outputs.python_version }}
|
||||
build_and_push:
|
||||
runs-on: [self-hosted, truenas, starpilot-build]
|
||||
timeout-minutes: 720
|
||||
steps:
|
||||
- name: Get Current Branch
|
||||
id: get_branch
|
||||
- name: Validate target branch
|
||||
shell: bash
|
||||
run: |
|
||||
cd "$BUILD_DIR"
|
||||
echo "branch=$(git rev-parse --abbrev-ref HEAD)" >> "$GITHUB_OUTPUT"
|
||||
set -euo pipefail
|
||||
|
||||
- name: Get Python Version
|
||||
id: get_python_version
|
||||
run: |
|
||||
echo "python_version=$(tr -d '[:space:]' < "$BUILD_DIR/.python-version")" >> $GITHUB_OUTPUT
|
||||
if [[ -z "${TARGET_REF}" ]]; then
|
||||
echo "target_ref is required." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
translate:
|
||||
needs: get_branch
|
||||
if: inputs.update_translations
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Configure Git Identity
|
||||
run: |
|
||||
git config --global user.name "$GIT_NAME"
|
||||
git config --global user.email "$GIT_EMAIL"
|
||||
if [[ "${TARGET_REF}" == refs/heads/* ]]; then
|
||||
echo "BUILD_BRANCH=${TARGET_REF#refs/heads/}" >> "$GITHUB_ENV"
|
||||
else
|
||||
echo "BUILD_BRANCH=${TARGET_REF}" >> "$GITHUB_ENV"
|
||||
fi
|
||||
|
||||
- name: Checkout Required Files
|
||||
- name: Checkout target branch
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ needs.get_branch.outputs.branch }}
|
||||
sparse-checkout: |
|
||||
starpilot/ui/
|
||||
selfdrive/controls/lib/alerts_offroad.json
|
||||
selfdrive/ui/
|
||||
selfdrive/ui/translations/
|
||||
selfdrive/ui/translations/auto_translate.py
|
||||
selfdrive/ui/update_translations.py
|
||||
ref: ${{ env.BUILD_BRANCH }}
|
||||
fetch-depth: 0
|
||||
submodules: recursive
|
||||
|
||||
- name: Set Up Python
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
cache: "pip"
|
||||
python-version: ${{ needs.get_branch.outputs.python_version }}
|
||||
|
||||
- name: Install Dependencies
|
||||
- name: Configure git identity
|
||||
shell: bash
|
||||
run: |
|
||||
pip install requests
|
||||
sudo apt-get update && sudo apt-get install -y --no-install-recommends qttools5-dev-tools
|
||||
set -euo pipefail
|
||||
git config user.name "$GIT_NAME"
|
||||
git config user.email "$GIT_EMAIL"
|
||||
git config http.postBuffer 104857600
|
||||
|
||||
- name: Update Translations
|
||||
- name: Ensure branch exists on origin
|
||||
shell: bash
|
||||
run: |
|
||||
python selfdrive/ui/update_translations.py --vanish
|
||||
set -euo pipefail
|
||||
git ls-remote --exit-code --heads origin "refs/heads/${BUILD_BRANCH}" >/dev/null
|
||||
|
||||
- name: Update Missing Translations
|
||||
continue-on-error: true
|
||||
timeout-minutes: 300
|
||||
- name: Link persistent sysroot
|
||||
shell: bash
|
||||
run: |
|
||||
python selfdrive/ui/translations/auto_translate.py --all-files
|
||||
set -euo pipefail
|
||||
ls -ld /runner /runner/sysroots /runner/sysroots/default || true
|
||||
ls -ld /runner/sysroots/default/usr /runner/sysroots/default/usr/local /runner/sysroots/default/usr/local/lib || true
|
||||
rm -rf .comma_sysroot
|
||||
ln -s /runner/sysroots/default .comma_sysroot
|
||||
ls -ld .comma_sysroot .comma_sysroot/usr .comma_sysroot/usr/local .comma_sysroot/usr/local/lib
|
||||
|
||||
- name: Vet Existing Translations
|
||||
if: inputs.vet_existing_translations
|
||||
continue-on-error: true
|
||||
timeout-minutes: 300
|
||||
- name: Run StarPilot device build
|
||||
shell: bash
|
||||
run: |
|
||||
python selfdrive/ui/translations/auto_translate.py --all-files --vet-translations
|
||||
set -euo pipefail
|
||||
|
||||
- name: Commit and Push Translations
|
||||
export COMMA_HOST_ROOT_DIR="/mnt/Apparition/My_App_Data/starpilot-runner/_work/${GITHUB_REPOSITORY#*/}/${GITHUB_REPOSITORY#*/}"
|
||||
export COMMA_HOST_SYSROOT_DIR="/mnt/Apparition/My_App_Data/starpilot-runner/sysroots/default"
|
||||
export COMMA_HOST_CACHE_DIR="/mnt/Apparition/My_App_Data/starpilot-runner/cache/work"
|
||||
export COMMA_HOST_VENV_DIR="/mnt/Apparition/My_App_Data/starpilot-runner/cache/venv-linux-arm64"
|
||||
export COMMA_SYSROOT_DIR="/runner/sysroots/default"
|
||||
|
||||
if [[ -n "${{ inputs.jobs }}" ]]; then
|
||||
./build "${{ inputs.jobs }}"
|
||||
else
|
||||
./build
|
||||
fi
|
||||
|
||||
- name: Commit build output
|
||||
shell: bash
|
||||
run: |
|
||||
if git diff --quiet selfdrive/ui/translations/*.ts; then
|
||||
echo "No translation updates detected."
|
||||
set -euo pipefail
|
||||
|
||||
if [[ -z "$(git status --porcelain --untracked-files=no)" ]]; then
|
||||
echo "No build output changes detected."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
git fetch --unshallow origin "${{ needs.get_branch.outputs.branch }}"
|
||||
git checkout "${{ needs.get_branch.outputs.branch }}"
|
||||
git add selfdrive/ui/translations/*.ts
|
||||
git commit --amend --no-edit
|
||||
git push --force origin "${{ needs.get_branch.outputs.branch }}"
|
||||
git add -u
|
||||
git commit -m "build"
|
||||
|
||||
build_and_push:
|
||||
needs: [get_branch, translate]
|
||||
if: ${{ !failure() && !cancelled() && needs.get_branch.result == 'success' }}
|
||||
runs-on: [self-hosted, "${{ inputs.runner }}"]
|
||||
permissions:
|
||||
contents: write
|
||||
defaults:
|
||||
run:
|
||||
working-directory: ${{ env.BUILD_DIR }}
|
||||
steps:
|
||||
- name: Configure Git
|
||||
- name: Push build commit
|
||||
shell: bash
|
||||
run: |
|
||||
git config http.postBuffer 104857600
|
||||
git config user.name "$GIT_NAME"
|
||||
git config user.email "$GIT_EMAIL"
|
||||
git remote set-url origin "https://${{ secrets.PERSONAL_ACCESS_TOKEN }}@github.com/FrogAi/StarPilot.git"
|
||||
|
||||
- name: Sync Translation Updates
|
||||
if: inputs.update_translations
|
||||
run: |
|
||||
git fetch origin "${{ needs.get_branch.outputs.branch }}"
|
||||
git reset --hard FETCH_HEAD
|
||||
|
||||
- name: Take Ownership of Build Directory
|
||||
run: |
|
||||
sudo chown -R $(whoami):$(whoami) .
|
||||
|
||||
- name: Clean Build Artifacts
|
||||
run: |
|
||||
find . -name "matlab.*.md" -delete
|
||||
|
||||
find . -type d \( -iname "debug" -o -iname "test" -o -iname "tests" -o -name '__pycache__' \) -exec rm -rf {} +
|
||||
|
||||
find . -type f \( \
|
||||
-name '*.a' -o \
|
||||
-name '*.o' -o \
|
||||
-name '*.onnx' -o \
|
||||
-name '*.os' -o \
|
||||
-name '*.pyc' -o \
|
||||
-name 'moc_*' \
|
||||
\) -delete
|
||||
|
||||
find .github -mindepth 1 -maxdepth 1 ! -name 'workflows' -exec rm -rf {} +
|
||||
find .github/workflows -mindepth 1 ! \( \
|
||||
-type f \( \
|
||||
-name 'compile_starpilot.yaml' -o \
|
||||
-name 'review_pull_request.yaml' -o \
|
||||
-name 'schedule_update.yaml' -o \
|
||||
-name 'update_pr_branch.yaml' -o \
|
||||
-name 'update_release_branch.yaml' -o \
|
||||
-name 'update_tinygrad.yaml' \
|
||||
\) \
|
||||
\) -exec rm -rf {} +
|
||||
|
||||
find panda/board -type f \
|
||||
! -name '__init__.py' \
|
||||
! -name 'bootstub.panda.bin' \
|
||||
! -name 'bootstub.panda_h7.bin' \
|
||||
! -name 'panda.bin.signed' \
|
||||
! -name 'panda_h7.bin.signed' \
|
||||
-delete
|
||||
|
||||
find third_party/ -name '*Darwin*' -exec rm -rf {} +
|
||||
find third_party/ -name '*x86*' -exec rm -rf {} +
|
||||
|
||||
rm -f .gitignore .gitmodules .gitattributes .lfsconfig .overlay_init
|
||||
|
||||
rm -rf .sconsign.dblite .vscode/ Jenkinsfile release/ scripts/ site_scons/ teleoprtc_repo/
|
||||
|
||||
find . -type d -empty ! -path "./.git*" -delete
|
||||
|
||||
touch prebuilt
|
||||
[ "${{ inputs.not_vetted }}" = "true" ] && touch not_vetted || true
|
||||
|
||||
- name: Add Update Date File
|
||||
if: inputs.publish_staging
|
||||
continue-on-error: true
|
||||
run: |
|
||||
curl -fLsS https://raw.githubusercontent.com/FrogAi/StarPilot/StarPilot-Staging/.github/update_date -o .github/update_date || echo "No update_date found, skipping..."
|
||||
|
||||
- name: Commit and Push Build
|
||||
run: |
|
||||
git add -f .
|
||||
git commit -m "Compile StarPilot"
|
||||
git push --force origin HEAD
|
||||
|
||||
if [ "${{ inputs.publish_starpilot }}" = "true" ]; then
|
||||
git push --force origin HEAD:StarPilot
|
||||
fi
|
||||
|
||||
if [ "${{ inputs.publish_staging }}" = "true" ]; then
|
||||
git push --force origin HEAD:StarPilot-Staging
|
||||
fi
|
||||
|
||||
if [ "${{ inputs.publish_testing }}" = "true" ]; then
|
||||
git push --force origin HEAD:StarPilot-Testing
|
||||
fi
|
||||
|
||||
if [ -n "$CUSTOM_BRANCH" ]; then
|
||||
git push --force origin HEAD:"$CUSTOM_BRANCH"
|
||||
fi
|
||||
set -euo pipefail
|
||||
git push origin "HEAD:refs/heads/${BUILD_BRANCH}"
|
||||
|
||||
@@ -1,65 +0,0 @@
|
||||
name: docs
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
pull_request:
|
||||
workflow_call:
|
||||
inputs:
|
||||
run_number:
|
||||
default: '1'
|
||||
required: true
|
||||
type: string
|
||||
concurrency:
|
||||
group: docs-tests-ci-run-${{ inputs.run_number }}-${{ github.event_name == 'push' && github.ref == 'refs/heads/master' && github.run_id || github.head_ref || github.ref }}-${{ github.workflow }}-${{ github.event_name }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
docs:
|
||||
name: build docs
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: commaai/timeout@v1
|
||||
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
|
||||
# Build
|
||||
- name: Build docs
|
||||
run: |
|
||||
# TODO: can we install just the "docs" dependency group without the normal deps?
|
||||
pip install mkdocs
|
||||
mkdocs build
|
||||
|
||||
# Push to docs.comma.ai
|
||||
- uses: actions/checkout@v4
|
||||
if: github.ref == 'refs/heads/master' && github.repository == 'commaai/openpilot'
|
||||
with:
|
||||
path: openpilot-docs
|
||||
ssh-key: ${{ secrets.OPENPILOT_DOCS_KEY }}
|
||||
repository: commaai/openpilot-docs
|
||||
- name: Push
|
||||
if: github.ref == 'refs/heads/master' && github.repository == 'commaai/openpilot'
|
||||
run: |
|
||||
set -x
|
||||
|
||||
source release/identity.sh
|
||||
|
||||
cd openpilot-docs
|
||||
git checkout --orphan tmp
|
||||
git rm -rf .
|
||||
|
||||
# copy over docs
|
||||
cp -r ../docs_site/ docs/
|
||||
|
||||
# GitHub pages config
|
||||
touch docs/.nojekyll
|
||||
echo -n docs.comma.ai > docs/CNAME
|
||||
|
||||
git add -f .
|
||||
git commit -m "build docs"
|
||||
|
||||
# docs live in different repo to not bloat openpilot's full clone size
|
||||
git push -f origin tmp:gh-pages
|
||||
@@ -1,59 +0,0 @@
|
||||
name: jenkins scan
|
||||
|
||||
on:
|
||||
issue_comment:
|
||||
types: [created, edited]
|
||||
|
||||
jobs:
|
||||
# TODO: gc old branches in a separate job in this workflow
|
||||
scan-comments:
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ github.event.issue.pull_request }}
|
||||
permissions:
|
||||
contents: write
|
||||
issues: write
|
||||
steps:
|
||||
- name: Check for trigger phrase
|
||||
id: check_comment
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
const triggerPhrase = "trigger-jenkins";
|
||||
const comment = context.payload.comment.body;
|
||||
const commenter = context.payload.comment.user.login;
|
||||
|
||||
const { data: permissions } = await github.rest.repos.getCollaboratorPermissionLevel({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
username: commenter
|
||||
});
|
||||
|
||||
const hasWriteAccess = permissions.permission === 'write' || permissions.permission === 'admin';
|
||||
|
||||
return (hasWriteAccess && comment.includes(triggerPhrase));
|
||||
result-encoding: json
|
||||
|
||||
- name: Checkout repository
|
||||
if: steps.check_comment.outputs.result == 'true'
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: refs/pull/${{ github.event.issue.number }}/head
|
||||
|
||||
- name: Push to tmp-jenkins branch
|
||||
if: steps.check_comment.outputs.result == 'true'
|
||||
run: |
|
||||
git config --global user.name "github-actions[bot]"
|
||||
git config --global user.email "github-actions[bot]@users.noreply.github.com"
|
||||
git checkout -b tmp-jenkins-${{ github.event.issue.number }}
|
||||
GIT_LFS_SKIP_PUSH=1 git push -f origin tmp-jenkins-${{ github.event.issue.number }}
|
||||
|
||||
- name: Delete trigger comment
|
||||
if: steps.check_comment.outputs.result == 'true' && always()
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
await github.rest.issues.deleteComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
comment_id: context.payload.comment.id,
|
||||
});
|
||||
@@ -1,151 +0,0 @@
|
||||
name: "mici raylib ui preview"
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
pull_request_target:
|
||||
types: [assigned, opened, synchronize, reopened, edited]
|
||||
branches:
|
||||
- 'master'
|
||||
paths:
|
||||
- 'selfdrive/assets/**'
|
||||
- 'selfdrive/ui/**'
|
||||
- 'system/ui/**'
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
UI_JOB_NAME: "Create mici raylib UI Report"
|
||||
REPORT_NAME: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' && 'master' || github.event.number }}
|
||||
SHA: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' && github.sha || github.event.pull_request.head.sha }}
|
||||
BRANCH_NAME: "openpilot/pr-${{ github.event.number }}-mici-raylib-ui"
|
||||
MASTER_BRANCH_NAME: "openpilot_master_ui_mici_raylib"
|
||||
# All report files are pushed here
|
||||
REPORT_FILES_BRANCH_NAME: "mici-raylib-ui-reports"
|
||||
|
||||
jobs:
|
||||
preview:
|
||||
if: github.repository == 'commaai/openpilot'
|
||||
name: preview
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 20
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
actions: read
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
|
||||
- name: Waiting for ui generation to end
|
||||
uses: lewagon/wait-on-check-action@v1.3.4
|
||||
with:
|
||||
ref: ${{ env.SHA }}
|
||||
check-name: ${{ env.UI_JOB_NAME }}
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
allowed-conclusions: success
|
||||
wait-interval: 20
|
||||
|
||||
- name: Getting workflow run ID
|
||||
id: get_run_id
|
||||
run: |
|
||||
echo "run_id=$(curl https://api.github.com/repos/${{ github.repository }}/commits/${{ env.SHA }}/check-runs | jq -r '.check_runs[] | select(.name == "${{ env.UI_JOB_NAME }}") | .html_url | capture("(?<number>[0-9]+)") | .number')" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Getting proposed ui # filename: pr_ui/mici_ui_replay.mp4
|
||||
id: download-artifact
|
||||
uses: dawidd6/action-download-artifact@v6
|
||||
with:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
run_id: ${{ steps.get_run_id.outputs.run_id }}
|
||||
search_artifacts: true
|
||||
name: mici-raylib-report-1-${{ env.REPORT_NAME }}
|
||||
path: ${{ github.workspace }}/pr_ui
|
||||
|
||||
- name: Getting master ui # filename: master_ui_raylib/mici_ui_replay.mp4
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: commaai/ci-artifacts
|
||||
ssh-key: ${{ secrets.CI_ARTIFACTS_DEPLOY_KEY }}
|
||||
path: ${{ github.workspace }}/master_ui_raylib
|
||||
ref: ${{ env.MASTER_BRANCH_NAME }}
|
||||
|
||||
- name: Saving new master ui
|
||||
if: github.ref == 'refs/heads/master' && github.event_name == 'push'
|
||||
working-directory: ${{ github.workspace }}/master_ui_raylib
|
||||
run: |
|
||||
git checkout --orphan=new_master_ui_mici_raylib
|
||||
git rm -rf *
|
||||
git branch -D ${{ env.MASTER_BRANCH_NAME }}
|
||||
git branch -m ${{ env.MASTER_BRANCH_NAME }}
|
||||
git config user.name "GitHub Actions Bot"
|
||||
git config user.email "<>"
|
||||
mv ${{ github.workspace }}/pr_ui/* .
|
||||
git add .
|
||||
git commit -m "mici raylib video for commit ${{ env.SHA }}"
|
||||
git push origin ${{ env.MASTER_BRANCH_NAME }} --force
|
||||
|
||||
- name: Setup FFmpeg
|
||||
uses: AnimMouse/setup-ffmpeg@ae28d57dabbb148eff63170b6bf7f2b60062cbae
|
||||
|
||||
- name: Finding diff
|
||||
if: github.event_name == 'pull_request_target'
|
||||
id: find_diff
|
||||
run: |
|
||||
# Find the video file from PR
|
||||
pr_video="${{ github.workspace }}/pr_ui/mici_ui_replay_proposed.mp4"
|
||||
mv "${{ github.workspace }}/pr_ui/mici_ui_replay.mp4" "$pr_video"
|
||||
|
||||
master_video="${{ github.workspace }}/pr_ui/mici_ui_replay_master.mp4"
|
||||
mv "${{ github.workspace }}/master_ui_raylib/mici_ui_replay.mp4" "$master_video"
|
||||
|
||||
# Run report
|
||||
export PYTHONPATH=${{ github.workspace }}
|
||||
baseurl="https://github.com/commaai/ci-artifacts/raw/refs/heads/${{ env.BRANCH_NAME }}"
|
||||
diff_exit_code=0
|
||||
python3 ${{ github.workspace }}/selfdrive/ui/tests/diff/diff.py "${{ github.workspace }}/pr_ui/mici_ui_replay_master.mp4" "${{ github.workspace }}/pr_ui/mici_ui_replay_proposed.mp4" "diff.html" --basedir "$baseurl" --no-open || diff_exit_code=$?
|
||||
|
||||
# Copy diff report files
|
||||
cp ${{ github.workspace }}/selfdrive/ui/tests/diff/report/diff.html ${{ github.workspace }}/pr_ui/
|
||||
cp ${{ github.workspace }}/selfdrive/ui/tests/diff/report/diff.mp4 ${{ github.workspace }}/pr_ui/
|
||||
|
||||
REPORT_URL="https://commaai.github.io/ci-artifacts/diff_pr_${{ github.event.number }}.html"
|
||||
if [ $diff_exit_code -eq 0 ]; then
|
||||
DIFF="✅ Videos are identical! [View Diff Report]($REPORT_URL)"
|
||||
else
|
||||
DIFF="❌ <strong>Videos differ!</strong> [View Diff Report]($REPORT_URL)"
|
||||
fi
|
||||
echo "DIFF=$DIFF" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Saving proposed ui
|
||||
if: github.event_name == 'pull_request_target'
|
||||
working-directory: ${{ github.workspace }}/master_ui_raylib
|
||||
run: |
|
||||
# Overwrite PR branch w/ proposed ui, and master ui at this point in time for future reference
|
||||
git config user.name "GitHub Actions Bot"
|
||||
git config user.email "<>"
|
||||
git checkout --orphan=${{ env.BRANCH_NAME }}
|
||||
git rm -rf *
|
||||
mv ${{ github.workspace }}/pr_ui/* .
|
||||
git add .
|
||||
git commit -m "mici raylib video for PR #${{ github.event.number }}"
|
||||
git push origin ${{ env.BRANCH_NAME }} --force
|
||||
|
||||
# Append diff report to report files branch
|
||||
git fetch origin ${{ env.REPORT_FILES_BRANCH_NAME }}
|
||||
git checkout ${{ env.REPORT_FILES_BRANCH_NAME }}
|
||||
cp ${{ github.workspace }}/selfdrive/ui/tests/diff/report/diff.html diff_pr_${{ github.event.number }}.html
|
||||
git add diff_pr_${{ github.event.number }}.html
|
||||
git commit -m "mici raylib ui diff report for PR #${{ github.event.number }}" || echo "No changes to commit"
|
||||
git push origin ${{ env.REPORT_FILES_BRANCH_NAME }}
|
||||
|
||||
- name: Comment Video on PR
|
||||
if: github.event_name == 'pull_request_target'
|
||||
uses: thollander/actions-comment-pull-request@v2
|
||||
with:
|
||||
message: |
|
||||
<!-- _(run_id_video_mici_raylib **${{ github.run_id }}**)_ -->
|
||||
## mici raylib UI Preview
|
||||
${{ steps.find_diff.outputs.DIFF }}
|
||||
comment_tag: run_id_video_mici_raylib
|
||||
pr_number: ${{ github.event.number }}
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
@@ -1,42 +0,0 @@
|
||||
name: "model review"
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, reopened, synchronize]
|
||||
paths:
|
||||
- 'selfdrive/modeld/models/*.onnx'
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
comment:
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository == 'commaai/openpilot'
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Checkout master
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: master
|
||||
path: base
|
||||
- run: git lfs pull
|
||||
- run: cd base && git lfs pull
|
||||
|
||||
- run: pip install onnx
|
||||
|
||||
- name: scripts/reporter.py
|
||||
id: report
|
||||
run: |
|
||||
echo "content<<EOF" >> $GITHUB_OUTPUT
|
||||
echo "## Model Review" >> $GITHUB_OUTPUT
|
||||
MASTER_PATH=${{ github.workspace }}/base python scripts/reporter.py >> $GITHUB_OUTPUT
|
||||
echo "EOF" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Post model report comment
|
||||
uses: marocchino/sticky-pull-request-comment@baa7203ed60924babbe5dcd0ac8eae3b66ec5e16
|
||||
with:
|
||||
header: model-review
|
||||
message: ${{ steps.report.outputs.content }}
|
||||
@@ -1,39 +0,0 @@
|
||||
name: prebuilt
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 * * * *'
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
DOCKER_LOGIN: docker login ghcr.io -u ${{ github.actor }} -p ${{ secrets.GITHUB_TOKEN }}
|
||||
BUILD: selfdrive/test/docker_build.sh prebuilt
|
||||
|
||||
jobs:
|
||||
build_prebuilt:
|
||||
name: build prebuilt
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository == 'commaai/openpilot'
|
||||
env:
|
||||
PUSH_IMAGE: true
|
||||
permissions:
|
||||
checks: read
|
||||
contents: read
|
||||
packages: write
|
||||
steps:
|
||||
- name: Wait for green check mark
|
||||
if: ${{ github.event_name != 'workflow_dispatch' }}
|
||||
uses: lewagon/wait-on-check-action@ccfb013c15c8afb7bf2b7c028fb74dc5a068cccc
|
||||
with:
|
||||
ref: master
|
||||
wait-interval: 30
|
||||
running-workflow-name: 'build prebuilt'
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
check-regexp: ^((?!.*(build master-ci).*).)*$
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
- run: git lfs pull
|
||||
- name: Build and Push docker image
|
||||
run: |
|
||||
$DOCKER_LOGIN
|
||||
eval "$BUILD"
|
||||
@@ -0,0 +1,35 @@
|
||||
name: Publish TrueNAS Runner Image
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
image_tag:
|
||||
description: "Container tag to publish to GHCR"
|
||||
type: string
|
||||
default: "latest"
|
||||
required: true
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
|
||||
jobs:
|
||||
publish:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Log in to GHCR
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Build and push runner image
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: .
|
||||
file: tools/truenas_github_runner/Dockerfile
|
||||
push: true
|
||||
tags: ghcr.io/${{ github.repository_owner }}/starpilot-truenas-runner:${{ inputs.image_tag }}
|
||||
@@ -1,175 +0,0 @@
|
||||
name: "raylib ui preview"
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
pull_request_target:
|
||||
types: [assigned, opened, synchronize, reopened, edited]
|
||||
branches:
|
||||
- 'master'
|
||||
paths:
|
||||
- 'selfdrive/assets/**'
|
||||
- 'selfdrive/ui/**'
|
||||
- 'system/ui/**'
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
UI_JOB_NAME: "Create raylib UI Report"
|
||||
REPORT_NAME: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' && 'master' || github.event.number }}
|
||||
SHA: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' && github.sha || github.event.pull_request.head.sha }}
|
||||
BRANCH_NAME: "openpilot/pr-${{ github.event.number }}-raylib-ui"
|
||||
|
||||
jobs:
|
||||
preview:
|
||||
if: github.repository == 'commaai/openpilot'
|
||||
name: preview
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 20
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
actions: read
|
||||
steps:
|
||||
- name: Waiting for ui generation to start
|
||||
run: sleep 30
|
||||
|
||||
- name: Waiting for ui generation to end
|
||||
uses: lewagon/wait-on-check-action@v1.3.4
|
||||
with:
|
||||
ref: ${{ env.SHA }}
|
||||
check-name: ${{ env.UI_JOB_NAME }}
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
allowed-conclusions: success
|
||||
wait-interval: 20
|
||||
|
||||
- name: Getting workflow run ID
|
||||
id: get_run_id
|
||||
run: |
|
||||
echo "run_id=$(curl https://api.github.com/repos/${{ github.repository }}/commits/${{ env.SHA }}/check-runs | jq -r '.check_runs[] | select(.name == "${{ env.UI_JOB_NAME }}") | .html_url | capture("(?<number>[0-9]+)") | .number')" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Getting proposed ui
|
||||
id: download-artifact
|
||||
uses: dawidd6/action-download-artifact@v6
|
||||
with:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
run_id: ${{ steps.get_run_id.outputs.run_id }}
|
||||
search_artifacts: true
|
||||
name: raylib-report-1-${{ env.REPORT_NAME }}
|
||||
path: ${{ github.workspace }}/pr_ui
|
||||
|
||||
- name: Getting master ui
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: commaai/ci-artifacts
|
||||
ssh-key: ${{ secrets.CI_ARTIFACTS_DEPLOY_KEY }}
|
||||
path: ${{ github.workspace }}/master_ui_raylib
|
||||
ref: openpilot_master_ui_raylib
|
||||
|
||||
- name: Saving new master ui
|
||||
if: github.ref == 'refs/heads/master' && github.event_name == 'push'
|
||||
working-directory: ${{ github.workspace }}/master_ui_raylib
|
||||
run: |
|
||||
git checkout --orphan=new_master_ui_raylib
|
||||
git rm -rf *
|
||||
git branch -D openpilot_master_ui_raylib
|
||||
git branch -m openpilot_master_ui_raylib
|
||||
git config user.name "GitHub Actions Bot"
|
||||
git config user.email "<>"
|
||||
mv ${{ github.workspace }}/pr_ui/*.png .
|
||||
git add .
|
||||
git commit -m "raylib screenshots for commit ${{ env.SHA }}"
|
||||
git push origin openpilot_master_ui_raylib --force
|
||||
|
||||
- name: Finding diff
|
||||
if: github.event_name == 'pull_request_target'
|
||||
id: find_diff
|
||||
run: >-
|
||||
sudo apt-get update && sudo apt-get install -y imagemagick
|
||||
|
||||
scenes=$(find ${{ github.workspace }}/pr_ui/*.png -type f -printf "%f\n" | cut -d '.' -f 1 | grep -v 'pair_device')
|
||||
A=($scenes)
|
||||
|
||||
DIFF=""
|
||||
TABLE="<details><summary>All Screenshots</summary>"
|
||||
TABLE="${TABLE}<table>"
|
||||
|
||||
for ((i=0; i<${#A[*]}; i=i+1));
|
||||
do
|
||||
# Check if the master file exists
|
||||
if [ ! -f "${{ github.workspace }}/master_ui_raylib/${A[$i]}.png" ]; then
|
||||
# This is a new file in PR UI that doesn't exist in master
|
||||
DIFF="${DIFF}<details open>"
|
||||
DIFF="${DIFF}<summary>${A[$i]} : \$\${\\color{cyan}\\text{NEW}}\$\$</summary>"
|
||||
DIFF="${DIFF}<table>"
|
||||
|
||||
DIFF="${DIFF}<tr>"
|
||||
DIFF="${DIFF} <td> <img src=\"https://raw.githubusercontent.com/commaai/ci-artifacts/${{ env.BRANCH_NAME }}/${A[$i]}.png\"> </td>"
|
||||
DIFF="${DIFF}</tr>"
|
||||
|
||||
DIFF="${DIFF}</table>"
|
||||
DIFF="${DIFF}</details>"
|
||||
elif ! compare -fuzz 2% -highlight-color DeepSkyBlue1 -lowlight-color Black -compose Src ${{ github.workspace }}/master_ui_raylib/${A[$i]}.png ${{ github.workspace }}/pr_ui/${A[$i]}.png ${{ github.workspace }}/pr_ui/${A[$i]}_diff.png; then
|
||||
convert ${{ github.workspace }}/pr_ui/${A[$i]}_diff.png -transparent black mask.png
|
||||
composite mask.png ${{ github.workspace }}/master_ui_raylib/${A[$i]}.png composite_diff.png
|
||||
convert -delay 100 ${{ github.workspace }}/master_ui_raylib/${A[$i]}.png composite_diff.png -loop 0 ${{ github.workspace }}/pr_ui/${A[$i]}_diff.gif
|
||||
|
||||
mv ${{ github.workspace }}/master_ui_raylib/${A[$i]}.png ${{ github.workspace }}/pr_ui/${A[$i]}_master_ref.png
|
||||
|
||||
DIFF="${DIFF}<details open>"
|
||||
DIFF="${DIFF}<summary>${A[$i]} : \$\${\\color{red}\\text{DIFFERENT}}\$\$</summary>"
|
||||
DIFF="${DIFF}<table>"
|
||||
|
||||
DIFF="${DIFF}<tr>"
|
||||
DIFF="${DIFF} <td> master <img src=\"https://raw.githubusercontent.com/commaai/ci-artifacts/${{ env.BRANCH_NAME }}/${A[$i]}_master_ref.png\"> </td>"
|
||||
DIFF="${DIFF} <td> proposed <img src=\"https://raw.githubusercontent.com/commaai/ci-artifacts/${{ env.BRANCH_NAME }}/${A[$i]}.png\"> </td>"
|
||||
DIFF="${DIFF}</tr>"
|
||||
|
||||
DIFF="${DIFF}<tr>"
|
||||
DIFF="${DIFF} <td> diff <img src=\"https://raw.githubusercontent.com/commaai/ci-artifacts/${{ env.BRANCH_NAME }}/${A[$i]}_diff.png\"> </td>"
|
||||
DIFF="${DIFF} <td> composite diff <img src=\"https://raw.githubusercontent.com/commaai/ci-artifacts/${{ env.BRANCH_NAME }}/${A[$i]}_diff.gif\"> </td>"
|
||||
DIFF="${DIFF}</tr>"
|
||||
|
||||
DIFF="${DIFF}</table>"
|
||||
DIFF="${DIFF}</details>"
|
||||
else
|
||||
rm -f ${{ github.workspace }}/pr_ui/${A[$i]}_diff.png
|
||||
fi
|
||||
|
||||
INDEX=$(($i % 2))
|
||||
if [[ $INDEX -eq 0 ]]; then
|
||||
TABLE="${TABLE}<tr>"
|
||||
fi
|
||||
TABLE="${TABLE} <td> <img src=\"https://raw.githubusercontent.com/commaai/ci-artifacts/${{ env.BRANCH_NAME }}/${A[$i]}.png\"> </td>"
|
||||
if [[ $INDEX -eq 1 || $(($i + 1)) -eq ${#A[*]} ]]; then
|
||||
TABLE="${TABLE}</tr>"
|
||||
fi
|
||||
done
|
||||
|
||||
TABLE="${TABLE}</table></details>"
|
||||
|
||||
echo "DIFF=$DIFF$TABLE" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Saving proposed ui
|
||||
if: github.event_name == 'pull_request_target'
|
||||
working-directory: ${{ github.workspace }}/master_ui_raylib
|
||||
run: |
|
||||
git config user.name "GitHub Actions Bot"
|
||||
git config user.email "<>"
|
||||
git checkout --orphan=${{ env.BRANCH_NAME }}
|
||||
git rm -rf *
|
||||
mv ${{ github.workspace }}/pr_ui/* .
|
||||
git add .
|
||||
git commit -m "raylib screenshots for PR #${{ github.event.number }}"
|
||||
git push origin ${{ env.BRANCH_NAME }} --force
|
||||
|
||||
- name: Comment Screenshots on PR
|
||||
if: github.event_name == 'pull_request_target'
|
||||
uses: thollander/actions-comment-pull-request@v2
|
||||
with:
|
||||
message: |
|
||||
<!-- _(run_id_screenshots_raylib **${{ github.run_id }}**)_ -->
|
||||
## raylib UI Preview
|
||||
${{ steps.find_diff.outputs.DIFF }}
|
||||
comment_tag: run_id_screenshots_raylib
|
||||
pr_number: ${{ github.event.number }}
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
@@ -1,42 +0,0 @@
|
||||
name: release
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 9 * * *'
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
build_masterci:
|
||||
name: build master-ci
|
||||
env:
|
||||
ImageOS: ubuntu24
|
||||
container:
|
||||
image: ghcr.io/commaai/openpilot-base:latest
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository == 'commaai/openpilot'
|
||||
permissions:
|
||||
checks: read
|
||||
contents: write
|
||||
steps:
|
||||
- name: Install wait-on-check-action dependencies
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y libyaml-dev
|
||||
- name: Wait for green check mark
|
||||
if: ${{ github.event_name == 'schedule' }}
|
||||
uses: lewagon/wait-on-check-action@ccfb013c15c8afb7bf2b7c028fb74dc5a068cccc
|
||||
with:
|
||||
ref: master
|
||||
wait-interval: 30
|
||||
running-workflow-name: 'build master-ci'
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
check-regexp: ^((?!.*(build prebuilt).*).)*$
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
fetch-depth: 0
|
||||
- name: Pull LFS
|
||||
run: |
|
||||
git config --global --add safe.directory '*'
|
||||
git lfs pull
|
||||
- name: Push master-ci
|
||||
run: BRANCH=__nightly release/build_stripped.sh
|
||||
@@ -1,72 +0,0 @@
|
||||
name: repo maintenance
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 14 * * 1" # every Monday at 2am UTC (6am PST)
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
BASE_IMAGE: openpilot-base
|
||||
BUILD: selfdrive/test/docker_build.sh base
|
||||
RUN: docker run --shm-size 2G -v $PWD:/tmp/openpilot -w /tmp/openpilot -e CI=1 -e PYTHONWARNINGS=error -e FILEREADER_CACHE=1 -e PYTHONPATH=/tmp/openpilot -e NUM_JOBS -e JOB_ID -e GITHUB_ACTION -e GITHUB_REF -e GITHUB_HEAD_REF -e GITHUB_SHA -e GITHUB_REPOSITORY -e GITHUB_RUN_ID -v $GITHUB_WORKSPACE/.ci_cache/scons_cache:/tmp/scons_cache -v $GITHUB_WORKSPACE/.ci_cache/comma_download_cache:/tmp/comma_download_cache -v $GITHUB_WORKSPACE/.ci_cache/openpilot_cache:/tmp/openpilot_cache $BASE_IMAGE /bin/bash -c
|
||||
|
||||
jobs:
|
||||
update_translations:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository == 'commaai/openpilot'
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: ./.github/workflows/setup-with-retry
|
||||
- name: Update translations
|
||||
run: |
|
||||
${{ env.RUN }} "python3 selfdrive/ui/update_translations.py --vanish"
|
||||
- name: Create Pull Request
|
||||
uses: peter-evans/create-pull-request@9153d834b60caba6d51c9b9510b087acf9f33f83
|
||||
with:
|
||||
author: Vehicle Researcher <user@comma.ai>
|
||||
commit-message: "Update translations"
|
||||
title: "[bot] Update translations"
|
||||
body: "Automatic PR from repo-maintenance -> update_translations"
|
||||
branch: "update-translations"
|
||||
base: "master"
|
||||
delete-branch: true
|
||||
labels: bot
|
||||
|
||||
package_updates:
|
||||
name: package_updates
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: ghcr.io/commaai/openpilot-base:latest
|
||||
if: github.repository == 'commaai/openpilot'
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
- name: uv lock
|
||||
run: |
|
||||
python3 -m ensurepip --upgrade
|
||||
pip3 install uv
|
||||
uv lock --upgrade
|
||||
- name: bump submodules
|
||||
run: |
|
||||
git config --global --add safe.directory '*'
|
||||
git submodule update --remote
|
||||
git add .
|
||||
- name: update car docs
|
||||
run: |
|
||||
export PYTHONPATH="$PWD"
|
||||
scons -j$(nproc) --minimal opendbc_repo
|
||||
python selfdrive/car/docs.py
|
||||
git add docs/CARS.md
|
||||
- name: Create Pull Request
|
||||
uses: peter-evans/create-pull-request@9153d834b60caba6d51c9b9510b087acf9f33f83
|
||||
with:
|
||||
author: Vehicle Researcher <user@comma.ai>
|
||||
token: ${{ secrets.ACTIONS_CREATE_PR_PAT }}
|
||||
commit-message: Update Python packages
|
||||
title: '[bot] Update Python packages'
|
||||
branch: auto-package-updates
|
||||
base: master
|
||||
delete-branch: true
|
||||
body: 'Automatic PR from repo-maintenance -> package_updates'
|
||||
labels: bot
|
||||
@@ -1,71 +0,0 @@
|
||||
name: Schedule StarPilot Update
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
scheduled_date:
|
||||
description: "Enter the date to update the \"StarPilot\" branch (YYYY-MM-DD)"
|
||||
required: true
|
||||
|
||||
env:
|
||||
GIT_EMAIL: "91348155+FrogAi@users.noreply.github.com"
|
||||
GIT_NAME: "James"
|
||||
TARGET_BRANCH: "StarPilot-Staging"
|
||||
UPDATE_FILE_PATH: ".github/update_date"
|
||||
|
||||
jobs:
|
||||
schedule_update:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout Target Branch
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ env.TARGET_BRANCH }}
|
||||
token: ${{ secrets.PERSONAL_ACCESS_TOKEN }}
|
||||
fetch-depth: 3
|
||||
|
||||
- name: Configure Git Identity
|
||||
run: |
|
||||
git config --global user.name "$GIT_NAME"
|
||||
git config --global user.email "$GIT_EMAIL"
|
||||
|
||||
- name: Write Schedule Date
|
||||
env:
|
||||
SCHEDULED_DATE: ${{ github.event.inputs.scheduled_date }}
|
||||
run: |
|
||||
echo "$SCHEDULED_DATE" > "$UPDATE_FILE_PATH"
|
||||
git add "$UPDATE_FILE_PATH"
|
||||
|
||||
- name: Get Target Commit Data
|
||||
id: get_target
|
||||
run: |
|
||||
TARGET_COMMIT=$(git rev-parse HEAD~1)
|
||||
AUTHOR_DATE=$(git show -s --format=%aD "$TARGET_COMMIT")
|
||||
COMMITTER_DATE=$(git show -s --format=%cD "$TARGET_COMMIT")
|
||||
|
||||
echo "AUTHOR_DATE=$AUTHOR_DATE" >> "$GITHUB_ENV"
|
||||
echo "COMMITTER_DATE=$COMMITTER_DATE" >> "$GITHUB_ENV"
|
||||
echo "TARGET_COMMIT=$TARGET_COMMIT" >> "$GITHUB_ENV"
|
||||
|
||||
- name: Create Fixup Commit
|
||||
id: fixup_commit
|
||||
run: |
|
||||
if git diff --cached --quiet; then
|
||||
echo "No changes detected."
|
||||
echo "has_changes=false" >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
echo "Changes detected. Creating fixup commit."
|
||||
git commit --fixup="$TARGET_COMMIT"
|
||||
echo "has_changes=true" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
|
||||
- name: Autosquash and Restore Timestamps
|
||||
if: steps.fixup_commit.outputs.has_changes == 'true'
|
||||
run: |
|
||||
GIT_SEQUENCE_EDITOR=: git rebase --autosquash -i HEAD~3
|
||||
git rebase --exec "GIT_COMMITTER_DATE='$COMMITTER_DATE' git commit --amend --no-edit --date='$AUTHOR_DATE'" HEAD~2
|
||||
|
||||
- name: Push Changes
|
||||
if: steps.fixup_commit.outputs.has_changes == 'true'
|
||||
run: |
|
||||
git push origin "$TARGET_BRANCH" --force-with-lease
|
||||
@@ -1,52 +0,0 @@
|
||||
name: stale
|
||||
on:
|
||||
schedule:
|
||||
- cron: '30 1 * * *'
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
DAYS_BEFORE_PR_CLOSE: 7
|
||||
DAYS_BEFORE_PR_STALE: 24
|
||||
DAYS_BEFORE_PR_STALE_DRAFT: 30
|
||||
|
||||
jobs:
|
||||
stale:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/stale@v9
|
||||
with:
|
||||
exempt-all-milestones: true
|
||||
|
||||
# pull request config
|
||||
stale-pr-message: 'This PR has had no activity for ${{ env.DAYS_BEFORE_PR_STALE }} days. It will be automatically closed in ${{ env.DAYS_BEFORE_PR_CLOSE }} days if there is no activity.'
|
||||
close-pr-message: 'This PR has been automatically closed due to inactivity. Feel free to re-open once activity resumes.'
|
||||
stale-pr-label: stale
|
||||
delete-branch: ${{ github.event.pull_request.head.repo.full_name == 'commaai/openpilot' }} # only delete branches on the main repo
|
||||
exempt-pr-labels: "ignore stale,needs testing" # if wip or it needs testing from the community, don't mark as stale
|
||||
days-before-pr-stale: ${{ env.DAYS_BEFORE_PR_STALE }}
|
||||
days-before-pr-close: ${{ env.DAYS_BEFORE_PR_CLOSE }}
|
||||
exempt-draft-pr: false
|
||||
|
||||
# issue config
|
||||
days-before-issue-stale: -1 # ignore issues for now
|
||||
|
||||
# same as above, but give draft PRs more time
|
||||
stale_drafts:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/stale@v9
|
||||
with:
|
||||
exempt-all-milestones: true
|
||||
|
||||
# pull request config
|
||||
stale-pr-message: 'This PR has had no activity for ${{ env.DAYS_BEFORE_PR_STALE_DRAFT }} days. It will be automatically closed in ${{ env.DAYS_BEFORE_PR_CLOSE }} days if there is no activity.'
|
||||
close-pr-message: 'This PR has been automatically closed due to inactivity. Feel free to re-open once activity resumes.'
|
||||
stale-pr-label: stale
|
||||
delete-branch: ${{ github.event.pull_request.head.repo.full_name == 'commaai/openpilot' }} # only delete branches on the main repo
|
||||
exempt-pr-labels: "ignore stale,needs testing" # if wip or it needs testing from the community, don't mark as stale
|
||||
days-before-pr-stale: ${{ env.DAYS_BEFORE_PR_STALE_DRAFT }}
|
||||
days-before-pr-close: ${{ env.DAYS_BEFORE_PR_CLOSE }}
|
||||
exempt-draft-pr: true
|
||||
|
||||
# issue config
|
||||
days-before-issue-stale: -1 # ignore issues for now
|
||||
@@ -1,294 +0,0 @@
|
||||
name: tests
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
pull_request:
|
||||
workflow_dispatch:
|
||||
workflow_call:
|
||||
inputs:
|
||||
run_number:
|
||||
default: '1'
|
||||
required: true
|
||||
type: string
|
||||
|
||||
concurrency:
|
||||
group: tests-ci-run-${{ inputs.run_number }}-${{ github.event_name == 'push' && github.ref == 'refs/heads/master' && github.run_id || github.head_ref || github.ref }}-${{ github.workflow }}-${{ github.event_name }}
|
||||
cancel-in-progress: true
|
||||
|
||||
env:
|
||||
PYTHONWARNINGS: error
|
||||
BASE_IMAGE: openpilot-base
|
||||
AZURE_TOKEN: ${{ secrets.AZURE_COMMADATACI_OPENPILOTCI_TOKEN }}
|
||||
|
||||
DOCKER_LOGIN: docker login ghcr.io -u ${{ github.actor }} -p ${{ secrets.GITHUB_TOKEN }}
|
||||
BUILD: selfdrive/test/docker_build.sh base
|
||||
|
||||
RUN: docker run --shm-size 2G -v $PWD:/tmp/openpilot -w /tmp/openpilot -e CI=1 -e PYTHONWARNINGS=error -e FILEREADER_CACHE=1 -e PYTHONPATH=/tmp/openpilot -e NUM_JOBS -e JOB_ID -e GITHUB_ACTION -e GITHUB_REF -e GITHUB_HEAD_REF -e GITHUB_SHA -e GITHUB_REPOSITORY -e GITHUB_RUN_ID -v $GITHUB_WORKSPACE/.ci_cache/scons_cache:/tmp/scons_cache -v $GITHUB_WORKSPACE/.ci_cache/comma_download_cache:/tmp/comma_download_cache -v $GITHUB_WORKSPACE/.ci_cache/openpilot_cache:/tmp/openpilot_cache $BASE_IMAGE /bin/bash -c
|
||||
|
||||
PYTEST: pytest --continue-on-collection-errors --durations=0 -n logical
|
||||
|
||||
jobs:
|
||||
build_release:
|
||||
name: build release
|
||||
runs-on: ${{
|
||||
(github.repository == 'commaai/openpilot') &&
|
||||
((github.event_name != 'pull_request') ||
|
||||
(github.event.pull_request.head.repo.full_name == 'commaai/openpilot'))
|
||||
&& fromJSON('["namespace-profile-amd64-8x16", "namespace-experiments:docker.builds.local-cache=separate"]')
|
||||
|| fromJSON('["ubuntu-24.04"]') }}
|
||||
env:
|
||||
STRIPPED_DIR: /tmp/releasepilot
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
- name: Getting LFS files
|
||||
uses: nick-fields/retry@7152eba30c6575329ac0576536151aca5a72780e
|
||||
with:
|
||||
timeout_minutes: 2
|
||||
max_attempts: 3
|
||||
command: git lfs pull
|
||||
- name: Build devel
|
||||
timeout-minutes: 1
|
||||
run: TARGET_DIR=$STRIPPED_DIR release/build_stripped.sh
|
||||
- uses: ./.github/workflows/setup-with-retry
|
||||
- name: Build openpilot and run checks
|
||||
timeout-minutes: ${{ ((steps.restore-scons-cache.outputs.cache-hit == 'true') && 10 || 30) }} # allow more time when we missed the scons cache
|
||||
run: |
|
||||
cd $STRIPPED_DIR
|
||||
${{ env.RUN }} "python3 system/manager/build.py"
|
||||
- name: Run tests
|
||||
timeout-minutes: 1
|
||||
run: |
|
||||
cd $STRIPPED_DIR
|
||||
${{ env.RUN }} "release/check-dirty.sh"
|
||||
- name: Check submodules
|
||||
if: github.repository == 'commaai/openpilot'
|
||||
timeout-minutes: 3
|
||||
run: release/check-submodules.sh
|
||||
|
||||
build:
|
||||
runs-on: ${{
|
||||
(github.repository == 'commaai/openpilot') &&
|
||||
((github.event_name != 'pull_request') ||
|
||||
(github.event.pull_request.head.repo.full_name == 'commaai/openpilot'))
|
||||
&& fromJSON('["namespace-profile-amd64-8x16", "namespace-experiments:docker.builds.local-cache=separate"]')
|
||||
|| fromJSON('["ubuntu-24.04"]') }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
- name: Setup docker push
|
||||
if: github.ref == 'refs/heads/master' && github.event_name != 'pull_request' && github.repository == 'commaai/openpilot'
|
||||
run: |
|
||||
echo "PUSH_IMAGE=true" >> "$GITHUB_ENV"
|
||||
$DOCKER_LOGIN
|
||||
- uses: ./.github/workflows/setup-with-retry
|
||||
- uses: ./.github/workflows/compile-openpilot
|
||||
timeout-minutes: 30
|
||||
|
||||
build_mac:
|
||||
name: build macOS
|
||||
if: false # tmp disable due to brew install not working
|
||||
runs-on: ${{ ((github.repository == 'commaai/openpilot') && ((github.event_name != 'pull_request') || (github.event.pull_request.head.repo.full_name == 'commaai/openpilot'))) && 'namespace-profile-macos-8x14' || 'macos-latest' }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
- run: echo "CACHE_COMMIT_DATE=$(git log -1 --pretty='format:%cd' --date=format:'%Y-%m-%d-%H:%M')" >> $GITHUB_ENV
|
||||
- name: Homebrew cache
|
||||
uses: ./.github/workflows/auto-cache
|
||||
with:
|
||||
path: ~/Library/Caches/Homebrew
|
||||
key: brew-macos-${{ env.CACHE_COMMIT_DATE }}-${{ github.sha }}
|
||||
restore-keys: |
|
||||
brew-macos-${{ env.CACHE_COMMIT_DATE }}
|
||||
brew-macos
|
||||
- name: Install dependencies
|
||||
run: ./tools/mac_setup.sh
|
||||
env:
|
||||
PYTHONWARNINGS: default # package install has DeprecationWarnings
|
||||
HOMEBREW_DISPLAY_INSTALL_TIMES: 1
|
||||
- run: git lfs pull
|
||||
- name: Getting scons cache
|
||||
uses: ./.github/workflows/auto-cache
|
||||
with:
|
||||
path: /tmp/scons_cache
|
||||
key: scons-${{ runner.arch }}-macos-${{ env.CACHE_COMMIT_DATE }}-${{ github.sha }}
|
||||
restore-keys: |
|
||||
scons-${{ runner.arch }}-macos-${{ env.CACHE_COMMIT_DATE }}
|
||||
scons-${{ runner.arch }}-macos
|
||||
- name: Building openpilot
|
||||
run: . .venv/bin/activate && scons -j$(nproc)
|
||||
|
||||
static_analysis:
|
||||
name: static analysis
|
||||
runs-on: ${{
|
||||
(github.repository == 'commaai/openpilot') &&
|
||||
((github.event_name != 'pull_request') ||
|
||||
(github.event.pull_request.head.repo.full_name == 'commaai/openpilot'))
|
||||
&& fromJSON('["namespace-profile-amd64-8x16", "namespace-experiments:docker.builds.local-cache=separate"]')
|
||||
|| fromJSON('["ubuntu-24.04"]') }}
|
||||
env:
|
||||
PYTHONWARNINGS: default
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
- uses: ./.github/workflows/setup-with-retry
|
||||
- name: Static analysis
|
||||
timeout-minutes: 1
|
||||
run: ${{ env.RUN }} "scripts/lint/lint.sh"
|
||||
|
||||
unit_tests:
|
||||
name: unit tests
|
||||
runs-on: ${{
|
||||
(github.repository == 'commaai/openpilot') &&
|
||||
((github.event_name != 'pull_request') ||
|
||||
(github.event.pull_request.head.repo.full_name == 'commaai/openpilot'))
|
||||
&& fromJSON('["namespace-profile-amd64-8x16", "namespace-experiments:docker.builds.local-cache=separate"]')
|
||||
|| fromJSON('["ubuntu-24.04"]') }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
- uses: ./.github/workflows/setup-with-retry
|
||||
id: setup-step
|
||||
- name: Build openpilot
|
||||
run: ${{ env.RUN }} "scons -j$(nproc)"
|
||||
- name: Run unit tests
|
||||
timeout-minutes: ${{ contains(runner.name, 'nsc') && ((steps.setup-step.outputs.duration < 18) && 1 || 2) || 20 }}
|
||||
run: |
|
||||
${{ env.RUN }} "source selfdrive/test/setup_xvfb.sh && \
|
||||
# Pre-compile Python bytecode so each pytest worker doesn't need to
|
||||
$PYTEST --collect-only -m 'not slow' -qq && \
|
||||
MAX_EXAMPLES=1 $PYTEST -m 'not slow' && \
|
||||
chmod -R 777 /tmp/comma_download_cache"
|
||||
|
||||
process_replay:
|
||||
name: process replay
|
||||
runs-on: ${{
|
||||
(github.repository == 'commaai/openpilot') &&
|
||||
((github.event_name != 'pull_request') ||
|
||||
(github.event.pull_request.head.repo.full_name == 'commaai/openpilot'))
|
||||
&& fromJSON('["namespace-profile-amd64-8x16", "namespace-experiments:docker.builds.local-cache=separate"]')
|
||||
|| fromJSON('["ubuntu-24.04"]') }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
- uses: ./.github/workflows/setup-with-retry
|
||||
id: setup-step
|
||||
- name: Cache test routes
|
||||
id: dependency-cache
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: .ci_cache/comma_download_cache
|
||||
key: proc-replay-${{ hashFiles('selfdrive/test/process_replay/ref_commit', 'selfdrive/test/process_replay/test_processes.py') }}
|
||||
- name: Build openpilot
|
||||
run: |
|
||||
${{ env.RUN }} "scons -j$(nproc)"
|
||||
- name: Run replay
|
||||
timeout-minutes: ${{ contains(runner.name, 'nsc') && (steps.dependency-cache.outputs.cache-hit == 'true') && ((steps.setup-step.outputs.duration < 18) && 1 || 2) || 20 }}
|
||||
run: |
|
||||
${{ env.RUN }} "selfdrive/test/process_replay/test_processes.py -j$(nproc) && \
|
||||
chmod -R 777 /tmp/comma_download_cache"
|
||||
- name: Print diff
|
||||
id: print-diff
|
||||
if: always()
|
||||
run: cat selfdrive/test/process_replay/diff.txt
|
||||
- uses: actions/upload-artifact@v4
|
||||
if: always()
|
||||
continue-on-error: true
|
||||
with:
|
||||
name: process_replay_diff.txt
|
||||
path: selfdrive/test/process_replay/diff.txt
|
||||
- name: Upload reference logs
|
||||
if: false # TODO: move this to github instead of azure
|
||||
run: |
|
||||
${{ env.RUN }} "unset PYTHONWARNINGS && AZURE_TOKEN='$AZURE_TOKEN' python3 selfdrive/test/process_replay/test_processes.py -j$(nproc) --upload-only"
|
||||
- name: Run regen
|
||||
if: false
|
||||
timeout-minutes: 4
|
||||
run: |
|
||||
${{ env.RUN }} "ONNXCPU=1 $PYTEST selfdrive/test/process_replay/test_regen.py && \
|
||||
chmod -R 777 /tmp/comma_download_cache"
|
||||
|
||||
simulator_driving:
|
||||
name: simulator driving
|
||||
runs-on: ${{
|
||||
(github.repository == 'commaai/openpilot') &&
|
||||
((github.event_name != 'pull_request') ||
|
||||
(github.event.pull_request.head.repo.full_name == 'commaai/openpilot'))
|
||||
&& fromJSON('["namespace-profile-amd64-8x16", "namespace-experiments:docker.builds.local-cache=separate"]')
|
||||
|| fromJSON('["ubuntu-24.04"]') }}
|
||||
if: false # FIXME: Started to timeout recently
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
- uses: ./.github/workflows/setup-with-retry
|
||||
id: setup-step
|
||||
- name: Build openpilot
|
||||
run: |
|
||||
${{ env.RUN }} "scons -j$(nproc)"
|
||||
- name: Driving test
|
||||
timeout-minutes: ${{ (steps.setup-step.outputs.duration < 18) && 1 || 2 }}
|
||||
run: |
|
||||
${{ env.RUN }} "source selfdrive/test/setup_xvfb.sh && \
|
||||
source selfdrive/test/setup_vsound.sh && \
|
||||
CI=1 pytest -s tools/sim/tests/test_metadrive_bridge.py"
|
||||
|
||||
create_raylib_ui_report:
|
||||
name: Create raylib UI Report
|
||||
runs-on: ${{
|
||||
(github.repository == 'commaai/openpilot') &&
|
||||
((github.event_name != 'pull_request') ||
|
||||
(github.event.pull_request.head.repo.full_name == 'commaai/openpilot'))
|
||||
&& fromJSON('["namespace-profile-amd64-8x16", "namespace-experiments:docker.builds.local-cache=separate"]')
|
||||
|| fromJSON('["ubuntu-24.04"]') }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
- uses: ./.github/workflows/setup-with-retry
|
||||
- name: Build openpilot
|
||||
run: ${{ env.RUN }} "scons -j$(nproc)"
|
||||
- name: Create raylib UI Report
|
||||
run: >
|
||||
${{ env.RUN }} "PYTHONWARNINGS=ignore &&
|
||||
source selfdrive/test/setup_xvfb.sh &&
|
||||
python3 selfdrive/ui/tests/test_ui/raylib_screenshots.py"
|
||||
- name: Upload Raylib UI Report
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: raylib-report-${{ inputs.run_number || '1' }}-${{ github.event_name == 'push' && github.ref == 'refs/heads/master' && 'master' || github.event.number }}
|
||||
path: selfdrive/ui/tests/test_ui/raylib_report/screenshots
|
||||
|
||||
create_mici_raylib_ui_report:
|
||||
name: Create mici raylib UI Report
|
||||
runs-on: ${{
|
||||
(github.repository == 'commaai/openpilot') &&
|
||||
((github.event_name != 'pull_request') ||
|
||||
(github.event.pull_request.head.repo.full_name == 'commaai/openpilot'))
|
||||
&& fromJSON('["namespace-profile-amd64-8x16", "namespace-experiments:docker.builds.local-cache=separate"]')
|
||||
|| fromJSON('["ubuntu-24.04"]') }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
- uses: ./.github/workflows/setup-with-retry
|
||||
- name: Build openpilot
|
||||
run: ${{ env.RUN }} "scons -j$(nproc)"
|
||||
- name: Create mici raylib UI Report
|
||||
run: >
|
||||
${{ env.RUN }} "PYTHONWARNINGS=ignore &&
|
||||
source selfdrive/test/setup_xvfb.sh &&
|
||||
WINDOWED=1 python3 selfdrive/ui/tests/diff/replay.py"
|
||||
- name: Upload Raylib UI Report
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: mici-raylib-report-${{ inputs.run_number || '1' }}-${{ github.event_name == 'push' && github.ref == 'refs/heads/master' && 'master' || github.event.number }}
|
||||
path: selfdrive/ui/tests/diff/report
|
||||
@@ -1,107 +0,0 @@
|
||||
name: Update StarPilot Branch
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 18 * * 6"
|
||||
|
||||
env:
|
||||
BRANCH_STARPILOT: StarPilot
|
||||
BRANCH_PREVIOUS: StarPilot-Previous
|
||||
BRANCH_STAGING: StarPilot-Staging
|
||||
GIT_EMAIL: "91348155+FrogAi@users.noreply.github.com"
|
||||
GIT_NAME: "James"
|
||||
GITHUB_TOKEN: ${{ secrets.PERSONAL_ACCESS_TOKEN }}
|
||||
TZ: America/Phoenix
|
||||
UPDATE_FILE: .github/update_date
|
||||
|
||||
jobs:
|
||||
check_update:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
update_due: ${{ steps.check_update.outputs.update_due }}
|
||||
scheduled_date: ${{ steps.check_update.outputs.scheduled_date }}
|
||||
steps:
|
||||
- name: Check Update Status
|
||||
id: check_update
|
||||
env:
|
||||
REPO_NAME: ${{ github.repository }}
|
||||
run: |
|
||||
URL="https://raw.githubusercontent.com/$REPO_NAME/$BRANCH_STAGING/$UPDATE_FILE"
|
||||
STATUS=$(curl -o /dev/null -s -w "%{http_code}\n" "$URL")
|
||||
|
||||
if [ "$STATUS" != "200" ]; then
|
||||
echo "update_due=false" >> "$GITHUB_OUTPUT"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
SCHEDULED_DATE=$(curl -s "$URL")
|
||||
CURRENT_DATE=$(TZ="$TZ" date +%F)
|
||||
|
||||
if [ "$SCHEDULED_DATE" == "$CURRENT_DATE" ]; then
|
||||
echo "update_due=true" >> "$GITHUB_OUTPUT"
|
||||
echo "scheduled_date=$SCHEDULED_DATE" >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
echo "update_due=false" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
|
||||
update_branch:
|
||||
needs: check_update
|
||||
if: ${{ needs.check_update.outputs.update_due == 'true' }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout Staging
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ env.BRANCH_STAGING }}
|
||||
fetch-depth: 0
|
||||
token: ${{ secrets.PERSONAL_ACCESS_TOKEN }}
|
||||
|
||||
- name: Configure Git Identity
|
||||
run: |
|
||||
git config --global user.name "$GIT_NAME"
|
||||
git config --global user.email "$GIT_EMAIL"
|
||||
|
||||
- name: Update README and Cleanup
|
||||
env:
|
||||
SCHEDULED_DATE: ${{ needs.check_update.outputs.scheduled_date }}
|
||||
run: |
|
||||
DAY=$(TZ="$TZ" date +'%d' | sed 's/^0//')
|
||||
case "$DAY" in
|
||||
1|21|31) SUFFIX="st" ;;
|
||||
2|22) SUFFIX="nd" ;;
|
||||
3|23) SUFFIX="rd" ;;
|
||||
*) SUFFIX="th" ;;
|
||||
esac
|
||||
|
||||
MONTH=$(TZ="$TZ" date +'%B')
|
||||
YEAR=$(TZ="$TZ" date +'%Y')
|
||||
DATE_FMT="${MONTH} ${DAY}${SUFFIX}, ${YEAR}"
|
||||
DATE_ESCAPED=$(printf '%s' "$DATE_FMT" | sed -E 's/ /%20/g; s/,/%2C/g')
|
||||
|
||||
sed -i -E "s|(Last%20Updated-)[^-)]*|\1${DATE_ESCAPED}|g" README.md
|
||||
|
||||
git rm -f "$UPDATE_FILE"
|
||||
git add README.md
|
||||
git commit -m "Updated README date to ${DATE_FMT}"
|
||||
|
||||
git reset --soft HEAD~2
|
||||
ORIGINAL_MSG=$(git log -1 --pretty=%B HEAD)
|
||||
|
||||
COMMIT_PHX=$(TZ="$TZ" date -d "$SCHEDULED_DATE 12:00" +"%Y-%m-%dT%H:%M:%S %z")
|
||||
GIT_COMMITTER_DATE="$COMMIT_PHX" GIT_AUTHOR_DATE="$COMMIT_PHX" git commit -m "$ORIGINAL_MSG"
|
||||
|
||||
- name: Wait Until Noon ${{ env.TZ }}
|
||||
run: |
|
||||
NOW=$(TZ="$TZ" date +%s)
|
||||
TARGET=$(TZ="$TZ" date -d "12:00" +%s)
|
||||
|
||||
if [ "$NOW" -lt "$TARGET" ]; then
|
||||
sleep $((TARGET - NOW))
|
||||
fi
|
||||
|
||||
- name: Push and Sync Branches
|
||||
run: |
|
||||
git push origin "$BRANCH_STAGING" --force
|
||||
git fetch origin "$BRANCH_STARPILOT:$BRANCH_STARPILOT"
|
||||
git push origin "$BRANCH_STARPILOT:$BRANCH_PREVIOUS" --force
|
||||
git push origin "$BRANCH_STAGING:$BRANCH_STARPILOT" --force
|
||||
@@ -1,95 +0,0 @@
|
||||
name: Update Tinygrad
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
runner:
|
||||
description: "Select runner"
|
||||
type: choice
|
||||
options:
|
||||
- c3
|
||||
- c3x
|
||||
default: "c3"
|
||||
required: true
|
||||
|
||||
env:
|
||||
GIT_EMAIL: "91348155+FrogAi@users.noreply.github.com"
|
||||
GIT_NAME: "James"
|
||||
GITLAB_REPO_DIR: "StarPilot-Resources"
|
||||
GITLAB_URL: "gitlab.com/FrogAi/StarPilot-Resources.git"
|
||||
OPENPILOT_DIR: "/data/openpilot"
|
||||
|
||||
jobs:
|
||||
update_tinygrad:
|
||||
runs-on: [self-hosted, "${{ inputs.runner }}"]
|
||||
steps:
|
||||
- name: Get Version
|
||||
id: get_version
|
||||
run: |
|
||||
VERSION=$(grep -oP '^VERSION\s*=\s*"\K[^"]+' "$OPENPILOT_DIR/starpilot/assets/model_manager.py")
|
||||
echo "VERSION=$VERSION"
|
||||
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Setup Workspace and Clone GitLab
|
||||
id: setup
|
||||
env:
|
||||
GITLAB_TOKEN: ${{ secrets.GITLAB_TOKEN }}
|
||||
run: |
|
||||
WORK_DIR="$RUNNER_TEMP/starpilot_tinygrad"
|
||||
rm -rf "$WORK_DIR" && mkdir -p "$WORK_DIR"
|
||||
echo "work_dir=$WORK_DIR" >> "$GITHUB_OUTPUT"
|
||||
|
||||
cd "$WORK_DIR"
|
||||
git clone --depth 1 --branch Tinygrad "https://oauth2:${GITLAB_TOKEN}@$GITLAB_URL"
|
||||
|
||||
- name: Create Tinygrad Archive
|
||||
working-directory: ${{ env.OPENPILOT_DIR }}
|
||||
env:
|
||||
WORK_DIR: ${{ steps.setup.outputs.work_dir }}
|
||||
VERSION: ${{ steps.get_version.outputs.version }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
ARCHIVE_DEST="$WORK_DIR/$GITLAB_REPO_DIR"
|
||||
ARCHIVE_NAME="Tinygrad_$VERSION.tar.gz"
|
||||
|
||||
DUMMY_DIR=$(mktemp -d)
|
||||
touch "$DUMMY_DIR/SConscript"
|
||||
|
||||
tar -czf "$ARCHIVE_DEST/$ARCHIVE_NAME" \
|
||||
--exclude="*.a" \
|
||||
--exclude="*.o" \
|
||||
--exclude="*.onnx" \
|
||||
--exclude="*__pycache__*" \
|
||||
--exclude="*tests*" \
|
||||
--exclude="selfdrive/modeld/SConscript" \
|
||||
selfdrive/modeld tinygrad_repo \
|
||||
-C "$DUMMY_DIR" \
|
||||
--transform 's|^SConscript$|selfdrive/modeld/SConscript|' \
|
||||
SConscript
|
||||
|
||||
rm -rf "$DUMMY_DIR"
|
||||
|
||||
- name: Push Updated Tinygrad
|
||||
working-directory: ${{ steps.setup.outputs.work_dir }}/${{ env.GITLAB_REPO_DIR }}
|
||||
env:
|
||||
VERSION: ${{ steps.get_version.outputs.version }}
|
||||
run: |
|
||||
git config user.name "$GIT_NAME"
|
||||
git config user.email "$GIT_EMAIL"
|
||||
|
||||
git add Tinygrad_*.tar.gz
|
||||
|
||||
if git diff --staged --quiet; then
|
||||
echo "No changes to commit."
|
||||
else
|
||||
git commit -m "Updated Tinygrad: $VERSION"
|
||||
git push origin Tinygrad
|
||||
fi
|
||||
|
||||
- name: Cleanup Temporary Files
|
||||
if: always()
|
||||
env:
|
||||
WORK_DIR: ${{ steps.setup.outputs.work_dir }}
|
||||
run: |
|
||||
rm -rf "$WORK_DIR"
|
||||
+202
@@ -0,0 +1,202 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
@@ -8,7 +8,6 @@
|
||||
**StarPilot** is a custom fork of [comma.ai's openpilot](https://comma.ai/openpilot),
|
||||
an open source driver assistance system.
|
||||
|
||||
whoisdomi
|
||||
|
||||
Openpilot provides
|
||||
* Automated Lane Centering
|
||||
@@ -75,3 +74,6 @@ Download models, change settings, update software, visualize live model outputs
|
||||
Uses your comma's sysroot/toolchain
|
||||
* Toggle: "Use Precompiled Binaries" to allow switching between fast boot / editable builds
|
||||
* 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.
|
||||
|
||||
+17
-9
@@ -153,7 +153,7 @@ lenv = {
|
||||
}
|
||||
|
||||
# Allow callers to override cache/temp dirs used by subprocesses (e.g. tinygrad model compilation).
|
||||
for key in ("HOME", "TMPDIR", "XDG_CACHE_HOME", "CACHEDB"):
|
||||
for key in ("HOME", "TMPDIR", "XDG_CACHE_HOME", "CACHEDB", "PARAMS_ROOT"):
|
||||
if key in os.environ:
|
||||
lenv[key] = os.environ[key]
|
||||
|
||||
@@ -404,12 +404,22 @@ if arch == "larch64" and os.environ.get("SP_TICI_SYSROOT"):
|
||||
qt_arm_moc = os.path.join(qt_tool_bin, "moc")
|
||||
qt_arm_uic = os.path.join(qt_tool_bin, "uic")
|
||||
qt_arm_rcc = os.path.join(qt_tool_bin, "rcc")
|
||||
qt_host_bin = os.environ.get("SP_QT_HOST_BIN", "/usr/lib/qt5/bin")
|
||||
qt_host_moc = os.environ.get("SP_QT_HOST_MOC", os.path.join(qt_host_bin, "moc"))
|
||||
qt_host_uic = os.environ.get("SP_QT_HOST_UIC", os.path.join(qt_host_bin, "uic"))
|
||||
qt_host_rcc = os.environ.get("SP_QT_HOST_RCC", "rcc")
|
||||
if platform.machine() in ("aarch64", "arm64"):
|
||||
if os.path.isfile(qt_arm_moc):
|
||||
if "SP_QT_HOST_MOC" in os.environ:
|
||||
qt_env['QT3_MOC'] = qt_host_moc
|
||||
elif os.path.isfile(qt_arm_moc):
|
||||
qt_env['QT3_MOC'] = qt_arm_moc
|
||||
if os.path.isfile(qt_arm_uic):
|
||||
if "SP_QT_HOST_UIC" in os.environ:
|
||||
qt_env['QT3_UIC'] = qt_host_uic
|
||||
elif os.path.isfile(qt_arm_uic):
|
||||
qt_env['QT3_UIC'] = qt_arm_uic
|
||||
if os.path.isfile(qt_arm_rcc):
|
||||
if "SP_QT_HOST_RCC" in os.environ:
|
||||
qt_env['SP_QT_RCC'] = qt_host_rcc
|
||||
elif os.path.isfile(qt_arm_rcc):
|
||||
qt_env['SP_QT_RCC'] = qt_arm_rcc
|
||||
else:
|
||||
qt_qemu = shutil.which("qemu-aarch64-static") or shutil.which("qemu-aarch64")
|
||||
@@ -417,19 +427,17 @@ if arch == "larch64" and os.environ.get("SP_TICI_SYSROOT"):
|
||||
if qt_qemu and os.path.isfile(qt_arm_moc):
|
||||
qt_env['QT3_MOC'] = f"{qt_qemu} -L {qt_tool_root} {qt_arm_moc}"
|
||||
else:
|
||||
qt_host_bin = os.environ.get("SP_QT_HOST_BIN", "/usr/lib/qt5/bin")
|
||||
qt_env['QT3_MOC'] = os.environ.get("SP_QT_HOST_MOC", os.path.join(qt_host_bin, "moc"))
|
||||
qt_env['QT3_MOC'] = qt_host_moc
|
||||
|
||||
if qt_qemu and os.path.isfile(qt_arm_uic):
|
||||
qt_env['QT3_UIC'] = f"{qt_qemu} -L {qt_tool_root} {qt_arm_uic}"
|
||||
else:
|
||||
qt_host_bin = os.environ.get("SP_QT_HOST_BIN", "/usr/lib/qt5/bin")
|
||||
qt_env['QT3_UIC'] = os.environ.get("SP_QT_HOST_UIC", os.path.join(qt_host_bin, "uic"))
|
||||
qt_env['QT3_UIC'] = qt_host_uic
|
||||
|
||||
if qt_qemu and os.path.isfile(qt_arm_rcc):
|
||||
qt_env['SP_QT_RCC'] = f"{qt_qemu} -L {qt_tool_root} {qt_arm_rcc}"
|
||||
else:
|
||||
qt_env['SP_QT_RCC'] = os.environ.get("SP_QT_HOST_RCC", "rcc")
|
||||
qt_env['SP_QT_RCC'] = qt_host_rcc
|
||||
|
||||
qt_env['CPPPATH'] += qt_dirs + ["#third_party/qrcode"]
|
||||
qt_flags = [
|
||||
|
||||
@@ -59,6 +59,8 @@ struct StarPilotCarParams @0xaedffd8f31e7b55d {
|
||||
isHDA2 @4 :Bool;
|
||||
openpilotLongitudinalControlDisabled @5 :Bool;
|
||||
safetyConfigs @6 :List(SafetyConfig);
|
||||
pcmCruiseSpeed @7 :Bool = true;
|
||||
redneckCruiseAvailable @8 :Bool;
|
||||
|
||||
struct SafetyConfig {
|
||||
safetyParam @0 :UInt16;
|
||||
@@ -85,6 +87,10 @@ struct StarPilotCarState @0xf35cc4560bbf6ec2 {
|
||||
modePressed @16 :Bool;
|
||||
customPressed @17 :Bool;
|
||||
alwaysOnLateralAllowed @18 :Bool;
|
||||
dashboardStopSign @19 :UInt8; # 0 = no signal / platform doesn't publish
|
||||
cancelPressed @20 :Bool;
|
||||
cancelLongPressed @21 :Bool;
|
||||
cancelVeryLongPressed @22 :Bool;
|
||||
}
|
||||
|
||||
struct StarPilotDeviceState @0xda96579883444c35 {
|
||||
@@ -193,6 +199,7 @@ struct StarPilotPlan @0xf98d843bfd7004a3 {
|
||||
weatherId @34 :Int16;
|
||||
disableThrottle @35 :Bool;
|
||||
trackingLead @36 :Bool;
|
||||
stopSignConfirmed @37 :Bool;
|
||||
}
|
||||
|
||||
struct StarPilotRadarState @0xb86e6369214c01c8 {
|
||||
|
||||
Binary file not shown.
+12
-13
@@ -131,6 +131,13 @@ struct OnroadEvent @0xc4fa6047f024e718 {
|
||||
excessiveActuation @96;
|
||||
audioFeedback @97;
|
||||
lateralManeuver @98;
|
||||
pedalCruiseEnabled @99;
|
||||
pedalCruiseDisabled @100;
|
||||
pedalMaxRegen @101;
|
||||
teslaCCEngaged @102;
|
||||
teslaCCDisengaged @103;
|
||||
teslaCCNotArmed @104;
|
||||
pedalNotCalibrated @105;
|
||||
|
||||
soundsUnavailableDEPRECATED @47;
|
||||
}
|
||||
@@ -1262,12 +1269,6 @@ struct LongitudinalPlan @0xe00b5b3eba12876c {
|
||||
|
||||
solverExecutionTime @35 :Float32;
|
||||
|
||||
# lead trajectories the MPC solved against (13 points at T_IDXS)
|
||||
leadTrajectoryX0 @40 :List(Float32);
|
||||
leadTrajectoryV0 @41 :List(Float32);
|
||||
leadTrajectoryX1 @42 :List(Float32);
|
||||
leadTrajectoryV1 @43 :List(Float32);
|
||||
|
||||
enum LongitudinalPlanSource {
|
||||
cruise @0;
|
||||
lead0 @1;
|
||||
@@ -2172,14 +2173,12 @@ struct DriverStateV2 {
|
||||
facePosition @2 :List(Float32);
|
||||
facePositionStd @3 :List(Float32);
|
||||
faceProb @4 :Float32;
|
||||
eyesVisibleProb @14 :Float32;
|
||||
eyesClosedProb @15 :Float32;
|
||||
leftEyeProb @5 :Float32;
|
||||
rightEyeProb @6 :Float32;
|
||||
leftBlinkProb @7 :Float32;
|
||||
rightBlinkProb @8 :Float32;
|
||||
sunglassesProb @9 :Float32;
|
||||
phoneProb @13 :Float32;
|
||||
leftEyeProbDEPRECATED @5 :Float32;
|
||||
rightEyeProbDEPRECATED @6 :Float32;
|
||||
leftBlinkProbDEPRECATED @7 :Float32;
|
||||
rightBlinkProbDEPRECATED @8 :Float32;
|
||||
sunglassesProbDEPRECATED @9 :Float32;
|
||||
notReadyProbDEPRECATED @12 :List(Float32);
|
||||
occludedProbDEPRECATED @10 :Float32;
|
||||
readyProbDEPRECATED @11 :List(Float32);
|
||||
|
||||
Binary file not shown.
+243
-58
@@ -1,70 +1,255 @@
|
||||
from openpilot.common.params_pyx import Params as _Params, ParamKeyFlag, ParamKeyType, UnknownKeyName
|
||||
assert _Params
|
||||
assert ParamKeyFlag
|
||||
assert ParamKeyType
|
||||
assert UnknownKeyName
|
||||
from __future__ import annotations
|
||||
|
||||
from enum import IntEnum, IntFlag
|
||||
from pathlib import Path
|
||||
import tempfile
|
||||
|
||||
class Params(_Params):
|
||||
def get(self, key, block=False, return_default=False, encoding=None, default=None):
|
||||
try:
|
||||
value = super().get(key, block=block, return_default=return_default)
|
||||
except UnknownKeyName:
|
||||
return default
|
||||
if value is None:
|
||||
return default
|
||||
if encoding is not None and isinstance(value, bytes):
|
||||
try:
|
||||
from openpilot.common.params_pyx import Params as _Params, ParamKeyFlag, ParamKeyType, UnknownKeyName
|
||||
except Exception:
|
||||
class UnknownKeyName(KeyError):
|
||||
pass
|
||||
|
||||
class ParamKeyFlag(IntFlag):
|
||||
PERSISTENT = 0x02
|
||||
CLEAR_ON_MANAGER_START = 0x04
|
||||
CLEAR_ON_ONROAD_TRANSITION = 0x08
|
||||
CLEAR_ON_OFFROAD_TRANSITION = 0x10
|
||||
DONT_LOG = 0x20
|
||||
DEVELOPMENT_ONLY = 0x40
|
||||
CLEAR_ON_IGNITION_ON = 0x80
|
||||
ALL = 0xFFFFFFFF
|
||||
|
||||
class ParamKeyType(IntEnum):
|
||||
STRING = 0
|
||||
BOOL = 1
|
||||
INT = 2
|
||||
FLOAT = 3
|
||||
TIME = 4
|
||||
JSON = 5
|
||||
BYTES = 6
|
||||
|
||||
def _load_key_types() -> dict[str, ParamKeyType]:
|
||||
key_types: dict[str, ParamKeyType] = {}
|
||||
params_keys = Path(__file__).with_name("params_keys.h")
|
||||
if not params_keys.exists():
|
||||
return key_types
|
||||
|
||||
for line in params_keys.read_text(encoding="utf-8", errors="ignore").splitlines():
|
||||
if not line.lstrip().startswith('{"'):
|
||||
continue
|
||||
|
||||
parts = line.split('"')
|
||||
if len(parts) < 2:
|
||||
continue
|
||||
|
||||
key = parts[1]
|
||||
for type_name in ParamKeyType.__members__:
|
||||
if f", {type_name}" in line:
|
||||
key_types[key] = ParamKeyType[type_name]
|
||||
break
|
||||
else:
|
||||
key_types[key] = ParamKeyType.STRING
|
||||
|
||||
return key_types
|
||||
|
||||
_KEY_TYPES = _load_key_types()
|
||||
_PERSISTENT_STORE: dict[str, object] = {}
|
||||
_MEMORY_STORE: dict[str, object] = {}
|
||||
|
||||
class Params:
|
||||
def __init__(self, d: str | None = None, memory: bool = False, return_defaults: bool = False):
|
||||
self.d = d if d is not None else ""
|
||||
self.m = memory
|
||||
self.return_defaults = return_defaults
|
||||
self._store = _MEMORY_STORE if memory else _PERSISTENT_STORE
|
||||
|
||||
def __reduce__(self):
|
||||
return type(self), (self.d, self.m, self.return_defaults)
|
||||
|
||||
def clear_all(self, tx_flag=ParamKeyFlag.ALL):
|
||||
self._store.clear()
|
||||
|
||||
def check_key(self, key):
|
||||
if isinstance(key, bytes):
|
||||
key = key.decode("utf-8")
|
||||
return str(key)
|
||||
|
||||
def _coerce_bool(self, value) -> bool:
|
||||
if isinstance(value, bool):
|
||||
return value
|
||||
if isinstance(value, (int, float)):
|
||||
return bool(value)
|
||||
if isinstance(value, bytes):
|
||||
return value == b"1"
|
||||
if isinstance(value, str):
|
||||
return value.strip().lower() not in ("", "0", "false", "none", "null")
|
||||
return bool(value)
|
||||
|
||||
def _coerce_value(self, key: str, value):
|
||||
value_type = self.get_type(key)
|
||||
if value_type == ParamKeyType.BOOL:
|
||||
return self._coerce_bool(value)
|
||||
if value_type == ParamKeyType.INT:
|
||||
return int(float(value))
|
||||
if value_type == ParamKeyType.FLOAT:
|
||||
return float(value)
|
||||
if value_type == ParamKeyType.BYTES and isinstance(value, str):
|
||||
return value.encode("utf-8")
|
||||
return value
|
||||
|
||||
def get(self, key, block: bool = False, return_default: bool = False, encoding=None, default=None):
|
||||
key = self.check_key(key)
|
||||
value = self._store.get(key, default)
|
||||
if value is None:
|
||||
return default
|
||||
if encoding is not None and isinstance(value, bytes):
|
||||
try:
|
||||
return value.decode(encoding)
|
||||
except Exception:
|
||||
return value.decode("utf-8", errors="replace")
|
||||
return value
|
||||
|
||||
def get_bool(self, key, block: bool = False, default: bool = False):
|
||||
value = self.get(key, block=block, return_default=True, default=default)
|
||||
return self._coerce_bool(value)
|
||||
|
||||
def get_int(self, key, block: bool = False, return_default: bool = False, default: int = 0):
|
||||
value = self.get(key, block=block, return_default=return_default, encoding="utf-8", default=default)
|
||||
if value is None or value == "":
|
||||
return default
|
||||
try:
|
||||
return value.decode(encoding)
|
||||
except Exception:
|
||||
return value.decode("utf-8", errors="replace")
|
||||
return value
|
||||
return int(float(value))
|
||||
except (TypeError, ValueError):
|
||||
return default
|
||||
|
||||
def get_bool(self, key, block=False, default=False):
|
||||
try:
|
||||
return super().get_bool(key, block=block)
|
||||
except UnknownKeyName:
|
||||
return bool(default)
|
||||
def get_float(self, key, block: bool = False, return_default: bool = False, default: float = 0.0):
|
||||
value = self.get(key, block=block, return_default=return_default, encoding="utf-8", default=default)
|
||||
if value is None or value == "":
|
||||
return default
|
||||
try:
|
||||
return float(value)
|
||||
except (TypeError, ValueError):
|
||||
return default
|
||||
|
||||
def get_int(self, key, block=False, return_default=False, default=0):
|
||||
val = self.get(key, block=block, return_default=return_default, encoding="utf-8")
|
||||
if val is None or val == "":
|
||||
return default
|
||||
try:
|
||||
return int(float(val))
|
||||
except ValueError:
|
||||
return default
|
||||
def put(self, key, dat):
|
||||
key = self.check_key(key)
|
||||
self._store[key] = self._coerce_value(key, dat)
|
||||
|
||||
def get_float(self, key, block=False, return_default=False, default=0.0):
|
||||
val = self.get(key, block=block, return_default=return_default, encoding="utf-8")
|
||||
if val is None or val == "":
|
||||
return default
|
||||
try:
|
||||
return float(val)
|
||||
except ValueError:
|
||||
return default
|
||||
|
||||
def put_int(self, key, val):
|
||||
t = self.get_type(key)
|
||||
if t == ParamKeyType.FLOAT:
|
||||
self.put(key, float(val))
|
||||
elif t == ParamKeyType.INT:
|
||||
self.put(key, int(val))
|
||||
elif t == ParamKeyType.BOOL:
|
||||
def put_bool(self, key, val: bool):
|
||||
self.put(key, bool(val))
|
||||
else:
|
||||
self.put(key, str(int(val)))
|
||||
|
||||
def put_float(self, key, val):
|
||||
t = self.get_type(key)
|
||||
if t == ParamKeyType.FLOAT:
|
||||
self.put(key, float(val))
|
||||
elif t == ParamKeyType.INT:
|
||||
def put_nonblocking(self, key, dat):
|
||||
self.put(key, dat)
|
||||
|
||||
def put_bool_nonblocking(self, key, val: bool):
|
||||
self.put_bool(key, val)
|
||||
|
||||
def put_int(self, key, val):
|
||||
self.put(key, int(val))
|
||||
elif t == ParamKeyType.BOOL:
|
||||
self.put(key, bool(val))
|
||||
else:
|
||||
self.put(key, str(float(val)))
|
||||
|
||||
def put_float(self, key, val):
|
||||
self.put(key, float(val))
|
||||
|
||||
def remove(self, key):
|
||||
self._store.pop(self.check_key(key), None)
|
||||
|
||||
def get_param_path(self, key: str = ""):
|
||||
base = Path(tempfile.gettempdir()) / ("params_memory" if self.m else "params")
|
||||
base.mkdir(parents=True, exist_ok=True)
|
||||
return str(base / key) if key else str(base)
|
||||
|
||||
def get_type(self, key):
|
||||
return _KEY_TYPES.get(self.check_key(key), ParamKeyType.STRING)
|
||||
|
||||
def all_keys(self):
|
||||
return list(_KEY_TYPES)
|
||||
|
||||
def get_default_value(self, key):
|
||||
return None
|
||||
|
||||
def cpp2python(self, key, value):
|
||||
return self._coerce_value(self.check_key(key), value)
|
||||
|
||||
def get_key_flag(self, key):
|
||||
return ParamKeyFlag.PERSISTENT
|
||||
|
||||
def get_stock_value(self, key):
|
||||
return None
|
||||
|
||||
def get_tuning_level(self, key):
|
||||
return 0
|
||||
|
||||
else:
|
||||
assert _Params
|
||||
assert ParamKeyFlag
|
||||
assert ParamKeyType
|
||||
assert UnknownKeyName
|
||||
|
||||
class Params(_Params):
|
||||
def get(self, key, block=False, return_default=False, encoding=None, default=None):
|
||||
try:
|
||||
value = super().get(key, block=block, return_default=return_default)
|
||||
except UnknownKeyName:
|
||||
return default
|
||||
if value is None:
|
||||
return default
|
||||
if encoding is not None and isinstance(value, bytes):
|
||||
try:
|
||||
return value.decode(encoding)
|
||||
except Exception:
|
||||
return value.decode("utf-8", errors="replace")
|
||||
return value
|
||||
|
||||
def get_bool(self, key, block=False, default=False):
|
||||
try:
|
||||
result = super().get(key, block=block, return_default=True)
|
||||
if result is None:
|
||||
return bool(default)
|
||||
return bool(result)
|
||||
except UnknownKeyName:
|
||||
return bool(default)
|
||||
|
||||
def get_int(self, key, block=False, return_default=False, default=0):
|
||||
val = self.get(key, block=block, return_default=return_default, encoding="utf-8")
|
||||
if val is None or val == "":
|
||||
return default
|
||||
try:
|
||||
return int(float(val))
|
||||
except ValueError:
|
||||
return default
|
||||
|
||||
def get_float(self, key, block=False, return_default=False, default=0.0):
|
||||
val = self.get(key, block=block, return_default=return_default, encoding="utf-8")
|
||||
if val is None or val == "":
|
||||
return default
|
||||
try:
|
||||
return float(val)
|
||||
except ValueError:
|
||||
return default
|
||||
|
||||
def put_int(self, key, val):
|
||||
t = self.get_type(key)
|
||||
if t == ParamKeyType.FLOAT:
|
||||
self.put(key, float(val))
|
||||
elif t == ParamKeyType.INT:
|
||||
self.put(key, int(val))
|
||||
elif t == ParamKeyType.BOOL:
|
||||
self.put(key, bool(val))
|
||||
else:
|
||||
self.put(key, str(int(val)))
|
||||
|
||||
def put_float(self, key, val):
|
||||
t = self.get_type(key)
|
||||
if t == ParamKeyType.FLOAT:
|
||||
self.put(key, float(val))
|
||||
elif t == ParamKeyType.INT:
|
||||
self.put(key, int(val))
|
||||
elif t == ParamKeyType.BOOL:
|
||||
self.put(key, bool(val))
|
||||
else:
|
||||
self.put(key, str(float(val)))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import sys
|
||||
|
||||
+49
-17
@@ -59,6 +59,8 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
|
||||
{"GsmRoaming", {PERSISTENT, BOOL}},
|
||||
{"HardwareSerial", {PERSISTENT, STRING}},
|
||||
{"HasAcceptedTerms", {PERSISTENT, STRING, "0"}},
|
||||
{"HondaGasFactorParams", {PERSISTENT, FLOAT}},
|
||||
{"HondaWindFactorParams", {PERSISTENT, FLOAT}},
|
||||
{"InstallDate", {PERSISTENT, TIME}},
|
||||
{"IsDriverViewEnabled", {CLEAR_ON_MANAGER_START, BOOL}},
|
||||
{"IsEngaged", {PERSISTENT, BOOL}},
|
||||
@@ -142,7 +144,7 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
|
||||
|
||||
// StarPilot variables
|
||||
{"AccelerationPath", {PERSISTENT, BOOL, "1", "0", 2}},
|
||||
{"AccelerationProfile", {PERSISTENT, INT, "2", "0", 0}},
|
||||
{"AccelerationProfile", {PERSISTENT, INT, "0", "0", 0}},
|
||||
{"AdjacentLeadsUI", {PERSISTENT, BOOL, "1", "0", 3}},
|
||||
{"AdjacentPath", {PERSISTENT, BOOL, "0", "0", 3}},
|
||||
{"AdjacentPathMetrics", {PERSISTENT, BOOL, "0", "0", 3}},
|
||||
@@ -150,7 +152,7 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
|
||||
{"AdvancedLateralTune", {PERSISTENT, BOOL, "1", "0", 2}},
|
||||
{"AdvancedLongitudinalTune", {PERSISTENT, BOOL, "1", "0", 3}},
|
||||
{"AggressiveFollow", {PERSISTENT, FLOAT, "1.25", "1.25", 2}},
|
||||
{"AggressiveFollowHigh", {PERSISTENT, FLOAT, "1.25", "1.25", 2}},
|
||||
{"AggressiveFollowHigh", {PERSISTENT, FLOAT, "1.0", "1.0", 2}},
|
||||
{"AggressiveJerkAcceleration", {PERSISTENT, FLOAT, "50.0", "50.0", 3}},
|
||||
{"AggressiveJerkDanger", {PERSISTENT, FLOAT, "100.0", "100.0", 3}},
|
||||
{"AggressiveJerkDeceleration", {PERSISTENT, FLOAT, "50.0", "50.0", 3}},
|
||||
@@ -177,6 +179,7 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
|
||||
{"CalibrationProgress", {PERSISTENT, FLOAT, "0.0", "0.0", 3}},
|
||||
{"CameraView", {PERSISTENT, INT, "3", "0", 2}},
|
||||
{"CancelDownloadMaps", {CLEAR_ON_MANAGER_START, BOOL, "0", "0"}},
|
||||
{"DisableWideRoad", {PERSISTENT, BOOL, "0", "0", 3}},
|
||||
{"CancelModelDownload", {CLEAR_ON_MANAGER_START, BOOL, "0", "0"}},
|
||||
{"CancelThemeDownload", {CLEAR_ON_MANAGER_START, BOOL, "0", "0"}},
|
||||
{"CarMake", {PERSISTENT, STRING, "mock", "mock", 0}},
|
||||
@@ -193,7 +196,7 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
|
||||
{"CESpeedLead", {PERSISTENT, FLOAT, "0.0", "0.0", 1}},
|
||||
{"CEStatus", {CLEAR_ON_OFFROAD_TRANSITION, INT, "0", "0"}},
|
||||
{"CEStopLights", {PERSISTENT, BOOL, "1", "0", 1}},
|
||||
{"CEStoppedLead", {PERSISTENT, BOOL, "1", "0", 1}},
|
||||
{"CEStoppedLead", {PERSISTENT, BOOL, "0", "0", 1}},
|
||||
{"ClusterOffset", {PERSISTENT, FLOAT, "1.0", "1.0", 2}},
|
||||
{"ColorScheme", {PERSISTENT, STRING, "frog", "stock", 0}},
|
||||
{"ColorToDownload", {CLEAR_ON_MANAGER_START, STRING, "", ""}},
|
||||
@@ -216,6 +219,8 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
|
||||
{"CustomCruise", {PERSISTENT, FLOAT, "1.0", "1.0", 2}},
|
||||
{"CustomCruiseLong", {PERSISTENT, FLOAT, "5.0", "5.0", 2}},
|
||||
{"CustomPersonalities", {PERSISTENT, BOOL, "0", "0", 2}},
|
||||
{"CancelButtonControl", {PERSISTENT, INT, "1", "0", 2}},
|
||||
{"CancelButtonControlsMigrated", {PERSISTENT, BOOL, "0", "0"}},
|
||||
{"TrafficPersonalityProfile", {PERSISTENT, BOOL, "1", "1", 2}},
|
||||
{"AggressivePersonalityProfile", {PERSISTENT, BOOL, "1", "1", 2}},
|
||||
{"StandardPersonalityProfile", {PERSISTENT, BOOL, "1", "1", 2}},
|
||||
@@ -270,12 +275,27 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
|
||||
{"LongPitch", {PERSISTENT, BOOL, "1", "0", 2}},
|
||||
{"RemoteStartBootsComma", {PERSISTENT, BOOL, "0", "0"}},
|
||||
{"RemapCancelToDistance", {PERSISTENT, BOOL, "0", "0"}},
|
||||
{"NAPAdaptiveAccel", {PERSISTENT, BOOL, "1", "1"}},
|
||||
{"NAPFollowDistance", {PERSISTENT, INT, "4", "4"}},
|
||||
{"NAPForcePreAP", {PERSISTENT, BOOL, "0", "0"}},
|
||||
{"NAPPedalEnabled", {PERSISTENT, BOOL, "0", "0"}},
|
||||
{"NAPPedalCanBus", {PERSISTENT, INT, "2", "2"}},
|
||||
{"NAPPedalCalibDone", {PERSISTENT, BOOL, "0", "0"}},
|
||||
{"NAPPedalCalibMin", {PERSISTENT, FLOAT, "-3.0", "-3.0"}},
|
||||
{"NAPPedalCalibMax", {PERSISTENT, FLOAT, "99.6", "99.6"}},
|
||||
{"NAPPedalCalibFactor", {PERSISTENT, FLOAT, "1.0", "1.0"}},
|
||||
{"NAPPedalCalibZero", {PERSISTENT, FLOAT, "0.0", "0.0"}},
|
||||
{"NAPPedalProfile", {PERSISTENT, INT, "4", "4"}},
|
||||
{"NAPRadarBehindNosecone", {PERSISTENT, BOOL, "0", "0"}},
|
||||
{"NAPRadarEnabled", {PERSISTENT, BOOL, "0", "0"}},
|
||||
{"NAPRadarOffset", {PERSISTENT, FLOAT, "0.0", "0.0"}},
|
||||
{"ForceAutoTune", {PERSISTENT, BOOL, "0", "0", 3}},
|
||||
{"ForceAutoTuneOff", {PERSISTENT, BOOL, "1", "0", 2}},
|
||||
{"ForceFingerprint", {PERSISTENT, BOOL, "0", "0", 2}},
|
||||
{"ForceOffroad", {CLEAR_ON_MANAGER_START, BOOL, "0", "0"}},
|
||||
{"ForceOnroad", {CLEAR_ON_MANAGER_START, BOOL, "0", "0"}},
|
||||
{"ForceStops", {PERSISTENT, BOOL, "0", "0", 2}},
|
||||
{"ForceStops", {PERSISTENT, BOOL, "1", "0", 2}},
|
||||
{"ForceStopDistanceOffset", {PERSISTENT, INT, "0", "0", 2}},
|
||||
{"ForceStandstill", {PERSISTENT, BOOL, "0", "0", 2}},
|
||||
{"ForceTorqueController", {PERSISTENT, BOOL, "0", "0", 3}},
|
||||
{"FPSCounter", {PERSISTENT, BOOL, "1", "0", 3}},
|
||||
@@ -290,6 +310,10 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
|
||||
{"GoatScreamCriticalAlerts", {PERSISTENT, BOOL, "0", "0", 1}},
|
||||
{"GreenLightAlert", {PERSISTENT, BOOL, "0", "0", 0}},
|
||||
{"HideAlerts", {PERSISTENT, BOOL, "0", "0", 2}},
|
||||
{"HideChangingLanesBanner", {PERSISTENT, BOOL, "0", "0", 2}},
|
||||
{"HideDistanceProfileBanner", {PERSISTENT, BOOL, "0", "0", 2}},
|
||||
{"HideTurningBanner", {PERSISTENT, BOOL, "0", "0", 2}},
|
||||
{"HideDMIcon", {PERSISTENT, BOOL, "0", "0", 2}},
|
||||
{"HideLeadMarker", {PERSISTENT, BOOL, "0", "0", 2}},
|
||||
{"HideMaxSpeed", {PERSISTENT, BOOL, "0", "0", 2}},
|
||||
{"HideSpeed", {PERSISTENT, BOOL, "0", "0", 2}},
|
||||
@@ -297,7 +321,6 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
|
||||
{"HigherBitrate", {PERSISTENT, BOOL, "0", "0", 2}},
|
||||
{"HolidayThemes", {PERSISTENT, BOOL, "1", "0", 0}},
|
||||
{"HumanAcceleration", {PERSISTENT, BOOL, "0", "0", 2}},
|
||||
{"HumanFollowing", {PERSISTENT, BOOL, "0", "0", 2}},
|
||||
{"CoastUpToLeads", {PERSISTENT, BOOL, "1", "1", 2}},
|
||||
{"HumanLaneChanges", {PERSISTENT, BOOL, "0", "0", 2}},
|
||||
{"IconPack", {PERSISTENT, STRING, "frog-animated", "stock", 0}},
|
||||
@@ -307,6 +330,7 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
|
||||
{"IncreasedStoppedDistanceRain", {PERSISTENT, FLOAT, "0.0", "0.0", 2}},
|
||||
{"IncreasedStoppedDistanceRainStorm", {PERSISTENT, FLOAT, "0.0", "0.0", 2}},
|
||||
{"IncreasedStoppedDistanceSnow", {PERSISTENT, FLOAT, "0.0", "0.0", 2}},
|
||||
{"RedneckCruise", {PERSISTENT, BOOL, "0", "0", 1}},
|
||||
{"IncreaseFollowingLowVisibility", {PERSISTENT, FLOAT, "0.0", "0.0", 2}},
|
||||
{"IncreaseFollowingRain", {PERSISTENT, FLOAT, "0.0", "0.0", 2}},
|
||||
{"IncreaseFollowingRainStorm", {PERSISTENT, FLOAT, "0.0", "0.0", 2}},
|
||||
@@ -330,6 +354,7 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
|
||||
{"LKASButtonControl", {PERSISTENT, INT, "5", "0", 2}},
|
||||
{"LockDoors", {PERSISTENT, BOOL, "1", "0", 0}},
|
||||
{"LockDoorsTimer", {PERSISTENT, INT, "0", "0", 0}},
|
||||
{"LongCancelButtonControl", {PERSISTENT, INT, "5", "0", 2}},
|
||||
{"LongDistanceButtonControl", {PERSISTENT, INT, "5", "0", 2}},
|
||||
{"LongModeButtonControl", {PERSISTENT, INT, "0", "0", 2}},
|
||||
{"LongStarButtonControl", {PERSISTENT, INT, "0", "0", 2}},
|
||||
@@ -400,6 +425,7 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
|
||||
{"PauseAOLOnBrake", {PERSISTENT, BOOL, "0", "0", 1}},
|
||||
{"PauseLateralOnSignal", {PERSISTENT, BOOL, "0", "0", 1}},
|
||||
{"PauseLateralSpeed", {PERSISTENT, FLOAT, "0.0", "0.0", 1}},
|
||||
{"LateralResumeDelay", {PERSISTENT, FLOAT, "0.0", "0.0", 1}},
|
||||
{"PedalsOnUI", {PERSISTENT, BOOL, "0", "0", 1}},
|
||||
{"PondPaired", {PERSISTENT, BOOL, "0", "0", 0}},
|
||||
{"PondUploadPending", {PERSISTENT, BOOL, "0", "0", 0}},
|
||||
@@ -424,13 +450,13 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
|
||||
{"ReduceLateralAccelerationRainStorm", {PERSISTENT, INT, "0", "0", 2}},
|
||||
{"ReduceLateralAccelerationSnow", {PERSISTENT, INT, "0", "0", 2}},
|
||||
{"RefuseVolume", {PERSISTENT, INT, "101", "101", 2}},
|
||||
{"RelaxedFollow", {PERSISTENT, FLOAT, "1.75", "1.75", 2}},
|
||||
{"RelaxedFollowHigh", {PERSISTENT, FLOAT, "1.75", "1.75", 2}},
|
||||
{"RelaxedJerkAcceleration", {PERSISTENT, FLOAT, "50.0", "50.0", 3}},
|
||||
{"RelaxedFollow", {PERSISTENT, FLOAT, "1.6", "1.6", 2}},
|
||||
{"RelaxedFollowHigh", {PERSISTENT, FLOAT, "1.4", "1.4", 2}},
|
||||
{"RelaxedJerkAcceleration", {PERSISTENT, FLOAT, "100.0", "100.0", 3}},
|
||||
{"RelaxedJerkDanger", {PERSISTENT, FLOAT, "100.0", "100.0", 3}},
|
||||
{"RelaxedJerkDeceleration", {PERSISTENT, FLOAT, "50.0", "50.0", 3}},
|
||||
{"RelaxedJerkSpeed", {PERSISTENT, FLOAT, "50.0", "50.0", 3}},
|
||||
{"RelaxedJerkSpeedDecrease", {PERSISTENT, FLOAT, "50.0", "50.0", 3}},
|
||||
{"RelaxedJerkDeceleration", {PERSISTENT, FLOAT, "100.0", "100.0", 3}},
|
||||
{"RelaxedJerkSpeed", {PERSISTENT, FLOAT, "100.0", "100.0", 3}},
|
||||
{"RelaxedJerkSpeedDecrease", {PERSISTENT, FLOAT, "100.0", "100.0", 3}},
|
||||
{"ReverseCruise", {PERSISTENT, BOOL, "0", "0", 1}},
|
||||
{"RecoveryPower", {PERSISTENT, FLOAT, "1.0", "1.0", 2}},
|
||||
{"RoadEdgesWidth", {PERSISTENT, FLOAT, "2.0", "2.0", 2}},
|
||||
@@ -453,6 +479,7 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
|
||||
{"ShowGPU", {PERSISTENT, BOOL, "0", "0", 3}},
|
||||
{"ShowIP", {PERSISTENT, BOOL, "0", "0", 3}},
|
||||
{"ShowMemoryUsage", {PERSISTENT, BOOL, "1", "0", 3}},
|
||||
{"ShowModeStatusBanner", {PERSISTENT, BOOL, "1", "0", 2}},
|
||||
{"ShownToggleDescriptions", {PERSISTENT, JSON, "{}", "{}"}},
|
||||
{"ShowSLCOffset", {PERSISTENT, BOOL, "1", "0", 0}},
|
||||
{"ShowSpeedLimits", {PERSISTENT, BOOL, "1", "0", 1}},
|
||||
@@ -467,6 +494,8 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
|
||||
{"SignalMetrics", {PERSISTENT, BOOL, "0", "0", 3}},
|
||||
{"SignalToDownload", {CLEAR_ON_MANAGER_START, STRING, "", ""}},
|
||||
{"SimpleMode", {PERSISTENT, BOOL, "0", "0", 0}},
|
||||
{"SLCAbbreviatedSources", {PERSISTENT, BOOL, "0", "0", 3}},
|
||||
{"SLCActiveSourcesOnly", {PERSISTENT, BOOL, "0", "0", 3}},
|
||||
{"SLCConfirmation", {PERSISTENT, BOOL, "0", "0", 0}},
|
||||
{"SLCConfirmationHigher", {PERSISTENT, BOOL, "0", "0", 0}},
|
||||
{"SLCConfirmationLower", {PERSISTENT, BOOL, "0", "0", 0}},
|
||||
@@ -481,6 +510,8 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
|
||||
{"SNGHack", {PERSISTENT, BOOL, "1", "0", 2}},
|
||||
{"SoundPack", {PERSISTENT, STRING, "frog", "stock", 0}},
|
||||
{"SoundToDownload", {CLEAR_ON_MANAGER_START, STRING, "", ""}},
|
||||
{"SLCAdoptSpeedLimit", {CLEAR_ON_MANAGER_START, BOOL, "0", "0"}},
|
||||
{"SLCForceCruiseSpeed", {CLEAR_ON_MANAGER_START, FLOAT, "0.0", "0.0"}},
|
||||
{"SpeedLimitAccepted", {CLEAR_ON_MANAGER_START, BOOL, "0", "0"}},
|
||||
{"SpeedLimitChangedAlert", {PERSISTENT, BOOL, "0", "0", 0}},
|
||||
{"SpeedLimitController", {PERSISTENT, BOOL, "0", "0", 0}},
|
||||
@@ -493,12 +524,12 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
|
||||
{"VisionSpeedLimitDetection", {PERSISTENT, BOOL, "0", "0", 0}},
|
||||
{"VisionSpeedLimitTrainingCollector", {PERSISTENT, BOOL, "1", "1", 0}},
|
||||
{"StandardFollow", {PERSISTENT, FLOAT, "1.45", "1.45", 2}},
|
||||
{"StandardFollowHigh", {PERSISTENT, FLOAT, "1.45", "1.45", 2}},
|
||||
{"StandardJerkAcceleration", {PERSISTENT, FLOAT, "50.0", "50.0", 3}},
|
||||
{"StandardFollowHigh", {PERSISTENT, FLOAT, "1.2", "1.2", 2}},
|
||||
{"StandardJerkAcceleration", {PERSISTENT, FLOAT, "100.0", "100.0", 3}},
|
||||
{"StandardJerkDanger", {PERSISTENT, FLOAT, "100.0", "100.0", 3}},
|
||||
{"StandardJerkDeceleration", {PERSISTENT, FLOAT, "50.0", "50.0", 3}},
|
||||
{"StandardJerkSpeed", {PERSISTENT, FLOAT, "50.0", "50.0", 3}},
|
||||
{"StandardJerkSpeedDecrease", {PERSISTENT, FLOAT, "50.0", "50.0", 3}},
|
||||
{"StandardJerkDeceleration", {PERSISTENT, FLOAT, "100.0", "100.0", 3}},
|
||||
{"StandardJerkSpeed", {PERSISTENT, FLOAT, "100.0", "100.0", 3}},
|
||||
{"StandardJerkSpeedDecrease", {PERSISTENT, FLOAT, "100.0", "100.0", 3}},
|
||||
{"StandbyMode", {PERSISTENT, BOOL, "0", "0", 1}},
|
||||
{"StartAccel", {PERSISTENT, FLOAT, "0.0", "0.0", 3}},
|
||||
{"StartAccelStock", {PERSISTENT, FLOAT, "0.0", "0.0", 3}},
|
||||
@@ -529,7 +560,6 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
|
||||
{"SwitchbackModeEnabled", {CLEAR_ON_OFFROAD_TRANSITION, BOOL, "0", "0"}},
|
||||
{"SubaruSNG", {PERSISTENT, BOOL, "1", "0", 2}},
|
||||
{"TacoTune", {PERSISTENT, BOOL, "0", "0", 2}},
|
||||
{"TacoTuneHacks", {PERSISTENT, BOOL, "0", "0", 2}},
|
||||
{"TestAlert", {CLEAR_ON_MANAGER_START, STRING, "", ""}},
|
||||
{"TetheringEnabled", {PERSISTENT, INT, "0", "0", 0}},
|
||||
{"ThemeDownloadProgress", {CLEAR_ON_MANAGER_START, STRING, "", ""}},
|
||||
@@ -562,10 +592,12 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
|
||||
{"VEgoStartingStock", {PERSISTENT, FLOAT, "0.0", "0.0", 3}},
|
||||
{"VEgoStopping", {PERSISTENT, FLOAT, "0.0", "0.0", 3}},
|
||||
{"VEgoStoppingStock", {PERSISTENT, FLOAT, "0.0", "0.0", 3}},
|
||||
{"VeryLongCancelButtonControl", {PERSISTENT, INT, "6", "0", 2}},
|
||||
{"VeryLongDistanceButtonControl", {PERSISTENT, INT, "6", "0", 2}},
|
||||
{"VeryLongModeButtonControl", {PERSISTENT, INT, "0", "0", 2}},
|
||||
{"VeryLongStarButtonControl", {PERSISTENT, INT, "0", "0", 2}},
|
||||
{"VoltSNG", {PERSISTENT, BOOL, "0", "0", 2}},
|
||||
{"GMAutoHold", {PERSISTENT, BOOL, "0", "0", 2}},
|
||||
{"WarningImmediateVolume", {PERSISTENT, INT, "101", "101", 2}},
|
||||
{"WarningSoftVolume", {PERSISTENT, INT, "101", "101", 2}},
|
||||
{"WeatherPresets", {PERSISTENT, BOOL, "0", "0", 2}},
|
||||
|
||||
Binary file not shown.
@@ -0,0 +1,113 @@
|
||||
# TrueNAS SCALE GitHub Build Runner
|
||||
|
||||
This setup runs a single GitHub self-hosted runner as a TrueNAS SCALE custom app and routes the `Compile StarPilot` workflow onto that runner.
|
||||
|
||||
The app container:
|
||||
|
||||
- registers one repository-scoped GitHub Actions runner
|
||||
- stays visible in the TrueNAS Apps UI
|
||||
- mounts the host Docker socket so `./build` can launch the existing larch64 build container flow
|
||||
- persists runner state and build caches in one dataset mounted at `/runner`
|
||||
|
||||
## What this workflow does
|
||||
|
||||
`.github/workflows/compile_starpilot.yaml` now:
|
||||
|
||||
- lets an admin choose a `target_ref` branch from the Actions UI
|
||||
- checks out that branch on the NAS runner
|
||||
- runs `./build`
|
||||
- prunes the tree with `scripts/ci_package_prebuilt_tree.sh`
|
||||
- commits the result as `build`
|
||||
- pushes that commit back to the selected branch without force-pushing
|
||||
|
||||
Use a branch intended for built output. The prune step removes developer-facing files and is not meant for normal day-to-day development branches.
|
||||
|
||||
## 1. Publish the runner image
|
||||
|
||||
You need a registry image for TrueNAS to deploy. The repo includes `.github/workflows/publish_truenas_runner.yaml` for this.
|
||||
|
||||
1. Open **Actions** on GitHub.
|
||||
2. Run **Publish TrueNAS Runner Image**.
|
||||
3. Leave `image_tag` as `latest` unless you want a versioned tag.
|
||||
4. After it finishes, the image is available at:
|
||||
|
||||
```text
|
||||
ghcr.io/<repo-owner>/starpilot-truenas-runner:latest
|
||||
```
|
||||
|
||||
If you prefer to build it locally, run:
|
||||
|
||||
```bash
|
||||
docker build -f tools/truenas_github_runner/Dockerfile -t ghcr.io/<repo-owner>/starpilot-truenas-runner:latest .
|
||||
docker push ghcr.io/<repo-owner>/starpilot-truenas-runner:latest
|
||||
```
|
||||
|
||||
## 2. Create a runner token
|
||||
|
||||
Generate a repository-scoped self-hosted runner token in GitHub:
|
||||
|
||||
1. Open the repository.
|
||||
2. Go to **Settings > Actions > Runners**.
|
||||
3. Click **New self-hosted runner**.
|
||||
4. Choose **Linux** and **x64**.
|
||||
5. Copy the one-time registration token from the generated setup commands.
|
||||
|
||||
That token expires quickly. Paste it into TrueNAS during deployment and restart the app if you need to re-register.
|
||||
|
||||
## 3. Deploy on TrueNAS SCALE
|
||||
|
||||
TrueNAS custom apps support Docker Compose YAML through **Apps > Discover Apps > Custom App > Install via YAML**.
|
||||
|
||||
Create a dataset for persistent runner state first, for example:
|
||||
|
||||
```text
|
||||
/mnt/<pool>/apps/starpilot-runner
|
||||
```
|
||||
|
||||
Then paste this compose YAML into TrueNAS after replacing the placeholders:
|
||||
|
||||
```yaml
|
||||
services:
|
||||
starpilot-runner:
|
||||
image: ghcr.io/<repo-owner>/starpilot-truenas-runner:latest
|
||||
container_name: starpilot-runner
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
GITHUB_REPOSITORY_URL: https://github.com/<repo-owner>/<repo-name>
|
||||
RUNNER_NAME: starpilot-truenas-runner
|
||||
RUNNER_LABELS: self-hosted,truenas,starpilot-build
|
||||
RUNNER_WORKDIR: /runner/_work
|
||||
RUNNER_TOKEN: <paste-one-time-runner-token>
|
||||
DOCKER_GID: ""
|
||||
REMOVE_RUNNER_ON_EXIT: "0"
|
||||
volumes:
|
||||
- /mnt/<pool>/apps/starpilot-runner:/runner
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
```
|
||||
|
||||
If the container starts but cannot talk to Docker, inspect the Docker socket group on TrueNAS and set `DOCKER_GID` to that numeric group id.
|
||||
|
||||
## 4. Verify runner registration
|
||||
|
||||
Confirm all of the following:
|
||||
|
||||
- the app shows as `Running` in TrueNAS
|
||||
- the container passes its healthcheck
|
||||
- GitHub shows the runner as online with labels `self-hosted`, `truenas`, and `starpilot-build`
|
||||
|
||||
## 5. Run a build
|
||||
|
||||
1. Open **Actions > Compile StarPilot**.
|
||||
2. Click **Run workflow**.
|
||||
3. Enter the branch name in `target_ref`.
|
||||
4. Optionally set `jobs` to the parallelism you want for `./build`.
|
||||
5. Start the run.
|
||||
|
||||
On success, the workflow pushes a new commit named `build` back to that branch.
|
||||
|
||||
## Operational notes
|
||||
|
||||
- `RUNNER_TOKEN` is only required on first registration or when you want to replace a stale runner with the same name.
|
||||
- Runner state is persisted in `/runner`, so normal restarts do not need a new token.
|
||||
- `REMOVE_RUNNER_ON_EXIT=1` enables best-effort deregistration on shutdown, but that only works when the provided token is still valid.
|
||||
- The build cache, GitHub workspace, and `.comma_sysroot` data all persist under the runner dataset as long as the workflow runs in the same mounted work area.
|
||||
@@ -0,0 +1,573 @@
|
||||
#!/bin/sh
|
||||
""":"
|
||||
REPO_ROOT="$(CDPATH= cd -- "$(dirname "$0")" && pwd)"
|
||||
PYTHON_BIN="$REPO_ROOT/.venv/bin/python"
|
||||
|
||||
if [ ! -x "$PYTHON_BIN" ]; then
|
||||
echo "Missing $PYTHON_BIN. Run 'uv sync' from $REPO_ROOT first." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
exec "$PYTHON_BIN" "$0" "$@"
|
||||
":"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import bz2
|
||||
import io
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
import sys
|
||||
import zipfile
|
||||
|
||||
from dataclasses import dataclass
|
||||
from datetime import UTC, datetime
|
||||
from email.utils import parsedate_to_datetime
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
from urllib.parse import quote, urlparse
|
||||
|
||||
import requests
|
||||
import zstandard
|
||||
|
||||
from cereal import log
|
||||
|
||||
|
||||
API_HOST = os.getenv("COMMA_API_HOST", "https://api.commadotai.com").rstrip("/")
|
||||
ROUTE_ID_RE = re.compile(r"([0-9a-f]{16})/([^/]+)")
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class StreamSpec:
|
||||
api_key: str
|
||||
display_name: str
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class RouteId:
|
||||
dongle_id: str
|
||||
log_id: str
|
||||
|
||||
@property
|
||||
def canonical_name(self) -> str:
|
||||
return f"{self.dongle_id}|{self.log_id}"
|
||||
|
||||
@property
|
||||
def cli_name(self) -> str:
|
||||
return f"{self.dongle_id}/{self.log_id}"
|
||||
|
||||
@property
|
||||
def safe_name(self) -> str:
|
||||
return f"{self.dongle_id}_{self.log_id}"
|
||||
|
||||
|
||||
@dataclass
|
||||
class DownloadedFile:
|
||||
segment: int
|
||||
stream: str
|
||||
filename: str
|
||||
relative_path: str
|
||||
mtime_epoch: int | None
|
||||
|
||||
|
||||
@dataclass
|
||||
class MapTileSummary:
|
||||
qlogs_scanned: int = 0
|
||||
mapd_messages: int = 0
|
||||
tile_loaded_messages: int = 0
|
||||
first_tile_segment: int | None = None
|
||||
|
||||
@property
|
||||
def tile_loaded_any(self) -> bool:
|
||||
return self.tile_loaded_messages > 0
|
||||
|
||||
|
||||
class ValidationError(Exception):
|
||||
def __init__(self, route: RouteId, reasons: list[str]):
|
||||
self.route = route
|
||||
self.reasons = reasons
|
||||
super().__init__(self._render())
|
||||
|
||||
def _render(self) -> str:
|
||||
lines = [f"Validation failed for {self.route.cli_name}:"]
|
||||
lines.extend(f"- {reason}" for reason in self.reasons)
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
REQUIRED_STREAMS = (
|
||||
StreamSpec("qlogs", "qlog"),
|
||||
StreamSpec("logs", "rlog"),
|
||||
StreamSpec("cameras", "fcamera.hevc"),
|
||||
)
|
||||
|
||||
|
||||
def parse_args() -> argparse.Namespace:
|
||||
argv = list(sys.argv[1:])
|
||||
if argv and ROUTE_ID_RE.fullmatch(argv[0].lstrip("-")):
|
||||
if argv[0].startswith("--"):
|
||||
argv[0] = argv[0][2:]
|
||||
argv = ["--routeid", argv[0], *argv[1:]]
|
||||
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Validate and bundle a public comma route for speed-limit vision review."
|
||||
)
|
||||
parser.add_argument("routeid_positional", nargs="?", help="Route id like 'dongle/logid' or 'dongle/logid--7'.")
|
||||
parser.add_argument("--routeid", help="Route id like 'dongle/logid' or 'dongle/logid--7'.")
|
||||
parser.add_argument("--desktop-root", type=Path, default=Path.home() / "Desktop", help="Destination root for the bundle folder and zip.")
|
||||
parser.add_argument("--timeout", type=float, default=60.0, help="HTTP timeout in seconds.")
|
||||
parser.add_argument("--overwrite", action="store_true", help="Replace an existing Desktop bundle for this route.")
|
||||
parser.add_argument("--validate-only", action="store_true", help="Run validation only and skip downloads/zipping.")
|
||||
args = parser.parse_args(argv)
|
||||
args.routeid = args.routeid or args.routeid_positional
|
||||
if not args.routeid:
|
||||
parser.error("the following arguments are required: --routeid")
|
||||
return args
|
||||
|
||||
|
||||
def parse_route_id(raw: str) -> RouteId:
|
||||
text = raw.strip().strip("'\"").replace("|", "/")
|
||||
match = ROUTE_ID_RE.fullmatch(text)
|
||||
if match is None:
|
||||
raise ValueError(f"Unrecognized route id: {raw}")
|
||||
|
||||
dongle_id = match.group(1)
|
||||
tail = match.group(2)
|
||||
parts = tail.split("--")
|
||||
if len(parts) == 3 and parts[-1].isdigit():
|
||||
log_id = "--".join(parts[:2])
|
||||
else:
|
||||
log_id = tail
|
||||
|
||||
if len(log_id) != 20:
|
||||
raise ValueError(f"Invalid log id in route: {raw}")
|
||||
return RouteId(dongle_id=dongle_id, log_id=log_id)
|
||||
|
||||
|
||||
def route_url(route: RouteId) -> str:
|
||||
return f"{API_HOST}/v1/route/{quote(route.canonical_name, safe='')}/"
|
||||
|
||||
|
||||
def route_files_url(route: RouteId) -> str:
|
||||
return f"{API_HOST}/v1/route/{quote(route.canonical_name, safe='')}/files"
|
||||
|
||||
|
||||
def format_segments(segments: list[int]) -> str:
|
||||
if not segments:
|
||||
return "none"
|
||||
|
||||
ranges: list[str] = []
|
||||
start = prev = segments[0]
|
||||
for segment in segments[1:]:
|
||||
if segment == prev + 1:
|
||||
prev = segment
|
||||
continue
|
||||
ranges.append(f"{start}-{prev}" if start != prev else str(start))
|
||||
start = prev = segment
|
||||
ranges.append(f"{start}-{prev}" if start != prev else str(start))
|
||||
return ", ".join(ranges)
|
||||
|
||||
|
||||
def as_int(value: Any) -> int | None:
|
||||
if isinstance(value, bool):
|
||||
return int(value)
|
||||
if isinstance(value, int):
|
||||
return value
|
||||
if isinstance(value, str) and value.isdigit():
|
||||
return int(value)
|
||||
return None
|
||||
|
||||
|
||||
def relative_posix(path: Path, root: Path) -> str:
|
||||
return path.relative_to(root).as_posix()
|
||||
|
||||
|
||||
def fetch_json(session: requests.Session, url: str, timeout: float) -> Any:
|
||||
response = session.get(url, timeout=timeout, allow_redirects=True)
|
||||
response.raise_for_status()
|
||||
return response.json()
|
||||
|
||||
|
||||
def fetch_bytes(session: requests.Session, url: str, timeout: float) -> bytes:
|
||||
response = session.get(url, timeout=timeout, allow_redirects=True)
|
||||
response.raise_for_status()
|
||||
return response.content
|
||||
|
||||
|
||||
def fetch_stream_to_path(session: requests.Session, url: str, dest_path: Path, timeout: float) -> int | None:
|
||||
dest_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
response = session.get(url, stream=True, timeout=timeout, allow_redirects=True)
|
||||
response.raise_for_status()
|
||||
|
||||
temp_path = dest_path.with_suffix(dest_path.suffix + ".part")
|
||||
with temp_path.open("wb") as handle:
|
||||
for chunk in response.iter_content(chunk_size=1 << 20):
|
||||
if chunk:
|
||||
handle.write(chunk)
|
||||
temp_path.replace(dest_path)
|
||||
|
||||
last_modified = response.headers.get("Last-Modified")
|
||||
if not last_modified:
|
||||
return None
|
||||
|
||||
epoch = int(parsedate_to_datetime(last_modified).timestamp())
|
||||
os.utime(dest_path, (epoch, epoch))
|
||||
return epoch
|
||||
|
||||
|
||||
def decompress_log_bytes(payload: bytes) -> bytes:
|
||||
if payload[:3] == b"BZh":
|
||||
return bz2.decompress(payload)
|
||||
if payload[:4] == b"\x28\xb5\x2f\xfd":
|
||||
reader = zstandard.ZstdDecompressor().stream_reader(io.BytesIO(payload))
|
||||
try:
|
||||
return reader.read()
|
||||
finally:
|
||||
reader.close()
|
||||
return payload
|
||||
|
||||
|
||||
def load_init_params(qlog_bytes: bytes) -> dict[str, str]:
|
||||
payload = decompress_log_bytes(qlog_bytes)
|
||||
for msg in log.Event.read_multiple_bytes(payload):
|
||||
if msg.which() != "initData":
|
||||
continue
|
||||
|
||||
params: dict[str, str] = {}
|
||||
entries = msg.initData.params.to_dict().get("entries", [])
|
||||
for entry in entries:
|
||||
key = entry.get("key")
|
||||
if not key:
|
||||
continue
|
||||
value = entry.get("value", b"")
|
||||
if isinstance(value, bytes):
|
||||
value = value.decode("utf-8", "replace")
|
||||
elif value is None:
|
||||
value = ""
|
||||
else:
|
||||
value = str(value)
|
||||
params[str(key)] = value
|
||||
return params
|
||||
|
||||
raise RuntimeError("No initData params found in qlog.")
|
||||
|
||||
|
||||
def scan_qlogs_for_map_tiles(session: requests.Session, qlog_urls: list[str | None], timeout: float) -> MapTileSummary:
|
||||
summary = MapTileSummary()
|
||||
for segment, url in enumerate(qlog_urls):
|
||||
if not isinstance(url, str) or not url:
|
||||
continue
|
||||
|
||||
summary.qlogs_scanned += 1
|
||||
payload = decompress_log_bytes(fetch_bytes(session, url, timeout))
|
||||
for msg in log.Event.read_multiple_bytes(payload):
|
||||
if msg.which() != "mapdOut":
|
||||
continue
|
||||
summary.mapd_messages += 1
|
||||
if msg.mapdOut.tileLoaded:
|
||||
summary.tile_loaded_messages += 1
|
||||
if summary.first_tile_segment is None:
|
||||
summary.first_tile_segment = segment
|
||||
if summary.tile_loaded_any:
|
||||
break
|
||||
return summary
|
||||
|
||||
|
||||
def expected_segment_count(route_meta: dict[str, Any], files_payload: dict[str, Any]) -> int:
|
||||
candidates: list[int] = []
|
||||
for key in ("maxqlog", "maxlog"):
|
||||
value = as_int(route_meta.get(key))
|
||||
if value is not None:
|
||||
candidates.append(value + 1)
|
||||
|
||||
for key in ("qlogs", "logs", "cameras"):
|
||||
value = files_payload.get(key)
|
||||
if isinstance(value, list):
|
||||
candidates.append(len(value))
|
||||
|
||||
return max(candidates, default=0)
|
||||
|
||||
|
||||
def collect_missing_segments(urls: list[Any], expected_segments: int) -> list[int]:
|
||||
missing: list[int] = []
|
||||
for segment in range(expected_segments):
|
||||
url = urls[segment] if segment < len(urls) else None
|
||||
if not isinstance(url, str) or not url:
|
||||
missing.append(segment)
|
||||
return missing
|
||||
|
||||
|
||||
def first_available_url(urls: list[Any]) -> str | None:
|
||||
for url in urls:
|
||||
if isinstance(url, str) and url:
|
||||
return url
|
||||
return None
|
||||
|
||||
|
||||
def filename_from_url(url: str) -> str:
|
||||
return Path(urlparse(url).path).name
|
||||
|
||||
|
||||
def validate_route(route: RouteId, session: requests.Session, timeout: float) -> dict[str, Any]:
|
||||
try:
|
||||
route_meta = fetch_json(session, route_url(route), timeout)
|
||||
except requests.HTTPError as exc:
|
||||
status_code = exc.response.status_code if exc.response is not None else "unknown"
|
||||
raise ValidationError(route, [f"route is not publicly accessible from comma connect (HTTP {status_code})."]) from exc
|
||||
|
||||
if not route_meta.get("is_public", False):
|
||||
raise ValidationError(route, ["route metadata loaded, but `is_public` was false."])
|
||||
|
||||
try:
|
||||
files_payload = fetch_json(session, route_files_url(route), timeout)
|
||||
except requests.HTTPError as exc:
|
||||
status_code = exc.response.status_code if exc.response is not None else "unknown"
|
||||
raise ValidationError(route, [f"public route files could not be fetched from comma connect (HTTP {status_code})."]) from exc
|
||||
|
||||
expected_segments = expected_segment_count(route_meta, files_payload)
|
||||
if expected_segments <= 0:
|
||||
raise ValidationError(route, ["could not determine any route segments from comma connect."])
|
||||
|
||||
failures: list[str] = []
|
||||
stream_urls: dict[str, list[Any]] = {}
|
||||
for spec in REQUIRED_STREAMS:
|
||||
urls = files_payload.get(spec.api_key)
|
||||
if not isinstance(urls, list):
|
||||
urls = []
|
||||
stream_urls[spec.api_key] = urls
|
||||
missing_segments = collect_missing_segments(urls, expected_segments)
|
||||
if missing_segments:
|
||||
failures.append(f"missing uploaded {spec.display_name} files for segment(s): {format_segments(missing_segments)}.")
|
||||
|
||||
first_qlog_url = first_available_url(stream_urls["qlogs"])
|
||||
params: dict[str, str] = {}
|
||||
map_tiles = MapTileSummary()
|
||||
if first_qlog_url is None:
|
||||
failures.append("no qlog was available to inspect route params.")
|
||||
else:
|
||||
try:
|
||||
params = load_init_params(fetch_bytes(session, first_qlog_url, timeout))
|
||||
except Exception as exc:
|
||||
failures.append(f"could not read `initData.params` from the first qlog: {exc}.")
|
||||
|
||||
if params:
|
||||
auto_bookmark = params.get("VisionSpeedLimitAutoBookmark", "")
|
||||
if auto_bookmark != "1":
|
||||
failures.append(f"`VisionSpeedLimitAutoBookmark` was {auto_bookmark!r}; it must be '1'.")
|
||||
|
||||
vision_detection = params.get("VisionSpeedLimitDetection", "")
|
||||
if vision_detection != "1":
|
||||
failures.append(f"`VisionSpeedLimitDetection` was {vision_detection!r}; it must be '1' for useful vision-model collection.")
|
||||
|
||||
try:
|
||||
qlog_urls = [url if isinstance(url, str) and url else None for url in stream_urls["qlogs"]]
|
||||
map_tiles = scan_qlogs_for_map_tiles(session, qlog_urls, timeout)
|
||||
if not map_tiles.tile_loaded_any:
|
||||
maps_selected = params.get("MapsSelected", "")
|
||||
if maps_selected:
|
||||
failures.append(
|
||||
"offline map tiles never loaded during this route even though "
|
||||
f"`MapsSelected` was {maps_selected!r}; tell the user to download local maps for the driven area so collection trains faster."
|
||||
)
|
||||
else:
|
||||
failures.append(
|
||||
"offline map tiles never loaded during this route and `MapsSelected` was empty; "
|
||||
"tell the user to download local maps for the driven area so collection trains faster."
|
||||
)
|
||||
except Exception as exc:
|
||||
failures.append(f"could not inspect qlogs for offline map usage: {exc}.")
|
||||
|
||||
if failures:
|
||||
raise ValidationError(route, failures)
|
||||
|
||||
return {
|
||||
"route_meta": route_meta,
|
||||
"files_payload": files_payload,
|
||||
"expected_segments": expected_segments,
|
||||
"params": params,
|
||||
"map_tiles": map_tiles,
|
||||
}
|
||||
|
||||
|
||||
def prepare_output_paths(route: RouteId, desktop_root: Path, overwrite: bool) -> tuple[Path, Path]:
|
||||
desktop_root = desktop_root.expanduser().resolve()
|
||||
desktop_root.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
bundle_root = desktop_root / f"speed_limit_route_bundle_{route.safe_name}"
|
||||
zip_path = desktop_root / f"{bundle_root.name}.zip"
|
||||
if overwrite:
|
||||
if bundle_root.exists():
|
||||
shutil.rmtree(bundle_root)
|
||||
if zip_path.exists():
|
||||
zip_path.unlink()
|
||||
else:
|
||||
if bundle_root.exists():
|
||||
raise FileExistsError(f"Bundle folder already exists: {bundle_root}")
|
||||
if zip_path.exists():
|
||||
raise FileExistsError(f"Bundle zip already exists: {zip_path}")
|
||||
return bundle_root, zip_path
|
||||
|
||||
|
||||
def write_bundle_manifests(
|
||||
bundle_root: Path,
|
||||
route: RouteId,
|
||||
validation: dict[str, Any],
|
||||
downloaded_files: list[DownloadedFile],
|
||||
) -> None:
|
||||
live_routes_meta = bundle_root / "live_routes_meta"
|
||||
live_routes_meta.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
files_txt_lines: list[str] = []
|
||||
qlog_mtimes_lines: list[str] = []
|
||||
for record in sorted(downloaded_files, key=lambda item: (item.segment, item.stream, item.filename)):
|
||||
segment_name = f"{route.log_id}--{record.segment}"
|
||||
files_txt_lines.append(f"{segment_name} {record.relative_path}")
|
||||
if record.filename.startswith("qlog") and record.mtime_epoch is not None:
|
||||
qlog_mtimes_lines.append(f"{record.relative_path} {record.mtime_epoch}")
|
||||
|
||||
(live_routes_meta / "files.txt").write_text("\n".join(files_txt_lines) + ("\n" if files_txt_lines else ""), encoding="utf-8")
|
||||
(live_routes_meta / "qlog_mtimes.txt").write_text("\n".join(qlog_mtimes_lines) + ("\n" if qlog_mtimes_lines else ""), encoding="utf-8")
|
||||
|
||||
params = validation["params"]
|
||||
route_meta = validation["route_meta"]
|
||||
map_tiles: MapTileSummary = validation["map_tiles"]
|
||||
manifest = {
|
||||
"bundleCreatedAt": datetime.now(UTC).isoformat(),
|
||||
"routeId": route.cli_name,
|
||||
"routeFullname": route.canonical_name,
|
||||
"expectedSegments": validation["expected_segments"],
|
||||
"downloadedStreams": [spec.display_name for spec in REQUIRED_STREAMS],
|
||||
"validation": {
|
||||
"isPublic": bool(route_meta.get("is_public")),
|
||||
"visionSpeedLimitAutoBookmark": params.get("VisionSpeedLimitAutoBookmark", ""),
|
||||
"visionSpeedLimitDetection": params.get("VisionSpeedLimitDetection", ""),
|
||||
"mapsSelected": params.get("MapsSelected", ""),
|
||||
"lastMapsUpdate": params.get("LastMapsUpdate", ""),
|
||||
"mapdMessagesScanned": map_tiles.mapd_messages,
|
||||
"offlineTileLoaded": map_tiles.tile_loaded_any,
|
||||
"firstTileSegment": map_tiles.first_tile_segment,
|
||||
},
|
||||
"routeMeta": {
|
||||
"startTime": route_meta.get("start_time"),
|
||||
"endTime": route_meta.get("end_time"),
|
||||
"distance": route_meta.get("distance"),
|
||||
"startLat": route_meta.get("start_lat"),
|
||||
"startLng": route_meta.get("start_lng"),
|
||||
"endLat": route_meta.get("end_lat"),
|
||||
"endLng": route_meta.get("end_lng"),
|
||||
"gitBranch": route_meta.get("git_branch"),
|
||||
"gitCommit": route_meta.get("git_commit"),
|
||||
"platform": route_meta.get("platform"),
|
||||
"version": route_meta.get("version"),
|
||||
"make": route_meta.get("make"),
|
||||
},
|
||||
"files": [
|
||||
{
|
||||
"segment": record.segment,
|
||||
"stream": record.stream,
|
||||
"filename": record.filename,
|
||||
"relativePath": record.relative_path,
|
||||
"mtimeEpoch": record.mtime_epoch,
|
||||
}
|
||||
for record in sorted(downloaded_files, key=lambda item: (item.segment, item.stream, item.filename))
|
||||
],
|
||||
}
|
||||
(bundle_root / "bundle_manifest.json").write_text(json.dumps(manifest, indent=2, sort_keys=True) + "\n", encoding="utf-8")
|
||||
|
||||
|
||||
def zip_bundle(bundle_root: Path, zip_path: Path) -> None:
|
||||
with zipfile.ZipFile(zip_path, "w", compression=zipfile.ZIP_STORED) as archive:
|
||||
for path in sorted(bundle_root.rglob("*")):
|
||||
if path.is_file():
|
||||
archive.write(path, arcname=Path(bundle_root.name) / path.relative_to(bundle_root))
|
||||
|
||||
|
||||
def download_bundle(
|
||||
route: RouteId,
|
||||
validation: dict[str, Any],
|
||||
session: requests.Session,
|
||||
desktop_root: Path,
|
||||
timeout: float,
|
||||
overwrite: bool,
|
||||
) -> tuple[Path, Path]:
|
||||
bundle_root, zip_path = prepare_output_paths(route, desktop_root, overwrite)
|
||||
clip_root = bundle_root / "data" / "media" / "0" / "realdata"
|
||||
|
||||
expected_segments = validation["expected_segments"]
|
||||
files_payload = validation["files_payload"]
|
||||
downloaded_files: list[DownloadedFile] = []
|
||||
|
||||
for spec in REQUIRED_STREAMS:
|
||||
urls = files_payload.get(spec.api_key, [])
|
||||
for segment in range(expected_segments):
|
||||
url = urls[segment]
|
||||
if not isinstance(url, str) or not url:
|
||||
raise RuntimeError(f"Unexpected missing {spec.display_name} URL for segment {segment} after validation.")
|
||||
|
||||
filename = filename_from_url(url)
|
||||
segment_name = f"{route.log_id}--{segment}"
|
||||
dest_path = clip_root / segment_name / filename
|
||||
print(f"Downloading {segment_name}/{filename}")
|
||||
mtime_epoch = fetch_stream_to_path(session, url, dest_path, timeout)
|
||||
downloaded_files.append(
|
||||
DownloadedFile(
|
||||
segment=segment,
|
||||
stream=spec.display_name,
|
||||
filename=filename,
|
||||
relative_path=relative_posix(dest_path, bundle_root),
|
||||
mtime_epoch=mtime_epoch,
|
||||
)
|
||||
)
|
||||
|
||||
write_bundle_manifests(bundle_root, route, validation, downloaded_files)
|
||||
print(f"Creating {zip_path.name}")
|
||||
zip_bundle(bundle_root, zip_path)
|
||||
return bundle_root, zip_path
|
||||
|
||||
|
||||
def print_validation_summary(route: RouteId, validation: dict[str, Any]) -> None:
|
||||
params = validation["params"]
|
||||
map_tiles: MapTileSummary = validation["map_tiles"]
|
||||
print(f"Validated {route.cli_name}")
|
||||
print(f" public route: yes")
|
||||
print(f" segments: {validation['expected_segments']}")
|
||||
print(f" VisionSpeedLimitDetection: {params.get('VisionSpeedLimitDetection', '')}")
|
||||
print(f" VisionSpeedLimitAutoBookmark: {params.get('VisionSpeedLimitAutoBookmark', '')}")
|
||||
print(f" MapsSelected: {params.get('MapsSelected', '') or '(empty)'}")
|
||||
print(f" offline tile loaded: {'yes' if map_tiles.tile_loaded_any else 'no'}")
|
||||
|
||||
|
||||
def main() -> int:
|
||||
args = parse_args()
|
||||
route = parse_route_id(args.routeid)
|
||||
|
||||
session = requests.Session()
|
||||
session.headers.update({"User-Agent": "starpilot-speed-limit-route-bundler/1.0"})
|
||||
|
||||
try:
|
||||
validation = validate_route(route, session, args.timeout)
|
||||
print_validation_summary(route, validation)
|
||||
|
||||
if args.validate_only:
|
||||
return 0
|
||||
|
||||
bundle_root, zip_path = download_bundle(route, validation, session, args.desktop_root, args.timeout, args.overwrite)
|
||||
print(f"Bundle folder: {bundle_root}")
|
||||
print(f"Bundle zip: {zip_path}")
|
||||
return 0
|
||||
except ValidationError as exc:
|
||||
print(str(exc), file=sys.stderr)
|
||||
return 2
|
||||
except FileExistsError as exc:
|
||||
print(f"{exc}\nUse --overwrite to replace the existing Desktop bundle, or --validate-only to skip packaging.", file=sys.stderr)
|
||||
return 3
|
||||
except Exception as exc:
|
||||
print(f"Error: {exc}", file=sys.stderr)
|
||||
return 1
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
@@ -122,7 +122,7 @@ class CanBusBase:
|
||||
assert fingerprint is not None
|
||||
num = max([k for k, v in fingerprint.items() if len(v)], default=0) // 4 + 1
|
||||
else:
|
||||
num = len(CP.safetyConfigs)
|
||||
num = max(1, len(CP.safetyConfigs))
|
||||
self.offset = 4 * (num - 1)
|
||||
|
||||
|
||||
|
||||
@@ -205,6 +205,11 @@ struct CarState {
|
||||
vehicleSensorsInvalid @52 :Bool; # invalid steering angle readings, etc.
|
||||
lowSpeedAlert @56 :Bool; # lost steering control due to a dynamic min steering speed
|
||||
blockPcmEnable @60 :Bool; # whether to allow PCM to enable this frame
|
||||
pedalMaxRegen @61 :Bool; # pedal at max regen, driver should use brake for more decel
|
||||
pedalLongActive @62 :Bool; # Pre-AP pedal longitudinal mode is active (enableLongControl)
|
||||
teslaCCEngaged @63 :Bool; # rising edge of stock Tesla CC engaging (no-pedal mode)
|
||||
teslaCCDisengaged @64 :Bool; # falling edge of stock Tesla CC
|
||||
teslaCCNotArmed @65 :Bool; # lateral engaged but DI_cruiseState != STANDBY/ENABLED
|
||||
|
||||
# cruise state
|
||||
cruiseState @10 :CruiseState;
|
||||
@@ -635,6 +640,7 @@ struct CarParams {
|
||||
fcaGiorgio @32;
|
||||
rivian @33;
|
||||
volkswagenMeb @34;
|
||||
teslaPreap @35;
|
||||
}
|
||||
|
||||
enum SteerControlType {
|
||||
|
||||
@@ -11,6 +11,7 @@ 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.mock.values import CAR as MOCK
|
||||
from opendbc.car.toyota.values import ToyotaSafetyFlags
|
||||
from opendbc.car.values import BRANDS
|
||||
from opendbc.car.vin import get_vin, is_valid_vin, VIN_UNKNOWN
|
||||
from openpilot.common.params import Params
|
||||
@@ -108,6 +109,20 @@ def _normalize_gm_bolt_candidate(candidate: str | None, fingerprints: dict[int,
|
||||
return candidate
|
||||
|
||||
|
||||
def _apply_disable_openpilot_long(CP: CarParams, FPCP: StarPilotCarParams) -> None:
|
||||
CP.openpilotLongitudinalControl = False
|
||||
CP.pcmCruise = True
|
||||
FPCP.openpilotLongitudinalControlDisabled = True
|
||||
|
||||
if CP.brand == "toyota":
|
||||
# Toyota stock longitudinal safety changes the forwarding rules so the
|
||||
# camera's ACC_CONTROL message can reach the PT bus again.
|
||||
for cfg in CP.safetyConfigs:
|
||||
cfg.safetyParam |= ToyotaSafetyFlags.STOCK_LONGITUDINAL.value
|
||||
for cfg in FPCP.safetyConfigs:
|
||||
cfg.safetyParam |= ToyotaSafetyFlags.STOCK_LONGITUDINAL.value
|
||||
|
||||
|
||||
def _normalize_gm_volt_candidate(candidate: str | None, fingerprints: dict[int, dict]) -> str | None:
|
||||
cam = fingerprints.get(GM_CAMERA_BUS, {})
|
||||
has_live_camera_msg = GM_VOLT_CAMERA_MSG in cam
|
||||
@@ -196,6 +211,11 @@ def fingerprint(can_recv: CanRecvCallable, can_send: CanSendCallable, set_obd_mu
|
||||
disable_fw_cache = os.environ.get('DISABLE_FW_CACHE', False)
|
||||
ecu_rx_addrs = set()
|
||||
|
||||
if not fixed_fingerprint and Params().get_bool("NAPForcePreAP"):
|
||||
fixed_fingerprint = "TESLA_MODEL_S_PREAP"
|
||||
skip_fw_query = True
|
||||
carlog.warning("NAPForcePreAP enabled; forcing TESLA_MODEL_S_PREAP fingerprint")
|
||||
|
||||
start_time = time.monotonic()
|
||||
if not skip_fw_query:
|
||||
if cached_params is not None and cached_params.brand != "mock" and len(cached_params.carFw) > 0 and \
|
||||
@@ -318,8 +338,7 @@ def get_car(can_recv: CanRecvCallable, can_send: CanSendCallable, set_obd_multip
|
||||
FPCP: StarPilotCarParams = CarInterface.get_starpilot_params(candidate, fingerprints, car_fw, CP, starpilot_toggles)
|
||||
|
||||
if not CP.alphaLongitudinalAvailable and starpilot_toggles.disable_openpilot_long:
|
||||
CP.openpilotLongitudinalControl = False
|
||||
FPCP.openpilotLongitudinalControlDisabled = True
|
||||
_apply_disable_openpilot_long(CP, FPCP)
|
||||
|
||||
return interfaces[CP.carFingerprint](CP, FPCP)
|
||||
|
||||
|
||||
@@ -97,6 +97,25 @@ class CAR(Platforms):
|
||||
)
|
||||
|
||||
|
||||
PACIFICA_HYBRID_AOL_CARS = frozenset({
|
||||
CAR.CHRYSLER_PACIFICA_2019_HYBRID,
|
||||
})
|
||||
|
||||
|
||||
def pacifica_hybrid_aol_requires_set_press(car_fingerprint, pcm_cruise: bool) -> bool:
|
||||
return car_fingerprint in PACIFICA_HYBRID_AOL_CARS and pcm_cruise
|
||||
|
||||
|
||||
def pacifica_hybrid_aol_stock_acc_mode(car_fingerprint, pcm_cruise: bool,
|
||||
controls_enabled: bool, always_on_lateral_enabled: bool) -> bool:
|
||||
# Keep this narrow until we have logs proving other Chrysler platforms need the same exemption.
|
||||
return (
|
||||
pacifica_hybrid_aol_requires_set_press(car_fingerprint, pcm_cruise) and
|
||||
always_on_lateral_enabled and
|
||||
not controls_enabled
|
||||
)
|
||||
|
||||
|
||||
class CarControllerParams:
|
||||
def __init__(self, CP):
|
||||
self.STEER_STEP = 2 # 50 Hz
|
||||
|
||||
@@ -5,6 +5,8 @@ from opendbc.car.isotp_parallel_query import IsoTpParallelQuery
|
||||
|
||||
EXT_DIAG_REQUEST = b'\x10\x03'
|
||||
EXT_DIAG_RESPONSE = b'\x50\x03'
|
||||
RESET_REQUEST = b'\x11\x01'
|
||||
RESET_RESPONSE = b''
|
||||
|
||||
# File-based logging for debugging
|
||||
ECU_LOG_FILE = "/data/ecu_disable.log"
|
||||
@@ -21,7 +23,7 @@ def ecu_log(msg):
|
||||
pass
|
||||
|
||||
|
||||
def disable_ecu(can_recv, can_send, bus=0, addr=0x7d0, sub_addr=None, com_cont_req=b'\x28\x83\x01', timeout=0.1, retry=10):
|
||||
def disable_ecu(can_recv, can_send, bus=0, addr=0x7d0, sub_addr=None, com_cont_req=b'\x28\x83\x01', timeout=0.1, retry=10, reset=False):
|
||||
"""Silence an ECU by disabling sending and receiving messages using UDS 0x28.
|
||||
The ECU will stay silent as long as openpilot keeps sending Tester Present.
|
||||
|
||||
@@ -29,6 +31,15 @@ def disable_ecu(can_recv, can_send, bus=0, addr=0x7d0, sub_addr=None, com_cont_r
|
||||
WARNING: THIS DISABLES AEB!"""
|
||||
ecu_log(f"=== ECU DISABLE START === addr={hex(addr)}, bus={bus}")
|
||||
|
||||
if reset:
|
||||
try:
|
||||
ecu_log("sending ECU reset before communication control...")
|
||||
reset_query = IsoTpParallelQuery(can_send, can_recv, bus, [(addr, sub_addr)], [RESET_REQUEST], [RESET_RESPONSE])
|
||||
reset_query.get_data(timeout=timeout)
|
||||
time.sleep(0.2)
|
||||
except Exception as e:
|
||||
ecu_log(f"reset exception: {e}")
|
||||
|
||||
# Try multiple times with different approaches
|
||||
for i in range(retry):
|
||||
try:
|
||||
@@ -50,6 +61,7 @@ def disable_ecu(can_recv, can_send, bus=0, addr=0x7d0, sub_addr=None, com_cont_r
|
||||
# Log what we got back
|
||||
cc_success = False
|
||||
cc_rejected = False
|
||||
cc_nrc = None
|
||||
for (rx_addr, _), data in cc_response.items():
|
||||
ecu_log(f"CC response: {data.hex() if data else 'empty'}")
|
||||
# Check for positive response (0x68 = 0x28 + 0x40)
|
||||
@@ -59,6 +71,7 @@ def disable_ecu(can_recv, can_send, bus=0, addr=0x7d0, sub_addr=None, com_cont_r
|
||||
# Check for negative response
|
||||
elif len(data) >= 3 and data[0] == 0x7F:
|
||||
nrc = data[2]
|
||||
cc_nrc = nrc
|
||||
nrc_meanings = {
|
||||
0x12: "subFunctionNotSupported",
|
||||
0x13: "incorrectMessageLengthOrInvalidFormat",
|
||||
@@ -73,6 +86,10 @@ def disable_ecu(can_recv, can_send, bus=0, addr=0x7d0, sub_addr=None, com_cont_r
|
||||
if cc_success:
|
||||
return True
|
||||
elif cc_rejected:
|
||||
if reset and cc_nrc == 0x22 and i < retry - 1:
|
||||
ecu_log("CC rejected with NRC 0x22 after reset; retrying...")
|
||||
time.sleep(0.2)
|
||||
continue
|
||||
# ECU explicitly rejected - don't retry, it won't work
|
||||
ecu_log("=== ECU DISABLE REJECTED ===")
|
||||
return False
|
||||
|
||||
@@ -72,6 +72,7 @@ MIGRATION = {
|
||||
"HONDA CR-V 2016 TOURING": HONDA.HONDA_CRV,
|
||||
"HONDA CR-V 2017 EX": HONDA.HONDA_CRV_5G,
|
||||
"HONDA CR-V 2019 HYBRID": HONDA.HONDA_CRV_HYBRID,
|
||||
"HONDA CLARITY 2018": HONDA.HONDA_CLARITY,
|
||||
"HONDA FIT 2018 EX": HONDA.HONDA_FIT,
|
||||
"HONDA HRV 2019 TOURING": HONDA.HONDA_HRV,
|
||||
"HONDA INSIGHT 2019 TOURING": HONDA.HONDA_INSIGHT,
|
||||
@@ -205,6 +206,7 @@ MIGRATION = {
|
||||
"HYUNDAI STARIA 4TH GEN": HYUNDAI.HYUNDAI_STARIA_4TH_GEN,
|
||||
"HYUNDAI TUCSON 2019": HYUNDAI.HYUNDAI_TUCSON,
|
||||
"HYUNDAI PALISADE 2020": HYUNDAI.HYUNDAI_PALISADE,
|
||||
"HYUNDAI PALISADE 2023": HYUNDAI.HYUNDAI_PALISADE_2023,
|
||||
"HYUNDAI VELOSTER 2019": HYUNDAI.HYUNDAI_VELOSTER,
|
||||
"HYUNDAI SONATA HYBRID 2021": HYUNDAI.HYUNDAI_SONATA_HYBRID,
|
||||
"HYUNDAI IONIQ 5 2022": HYUNDAI.HYUNDAI_IONIQ_5,
|
||||
|
||||
@@ -7,7 +7,7 @@ from opendbc.car.gm import gmcan
|
||||
from opendbc.car.common.conversions import Conversions as CV
|
||||
from opendbc.car.gm.values import (
|
||||
ASCM_INT, CAR, CC_ONLY_CAR, CC_REGEN_PADDLE_CAR, DBC, EV_CAR, SDGM_CAR, AccState, CanBus, CarControllerParams,
|
||||
CruiseButtons, GMFlags,
|
||||
CruiseButtons, GMFlags, GMSafetyFlags,
|
||||
)
|
||||
from opendbc.car.interfaces import CarControllerBase
|
||||
from openpilot.common.params import Params, UnknownKeyName
|
||||
@@ -16,11 +16,26 @@ from openpilot.starpilot.common.testing_grounds import testing_ground
|
||||
VisualAlert = structs.CarControl.HUDControl.VisualAlert
|
||||
NetworkLocation = structs.CarParams.NetworkLocation
|
||||
LongCtrlState = structs.CarControl.Actuators.LongControlState
|
||||
GearShifter = structs.CarState.GearShifter
|
||||
|
||||
# Camera cancels up to 0.1s after brake is pressed, ECM allows 0.5s
|
||||
CAMERA_CANCEL_DELAY_FRAMES = 10
|
||||
# Enforce a minimum interval between steering messages to avoid a fault
|
||||
MIN_STEER_MSG_INTERVAL_MS = 15
|
||||
AUTO_HOLD_VOLT_CARS = {
|
||||
CAR.CHEVROLET_VOLT,
|
||||
CAR.CHEVROLET_VOLT_2019,
|
||||
CAR.CHEVROLET_VOLT_ASCM,
|
||||
CAR.CHEVROLET_VOLT_CAMERA,
|
||||
}
|
||||
AUTO_HOLD_DRIVE_GEARS = {
|
||||
GearShifter.drive,
|
||||
GearShifter.low,
|
||||
GearShifter.manumatic,
|
||||
}
|
||||
AUTO_HOLD_MIN_BRAKE = 80
|
||||
AUTO_HOLD_MAX_BRAKE = 240
|
||||
AUTO_HOLD_MIN_DRIVE_TIME_S = 3.0
|
||||
|
||||
|
||||
def get_stock_cc_active_for_cancel(CP, CS):
|
||||
@@ -62,6 +77,21 @@ def should_send_acc_dashboard_status(CP, dash_speed_spoof_active):
|
||||
return status_car and (dash_speed_spoof_active or volt_camera_no_camera)
|
||||
|
||||
|
||||
def get_acc_dashboard_fcw_alert(hud_alert, CS):
|
||||
if hud_alert == VisualAlert.fcw:
|
||||
return 0x3
|
||||
|
||||
stock_fcw_alert = int(getattr(CS, "stock_fcw_alert", 0)) & 0x3
|
||||
if stock_fcw_alert != 0:
|
||||
return stock_fcw_alert
|
||||
|
||||
cs_out = getattr(CS, "out", None)
|
||||
if cs_out is not None and (getattr(cs_out, "stockAeb", False) or getattr(cs_out, "stockFcw", False)):
|
||||
return 0x3
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
ECM_CRUISE_SPOOF_CARS = {
|
||||
CAR.CHEVROLET_BOLT_CC_2017,
|
||||
CAR.CHEVROLET_BOLT_CC_2018_2021,
|
||||
@@ -101,6 +131,53 @@ def get_testing_ground_1_brake_switch_bias(v_ego: float) -> int:
|
||||
return int(round(np.interp(v_ego, [0.0, 6.0, 15.0, 30.0], [40.0, 85.0, 130.0, 170.0])))
|
||||
|
||||
|
||||
def supports_volt_auto_hold(CP, auto_hold_enabled: bool):
|
||||
safety_cfg = getattr(CP, "safetyConfigs", ())
|
||||
safety_param = safety_cfg[0].safetyParam if safety_cfg else 0
|
||||
stock_hold_safety_ready = CP.openpilotLongitudinalControl or bool(safety_param & GMSafetyFlags.FLAG_GM_PANDA_PADDLE_SCHED.value)
|
||||
return (
|
||||
auto_hold_enabled and
|
||||
stock_hold_safety_ready and
|
||||
CP.carFingerprint in AUTO_HOLD_VOLT_CARS
|
||||
)
|
||||
|
||||
|
||||
def estimate_auto_hold_brake(driver_brake: float, op_brake: float) -> int:
|
||||
driver_hold = np.interp(float(driver_brake), [8.0, 20.0, 40.0, 80.0], [80.0, 110.0, 150.0, 220.0])
|
||||
hold_brake = max(float(op_brake), float(driver_hold))
|
||||
return int(round(np.clip(hold_brake, AUTO_HOLD_MIN_BRAKE, AUTO_HOLD_MAX_BRAKE)))
|
||||
|
||||
|
||||
def should_activate_auto_hold(hold_ready: bool, auto_hold_armed: bool, auto_hold_engaged: bool,
|
||||
brake_pressed: bool, standstill: bool, long_active: bool,
|
||||
regen_braking: bool, v_ego: float) -> bool:
|
||||
stopped = standstill or v_ego < 0.02
|
||||
return (
|
||||
hold_ready and
|
||||
(auto_hold_armed or auto_hold_engaged or brake_pressed) and
|
||||
stopped and
|
||||
not long_active and
|
||||
not regen_braking
|
||||
)
|
||||
|
||||
|
||||
def get_friction_brake_bus(CP):
|
||||
volt_gateway_alt_brake = (
|
||||
CP.carFingerprint == CAR.CHEVROLET_VOLT and
|
||||
CP.networkLocation == NetworkLocation.gateway and
|
||||
bool(CP.flags & GMFlags.NO_ACCELERATOR_POS_MSG.value)
|
||||
)
|
||||
if volt_gateway_alt_brake:
|
||||
return CanBus.POWERTRAIN
|
||||
|
||||
if CP.networkLocation == NetworkLocation.fwdCamera:
|
||||
if CP.carFingerprint in SDGM_CAR:
|
||||
return CanBus.CAMERA
|
||||
return CanBus.POWERTRAIN
|
||||
|
||||
return CanBus.CHASSIS
|
||||
|
||||
|
||||
class CarController(CarControllerBase):
|
||||
def __init__(self, dbc_names, CP):
|
||||
super().__init__(dbc_names, CP)
|
||||
@@ -153,6 +230,11 @@ class CarController(CarControllerBase):
|
||||
self.malibu_cancel_phase = 0
|
||||
self.malibu_button_phase = 0
|
||||
self.malibu_last_button_ts_nanos = 0
|
||||
self.auto_hold_brake = 0
|
||||
try:
|
||||
self.gm_auto_hold_enabled = self.params_.get_bool("GMAutoHold")
|
||||
except UnknownKeyName:
|
||||
self.gm_auto_hold_enabled = False
|
||||
|
||||
def calc_pedal_command(self, accel: float, long_active: bool, v_ego: float):
|
||||
if not long_active:
|
||||
@@ -275,6 +357,9 @@ class CarController(CarControllerBase):
|
||||
else:
|
||||
urgency = float(np.clip(abs(accel) / 2.0, 0.0, 1.0))
|
||||
rate_up = np.interp(v_ego, [0.0, 3.0, 8.0, 20.0], [0.007, 0.012, 0.022, 0.036]) + 0.011 * urgency
|
||||
if accel > 0.0 and v_ego > 6.0:
|
||||
comfort_factor = np.interp(abs(accel), [0.0, 0.12, 0.25, 0.45, 0.8], [0.55, 0.58, 0.68, 0.82, 1.0])
|
||||
rate_up *= comfort_factor
|
||||
if accel > 1.2:
|
||||
rate_up += np.interp(v_ego, [0.0, 4.0, 12.0, 25.0], [0.006, 0.005, 0.003, 0.002])
|
||||
rate_down = np.interp(v_ego, [0.0, 3.0, 8.0, 20.0], [0.008, 0.014, 0.026, 0.045]) + 0.015 * urgency
|
||||
@@ -302,8 +387,32 @@ class CarController(CarControllerBase):
|
||||
self.aego = CS.out.aEgo
|
||||
accel = actuators.accel
|
||||
press_regen_paddle = False
|
||||
auto_hold_enabled = supports_volt_auto_hold(self.CP, self.gm_auto_hold_enabled)
|
||||
stock_hold_apply_brake = self.apply_brake if self.CP.openpilotLongitudinalControl else 0
|
||||
|
||||
hold_ready = (
|
||||
auto_hold_enabled and
|
||||
CS.out.cruiseState.available and
|
||||
CS.out.gearShifter in AUTO_HOLD_DRIVE_GEARS and
|
||||
CS.auto_hold_drive_time >= AUTO_HOLD_MIN_DRIVE_TIME_S
|
||||
)
|
||||
if not hold_ready or CS.out.gasPressed:
|
||||
CS.auto_hold_armed = False
|
||||
elif CS.regen_release_timer > 0.0:
|
||||
CS.auto_hold_armed = False
|
||||
elif not CS.auto_hold_armed and (CS.out.vEgo > 0.03 or ((CS.out.standstill or CS.out.vEgo < 0.02) and CS.out.brakePressed)):
|
||||
CS.auto_hold_armed = True
|
||||
|
||||
if CS.out.vEgo > 0.1 or CS.out.gasPressed or CS.out.gearShifter not in AUTO_HOLD_DRIVE_GEARS:
|
||||
self.auto_hold_brake = 0
|
||||
elif CS.out.brakePressed or stock_hold_apply_brake > 0:
|
||||
self.auto_hold_brake = estimate_auto_hold_brake(CS.out.brake, stock_hold_apply_brake)
|
||||
|
||||
if self.frame % 25 == 0:
|
||||
try:
|
||||
self.gm_auto_hold_enabled = self.params_.get_bool("GMAutoHold")
|
||||
except UnknownKeyName:
|
||||
self.gm_auto_hold_enabled = False
|
||||
try:
|
||||
mode = self.params_.get("LongitudinalManeuverPaddleMode")
|
||||
except UnknownKeyName:
|
||||
@@ -383,6 +492,16 @@ class CarController(CarControllerBase):
|
||||
paddle_sched_feed_active = False
|
||||
|
||||
paddle_spoof_pressed = raw_regen_active and (CS.out.vEgo > 2.68)
|
||||
auto_hold_active = should_activate_auto_hold(
|
||||
hold_ready,
|
||||
CS.auto_hold_armed,
|
||||
CS.auto_hold_engaged,
|
||||
CS.out.brakePressed,
|
||||
CS.out.standstill,
|
||||
CC.longActive,
|
||||
CS.out.regenBraking,
|
||||
CS.out.vEgo,
|
||||
)
|
||||
|
||||
# Steering (Active: 50Hz, inactive: 10Hz)
|
||||
steer_step = self.params.STEER_STEP if CC.latActive else self.params.INACTIVE_STEER_STEP
|
||||
@@ -551,14 +670,11 @@ class CarController(CarControllerBase):
|
||||
if self.CP.enableGasInterceptorDEPRECATED:
|
||||
can_sends.append(create_gas_interceptor_command(self.packer_pt, interceptor_gas_cmd, idx))
|
||||
if self.CP.carFingerprint not in CC_ONLY_CAR:
|
||||
friction_brake_bus = CanBus.CHASSIS
|
||||
friction_brake_bus = get_friction_brake_bus(self.CP)
|
||||
# GM Camera exceptions
|
||||
# TODO: can we always check the longControlState?
|
||||
if self.CP.networkLocation == NetworkLocation.fwdCamera:
|
||||
at_full_stop = at_full_stop and stopping
|
||||
friction_brake_bus = CanBus.POWERTRAIN
|
||||
if self.CP.carFingerprint in SDGM_CAR:
|
||||
friction_brake_bus = CanBus.CAMERA
|
||||
|
||||
if self.CP.autoResumeSng:
|
||||
resume = actuators.longControlState != LongCtrlState.starting or CC.cruiseControl.resume
|
||||
@@ -569,17 +685,27 @@ class CarController(CarControllerBase):
|
||||
else:
|
||||
acc_engaged = CC.enabled
|
||||
|
||||
# GasRegenCmdActive needs to be 1 to avoid cruise faults. It describes the ACC state, not actuation
|
||||
can_sends.append(gmcan.create_gas_regen_command(
|
||||
self.packer_pt, CanBus.POWERTRAIN, self.apply_gas, idx, acc_engaged, at_full_stop,
|
||||
include_always_one3=self.CP.carFingerprint in kaofui_cars, use_volt_layout=self.is_volt))
|
||||
can_sends.append(gmcan.create_friction_brake_command(self.packer_ch, friction_brake_bus, self.apply_brake,
|
||||
idx, CC.enabled, near_stop, at_full_stop, self.CP))
|
||||
if auto_hold_active:
|
||||
hold_brake = self.auto_hold_brake or estimate_auto_hold_brake(CS.out.brake, self.apply_brake)
|
||||
hold_standstill = CS.pcm_acc_status == AccState.STANDSTILL
|
||||
hold_near_stop = CS.out.vEgo < self.params.NEAR_STOP_BRAKE_PHASE
|
||||
can_sends.append(gmcan.create_friction_brake_command(
|
||||
self.packer_ch, friction_brake_bus, hold_brake, idx, False, hold_near_stop, hold_standstill, self.CP))
|
||||
CS.auto_hold_engaged = True
|
||||
CS.auto_hold_fault_suppression_timer = 1.0
|
||||
else:
|
||||
# GasRegenCmdActive needs to be 1 to avoid cruise faults. It describes the ACC state, not actuation
|
||||
can_sends.append(gmcan.create_gas_regen_command(
|
||||
self.packer_pt, CanBus.POWERTRAIN, self.apply_gas, idx, acc_engaged, at_full_stop,
|
||||
include_always_one3=self.CP.carFingerprint in kaofui_cars, use_volt_layout=self.is_volt))
|
||||
can_sends.append(gmcan.create_friction_brake_command(self.packer_ch, friction_brake_bus, self.apply_brake,
|
||||
idx, CC.enabled, near_stop, at_full_stop, self.CP))
|
||||
CS.auto_hold_engaged = False
|
||||
|
||||
if should_send_acc_dashboard_status(self.CP, dash_speed_spoof_active):
|
||||
send_fcw = hud_alert == VisualAlert.fcw
|
||||
fcw_alert = get_acc_dashboard_fcw_alert(hud_alert, CS)
|
||||
can_sends.append(gmcan.create_acc_dashboard_command(self.packer_pt, CanBus.POWERTRAIN, CC.enabled,
|
||||
hud_v_cruise * CV.MS_TO_KPH, hud_control, send_fcw))
|
||||
hud_v_cruise * CV.MS_TO_KPH, hud_control, fcw_alert))
|
||||
|
||||
# Radar needs to know current speed and yaw rate (50hz),
|
||||
# and that ADAS is alive (10hz)
|
||||
@@ -633,6 +759,18 @@ class CarController(CarControllerBase):
|
||||
can_sends.append(gmcan.create_buttons(self.packer_pt, cancel_bus, (CS.buttons_counter + 1) % 4, CruiseButtons.CANCEL))
|
||||
|
||||
else:
|
||||
if self.frame % 4 == 0 and auto_hold_active:
|
||||
idx = (self.frame // 4) % 4
|
||||
hold_brake = self.auto_hold_brake or estimate_auto_hold_brake(CS.out.brake, stock_hold_apply_brake)
|
||||
hold_standstill = CS.pcm_acc_status == AccState.STANDSTILL
|
||||
hold_near_stop = CS.out.vEgo < self.params.NEAR_STOP_BRAKE_PHASE
|
||||
can_sends.append(gmcan.create_friction_brake_command(
|
||||
self.packer_ch, get_friction_brake_bus(self.CP), hold_brake, idx, False, hold_near_stop, hold_standstill, self.CP))
|
||||
CS.auto_hold_engaged = True
|
||||
CS.auto_hold_fault_suppression_timer = 1.0
|
||||
elif self.frame % 4 == 0:
|
||||
CS.auto_hold_engaged = False
|
||||
|
||||
# While car is braking, cancel button causes ECM to enter a soft disable state with a fault status.
|
||||
# A delayed cancellation allows camera to cancel and avoids a fault when user depresses brake quickly
|
||||
self.cancel_counter = self.cancel_counter + 1 if CC.cruiseControl.cancel else 0
|
||||
|
||||
@@ -2,6 +2,7 @@ import copy
|
||||
from cereal import custom
|
||||
from opendbc.can import CANDefine, CANParser
|
||||
from opendbc.car import Bus, create_button_events, structs
|
||||
from opendbc.car import DT_CTRL
|
||||
from opendbc.car.common.conversions import Conversions as CV
|
||||
from opendbc.car.interfaces import CarStateBase
|
||||
from opendbc.car.gm.values import (
|
||||
@@ -26,6 +27,9 @@ TransmissionType = structs.CarParams.TransmissionType
|
||||
NetworkLocation = structs.CarParams.NetworkLocation
|
||||
|
||||
STANDSTILL_THRESHOLD = 10 * 0.0311
|
||||
VOLT_EBCM_BRAKE_PRESSED_THRESHOLD = 6 / 0xd0
|
||||
AUTO_HOLD_MIN_DRIVE_TIME_S = 3.0
|
||||
AUTO_HOLD_REGEN_RELEASE_COOLDOWN_S = 1.0
|
||||
|
||||
BUTTONS_DICT = {CruiseButtons.RES_ACCEL: ButtonType.accelCruise, CruiseButtons.DECEL_SET: ButtonType.decelCruise,
|
||||
CruiseButtons.MAIN: ButtonType.mainCruise, CruiseButtons.CANCEL: ButtonType.cancel}
|
||||
@@ -35,6 +39,10 @@ BOLT_GEN1_CANCEL_PERSONALITY_CARS = {
|
||||
CAR.CHEVROLET_BOLT_CC_2017,
|
||||
CAR.CHEVROLET_BOLT_CC_2018_2021,
|
||||
}
|
||||
BOLT_CANCEL_BUTTON_CARS = BOLT_GEN1_CANCEL_PERSONALITY_CARS | {
|
||||
CAR.CHEVROLET_BOLT_ACC_2022_2023_PEDAL,
|
||||
CAR.CHEVROLET_BOLT_CC_2022_2023,
|
||||
}
|
||||
|
||||
|
||||
class CarState(CarStateBase):
|
||||
@@ -58,6 +66,12 @@ class CarState(CarStateBase):
|
||||
self.distance_button = 0
|
||||
|
||||
self.single_pedal_mode = False
|
||||
self.auto_hold_armed = False
|
||||
self.auto_hold_engaged = False
|
||||
self.auto_hold_drive_time = 0.0
|
||||
self.auto_hold_fault_suppression_timer = 0.0
|
||||
self.regen_release_timer = 0.0
|
||||
self.user_regen_paddle_pressed = False
|
||||
|
||||
self.pedal_steady = 0
|
||||
self.ecm_cruise_control_ts_nanos = 0
|
||||
@@ -66,6 +80,7 @@ class CarState(CarStateBase):
|
||||
self.lkas_previously_enabled = 0
|
||||
self.lkas_enabled = 0
|
||||
self.pcm_acc_status = AccState.OFF
|
||||
self.stock_fcw_alert = 0
|
||||
|
||||
def update_button_enable(self, buttonEvents: list[structs.CarState.ButtonEvent]):
|
||||
if not self.CP.pcmCruise:
|
||||
@@ -153,7 +168,9 @@ class CarState(CarStateBase):
|
||||
else:
|
||||
ret.brake = pt_cp.vl["ECMAcceleratorPos"]["BrakePedalPos"]
|
||||
|
||||
if self.CP.carFingerprint in {CAR.CHEVROLET_MALIBU_CC} or (self.CP.carFingerprint == CAR.CHEVROLET_BLAZER and not no_accel_pos):
|
||||
if self.CP.carFingerprint == CAR.CHEVROLET_VOLT and no_accel_pos:
|
||||
ret.brakePressed = ret.brake >= VOLT_EBCM_BRAKE_PRESSED_THRESHOLD
|
||||
elif self.CP.carFingerprint in {CAR.CHEVROLET_MALIBU_CC} or (self.CP.carFingerprint == CAR.CHEVROLET_BLAZER and not no_accel_pos):
|
||||
ret.brakePressed = ret.brake >= 8
|
||||
elif (self.CP.flags & GMFlags.FORCE_BRAKE_C9.value) or ((self.CP.networkLocation == NetworkLocation.fwdCamera) and (self.CP.carFingerprint != CAR.CHEVROLET_BLAZER)):
|
||||
ret.brakePressed = pt_cp.vl["ECMEngineStatus"]["BrakePressed"] != 0
|
||||
@@ -165,9 +182,22 @@ class CarState(CarStateBase):
|
||||
analog_thresh = 0.10 if no_accel_pos else 8
|
||||
ret.brakePressed = ret.brake >= analog_thresh
|
||||
|
||||
in_drive_for_hold = ret.gearShifter in (GearShifter.drive, GearShifter.low, GearShifter.manumatic)
|
||||
if in_drive_for_hold:
|
||||
if ret.brakePressed:
|
||||
self.auto_hold_drive_time = AUTO_HOLD_MIN_DRIVE_TIME_S
|
||||
else:
|
||||
self.auto_hold_drive_time = min(self.auto_hold_drive_time + DT_CTRL, AUTO_HOLD_MIN_DRIVE_TIME_S)
|
||||
else:
|
||||
self.auto_hold_drive_time = 0.0
|
||||
self.auto_hold_armed = False
|
||||
self.auto_hold_engaged = False
|
||||
|
||||
# Regen braking is braking
|
||||
if self.CP.transmissionType == TransmissionType.direct:
|
||||
ret.regenBraking = pt_cp.vl["EBCMRegenPaddle"]["RegenPaddle"] != 0
|
||||
if not ret.regenBraking and self.user_regen_paddle_pressed:
|
||||
self.regen_release_timer = AUTO_HOLD_REGEN_RELEASE_COOLDOWN_S
|
||||
self.single_pedal_mode = (ret.gearShifter == GearShifter.low or
|
||||
pt_cp.vl["EVDriveMode"]["SinglePedalModeActive"] == 1 or
|
||||
(ret.regenBraking and ret.gearShifter == GearShifter.manumatic) or
|
||||
@@ -176,6 +206,10 @@ class CarState(CarStateBase):
|
||||
CAR.CHEVROLET_BOLT_ACC_2022_2023_PEDAL,
|
||||
CAR.CHEVROLET_BOLT_CC_2022_2023,
|
||||
} and self.CP.enableGasInterceptorDEPRECATED))
|
||||
self.user_regen_paddle_pressed = ret.regenBraking
|
||||
|
||||
if self.regen_release_timer > 0.0:
|
||||
self.regen_release_timer = max(self.regen_release_timer - DT_CTRL, 0.0)
|
||||
|
||||
if self.CP.enableGasInterceptorDEPRECATED:
|
||||
gas = (pt_cp.vl["GAS_SENSOR"]["INTERCEPTOR_GAS"] + pt_cp.vl["GAS_SENSOR"]["INTERCEPTOR_GAS2"]) / 2.
|
||||
@@ -232,9 +266,16 @@ class CarState(CarStateBase):
|
||||
|
||||
ret.cruiseState.enabled = pt_cp.vl["AcceleratorPedal2"]["CruiseState"] != AccState.OFF
|
||||
ret.cruiseState.standstill = pt_cp.vl["AcceleratorPedal2"]["CruiseState"] == AccState.STANDSTILL
|
||||
self.stock_fcw_alert = 0
|
||||
if self.CP.networkLocation == NetworkLocation.fwdCamera and not self.CP.flags & GMFlags.NO_CAMERA.value:
|
||||
if self.CP.carFingerprint not in CC_ONLY_CAR:
|
||||
ret.cruiseState.speed = cam_cp.vl["ASCMActiveCruiseControlStatus"]["ACCSpeedSetpoint"] * CV.KPH_TO_MS
|
||||
has_acc_dashboard_status = self.CP.carFingerprint not in CC_ONLY_CAR or self.CP.carFingerprint == CAR.CHEVROLET_BOLT_ACC_2022_2023_PEDAL
|
||||
if has_acc_dashboard_status:
|
||||
acc_dashboard_status = cam_cp.vl["ASCMActiveCruiseControlStatus"]
|
||||
if self.CP.carFingerprint not in CC_ONLY_CAR:
|
||||
ret.cruiseState.speed = acc_dashboard_status["ACCSpeedSetpoint"] * CV.KPH_TO_MS
|
||||
# Preserve the stock camera FCW level from 0x370 so the controller can
|
||||
# replay it when that message is blocked and spoofed by openpilot long.
|
||||
self.stock_fcw_alert = int(acc_dashboard_status["FCWAlert"])
|
||||
|
||||
if self.CP.carFingerprint not in (SDGM_CAR | ASCM_INT):
|
||||
ret.stockAeb = cam_cp.vl["AEBCmd"]["AEBCmdActive"] != 0
|
||||
@@ -264,6 +305,10 @@ class CarState(CarStateBase):
|
||||
self.ecm_cruise_control_ts_nanos = 0
|
||||
self.accelerator_pedal2_ts_nanos = 0
|
||||
|
||||
if self.auto_hold_fault_suppression_timer > 0.0:
|
||||
self.auto_hold_fault_suppression_timer = max(self.auto_hold_fault_suppression_timer - DT_CTRL, 0.0)
|
||||
ret.accFaulted = False
|
||||
|
||||
if self.CP.enableBsm and not sdgm_non_volt:
|
||||
ret.leftBlindspot = pt_cp.vl["BCMBlindSpotMonitor"]["LeftBSM"] == 1
|
||||
ret.rightBlindspot = pt_cp.vl["BCMBlindSpotMonitor"]["RightBSM"] == 1
|
||||
@@ -278,46 +323,34 @@ class CarState(CarStateBase):
|
||||
self.lkas_enabled = pt_cp.vl["ASCMSteeringButton"]["LKAButton"]
|
||||
self.pcm_acc_status = pt_cp.vl["AcceleratorPedal2"]["CruiseState"]
|
||||
|
||||
# Only activate cancel remap when panda safety was configured for it at startup.
|
||||
remap_cancel_to_distance = bool(self.CP.alternativeExperience & ALTERNATIVE_EXPERIENCE.GM_REMAP_CANCEL_TO_DISTANCE)
|
||||
if not remap_cancel_to_distance:
|
||||
remap_cancel_to_distance = (
|
||||
getattr(starpilot_toggles, "remap_cancel_to_distance", False) and
|
||||
self.CP.openpilotLongitudinalControl and
|
||||
bool(self.CP.flags & GMFlags.PEDAL_LONG.value) and
|
||||
self.CP.carFingerprint in (BOLT_GEN1_CANCEL_PERSONALITY_CARS | {CAR.CHEVROLET_MALIBU_HYBRID_CC})
|
||||
)
|
||||
malibu_cancel_passthrough = (
|
||||
remap_cancel_to_distance and
|
||||
self.CP.carFingerprint == CAR.CHEVROLET_MALIBU_HYBRID_CC and
|
||||
self.CP.openpilotLongitudinalControl and
|
||||
bool(self.CP.flags & GMFlags.PEDAL_LONG.value)
|
||||
)
|
||||
bolt_cancel_personality = (
|
||||
bolt_cancel_button = (
|
||||
remap_cancel_to_distance and
|
||||
self.CP.carFingerprint in BOLT_GEN1_CANCEL_PERSONALITY_CARS and
|
||||
self.CP.carFingerprint in BOLT_CANCEL_BUTTON_CARS and
|
||||
self.CP.openpilotLongitudinalControl and
|
||||
bool(self.CP.flags & GMFlags.PEDAL_LONG.value)
|
||||
)
|
||||
bolt_cancel_lkas_conflict = bolt_cancel_button and self.CP.carFingerprint in BOLT_GEN1_CANCEL_PERSONALITY_CARS
|
||||
|
||||
cruise_button_map = BUTTONS_DICT
|
||||
if malibu_cancel_passthrough or bolt_cancel_personality:
|
||||
if malibu_cancel_passthrough or bolt_cancel_button:
|
||||
cruise_button_map = {k: v for k, v in BUTTONS_DICT.items() if k != CruiseButtons.CANCEL}
|
||||
|
||||
cruise_events = create_button_events(
|
||||
self.cruise_buttons, prev_cruise_buttons, cruise_button_map, unpressed_btn=CruiseButtons.UNPRESS
|
||||
)
|
||||
cancel_gap_events = []
|
||||
if bolt_cancel_personality and self.cruise_buttons != prev_cruise_buttons:
|
||||
if prev_cruise_buttons == CruiseButtons.CANCEL:
|
||||
cancel_gap_events.append(structs.CarState.ButtonEvent(pressed=False, type=ButtonType.gapAdjustCruise))
|
||||
if self.cruise_buttons == CruiseButtons.CANCEL:
|
||||
cancel_gap_events.append(structs.CarState.ButtonEvent(pressed=True, type=ButtonType.gapAdjustCruise))
|
||||
|
||||
suppress_malibu_side_buttons = malibu_cancel_passthrough and (
|
||||
self.cruise_buttons in (CruiseButtons.CANCEL, CruiseButtons.MAIN) or
|
||||
prev_cruise_buttons in (CruiseButtons.CANCEL, CruiseButtons.MAIN)
|
||||
)
|
||||
suppress_bolt_cancel_lkas = bolt_cancel_personality and (
|
||||
suppress_bolt_cancel_lkas = bolt_cancel_lkas_conflict and (
|
||||
self.cruise_buttons == CruiseButtons.CANCEL or
|
||||
prev_cruise_buttons == CruiseButtons.CANCEL
|
||||
)
|
||||
@@ -332,7 +365,6 @@ class CarState(CarStateBase):
|
||||
if self.cruise_buttons != CruiseButtons.UNPRESS or prev_cruise_buttons != CruiseButtons.INIT:
|
||||
ret.buttonEvents = [
|
||||
*cruise_events,
|
||||
*cancel_gap_events,
|
||||
*distance_events,
|
||||
*lkas_events,
|
||||
]
|
||||
@@ -341,9 +373,8 @@ class CarState(CarStateBase):
|
||||
ret.lowSpeedAlert = True
|
||||
|
||||
fp_ret = custom.StarPilotCarState.new_message()
|
||||
if bolt_cancel_personality and self.cruise_buttons == CruiseButtons.CANCEL:
|
||||
# Feed long-press personality logic as if distance is held while CANCEL is held.
|
||||
fp_ret.distancePressed = True
|
||||
if bolt_cancel_button and self.cruise_buttons == CruiseButtons.CANCEL:
|
||||
fp_ret.cancelPressed = True
|
||||
fp_ret.sportGear = pt_cp.vl["SportMode"]["SportMode"] == 1
|
||||
|
||||
return ret, fp_ret
|
||||
|
||||
@@ -209,7 +209,7 @@ def create_friction_brake_command(packer, bus, apply_brake, idx, enabled, near_s
|
||||
return packer.make_can_msg("EBCMFrictionBrakeCmd", bus, values)
|
||||
|
||||
|
||||
def create_acc_dashboard_command(packer, bus, enabled, target_speed_kph, hud_control, fcw):
|
||||
def create_acc_dashboard_command(packer, bus, enabled, target_speed_kph, hud_control, fcw_alert):
|
||||
target_speed = min(target_speed_kph, 255)
|
||||
|
||||
values = {
|
||||
@@ -220,7 +220,7 @@ def create_acc_dashboard_command(packer, bus, enabled, target_speed_kph, hud_con
|
||||
"ACCCmdActive": enabled,
|
||||
"ACCAlwaysOne2": 1,
|
||||
"ACCLeadCar": hud_control.leadVisible,
|
||||
"FCWAlert": 0x3 if fcw else 0
|
||||
"FCWAlert": int(fcw_alert) & 0x3,
|
||||
}
|
||||
|
||||
return packer.make_can_msg("ASCMActiveCruiseControlStatus", bus, values)
|
||||
|
||||
@@ -93,12 +93,16 @@ BOLT_PEDAL_LONG_CARS = {
|
||||
CAR.CHEVROLET_MALIBU_HYBRID_CC,
|
||||
}
|
||||
|
||||
# Cancel-to-personality mapping target: gen1 Bolt pedal-long paths only.
|
||||
# Cancel-button remap support uses the same safety path on all pedal-long Bolts,
|
||||
# but only gen1 needs extra LKAS suppression in CarState.
|
||||
BOLT_GEN1_CANCEL_PERSONALITY_CARS = {
|
||||
CAR.CHEVROLET_BOLT_CC_2017,
|
||||
CAR.CHEVROLET_BOLT_CC_2018_2021,
|
||||
}
|
||||
CANCEL_REMAP_DISTANCE_CARS = BOLT_GEN1_CANCEL_PERSONALITY_CARS
|
||||
CANCEL_REMAP_DISTANCE_CARS = BOLT_GEN1_CANCEL_PERSONALITY_CARS | {
|
||||
CAR.CHEVROLET_BOLT_ACC_2022_2023_PEDAL,
|
||||
CAR.CHEVROLET_BOLT_CC_2022_2023,
|
||||
}
|
||||
|
||||
|
||||
class CarInterface(CarInterfaceBase):
|
||||
@@ -189,6 +193,10 @@ class CarInterface(CarInterfaceBase):
|
||||
disable_openpilot_long = params.get_bool("DisableOpenpilotLongitudinal")
|
||||
except UnknownKeyName:
|
||||
disable_openpilot_long = False
|
||||
try:
|
||||
gm_auto_hold = params.get_bool("GMAutoHold")
|
||||
except UnknownKeyName:
|
||||
gm_auto_hold = False
|
||||
|
||||
ret.brand = "gm"
|
||||
ret.safetyConfigs = [get_safety_config(structs.CarParams.SafetyModel.gm)]
|
||||
@@ -620,6 +628,9 @@ class CarInterface(CarInterfaceBase):
|
||||
|
||||
if ACCELERATOR_POS_MSG not in fingerprint[CanBus.POWERTRAIN]:
|
||||
ret.flags |= GMFlags.NO_ACCELERATOR_POS_MSG.value
|
||||
if candidate == CAR.CHEVROLET_VOLT and ret.networkLocation == NetworkLocation.gateway:
|
||||
# Reuse the no-camera safety bit as an ASCM Volt selector for the alternate EBCM brake path.
|
||||
ret.safetyConfigs[0].safetyParam |= GMSafetyFlags.FLAG_GM_NO_CAMERA.value
|
||||
|
||||
try:
|
||||
remote_start_boots_comma = params.get_bool("RemoteStartBootsComma")
|
||||
@@ -629,6 +640,21 @@ class CarInterface(CarInterfaceBase):
|
||||
if remote_start_boots_comma:
|
||||
ret.safetyConfigs[0].safetyParam |= GMSafetyFlags.FLAG_GM_REMOTE_START_BOOTS_COMMA.value
|
||||
|
||||
volt_stock_auto_hold_safety = (
|
||||
gm_auto_hold and
|
||||
not ret.openpilotLongitudinalControl and
|
||||
candidate in {
|
||||
CAR.CHEVROLET_VOLT,
|
||||
CAR.CHEVROLET_VOLT_2019,
|
||||
CAR.CHEVROLET_VOLT_ASCM,
|
||||
CAR.CHEVROLET_VOLT_CAMERA,
|
||||
}
|
||||
)
|
||||
if volt_stock_auto_hold_safety:
|
||||
# Reuse the paddle-scheduler safety bit as a stock-Volt auto-hold marker on
|
||||
# non-pedal paths. The scheduler logic remains inactive without pedal-long.
|
||||
ret.safetyConfigs[0].safetyParam |= GMSafetyFlags.FLAG_GM_PANDA_PADDLE_SCHED.value
|
||||
|
||||
use_panda_3d1_sched = (
|
||||
ret.openpilotLongitudinalControl and
|
||||
ret.enableGasInterceptorDEPRECATED and
|
||||
|
||||
@@ -2,15 +2,23 @@ import pytest
|
||||
from types import SimpleNamespace
|
||||
from parameterized import parameterized
|
||||
|
||||
from opendbc.can import CANPacker
|
||||
from opendbc.can import CANPacker, CANParser
|
||||
from opendbc.car import Bus, DT_CTRL
|
||||
from opendbc.car.car_helpers import interfaces
|
||||
from opendbc.car.gm import gmcan
|
||||
from opendbc.car.gm.carcontroller import should_send_acc_dashboard_status, should_send_cc_button_spam, should_spoof_dash_speed
|
||||
from opendbc.car.gm.carcontroller import (
|
||||
VisualAlert,
|
||||
get_acc_dashboard_fcw_alert,
|
||||
should_send_acc_dashboard_status,
|
||||
should_send_cc_button_spam,
|
||||
should_spoof_dash_speed,
|
||||
)
|
||||
import opendbc.car.gm.interface as gm_interface
|
||||
from opendbc.car.common.conversions import Conversions as CV
|
||||
from opendbc.car.gm.fingerprints import FINGERPRINTS
|
||||
from opendbc.car.gm.values import CAMERA_ACC_CAR, CAR, CC_ONLY_CAR, DBC, GM_RX_OFFSET, GMFlags, GMSafetyFlags
|
||||
from opendbc.safety import ALTERNATIVE_EXPERIENCE
|
||||
from openpilot.common.params import Params
|
||||
|
||||
CAMERA_DIAGNOSTIC_ADDRESS = 0x24b
|
||||
VOLT_CARS = (
|
||||
@@ -32,6 +40,7 @@ def _test_starpilot_toggles():
|
||||
cluster_offset=1.0,
|
||||
disable_openpilot_long=False,
|
||||
force_fingerprint=False,
|
||||
remap_cancel_to_distance=False,
|
||||
vEgoStopping=0.5,
|
||||
volt_sng=False,
|
||||
)
|
||||
@@ -64,22 +73,34 @@ class TestGMInterface:
|
||||
("interceptor", True),
|
||||
("ascm_int", False),
|
||||
])
|
||||
def test_volt_testing_ground_tune_sets_nonzero_p_and_starting_state(self, _name, pedal_present, monkeypatch):
|
||||
def test_volt_testing_ground_tune_sets_nonzero_p_and_starting_state(self, _name, pedal_present):
|
||||
CarInterface = interfaces[CAR.CHEVROLET_VOLT_ASCM]
|
||||
fingerprint = _empty_fingerprint()
|
||||
if pedal_present:
|
||||
fingerprint[0][0x201] = 8 # pedal detected
|
||||
fingerprint[0][0x2FF] = 8 # SASCM detected
|
||||
|
||||
monkeypatch.setattr(gm_interface.testing_ground, "use_2", True, raising=False)
|
||||
old_testing_ground = gm_interface.testing_ground
|
||||
gm_interface.testing_ground = SimpleNamespace(use_2=True)
|
||||
|
||||
car_params = CarInterface.get_params(CAR.CHEVROLET_VOLT_ASCM, fingerprint, [], alpha_long=False, is_release=False, docs=False,
|
||||
starpilot_toggles=_test_starpilot_toggles())
|
||||
try:
|
||||
car_params = CarInterface.get_params(CAR.CHEVROLET_VOLT_ASCM, fingerprint, [], alpha_long=False, is_release=False, docs=False,
|
||||
starpilot_toggles=_test_starpilot_toggles())
|
||||
finally:
|
||||
gm_interface.testing_ground = old_testing_ground
|
||||
|
||||
assert list(car_params.longitudinalTuning.kpV) == [0.10, 0.072, 0.05, 0.04]
|
||||
assert list(car_params.longitudinalTuning.kiV) == [0.025, 0.03, 0.04, 0.055]
|
||||
assert car_params.startingState
|
||||
assert car_params.startAccel == pytest.approx(1.15)
|
||||
if pedal_present:
|
||||
assert list(car_params.longitudinalTuning.kpV) == pytest.approx([0.10, 0.072, 0.05, 0.04])
|
||||
assert list(car_params.longitudinalTuning.kiV) == pytest.approx([0.025, 0.03, 0.04, 0.055])
|
||||
assert car_params.startingState
|
||||
assert car_params.startAccel == pytest.approx(1.15)
|
||||
else:
|
||||
assert not car_params.openpilotLongitudinalControl
|
||||
assert not car_params.enableGasInterceptorDEPRECATED
|
||||
assert list(car_params.longitudinalTuning.kpV) == [0.0]
|
||||
assert list(car_params.longitudinalTuning.kiV) == [0.5, 0.5]
|
||||
assert not car_params.startingState
|
||||
assert car_params.startAccel == pytest.approx(0.0)
|
||||
|
||||
def test_volt_cc_sparse_fingerprint_without_camera_sets_no_camera(self):
|
||||
CarInterface = interfaces[CAR.CHEVROLET_VOLT_CC]
|
||||
@@ -103,11 +124,29 @@ class TestGMInterface:
|
||||
starpilot_toggles=_test_starpilot_toggles())
|
||||
|
||||
assert car_params.flags & GMFlags.NO_ACCELERATOR_POS_MSG.value
|
||||
assert car_params.safetyConfigs[0].safetyParam & GMSafetyFlags.FLAG_GM_NO_CAMERA.value
|
||||
|
||||
pt_parser = CarInterface.CarState.get_can_parsers(car_params)[Bus.pt]
|
||||
assert "ECMAcceleratorPos" not in pt_parser.vl
|
||||
assert "EBCMBrakePedalPosition" in pt_parser.vl
|
||||
|
||||
def test_bolt_gen2_pedal_cancel_remap_sets_alt_exp(self):
|
||||
CarInterface = interfaces[CAR.CHEVROLET_BOLT_ACC_2022_2023_PEDAL]
|
||||
fingerprint = _empty_fingerprint()
|
||||
fingerprint[0][0x201] = 8
|
||||
|
||||
params = Params()
|
||||
toggles = _test_starpilot_toggles()
|
||||
try:
|
||||
params.put_bool("RemapCancelToDistance", True)
|
||||
car_params = CarInterface.get_params(CAR.CHEVROLET_BOLT_ACC_2022_2023_PEDAL, fingerprint, [], alpha_long=False,
|
||||
is_release=False, docs=False, starpilot_toggles=toggles)
|
||||
finally:
|
||||
params.remove("RemapCancelToDistance")
|
||||
|
||||
assert car_params.alternativeExperience & ALTERNATIVE_EXPERIENCE.GM_REMAP_CANCEL_TO_DISTANCE
|
||||
assert car_params.safetyConfigs[0].safetyParam & GMSafetyFlags.FLAG_GM_BOLT_2022_PEDAL.value
|
||||
|
||||
|
||||
class TestGMCarController:
|
||||
def test_dash_speed_spoof_respects_live_stock_acc_toggles(self):
|
||||
@@ -213,3 +252,43 @@ class TestGMCarController:
|
||||
msgs = gmcan.create_gm_cc_spam_command(packer, controller, cs, actuators, SimpleNamespace(is_metric=False))
|
||||
|
||||
assert [msg[2] for msg in msgs] == [0]
|
||||
|
||||
def test_acc_dashboard_command_preserves_raw_fcw_alert_level(self):
|
||||
packer = CANPacker(DBC[CAR.CHEVROLET_BOLT_ACC_2022_2023][Bus.pt])
|
||||
parser = CANParser(DBC[CAR.CHEVROLET_BOLT_ACC_2022_2023][Bus.pt], [("ASCMActiveCruiseControlStatus", 0)], 0)
|
||||
msg = gmcan.create_acc_dashboard_command(
|
||||
packer,
|
||||
0,
|
||||
True,
|
||||
100,
|
||||
SimpleNamespace(leadDistanceBars=3, leadVisible=True),
|
||||
0x2,
|
||||
)
|
||||
|
||||
parser.update([0, [msg]])
|
||||
|
||||
assert parser.vl["ASCMActiveCruiseControlStatus"]["FCWAlert"] == 2
|
||||
|
||||
def test_acc_dashboard_fcw_alert_prefers_openpilot_alert(self):
|
||||
cs = SimpleNamespace(
|
||||
stock_fcw_alert=1,
|
||||
out=SimpleNamespace(stockAeb=False, stockFcw=False),
|
||||
)
|
||||
|
||||
assert get_acc_dashboard_fcw_alert(VisualAlert.fcw, cs) == 0x3
|
||||
|
||||
def test_acc_dashboard_fcw_alert_replays_stock_camera_alert_level(self):
|
||||
cs = SimpleNamespace(
|
||||
stock_fcw_alert=2,
|
||||
out=SimpleNamespace(stockAeb=False, stockFcw=False),
|
||||
)
|
||||
|
||||
assert get_acc_dashboard_fcw_alert(VisualAlert.none, cs) == 2
|
||||
|
||||
def test_acc_dashboard_fcw_alert_falls_back_to_stock_aeb_event(self):
|
||||
cs = SimpleNamespace(
|
||||
stock_fcw_alert=0,
|
||||
out=SimpleNamespace(stockAeb=True, stockFcw=False),
|
||||
)
|
||||
|
||||
assert get_acc_dashboard_fcw_alert(VisualAlert.none, cs) == 0x3
|
||||
|
||||
@@ -1,16 +1,130 @@
|
||||
import math
|
||||
import numpy as np
|
||||
|
||||
from opendbc.can import CANPacker
|
||||
from opendbc.car import Bus, DT_CTRL, rate_limit, make_tester_present_msg, structs
|
||||
from opendbc.car import ACCELERATION_DUE_TO_GRAVITY, Bus, DT_CTRL, create_gas_interceptor_command, rate_limit, make_tester_present_msg, structs
|
||||
from opendbc.car.honda import hondacan
|
||||
from opendbc.car.honda.values import CAR, CruiseButtons, HONDA_BOSCH, HONDA_BOSCH_CANFD, HONDA_BOSCH_RADARLESS, \
|
||||
HONDA_BOSCH_TJA_CONTROL, HONDA_NIDEC_ALT_PCM_ACCEL, CarControllerParams
|
||||
from opendbc.car.honda.values import (
|
||||
CAR,
|
||||
CruiseButtons,
|
||||
HONDA_BOSCH,
|
||||
HONDA_BOSCH_CANFD,
|
||||
HONDA_BOSCH_RADARLESS,
|
||||
HONDA_BOSCH_TJA_CONTROL,
|
||||
HONDA_NIDEC_ALT_PCM_ACCEL,
|
||||
CarControllerParams,
|
||||
HondaFlags,
|
||||
)
|
||||
from opendbc.car.interfaces import CarControllerBase
|
||||
from openpilot.common.params import Params
|
||||
|
||||
VisualAlert = structs.CarControl.HUDControl.VisualAlert
|
||||
LongCtrlState = structs.CarControl.Actuators.LongControlState
|
||||
|
||||
|
||||
def get_civic_bosch_modified_torque_lpf_tau(torque_cmd: float, prev_torque_cmd: float, v_ego: float) -> float:
|
||||
torque_delta = abs(float(torque_cmd) - float(prev_torque_cmd))
|
||||
torque_cmd_abs = abs(float(torque_cmd))
|
||||
sign_change = (float(torque_cmd) * float(prev_torque_cmd)) < 0.0
|
||||
highway = v_ego > (50.0 * 0.44704)
|
||||
low_speed = v_ego < (30.0 * 0.44704)
|
||||
|
||||
if highway:
|
||||
if torque_cmd_abs < 0.12:
|
||||
return 0.18 if sign_change else 0.16
|
||||
if sign_change and torque_delta > 0.15:
|
||||
return 0.10
|
||||
return 0.12
|
||||
|
||||
if sign_change and torque_cmd_abs < 0.25:
|
||||
return 0.28 if low_speed else 0.22
|
||||
|
||||
# Extra damping for the tiny near-center commands where both modified EPS
|
||||
# firmwares still show hunting and escalating sway.
|
||||
if torque_cmd_abs < 0.12:
|
||||
return 0.28 if low_speed else 0.20
|
||||
|
||||
if low_speed:
|
||||
if torque_delta > 0.50:
|
||||
return 0.14
|
||||
elif torque_delta > 0.20:
|
||||
return 0.16
|
||||
elif torque_delta > 0.05:
|
||||
return 0.18
|
||||
else:
|
||||
return 0.22
|
||||
|
||||
if torque_delta > 0.50:
|
||||
return 0.12
|
||||
elif torque_delta > 0.20:
|
||||
return 0.13
|
||||
elif torque_delta > 0.05:
|
||||
return 0.15
|
||||
else:
|
||||
return 0.18
|
||||
|
||||
|
||||
def get_civic_bosch_modified_steering_pressed(
|
||||
raw_pressed: bool, steering_torque: float, torque_cmd: float, filter_s: float, was_pressed: bool
|
||||
) -> tuple[float, bool]:
|
||||
torque_product = steering_torque * torque_cmd
|
||||
torque_cmd_abs = abs(torque_cmd)
|
||||
|
||||
if raw_pressed:
|
||||
if torque_product < 0.0:
|
||||
trigger_s = 0.08 if was_pressed else 0.10
|
||||
rise_rate = 1.0
|
||||
elif torque_cmd_abs < 0.10:
|
||||
trigger_s = 0.20 if was_pressed else 0.24
|
||||
rise_rate = 0.75
|
||||
else:
|
||||
trigger_s = 0.70 if was_pressed else 0.80
|
||||
rise_rate = 0.50
|
||||
|
||||
filter_s = min(1.0, filter_s + (rise_rate * DT_CTRL))
|
||||
steering_pressed = filter_s >= trigger_s
|
||||
else:
|
||||
filter_s = max(0.0, filter_s - 8.0 * DT_CTRL)
|
||||
steering_pressed = filter_s > 0.04 and was_pressed
|
||||
|
||||
return filter_s, steering_pressed
|
||||
|
||||
|
||||
def get_honda_bosch_wind_brake_mps2(v_ego: float) -> float:
|
||||
return float(np.interp(v_ego, [0.0, 13.4, 22.4, 31.3, 40.2], [0.000, 0.049, 0.136, 0.267, 0.441]))
|
||||
|
||||
|
||||
def update_honda_bosch_live_learning(
|
||||
gas_factor: float,
|
||||
wind_factor: float,
|
||||
wind_factor_before_brake: float,
|
||||
desired_accel: float,
|
||||
actual_accel: float,
|
||||
gas_pedal_force: float,
|
||||
wind_brake_mps2: float,
|
||||
brake_pressed: bool,
|
||||
v_ego: float,
|
||||
) -> tuple[float, float, float]:
|
||||
accel_error = desired_accel - actual_accel
|
||||
|
||||
if accel_error != 0.0 and gas_pedal_force > 0.0:
|
||||
gas_factor = float(np.clip(gas_factor + accel_error / 50.0 * gas_pedal_force, 0.1, 3.0))
|
||||
|
||||
if accel_error != 0.0 and not brake_pressed and v_ego > 0.0:
|
||||
wind_adjust = 1.0 + wind_brake_mps2 / 1000.0
|
||||
if accel_error > 0.0:
|
||||
wind_factor = float(np.clip(wind_factor * wind_adjust, 0.1, 3.0))
|
||||
else:
|
||||
wind_factor = float(np.clip(wind_factor / wind_adjust, 0.1, 3.0))
|
||||
|
||||
if gas_pedal_force <= 0.0:
|
||||
wind_factor = max(wind_factor, wind_factor_before_brake)
|
||||
else:
|
||||
wind_factor_before_brake = wind_factor
|
||||
|
||||
return gas_factor, wind_factor, wind_factor_before_brake
|
||||
|
||||
|
||||
def compute_gb_honda_bosch(accel, speed):
|
||||
# TODO returns 0s, is unused
|
||||
return 0.0, 0.0
|
||||
@@ -36,18 +150,18 @@ def compute_gas_brake(accel, speed, fingerprint):
|
||||
# TODO not clear this does anything useful
|
||||
def actuator_hysteresis(brake, braking, brake_steady, v_ego, car_fingerprint):
|
||||
# hyst params
|
||||
brake_hyst_on = 0.02 # to activate brakes exceed this value
|
||||
brake_hyst_on = 0.02 # to activate brakes exceed this value
|
||||
brake_hyst_off = 0.005 # to deactivate brakes below this value
|
||||
brake_hyst_gap = 0.01 # don't change brake command for small oscillations within this value
|
||||
brake_hyst_gap = 0.01 # don't change brake command for small oscillations within this value
|
||||
|
||||
# *** hysteresis logic to avoid brake blinking. go above 0.1 to trigger
|
||||
if (brake < brake_hyst_on and not braking) or brake < brake_hyst_off:
|
||||
brake = 0.
|
||||
braking = brake > 0.
|
||||
brake = 0.0
|
||||
braking = brake > 0.0
|
||||
|
||||
# for small brake oscillations within brake_hyst_gap, don't change the brake command
|
||||
if brake == 0.:
|
||||
brake_steady = 0.
|
||||
if brake == 0.0:
|
||||
brake_steady = 0.0
|
||||
elif brake > brake_steady + brake_hyst_gap:
|
||||
brake_steady = brake - brake_hyst_gap
|
||||
elif brake < brake_steady - brake_hyst_gap:
|
||||
@@ -64,7 +178,7 @@ def brake_pump_hysteresis(apply_brake, apply_brake_last, last_pump_ts, ts):
|
||||
# - there is an increment in brake request
|
||||
# - we are applying steady state brakes and we haven't been running the pump
|
||||
# for more than 20s (to prevent pressure bleeding)
|
||||
if apply_brake > apply_brake_last or (ts - last_pump_ts > 20. and apply_brake > 0):
|
||||
if apply_brake > apply_brake_last or (ts - last_pump_ts > 20.0 and apply_brake > 0):
|
||||
last_pump_ts = ts
|
||||
|
||||
# once the pump is on, run it for at least 0.2s
|
||||
@@ -93,14 +207,15 @@ class CarController(CarControllerBase):
|
||||
super().__init__(dbc_names, CP)
|
||||
self.packer = CANPacker(dbc_names[Bus.pt])
|
||||
self.params = CarControllerParams(CP)
|
||||
self.param_store = Params()
|
||||
self.CAN = hondacan.CanBus(CP)
|
||||
self.tja_control = CP.carFingerprint in HONDA_BOSCH_TJA_CONTROL
|
||||
|
||||
self.braking = False
|
||||
self.brake_steady = 0.
|
||||
self.brake_last = 0.
|
||||
self.brake_steady = 0.0
|
||||
self.brake_last = 0.0
|
||||
self.apply_brake_last = 0
|
||||
self.last_pump_ts = 0.
|
||||
self.last_pump_ts = 0.0
|
||||
self.stopping_counter = 0
|
||||
|
||||
self.accel = 0.0
|
||||
@@ -108,12 +223,41 @@ class CarController(CarControllerBase):
|
||||
self.gas = 0.0
|
||||
self.brake = 0.0
|
||||
self.last_torque = 0.0
|
||||
self.torque_lpf = 0.0
|
||||
self.prev_torque_cmd = 0.0
|
||||
self.steering_pressed_filter_s = 0.0
|
||||
self.steering_pressed_robust_prev = False
|
||||
self.bosch_last_gas = 0.0
|
||||
self.bosch_gas_factor = self.param_store.get_float("HondaGasFactorParams", default=1.0)
|
||||
self.bosch_wind_factor = self.param_store.get_float("HondaWindFactorParams", default=1.0)
|
||||
self.bosch_wind_factor_before_brake = self.bosch_wind_factor
|
||||
self.bosch_gas_factor_before_gasmax = self.bosch_gas_factor
|
||||
self.bosch_wind_factor_before_gasmax = self.bosch_wind_factor
|
||||
self.pitch = 0.0
|
||||
|
||||
def _modified_civic_standard_active(self) -> bool:
|
||||
return self.CP.carFingerprint == CAR.HONDA_CIVIC_BOSCH and bool(self.CP.flags & HondaFlags.EPS_MODIFIED)
|
||||
|
||||
def _filtered_steering_pressed(self, CS, torque_cmd: float) -> bool:
|
||||
self.steering_pressed_filter_s, steering_pressed = get_civic_bosch_modified_steering_pressed(
|
||||
bool(CS.out.steeringPressed),
|
||||
float(getattr(CS.out, "steeringTorque", 0.0)),
|
||||
float(torque_cmd),
|
||||
self.steering_pressed_filter_s,
|
||||
self.steering_pressed_robust_prev,
|
||||
)
|
||||
self.steering_pressed_robust_prev = steering_pressed
|
||||
return steering_pressed
|
||||
|
||||
def update(self, CC, CS, now_nanos, starpilot_toggles):
|
||||
actuators = CC.actuators
|
||||
hud_control = CC.hudControl
|
||||
hud_v_cruise = hud_control.setSpeed / CS.v_cruise_factor if hud_control.speedVisible else 255
|
||||
pcm_cancel_cmd = CC.cruiseControl.cancel
|
||||
gas_interceptor_command = 0.0
|
||||
if len(CC.orientationNED) == 3:
|
||||
self.pitch = CC.orientationNED[1]
|
||||
hill_brake = math.sin(self.pitch) * ACCELERATION_DUE_TO_GRAVITY
|
||||
|
||||
if CC.longActive:
|
||||
accel = actuators.accel
|
||||
@@ -122,17 +266,36 @@ class CarController(CarControllerBase):
|
||||
accel = 0.0
|
||||
gas, brake = 0.0, 0.0
|
||||
|
||||
torque_cmd = float(actuators.torque)
|
||||
filtered_steering_pressed = bool(CS.out.steeringPressed)
|
||||
if self._modified_civic_standard_active():
|
||||
if CC.latActive:
|
||||
filtered_steering_pressed = self._filtered_steering_pressed(CS, torque_cmd)
|
||||
if filtered_steering_pressed:
|
||||
self.torque_lpf = 0.0
|
||||
self.prev_torque_cmd = 0.0
|
||||
torque_cmd = 0.0
|
||||
else:
|
||||
tau = get_civic_bosch_modified_torque_lpf_tau(torque_cmd, self.prev_torque_cmd, CS.out.vEgo)
|
||||
alpha = DT_CTRL / (tau + DT_CTRL)
|
||||
self.torque_lpf = alpha * torque_cmd + ((1.0 - alpha) * self.torque_lpf)
|
||||
self.prev_torque_cmd = torque_cmd
|
||||
torque_cmd = self.torque_lpf
|
||||
else:
|
||||
self.torque_lpf = 0.0
|
||||
self.prev_torque_cmd = 0.0
|
||||
self.steering_pressed_filter_s = 0.0
|
||||
self.steering_pressed_robust_prev = False
|
||||
|
||||
# *** rate limit steer ***
|
||||
limited_torque = rate_limit(actuators.torque, self.last_torque, -self.params.STEER_DELTA_DOWN * DT_CTRL,
|
||||
self.params.STEER_DELTA_UP * DT_CTRL)
|
||||
limited_torque = rate_limit(torque_cmd, self.last_torque, -self.params.STEER_DELTA_DOWN * DT_CTRL, self.params.STEER_DELTA_UP * DT_CTRL)
|
||||
self.last_torque = limited_torque
|
||||
|
||||
# *** apply brake hysteresis ***
|
||||
pre_limit_brake, self.braking, self.brake_steady = actuator_hysteresis(brake, self.braking, self.brake_steady,
|
||||
CS.out.vEgo, self.CP.carFingerprint)
|
||||
pre_limit_brake, self.braking, self.brake_steady = actuator_hysteresis(brake, self.braking, self.brake_steady, CS.out.vEgo, self.CP.carFingerprint)
|
||||
|
||||
# *** rate limit after the enable check ***
|
||||
self.brake_last = rate_limit(pre_limit_brake, self.brake_last, -2., DT_CTRL)
|
||||
self.brake_last = rate_limit(pre_limit_brake, self.brake_last, -2.0, DT_CTRL)
|
||||
|
||||
# vehicle hud display, wait for one update from 10Hz 0x304 msg
|
||||
alert_fcw, alert_steer_required = process_hud_alert(hud_control.visualAlert)
|
||||
@@ -140,8 +303,7 @@ class CarController(CarControllerBase):
|
||||
# **** process the car messages ****
|
||||
|
||||
# steer torque is converted back to CAN reference (positive when steering right)
|
||||
apply_torque = int(np.interp(-limited_torque * self.params.STEER_MAX,
|
||||
self.params.STEER_LOOKUP_BP, self.params.STEER_LOOKUP_V))
|
||||
apply_torque = int(np.interp(-limited_torque * self.params.STEER_MAX, self.params.STEER_LOOKUP_BP, self.params.STEER_LOOKUP_V))
|
||||
|
||||
# Send CAN commands
|
||||
can_sends = []
|
||||
@@ -149,37 +311,29 @@ class CarController(CarControllerBase):
|
||||
# tester present - w/ no response (keeps radar disabled)
|
||||
if self.CP.carFingerprint in (HONDA_BOSCH - HONDA_BOSCH_RADARLESS) and self.CP.openpilotLongitudinalControl:
|
||||
if self.frame % 10 == 0:
|
||||
can_sends.append(make_tester_present_msg(0x18DAB0F1, 1, suppress_response=True))
|
||||
can_sends.append(make_tester_present_msg(0x18DAB0F1, self.CAN.pt, suppress_response=True))
|
||||
|
||||
# Send steering command.
|
||||
can_sends.append(hondacan.create_steering_control(self.packer, self.CAN, apply_torque, CC.latActive, self.tja_control))
|
||||
|
||||
# wind brake from air resistance decel at high speed
|
||||
wind_brake = np.interp(CS.out.vEgo, [0.0, 2.3, 35.0], [0.001, 0.002, 0.15])
|
||||
wind_brake = float(np.interp(CS.out.vEgo, [0.0, 2.3, 35.0], [0.001, 0.002, 0.15]))
|
||||
wind_brake_mps2 = get_honda_bosch_wind_brake_mps2(CS.out.vEgo)
|
||||
# all of this is only relevant for HONDA NIDEC
|
||||
max_accel = np.interp(CS.out.vEgo, self.params.NIDEC_MAX_ACCEL_BP, self.params.NIDEC_MAX_ACCEL_V)
|
||||
# TODO this 1.44 is just to maintain previous behavior
|
||||
pcm_speed_BP = [-wind_brake,
|
||||
-wind_brake * (3 / 4),
|
||||
0.0,
|
||||
0.5]
|
||||
pcm_speed_BP = [-wind_brake, -wind_brake * (3 / 4), 0.0, 0.5]
|
||||
# The Honda ODYSSEY seems to have different PCM_ACCEL
|
||||
# msgs, is it other cars too?
|
||||
if not CC.longActive:
|
||||
if self.CP.enableGasInterceptorDEPRECATED or not CC.longActive:
|
||||
pcm_speed = 0.0
|
||||
pcm_accel = int(0.0)
|
||||
elif self.CP.carFingerprint in HONDA_NIDEC_ALT_PCM_ACCEL:
|
||||
pcm_speed_V = [0.0,
|
||||
np.clip(CS.out.vEgo - 3.0, 0.0, 100.0),
|
||||
np.clip(CS.out.vEgo + 0.0, 0.0, 100.0),
|
||||
np.clip(CS.out.vEgo + 5.0, 0.0, 100.0)]
|
||||
pcm_speed_V = [0.0, np.clip(CS.out.vEgo - 3.0, 0.0, 100.0), np.clip(CS.out.vEgo + 0.0, 0.0, 100.0), np.clip(CS.out.vEgo + 5.0, 0.0, 100.0)]
|
||||
pcm_speed = float(np.interp(gas - brake, pcm_speed_BP, pcm_speed_V))
|
||||
pcm_accel = int(1.0 * self.params.NIDEC_GAS_MAX)
|
||||
else:
|
||||
pcm_speed_V = [0.0,
|
||||
np.clip(CS.out.vEgo - 2.0, 0.0, 100.0),
|
||||
np.clip(CS.out.vEgo + 2.0, 0.0, 100.0),
|
||||
np.clip(CS.out.vEgo + 5.0, 0.0, 100.0)]
|
||||
pcm_speed_V = [0.0, np.clip(CS.out.vEgo - 2.0, 0.0, 100.0), np.clip(CS.out.vEgo + 2.0, 0.0, 100.0), np.clip(CS.out.vEgo + 5.0, 0.0, 100.0)]
|
||||
pcm_speed = float(np.interp(gas - brake, pcm_speed_BP, pcm_speed_V))
|
||||
pcm_accel = int(np.clip((accel / 1.44) / max_accel, 0.0, 1.0) * self.params.NIDEC_GAS_MAX)
|
||||
|
||||
@@ -199,35 +353,108 @@ class CarController(CarControllerBase):
|
||||
|
||||
if self.CP.carFingerprint in HONDA_BOSCH:
|
||||
self.accel = float(np.clip(accel, self.params.BOSCH_ACCEL_MIN, self.params.BOSCH_ACCEL_MAX))
|
||||
self.gas = float(np.interp(accel, self.params.BOSCH_GAS_LOOKUP_BP, self.params.BOSCH_GAS_LOOKUP_V))
|
||||
gas_pedal_force = self.accel + hill_brake
|
||||
|
||||
if self.CP.carFingerprint not in HONDA_BOSCH_RADARLESS:
|
||||
gas_pedal_force += wind_brake_mps2 * self.bosch_wind_factor
|
||||
|
||||
if actuators.longControlState == LongCtrlState.pid and not CS.out.gasPressed:
|
||||
gas_error = self.accel - CS.out.aEgo
|
||||
|
||||
if gas_error != 0.0 and gas_pedal_force > 0.0:
|
||||
if self.CP.carFingerprint == CAR.HONDA_INSIGHT:
|
||||
gas_learn_speed = 150.0
|
||||
elif self.CP.carFingerprint in (CAR.ACURA_RDX_3G, CAR.ACURA_RDX_3G_MMR):
|
||||
gas_learn_speed = 300.0
|
||||
else:
|
||||
gas_learn_speed = 50.0
|
||||
self.bosch_gas_factor = float(np.clip(self.bosch_gas_factor + gas_error / gas_learn_speed * gas_pedal_force, 0.1, 3.0))
|
||||
|
||||
if gas_error != 0.0 and not CS.out.brakePressed and CS.out.vEgo > 0.0:
|
||||
wind_learn_speed = 100.0 if self.CP.carFingerprint in (CAR.ACURA_RDX_3G, CAR.ACURA_RDX_3G_MMR) else 1000.0
|
||||
wind_adjust = 1.0 + wind_brake_mps2 / wind_learn_speed
|
||||
if gas_error > 0.0:
|
||||
self.bosch_wind_factor = float(np.clip(self.bosch_wind_factor * wind_adjust, 0.1, 3.0))
|
||||
else:
|
||||
self.bosch_wind_factor = float(np.clip(self.bosch_wind_factor / wind_adjust, 0.1, 3.0))
|
||||
|
||||
if gas_pedal_force <= 0.0:
|
||||
self.bosch_wind_factor = max(self.bosch_wind_factor, self.bosch_wind_factor_before_brake)
|
||||
else:
|
||||
self.bosch_wind_factor_before_brake = self.bosch_wind_factor
|
||||
|
||||
if gas_pedal_force >= self.params.BOSCH_ACCEL_MAX:
|
||||
self.bosch_gas_factor = min(self.bosch_gas_factor, self.bosch_gas_factor_before_gasmax)
|
||||
self.bosch_wind_factor = min(self.bosch_wind_factor, self.bosch_wind_factor_before_gasmax)
|
||||
else:
|
||||
self.bosch_gas_factor_before_gasmax = self.bosch_gas_factor
|
||||
self.bosch_wind_factor_before_gasmax = self.bosch_wind_factor
|
||||
|
||||
self.gas = float(np.interp(gas_pedal_force * self.bosch_gas_factor, self.params.BOSCH_GAS_LOOKUP_BP, self.params.BOSCH_GAS_LOOKUP_V))
|
||||
self.gas = min(self.gas, max(60.0, self.bosch_last_gas + 60.0))
|
||||
self.bosch_last_gas = self.gas
|
||||
|
||||
stopping = actuators.longControlState == LongCtrlState.stopping
|
||||
self.stopping_counter = self.stopping_counter + 1 if stopping else 0
|
||||
can_sends.extend(hondacan.create_acc_commands(self.packer, self.CAN, CC.enabled, CC.longActive, self.accel, self.gas,
|
||||
self.stopping_counter, self.CP.carFingerprint))
|
||||
can_sends.extend(
|
||||
hondacan.create_acc_commands(self.packer, self.CAN, CC.enabled, CC.longActive, self.accel, self.gas, self.stopping_counter, self.CP.carFingerprint)
|
||||
)
|
||||
else:
|
||||
apply_brake = np.clip(self.brake_last - wind_brake, 0.0, 1.0)
|
||||
apply_brake = int(np.clip(apply_brake * self.params.NIDEC_BRAKE_MAX, 0, self.params.NIDEC_BRAKE_MAX - 1))
|
||||
pump_on, self.last_pump_ts = brake_pump_hysteresis(apply_brake, self.apply_brake_last, self.last_pump_ts, ts)
|
||||
|
||||
pcm_override = True
|
||||
can_sends.append(hondacan.create_brake_command(self.packer, self.CAN, apply_brake, pump_on,
|
||||
pcm_override, pcm_cancel_cmd, alert_fcw,
|
||||
self.CP.carFingerprint, CS.stock_brake))
|
||||
can_sends.append(
|
||||
hondacan.create_brake_command(
|
||||
self.packer, self.CAN, apply_brake, pump_on, pcm_override, pcm_cancel_cmd, alert_fcw, self.CP.carFingerprint, CS.stock_brake
|
||||
)
|
||||
)
|
||||
self.apply_brake_last = apply_brake
|
||||
self.brake = apply_brake / self.params.NIDEC_BRAKE_MAX
|
||||
|
||||
if self.CP.enableGasInterceptorDEPRECATED:
|
||||
gas_error = actuators.accel - CS.out.aEgo
|
||||
|
||||
if not CS.out.gasPressed and actuators.longControlState == LongCtrlState.pid:
|
||||
if gas_error != 0.0 and gas > 0.0:
|
||||
self.bosch_gas_factor = float(np.clip(self.bosch_gas_factor + gas_error / 150.0 * (gas * 4.8), 0.1, 3.0))
|
||||
if gas_error != 0.0 and not CS.out.brakePressed and CS.out.vEgo > 0.0:
|
||||
wind_adjust = 1.0 + (wind_brake * 4.8) / 1000.0
|
||||
if gas_error > 0.0:
|
||||
self.bosch_wind_factor = float(np.clip(self.bosch_wind_factor * wind_adjust, 0.1, 5.0))
|
||||
else:
|
||||
self.bosch_wind_factor = float(np.clip(self.bosch_wind_factor / wind_adjust, 0.1, 5.0))
|
||||
if gas <= 0.0:
|
||||
self.bosch_wind_factor = max(self.bosch_wind_factor, self.bosch_wind_factor_before_brake)
|
||||
else:
|
||||
self.bosch_wind_factor_before_brake = self.bosch_wind_factor
|
||||
|
||||
gas_mult = float(np.interp(CS.out.vEgo, [0.0, 10.0], [0.4, 1.0]))
|
||||
if CC.longActive:
|
||||
gas_interceptor_command = float(np.clip(
|
||||
gas_mult * ((gas * self.bosch_gas_factor) - brake + (wind_brake * self.bosch_wind_factor * 3.0 / 4.0)),
|
||||
0.0,
|
||||
1.0,
|
||||
))
|
||||
idx = (self.frame // 2) % 0x10
|
||||
can_sends.append(create_gas_interceptor_command(self.packer, gas_interceptor_command, idx))
|
||||
|
||||
# Send dashboard UI commands.
|
||||
if self.frame % 10 == 0:
|
||||
if self.CP.openpilotLongitudinalControl:
|
||||
# On Nidec, this also controls longitudinal positive acceleration
|
||||
can_sends.append(hondacan.create_acc_hud(self.packer, self.CAN.pt, self.CP, CC.enabled, pcm_speed, pcm_accel,
|
||||
hud_control, hud_v_cruise, CS.is_metric, CS.acc_hud))
|
||||
can_sends.append(
|
||||
hondacan.create_acc_hud(self.packer, self.CAN.pt, self.CP, CC.enabled, pcm_speed, pcm_accel, hud_control, hud_v_cruise, CS.is_metric, CS.acc_hud)
|
||||
)
|
||||
|
||||
steering_available = CS.out.cruiseState.available and CS.out.vEgo > self.CP.minSteerSpeed
|
||||
reduced_steering = CS.out.steeringPressed
|
||||
can_sends.extend(hondacan.create_lkas_hud(self.packer, self.CAN.lkas, self.CP, hud_control, CC.latActive,
|
||||
steering_available, reduced_steering, alert_steer_required, CS.lkas_hud))
|
||||
reduced_steering = filtered_steering_pressed
|
||||
can_sends.extend(
|
||||
hondacan.create_lkas_hud(
|
||||
self.packer, self.CAN.lkas, self.CP, hud_control, CC.latActive, steering_available, reduced_steering, alert_steer_required, CS.lkas_hud
|
||||
)
|
||||
)
|
||||
|
||||
if self.CP.openpilotLongitudinalControl:
|
||||
# TODO: combining with create_acc_hud block above will change message order and will need replay logs regenerated
|
||||
@@ -237,7 +464,14 @@ class CarController(CarControllerBase):
|
||||
can_sends.append(hondacan.create_legacy_brake_command(self.packer, self.CAN.pt))
|
||||
if self.CP.carFingerprint not in HONDA_BOSCH:
|
||||
self.speed = pcm_speed
|
||||
self.gas = pcm_accel / self.params.NIDEC_GAS_MAX
|
||||
if self.CP.enableGasInterceptorDEPRECATED:
|
||||
self.gas = gas_interceptor_command
|
||||
else:
|
||||
self.gas = pcm_accel / self.params.NIDEC_GAS_MAX
|
||||
|
||||
if self.frame > 0 and self.frame % 6000 == 0:
|
||||
self.param_store.put_float("HondaGasFactorParams", self.bosch_gas_factor)
|
||||
self.param_store.put_float("HondaWindFactorParams", self.bosch_wind_factor)
|
||||
|
||||
new_actuators = actuators.as_builder()
|
||||
new_actuators.speed = self.speed
|
||||
|
||||
@@ -3,11 +3,11 @@ from collections import defaultdict
|
||||
|
||||
from cereal import custom
|
||||
from opendbc.can import CANDefine, CANParser
|
||||
from opendbc.car import Bus, create_button_events, structs
|
||||
from opendbc.car import Bus, DT_CTRL, create_button_events, structs
|
||||
from opendbc.car.common.conversions import Conversions as CV
|
||||
from opendbc.car.honda.hondacan import CanBus
|
||||
from opendbc.car.honda.values import CAR, DBC, STEER_THRESHOLD, HONDA_BOSCH, HONDA_BOSCH_ALT_RADAR, HONDA_BOSCH_CANFD, \
|
||||
HONDA_NIDEC_ALT_SCM_MESSAGES, HONDA_BOSCH_RADARLESS, \
|
||||
HONDA_NIDEC_ALT_SCM_MESSAGES, HONDA_BOSCH_RADARLESS, HONDA_BOSCH_TJA_CONTROL, \
|
||||
HondaFlags, CruiseButtons, CruiseSettings, GearShifter, CarControllerParams, HondaStarPilotFlags
|
||||
from opendbc.car.interfaces import CarStateBase
|
||||
|
||||
@@ -54,13 +54,19 @@ class CarState(CarStateBase):
|
||||
self.brake_switch_active = False
|
||||
self.low_speed_alert = False
|
||||
|
||||
self.dynamic_v_cruise_units = self.CP.carFingerprint in (HONDA_BOSCH_RADARLESS | HONDA_BOSCH_ALT_RADAR | HONDA_BOSCH_CANFD)
|
||||
self.dynamic_v_cruise_units = self.CP.carFingerprint in (HONDA_BOSCH_RADARLESS | HONDA_BOSCH_ALT_RADAR |
|
||||
HONDA_BOSCH_TJA_CONTROL | HONDA_BOSCH_CANFD)
|
||||
self.cruise_setting = 0
|
||||
self.v_cruise_pcm_prev = 0
|
||||
|
||||
# When available we use cp.vl["CAR_SPEED"]["ROUGH_CAR_SPEED_2"] to populate vEgoCluster
|
||||
# However, on cars without a digital speedometer this is not always present (HRV, FIT, CRV 2016, ILX and RDX)
|
||||
self.dash_speed_seen = False
|
||||
self.is_metric = False
|
||||
self.v_cruise_factor = 1.
|
||||
|
||||
self.initial_accFault_cleared = False
|
||||
self.initial_accFault_cleared_timer = int(10 / DT_CTRL)
|
||||
|
||||
def update(self, can_parsers, starpilot_toggles) -> structs.CarState:
|
||||
cp = can_parsers[Bus.pt]
|
||||
@@ -81,18 +87,19 @@ class CarState(CarStateBase):
|
||||
self.cruise_buttons = cp.vl["SCM_BUTTONS"]["CRUISE_BUTTONS"]
|
||||
|
||||
# used for car hud message
|
||||
self.is_metric = not cp.vl["CAR_SPEED"]["IMPERIAL_UNIT"]
|
||||
self.is_metric = self.CP.carFingerprint in (CAR.HONDA_ODYSSEY_TWN,) or not cp.vl["CAR_SPEED"]["IMPERIAL_UNIT"]
|
||||
self.v_cruise_factor = CV.MPH_TO_MS if self.dynamic_v_cruise_units and not self.is_metric else CV.KPH_TO_MS
|
||||
|
||||
# ******************* parse out can *******************
|
||||
|
||||
# blend in transmission speed at low speed, since it has more low speed accuracy
|
||||
# STANDSTILL->WHEELS_MOVING bit can be noisy around zero, so use XMISSION_SPEED
|
||||
lowspeed_source = cp.vl["CAR_SPEED"]["CAR_SPEED"] if self.CP.carFingerprint == CAR.ACURA_INTEGRA else cp.vl["ENGINE_DATA"]["XMISSION_SPEED"]
|
||||
v_wheel = sum([cp.vl["WHEEL_SPEEDS"][f"WHEEL_SPEED_{s}"] for s in ("FL", "FR", "RL", "RR")]) / 4.0 * CV.KPH_TO_MS
|
||||
v_weight = float(np.interp(v_wheel, v_weight_bp, v_weight_v))
|
||||
ret.vEgoRaw = (1. - v_weight) * cp.vl["ENGINE_DATA"]["XMISSION_SPEED"] * CV.KPH_TO_MS * self.CP.wheelSpeedFactor + v_weight * v_wheel
|
||||
ret.vEgoRaw = (1. - v_weight) * lowspeed_source * CV.KPH_TO_MS * self.CP.wheelSpeedFactor + v_weight * v_wheel
|
||||
ret.vEgo, ret.aEgo = self.update_speed_kf(ret.vEgoRaw)
|
||||
ret.standstill = cp.vl["ENGINE_DATA"]["XMISSION_SPEED"] < 1e-5
|
||||
ret.standstill = lowspeed_source < 1e-5
|
||||
|
||||
# doorOpen is true if we can find any door open, but signal locations vary, and we may only see the driver's door
|
||||
# TODO: Test the eight Nidec cars without SCM signals for driver's door state, may be able to consolidate further
|
||||
@@ -107,7 +114,8 @@ class CarState(CarStateBase):
|
||||
ret.seatbeltUnlatched = bool(cp.vl["SEATBELT_STATUS"]["SEATBELT_DRIVER_LAMP"] or not cp.vl["SEATBELT_STATUS"]["SEATBELT_DRIVER_LATCHED"])
|
||||
|
||||
steer_status = self.steer_status_values[cp.vl["STEER_STATUS"]["STEER_STATUS"]]
|
||||
ret.steerFaultPermanent = steer_status not in ("NORMAL", "NO_TORQUE_ALERT_1", "NO_TORQUE_ALERT_2", "LOW_SPEED_LOCKOUT", "TMP_FAULT")
|
||||
ret.steerFaultPermanent = steer_status not in ("NORMAL", "NO_TORQUE_ALERT_1", "NO_TORQUE_ALERT_2", "LOW_SPEED_LOCKOUT", "TJA_LOW_SPEED_LOCKOUT",
|
||||
"TMP_FAULT")
|
||||
if self.CP.carFingerprint in HONDA_BOSCH_ALT_RADAR:
|
||||
# TODO: See if this logic works for all other Honda
|
||||
min_steer_speed = max(CarControllerParams.STEER_GLOBAL_MIN_SPEED, self.CP.minSteerSpeed)
|
||||
@@ -117,7 +125,11 @@ class CarState(CarStateBase):
|
||||
# LOW_SPEED_LOCKOUT is not worth a warning
|
||||
# NO_TORQUE_ALERT_2 can be caused by bump or steering nudge from driver
|
||||
# FIXME: the stock camera stops steering on NO_TORQUE_ALERT_1
|
||||
ret.steerFaultTemporary = steer_status not in ("NORMAL", "LOW_SPEED_LOCKOUT", "NO_TORQUE_ALERT_2")
|
||||
ret.steerFaultTemporary = steer_status not in ("NORMAL", "LOW_SPEED_LOCKOUT", "TJA_LOW_SPEED_LOCKOUT", "NO_TORQUE_ALERT_2")
|
||||
|
||||
if self.CP.carFingerprint == CAR.ACURA_MDX_4G and steer_status == "TJA_LOW_SPEED_LOCKOUT":
|
||||
ret.steerFaultPermanent = False
|
||||
ret.steerFaultTemporary = False
|
||||
|
||||
# All Honda EPS cut off slightly above standstill, some much higher
|
||||
# Don't alert in the near-standstill range, but alert for per-vehicle configured minimums above that
|
||||
@@ -128,22 +140,23 @@ class CarState(CarStateBase):
|
||||
self.low_speed_alert = False
|
||||
ret.lowSpeedAlert = self.low_speed_alert
|
||||
|
||||
if self.CP.carFingerprint in HONDA_BOSCH_RADARLESS:
|
||||
if self.CP.carFingerprint not in HONDA_BOSCH:
|
||||
ret.carFaultedNonCritical = bool(cp_cam.vl["ACC_HUD"]["ACC_PROBLEM"] or cp_cam.vl["LKAS_HUD"]["LKAS_PROBLEM"])
|
||||
elif self.CP.carFingerprint in HONDA_BOSCH_RADARLESS:
|
||||
ret.accFaulted = bool(cp.vl["CRUISE_FAULT_STATUS"]["CRUISE_FAULT"])
|
||||
else:
|
||||
if self.CP.openpilotLongitudinalControl:
|
||||
elif self.CP.openpilotLongitudinalControl:
|
||||
if self.CP.carFingerprint in (HONDA_BOSCH_CANFD | HONDA_BOSCH_TJA_CONTROL) and (self.CP.flags & HondaFlags.BOSCH_ALT_BRAKE):
|
||||
ret.accFaulted = bool(cp.vl["BRAKE_MODULE"]["CRUISE_FAULT"])
|
||||
else:
|
||||
ret.accFaulted = bool(cp.vl[self.brake_error_msg]["BRAKE_ERROR_1"] or cp.vl[self.brake_error_msg]["BRAKE_ERROR_2"])
|
||||
|
||||
# Log non-critical stock ACC/LKAS faults if Nidec (camera)
|
||||
if self.CP.carFingerprint not in HONDA_BOSCH:
|
||||
ret.carFaultedNonCritical = bool(cp_cam.vl["ACC_HUD"]["ACC_PROBLEM"] or cp_cam.vl["LKAS_HUD"]["LKAS_PROBLEM"])
|
||||
|
||||
ret.espDisabled = cp.vl["VSA_STATUS"]["ESP_DISABLED"] != 0
|
||||
|
||||
self.dash_speed_seen = self.dash_speed_seen or cp.vl["CAR_SPEED"]["ROUGH_CAR_SPEED_2"] > 1e-3
|
||||
if self.dash_speed_seen:
|
||||
conversion = CV.KPH_TO_MS if self.is_metric else CV.MPH_TO_MS
|
||||
ret.vEgoCluster = cp.vl["CAR_SPEED"]["ROUGH_CAR_SPEED_2"] * conversion
|
||||
if self.CP.carFingerprint not in (CAR.HONDA_ODYSSEY_TWN,):
|
||||
self.dash_speed_seen = self.dash_speed_seen or cp.vl["CAR_SPEED"]["ROUGH_CAR_SPEED_2"] > 1e-3
|
||||
if self.dash_speed_seen:
|
||||
conversion = CV.KPH_TO_MS if self.is_metric else CV.MPH_TO_MS
|
||||
ret.vEgoCluster = cp.vl["CAR_SPEED"]["ROUGH_CAR_SPEED_2"] * conversion
|
||||
|
||||
ret.steeringAngleDeg = cp.vl["STEERING_SENSORS"]["STEER_ANGLE"]
|
||||
ret.steeringRateDeg = cp.vl["STEERING_SENSORS"]["STEER_ANGLE_RATE"]
|
||||
@@ -154,12 +167,16 @@ class CarState(CarStateBase):
|
||||
ret.parkingBrake = bool(cp.vl[self.car_state_scm_msg]["PARKING_BRAKE_ON"])
|
||||
|
||||
if self.CP.transmissionType == TransmissionType.manual:
|
||||
ret.gearShifter = GearShifter.reverse if bool(cp.vl["SCM_FEEDBACK"]["REVERSE_LIGHT"]) else GearShifter.drive
|
||||
ret.gearShifter = GearShifter.reverse if bool(cp.vl[self.car_state_scm_msg]["REVERSE_LIGHT"]) else GearShifter.drive
|
||||
else:
|
||||
gear_position = self.shifter_values.get(cp.vl[self.gearbox_msg]["GEAR_SHIFTER"], None)
|
||||
ret.gearShifter = self.parse_gear_shifter(gear_position)
|
||||
|
||||
ret.gasPressed = cp.vl["POWERTRAIN_DATA"]["PEDAL_GAS"] > 1e-5
|
||||
if self.CP.enableGasInterceptorDEPRECATED:
|
||||
gas = (cp.vl["GAS_SENSOR"]["INTERCEPTOR_GAS"] + cp.vl["GAS_SENSOR"]["INTERCEPTOR_GAS2"]) / 2.
|
||||
ret.gasPressed = gas > 492
|
||||
else:
|
||||
ret.gasPressed = cp.vl["POWERTRAIN_DATA"]["PEDAL_GAS"] > 1e-5
|
||||
|
||||
ret.steeringTorque = cp.vl["STEER_STATUS"]["STEER_TORQUE_SENSOR"]
|
||||
ret.steeringPressed = abs(ret.steeringTorque) > STEER_THRESHOLD.get(self.CP.carFingerprint, 1200)
|
||||
@@ -200,6 +217,16 @@ class CarState(CarStateBase):
|
||||
ret.cruiseState.enabled = cp.vl["POWERTRAIN_DATA"]["ACC_STATUS"] != 0
|
||||
ret.cruiseState.available = bool(cp.vl[self.car_state_scm_msg]["MAIN_ON"])
|
||||
|
||||
if ret.accFaulted:
|
||||
if self.CP.carFingerprint in HONDA_BOSCH and not self.initial_accFault_cleared:
|
||||
ret.accFaulted = False
|
||||
ret.cruiseState.available = False
|
||||
elif self.initial_accFault_cleared_timer == 0:
|
||||
self.initial_accFault_cleared = True
|
||||
|
||||
if self.initial_accFault_cleared_timer > 0:
|
||||
self.initial_accFault_cleared_timer -= 1
|
||||
|
||||
# Gets rid of Pedal Grinding noise when brake is pressed at slow speeds for some models
|
||||
if self.CP.carFingerprint in (CAR.HONDA_PILOT, CAR.HONDA_RIDGELINE):
|
||||
if ret.brake > 0.1:
|
||||
@@ -218,7 +245,7 @@ class CarState(CarStateBase):
|
||||
ret.stockFcw = cp_cam.vl["BRAKE_COMMAND"]["FCW"] != 0
|
||||
self.acc_hud = cp_cam.vl["ACC_HUD"]
|
||||
self.stock_brake = cp_cam.vl["BRAKE_COMMAND"]
|
||||
if self.CP.carFingerprint in HONDA_BOSCH_RADARLESS:
|
||||
if self.CP.carFingerprint in (HONDA_BOSCH_RADARLESS | HONDA_BOSCH_CANFD):
|
||||
self.lkas_hud = cp_cam.vl["LKAS_HUD"]
|
||||
|
||||
if self.CP.enableBsm:
|
||||
@@ -235,6 +262,9 @@ class CarState(CarStateBase):
|
||||
fp_ret = custom.StarPilotCarState.new_message()
|
||||
|
||||
fp_ret.dashboardSpeedLimit = calculate_speed_limit(self.CP, self.FPCP, cp, cp_cam)
|
||||
if self.FPCP.flags & HondaStarPilotFlags.HAS_CAMERA_MESSAGES:
|
||||
sign_bus = cp if (self.CP.carFingerprint in (HONDA_BOSCH - HONDA_BOSCH_RADARLESS - HONDA_BOSCH_CANFD)) else cp_cam
|
||||
fp_ret.dashboardStopSign = 1 if sign_bus.vl["CAMERA_MESSAGES"]["ROAD_SIGN"] == 89 else 0
|
||||
|
||||
return ret, fp_ret
|
||||
|
||||
|
||||
@@ -84,6 +84,7 @@ FW_VERSIONS = {
|
||||
],
|
||||
(Ecu.eps, 0x18da30f1, None): [
|
||||
b'39990-TBX-H120\x00\x00',
|
||||
b'39990-TVA,A150\x00\x00',
|
||||
b'39990-TVA-A140\x00\x00',
|
||||
b'39990-TVA-A150\x00\x00',
|
||||
b'39990-TVA-A160\x00\x00',
|
||||
@@ -142,6 +143,29 @@ FW_VERSIONS = {
|
||||
b'38897-TWD-J020\x00\x00',
|
||||
],
|
||||
},
|
||||
CAR.HONDA_CLARITY: {
|
||||
(Ecu.shiftByWire, 0x18da0bf1, None): [
|
||||
b'54008-TRW-A910\x00\x00',
|
||||
],
|
||||
(Ecu.vsa, 0x18da28f1, None): [
|
||||
b'57114-TRW-A010\x00\x00',
|
||||
b'57114-TRW-A020\x00\x00',
|
||||
],
|
||||
(Ecu.eps, 0x18da30f1, None): [
|
||||
b'39990-TRW-A020\x00\x00',
|
||||
b'39990-TRW,A020\x00\x00',
|
||||
],
|
||||
(Ecu.srs, 0x18da53f1, None): [
|
||||
b'77959-TRW-A210\x00\x00',
|
||||
b'77959-TRW-A220\x00\x00',
|
||||
],
|
||||
(Ecu.gateway, 0x18daeff1, None): [
|
||||
b'38897-TRW-A010\x00\x00',
|
||||
],
|
||||
(Ecu.fwdRadar, 0x18dab0f1, None): [
|
||||
b'36161-TRW-A110\x00\x00',
|
||||
],
|
||||
},
|
||||
CAR.HONDA_CIVIC: {
|
||||
(Ecu.transmission, 0x18da1ef1, None): [
|
||||
b'28101-5CG-A040\x00\x00',
|
||||
@@ -163,6 +187,7 @@ FW_VERSIONS = {
|
||||
b'57114-TEA-Q220\x00\x00',
|
||||
],
|
||||
(Ecu.eps, 0x18da30f1, None): [
|
||||
b'39990-TBA,A030\x00\x00',
|
||||
b'39990-TBA-A030\x00\x00',
|
||||
b'39990-TBG-A030\x00\x00',
|
||||
b'39990-TEA-T020\x00\x00',
|
||||
@@ -230,11 +255,14 @@ FW_VERSIONS = {
|
||||
b'57114-TGL-G330\x00\x00',
|
||||
],
|
||||
(Ecu.eps, 0x18da30f1, None): [
|
||||
b'39990-TBA-C120\x00\x00',
|
||||
b'39990-TBA-C020\x00\x00',
|
||||
b'39990-TBA-C120\x00\x00',
|
||||
b'39990-TEA-T330\x00\x00',
|
||||
b'39990-TEA-T820\x00\x00',
|
||||
b'39990-TEZ-T020\x00\x00',
|
||||
b'39990-TGG,A020\x00\x00',
|
||||
b'39990-TGG,A120\x00\x00',
|
||||
b'39990-TGG-A020\x00\x00',
|
||||
b'39990-TGG-A120\x00\x00',
|
||||
b'39990-TGG-J510\x00\x00',
|
||||
@@ -368,6 +396,7 @@ FW_VERSIONS = {
|
||||
b'57114-TMC-Z050\x00\x00',
|
||||
],
|
||||
(Ecu.eps, 0x18da30f1, None): [
|
||||
b'39990-TLA,A040\x00\x00',
|
||||
b'39990-TLA-A040\x00\x00',
|
||||
b'39990-TLA-A110\x00\x00',
|
||||
b'39990-TLA-A220\x00\x00',
|
||||
|
||||
@@ -167,18 +167,19 @@ def create_acc_hud(packer, bus, CP, enabled, pcm_speed, pcm_accel, hud_control,
|
||||
|
||||
def create_lkas_hud(packer, bus, CP, hud_control, lat_active, steering_available, reduced_steering, alert_steer_required, lkas_hud):
|
||||
commands = []
|
||||
lanes_visible = bool(hud_control.lanesVisible or lat_active)
|
||||
|
||||
lkas_hud_values = {
|
||||
'LKAS_READY': 1,
|
||||
'LKAS_STATE_CHANGE': 1,
|
||||
'STEERING_REQUIRED': alert_steer_required,
|
||||
'SOLID_LANES': hud_control.lanesVisible,
|
||||
'SOLID_LANES': lanes_visible,
|
||||
'BEEP': 0,
|
||||
}
|
||||
|
||||
if CP.carFingerprint in (HONDA_BOSCH_RADARLESS | HONDA_BOSCH_CANFD):
|
||||
lkas_hud_values['LANE_LINES'] = 3
|
||||
lkas_hud_values['DASHED_LANES'] = hud_control.lanesVisible
|
||||
lkas_hud_values['DASHED_LANES'] = lanes_visible
|
||||
|
||||
# car likely needs to see LKAS_PROBLEM fall within a specific time frame, so forward from camera
|
||||
# TODO: needed for Bosch CAN FD?
|
||||
@@ -192,8 +193,8 @@ def create_lkas_hud(packer, bus, CP, hud_control, lat_active, steering_available
|
||||
# New HUD concept for selected Bosch cars, overwrites some of the above
|
||||
# TODO: make global across all Honda if feedback is favorable
|
||||
if CP.carFingerprint in HONDA_BOSCH_ALT_RADAR:
|
||||
lkas_hud_values['DASHED_LANES'] = steering_available
|
||||
lkas_hud_values['SOLID_LANES'] = lat_active
|
||||
lkas_hud_values['DASHED_LANES'] = bool(steering_available or lanes_visible)
|
||||
lkas_hud_values['SOLID_LANES'] = lanes_visible
|
||||
lkas_hud_values['LKAS_PROBLEM'] = lat_active and reduced_steering
|
||||
|
||||
if CP.flags & HondaFlags.BOSCH_EXT_HUD and not CP.openpilotLongitudinalControl:
|
||||
|
||||
@@ -23,6 +23,8 @@ class CarInterface(CarInterfaceBase):
|
||||
def get_pid_accel_limits(CP, current_speed, cruise_speed):
|
||||
if CP.carFingerprint in HONDA_BOSCH:
|
||||
return CarControllerParams.BOSCH_ACCEL_MIN, CarControllerParams.BOSCH_ACCEL_MAX
|
||||
elif CP.enableGasInterceptorDEPRECATED:
|
||||
return CarControllerParams.NIDEC_ACCEL_MIN, CarControllerParams.NIDEC_ACCEL_MAX
|
||||
else:
|
||||
# NIDECs don't allow acceleration near cruise_speed,
|
||||
# so limit limits of pid to prevent windup
|
||||
@@ -46,15 +48,17 @@ class CarInterface(CarInterfaceBase):
|
||||
# Disable the radar and let openpilot control longitudinal
|
||||
# WARNING: THIS DISABLES AEB!
|
||||
# If Bosch radarless, this blocks ACC messages from the camera
|
||||
# TODO: get radar disable working on Bosch CANFD
|
||||
ret.alphaLongitudinalAvailable = candidate not in HONDA_BOSCH_CANFD
|
||||
ret.openpilotLongitudinalControl = alpha_long and (candidate not in HONDA_BOSCH_CANFD)
|
||||
ret.alphaLongitudinalAvailable = True
|
||||
ret.openpilotLongitudinalControl = alpha_long
|
||||
ret.pcmCruise = not ret.openpilotLongitudinalControl
|
||||
else:
|
||||
ret.safetyConfigs = [get_safety_config(structs.CarParams.SafetyModel.hondaNidec)]
|
||||
ret.openpilotLongitudinalControl = True
|
||||
|
||||
ret.pcmCruise = True
|
||||
ret.enableGasInterceptorDEPRECATED = 0x201 in fingerprint[CAN.pt]
|
||||
if ret.enableGasInterceptorDEPRECATED:
|
||||
ret.pcmCruise = False
|
||||
|
||||
if candidate == CAR.HONDA_CRV_5G:
|
||||
ret.enableBsm = 0x12f8bfa7 in fingerprint[CAN.radar]
|
||||
@@ -80,6 +84,7 @@ class CarInterface(CarInterfaceBase):
|
||||
ret.lateralTuning.pid.kiBP, ret.lateralTuning.pid.kpBP = [[0.], [0.]]
|
||||
ret.lateralTuning.pid.kf = 0.00006 # conservative feed-forward
|
||||
ret.steerActuatorDelay = 0.1
|
||||
ret.stoppingDecelRate = 0.3
|
||||
|
||||
if candidate in HONDA_BOSCH:
|
||||
ret.longitudinalActuatorDelay = 0.5 # s
|
||||
@@ -90,14 +95,32 @@ class CarInterface(CarInterfaceBase):
|
||||
ret.longitudinalTuning.kiBP = [0., 5., 35.]
|
||||
ret.longitudinalTuning.kiV = [1.2, 0.8, 0.5]
|
||||
|
||||
# Disable control if EPS mod detected
|
||||
eps_modified = False
|
||||
for fw in car_fw:
|
||||
if fw.ecu == "eps" and b"," in fw.fwVersion:
|
||||
ret.dashcamOnly = True
|
||||
eps_modified = True
|
||||
|
||||
if eps_modified:
|
||||
ret.flags |= HondaFlags.EPS_MODIFIED.value
|
||||
|
||||
if candidate == CAR.HONDA_CITY_7G:
|
||||
ret.vEgoStopping = 2.0
|
||||
ret.vEgoStarting = ret.vEgoStopping
|
||||
ret.stoppingDecelRate = 0.3
|
||||
|
||||
if candidate == CAR.HONDA_CIVIC:
|
||||
ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 2560], [0, 2560]]
|
||||
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[1.1], [0.33]]
|
||||
if eps_modified:
|
||||
# stock request input values: 0x0000, 0x00DE, 0x014D, 0x01EF, 0x0290, 0x0377, 0x0454, 0x0610, 0x06EE
|
||||
# stock request output values: 0x0000, 0x0917, 0x0DC5, 0x1017, 0x119F, 0x140B, 0x1680, 0x1680, 0x1680
|
||||
# modified request output values: 0x0000, 0x0917, 0x0DC5, 0x1017, 0x119F, 0x140B, 0x1680, 0x2880, 0x3180
|
||||
# stock filter output values: 0x009F, 0x0108, 0x0108, 0x0108, 0x0108, 0x0108, 0x0108, 0x0108, 0x0108
|
||||
# modified filter output values: 0x009F, 0x0108, 0x0108, 0x0108, 0x0108, 0x0108, 0x0108, 0x0400, 0x0480
|
||||
# note: max request allowed is 4096, but request is capped at 3840 in firmware, so modifications result in 2x max
|
||||
ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 2560, 8000], [0, 2560, 3840]]
|
||||
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.3], [0.1]]
|
||||
else:
|
||||
ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 2560], [0, 2560]]
|
||||
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[1.1], [0.33]]
|
||||
|
||||
elif candidate in (CAR.HONDA_CIVIC_BOSCH, CAR.HONDA_CIVIC_BOSCH_DIESEL):
|
||||
ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 4096], [0, 4096]] # TODO: determine if there is a dead zone at the top end
|
||||
@@ -112,7 +135,17 @@ class CarInterface(CarInterfaceBase):
|
||||
|
||||
elif candidate == CAR.HONDA_ACCORD:
|
||||
ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 4096], [0, 4096]] # TODO: determine if there is a dead zone at the top end
|
||||
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.6], [0.18]]
|
||||
if eps_modified:
|
||||
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.3], [0.09]]
|
||||
else:
|
||||
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.6], [0.18]]
|
||||
if ret.transmissionType == TransmissionType.manual:
|
||||
CarControllerParams.BOSCH_GAS_LOOKUP_BP = [-0.2, 2.0]
|
||||
|
||||
elif candidate == CAR.HONDA_ACCORD_11G:
|
||||
ret.steerActuatorDelay = 0.22
|
||||
ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 2560, 5200], [0, 2560, 12747]]
|
||||
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
|
||||
|
||||
elif candidate == CAR.ACURA_ILX:
|
||||
ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 3840], [0, 3840]] # TODO: determine if there is a dead zone at the top end
|
||||
@@ -124,8 +157,15 @@ class CarInterface(CarInterfaceBase):
|
||||
ret.wheelSpeedFactor = 1.025
|
||||
|
||||
elif candidate == CAR.HONDA_CRV_5G:
|
||||
ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 3840], [0, 3840]]
|
||||
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.64], [0.192]]
|
||||
if eps_modified:
|
||||
# stock request input values: 0x0000, 0x00DB, 0x01BB, 0x0296, 0x0377, 0x0454, 0x0532, 0x0610, 0x067F
|
||||
# stock request output values: 0x0000, 0x0500, 0x0A15, 0x0E6D, 0x1100, 0x1200, 0x129A, 0x134D, 0x1400
|
||||
# modified request output values: 0x0000, 0x0500, 0x0A15, 0x0E6D, 0x1100, 0x1200, 0x1ACD, 0x239A, 0x2800
|
||||
ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 2560, 10000], [0, 2560, 3840]]
|
||||
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.21], [0.07]]
|
||||
else:
|
||||
ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 3840], [0, 3840]]
|
||||
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.64], [0.192]]
|
||||
ret.wheelSpeedFactor = 1.025
|
||||
|
||||
elif candidate == CAR.HONDA_CRV_HYBRID:
|
||||
@@ -133,6 +173,13 @@ class CarInterface(CarInterfaceBase):
|
||||
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.6], [0.18]]
|
||||
ret.wheelSpeedFactor = 1.025
|
||||
|
||||
elif candidate == CAR.HONDA_CRV_6G:
|
||||
ret.steerActuatorDelay = 0.15
|
||||
ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 5100], [0, 5100]]
|
||||
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
|
||||
if ret.flags & HondaFlags.HYBRID:
|
||||
CarControllerParams.BOSCH_GAS_LOOKUP_BP = [-0.3, 2.0]
|
||||
|
||||
elif candidate == CAR.HONDA_FIT:
|
||||
ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 4096], [0, 4096]] # TODO: determine if there is a dead zone at the top end
|
||||
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.2], [0.05]]
|
||||
@@ -148,22 +195,52 @@ class CarInterface(CarInterfaceBase):
|
||||
ret.wheelSpeedFactor = 1.025
|
||||
else:
|
||||
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.8], [0.24]] # TODO: can probably use some tuning
|
||||
# The 3G HR-V settles more cleanly in lead follow when planner delay
|
||||
# better matches its stronger immediate longitudinal response.
|
||||
ret.longitudinalActuatorDelay = 0.4
|
||||
|
||||
elif candidate == CAR.HONDA_CLARITY:
|
||||
ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 2560], [0, 2560]]
|
||||
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.8], [0.24]]
|
||||
ret.stopAccel = 0.0
|
||||
|
||||
elif candidate == CAR.ACURA_RDX:
|
||||
ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 1000], [0, 1000]] # TODO: determine if there is a dead zone at the top end
|
||||
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.8], [0.24]]
|
||||
|
||||
elif candidate == CAR.ACURA_RDX_3G:
|
||||
ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 3840], [0, 3840]]
|
||||
ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 4095], [0, 4095]]
|
||||
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.2], [0.06]]
|
||||
CarControllerParams.BOSCH_GAS_LOOKUP_V = [0, 2200]
|
||||
|
||||
elif candidate == CAR.ACURA_RDX_3G_MMR:
|
||||
ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 3840], [0, 3840]]
|
||||
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
|
||||
CarControllerParams.BOSCH_GAS_LOOKUP_V = [0, 2000]
|
||||
if not ret.openpilotLongitudinalControl:
|
||||
ret.minSteerSpeed = 70. * CV.KPH_TO_MS
|
||||
|
||||
elif candidate == CAR.HONDA_ODYSSEY:
|
||||
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.28], [0.08]]
|
||||
ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 4096], [0, 4096]] # TODO: determine if there is a dead zone at the top end
|
||||
|
||||
elif candidate == CAR.HONDA_PILOT:
|
||||
elif candidate == CAR.HONDA_ODYSSEY_TWN:
|
||||
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.28], [0.08]]
|
||||
ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 32767], [0, 32767]]
|
||||
|
||||
elif candidate in (CAR.HONDA_PILOT, CAR.HONDA_PILOT_4G):
|
||||
ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 4096], [0, 4096]] # TODO: determine if there is a dead zone at the top end
|
||||
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.38], [0.11]]
|
||||
ret.lateralTuning.pid.kpBP, ret.lateralTuning.pid.kpV = [[0, 10], [0.05, 0.5]]
|
||||
ret.lateralTuning.pid.kiBP, ret.lateralTuning.pid.kiV = [[0, 10], [0.0125, 0.125]]
|
||||
|
||||
elif candidate == CAR.ACURA_MDX_4G:
|
||||
ret.steerActuatorDelay = 0.15
|
||||
ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 2560, 4209], [0, 2560, 9150]]
|
||||
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
|
||||
|
||||
elif candidate == CAR.ACURA_MDX_4G_MMR:
|
||||
ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 2560, 4920], [0, 2560, 12000]]
|
||||
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
|
||||
|
||||
elif candidate == CAR.HONDA_RIDGELINE:
|
||||
ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 4096], [0, 4096]] # TODO: determine if there is a dead zone at the top end
|
||||
@@ -173,7 +250,7 @@ class CarInterface(CarInterfaceBase):
|
||||
ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 4096], [0, 4096]] # TODO: determine if there is a dead zone at the top end
|
||||
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.6], [0.18]]
|
||||
|
||||
elif candidate == CAR.HONDA_E:
|
||||
elif candidate in (CAR.HONDA_E, CAR.HONDA_E_ADVANCE):
|
||||
ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 4096], [0, 4096]] # TODO: determine if there is a dead zone at the top end
|
||||
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.6], [0.18]] # TODO: can probably use some tuning
|
||||
|
||||
@@ -189,19 +266,48 @@ class CarInterface(CarInterfaceBase):
|
||||
# When using stock ACC, the radar intercepts and filters steering commands the EPS would otherwise accept
|
||||
ret.minSteerSpeed = 70. * CV.KPH_TO_MS
|
||||
|
||||
elif candidate == CAR.ACURA_TLX_2G_MMR:
|
||||
ret.steerActuatorDelay = 0.15
|
||||
ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 4096], [0, 4096]]
|
||||
ret.lateralTuning.pid.kpBP, ret.lateralTuning.pid.kpV = [[0, 10], [0.05, 0.5]]
|
||||
ret.lateralTuning.pid.kiBP, ret.lateralTuning.pid.kiV = [[0, 10], [0.0125, 0.125]]
|
||||
|
||||
elif candidate in (CAR.HONDA_FIT_4G,):
|
||||
ret.steerActuatorDelay = 0.15
|
||||
ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 4096], [0, 4096]]
|
||||
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
|
||||
|
||||
elif candidate == CAR.ACURA_INTEGRA:
|
||||
ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 4096], [0, 4096]]
|
||||
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.8], [0.24]]
|
||||
|
||||
elif candidate == CAR.ACURA_ADX:
|
||||
ret.steerActuatorDelay = 0.15
|
||||
ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 5000], [0, 5000]]
|
||||
ret.lateralTuning.pid.kpBP, ret.lateralTuning.pid.kpV = [[0, 10], [0.05, 0.5]]
|
||||
ret.lateralTuning.pid.kiBP, ret.lateralTuning.pid.kiV = [[0, 10], [0.0125, 0.125]]
|
||||
|
||||
elif candidate == CAR.HONDA_PASSPORT_4G:
|
||||
ret.steerActuatorDelay = 0.15
|
||||
ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 2560, 5120], [0, 2560, 12789]]
|
||||
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
|
||||
|
||||
else:
|
||||
ret.steerActuatorDelay = 0.15
|
||||
ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 2560], [0, 2560]]
|
||||
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
|
||||
|
||||
# These cars use alternate user brake msg (0x1BE)
|
||||
if 0x1BE in fingerprint[CAN.pt] and candidate in (CAR.HONDA_ACCORD, CAR.HONDA_HRV_3G, *HONDA_BOSCH_CANFD):
|
||||
if 0x1BE in fingerprint[CAN.pt] and candidate in (CAR.HONDA_ACCORD, CAR.HONDA_HRV_3G, CAR.ACURA_RDX_3G, CAR.ACURA_MDX_4G,
|
||||
CAR.ACURA_ADX, *HONDA_BOSCH_CANFD):
|
||||
ret.flags |= HondaFlags.BOSCH_ALT_BRAKE.value
|
||||
|
||||
if ret.flags & HondaFlags.BOSCH_ALT_BRAKE:
|
||||
ret.safetyConfigs[-1].safetyParam |= HondaSafetyFlags.ALT_BRAKE.value
|
||||
if candidate in HONDA_NIDEC_ALT_SCM_MESSAGES:
|
||||
ret.safetyConfigs[-1].safetyParam |= HondaSafetyFlags.NIDEC_ALT.value
|
||||
if ret.enableGasInterceptorDEPRECATED:
|
||||
ret.safetyConfigs[-1].safetyParam |= HondaSafetyFlags.GAS_INTERCEPTOR.value
|
||||
if ret.openpilotLongitudinalControl and candidate in HONDA_BOSCH:
|
||||
ret.safetyConfigs[-1].safetyParam |= HondaSafetyFlags.BOSCH_LONG.value
|
||||
if candidate in HONDA_BOSCH_RADARLESS:
|
||||
@@ -212,8 +318,24 @@ class CarInterface(CarInterfaceBase):
|
||||
# min speed to enable ACC. if car can do stop and go, then set enabling speed
|
||||
# to a negative value, so it won't matter. Otherwise, add 0.5 mph margin to not
|
||||
# conflict with PCM acc
|
||||
ret.autoResumeSng = candidate in (HONDA_BOSCH | {CAR.HONDA_CIVIC})
|
||||
ret.minEnableSpeed = -1. if ret.autoResumeSng else 25.51 * CV.MPH_TO_MS
|
||||
if candidate == CAR.HONDA_FIT_4G and not ret.openpilotLongitudinalControl:
|
||||
ret.autoResumeSng = False
|
||||
elif ret.transmissionType == TransmissionType.manual and not ret.openpilotLongitudinalControl:
|
||||
ret.autoResumeSng = False
|
||||
else:
|
||||
ret.autoResumeSng = candidate in (HONDA_BOSCH | {CAR.HONDA_CIVIC, CAR.HONDA_CLARITY}) or ret.enableGasInterceptorDEPRECATED
|
||||
|
||||
if ret.autoResumeSng:
|
||||
ret.minEnableSpeed = -1.
|
||||
elif candidate == CAR.HONDA_ODYSSEY_TWN:
|
||||
ret.minEnableSpeed = 19. * CV.MPH_TO_MS
|
||||
elif candidate == CAR.HONDA_FIT_4G:
|
||||
ret.minEnableSpeed = 30. * CV.KPH_TO_MS
|
||||
else:
|
||||
ret.minEnableSpeed = 25.51 * CV.MPH_TO_MS
|
||||
|
||||
if candidate == CAR.HONDA_PILOT_4G:
|
||||
CarControllerParams.BOSCH_GAS_LOOKUP_V = [0, 2200]
|
||||
|
||||
ret.steerLimitTimer = 0.8
|
||||
ret.radarDelay = 0.1
|
||||
|
||||
@@ -1,12 +1,44 @@
|
||||
import re
|
||||
from types import SimpleNamespace
|
||||
import pytest
|
||||
|
||||
from opendbc.car import structs
|
||||
from opendbc.car.structs import CarParams
|
||||
from opendbc.car import gen_empty_fingerprint
|
||||
from opendbc.car.honda.interface import CarInterface
|
||||
from opendbc.car.honda.carcontroller import (
|
||||
CarController,
|
||||
get_civic_bosch_modified_steering_pressed,
|
||||
get_civic_bosch_modified_torque_lpf_tau,
|
||||
get_honda_bosch_wind_brake_mps2,
|
||||
update_honda_bosch_live_learning,
|
||||
)
|
||||
from opendbc.car.honda.hondacan import create_lkas_hud
|
||||
from opendbc.car.honda.fingerprints import FW_VERSIONS
|
||||
from opendbc.car.honda.values import HONDA_BOSCH, HONDA_BOSCH_TJA_CONTROL
|
||||
from opendbc.car.honda.values import CAR, DBC, HONDA_BOSCH, HONDA_BOSCH_TJA_CONTROL, CarControllerParams, HondaFlags, HondaSafetyFlags, \
|
||||
HondaStarPilotFlags
|
||||
|
||||
HONDA_FW_VERSION_RE = br"[A-Z0-9]{5}-[A-Z0-9]{3}(-|,)[A-Z0-9]{4}(\x00){2}$"
|
||||
HONDA_FW_VERSION_RE = rb"[A-Z0-9]{5}-[A-Z0-9]{3}(-|,)[A-Z0-9]{4}(\x00){2}$"
|
||||
|
||||
|
||||
def get_test_toggles() -> SimpleNamespace:
|
||||
return SimpleNamespace(always_on_lateral_lkas=False, force_torque_controller=False, nnff=False, nnff_lite=False)
|
||||
|
||||
|
||||
class TestHondaFingerprint:
|
||||
def test_honda_lkas_hud_shows_lane_lines_when_lateral_only_is_active(self):
|
||||
class FakePacker:
|
||||
@staticmethod
|
||||
def make_can_msg(name, bus, values):
|
||||
return name, bus, values
|
||||
|
||||
CP = CarInterface.get_non_essential_params(CAR.HONDA_CIVIC_BOSCH)
|
||||
hud_control = SimpleNamespace(lanesVisible=False)
|
||||
|
||||
cmds = create_lkas_hud(FakePacker(), 0, CP, hud_control, True, True, False, False, {})
|
||||
|
||||
assert cmds[0][2]["SOLID_LANES"] is True
|
||||
|
||||
def test_fw_version_format(self):
|
||||
# Asserts all FW versions follow an expected format
|
||||
for fw_by_ecu in FW_VERSIONS.values():
|
||||
@@ -16,3 +48,230 @@ class TestHondaFingerprint:
|
||||
|
||||
def test_tja_bosch_only(self):
|
||||
assert set(HONDA_BOSCH_TJA_CONTROL).issubset(set(HONDA_BOSCH)), "Nidec car found in TJA control list"
|
||||
|
||||
def test_modified_civic_torque_lpf_tau_reacts_to_sign_change(self):
|
||||
assert get_civic_bosch_modified_torque_lpf_tau(0.7, -0.1, 25.0) == 0.10
|
||||
assert get_civic_bosch_modified_torque_lpf_tau(0.02, -0.01, 8.0) == 0.28
|
||||
assert get_civic_bosch_modified_torque_lpf_tau(0.02, 0.01, 12.0) == 0.28
|
||||
assert get_civic_bosch_modified_torque_lpf_tau(0.02, 0.01, 20.0) == 0.20
|
||||
assert get_civic_bosch_modified_torque_lpf_tau(0.02, 0.01, 25.0) == 0.16
|
||||
assert get_civic_bosch_modified_torque_lpf_tau(0.30, 0.0, 12.0) == 0.16
|
||||
assert get_civic_bosch_modified_torque_lpf_tau(0.30, 0.0, 20.0) == 0.13
|
||||
|
||||
def test_modified_civic_steering_pressed_filter_rejects_short_same_direction_spikes(self):
|
||||
filter_s, pressed = get_civic_bosch_modified_steering_pressed(True, 1500.0, 0.8, 0.01, False)
|
||||
assert not pressed
|
||||
assert filter_s > 0.01
|
||||
|
||||
filter_s = 0.79
|
||||
filter_s, pressed = get_civic_bosch_modified_steering_pressed(True, 1500.0, 0.8, filter_s, False)
|
||||
assert not pressed
|
||||
|
||||
filter_s = 0.80
|
||||
filter_s, pressed = get_civic_bosch_modified_steering_pressed(True, 1500.0, 0.8, filter_s, False)
|
||||
assert pressed
|
||||
|
||||
def test_modified_civic_steering_pressed_filter_allows_opposing_driver_torque_quickly(self):
|
||||
filter_s, pressed = get_civic_bosch_modified_steering_pressed(True, -1500.0, 0.8, 0.10, False)
|
||||
assert pressed
|
||||
|
||||
def test_honda_bosch_wind_brake_curve_matches_reference_points(self):
|
||||
assert get_honda_bosch_wind_brake_mps2(0.0) == pytest.approx(0.0)
|
||||
assert get_honda_bosch_wind_brake_mps2(22.4) == pytest.approx(0.136)
|
||||
assert get_honda_bosch_wind_brake_mps2(40.2) == pytest.approx(0.441)
|
||||
|
||||
def test_honda_bosch_live_learning_increases_factors_when_under_accelerating(self):
|
||||
gas_factor, wind_factor, wind_factor_before_brake = update_honda_bosch_live_learning(
|
||||
1.0,
|
||||
1.0,
|
||||
0.0,
|
||||
desired_accel=1.0,
|
||||
actual_accel=0.5,
|
||||
gas_pedal_force=1.2,
|
||||
wind_brake_mps2=0.136,
|
||||
brake_pressed=False,
|
||||
v_ego=22.4,
|
||||
)
|
||||
|
||||
assert gas_factor == pytest.approx(1.012)
|
||||
assert wind_factor == pytest.approx(1.000136)
|
||||
assert wind_factor_before_brake == pytest.approx(wind_factor)
|
||||
|
||||
def test_honda_bosch_live_learning_restores_wind_factor_while_braking(self):
|
||||
gas_factor, wind_factor, wind_factor_before_brake = update_honda_bosch_live_learning(
|
||||
1.4,
|
||||
1.1,
|
||||
1.3,
|
||||
desired_accel=-0.2,
|
||||
actual_accel=0.0,
|
||||
gas_pedal_force=-0.1,
|
||||
wind_brake_mps2=0.136,
|
||||
brake_pressed=True,
|
||||
v_ego=22.4,
|
||||
)
|
||||
|
||||
assert gas_factor == pytest.approx(1.4)
|
||||
assert wind_factor == pytest.approx(1.3)
|
||||
assert wind_factor_before_brake == pytest.approx(1.3)
|
||||
|
||||
def test_official_modified_eps_firmwares_restored(self):
|
||||
assert b'39990-TVA,A150\x00\x00' in FW_VERSIONS[CAR.HONDA_ACCORD][(CarParams.Ecu.eps, 0x18DA30F1, None)]
|
||||
assert b'39990-TBA,A030\x00\x00' in FW_VERSIONS[CAR.HONDA_CIVIC][(CarParams.Ecu.eps, 0x18DA30F1, None)]
|
||||
assert b'39990-TBA-C120\x00\x00' in FW_VERSIONS[CAR.HONDA_CIVIC_BOSCH][(CarParams.Ecu.eps, 0x18DA30F1, None)]
|
||||
assert b'39990-TGG,A020\x00\x00' in FW_VERSIONS[CAR.HONDA_CIVIC_BOSCH][(CarParams.Ecu.eps, 0x18DA30F1, None)]
|
||||
assert b'39990-TLA,A040\x00\x00' in FW_VERSIONS[CAR.HONDA_CRV_5G][(CarParams.Ecu.eps, 0x18DA30F1, None)]
|
||||
|
||||
def test_modified_eps_candidates_keep_support_and_restore_upstream_tunes(self):
|
||||
toggles = SimpleNamespace(force_torque_controller=False, nnff=False, nnff_lite=False)
|
||||
|
||||
civic_fw = [CarParams.CarFw(ecu=CarParams.Ecu.eps, fwVersion=b'39990-TBA,A030\x00\x00', address=0x18DA30F1, subAddress=0)]
|
||||
civic_cp = CarInterface.get_params(CAR.HONDA_CIVIC, gen_empty_fingerprint(), civic_fw, False, False, False, toggles)
|
||||
assert not civic_cp.dashcamOnly
|
||||
assert civic_cp.flags & HondaFlags.EPS_MODIFIED
|
||||
assert list(civic_cp.lateralParams.torqueBP) == [0, 2560, 8000]
|
||||
assert list(civic_cp.lateralParams.torqueV) == [0, 2560, 3840]
|
||||
assert list(civic_cp.lateralTuning.pid.kpV) == pytest.approx([0.3])
|
||||
assert list(civic_cp.lateralTuning.pid.kiV) == pytest.approx([0.1])
|
||||
|
||||
civic_bosch_fw = [CarParams.CarFw(ecu=CarParams.Ecu.eps, fwVersion=b'39990-TGG,A020\x00\x00', address=0x18DA30F1, subAddress=0)]
|
||||
civic_bosch_cp = CarInterface.get_params(CAR.HONDA_CIVIC_BOSCH, gen_empty_fingerprint(), civic_bosch_fw, False, False, False, toggles)
|
||||
assert not civic_bosch_cp.dashcamOnly
|
||||
assert civic_bosch_cp.flags & HondaFlags.EPS_MODIFIED
|
||||
|
||||
accord_fw = [CarParams.CarFw(ecu=CarParams.Ecu.eps, fwVersion=b'39990-TVA,A150\x00\x00', address=0x18DA30F1, subAddress=0)]
|
||||
accord_cp = CarInterface.get_params(CAR.HONDA_ACCORD, gen_empty_fingerprint(), accord_fw, False, False, False, toggles)
|
||||
assert not accord_cp.dashcamOnly
|
||||
assert accord_cp.flags & HondaFlags.EPS_MODIFIED
|
||||
assert list(accord_cp.lateralTuning.pid.kpV) == pytest.approx([0.3])
|
||||
assert list(accord_cp.lateralTuning.pid.kiV) == pytest.approx([0.09])
|
||||
|
||||
crv_fw = [CarParams.CarFw(ecu=CarParams.Ecu.eps, fwVersion=b'39990-TLA,A040\x00\x00', address=0x18DA30F1, subAddress=0)]
|
||||
crv_cp = CarInterface.get_params(CAR.HONDA_CRV_5G, gen_empty_fingerprint(), crv_fw, False, False, False, toggles)
|
||||
assert not crv_cp.dashcamOnly
|
||||
assert crv_cp.flags & HondaFlags.EPS_MODIFIED
|
||||
assert list(crv_cp.lateralParams.torqueBP) == [0, 2560, 10000]
|
||||
assert list(crv_cp.lateralParams.torqueV) == [0, 2560, 3840]
|
||||
assert list(crv_cp.lateralTuning.pid.kpV) == pytest.approx([0.21])
|
||||
assert list(crv_cp.lateralTuning.pid.kiV) == pytest.approx([0.07])
|
||||
|
||||
def test_modified_civic_bosch_keeps_official_support(self):
|
||||
toggles = SimpleNamespace(force_torque_controller=False, nnff=False, nnff_lite=False)
|
||||
car_fw = [CarParams.CarFw(ecu=CarParams.Ecu.eps, fwVersion=b'39990-TGG,A020\x00\x00', address=0x18DA30F1, subAddress=0)]
|
||||
|
||||
CP = CarInterface.get_params(CAR.HONDA_CIVIC_BOSCH, gen_empty_fingerprint(), car_fw, False, False, False, toggles)
|
||||
|
||||
assert not CP.dashcamOnly
|
||||
assert CP.flags & HondaFlags.EPS_MODIFIED
|
||||
assert CP.lateralTuning.which() == "torque"
|
||||
|
||||
def test_honda_clarity_supports_pid_and_torque_paths(self):
|
||||
pid_toggles = SimpleNamespace(force_torque_controller=False, nnff=False, nnff_lite=False)
|
||||
car_fw = [CarParams.CarFw(ecu=CarParams.Ecu.eps, fwVersion=b'39990-TRW,A020\x00\x00', address=0x18DA30F1, subAddress=0)]
|
||||
|
||||
pid_cp = CarInterface.get_params(CAR.HONDA_CLARITY, gen_empty_fingerprint(), car_fw, False, False, False, pid_toggles)
|
||||
|
||||
assert not pid_cp.dashcamOnly
|
||||
assert pid_cp.flags & HondaFlags.EPS_MODIFIED
|
||||
assert pid_cp.lateralTuning.which() == "pid"
|
||||
assert list(pid_cp.lateralParams.torqueBP) == [0, 2560]
|
||||
assert list(pid_cp.lateralParams.torqueV) == [0, 2560]
|
||||
assert list(pid_cp.lateralTuning.pid.kpV) == pytest.approx([0.8])
|
||||
assert list(pid_cp.lateralTuning.pid.kiV) == pytest.approx([0.24])
|
||||
assert pid_cp.autoResumeSng
|
||||
assert pid_cp.minEnableSpeed == pytest.approx(-1.0)
|
||||
assert pid_cp.stopAccel == pytest.approx(0.0)
|
||||
|
||||
torque_toggles = SimpleNamespace(force_torque_controller=True, nnff=False, nnff_lite=False)
|
||||
torque_cp = CarInterface.get_params(CAR.HONDA_CLARITY, gen_empty_fingerprint(), car_fw, False, False, False, torque_toggles)
|
||||
|
||||
assert torque_cp.lateralTuning.which() == "torque"
|
||||
|
||||
def test_canfd_bosch_alpha_long_is_available(self):
|
||||
toggles = get_test_toggles()
|
||||
|
||||
CP = CarInterface.get_params(CAR.HONDA_PILOT_4G, gen_empty_fingerprint(), [], True, False, False, toggles)
|
||||
|
||||
assert CP.alphaLongitudinalAvailable
|
||||
assert CP.openpilotLongitudinalControl
|
||||
assert CP.safetyConfigs[-1].safetyParam & HondaSafetyFlags.BOSCH_CANFD
|
||||
assert CP.safetyConfigs[-1].safetyParam & HondaSafetyFlags.BOSCH_LONG
|
||||
|
||||
def test_nidec_pedal_detection_enables_interceptor_path(self):
|
||||
toggles = get_test_toggles()
|
||||
fingerprint = gen_empty_fingerprint()
|
||||
fingerprint[0][0x201] = 6
|
||||
|
||||
CP = CarInterface.get_params(CAR.HONDA_CIVIC, fingerprint, [], False, False, False, toggles)
|
||||
accel_limits = CarInterface.get_pid_accel_limits(CP, current_speed=5.0, cruise_speed=12.0)
|
||||
|
||||
assert CP.enableGasInterceptorDEPRECATED
|
||||
assert not CP.pcmCruise
|
||||
assert accel_limits == (CarControllerParams.NIDEC_ACCEL_MIN, CarControllerParams.NIDEC_ACCEL_MAX)
|
||||
|
||||
def test_honda_camera_message_flag_uses_fingerprint_detection(self):
|
||||
toggles = get_test_toggles()
|
||||
fingerprint = gen_empty_fingerprint()
|
||||
fingerprint[0][0x35E] = 8
|
||||
|
||||
CP = CarInterface.get_params(CAR.HONDA_ACCORD, fingerprint, [], True, False, False, toggles)
|
||||
FPCP = CarInterface.get_starpilot_params(CAR.HONDA_ACCORD, fingerprint, [], CP, toggles)
|
||||
|
||||
assert FPCP.flags & HondaStarPilotFlags.HAS_CAMERA_MESSAGES
|
||||
|
||||
def test_honda_live_learning_params_reload(self, monkeypatch):
|
||||
toggles = get_test_toggles()
|
||||
|
||||
class FakeParams:
|
||||
def get_float(self, key, block=False, return_default=False, default=0.0):
|
||||
if key == "HondaGasFactorParams":
|
||||
return 1.25
|
||||
if key == "HondaWindFactorParams":
|
||||
return 0.85
|
||||
return default
|
||||
|
||||
monkeypatch.setattr("opendbc.car.honda.carcontroller.Params", lambda: FakeParams())
|
||||
|
||||
CP = CarInterface.get_params(CAR.HONDA_ACCORD, gen_empty_fingerprint(), [], True, False, False, toggles)
|
||||
controller = CarController(DBC[CP.carFingerprint], CP)
|
||||
|
||||
assert controller.bosch_gas_factor == pytest.approx(1.25)
|
||||
assert controller.bosch_wind_factor == pytest.approx(0.85)
|
||||
|
||||
def test_honda_bosch_controller_does_not_deepen_planner_braking(self, monkeypatch):
|
||||
toggles = get_test_toggles()
|
||||
CP = CarInterface.get_params(CAR.HONDA_HRV_3G, gen_empty_fingerprint(), [], True, False, False, toggles)
|
||||
controller = CarController(DBC[CP.carFingerprint], CP)
|
||||
|
||||
monkeypatch.setattr("opendbc.car.honda.carcontroller.hondacan.create_steering_control", lambda *args, **kwargs: (0, []))
|
||||
monkeypatch.setattr("opendbc.car.honda.carcontroller.hondacan.create_acc_commands", lambda *args, **kwargs: [])
|
||||
|
||||
CC = structs.CarControl.new_message()
|
||||
CC.enabled = True
|
||||
CC.longActive = True
|
||||
CC.latActive = False
|
||||
CC.cruiseControl.cancel = False
|
||||
CC.cruiseControl.resume = False
|
||||
CC.hudControl.speedVisible = False
|
||||
CC.hudControl.setSpeed = 0.0
|
||||
CC.hudControl.visualAlert = structs.CarControl.HUDControl.VisualAlert.none
|
||||
CC.actuators.accel = -0.3
|
||||
CC.actuators.torque = 0.0
|
||||
CC.actuators.longControlState = structs.CarControl.Actuators.LongControlState.pid
|
||||
|
||||
controller.frame = 2
|
||||
CS = SimpleNamespace(
|
||||
out=SimpleNamespace(vEgo=25.0, aEgo=1.5, steeringPressed=False, gasPressed=False, brakePressed=False),
|
||||
v_cruise_factor=1.0,
|
||||
)
|
||||
|
||||
new_actuators, _ = controller.update(CC.as_reader(), CS, 0, toggles)
|
||||
assert new_actuators.accel == pytest.approx(-0.3)
|
||||
|
||||
def test_honda_hrv_3g_uses_matched_longitudinal_delay(self):
|
||||
toggles = get_test_toggles()
|
||||
|
||||
hrv3g_cp = CarInterface.get_params(CAR.HONDA_HRV_3G, gen_empty_fingerprint(), [], True, False, False, toggles)
|
||||
accord_cp = CarInterface.get_params(CAR.HONDA_ACCORD, gen_empty_fingerprint(), [], True, False, False, toggles)
|
||||
|
||||
assert hrv3g_cp.longitudinalActuatorDelay == pytest.approx(0.4)
|
||||
assert accord_cp.longitudinalActuatorDelay == pytest.approx(0.5)
|
||||
|
||||
@@ -56,6 +56,7 @@ class HondaSafetyFlags(IntFlag):
|
||||
NIDEC_ALT = 4
|
||||
RADARLESS = 8
|
||||
BOSCH_CANFD = 16
|
||||
GAS_INTERCEPTOR = 32
|
||||
|
||||
|
||||
class HondaFlags(IntFlag):
|
||||
@@ -79,6 +80,7 @@ class HondaFlags(IntFlag):
|
||||
ALLOW_MANUAL_TRANS = 1024
|
||||
HYBRID = 2048
|
||||
BOSCH_TJA_CONTROL = 4096
|
||||
EPS_MODIFIED = 8192
|
||||
|
||||
|
||||
# Car button codes
|
||||
@@ -246,6 +248,12 @@ class CAR(Platforms):
|
||||
{Bus.pt: 'acura_rdx_2020_can_generated'},
|
||||
flags=HondaFlags.BOSCH_ALT_BRAKE,
|
||||
)
|
||||
ACURA_RDX_3G_MMR = HondaBoschPlatformConfig(
|
||||
[HondaCarDocs("Acura RDX 2022-26", "All", min_steer_speed=70. * CV.KPH_TO_MS)],
|
||||
CarSpecs(mass=4079 * CV.LB_TO_KG, wheelbase=2.75, centerToFrontRatio=0.41, steerRatio=16.2),
|
||||
{Bus.pt: 'acura_rdx_2020_can_generated'},
|
||||
flags=HondaFlags.BOSCH_ALT_BRAKE | HondaFlags.BOSCH_ALT_RADAR,
|
||||
)
|
||||
HONDA_INSIGHT = HondaBoschPlatformConfig(
|
||||
[HondaCarDocs("Honda Insight 2019-22", "All", min_steer_speed=3. * CV.MPH_TO_MS)],
|
||||
CarSpecs(mass=2987 * CV.LB_TO_KG, wheelbase=2.7, steerRatio=15.0, centerToFrontRatio=0.39, tireStiffnessFactor=0.82), # as spec
|
||||
@@ -256,6 +264,11 @@ class CAR(Platforms):
|
||||
CarSpecs(mass=3338.8 * CV.LB_TO_KG, wheelbase=2.5, centerToFrontRatio=0.5, steerRatio=16.71, tireStiffnessFactor=0.82),
|
||||
{Bus.pt: 'acura_rdx_2020_can_generated'},
|
||||
)
|
||||
HONDA_E_ADVANCE = HondaBoschPlatformConfig(
|
||||
[], # don't show in docs, base trim already in docs
|
||||
CarSpecs(mass=1527, wheelbase=2.5, centerToFrontRatio=0.5, steerRatio=16.71, tireStiffnessFactor=0.82),
|
||||
{Bus.pt: 'honda_e_advance_2020_can_generated'},
|
||||
)
|
||||
HONDA_PILOT_4G = HondaBoschCANFDPlatformConfig(
|
||||
[HondaCarDocs("Honda Pilot 2023-25", "All")],
|
||||
CarSpecs(mass=4660 * CV.LB_TO_KG, wheelbase=2.89, centerToFrontRatio=0.442, steerRatio=17.5),
|
||||
@@ -264,6 +277,12 @@ class CAR(Platforms):
|
||||
[HondaCarDocs("Honda Passport 2026", "All")],
|
||||
CarSpecs(mass=4620 * CV.LB_TO_KG, wheelbase=2.89, centerToFrontRatio=0.442, steerRatio=18.5),
|
||||
)
|
||||
ACURA_MDX_4G = HondaBoschPlatformConfig(
|
||||
[HondaCarDocs("Acura MDX 2022-24", "All", min_steer_speed=70. * CV.KPH_TO_MS)],
|
||||
CarSpecs(mass=4788 * CV.LB_TO_KG, wheelbase=2.89, steerRatio=15.8, centerToFrontRatio=0.428),
|
||||
{Bus.pt: 'honda_common_canfd_generated'},
|
||||
flags=HondaFlags.BOSCH_ALT_RADAR | HondaFlags.BOSCH_TJA_CONTROL,
|
||||
)
|
||||
# mid-model refresh
|
||||
ACURA_MDX_4G_MMR = HondaBoschCANFDPlatformConfig(
|
||||
[HondaCarDocs("Acura MDX 2025", "All except Type S")],
|
||||
@@ -281,6 +300,34 @@ class CAR(Platforms):
|
||||
{Bus.pt: 'honda_civic_hatchback_ex_2017_can_generated'},
|
||||
flags=HondaFlags.BOSCH_ALT_RADAR,
|
||||
)
|
||||
ACURA_TLX_2G_MMR = HondaBoschCANFDPlatformConfig(
|
||||
[HondaCarDocs("Acura TLX 2024-25", "All")],
|
||||
CarSpecs(mass=3990 * CV.LB_TO_KG, wheelbase=2.87, centerToFrontRatio=0.43, steerRatio=13.7),
|
||||
)
|
||||
HONDA_FIT_4G = HondaBoschPlatformConfig(
|
||||
[
|
||||
HondaCarDocs("Honda Fit (Taiwan) 2021", "All"),
|
||||
HondaCarDocs("Honda Fit (Taiwan) 2024-25", "All"),
|
||||
],
|
||||
CarSpecs(mass=1229, wheelbase=2.53, steerRatio=19.7, centerToFrontRatio=0.39, minSteerSpeed=23. * CV.KPH_TO_MS),
|
||||
{Bus.pt: 'honda_bosch_radarless_generated'},
|
||||
flags=HondaFlags.BOSCH_RADARLESS,
|
||||
)
|
||||
ACURA_INTEGRA = HondaBoschPlatformConfig(
|
||||
[
|
||||
HondaCarDocs("Acura Integra 2023-26", "All"),
|
||||
HondaCarDocs("Honda Prelude 2026", "All"),
|
||||
],
|
||||
CarSpecs(mass=3338.8 * CV.LB_TO_KG, wheelbase=2.5, centerToFrontRatio=0.5, steerRatio=16.71, tireStiffnessFactor=0.82),
|
||||
{Bus.pt: 'honda_bosch_radarless_generated'},
|
||||
flags=HondaFlags.BOSCH_RADARLESS,
|
||||
)
|
||||
ACURA_ADX = HondaBoschPlatformConfig(
|
||||
[HondaCarDocs("Acura ADX 2025-26", "All")],
|
||||
CarSpecs(mass=3578 * CV.LB_TO_KG, wheelbase=2.65, steerRatio=16.6, centerToFrontRatio=0.43),
|
||||
{Bus.pt: 'honda_bosch_radarless_generated'},
|
||||
flags=HondaFlags.BOSCH_RADARLESS,
|
||||
)
|
||||
|
||||
# Nidec Cars
|
||||
ACURA_ILX = HondaNidecPlatformConfig(
|
||||
@@ -304,6 +351,12 @@ class CAR(Platforms):
|
||||
radar_dbc_dict('honda_crv_touring_2016_can_generated'),
|
||||
flags=HondaFlags.NIDEC_ALT_SCM_MESSAGES | HondaFlags.HAS_ALL_DOOR_STATES,
|
||||
)
|
||||
HONDA_CRV_SA = HondaNidecPlatformConfig(
|
||||
[], # South Africa version of CRV Touring, don't show in docs
|
||||
HONDA_CRV.specs,
|
||||
radar_dbc_dict('acura_rdx_2018_can_generated'),
|
||||
flags=HondaFlags.NIDEC_ALT_SCM_MESSAGES | HondaFlags.HAS_ALL_DOOR_STATES,
|
||||
)
|
||||
HONDA_FIT = HondaNidecPlatformConfig(
|
||||
[HondaCarDocs("Honda Fit 2018-20", min_steer_speed=12. * CV.MPH_TO_MS)],
|
||||
CarSpecs(mass=2644 * CV.LB_TO_KG, wheelbase=2.53, steerRatio=13.06, centerToFrontRatio=0.39, tireStiffnessFactor=0.75),
|
||||
@@ -322,12 +375,27 @@ class CAR(Platforms):
|
||||
radar_dbc_dict('acura_ilx_2016_can_generated'),
|
||||
flags=HondaFlags.NIDEC_ALT_SCM_MESSAGES,
|
||||
)
|
||||
HONDA_CLARITY = HondaNidecPlatformConfig(
|
||||
[HondaCarDocs("Honda Clarity 2018-21", "All", min_steer_speed=3. * CV.MPH_TO_MS)],
|
||||
CarSpecs(mass=1838, wheelbase=2.75, centerToFrontRatio=0.4, steerRatio=16.5),
|
||||
radar_dbc_dict('honda_clarity_hybrid_2018_can_generated'),
|
||||
flags=HondaFlags.HAS_ALL_DOOR_STATES,
|
||||
)
|
||||
HONDA_ODYSSEY = HondaNidecPlatformConfig(
|
||||
[HondaCarDocs("Honda Odyssey 2018-20")],
|
||||
CarSpecs(mass=1900, wheelbase=3.0, steerRatio=14.35, centerToFrontRatio=0.41, tireStiffnessFactor=0.82),
|
||||
radar_dbc_dict('honda_odyssey_exl_2018_generated'),
|
||||
flags=HondaFlags.NIDEC_ALT_PCM_ACCEL | HondaFlags.HAS_ALL_DOOR_STATES,
|
||||
)
|
||||
HONDA_ODYSSEY_TWN = HondaNidecPlatformConfig(
|
||||
[
|
||||
HondaCarDocs("Honda Odyssey (Taiwan) 2018-19"),
|
||||
HondaCarDocs("Honda Odyssey (Singapore) 2021"),
|
||||
],
|
||||
CarSpecs(mass=1865, wheelbase=2.9, steerRatio=14.35, centerToFrontRatio=0.44, tireStiffnessFactor=0.82),
|
||||
radar_dbc_dict('honda_odyssey_twn_2018_generated'),
|
||||
flags=HondaFlags.NIDEC_ALT_SCM_MESSAGES,
|
||||
)
|
||||
ACURA_RDX = HondaNidecPlatformConfig(
|
||||
[HondaCarDocs("Acura RDX 2016-18", "AcuraWatch Plus or Advance Package", min_steer_speed=12. * CV.MPH_TO_MS)],
|
||||
CarSpecs(mass=3925 * CV.LB_TO_KG, wheelbase=2.68, steerRatio=15.0, centerToFrontRatio=0.38, tireStiffnessFactor=0.444), # as spec
|
||||
@@ -355,6 +423,43 @@ class CAR(Platforms):
|
||||
radar_dbc_dict('honda_civic_touring_2016_can_generated'),
|
||||
flags=HondaFlags.HAS_ALL_DOOR_STATES
|
||||
)
|
||||
HONDA_ACCORD_9G = HondaNidecPlatformConfig(
|
||||
[
|
||||
HondaCarDocs("Honda Accord 2016-17"),
|
||||
HondaCarDocs("Honda Accord Hybrid 2017", "All"),
|
||||
],
|
||||
CarSpecs(mass=3343 * CV.LB_TO_KG, wheelbase=2.78, steerRatio=17.5, centerToFrontRatio=0.37),
|
||||
radar_dbc_dict('honda_accord_2017_can_ext_generated'),
|
||||
flags=HondaFlags.NIDEC_ALT_SCM_MESSAGES | HondaFlags.HAS_ALL_DOOR_STATES,
|
||||
)
|
||||
ACURA_MDX_3G = HondaNidecPlatformConfig(
|
||||
[
|
||||
HondaCarDocs("Acura MDX 2014-16", "Advance Package"),
|
||||
HondaCarDocs("Acura MDX 2017-19", "All"),
|
||||
HondaCarDocs("Acura MDX Hybrid 2017-19", "All"),
|
||||
],
|
||||
CarSpecs(mass=4215 * CV.LB_TO_KG, wheelbase=2.82, steerRatio=16.8, centerToFrontRatio=0.428),
|
||||
radar_dbc_dict('acura_mdx_2017_can_ext_generated'),
|
||||
flags=HondaFlags.NIDEC_ALT_SCM_MESSAGES,
|
||||
)
|
||||
ACURA_MDX_3G_MMR = HondaNidecPlatformConfig(
|
||||
[
|
||||
HondaCarDocs("Acura MDX 2020", "All"),
|
||||
HondaCarDocs("Acura MDX Hybrid 2020", "All"),
|
||||
],
|
||||
CarSpecs(mass=4215 * CV.LB_TO_KG, wheelbase=2.82, steerRatio=16.8, centerToFrontRatio=0.428),
|
||||
radar_dbc_dict('acura_ilx_2016_can_generated'),
|
||||
flags=HondaFlags.NIDEC_ALT_SCM_MESSAGES,
|
||||
)
|
||||
ACURA_TLX_1G = HondaNidecPlatformConfig(
|
||||
[
|
||||
HondaCarDocs("Acura TLX 2015-17", "Advance Package"),
|
||||
HondaCarDocs("Acura TLX 2018-20", "All"),
|
||||
],
|
||||
CarSpecs(mass=3680 * CV.LB_TO_KG, wheelbase=2.78, steerRatio=17.0, centerToFrontRatio=0.40, tireStiffnessFactor=0.18),
|
||||
radar_dbc_dict('acura_mdx_2017_can_ext_generated'),
|
||||
flags=HondaFlags.NIDEC_ALT_SCM_MESSAGES | HondaFlags.HAS_ALL_DOOR_STATES,
|
||||
)
|
||||
|
||||
|
||||
HONDA_NIDEC_ALT_PCM_ACCEL = CAR.with_flags(HondaFlags.NIDEC_ALT_PCM_ACCEL)
|
||||
@@ -389,6 +494,11 @@ STEER_THRESHOLD = {
|
||||
CAR.HONDA_CRV_6G: 600,
|
||||
CAR.HONDA_CITY_7G: 600,
|
||||
CAR.HONDA_NBOX_2G: 600,
|
||||
CAR.HONDA_ODYSSEY_5G_MMR: 600,
|
||||
CAR.HONDA_ACCORD_9G: 30,
|
||||
CAR.ACURA_MDX_3G: 30,
|
||||
CAR.ACURA_MDX_3G_MMR: 30,
|
||||
CAR.ACURA_TLX_1G: 30,
|
||||
}
|
||||
|
||||
|
||||
@@ -433,9 +543,11 @@ FW_QUERY_CONFIG = FwQueryConfig(
|
||||
# Note that we still attempt to match with them when they are present
|
||||
# This is or'd with (ALL_ECUS - ESSENTIAL_ECUS) from fw_versions.py
|
||||
non_essential_ecus={
|
||||
Ecu.eps: [CAR.ACURA_RDX_3G, CAR.HONDA_ACCORD, CAR.HONDA_E, *HONDA_BOSCH_ALT_RADAR, *HONDA_BOSCH_RADARLESS, *HONDA_BOSCH_CANFD],
|
||||
Ecu.eps: [CAR.ACURA_RDX_3G, CAR.HONDA_ACCORD, CAR.HONDA_E, CAR.HONDA_E_ADVANCE, CAR.ACURA_MDX_4G, CAR.HONDA_CRV_SA,
|
||||
CAR.ACURA_MDX_3G, CAR.HONDA_ACCORD_9G, *HONDA_BOSCH_ALT_RADAR, *HONDA_BOSCH_RADARLESS, *HONDA_BOSCH_CANFD],
|
||||
Ecu.vsa: [CAR.ACURA_RDX_3G, CAR.HONDA_ACCORD, CAR.HONDA_CIVIC, CAR.HONDA_CIVIC_BOSCH, CAR.HONDA_CRV_5G, CAR.HONDA_CRV_HYBRID,
|
||||
CAR.HONDA_E, CAR.HONDA_INSIGHT, CAR.HONDA_NBOX_2G, *HONDA_BOSCH_ALT_RADAR, *HONDA_BOSCH_RADARLESS, *HONDA_BOSCH_CANFD],
|
||||
CAR.HONDA_E, CAR.HONDA_E_ADVANCE, CAR.HONDA_INSIGHT, CAR.HONDA_NBOX_2G, CAR.ACURA_MDX_4G, CAR.HONDA_ACCORD_9G,
|
||||
*HONDA_BOSCH_ALT_RADAR, *HONDA_BOSCH_RADARLESS, *HONDA_BOSCH_CANFD],
|
||||
},
|
||||
extra_ecus=[
|
||||
(Ecu.combinationMeter, 0x18da60f1, None),
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
from dataclasses import dataclass
|
||||
|
||||
import numpy as np
|
||||
from opendbc.can import CANPacker
|
||||
from opendbc.car import Bus, DT_CTRL, make_tester_present_msg, structs
|
||||
@@ -18,6 +20,179 @@ LongCtrlState = structs.CarControl.Actuators.LongControlState
|
||||
MAX_ANGLE = 85
|
||||
MAX_ANGLE_FRAMES = 89
|
||||
MAX_ANGLE_CONSECUTIVE_FRAMES = 2
|
||||
CANFD_BLINDSPOT_STATUS_STALE_NS = 200_000_000
|
||||
CANFD_CAMERA_LEAD_STALE_NS = 300_000_000
|
||||
CANFD_LEAD_MIN_DISTANCE = 0.1
|
||||
CANFD_FALLBACK_LEAD_DISTANCE = 20.0
|
||||
HYUNDAI_DASH_DISENGAGE_BLINK_TIME = 1.0
|
||||
HYUNDAI_CANFD_SCC_ACCEL_STEP = 5.0 / 50.0
|
||||
HYUNDAI_CANFD_SCC_DECEL_STEP = 12.5 / 50.0
|
||||
IONIQ_6_RESPONSE_MULTIPLIER = 1.2
|
||||
IONIQ_6_CANFD_SCC_ACCEL_STEP = (6.0 / 50.0) * IONIQ_6_RESPONSE_MULTIPLIER
|
||||
IONIQ_6_CANFD_SCC_DECEL_STEP = (15.0 / 50.0) * IONIQ_6_RESPONSE_MULTIPLIER
|
||||
GENESIS_G90_STOP_HOLD_SPEED_BP = [0.0, 0.03, 0.08, 0.16, 0.3, 0.5, 0.8, 1.2, 2.0, 3.0]
|
||||
GENESIS_G90_STOP_HOLD_ACCEL_V = [-0.10, -0.10, -0.12, -0.18, -0.30, -0.50, -0.75, -1.00, -1.40, -1.80]
|
||||
GENESIS_G90_STOP_HOLD_RELAX_SPEED_BP = [0.0, 0.08, 0.16, 0.3, 0.5, 0.8, 1.2, 2.0, 3.0]
|
||||
GENESIS_G90_STOP_HOLD_RELAX_STEP_V = [0.10, 0.10, 0.08, 0.06, 0.04, 0.035, 0.03, 0.022, 0.018]
|
||||
GENESIS_G90_RELEASE_SPEED_BP = [0.0, 0.3, 0.6]
|
||||
GENESIS_G90_RELEASE_ACCEL_STEP_V = [0.05, 0.07, 0.11]
|
||||
GENESIS_G90_RELEASE_DECEL_STEP_V = [0.16, 0.18, 0.18]
|
||||
GENESIS_G90_RELEASE_MAX_SPEED = 0.8
|
||||
IONIQ_6_LONG_MIN_JERK = 0.5 * IONIQ_6_RESPONSE_MULTIPLIER
|
||||
IONIQ_6_LONG_JERK_LIMIT = 4.8 * IONIQ_6_RESPONSE_MULTIPLIER
|
||||
IONIQ_6_LONG_LOOKAHEAD_JERK_BP = [2.0, 5.0, 20.0]
|
||||
IONIQ_6_LONG_LOOKAHEAD_JERK_V = [0.3 / IONIQ_6_RESPONSE_MULTIPLIER,
|
||||
0.45 / IONIQ_6_RESPONSE_MULTIPLIER,
|
||||
0.6 / IONIQ_6_RESPONSE_MULTIPLIER]
|
||||
IONIQ_6_DYNAMIC_LOWER_JERK_BP = [-2.0, -1.5, -1.0, -0.25, -0.1, -0.025, -0.01, -0.005]
|
||||
IONIQ_6_DYNAMIC_LOWER_JERK_V = [3.3, 1.5, 1.0, 0.8, 0.7, 0.65, 0.55, 0.5]
|
||||
IONIQ_6_LAUNCH_HOLD_SPEED_BP = [0.0, 0.6, 1.25, 2.5]
|
||||
IONIQ_6_LAUNCH_HOLD_SPEED_V = [0.75, 0.6, 0.4, 0.0]
|
||||
IONIQ_6_STOP_BRAKE_CAP_MAX_SPEED = 2.0
|
||||
IONIQ_6_STOP_BRAKE_CAP_SPEED_BP = [0.0, 0.08, 0.25, 0.6, 1.2, 2.0, 3.0]
|
||||
IONIQ_6_STOP_BRAKE_CAP_ACCEL_V = [-0.15, -0.16, -0.22, -0.42, -0.78, -1.15, -1.40]
|
||||
IONIQ_6_STOP_HOLD_JERK_BP = [0.0, 0.15, 0.6, 1.2, 2.0, 3.0]
|
||||
IONIQ_6_STOP_HOLD_JERK_V = [0.35, 0.40, 0.48, 0.65, 0.85, 1.10]
|
||||
IONIQ_6_STOP_RELEASE_JERK_BP = [0.0, 0.15, 0.5]
|
||||
IONIQ_6_STOP_RELEASE_JERK_V = [3.6 * IONIQ_6_RESPONSE_MULTIPLIER,
|
||||
4.2 * IONIQ_6_RESPONSE_MULTIPLIER,
|
||||
4.8 * IONIQ_6_RESPONSE_MULTIPLIER]
|
||||
REDNECK_BUTTON_COPIES = 2
|
||||
REDNECK_BUTTON_COPIES_TIME = 7
|
||||
REDNECK_BUTTON_COPIES_TIME_IMPERIAL = [REDNECK_BUTTON_COPIES_TIME + 3, 70]
|
||||
REDNECK_BUTTON_COPIES_TIME_METRIC = [REDNECK_BUTTON_COPIES_TIME, 40]
|
||||
|
||||
|
||||
@dataclass
|
||||
class Ioniq6LongitudinalTuningState:
|
||||
desired_accel: float = 0.0
|
||||
actual_accel: float = 0.0
|
||||
accel_last: float = 0.0
|
||||
jerk_upper: float = 0.0
|
||||
jerk_lower: float = 0.0
|
||||
launch_active: bool = False
|
||||
stopping: bool = False
|
||||
stopping_count: int = 0
|
||||
long_control_state_last: LongCtrlState = LongCtrlState.off
|
||||
|
||||
|
||||
@dataclass
|
||||
class GenesisG90LongitudinalTuningState:
|
||||
actual_accel: float = 0.0
|
||||
release_active: bool = False
|
||||
long_control_state_last: LongCtrlState = LongCtrlState.off
|
||||
|
||||
|
||||
def _jerk_limited_integrator(desired_accel: float, last_accel: float, jerk_upper: float, jerk_lower: float) -> float:
|
||||
step = (jerk_upper if desired_accel >= last_accel else jerk_lower) * DT_CTRL * 5.0
|
||||
return float(np.clip(desired_accel, last_accel - step, last_accel + step))
|
||||
|
||||
|
||||
def _calculate_ioniq_6_dynamic_lower_jerk(accel_error: float) -> float:
|
||||
if accel_error < 0.0:
|
||||
scaled_values = np.array(IONIQ_6_DYNAMIC_LOWER_JERK_V) * (IONIQ_6_LONG_JERK_LIMIT / IONIQ_6_DYNAMIC_LOWER_JERK_V[0])
|
||||
return float(np.interp(accel_error, IONIQ_6_DYNAMIC_LOWER_JERK_BP, scaled_values))
|
||||
return IONIQ_6_LONG_MIN_JERK
|
||||
|
||||
|
||||
def update_ioniq_6_longitudinal_tuning(state: Ioniq6LongitudinalTuningState, accel_cmd: float, v_ego: float, a_ego: float,
|
||||
long_control_state: LongCtrlState, long_active: bool) -> Ioniq6LongitudinalTuningState:
|
||||
starting = long_control_state == LongCtrlState.starting
|
||||
stopping = long_control_state == LongCtrlState.stopping
|
||||
restart_from_stop = state.long_control_state_last in (LongCtrlState.stopping, LongCtrlState.starting) and \
|
||||
long_control_state in (LongCtrlState.starting, LongCtrlState.pid) and accel_cmd > 0.0 and v_ego < 0.5
|
||||
|
||||
state.stopping = long_active and stopping
|
||||
state.stopping_count = state.stopping_count + 1 if state.stopping else 0
|
||||
|
||||
if not long_active:
|
||||
state.desired_accel = 0.0
|
||||
state.actual_accel = 0.0
|
||||
state.accel_last = 0.0
|
||||
state.jerk_upper = 0.0
|
||||
state.jerk_lower = 0.0
|
||||
state.launch_active = False
|
||||
state.long_control_state_last = long_control_state
|
||||
return state
|
||||
|
||||
if accel_cmd <= 0.0 or v_ego >= IONIQ_6_LAUNCH_HOLD_SPEED_BP[-1]:
|
||||
state.launch_active = False
|
||||
elif starting or (state.launch_active and v_ego < IONIQ_6_LAUNCH_HOLD_SPEED_BP[-1]) or \
|
||||
(state.long_control_state_last == LongCtrlState.starting and long_control_state == LongCtrlState.pid and v_ego < IONIQ_6_LAUNCH_HOLD_SPEED_BP[-1]):
|
||||
state.launch_active = True
|
||||
|
||||
upper_speed_limit = float(np.interp(v_ego, [0.0, 5.0, 20.0], [2.0, 3.0, 2.0])) * IONIQ_6_RESPONSE_MULTIPLIER if long_control_state == LongCtrlState.pid else IONIQ_6_LONG_MIN_JERK
|
||||
lower_speed_limit = float(np.interp(v_ego, [0.0, 5.0, 20.0], [5.0, 3.5, 3.0])) * IONIQ_6_RESPONSE_MULTIPLIER
|
||||
|
||||
future_t_upper = float(np.interp(v_ego, IONIQ_6_LONG_LOOKAHEAD_JERK_BP, IONIQ_6_LONG_LOOKAHEAD_JERK_V))
|
||||
future_t_lower = float(np.interp(v_ego, IONIQ_6_LONG_LOOKAHEAD_JERK_BP, IONIQ_6_LONG_LOOKAHEAD_JERK_V))
|
||||
|
||||
accel_error = accel_cmd - state.accel_last
|
||||
j_ego_upper = float(np.clip(accel_error / future_t_upper, -IONIQ_6_LONG_JERK_LIMIT, IONIQ_6_LONG_JERK_LIMIT))
|
||||
j_ego_lower = float(np.clip(accel_error / future_t_lower, -IONIQ_6_LONG_JERK_LIMIT, IONIQ_6_LONG_JERK_LIMIT))
|
||||
desired_jerk_upper = min(max(j_ego_upper, IONIQ_6_LONG_MIN_JERK), upper_speed_limit)
|
||||
|
||||
dynamic_accel_error = a_ego - state.accel_last
|
||||
dynamic_lower_jerk = _calculate_ioniq_6_dynamic_lower_jerk(dynamic_accel_error)
|
||||
state.jerk_upper = desired_jerk_upper
|
||||
state.jerk_lower = min(dynamic_lower_jerk, lower_speed_limit)
|
||||
|
||||
if state.stopping:
|
||||
if v_ego <= IONIQ_6_STOP_BRAKE_CAP_MAX_SPEED:
|
||||
stop_brake_cap = float(np.interp(v_ego, IONIQ_6_STOP_BRAKE_CAP_SPEED_BP, IONIQ_6_STOP_BRAKE_CAP_ACCEL_V))
|
||||
state.desired_accel = min(0.0, max(accel_cmd, stop_brake_cap))
|
||||
state.jerk_upper = min(state.jerk_upper, float(np.interp(v_ego, IONIQ_6_STOP_HOLD_JERK_BP, IONIQ_6_STOP_HOLD_JERK_V)) * IONIQ_6_RESPONSE_MULTIPLIER)
|
||||
else:
|
||||
state.desired_accel = float(np.clip(accel_cmd, CarControllerParams.ACCEL_MIN, 0.0))
|
||||
else:
|
||||
state.desired_accel = float(np.clip(accel_cmd, CarControllerParams.ACCEL_MIN, CarControllerParams.ACCEL_MAX))
|
||||
if state.launch_active:
|
||||
state.desired_accel = max(state.desired_accel, float(np.interp(v_ego, IONIQ_6_LAUNCH_HOLD_SPEED_BP, IONIQ_6_LAUNCH_HOLD_SPEED_V)))
|
||||
state.jerk_upper = max(state.jerk_upper, float(np.interp(v_ego, [0.0, 2.5], [4.8, 3.2])) * IONIQ_6_RESPONSE_MULTIPLIER)
|
||||
state.jerk_lower = max(state.jerk_lower, 1.0)
|
||||
if restart_from_stop:
|
||||
state.jerk_upper = min(state.jerk_upper, float(np.interp(v_ego, IONIQ_6_STOP_RELEASE_JERK_BP, IONIQ_6_STOP_RELEASE_JERK_V)))
|
||||
|
||||
state.actual_accel = _jerk_limited_integrator(state.desired_accel, state.accel_last, state.jerk_upper, state.jerk_lower)
|
||||
state.accel_last = state.actual_accel
|
||||
state.long_control_state_last = long_control_state
|
||||
return state
|
||||
|
||||
|
||||
def update_genesis_g90_longitudinal_tuning(state: GenesisG90LongitudinalTuningState, accel_cmd: float, v_ego: float,
|
||||
long_control_state: LongCtrlState, long_active: bool) -> GenesisG90LongitudinalTuningState:
|
||||
if not long_active:
|
||||
state.actual_accel = 0.0
|
||||
state.release_active = False
|
||||
state.long_control_state_last = long_control_state
|
||||
return state
|
||||
|
||||
stopping = long_control_state == LongCtrlState.stopping
|
||||
if stopping and v_ego <= GENESIS_G90_STOP_HOLD_SPEED_BP[-1]:
|
||||
state.release_active = False
|
||||
stop_brake_cap = float(np.interp(v_ego, GENESIS_G90_STOP_HOLD_SPEED_BP, GENESIS_G90_STOP_HOLD_ACCEL_V))
|
||||
target_hold = min(0.0, max(accel_cmd, stop_brake_cap))
|
||||
if state.actual_accel < target_hold:
|
||||
relax_step = float(np.interp(v_ego, GENESIS_G90_STOP_HOLD_RELAX_SPEED_BP, GENESIS_G90_STOP_HOLD_RELAX_STEP_V))
|
||||
state.actual_accel = min(state.actual_accel + relax_step, target_hold)
|
||||
else:
|
||||
state.actual_accel = target_hold
|
||||
else:
|
||||
if state.long_control_state_last == LongCtrlState.stopping and long_control_state == LongCtrlState.pid and \
|
||||
accel_cmd > 0.0 and v_ego < GENESIS_G90_RELEASE_MAX_SPEED:
|
||||
state.release_active = True
|
||||
|
||||
if state.release_active:
|
||||
accel_step = float(np.interp(v_ego, GENESIS_G90_RELEASE_SPEED_BP, GENESIS_G90_RELEASE_ACCEL_STEP_V))
|
||||
decel_step = float(np.interp(v_ego, GENESIS_G90_RELEASE_SPEED_BP, GENESIS_G90_RELEASE_DECEL_STEP_V))
|
||||
state.actual_accel = float(np.clip(accel_cmd, state.actual_accel - decel_step, state.actual_accel + accel_step))
|
||||
if v_ego >= GENESIS_G90_RELEASE_MAX_SPEED or accel_cmd <= 0.0 or state.actual_accel >= accel_cmd - 1e-3:
|
||||
state.release_active = False
|
||||
else:
|
||||
state.actual_accel = accel_cmd
|
||||
|
||||
state.long_control_state_last = long_control_state
|
||||
return state
|
||||
|
||||
|
||||
def process_hud_alert(enabled, fingerprint, hud_control):
|
||||
@@ -58,14 +233,98 @@ class CarController(CarControllerBase):
|
||||
self.apply_angle_last = 0.0
|
||||
self.car_fingerprint = CP.carFingerprint
|
||||
self.last_button_frame = 0
|
||||
self.redneck_button_frame = 0
|
||||
self.ecu_disable_failed = False
|
||||
self._ecu_disable_checked = False
|
||||
self._params = Params()
|
||||
self.long_active_ecu = self.CP.openpilotLongitudinalControl
|
||||
self._ioniq_6_lane_change_ui_side = None
|
||||
self._ioniq_6_lane_change_ui_frames = 0
|
||||
self._ioniq_6_long_tuning = Ioniq6LongitudinalTuningState()
|
||||
self._genesis_g90_long_tuning = GenesisG90LongitudinalTuningState()
|
||||
self._dash_lat_disengage_blink_frame = 0
|
||||
self._dash_lat_disengage_init = False
|
||||
self._dash_prev_lat_active = False
|
||||
|
||||
def _update_dash_icon_state(self, CC):
|
||||
if CC.latActive:
|
||||
self._dash_lat_disengage_init = False
|
||||
elif self._dash_prev_lat_active:
|
||||
self._dash_lat_disengage_init = True
|
||||
|
||||
if not self._dash_lat_disengage_init:
|
||||
self._dash_lat_disengage_blink_frame = self.frame
|
||||
|
||||
disengaging = self._dash_lat_disengage_init and \
|
||||
(self.frame - self._dash_lat_disengage_blink_frame) * DT_CTRL < HYUNDAI_DASH_DISENGAGE_BLINK_TIME
|
||||
self._dash_prev_lat_active = CC.latActive
|
||||
lat_or_enabled = CC.enabled or CC.latActive
|
||||
lka_icon = 2 if lat_or_enabled else 3 if disengaging else 1
|
||||
lfa_icon = 2 if lat_or_enabled else 3 if disengaging else 0
|
||||
|
||||
return lka_icon, lfa_icon
|
||||
|
||||
def _get_canfd_scc_lead_state(self, CC, CS, now_nanos):
|
||||
openpilot_lead_visible = bool(getattr(CS, "openpilot_lead_visible", False) or CC.hudControl.leadVisible)
|
||||
openpilot_lead_distance = float(np.clip(getattr(CS, "openpilot_lead_distance", 0.0), 0.0, 204.7))
|
||||
openpilot_lead_rel_speed = float(np.clip(getattr(CS, "openpilot_lead_rel_speed", 0.0), -16.4, 34.7))
|
||||
stock_camera_lead_fresh = now_nanos - getattr(CS, "stock_camera_lead_ts", 0) <= CANFD_CAMERA_LEAD_STALE_NS
|
||||
stock_camera_lead_visible = stock_camera_lead_fresh and getattr(CS, "stock_camera_lead_visible", False)
|
||||
|
||||
if openpilot_lead_visible and openpilot_lead_distance > CANFD_LEAD_MIN_DISTANCE:
|
||||
return True, openpilot_lead_distance, openpilot_lead_rel_speed
|
||||
if stock_camera_lead_visible:
|
||||
lead_distance = float(np.clip(getattr(CS, "stock_camera_lead_distance", 0.0), 0.0, 204.7))
|
||||
lead_rel_speed = float(np.clip(getattr(CS, "stock_camera_lead_rel_speed", 0.0), -16.4, 34.7))
|
||||
return True, lead_distance, lead_rel_speed
|
||||
if openpilot_lead_visible:
|
||||
return True, CANFD_FALLBACK_LEAD_DISTANCE, 0.0
|
||||
|
||||
return False, 0.0, 0.0
|
||||
|
||||
@staticmethod
|
||||
def _get_redneck_button(CS):
|
||||
return {
|
||||
1: Buttons.RES_ACCEL,
|
||||
2: Buttons.SET_DECEL,
|
||||
}.get(getattr(CS, "redneck_send_button", 0), Buttons.NONE)
|
||||
|
||||
def _create_can_redneck_button_messages(self, CS):
|
||||
send_button = self._get_redneck_button(CS)
|
||||
if send_button == Buttons.NONE or (self.frame - self.last_button_frame) * DT_CTRL <= 0.1:
|
||||
return []
|
||||
|
||||
copies_xp = REDNECK_BUTTON_COPIES_TIME_METRIC if CS.is_metric else REDNECK_BUTTON_COPIES_TIME_IMPERIAL
|
||||
copies = int(np.interp(REDNECK_BUTTON_COPIES_TIME, copies_xp, [1, REDNECK_BUTTON_COPIES]))
|
||||
can_sends = [hyundaican.create_clu11(self.packer, self.frame, CS.clu11, send_button, self.CP)] * copies
|
||||
|
||||
if (self.frame - self.last_button_frame) * DT_CTRL >= 0.15:
|
||||
self.last_button_frame = self.frame
|
||||
|
||||
return can_sends
|
||||
|
||||
def _create_canfd_redneck_button_messages(self, CS):
|
||||
send_button = self._get_redneck_button(CS)
|
||||
if send_button == Buttons.NONE or self.CP.flags & HyundaiFlags.CANFD_ALT_BUTTONS or \
|
||||
(self.frame - self.last_button_frame) * DT_CTRL <= 0.2:
|
||||
return []
|
||||
|
||||
self.redneck_button_frame += 1
|
||||
button_counter_offset = [1, 1, 0, None][self.redneck_button_frame % 4]
|
||||
if button_counter_offset is None:
|
||||
return []
|
||||
|
||||
can_sends = [
|
||||
hyundaicanfd.create_buttons(self.packer, self.CP, self.CAN, (CS.buttons_counter + button_counter_offset) % 0xF, send_button)
|
||||
for _ in range(20)
|
||||
]
|
||||
self.last_button_frame = self.frame
|
||||
return can_sends
|
||||
|
||||
def update(self, CC, CS, now_nanos, starpilot_toggles):
|
||||
actuators = CC.actuators
|
||||
hud_control = CC.hudControl
|
||||
lka_icon, lfa_icon = self._update_dash_icon_state(CC)
|
||||
|
||||
self.params = CarControllerParams(self.CP, CS.out.vEgoRaw)
|
||||
apply_angle = CS.out.steeringAngleDeg
|
||||
@@ -86,6 +345,12 @@ class CarController(CarControllerBase):
|
||||
|
||||
apply_steer_req = CC.latActive and apply_torque > 0.0
|
||||
torque_fault = False
|
||||
|
||||
if apply_angle is None:
|
||||
apply_torque = 0.0
|
||||
apply_angle = CS.out.steeringAngleDeg
|
||||
apply_steer_req = False
|
||||
|
||||
self.apply_angle_last = apply_angle
|
||||
else:
|
||||
# steering torque
|
||||
@@ -107,7 +372,8 @@ class CarController(CarControllerBase):
|
||||
self.apply_torque_last = apply_torque
|
||||
|
||||
# accel + longitudinal
|
||||
accel = float(np.clip(actuators.accel, CarControllerParams.ACCEL_MIN, CarControllerParams.ACCEL_MAX))
|
||||
accel_cmd = float(np.clip(actuators.accel, CarControllerParams.ACCEL_MIN, CarControllerParams.ACCEL_MAX))
|
||||
accel = accel_cmd
|
||||
stopping = actuators.longControlState == LongCtrlState.stopping
|
||||
set_speed_in_units = hud_control.setSpeed * (CV.MS_TO_KPH if CS.is_metric else CV.MS_TO_MPH)
|
||||
|
||||
@@ -122,12 +388,46 @@ class CarController(CarControllerBase):
|
||||
# longitudinal messages - stock ECU is still active and these would conflict
|
||||
self.long_active_ecu = self.CP.openpilotLongitudinalControl and not self.ecu_disable_failed
|
||||
|
||||
use_ioniq_6_dynamic_long_tuning = self.CP.carFingerprint == CAR.HYUNDAI_IONIQ_6 and self.long_active_ecu and \
|
||||
actuators.longControlState in (LongCtrlState.starting, LongCtrlState.pid, LongCtrlState.stopping)
|
||||
if use_ioniq_6_dynamic_long_tuning and self.frame % 5 == 0:
|
||||
self._ioniq_6_long_tuning = update_ioniq_6_longitudinal_tuning(self._ioniq_6_long_tuning, accel_cmd,
|
||||
CS.out.vEgo, CS.out.aEgo,
|
||||
actuators.longControlState, self.long_active_ecu)
|
||||
use_ioniq_6_smoothed_accel = use_ioniq_6_dynamic_long_tuning and (
|
||||
accel_cmd >= self._ioniq_6_long_tuning.actual_accel or
|
||||
self._ioniq_6_long_tuning.launch_active or
|
||||
self._ioniq_6_long_tuning.stopping
|
||||
)
|
||||
if self.CP.carFingerprint == CAR.HYUNDAI_IONIQ_6 and self.long_active_ecu:
|
||||
if use_ioniq_6_smoothed_accel:
|
||||
accel = self._ioniq_6_long_tuning.actual_accel
|
||||
stopping = self._ioniq_6_long_tuning.stopping
|
||||
elif use_ioniq_6_dynamic_long_tuning:
|
||||
accel = float(np.clip(accel_cmd,
|
||||
self.accel_last - IONIQ_6_CANFD_SCC_DECEL_STEP,
|
||||
self.accel_last + IONIQ_6_CANFD_SCC_ACCEL_STEP))
|
||||
self._ioniq_6_long_tuning.desired_accel = accel_cmd
|
||||
self._ioniq_6_long_tuning.actual_accel = accel
|
||||
self._ioniq_6_long_tuning.accel_last = accel
|
||||
self._ioniq_6_long_tuning.jerk_upper = 3.0
|
||||
self._ioniq_6_long_tuning.jerk_lower = 5.0 if CC.enabled else 1.0
|
||||
self._ioniq_6_long_tuning.launch_active = False
|
||||
self._ioniq_6_long_tuning.stopping = stopping
|
||||
self._ioniq_6_long_tuning.long_control_state_last = actuators.longControlState
|
||||
|
||||
if self.CP.carFingerprint == CAR.GENESIS_G90 and self.long_active_ecu:
|
||||
self._genesis_g90_long_tuning = update_genesis_g90_longitudinal_tuning(self._genesis_g90_long_tuning, accel_cmd,
|
||||
CS.out.vEgo, actuators.longControlState,
|
||||
self.long_active_ecu)
|
||||
accel = self._genesis_g90_long_tuning.actual_accel
|
||||
|
||||
# *** common hyundai stuff ***
|
||||
|
||||
# tester present - w/ no response (keeps relevant ECU disabled)
|
||||
if self.frame % 100 == 0 and not (self.CP.flags & HyundaiFlags.CANFD_CAMERA_SCC) and self.long_active_ecu:
|
||||
# for longitudinal control, either radar or ADAS driving ECU
|
||||
addr, bus = 0x7d0, self.CAN.ECAN if self.CP.flags & HyundaiFlags.CANFD else 0
|
||||
addr, bus = 0x7d0, self.CAN.ECAN if self.CP.flags & (HyundaiFlags.CANFD | HyundaiFlags.CAN_CANFD_BLENDED) else 0
|
||||
if self.CP.flags & HyundaiFlags.CANFD_LKA_STEERING.value:
|
||||
addr, bus = 0x730, self.CAN.ECAN
|
||||
can_sends.append(make_tester_present_msg(addr, bus, suppress_response=True))
|
||||
@@ -138,11 +438,11 @@ class CarController(CarControllerBase):
|
||||
|
||||
# *** CAN/CAN FD specific ***
|
||||
if self.CP.flags & HyundaiFlags.CANFD:
|
||||
can_sends.extend(self.create_canfd_msgs(apply_steer_req, apply_torque, apply_angle, set_speed_in_units, accel,
|
||||
stopping, hud_control, CS, CC))
|
||||
can_sends.extend(self.create_canfd_msgs(now_nanos, apply_steer_req, apply_torque, apply_angle, set_speed_in_units, accel,
|
||||
stopping, hud_control, CS, CC, starpilot_toggles, lka_icon, lfa_icon))
|
||||
else:
|
||||
can_sends.extend(self.create_can_msgs(apply_steer_req, apply_torque, torque_fault, set_speed_in_units, accel,
|
||||
stopping, hud_control, actuators, CS, CC))
|
||||
stopping, hud_control, actuators, CS, CC, lfa_icon))
|
||||
|
||||
new_actuators = actuators.as_builder()
|
||||
if self.CP.flags & HyundaiFlags.CANFD_ANGLE_STEERING:
|
||||
@@ -157,17 +457,24 @@ class CarController(CarControllerBase):
|
||||
self.frame += 1
|
||||
return new_actuators, can_sends
|
||||
|
||||
def create_can_msgs(self, apply_steer_req, apply_torque, torque_fault, set_speed_in_units, accel, stopping, hud_control, actuators, CS, CC):
|
||||
def create_can_msgs(self, apply_steer_req, apply_torque, torque_fault, set_speed_in_units, accel, stopping, hud_control, actuators, CS, CC, lfa_icon):
|
||||
can_sends = []
|
||||
can_canfd_blended = bool(self.CP.flags & HyundaiFlags.CAN_CANFD_BLENDED)
|
||||
|
||||
# HUD messages
|
||||
sys_warning, sys_state, left_lane_warning, right_lane_warning = process_hud_alert(CC.enabled, self.car_fingerprint,
|
||||
hud_control)
|
||||
|
||||
can_sends.append(hyundaican.create_lkas11(self.packer, self.frame, self.CP, apply_torque, apply_steer_req,
|
||||
torque_fault, CS.lkas11, sys_warning, sys_state, CC.enabled,
|
||||
hud_control.leftLaneVisible, hud_control.rightLaneVisible,
|
||||
left_lane_warning, right_lane_warning))
|
||||
if can_canfd_blended:
|
||||
can_sends.extend(hyundaican.create_lkas11_can_canfd_blended(self.packer, self.frame, self.CP, apply_torque, apply_steer_req,
|
||||
torque_fault, CS.lkas11, sys_warning, sys_state, CC.enabled,
|
||||
hud_control.leftLaneVisible, hud_control.rightLaneVisible,
|
||||
left_lane_warning, right_lane_warning, CS.msg_364))
|
||||
else:
|
||||
can_sends.append(hyundaican.create_lkas11(self.packer, self.frame, self.CP, apply_torque, apply_steer_req,
|
||||
torque_fault, CS.lkas11, sys_warning, sys_state, CC.enabled,
|
||||
hud_control.leftLaneVisible, hud_control.rightLaneVisible,
|
||||
left_lane_warning, right_lane_warning))
|
||||
|
||||
# Button messages
|
||||
if not self.long_active_ecu:
|
||||
@@ -180,38 +487,57 @@ class CarController(CarControllerBase):
|
||||
can_sends.extend([hyundaican.create_clu11(self.packer, self.frame, CS.clu11, Buttons.RES_ACCEL, self.CP)] * 25)
|
||||
if (self.frame - self.last_button_frame) * DT_CTRL >= 0.15:
|
||||
self.last_button_frame = self.frame
|
||||
else:
|
||||
can_sends.extend(self._create_can_redneck_button_messages(CS))
|
||||
|
||||
if self.long_active_ecu and can_canfd_blended:
|
||||
can_sends.extend(hyundaican.create_radar_aux_messages(self.packer, self.CAN, self.frame))
|
||||
|
||||
if self.frame % 2 == 0 and self.long_active_ecu:
|
||||
# TODO: unclear if this is needed
|
||||
jerk = 3.0 if actuators.longControlState == LongCtrlState.pid else 1.0
|
||||
use_fca = self.CP.flags & HyundaiFlags.USE_FCA.value
|
||||
can_sends.extend(hyundaican.create_acc_commands(self.packer, CC.enabled, accel, jerk, int(self.frame / 2),
|
||||
hud_control, set_speed_in_units, stopping,
|
||||
CC.cruiseControl.override, use_fca, self.CP))
|
||||
if can_canfd_blended:
|
||||
can_sends.extend(hyundaican.create_acc_commands_can_canfd_blended(self.packer, CC.enabled, accel, jerk,
|
||||
int(self.frame / 2), hud_control,
|
||||
set_speed_in_units, stopping,
|
||||
CC.cruiseControl.override, use_fca, self.CP))
|
||||
else:
|
||||
can_sends.extend(hyundaican.create_acc_commands(self.packer, CC.enabled, accel, jerk, int(self.frame / 2),
|
||||
hud_control, set_speed_in_units, stopping,
|
||||
CC.cruiseControl.override, use_fca, self.CP))
|
||||
|
||||
# 20 Hz LFA MFA message
|
||||
if self.frame % 5 == 0 and self.CP.flags & HyundaiFlags.SEND_LFA.value:
|
||||
can_sends.append(hyundaican.create_lfahda_mfc(self.packer, CC.enabled))
|
||||
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:
|
||||
if self.frame % 20 == 0 and self.long_active_ecu and not can_canfd_blended:
|
||||
can_sends.extend(hyundaican.create_acc_opt(self.packer, self.CP))
|
||||
|
||||
# 2 Hz front radar options
|
||||
if self.frame % 50 == 0 and self.long_active_ecu:
|
||||
if self.frame % 50 == 0 and self.long_active_ecu and not can_canfd_blended:
|
||||
can_sends.append(hyundaican.create_frt_radar_opt(self.packer))
|
||||
|
||||
return can_sends
|
||||
|
||||
def create_canfd_msgs(self, apply_steer_req, apply_torque, apply_angle, set_speed_in_units, accel, stopping, hud_control, CS, CC):
|
||||
def create_canfd_msgs(self, now_nanos, apply_steer_req, apply_torque, apply_angle, set_speed_in_units, accel, stopping,
|
||||
hud_control, CS, CC, starpilot_toggles, lka_icon, lfa_icon):
|
||||
can_sends = []
|
||||
|
||||
lka_steering = self.CP.flags & HyundaiFlags.CANFD_LKA_STEERING
|
||||
lka_steering_long = lka_steering and self.long_active_ecu
|
||||
use_ioniq_6_dynamic_long_tuning = self.CP.carFingerprint == CAR.HYUNDAI_IONIQ_6 and self.long_active_ecu and \
|
||||
CC.actuators.longControlState == LongCtrlState.pid
|
||||
use_ioniq_6_smoothed_accel = use_ioniq_6_dynamic_long_tuning and CC.actuators.accel >= self._ioniq_6_long_tuning.actual_accel
|
||||
|
||||
# steering control
|
||||
preserve_stock_lkas = self.CP.carFingerprint == CAR.HYUNDAI_IONIQ_6 and not self.long_active_ecu
|
||||
can_sends.extend(hyundaicanfd.create_steering_messages(self.packer, self.CP, self.CAN, CC.enabled,
|
||||
apply_steer_req, apply_torque, apply_angle))
|
||||
apply_steer_req, apply_torque, apply_angle,
|
||||
CS.stock_lfa_msg,
|
||||
CS.stock_lkas_msg if preserve_stock_lkas else None,
|
||||
lka_icon=lka_icon))
|
||||
|
||||
# prevent LFA from activating on LKA steering cars by sending "no lane lines detected" to ADAS ECU
|
||||
if self.frame % 5 == 0 and lka_steering:
|
||||
@@ -220,26 +546,80 @@ class CarController(CarControllerBase):
|
||||
|
||||
# LFA and HDA icons
|
||||
if self.frame % 5 == 0 and (not lka_steering or lka_steering_long):
|
||||
can_sends.append(hyundaicanfd.create_lfahda_cluster(self.packer, self.CAN, CC.enabled))
|
||||
can_sends.append(hyundaicanfd.create_lfahda_cluster(self.packer, self.CAN, CC.enabled, CS.stock_lfahda_cluster_msg,
|
||||
lfa_icon=lfa_icon))
|
||||
|
||||
# blinkers
|
||||
if lka_steering and self.CP.flags & HyundaiFlags.ENABLE_BLINKERS:
|
||||
can_sends.extend(hyundaicanfd.create_spas_messages(self.packer, self.CAN, CC.leftBlinker, CC.rightBlinker))
|
||||
|
||||
lane_change_ui_side = None
|
||||
if self.CP.carFingerprint == CAR.HYUNDAI_IONIQ_6:
|
||||
if CC.leftBlinker and not CC.rightBlinker:
|
||||
lane_change_ui_side = "left"
|
||||
elif CC.rightBlinker and not CC.leftBlinker:
|
||||
lane_change_ui_side = "right"
|
||||
|
||||
if lane_change_ui_side != self._ioniq_6_lane_change_ui_side:
|
||||
self._ioniq_6_lane_change_ui_side = lane_change_ui_side
|
||||
self._ioniq_6_lane_change_ui_frames = 0
|
||||
|
||||
if lane_change_ui_side is None or not self.long_active_ecu:
|
||||
self._ioniq_6_lane_change_ui_frames = 0
|
||||
else:
|
||||
# The stock Ioniq 6 lane-change animation stops when the ADAS ECU is disabled,
|
||||
# so replay the captured ECAN cluster frames ourselves while OP long is active.
|
||||
can_sends.extend(hyundaicanfd.create_ioniq_6_cluster_lane_change_messages(self.CAN,
|
||||
self._ioniq_6_lane_change_ui_frames,
|
||||
lane_change_ui_side))
|
||||
self._ioniq_6_lane_change_ui_frames += 1
|
||||
|
||||
if self.long_active_ecu:
|
||||
if lka_steering:
|
||||
can_sends.extend(hyundaicanfd.create_adrv_messages(self.packer, self.CAN, self.frame))
|
||||
else:
|
||||
can_sends.extend(hyundaicanfd.create_fca_warning_light(self.packer, self.CAN, self.frame))
|
||||
if self.CP.carFingerprint == CAR.HYUNDAI_IONIQ_6 and self.frame % 5 == 0:
|
||||
rear_stale = now_nanos - CS.blindspots_rear_corners_ts > CANFD_BLINDSPOT_STATUS_STALE_NS
|
||||
front_stale = now_nanos - CS.blindspots_front_corner_1_ts > CANFD_BLINDSPOT_STATUS_STALE_NS
|
||||
if CS.blindspots_rear_corners_ts > 0 and CS.blindspots_front_corner_1_ts > 0 and rear_stale and front_stale:
|
||||
can_sends.extend(hyundaicanfd.create_blindspot_status_messages(self.packer, self.CAN,
|
||||
CS.blindspots_rear_corners,
|
||||
CS.blindspots_front_corner_1,
|
||||
CS.left_blindspot_from_radar,
|
||||
CS.right_blindspot_from_radar,
|
||||
CC.leftBlinker,
|
||||
CC.rightBlinker))
|
||||
if self.CP.carFingerprint == CAR.HYUNDAI_IONIQ_6 and lane_change_ui_side is None:
|
||||
can_sends.extend(hyundaicanfd.create_ioniq_6_cluster_blindspot_messages(self.CAN, self.frame,
|
||||
CS.left_blindspot_from_radar,
|
||||
CS.right_blindspot_from_radar,
|
||||
CC.leftBlinker,
|
||||
CC.rightBlinker))
|
||||
if self.frame % 2 == 0:
|
||||
lead_visible, lead_distance, lead_rel_speed = self._get_canfd_scc_lead_state(CC, CS, now_nanos)
|
||||
acc_kwargs = {
|
||||
"main_mode_acc": int(CS.out.cruiseState.available),
|
||||
"direct_accel": True,
|
||||
"jerk_lower": 5.0,
|
||||
"jerk_upper": 3.0 if CC.actuators.longControlState == LongCtrlState.pid else 1.0,
|
||||
"lead_distance": lead_distance,
|
||||
"lead_rel_speed": lead_rel_speed,
|
||||
"lead_visible": lead_visible,
|
||||
}
|
||||
if self.CP.carFingerprint == CAR.HYUNDAI_IONIQ_6:
|
||||
if use_ioniq_6_smoothed_accel:
|
||||
acc_kwargs["jerk_lower"] = self._ioniq_6_long_tuning.jerk_lower
|
||||
acc_kwargs["jerk_upper"] = self._ioniq_6_long_tuning.jerk_upper
|
||||
can_sends.append(hyundaicanfd.create_acc_control(self.packer, self.CAN, CC.enabled, self.accel_last, accel, stopping, CC.cruiseControl.override,
|
||||
set_speed_in_units, hud_control))
|
||||
set_speed_in_units, hud_control, **acc_kwargs))
|
||||
self.accel_last = accel
|
||||
else:
|
||||
# button presses
|
||||
if (self.frame - self.last_button_frame) * DT_CTRL > 0.25:
|
||||
# cruise cancel
|
||||
if CC.cruiseControl.cancel:
|
||||
# cruise cancel - suppress when stock ACC is the fallback (ECU disable failed),
|
||||
# so openpilot doesn't fight/cancel the user's stock cruise
|
||||
if CC.cruiseControl.cancel and not self.ecu_disable_failed:
|
||||
if self.CP.flags & HyundaiFlags.CANFD_ALT_BUTTONS:
|
||||
can_sends.append(hyundaicanfd.create_acc_cancel(self.packer, self.CP, self.CAN, CS.cruise_info))
|
||||
self.last_button_frame = self.frame
|
||||
@@ -257,5 +637,7 @@ class CarController(CarControllerBase):
|
||||
for _ in range(20):
|
||||
can_sends.append(hyundaicanfd.create_buttons(self.packer, self.CP, self.CAN, CS.buttons_counter + 1, Buttons.RES_ACCEL))
|
||||
self.last_button_frame = self.frame
|
||||
else:
|
||||
can_sends.extend(self._create_canfd_redneck_button_messages(CS))
|
||||
|
||||
return can_sends
|
||||
|
||||
@@ -7,7 +7,8 @@ from opendbc.can import CANDefine, CANParser
|
||||
from opendbc.car import Bus, create_button_events, structs
|
||||
from opendbc.car.common.conversions import Conversions as CV
|
||||
from opendbc.car.hyundai.hyundaicanfd import CanBus
|
||||
from opendbc.car.hyundai.values import HyundaiFlags, HyundaiStarPilotFlags, CAR, DBC, Buttons, CarControllerParams
|
||||
from opendbc.car.hyundai.values import HyundaiFlags, HyundaiStarPilotFlags, CAR, DBC, Buttons, CarControllerParams, \
|
||||
hyundai_cancel_button_enables_cruise
|
||||
from opendbc.car.interfaces import CarStateBase
|
||||
|
||||
ButtonType = structs.CarState.ButtonEvent.Type
|
||||
@@ -21,6 +22,18 @@ ENABLE_BUTTONS = (Buttons.RES_ACCEL, Buttons.SET_DECEL, Buttons.CANCEL)
|
||||
BUTTONS_DICT = {Buttons.RES_ACCEL: ButtonType.accelCruise, Buttons.SET_DECEL: ButtonType.decelCruise,
|
||||
Buttons.GAP_DIST: ButtonType.gapAdjustCruise, Buttons.CANCEL: ButtonType.cancel}
|
||||
|
||||
IONIQ_6_BLINDSPOT_RIGHT_MASK = 0x08
|
||||
IONIQ_6_BLINDSPOT_LEFT_MASK = 0x10
|
||||
CANFD_CAMERA_LEAD_MIN_DISTANCE = 0.1
|
||||
|
||||
|
||||
def get_non_scc_cruise_signals(CP) -> tuple[str, str, str, str, str, str]:
|
||||
if CP.flags & HyundaiFlags.EV:
|
||||
return "LABEL11", "CC_React", "EMS12", "ACC_ACT", "E_EMS11", "Cruise_Limit_Target"
|
||||
if CP.flags & HyundaiFlags.HYBRID:
|
||||
return "E_CRUISE_CONTROL", "CRUISE_LAMP_M", "E_CRUISE_CONTROL", "CRUISE_LAMP_S", "ELECT_GEAR", "SLC_SET_SPEED"
|
||||
return "EMS16", "CRUISE_LAMP_M", "EMS16", "CRUISE_LAMP_S", "LVR12", "CF_Lvr_CruiseSet"
|
||||
|
||||
|
||||
def calculate_canfd_speed_limit(CP, FPCP, cp, cp_cam, speed_factor):
|
||||
if not (FPCP.flags & HyundaiStarPilotFlags.SPEED_LIMIT_AVAILABLE):
|
||||
@@ -34,6 +47,19 @@ def calculate_canfd_speed_limit(CP, FPCP, cp, cp_cam, speed_factor):
|
||||
return 0.0
|
||||
|
||||
|
||||
def decode_ioniq_6_blindspot_radar_state(state: int) -> tuple[bool, bool]:
|
||||
state_int = int(state)
|
||||
return bool(state_int & IONIQ_6_BLINDSPOT_LEFT_MASK), bool(state_int & IONIQ_6_BLINDSPOT_RIGHT_MASK)
|
||||
|
||||
|
||||
def decode_canfd_camera_lead(distance: float, rel_speed: float) -> tuple[bool, float, float]:
|
||||
lead_distance = float(distance)
|
||||
lead_visible = lead_distance > CANFD_CAMERA_LEAD_MIN_DISTANCE
|
||||
if not lead_visible:
|
||||
return False, 0.0, 0.0
|
||||
return True, lead_distance, float(rel_speed)
|
||||
|
||||
|
||||
class CarState(CarStateBase):
|
||||
@staticmethod
|
||||
def get_canfd_blinker_sig_names(car_fingerprint, use_alt_lamp: bool) -> tuple[str, str]:
|
||||
@@ -51,6 +77,10 @@ class CarState(CarStateBase):
|
||||
self.left_paddle = 0
|
||||
self.mode_button = 0
|
||||
self.custom_button = 0
|
||||
self.cancel_button_enable_in_progress = False
|
||||
self.cruise_buttons_msg = {}
|
||||
self.redneck_send_button = Buttons.NONE
|
||||
self.redneck_v_target = 0
|
||||
|
||||
self.gear_msg_canfd = "ACCELERATOR" if CP.flags & HyundaiFlags.EV else \
|
||||
"GEAR_ALT" if CP.flags & HyundaiFlags.CANFD_ALT_GEARS else \
|
||||
@@ -78,6 +108,21 @@ class CarState(CarStateBase):
|
||||
self.buttons_counter = 0
|
||||
|
||||
self.cruise_info = {}
|
||||
self.msg_364 = {}
|
||||
self.stock_lkas_msg = {}
|
||||
self.stock_lfa_msg = {}
|
||||
self.stock_lfahda_cluster_msg = {}
|
||||
self.stock_camera_lead_visible = False
|
||||
self.stock_camera_lead_distance = 0.0
|
||||
self.stock_camera_lead_rel_speed = 0.0
|
||||
self.stock_camera_lead_ts = 0
|
||||
self.stock_blinker_stalks_ts = 0
|
||||
self.blindspots_rear_corners = {}
|
||||
self.blindspots_front_corner_1 = {}
|
||||
self.blindspots_rear_corners_ts = 0
|
||||
self.blindspots_front_corner_1_ts = 0
|
||||
self.left_blindspot_from_radar = False
|
||||
self.right_blindspot_from_radar = False
|
||||
|
||||
# On some cars, CLU15->CF_Clu_VehicleSpeed can oscillate faster than the dash updates. Sample at 5 Hz
|
||||
self.cluster_speed = 0
|
||||
@@ -91,6 +136,38 @@ class CarState(CarStateBase):
|
||||
# Main button also can trigger an engagement on these cars
|
||||
return any(btn in ENABLE_BUTTONS for btn in self.cruise_buttons) or any(self.main_buttons)
|
||||
|
||||
def create_cruise_button_events(self, cur_button: int, prev_button: int) -> list[structs.CarState.ButtonEvent]:
|
||||
if cur_button != prev_button and prev_button != Buttons.CANCEL and cur_button == Buttons.CANCEL:
|
||||
self.cancel_button_enable_in_progress = (
|
||||
self.CP.openpilotLongitudinalControl and
|
||||
hyundai_cancel_button_enables_cruise(self.CP.carFingerprint) and
|
||||
not self.out.cruiseState.enabled
|
||||
)
|
||||
|
||||
buttons_dict = BUTTONS_DICT
|
||||
if self.cancel_button_enable_in_progress:
|
||||
buttons_dict = BUTTONS_DICT | {Buttons.CANCEL: ButtonType.accelCruise}
|
||||
|
||||
events = create_button_events(cur_button, prev_button, buttons_dict)
|
||||
|
||||
if cur_button != Buttons.CANCEL:
|
||||
self.cancel_button_enable_in_progress = False
|
||||
|
||||
return events
|
||||
|
||||
def update_button_enable(self, buttonEvents: list[structs.CarState.ButtonEvent]):
|
||||
if super().update_button_enable(buttonEvents):
|
||||
return True
|
||||
|
||||
if not self.CP.pcmCruise and hyundai_cancel_button_enables_cruise(self.CP.carFingerprint):
|
||||
for b in buttonEvents:
|
||||
# Some Palisade 2023 routes still surface the pause/resume interaction as a
|
||||
# plain cancel event even though stock ACC engages on the release edge.
|
||||
if b.type == ButtonType.cancel and not b.pressed and not self.out.cruiseState.enabled:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def update(self, can_parsers, starpilot_toggles) -> structs.CarState:
|
||||
cp = can_parsers[Bus.pt]
|
||||
cp_cam = can_parsers[Bus.cam]
|
||||
@@ -147,22 +224,22 @@ class CarState(CarStateBase):
|
||||
ret.cruiseState.standstill = False
|
||||
ret.cruiseState.nonAdaptive = False
|
||||
elif no_scc:
|
||||
cruise_enabled = cp.vl["TCS13"]["ACC_REQ"] == 1
|
||||
cruise_set_speed = cp.vl["LVR12"]["CF_Lvr_CruiseSet"]
|
||||
|
||||
# Regular-cruise Forte trims don't publish SCC11/SCC12; use the stock cruise request and set speed.
|
||||
ret.cruiseState.available = cruise_enabled or cp.vl["TCS13"]["ACCEnable"] == 0
|
||||
ret.cruiseState.enabled = cruise_enabled
|
||||
cruise_available_msg, cruise_available_sig, cruise_enabled_msg, cruise_enabled_sig, cruise_speed_msg, cruise_speed_sig = get_non_scc_cruise_signals(self.CP)
|
||||
ret.cruiseState.available = cp.vl[cruise_available_msg][cruise_available_sig] != 0
|
||||
ret.cruiseState.enabled = cp.vl[cruise_enabled_msg][cruise_enabled_sig] != 0
|
||||
ret.cruiseState.standstill = False
|
||||
ret.cruiseState.nonAdaptive = False
|
||||
if 0 < cruise_set_speed < 255:
|
||||
ret.cruiseState.speed = cruise_set_speed * speed_conv
|
||||
ret.cruiseState.speed = cp.vl[cruise_speed_msg][cruise_speed_sig] * speed_conv
|
||||
else:
|
||||
ret.cruiseState.available = cp_cruise.vl["SCC11"]["MainMode_ACC"] == 1
|
||||
scc_msg = "SCC12" if self.CP.flags & HyundaiFlags.CAN_CANFD_BLENDED else "SCC11"
|
||||
ret.cruiseState.available = cp_cruise.vl[scc_msg]["MainMode_ACC"] == 1
|
||||
ret.cruiseState.enabled = cp_cruise.vl["SCC12"]["ACCMode"] != 0
|
||||
ret.cruiseState.standstill = cp_cruise.vl["SCC11"]["SCCInfoDisplay"] == 4.
|
||||
ret.cruiseState.nonAdaptive = cp_cruise.vl["SCC11"]["SCCInfoDisplay"] == 2. # Shows 'Cruise Control' on dash
|
||||
ret.cruiseState.speed = cp_cruise.vl["SCC11"]["VSetDis"] * speed_conv
|
||||
ret.cruiseState.standstill = cp_cruise.vl[scc_msg]["SCCInfoDisplay"] == 4.
|
||||
ret.cruiseState.nonAdaptive = cp_cruise.vl[scc_msg]["SCCInfoDisplay"] == 2. # Shows 'Cruise Control' on dash
|
||||
ret.cruiseState.speed = cp_cruise.vl[scc_msg]["VSetDis"] * speed_conv
|
||||
|
||||
if self.CP.flags & HyundaiFlags.CAN_CANFD_BLENDED:
|
||||
self.msg_364 = copy.copy(cp_cam.vl["ALERTS_364"])
|
||||
|
||||
# TODO: Find brake pressure
|
||||
ret.brake = 0
|
||||
@@ -198,14 +275,23 @@ class CarState(CarStateBase):
|
||||
|
||||
ret.gearShifter = self.parse_gear_shifter(self.shifter_values.get(gear))
|
||||
|
||||
if not self.CP.openpilotLongitudinalControl or self.CP.flags & HyundaiFlags.CAMERA_SCC:
|
||||
aeb_src = "FCA11" if self.CP.flags & HyundaiFlags.USE_FCA.value else "SCC12"
|
||||
aeb_sig = "FCA_CmdAct" if self.CP.flags & HyundaiFlags.USE_FCA.value else "AEB_CmdAct"
|
||||
aeb_warning = cp_cruise.vl[aeb_src]["CF_VSM_Warn"] != 0
|
||||
scc_warning = cp_cruise.vl["SCC12"]["TakeOverReq"] == 1 # sometimes only SCC system shows an FCW
|
||||
aeb_braking = cp_cruise.vl[aeb_src]["CF_VSM_DecCmdAct"] != 0 or cp_cruise.vl[aeb_src][aeb_sig] != 0
|
||||
ret.stockFcw = (aeb_warning or scc_warning) and not aeb_braking
|
||||
ret.stockAeb = aeb_warning and aeb_braking
|
||||
if (not self.CP.openpilotLongitudinalControl or self.CP.flags & HyundaiFlags.CAMERA_SCC) and \
|
||||
not (self.CP.flags & HyundaiFlags.CAN_CANFD_BLENDED):
|
||||
if no_scc:
|
||||
if not (self.CP.flags & HyundaiFlags.NON_SCC_NO_FCA):
|
||||
cp_fca = cp if self.CP.flags & HyundaiFlags.NON_SCC_RADAR_FCA else cp_cam
|
||||
aeb_warning = cp_fca.vl["FCA11"]["CF_VSM_Warn"] != 0
|
||||
aeb_braking = cp_fca.vl["FCA11"]["CF_VSM_DecCmdAct"] != 0 or cp_fca.vl["FCA11"]["FCA_CmdAct"] != 0
|
||||
ret.stockFcw = aeb_warning and not aeb_braking
|
||||
ret.stockAeb = aeb_warning and aeb_braking
|
||||
else:
|
||||
aeb_src = "FCA11" if self.CP.flags & HyundaiFlags.USE_FCA.value else "SCC12"
|
||||
aeb_sig = "FCA_CmdAct" if self.CP.flags & HyundaiFlags.USE_FCA.value else "AEB_CmdAct"
|
||||
aeb_warning = cp_cruise.vl[aeb_src]["CF_VSM_Warn"] != 0
|
||||
scc_warning = cp_cruise.vl["SCC12"]["TakeOverReq"] == 1
|
||||
aeb_braking = cp_cruise.vl[aeb_src]["CF_VSM_DecCmdAct"] != 0 or cp_cruise.vl[aeb_src][aeb_sig] != 0
|
||||
ret.stockFcw = (aeb_warning or scc_warning) and not aeb_braking
|
||||
ret.stockAeb = aeb_warning and aeb_braking
|
||||
|
||||
if self.CP.enableBsm:
|
||||
ret.leftBlindspot = cp.vl["LCA11"]["CF_Lca_IndLeft"] != 0
|
||||
@@ -223,7 +309,7 @@ class CarState(CarStateBase):
|
||||
if self.CP.flags & HyundaiFlags.HAS_LDA_BUTTON:
|
||||
self.lda_button = cp.vl["BCM_PO_11"]["LDA_BTN"]
|
||||
|
||||
ret.buttonEvents = [*create_button_events(self.cruise_buttons[-1], prev_cruise_buttons, BUTTONS_DICT),
|
||||
ret.buttonEvents = [*self.create_cruise_button_events(self.cruise_buttons[-1], prev_cruise_buttons),
|
||||
*create_button_events(self.main_buttons[-1], prev_main_buttons, {1: ButtonType.mainCruise}),
|
||||
*create_button_events(self.lda_button, prev_lda_button, {1: ButtonType.lkas})]
|
||||
|
||||
@@ -283,13 +369,26 @@ class CarState(CarStateBase):
|
||||
cp.vl["BLINKERS"]["USE_ALT_LAMP"] == 1)
|
||||
ret.leftBlinker, ret.rightBlinker = self.update_blinker_from_lamp(50, cp.vl["BLINKERS"][left_blinker_sig],
|
||||
cp.vl["BLINKERS"][right_blinker_sig])
|
||||
self.left_blindspot_from_radar = False
|
||||
self.right_blindspot_from_radar = False
|
||||
if self.CP.carFingerprint == CAR.HYUNDAI_IONIQ_6:
|
||||
self.left_blindspot_from_radar, self.right_blindspot_from_radar = decode_ioniq_6_blindspot_radar_state(
|
||||
cp.vl["BLINDSPOTS_FRONT_CORNER_2"]["SIDE_DETECT_STATE"])
|
||||
if self.CP.enableBsm:
|
||||
if self.CP.carFingerprint == CAR.HYUNDAI_IONIQ_6:
|
||||
ret.leftBlindspot = cp.vl["BLINDSPOTS_REAR_CORNERS"]["LEFT_MB"] != 0
|
||||
ret.rightBlindspot = cp.vl["BLINDSPOTS_REAR_CORNERS"]["MORE_LEFT_PROB"] != 0
|
||||
ret.leftBlindspot = (bool(cp.vl["BLINDSPOTS_REAR_CORNERS"]["BCW_LtIndSta"]) or
|
||||
self.left_blindspot_from_radar)
|
||||
ret.rightBlindspot = (bool(cp.vl["BLINDSPOTS_REAR_CORNERS"]["BCW_RtIndSta"]) or
|
||||
self.right_blindspot_from_radar)
|
||||
else:
|
||||
ret.leftBlindspot = cp.vl["BLINDSPOTS_REAR_CORNERS"]["FL_INDICATOR"] != 0
|
||||
ret.rightBlindspot = cp.vl["BLINDSPOTS_REAR_CORNERS"]["FR_INDICATOR"] != 0
|
||||
ret.leftBlindspot = bool(cp.vl["BLINDSPOTS_REAR_CORNERS"]["BCW_LtIndSta"])
|
||||
ret.rightBlindspot = bool(cp.vl["BLINDSPOTS_REAR_CORNERS"]["BCW_RtIndSta"])
|
||||
|
||||
if self.CP.carFingerprint == CAR.HYUNDAI_IONIQ_6:
|
||||
self.blindspots_rear_corners = copy.copy(cp.vl["BLINDSPOTS_REAR_CORNERS"])
|
||||
self.blindspots_front_corner_1 = copy.copy(cp.vl["BLINDSPOTS_FRONT_CORNER_1"])
|
||||
self.blindspots_rear_corners_ts = cp.ts_nanos["BLINDSPOTS_REAR_CORNERS"]["CHECKSUM"]
|
||||
self.blindspots_front_corner_1_ts = cp.ts_nanos["BLINDSPOTS_FRONT_CORNER_1"]["CHECKSUM"]
|
||||
|
||||
# cruise state
|
||||
# CAN FD cars enable on main button press, set available if no TCS faults preventing engagement
|
||||
@@ -312,7 +411,6 @@ class CarState(CarStateBase):
|
||||
# TODO: find this message on ICE & HYBRID cars + cruise control signals (if exists)
|
||||
if self.CP.flags & HyundaiFlags.EV:
|
||||
ret.cruiseState.nonAdaptive = cp.vl["MANUAL_SPEED_LIMIT_ASSIST"]["MSLA_ENABLED"] == 1
|
||||
|
||||
prev_cruise_buttons = self.cruise_buttons[-1]
|
||||
prev_main_buttons = self.main_buttons[-1]
|
||||
prev_lda_button = self.lda_button
|
||||
@@ -322,15 +420,32 @@ class CarState(CarStateBase):
|
||||
self.lda_button = cp.vl[self.cruise_btns_msg_canfd]["LDA_BTN"]
|
||||
self.left_paddle = 0
|
||||
if self.CP.carFingerprint == CAR.HYUNDAI_IONIQ_6:
|
||||
self.cruise_buttons_msg = copy.copy(cp.vl["CRUISE_BUTTONS"])
|
||||
self.left_paddle = cp.vl["CRUISE_BUTTONS"]["LEFT_PADDLE"]
|
||||
self.buttons_counter = cp.vl[self.cruise_btns_msg_canfd]["COUNTER"]
|
||||
ret.accFaulted = cp.vl["TCS"]["ACCEnable"] != 0 # 0 ACC CONTROL ENABLED, 1-3 ACC CONTROL DISABLED
|
||||
|
||||
if self.CP.flags & HyundaiFlags.CANFD_LKA_STEERING:
|
||||
lkas_msg = "LKAS_ALT" if self.CP.flags & HyundaiFlags.CANFD_LKA_STEERING_ALT else "LKAS"
|
||||
self.lfa_block_msg = copy.copy(cp_cam.vl["CAM_0x362"] if self.CP.flags & HyundaiFlags.CANFD_LKA_STEERING_ALT
|
||||
else cp_cam.vl["CAM_0x2a4"])
|
||||
if cp_cam.ts_nanos[lkas_msg]["CHECKSUM"] > 0:
|
||||
self.stock_lkas_msg = copy.copy(cp_cam.vl[lkas_msg])
|
||||
lead_cp = cp if self.CP.flags & HyundaiFlags.CANFD_LKA_STEERING else cp_cam
|
||||
if lead_cp.ts_nanos["FR_CMR_03_50ms"]["FR_CMR_Crc3Val"] > 0:
|
||||
self.stock_camera_lead_ts = lead_cp.ts_nanos["FR_CMR_03_50ms"]["FR_CMR_Crc3Val"]
|
||||
self.stock_camera_lead_visible, self.stock_camera_lead_distance, self.stock_camera_lead_rel_speed = decode_canfd_camera_lead(
|
||||
lead_cp.vl["FR_CMR_03_50ms"]["Longitudinal_Distance"],
|
||||
lead_cp.vl["FR_CMR_03_50ms"]["Relative_Velocity"],
|
||||
)
|
||||
if cp.ts_nanos["LFA"]["CHECKSUM"] > 0:
|
||||
self.stock_lfa_msg = copy.copy(cp.vl["LFA"])
|
||||
if cp.ts_nanos["LFAHDA_CLUSTER"]["CHECKSUM"] > 0:
|
||||
self.stock_lfahda_cluster_msg = copy.copy(cp.vl["LFAHDA_CLUSTER"])
|
||||
if cp.ts_nanos["BLINKER_STALKS"]["CHECKSUM_MAYBE"] > 0:
|
||||
self.stock_blinker_stalks_ts = cp.ts_nanos["BLINKER_STALKS"]["CHECKSUM_MAYBE"]
|
||||
|
||||
ret.buttonEvents = [*create_button_events(self.cruise_buttons[-1], prev_cruise_buttons, BUTTONS_DICT),
|
||||
ret.buttonEvents = [*self.create_cruise_button_events(self.cruise_buttons[-1], prev_cruise_buttons),
|
||||
*create_button_events(self.main_buttons[-1], prev_main_buttons, {1: ButtonType.mainCruise}),
|
||||
*create_button_events(self.lda_button, prev_lda_button, {1: ButtonType.lkas}),
|
||||
*create_button_events(self.left_paddle, prev_left_paddle, {1: ButtonType.altButton2})]
|
||||
@@ -350,6 +465,10 @@ class CarState(CarStateBase):
|
||||
fp_ret.modePressed = bool(self.mode_button)
|
||||
fp_ret.customPressed = bool(self.custom_button)
|
||||
|
||||
# ADAS camera dashboard stop-sign signal (CANFD HKG only). Registered as optional
|
||||
# (freq=0); cars without it never publish, so the read returns 0 and the field stays 0.
|
||||
fp_ret.dashboardStopSign = 1 if bool(cp_cam.vl["ADAS_0x380"]["STOP_SIGN"]) else 0
|
||||
|
||||
return ret, fp_ret
|
||||
|
||||
def get_can_parsers_canfd(self, CP):
|
||||
@@ -363,11 +482,21 @@ class CarState(CarStateBase):
|
||||
]
|
||||
if CP.flags & HyundaiFlags.CANFD_LKA_STEERING:
|
||||
msgs.append(("FR_CMR_02_100ms", 10))
|
||||
msgs.append(("FR_CMR_03_50ms", 0))
|
||||
cam_msgs.append(("LKAS_ALT" if CP.flags & HyundaiFlags.CANFD_LKA_STEERING_ALT else "LKAS", 0))
|
||||
else:
|
||||
cam_msgs.append(("FR_CMR_02_100ms", 0)) # optional: not all non-LKA CANFD cars have this on CAM bus
|
||||
cam_msgs.append(("FR_CMR_03_50ms", 0)) # optional: camera lead/cipv data is not present on every CAN-FD trim
|
||||
msgs += [
|
||||
("LFA", 0), # optional: may stop once OP takes over, but preserve stock UI fields when present
|
||||
("LFAHDA_CLUSTER", 0), # optional: carries cluster icon state on some variants
|
||||
("BLINKER_STALKS", 0), # optional: some trims publish live stalk/light state on ECAN during turn camera events
|
||||
]
|
||||
if CP.flags & HyundaiFlags.EV:
|
||||
msgs.append(("DRIVE_MODE_EV", 0)) # optional: not all CAN-FD EV variants publish drive mode
|
||||
msgs.append(("MANUAL_SPEED_LIMIT_ASSIST", 0)) # optional: used for non-adaptive cruise state and Ioniq 6 i-Pedal latch detection
|
||||
msgs.append(("STEERING_WHEEL_MEDIA_BUTTONS", 0)) # optional: absent or slower on some CAN-FD variants
|
||||
cam_msgs.append(("ADAS_0x380", 0)) # optional: dashboard stop-sign signal, only on ADAS-equipped HKG CANFD
|
||||
return {
|
||||
Bus.pt: CANParser(DBC[CP.carFingerprint][Bus.pt], msgs, CanBus(CP).ECAN),
|
||||
Bus.cam: CANParser(DBC[CP.carFingerprint][Bus.pt], cam_msgs, CanBus(CP).CAM),
|
||||
@@ -377,7 +506,11 @@ class CarState(CarStateBase):
|
||||
if CP.flags & HyundaiFlags.CANFD:
|
||||
return self.get_can_parsers_canfd(CP)
|
||||
|
||||
msgs = []
|
||||
if CP.flags & HyundaiFlags.NON_SCC and not (CP.flags & HyundaiFlags.NON_SCC_NO_FCA):
|
||||
msgs.append(("FCA11", 0)) # Non-SCC trims can stop publishing FCA11; don't let it poison canValid
|
||||
|
||||
return {
|
||||
Bus.pt: CANParser(DBC[CP.carFingerprint][Bus.pt], [], 0),
|
||||
Bus.pt: CANParser(DBC[CP.carFingerprint][Bus.pt], msgs, 0),
|
||||
Bus.cam: CANParser(DBC[CP.carFingerprint][Bus.pt], [], 2),
|
||||
}
|
||||
|
||||
@@ -467,6 +467,33 @@ FW_VERSIONS = {
|
||||
b'\xf1\x00ON MFC AT USA LHD 1.00 1.04 99211-S9100 211227',
|
||||
],
|
||||
},
|
||||
CAR.HYUNDAI_PALISADE_2023: {
|
||||
(Ecu.eps, 0x7d4, None): [
|
||||
b'\xf1\x00ON MDPS C 1.00 1.01 56300-S9500 2922',
|
||||
b'\xf1\x00LXP MDPS C 1.00 1.00 56310-S8620 4LXPC100',
|
||||
],
|
||||
(Ecu.fwdCamera, 0x7c4, None): [
|
||||
b'\xf1\x00ON MFC AT USA LHD 1.00 1.00 99211-S9170 240531',
|
||||
b'\xf1\x00ON MFC AT USA LHD 1.00 1.01 99211-S9160 230802',
|
||||
b'\xf1\x00LX2 MFC AT USA LHD 1.00 1.04 99211-S8150 220622',
|
||||
b'\xf1\x00LX2 MFC AT RUS LHD 1.00 1.04 99211-S8150 220622',
|
||||
b'\xf1\x00ON MFC AT USA LHD 1.00 1.01 99211-S9150 220708',
|
||||
b'\xf1\x00ON MFC AT USA LHD 1.00 1.00 99211-S9160 230303',
|
||||
b'\xf1\x00LX2 MFC AT USA LHD 1.00 1.01 99211-S8600 230817',
|
||||
b'\xf1\x00LX2 MFC AT USA LHD 1.00 1.00 99211-S8700 240221',
|
||||
],
|
||||
(Ecu.fwdRadar, 0x7d0, None): [
|
||||
b'\xf1\x00ON__ SCC FHCUP 1.00 1.00 99110-S9170 ',
|
||||
b'\xf1\x00ON__ SCC FHCUP 1.00 1.00 99110-S9160 ',
|
||||
b'\xf1\x00LX2_ SCC ----- 1.00 1.01 99110-S8150 ',
|
||||
b'\xf1\x00ON__ SCC ----- 1.00 1.01 99110-S9150 ',
|
||||
b'\xf1\x00LX2_ SCC FHCUP 1.00 1.01 99110-S8150 ',
|
||||
b'\xf1\x00ON__ SCC FHCUP 1.00 1.01 99110-S9150 ',
|
||||
b'\xf1\x00LX2 SCC FHCUP 1.00 1.00 99110-S8600 ',
|
||||
b'\xf1\x00LX2_ SCC FHCUP 1.00 1.01 99110-S8700 ',
|
||||
b'\xf1\x00LX2_ SCC F-CUP 1.00 1.01 99110-S8150 ',
|
||||
],
|
||||
},
|
||||
CAR.HYUNDAI_VELOSTER: {
|
||||
(Ecu.fwdRadar, 0x7d0, None): [
|
||||
b'\xf1\x00JS__ SCC H-CUP 1.00 1.02 95650-J3200 ',
|
||||
@@ -583,12 +610,14 @@ FW_VERSIONS = {
|
||||
CAR.KIA_FORTE: {
|
||||
(Ecu.eps, 0x7d4, None): [
|
||||
b'\xf1\x00BD MDPS C 1.00 1.02 56310-XX000 4BD2C102',
|
||||
b'\xf1\x00BD MDPS C 1.00 1.07 56310/M6300 4BDDC107',
|
||||
b'\xf1\x00BD MDPS C 1.00 1.08 56310/M6300 4BDDC108',
|
||||
b'\xf1\x00BD MDPS C 1.00 1.08 56310M6300\x00 4BDDC108',
|
||||
b'\xf1\x00BDm MDPS C A.01 1.01 56310M7800\x00 4BPMC101',
|
||||
b'\xf1\x00BDm MDPS C A.01 1.03 56310M7800\x00 4BPMC103',
|
||||
],
|
||||
(Ecu.fwdCamera, 0x7c4, None): [
|
||||
b'\xf1\x00BD LKAS AT USA LHD 1.00 1.02 95740-M6000 J31',
|
||||
b'\xf1\x00BD LKAS AT USA LHD 1.00 1.04 95740-M6000 J33',
|
||||
b'\xf1\x00BDP LKAS AT USA LHD 1.00 1.05 99211-M6500 744',
|
||||
],
|
||||
@@ -1066,6 +1095,7 @@ FW_VERSIONS = {
|
||||
b'\xf1\x00CE MFC AT EUR LHD 1.00 1.04 99211-KL000 221213',
|
||||
b'\xf1\x00CE MFC AT USA LHD 1.00 1.04 99211-KL000 221213',
|
||||
b'\xf1\x00CE MFC AT USA LHD 1.00 1.06 99211-KL000 230915',
|
||||
b'\xf1\x00CE MFC AT CAN LHD 1.00 1.06 99211-KL000 230915',
|
||||
],
|
||||
},
|
||||
CAR.HYUNDAI_TUCSON_4TH_GEN: {
|
||||
@@ -1282,4 +1312,103 @@ FW_VERSIONS = {
|
||||
b'\xf1\x00T01G00BL T01I00A1 DOS2T16X4XI00NS0\x99L\xeeq',
|
||||
],
|
||||
},
|
||||
CAR.KIA_CEED_PHEV_2022_NON_SCC: {
|
||||
(Ecu.eps, 0x7D4, None): [
|
||||
b'\xf1\x00CD MDPS C 1.00 1.01 56310-XX000 4CPHC101',
|
||||
],
|
||||
(Ecu.fwdCamera, 0x7C4, None): [
|
||||
b'\xf1\x00CDH LKAS AT EUR LHD 1.00 1.01 99211-CR700 931',
|
||||
],
|
||||
},
|
||||
CAR.GENESIS_G70_2021_NON_SCC: {
|
||||
(Ecu.eps, 0x7d4, None): [
|
||||
b'\xf1\x00IK MDPS R 1.00 1.08 57700-G9200 4I2CL108',
|
||||
],
|
||||
(Ecu.fwdRadar, 0x7d0, None): [
|
||||
b'\xf1\x00IK__ SCC --CUP 1.00 1.02 96400-G9100 ',
|
||||
],
|
||||
(Ecu.fwdCamera, 0x7c4, None): [
|
||||
b'\xf1\x00IK MFC MT USA LHD 1.00 1.01 95740-G9000 170920',
|
||||
],
|
||||
},
|
||||
CAR.HYUNDAI_KONA_NON_SCC: {
|
||||
(Ecu.eps, 0x7d4, None): [
|
||||
b'\xf1\x00OS MDPS C 1.00 1.05 56310J9030\x00 4OSDC105',
|
||||
b'\xf1\x00OS MDPS C 1.00 1.04 56310J9030\x00 4OSDC104',
|
||||
],
|
||||
(Ecu.fwdCamera, 0x7c4, None): [
|
||||
b'\xf1\x00OS9 LKAS AT USA LHD 1.00 1.00 95740-J9200 g30',
|
||||
],
|
||||
(Ecu.transmission, 0x7e1, None): [
|
||||
b'\xf1\x006T6J0_C2\x00\x006T6K1051\x00\x00TOS4N20NS2\x00\x00\x00\x00',
|
||||
],
|
||||
},
|
||||
CAR.KIA_FORTE_2019_NON_SCC: {
|
||||
(Ecu.eps, 0x7D4, None): [
|
||||
b'\xf1\x00BD MDPS C 1.00 1.04 56310/M6000 4BDDC104',
|
||||
b'\xf1\x00BD MDPS C 1.00 1.05 56310/M6000 4BDDC105',
|
||||
],
|
||||
(Ecu.fwdCamera, 0x7C4, None): [
|
||||
b'\xf1\x00BD LKAS AT USA LHD 1.00 1.02 95740-M6000 J31',
|
||||
],
|
||||
},
|
||||
CAR.KIA_FORTE_2021_NON_SCC: {
|
||||
(Ecu.eps, 0x7D4, None): [
|
||||
b'\xf1\x00BD MDPS C 1.00 1.07 56310/M6300 4BDDC107',
|
||||
b'\xf1\x00BD MDPS C 1.00 1.08 56310M6000\x00 4BDDC108',
|
||||
],
|
||||
(Ecu.fwdCamera, 0x7C4, None): [
|
||||
b'\xf1\x00BD LKAS AT USA LHD 1.00 1.02 95740-M6000 J31',
|
||||
b'\xf1\x00BD LKAS AT USA LHD 1.00 1.04 95740-M6000 J33',
|
||||
],
|
||||
},
|
||||
CAR.KIA_SELTOS_2023_NON_SCC: {
|
||||
(Ecu.abs, 0x7d1, None): [
|
||||
b'\xf1\x00SP ESC \t 101\"\t\x01 58910-Q5510',
|
||||
b'\xf1\x00SP ESC \r 100\"\x04\x01 58910-Q5510',
|
||||
],
|
||||
(Ecu.eps, 0x7d4, None): [
|
||||
b'\xf1\x00SP2 MDPS C 1.00 1.04 56310Q5240 4SPSC104',
|
||||
b'\xf1\x00SP2 MDPS C 1.00 1.01 56300Q5920 ',
|
||||
],
|
||||
(Ecu.fwdCamera, 0x7c4, None): [
|
||||
b'\xf1\x00SP2 MFC AT USA LHD 1.00 1.03 99210-Q5500 230208',
|
||||
b'\xf1\x00SP2 MFC AT AUS RHD 1.00 1.02 99210-Q5500 220624',
|
||||
],
|
||||
(Ecu.transmission, 0x7e1, None): [
|
||||
b'\xf1\x006V2B0_C2\x00\x006V2D5051\x00\x00CSP2N20NL0\x00\x00\x00\x00',
|
||||
b'\xf1\x006V2B0_C2\x00\x006V2D4051\x00\x00CSP2N20KL1\x00\x00\x00\x00',
|
||||
],
|
||||
},
|
||||
CAR.HYUNDAI_ELANTRA_2022_NON_SCC: {
|
||||
(Ecu.eps, 0x7d4, None): [
|
||||
b'\xf1\x00CN7 MDPS R 1.00 1.04 57700-IB000 4CNNP104',
|
||||
],
|
||||
(Ecu.fwdCamera, 0x7c4, None): [
|
||||
b'\xf1\x00CN7 MFC AT USA LHD 1.00 1.01 99210-AB000 210205',
|
||||
b'\xf1\x00CN7 MFC AT USA LHD 1.00 1.00 99210-IB000 210531',
|
||||
],
|
||||
(Ecu.abs, 0x7d1, None): [
|
||||
b'\xf1\x00CN ESC \t 100!\x05\x01 58910-IB000',
|
||||
],
|
||||
(Ecu.transmission, 0x7e1, None): [
|
||||
b'\xf1\x00T02601BL T02900A1 WCN7T20XXX900NS4\xf7\xccz\xf6',
|
||||
],
|
||||
},
|
||||
CAR.HYUNDAI_ELANTRA_HEV_2022_NON_SCC: {
|
||||
(Ecu.eps, 0x7d4, None): [
|
||||
b'\xf1\x00CN7 MDPS C 1.00 1.02 56310/BY050 4CNHC102',
|
||||
],
|
||||
(Ecu.fwdCamera, 0x7c4, None): [
|
||||
b'\xf1\x00CN7HMFC AT USA LHD 1.00 1.04 99210-AA000 210205',
|
||||
],
|
||||
(Ecu.transmission, 0x7e1, None): [
|
||||
b'\xf1\x006U3L0_C2\x00\x006U3K3051\x00\x00HCN0G16NS0\x00\x00\x00\x00',
|
||||
],
|
||||
},
|
||||
CAR.HYUNDAI_BAYON_1ST_GEN_NON_SCC: {
|
||||
(Ecu.fwdCamera, 0x7c4, None): [
|
||||
b'\xf1\x00BC3 LKA AT EUR LHD 1.00 1.01 99211-Q0100 261',
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import crcmod
|
||||
from opendbc.car.hyundai.hyundaicanfd import CanBus
|
||||
from opendbc.car.hyundai.values import CAR, HyundaiFlags
|
||||
|
||||
hyundai_checksum = crcmod.mkCrcFun(0x11D, initCrc=0xFD, rev=False, xorOut=0xdf)
|
||||
@@ -96,6 +97,47 @@ def create_lkas11(packer, frame, CP, apply_torque, steer_req,
|
||||
return packer.make_can_msg("LKAS11", 0, values)
|
||||
|
||||
|
||||
def create_checksum_can_canfd_blended(packer, bus, addr, values):
|
||||
dat = packer.make_can_msg(addr, bus, values)[1]
|
||||
return hyundai_checksum(dat[1:8])
|
||||
|
||||
|
||||
def create_lkas11_can_canfd_blended(packer, frame, CP, apply_steer, steer_req,
|
||||
torque_fault, lkas11, sys_warning, sys_state, enabled,
|
||||
left_lane, right_lane,
|
||||
left_lane_depart, right_lane_depart, msg_364):
|
||||
bus = CanBus(CP).ECAN
|
||||
values = {
|
||||
"CF_Lkas_LdwsActivemode": int(left_lane) + (int(right_lane) << 1),
|
||||
"CF_Lkas_LdwsLHWarning": left_lane_depart,
|
||||
"CF_Lkas_LdwsRHWarning": right_lane_depart,
|
||||
"CF_Lkas_FcwOpt_USM": 2 if enabled else 1,
|
||||
"CR_Lkas_StrToqReq": apply_steer,
|
||||
"CF_Lkas_ActToi": steer_req,
|
||||
"CF_Lkas_ToiFlt": torque_fault,
|
||||
"CF_Lkas_MsgCount": frame % 0x10,
|
||||
"NEW_SIGNAL_1": 0,
|
||||
"NEW_SIGNAL_5": 100,
|
||||
}
|
||||
values["CF_Lkas_Chksum"] = create_checksum_can_canfd_blended(packer, bus, "LKAS11", values)
|
||||
|
||||
alerts_364 = {k: v for k, v in msg_364.items() if k not in ("CHECKSUM", "COUNTER")} if msg_364 else {}
|
||||
alerts_364.setdefault("BYTE2", 0)
|
||||
alerts_364.setdefault("BYTE3", 0)
|
||||
alerts_364.setdefault("DAW_Status", 0)
|
||||
alerts_364["DAW_Warning"] = 0
|
||||
alerts_364.setdefault("BYTE5", 0)
|
||||
alerts_364.setdefault("BYTE6", 0)
|
||||
alerts_364.setdefault("BYTE7", 0)
|
||||
alerts_364["COUNTER"] = frame % 0x10
|
||||
alerts_364["CHECKSUM"] = create_checksum_can_canfd_blended(packer, bus, "ALERTS_364", alerts_364)
|
||||
|
||||
return [
|
||||
packer.make_can_msg("LKAS11", bus, values),
|
||||
packer.make_can_msg("ALERTS_364", bus, alerts_364),
|
||||
]
|
||||
|
||||
|
||||
def create_clu11(packer, frame, clu11, button, CP):
|
||||
values = {s: clu11[s] for s in [
|
||||
"CF_Clu_CruiseSwState",
|
||||
@@ -114,15 +156,80 @@ def create_clu11(packer, frame, clu11, button, CP):
|
||||
values["CF_Clu_CruiseSwState"] = button
|
||||
values["CF_Clu_AliveCnt1"] = frame % 0x10
|
||||
# send buttons to camera on camera-scc based cars
|
||||
bus = 2 if CP.flags & HyundaiFlags.CAMERA_SCC else 0
|
||||
if CP.flags & HyundaiFlags.CAMERA_SCC:
|
||||
bus = 2
|
||||
elif CP.flags & HyundaiFlags.CAN_CANFD_BLENDED:
|
||||
bus = CanBus(CP).ECAN
|
||||
else:
|
||||
bus = 0
|
||||
return packer.make_can_msg("CLU11", bus, values)
|
||||
|
||||
|
||||
def create_lfahda_mfc(packer, enabled):
|
||||
def create_lfahda_mfc(packer, enabled, frame=None, CP=None, lfa_icon=None):
|
||||
if lfa_icon is None:
|
||||
lfa_icon = 2 if enabled else 0
|
||||
|
||||
values = {
|
||||
"LFA_Icon_State": 2 if enabled else 0,
|
||||
"LFA_Icon_State": lfa_icon,
|
||||
}
|
||||
return packer.make_can_msg("LFAHDA_MFC", 0, values)
|
||||
can_canfd_blended = CP is not None and bool(CP.flags & HyundaiFlags.CAN_CANFD_BLENDED)
|
||||
bus = CanBus(CP).ECAN if can_canfd_blended else 0
|
||||
if can_canfd_blended:
|
||||
values["COUNTER"] = 0 if frame is None else frame % 0x10
|
||||
values["CHECKSUM"] = create_checksum_can_canfd_blended(packer, bus, "LFAHDA_MFC", values)
|
||||
return packer.make_can_msg("LFAHDA_MFC", bus, 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 = []
|
||||
bus = CanBus(CP).ECAN
|
||||
|
||||
scc11_values = {
|
||||
"aReqRaw": accel,
|
||||
"aReqValue": accel,
|
||||
"JerkUpperLimit": upper_jerk,
|
||||
"JerkLowerLimit": 5.0,
|
||||
"ComfortBandUpper": 0.0,
|
||||
"ComfortBandLower": 0.0,
|
||||
"COUNTER": idx % 0x10,
|
||||
}
|
||||
scc11_values["CHECKSUM"] = create_checksum_can_canfd_blended(packer, bus, "SCC11", scc11_values)
|
||||
commands.append(packer.make_can_msg("SCC11", bus, scc11_values))
|
||||
|
||||
scc12_values = {
|
||||
"MainMode_ACC": 1,
|
||||
"ACCMode_Inactive": 0 if enabled else 1,
|
||||
"TauGapSet": hud_control.leadDistanceBars,
|
||||
"VSetDis": set_speed if enabled else 0,
|
||||
"ACC_ObjDist": 1,
|
||||
"ACCMode": 2 if enabled and long_override else 1 if enabled else 0,
|
||||
"StopReq": 1 if stopping else 0,
|
||||
"ACC_ObjDist_Ref": 1,
|
||||
"COUNTER": idx % 0x10,
|
||||
}
|
||||
scc12_values["CHECKSUM"] = create_checksum_can_canfd_blended(packer, bus, "SCC12", scc12_values)
|
||||
commands.append(packer.make_can_msg("SCC12", bus, scc12_values))
|
||||
|
||||
scc14_values = {
|
||||
"ACC_ObjRelSpd": 0,
|
||||
"ObjValid": 1,
|
||||
"ObjStatus": 1,
|
||||
"COUNTER": idx % 0x10,
|
||||
}
|
||||
scc14_values["CHECKSUM"] = create_checksum_can_canfd_blended(packer, bus, "SCC14", scc14_values)
|
||||
commands.append(packer.make_can_msg("SCC14", bus, scc14_values))
|
||||
|
||||
if use_fca and not (CP.flags & HyundaiFlags.CAMERA_SCC):
|
||||
fca11_values = {
|
||||
"cr_vsm_deccmd": 255,
|
||||
"cf_vsm_deccmdact": 127,
|
||||
"COUNTER": idx % 0x10,
|
||||
}
|
||||
fca11_values["CHECKSUM"] = create_checksum_can_canfd_blended(packer, bus, "FCA11", fca11_values)
|
||||
commands.append(packer.make_can_msg("FCA11", bus, fca11_values))
|
||||
|
||||
return commands
|
||||
|
||||
|
||||
def create_acc_commands(packer, enabled, accel, upper_jerk, idx, hud_control, set_speed, stopping, long_override, use_fca, CP):
|
||||
@@ -149,11 +256,10 @@ def create_acc_commands(packer, enabled, accel, upper_jerk, idx, hud_control, se
|
||||
"CR_VSM_Alive": idx % 0xF,
|
||||
}
|
||||
|
||||
# show AEB disabled indicator on dash with SCC12 if not sending FCA messages.
|
||||
# these signals also prevent a TCS fault on non-FCA cars with alpha longitudinal
|
||||
# Keep ESC/TCS happy on non-FCA cars without explicitly showing the disabled AEB icon.
|
||||
if not use_fca:
|
||||
scc12_values["CF_VSM_ConfMode"] = 1
|
||||
scc12_values["AEB_Status"] = 1 # AEB disabled
|
||||
scc12_values["AEB_Status"] = 2
|
||||
|
||||
scc12_dat = packer.make_can_msg("SCC12", 0, scc12_values)[1]
|
||||
scc12_values["CR_VSM_ChkSum"] = 0x10 - sum(sum(divmod(i, 16)) for i in scc12_dat) % 0x10
|
||||
@@ -179,7 +285,7 @@ def create_acc_commands(packer, enabled, accel, upper_jerk, idx, hud_control, se
|
||||
"CR_FCA_Alive": idx % 0xF,
|
||||
"PAINT1_Status": 1,
|
||||
"FCA_DrvSetStatus": 1,
|
||||
"FCA_Status": 1, # AEB disabled
|
||||
"FCA_Status": 2,
|
||||
}
|
||||
fca11_dat = packer.make_can_msg("FCA11", 0, fca11_values)[1]
|
||||
fca11_values["CR_FCA_ChkSum"] = hyundai_checksum(fca11_dat[:7])
|
||||
@@ -203,7 +309,7 @@ def create_acc_opt(packer, CP):
|
||||
if not (CP.flags & HyundaiFlags.CAMERA_SCC):
|
||||
fca12_values = {
|
||||
"FCA_DrvSetState": 2,
|
||||
"FCA_USM": 1, # AEB disabled
|
||||
"FCA_USM": 2,
|
||||
}
|
||||
commands.append(packer.make_can_msg("FCA12", 0, fca12_values))
|
||||
|
||||
@@ -215,3 +321,20 @@ def create_frt_radar_opt(packer):
|
||||
"CF_FCA_Equip_Front_Radar": 1,
|
||||
}
|
||||
return packer.make_can_msg("FRT_RADAR11", 0, frt_radar11_values)
|
||||
|
||||
|
||||
def create_radar_aux_messages(packer, CAN, frame):
|
||||
commands = []
|
||||
|
||||
for addr, freq, values in (
|
||||
("RADAR_0x363", 2, {"FCA_ESA": 1}),
|
||||
("RADAR_0x398", 5, {"BYTE4": 0x80, "BYTE5": 0x10}),
|
||||
):
|
||||
if frame % freq != 0:
|
||||
continue
|
||||
|
||||
msg_values = values | {"COUNTER": frame % 0x10}
|
||||
msg_values["CHECKSUM"] = create_checksum_can_canfd_blended(packer, CAN.ECAN, addr, msg_values)
|
||||
commands.append(packer.make_can_msg(addr, CAN.ECAN, msg_values))
|
||||
|
||||
return commands
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import copy
|
||||
import numpy as np
|
||||
from opendbc.car import CanBusBase
|
||||
from opendbc.car import CanBusBase, CanData
|
||||
from opendbc.car.crc import CRC16_XMODEM
|
||||
from opendbc.car.hyundai.values import HyundaiFlags
|
||||
|
||||
@@ -91,24 +91,37 @@ def _create_angle_adas_cmd_msg(packer, CAN, apply_angle: float, lat_active: bool
|
||||
return packer.make_can_msg("ADAS_CMD_35_10ms", CAN.ECAN, values)
|
||||
|
||||
|
||||
def create_steering_messages(packer, CP, CAN, enabled, lat_active, apply_torque, apply_angle):
|
||||
common_values = {
|
||||
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):
|
||||
if lka_icon is None:
|
||||
lka_icon = 2 if enabled else 1
|
||||
|
||||
control_values = {
|
||||
"LKA_MODE": 2,
|
||||
"LKA_ICON": 2 if enabled else 1,
|
||||
"LKA_ICON": lka_icon,
|
||||
"TORQUE_REQUEST": 0 if CP.flags & HyundaiFlags.CANFD_ANGLE_STEERING else apply_torque,
|
||||
"LKA_ASSIST": 0,
|
||||
"STEER_REQ": 0 if CP.flags & HyundaiFlags.CANFD_ANGLE_STEERING else (1 if lat_active else 0),
|
||||
"STEER_MODE": 0,
|
||||
"HAS_LANE_SAFETY": 0, # hide LKAS settings
|
||||
"NEW_SIGNAL_2": 0,
|
||||
"DAMP_FACTOR": 100, # can potentially tuned for better perf [3, 200]
|
||||
}
|
||||
|
||||
lkas_values = copy.copy(common_values)
|
||||
lkas_values["LKA_AVAILABLE"] = 0
|
||||
if lkas_base_values:
|
||||
lkas_values = {k: v for k, v in lkas_base_values.items() if k not in ("CHECKSUM", "COUNTER")}
|
||||
lkas_values.update(control_values)
|
||||
else:
|
||||
lkas_values = copy.copy(control_values)
|
||||
lkas_values["LKA_AVAILABLE"] = 0
|
||||
|
||||
lfa_values = copy.copy(common_values)
|
||||
lfa_values["NEW_SIGNAL_1"] = 0
|
||||
if lfa_base_values:
|
||||
# Preserve stock UI/status fields and only override the actuation-relevant signals.
|
||||
lfa_values = {k: v for k, v in lfa_base_values.items() if k not in ("CHECKSUM", "COUNTER")}
|
||||
lfa_values.update(control_values)
|
||||
else:
|
||||
lfa_values = copy.copy(control_values)
|
||||
lfa_values["HAS_LANE_SAFETY"] = 0 # hide LKAS settings
|
||||
lfa_values["NEW_SIGNAL_1"] = 0
|
||||
lfa_values["NEW_SIGNAL_2"] = 0
|
||||
lfa_values["DAMP_FACTOR"] = 100 # can potentially tuned for better perf [3, 200]
|
||||
|
||||
if CP.flags & HyundaiFlags.CANFD_ANGLE_STEERING and CP.flags & HyundaiFlags.CANFD_LKA_STEERING_ALT:
|
||||
lkas_values["ADAS_StrAnglReqVal"] = apply_angle
|
||||
@@ -124,6 +137,9 @@ def create_steering_messages(packer, CP, CAN, enabled, lat_active, apply_torque,
|
||||
else:
|
||||
if CP.flags & HyundaiFlags.CANFD_ANGLE_STEERING:
|
||||
if CP.flags & HyundaiFlags.SEND_LFA:
|
||||
# Some CAN-FD angle-steering trims still expect the stock-style LFA status/UI
|
||||
# message to remain present even though angle actuation comes through ADAS_CMD.
|
||||
ret.append(packer.make_can_msg("LFA", CAN.ECAN, lfa_values))
|
||||
ret.append(_create_angle_adas_cmd_msg(packer, CAN, apply_angle, lat_active, apply_torque))
|
||||
else:
|
||||
ret.append(_create_angle_lfa_msg(packer, CAN, lfa_values, apply_angle, lat_active, apply_torque))
|
||||
@@ -146,12 +162,15 @@ def create_suppress_lfa(packer, CAN, lfa_block_msg, lka_steering_alt):
|
||||
return packer.make_can_msg(suppress_msg, CAN.ACAN, values)
|
||||
|
||||
|
||||
def create_buttons(packer, CP, CAN, cnt, btn):
|
||||
values = {
|
||||
def create_buttons(packer, CP, CAN, cnt, btn=0, base_values=None, left_paddle=False, right_paddle=False):
|
||||
values = {k: v for k, v in base_values.items() if k not in ("_CHECKSUM", "COUNTER")} if base_values else {}
|
||||
values.update({
|
||||
"COUNTER": cnt,
|
||||
"SET_ME_1": 1,
|
||||
"CRUISE_BUTTONS": btn,
|
||||
}
|
||||
"LEFT_PADDLE": int(left_paddle),
|
||||
"RIGHT_PADDLE": int(right_paddle),
|
||||
})
|
||||
|
||||
bus = CAN.ECAN if CP.flags & HyundaiFlags.CANFD_LKA_STEERING else CAN.CAM
|
||||
return packer.make_can_msg("CRUISE_BUTTONS", bus, values)
|
||||
@@ -188,36 +207,300 @@ def create_acc_cancel(packer, CP, CAN, cruise_info_copy):
|
||||
return packer.make_can_msg("SCC_CONTROL", CAN.ECAN, values)
|
||||
|
||||
|
||||
def create_lfahda_cluster(packer, CAN, enabled):
|
||||
values = {
|
||||
def create_lfahda_cluster(packer, CAN, enabled, base_values=None, lfa_icon=None):
|
||||
if lfa_icon is None:
|
||||
lfa_icon = 2 if enabled else 0
|
||||
|
||||
values = {k: v for k, v in base_values.items() if k not in ("CHECKSUM", "COUNTER")} if base_values else {}
|
||||
values.update({
|
||||
"HDA_ICON": 1 if enabled else 0,
|
||||
"LFA_ICON": 2 if enabled else 0,
|
||||
}
|
||||
"LFA_ICON": lfa_icon,
|
||||
})
|
||||
return packer.make_can_msg("LFAHDA_CLUSTER", CAN.ECAN, values)
|
||||
|
||||
|
||||
def create_acc_control(packer, CAN, enabled, accel_last, accel, stopping, gas_override, set_speed, hud_control):
|
||||
def create_blindspot_status_messages(packer, CAN, rear_values, front_corner_values,
|
||||
left_blindspot=False, right_blindspot=False,
|
||||
left_blinker=False, right_blinker=False):
|
||||
# Reuse the last known-good payload but regenerate the rolling counter/checksum.
|
||||
rear = {k: v for k, v in rear_values.items() if k not in ("CHECKSUM", "COUNTER")}
|
||||
front = {k: v for k, v in front_corner_values.items() if k not in ("CHECKSUM", "COUNTER")}
|
||||
left_state = 2 if left_blindspot and left_blinker else (1 if left_blindspot else 0)
|
||||
right_state = 2 if right_blindspot and right_blinker else (1 if right_blindspot else 0)
|
||||
|
||||
rear["BCW_Sta"] = int(left_blindspot or right_blindspot)
|
||||
rear["BCW_LtIndSta"] = left_state
|
||||
rear["BCW_RtIndSta"] = right_state
|
||||
rear["BCW_IndSta"] = max(left_state, right_state)
|
||||
rear["OSMrrLamp_LtIndSta"] = left_state
|
||||
rear["OSMrrLamp_RtIndSta"] = right_state
|
||||
# Keep the older fields aligned where they still correlate on some platforms.
|
||||
rear["FL_INDICATOR"] = left_state
|
||||
rear["FR_INDICATOR"] = right_state
|
||||
if "NEW_SIGNAL_3" not in front:
|
||||
front["NEW_SIGNAL_3"] = 1
|
||||
|
||||
return [
|
||||
packer.make_can_msg("BLINDSPOTS_REAR_CORNERS", CAN.ECAN, rear),
|
||||
packer.make_can_msg("BLINDSPOTS_FRONT_CORNER_1", CAN.ECAN, front),
|
||||
]
|
||||
|
||||
|
||||
IONIQ_6_CLUSTER_BLINDSPOT_31A = {
|
||||
"right": (
|
||||
bytes.fromhex("fa7c10f0f0ffff03898aff0b0a8678ff000000007e0055550000000000000000"),
|
||||
bytes.fromhex("ac0e11f0f0ffff03898aff0c0a8678ff000000007e0055550000000000000000"),
|
||||
bytes.fromhex("76ce12f0f0ffff03898aff0b0a8678ff000000007e0055550000000000000000"),
|
||||
bytes.fromhex("309713f0f0ffff03898aff0b0a8678ff000000007e0055550000000000000000"),
|
||||
bytes.fromhex("d32214f0f0ffff03898aff0c0a8678ff000000007e0055550000000000000000"),
|
||||
bytes.fromhex("957b15f0f0ffff03898aff0c0a8678ff000000007e0055550000000000000000"),
|
||||
),
|
||||
"left": (
|
||||
bytes.fromhex("851828f0f0ffff03898aff0a098678ff000000007e0055550000000000000000"),
|
||||
bytes.fromhex("c34129f0f0ffff03898aff0a098678ff000000007e0055550000000000000000"),
|
||||
bytes.fromhex("09aa2af0f0ffff03898aff0a098678ff000000007e0055550000000000000000"),
|
||||
bytes.fromhex("4ff32bf0f0ffff03898aff0a098678ff000000007e0055550000000000000000"),
|
||||
bytes.fromhex("bc6d2cf0f0ffff03898aff0a098678ff000000007e0055550000000000000000"),
|
||||
bytes.fromhex("fa342df0f0ffff03898aff0a098678ff000000007e0055550000000000000000"),
|
||||
),
|
||||
}
|
||||
|
||||
IONIQ_6_CLUSTER_BLINDSPOT_3B5 = {
|
||||
"right": (
|
||||
bytes.fromhex("caa95c00000000464600000000000000d7020000000069070000000000000000"),
|
||||
bytes.fromhex("8cf05d00000000464600000000000000d7020000000069070000000000000000"),
|
||||
bytes.fromhex("461b5e00000000464600000000000000d7020000000069070000000000000000"),
|
||||
bytes.fromhex("00425f00000000464600000000000000d7020000000069070000000000000000"),
|
||||
),
|
||||
"left": (
|
||||
bytes.fromhex("2c69c500000000464600000000000000da020000000069070000000000000000"),
|
||||
bytes.fromhex("e682c600000000464600000000000000da020000000069070000000000000000"),
|
||||
bytes.fromhex("21afc800000000464600000000000000da020000000069070000000000000000"),
|
||||
bytes.fromhex("67f6c900000000464600000000000000da020000000069070000000000000000"),
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
IONIQ_6_CLUSTER_LANE_CHANGE_3C1 = {
|
||||
"right": {
|
||||
"trigger": bytes.fromhex("e910300041000000"),
|
||||
"steady": bytes.fromhex("ab20300001000000"),
|
||||
},
|
||||
"left": {
|
||||
"trigger": bytes.fromhex("3d40304010000000"),
|
||||
"steady": bytes.fromhex("3e50300000000000"),
|
||||
},
|
||||
}
|
||||
|
||||
# Captured from a stock Ioniq 6 route that shows the cluster lane-change animation
|
||||
# on ECAN after the trigger/hold 0x3C1 states above.
|
||||
IONIQ_6_CLUSTER_LANE_CHANGE_3B5 = {
|
||||
"right": (
|
||||
bytes.fromhex("9f687600000000464600000000000000d7020000000069070000000000000000"),
|
||||
bytes.fromhex("d9317700000000464600000000000000d7020000000069070000000000000000"),
|
||||
bytes.fromhex("58457800000000464600000000000000d7020000000069070000000000000000"),
|
||||
bytes.fromhex("1e1c7900000000464600000000000000d7020000000069070000000000000000"),
|
||||
bytes.fromhex("d4f77a00000000464600000000000000d7020000000069070000000000000000"),
|
||||
bytes.fromhex("92ae7b00000000464600000000000000d7020000000069070000000000000000"),
|
||||
bytes.fromhex("61307c00000000464600000000000000d7020000000069070000000000000000"),
|
||||
bytes.fromhex("27697d00000000464600000000000000d7020000000069070000000000000000"),
|
||||
bytes.fromhex("ed827e00000000464600000000000000d7020000000069070000000000000000"),
|
||||
bytes.fromhex("abdb7f00000000464600000000000000d7020000000069070000000000000000"),
|
||||
bytes.fromhex("dd978000000000464600000000000000d7020000000069070000000000000000"),
|
||||
bytes.fromhex("9bce8100000000464600000000000000d7020000000069070000000000000000"),
|
||||
bytes.fromhex("51258200000000464600000000000000d7020000000069070000000000000000"),
|
||||
bytes.fromhex("177c8300000000464600000000000000d7020000000069070000000000000000"),
|
||||
bytes.fromhex("e4e28400000000464600000000000000d7020000000069070000000000000000"),
|
||||
bytes.fromhex("18ba8500000000464600000000000000d8020000000069070000000000000000"),
|
||||
bytes.fromhex("68508600000000464600000000000000d7020000000069070000000000000000"),
|
||||
bytes.fromhex("94088700000000464600000000000000d8020000000069070000000000000000"),
|
||||
bytes.fromhex("157c8800000000464600000000000000d8020000000069070000000000000000"),
|
||||
bytes.fromhex("53258900000000464600000000000000d8020000000069070000000000000000"),
|
||||
bytes.fromhex("99ce8a00000000464600000000000000d8020000000069070000000000000000"),
|
||||
bytes.fromhex("df978b00000000464600000000000000d8020000000069070000000000000000"),
|
||||
bytes.fromhex("2c098c00000000464600000000000000d8020000000069070000000000000000"),
|
||||
bytes.fromhex("6a508d00000000464600000000000000d8020000000069070000000000000000"),
|
||||
bytes.fromhex("a0bb8e00000000464600000000000000d8020000000069070000000000000000"),
|
||||
bytes.fromhex("e6e28f00000000464600000000000000d8020000000069070000000000000000"),
|
||||
bytes.fromhex("a2529000000000464600000000000000d8020000000069070000000000000000"),
|
||||
bytes.fromhex("e40b9100000000464600000000000000d8020000000069070000000000000000"),
|
||||
bytes.fromhex("2ee09200000000464600000000000000d8020000000069070000000000000000"),
|
||||
bytes.fromhex("d2b89300000000464600000000000000d7020000000069070000000000000000"),
|
||||
bytes.fromhex("9b279400000000464600000000000000d8020000000069070000000000000000"),
|
||||
bytes.fromhex("677f9500000000464600000000000000d7020000000069070000000000000000"),
|
||||
bytes.fromhex("17959600000000464600000000000000d8020000000069070000000000000000"),
|
||||
bytes.fromhex("ebcd9700000000464600000000000000d7020000000069070000000000000000"),
|
||||
bytes.fromhex("d0b89800000000464600000000000000d8020000000069070000000000000000"),
|
||||
bytes.fromhex("96e19900000000464600000000000000d8020000000069070000000000000000"),
|
||||
bytes.fromhex("5c0a9a00000000464600000000000000d8020000000069070000000000000000"),
|
||||
bytes.fromhex("1a539b00000000464600000000000000d8020000000069070000000000000000"),
|
||||
bytes.fromhex("e9cd9c00000000464600000000000000d8020000000069070000000000000000"),
|
||||
bytes.fromhex("af949d00000000464600000000000000d8020000000069070000000000000000"),
|
||||
bytes.fromhex("657f9e00000000464600000000000000d8020000000069070000000000000000"),
|
||||
bytes.fromhex("23269f00000000464600000000000000d8020000000069070000000000000000"),
|
||||
bytes.fromhex("cc0fa000000000464600000000000000d8020000000069070000000000000000"),
|
||||
bytes.fromhex("bba6a100000000464600000000000000d9020000000069070000000000000000"),
|
||||
bytes.fromhex("714da200000000464600000000000000d9020000000069070000000000000000"),
|
||||
bytes.fromhex("3714a300000000464600000000000000d9020000000069070000000000000000"),
|
||||
),
|
||||
"left": (
|
||||
bytes.fromhex("e682c600000000464600000000000000da020000000069070000000000000000"),
|
||||
bytes.fromhex("d2dbc700000000464600000000000000d9020000000069070000000000000000"),
|
||||
bytes.fromhex("21afc800000000464600000000000000da020000000069070000000000000000"),
|
||||
bytes.fromhex("67f6c900000000464600000000000000da020000000069070000000000000000"),
|
||||
bytes.fromhex("ad1dca00000000464600000000000000da020000000069070000000000000000"),
|
||||
bytes.fromhex("eb44cb00000000464600000000000000da020000000069070000000000000000"),
|
||||
bytes.fromhex("18dacc00000000464600000000000000da020000000069070000000000000000"),
|
||||
bytes.fromhex("5e83cd00000000464600000000000000da020000000069070000000000000000"),
|
||||
bytes.fromhex("9468ce00000000464600000000000000da020000000069070000000000000000"),
|
||||
bytes.fromhex("d231cf00000000464600000000000000da020000000069070000000000000000"),
|
||||
bytes.fromhex("9681d000000000464600000000000000da020000000069070000000000000000"),
|
||||
bytes.fromhex("d0d8d100000000464600000000000000da020000000069070000000000000000"),
|
||||
bytes.fromhex("1a33d200000000464600000000000000da020000000069070000000000000000"),
|
||||
bytes.fromhex("5c6ad300000000464600000000000000da020000000069070000000000000000"),
|
||||
bytes.fromhex("ddf4d400000000464600000000000000d9020000000069070000000000000000"),
|
||||
bytes.fromhex("e9add500000000464600000000000000da020000000069070000000000000000"),
|
||||
bytes.fromhex("2346d600000000464600000000000000da020000000069070000000000000000"),
|
||||
bytes.fromhex("651fd700000000464600000000000000da020000000069070000000000000000"),
|
||||
bytes.fromhex("e46bd800000000464600000000000000da020000000069070000000000000000"),
|
||||
bytes.fromhex("d032d900000000464600000000000000d9020000000069070000000000000000"),
|
||||
bytes.fromhex("68d9da00000000464600000000000000da020000000069070000000000000000"),
|
||||
bytes.fromhex("2e80db00000000464600000000000000da020000000069070000000000000000"),
|
||||
bytes.fromhex("dd1edc00000000464600000000000000da020000000069070000000000000000"),
|
||||
bytes.fromhex("9b47dd00000000464600000000000000da020000000069070000000000000000"),
|
||||
bytes.fromhex("51acde00000000464600000000000000da020000000069070000000000000000"),
|
||||
bytes.fromhex("17f5df00000000464600000000000000da020000000069070000000000000000"),
|
||||
bytes.fromhex("f8dce000000000464600000000000000da020000000069070000000000000000"),
|
||||
bytes.fromhex("be85e100000000464600000000000000da020000000069070000000000000000"),
|
||||
bytes.fromhex("746ee200000000464600000000000000da020000000069070000000000000000"),
|
||||
),
|
||||
}
|
||||
|
||||
IONIQ_6_CLUSTER_LANE_CHANGE_31A = {
|
||||
"right": (
|
||||
bytes.fromhex("eb4518f0f0ffff03898aff0a098678ff000000007e0055550000000000000000"),
|
||||
bytes.fromhex("757119f0f0ffff03898aff0a088678ff000000007e0055550000000000000000"),
|
||||
bytes.fromhex("bf9a1af0f0ffff03898aff0a088678ff000000007e0055550000000000000000"),
|
||||
bytes.fromhex("f9c31bf0f0ffff03898aff0a088678ff000000007e0055550000000000000000"),
|
||||
bytes.fromhex("0a5d1cf0f0ffff03898aff0a088678ff000000007e0055550000000000000000"),
|
||||
bytes.fromhex("4c041df0f0ffff03898aff0a088678ff000000007e0055550000000000000000"),
|
||||
bytes.fromhex("86ef1ef0f0ffff03898aff0a088678ff000000007e0055550000000000000000"),
|
||||
bytes.fromhex("18db1ff0f0ffff03898aff0a098678ff000000007e0055550000000000000000"),
|
||||
bytes.fromhex("f7f220f0f0ffff03898aff0a098678ff000000007e0055550000000000000000"),
|
||||
),
|
||||
"left": (
|
||||
bytes.fromhex("851828f0f0ffff03898aff0a098678ff000000007e0055550000000000000000"),
|
||||
bytes.fromhex("c34129f0f0ffff03898aff0a098678ff000000007e0055550000000000000000"),
|
||||
bytes.fromhex("09aa2af0f0ffff03898aff0a098678ff000000007e0055550000000000000000"),
|
||||
bytes.fromhex("4ff32bf0f0ffff03898aff0a098678ff000000007e0055550000000000000000"),
|
||||
bytes.fromhex("bc6d2cf0f0ffff03898aff0a098678ff000000007e0055550000000000000000"),
|
||||
bytes.fromhex("fa342df0f0ffff03898aff0a098678ff000000007e0055550000000000000000"),
|
||||
),
|
||||
}
|
||||
|
||||
IONIQ_6_CLUSTER_LANE_CHANGE_3C1_BURST = {
|
||||
0: "trigger",
|
||||
4: "trigger",
|
||||
7: "steady",
|
||||
10: "steady",
|
||||
13: "steady",
|
||||
16: "steady",
|
||||
}
|
||||
|
||||
IONIQ_6_CLUSTER_LANE_CHANGE_3C1_STEADY_START = 34
|
||||
IONIQ_6_CLUSTER_LANE_CHANGE_3B5_START = 4
|
||||
IONIQ_6_CLUSTER_LANE_CHANGE_31A_START = 30
|
||||
|
||||
|
||||
def create_ioniq_6_cluster_blindspot_messages(CAN, frame, left_blindspot=False, right_blindspot=False,
|
||||
left_blinker=False, right_blinker=False):
|
||||
side = None
|
||||
if left_blindspot and not right_blindspot:
|
||||
side = "left"
|
||||
elif right_blindspot and not left_blindspot:
|
||||
side = "right"
|
||||
elif left_blindspot and right_blindspot:
|
||||
if left_blinker and not right_blinker:
|
||||
side = "left"
|
||||
elif right_blinker and not left_blinker:
|
||||
side = "right"
|
||||
|
||||
if side is None:
|
||||
return []
|
||||
|
||||
ret = []
|
||||
if frame % 20 == 0:
|
||||
seq_3b5 = IONIQ_6_CLUSTER_BLINDSPOT_3B5[side]
|
||||
ret.append((0x3B5, seq_3b5[(frame // 20) % len(seq_3b5)], CAN.ECAN))
|
||||
if frame % 100 == 0:
|
||||
seq_31a = IONIQ_6_CLUSTER_BLINDSPOT_31A[side]
|
||||
ret.append((0x31A, seq_31a[(frame // 100) % len(seq_31a)], CAN.ECAN))
|
||||
|
||||
return ret
|
||||
|
||||
|
||||
def create_ioniq_6_cluster_lane_change_messages(CAN, frame, side=None):
|
||||
if side not in IONIQ_6_CLUSTER_LANE_CHANGE_3C1:
|
||||
return []
|
||||
|
||||
ret = []
|
||||
frame_phase = IONIQ_6_CLUSTER_LANE_CHANGE_3C1_BURST.get(frame)
|
||||
if frame_phase is None and frame >= IONIQ_6_CLUSTER_LANE_CHANGE_3C1_STEADY_START and \
|
||||
(frame - IONIQ_6_CLUSTER_LANE_CHANGE_3C1_STEADY_START) % 20 == 0:
|
||||
frame_phase = "steady"
|
||||
if frame_phase is not None:
|
||||
ret.append((0x3C1, IONIQ_6_CLUSTER_LANE_CHANGE_3C1[side][frame_phase], CAN.ECAN))
|
||||
|
||||
if frame >= IONIQ_6_CLUSTER_LANE_CHANGE_3B5_START and (frame - IONIQ_6_CLUSTER_LANE_CHANGE_3B5_START) % 20 == 0:
|
||||
seq_3b5 = IONIQ_6_CLUSTER_LANE_CHANGE_3B5[side]
|
||||
ret.append((0x3B5, seq_3b5[((frame - IONIQ_6_CLUSTER_LANE_CHANGE_3B5_START) // 20) % len(seq_3b5)], CAN.ECAN))
|
||||
if frame >= IONIQ_6_CLUSTER_LANE_CHANGE_31A_START and (frame - IONIQ_6_CLUSTER_LANE_CHANGE_31A_START) % 100 == 0:
|
||||
seq_31a = IONIQ_6_CLUSTER_LANE_CHANGE_31A[side]
|
||||
ret.append((0x31A, seq_31a[((frame - IONIQ_6_CLUSTER_LANE_CHANGE_31A_START) // 100) % len(seq_31a)], CAN.ECAN))
|
||||
|
||||
return ret
|
||||
|
||||
|
||||
def create_acc_control(packer, CAN, enabled, accel_last, accel, stopping, gas_override, set_speed, hud_control,
|
||||
main_mode_acc=1, jerk_lower=None, jerk_upper=None, direct_accel=False,
|
||||
lead_distance=None, lead_rel_speed=None, lead_visible=None):
|
||||
jerk = 5
|
||||
jn = jerk / 50
|
||||
if not enabled or gas_override:
|
||||
a_val, a_raw = 0, 0
|
||||
elif direct_accel:
|
||||
a_raw = accel
|
||||
a_val = accel
|
||||
else:
|
||||
a_raw = accel
|
||||
a_val = np.clip(accel, accel_last - jn, accel_last + jn)
|
||||
|
||||
if lead_distance is None and lead_rel_speed is None and lead_visible is None:
|
||||
acc_obj_dist = 1.0
|
||||
acc_obj_rel_spd = 0.0
|
||||
obj_valid = 0
|
||||
obj_status = 2
|
||||
else:
|
||||
lead_visible = bool(lead_visible)
|
||||
acc_obj_dist = float(np.clip(lead_distance if lead_visible else 0.0, 0.0, 204.7))
|
||||
acc_obj_rel_spd = float(np.clip(lead_rel_speed if lead_visible else 0.0, -16.4, 34.7))
|
||||
obj_valid = int(not lead_visible)
|
||||
obj_status = 0 if not (enabled and lead_visible) else (1 if gas_override else 2)
|
||||
|
||||
values = {
|
||||
"ACCMode": 0 if not enabled else (2 if gas_override else 1),
|
||||
"MainMode_ACC": 1,
|
||||
"MainMode_ACC": main_mode_acc,
|
||||
"StopReq": 1 if stopping else 0,
|
||||
"aReqValue": a_val,
|
||||
"aReqRaw": a_raw,
|
||||
"VSetDis": set_speed,
|
||||
"JerkLowerLimit": jerk if enabled else 1,
|
||||
"JerkUpperLimit": 3.0,
|
||||
"JerkLowerLimit": jerk_lower if jerk_lower is not None else (jerk if enabled else 1),
|
||||
"JerkUpperLimit": jerk_upper if jerk_upper is not None else 3.0,
|
||||
|
||||
"ACC_ObjDist": 1,
|
||||
"ObjValid": 0,
|
||||
"OBJ_STATUS": 2,
|
||||
"ACC_ObjDist": acc_obj_dist,
|
||||
"ACC_ObjRelSpd": acc_obj_rel_spd,
|
||||
"ObjValid": obj_valid,
|
||||
"OBJ_STATUS": obj_status,
|
||||
"SET_ME_2": 0x4,
|
||||
"SET_ME_3": 0x3,
|
||||
"SET_ME_TMP_64": 0x64,
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import time
|
||||
from opendbc.car import Bus, get_safety_config, structs, uds
|
||||
from opendbc.car.hyundai.hyundaicanfd import CanBus
|
||||
from opendbc.car.hyundai.values import HyundaiFlags, CAR, DBC, \
|
||||
from opendbc.car.hyundai.values import HyundaiFlags, CAR, DBC, CarControllerParams, \
|
||||
CANFD_UNSUPPORTED_LONGITUDINAL_CAR, \
|
||||
CANFD_SECURITYACCESS_CAR, \
|
||||
UNSUPPORTED_LONGITUDINAL_CAR, HyundaiSafetyFlags
|
||||
UNSUPPORTED_LONGITUDINAL_CAR, HyundaiSafetyFlags, \
|
||||
hyundai_cancel_button_enables_cruise
|
||||
from opendbc.car.hyundai.radar_interface import RADAR_START_ADDR
|
||||
from opendbc.car.interfaces import CarInterfaceBase
|
||||
from opendbc.car.interfaces import CarInterfaceBase, ACCEL_MIN
|
||||
from opendbc.car.disable_ecu import disable_ecu, ecu_log
|
||||
from opendbc.car.hyundai.carcontroller import CarController
|
||||
from opendbc.car.hyundai.carstate import CarState
|
||||
@@ -22,11 +23,34 @@ ENABLE_BUTTONS = (ButtonType.accelCruise, ButtonType.decelCruise, ButtonType.can
|
||||
ECU_DISABLE_TIMESTAMP = 0.0
|
||||
|
||||
|
||||
def apply_platform_longitudinal_params(ret: structs.CarParams) -> None:
|
||||
if not (ret.flags & HyundaiFlags.CANFD):
|
||||
return
|
||||
|
||||
ret.startingState = True
|
||||
ret.startAccel = 1.0
|
||||
ret.longitudinalActuatorDelay = 0.5
|
||||
ret.vEgoStopping = 0.3
|
||||
ret.vEgoStarting = 0.1
|
||||
ret.stoppingDecelRate = 0.4
|
||||
|
||||
|
||||
def apply_ecu_disable_failure_fallback(CP: structs.CarParams, params) -> None:
|
||||
params.put_bool("EcuDisableFailed", True)
|
||||
CP.safetyConfigs[-1].safetyParam &= ~HyundaiSafetyFlags.LONG.value
|
||||
CP.openpilotLongitudinalControl = False
|
||||
CP.pcmCruise = True
|
||||
|
||||
|
||||
class CarInterface(CarInterfaceBase):
|
||||
CarState = CarState
|
||||
CarController = CarController
|
||||
RadarInterface = RadarInterface
|
||||
|
||||
@staticmethod
|
||||
def get_pid_accel_limits(CP, current_speed, cruise_speed):
|
||||
return ACCEL_MIN, CarControllerParams.ACCEL_MAX
|
||||
|
||||
@staticmethod
|
||||
def _get_params(ret: structs.CarParams, candidate, fingerprint, car_fw, alpha_long, is_release, docs) -> structs.CarParams:
|
||||
ret.brand = "hyundai"
|
||||
@@ -95,14 +119,14 @@ class CarInterface(CarInterfaceBase):
|
||||
else:
|
||||
# Shared configuration for non CAN-FD cars
|
||||
ret.alphaLongitudinalAvailable = candidate not in UNSUPPORTED_LONGITUDINAL_CAR
|
||||
ret.enableBsm = 0x58b in fingerprint[0]
|
||||
ret.enableBsm = 0x58b in fingerprint[CAN.ECAN]
|
||||
|
||||
# Send LFA message on cars with HDA
|
||||
if 0x485 in fingerprint[2]:
|
||||
if 0x485 in fingerprint[CAN.CAM]:
|
||||
ret.flags |= HyundaiFlags.SEND_LFA.value
|
||||
|
||||
# These cars use the FCA11 message for the AEB and FCW signals, all others use SCC12
|
||||
if 0x38d in fingerprint[0] or 0x38d in fingerprint[2]:
|
||||
if 0x38d in fingerprint[CAN.ECAN] or 0x38d in fingerprint[CAN.CAM]:
|
||||
ret.flags |= HyundaiFlags.USE_FCA.value
|
||||
|
||||
if ret.flags & HyundaiFlags.LEGACY:
|
||||
@@ -115,14 +139,12 @@ class CarInterface(CarInterfaceBase):
|
||||
ret.safetyConfigs[0].safetyParam |= HyundaiSafetyFlags.CAMERA_SCC.value
|
||||
|
||||
# These cars have the LFA button on the steering wheel
|
||||
if 0x391 in fingerprint[0]:
|
||||
if 0x391 in fingerprint[0] or ret.flags & HyundaiFlags.CAN_CANFD_BLENDED:
|
||||
ret.flags |= HyundaiFlags.HAS_LDA_BUTTON.value
|
||||
|
||||
if candidate == CAR.KIA_FORTE:
|
||||
has_scc_fw = any(fw.ecu == Ecu.fwdRadar for fw in car_fw)
|
||||
has_scc_can = any(addr in fingerprint[bus] for bus in (0, 2) for addr in (0x420, 0x421))
|
||||
if not (has_scc_fw or has_scc_can):
|
||||
ret.flags |= HyundaiFlags.NON_SCC.value
|
||||
if ret.flags & HyundaiFlags.CAN_CANFD_BLENDED:
|
||||
ret.safetyConfigs[-1].safetyParam |= HyundaiSafetyFlags.CAN_CANFD_BLENDED.value
|
||||
if hyundai_cancel_button_enables_cruise(candidate):
|
||||
ret.safetyConfigs[-1].safetyParam |= HyundaiSafetyFlags.CANCEL_BTN_ENABLE.value
|
||||
|
||||
# Common lateral control setup
|
||||
|
||||
@@ -141,6 +163,9 @@ class CarInterface(CarInterfaceBase):
|
||||
# see https://github.com/commaai/opendbc/pull/1137/
|
||||
ret.dashcamOnly = True
|
||||
|
||||
if ret.flags & HyundaiFlags.NON_SCC:
|
||||
ret.safetyConfigs[-1].safetyParam |= HyundaiSafetyFlags.NON_SCC.value
|
||||
|
||||
# Common longitudinal control setup
|
||||
|
||||
ret.radarUnavailable = RADAR_START_ADDR not in fingerprint[1] or Bus.radar not in DBC[ret.carFingerprint]
|
||||
@@ -152,10 +177,7 @@ class CarInterface(CarInterfaceBase):
|
||||
if ret.openpilotLongitudinalControl:
|
||||
ret.radarUnavailable = True
|
||||
ret.pcmCruise = not ret.openpilotLongitudinalControl
|
||||
ret.startingState = True
|
||||
ret.vEgoStarting = 0.1
|
||||
ret.startAccel = 1.0
|
||||
ret.longitudinalActuatorDelay = 0.5
|
||||
apply_platform_longitudinal_params(ret)
|
||||
|
||||
if ret.openpilotLongitudinalControl:
|
||||
ret.safetyConfigs[-1].safetyParam |= HyundaiSafetyFlags.LONG.value
|
||||
@@ -168,6 +190,25 @@ class CarInterface(CarInterfaceBase):
|
||||
|
||||
# Car specific configuration overrides
|
||||
|
||||
if candidate == CAR.GENESIS_G90:
|
||||
ret.stoppingDecelRate = 0.55
|
||||
ret.vEgoStopping = 0.8
|
||||
|
||||
if candidate == CAR.HYUNDAI_PALISADE_2023:
|
||||
ret.startAccel = 1.3
|
||||
ret.stopAccel = -0.85
|
||||
ret.stoppingDecelRate = 0.35
|
||||
ret.vEgoStarting = 0.5
|
||||
ret.vEgoStopping = 0.35
|
||||
|
||||
if candidate == CAR.HYUNDAI_IONIQ_6:
|
||||
ret.longitudinalActuatorDelay = 0.6
|
||||
|
||||
if candidate == CAR.KIA_NIRO_PHEV_2022:
|
||||
ret.stopAccel = -1.4
|
||||
ret.stoppingDecelRate = 0.5
|
||||
ret.vEgoStopping = 0.7
|
||||
|
||||
if candidate == CAR.KIA_OPTIMA_G4_FL:
|
||||
ret.steerActuatorDelay = 0.2
|
||||
|
||||
@@ -197,7 +238,7 @@ class CarInterface(CarInterfaceBase):
|
||||
ecu_log(f"=== init() called: opLong={CP.openpilotLongitudinalControl}, flags=0x{CP.flags:x}, safetyParam={CP.safetyConfigs[-1].safetyParam} ===")
|
||||
|
||||
if CP.openpilotLongitudinalControl and not (CP.flags & (HyundaiFlags.CANFD_CAMERA_SCC | HyundaiFlags.CAMERA_SCC)):
|
||||
addr, bus = 0x7d0, CanBus(CP).ECAN if CP.flags & HyundaiFlags.CANFD else 0
|
||||
addr, bus = 0x7d0, CanBus(CP).ECAN if CP.flags & (HyundaiFlags.CANFD | HyundaiFlags.CAN_CANFD_BLENDED) else 0
|
||||
if CP.flags & HyundaiFlags.CANFD_LKA_STEERING.value:
|
||||
addr, bus = 0x730, CanBus(CP).ECAN
|
||||
|
||||
@@ -205,7 +246,8 @@ class CarInterface(CarInterfaceBase):
|
||||
# If it fails (READY mode returns NRC 0x22, or timeout), strip LONG safety flag
|
||||
# so panda forwards stock SCC messages normally (lateral-only mode).
|
||||
ecu_log(f"=== ECU DISABLE attempt: addr=0x{addr:x}, bus={bus} ===")
|
||||
ecu_disabled = disable_ecu(can_recv, can_send, bus=bus, addr=addr, com_cont_req=communication_control)
|
||||
ecu_disabled = disable_ecu(can_recv, can_send, bus=bus, addr=addr, com_cont_req=communication_control,
|
||||
reset=bool(CP.flags & HyundaiFlags.CAN_CANFD_BLENDED))
|
||||
|
||||
if CP.carFingerprint == CAR.HYUNDAI_IONIQ_6:
|
||||
# Ioniq 6: track success/failure to auto-switch between openpilot long and stock ACC
|
||||
@@ -215,15 +257,15 @@ class CarInterface(CarInterfaceBase):
|
||||
params.put_bool("ExperimentalMode", True)
|
||||
ecu_log("=== ECU DISABLE SUCCESS - Longitudinal + Experimental ENABLED ===")
|
||||
else:
|
||||
params.put_bool("EcuDisableFailed", True)
|
||||
CP.safetyConfigs[-1].safetyParam &= ~HyundaiSafetyFlags.LONG.value
|
||||
apply_ecu_disable_failure_fallback(CP, params)
|
||||
ecu_log(f"=== ECU DISABLE FAILED - safetyParam stripped to {CP.safetyConfigs[-1].safetyParam}, lateral-only mode ===")
|
||||
else:
|
||||
# Other cars: just log, don't change safety params or params store
|
||||
if ecu_disabled:
|
||||
params.put_bool("EcuDisableFailed", False)
|
||||
ecu_log("=== ECU DISABLE SUCCESS ===")
|
||||
else:
|
||||
ecu_log("=== ECU DISABLE FAILED ===")
|
||||
apply_ecu_disable_failure_fallback(CP, params)
|
||||
ecu_log(f"=== ECU DISABLE FAILED - safetyParam stripped to {CP.safetyConfigs[-1].safetyParam}, lateral-only mode ===")
|
||||
|
||||
# for blinkers
|
||||
if CP.flags & HyundaiFlags.ENABLE_BLINKERS:
|
||||
|
||||
@@ -3,7 +3,8 @@ import math
|
||||
from opendbc.can import CANParser
|
||||
from opendbc.car import Bus, structs
|
||||
from opendbc.car.interfaces import RadarInterfaceBase
|
||||
from opendbc.car.hyundai.values import DBC
|
||||
from opendbc.car.hyundai.values import CAR, DBC
|
||||
from openpilot.common.swaglog import cloudlog
|
||||
|
||||
RADAR_START_ADDR = 0x500
|
||||
RADAR_MSG_COUNT = 32
|
||||
@@ -27,9 +28,29 @@ class RadarInterface(RadarInterfaceBase):
|
||||
self.track_id = 0
|
||||
|
||||
self.radar_off_can = CP.radarUnavailable
|
||||
# Probe whether radar tracks still exist on the Ioniq 6 while OP long is active,
|
||||
# without changing planner behavior yet.
|
||||
self.ioniq_6_radar_probe = CP.carFingerprint == CAR.HYUNDAI_IONIQ_6 and CP.openpilotLongitudinalControl and self.radar_off_can
|
||||
self.ioniq_6_radar_probe_logged = False
|
||||
self.ioniq_6_radar_probe_updates = 0
|
||||
self.rcp = get_radar_can_parser(CP)
|
||||
|
||||
def update(self, can_strings):
|
||||
if self.ioniq_6_radar_probe and self.rcp is not None and not self.ioniq_6_radar_probe_logged:
|
||||
vls = self.rcp.update(can_strings)
|
||||
self.updated_messages.update(vls)
|
||||
self.ioniq_6_radar_probe_updates += 1
|
||||
|
||||
if self.trigger_msg in self.updated_messages:
|
||||
rr = self._update(self.updated_messages)
|
||||
cloudlog.warning(f"Ioniq 6 radar probe: saw {len(rr.points)} radar tracks with radarUnavailable forced on")
|
||||
self.updated_messages.clear()
|
||||
self.ioniq_6_radar_probe_logged = True
|
||||
elif self.ioniq_6_radar_probe_updates >= 500:
|
||||
cloudlog.warning("Ioniq 6 radar probe: no radar track frames observed after startup")
|
||||
self.ioniq_6_radar_probe_logged = True
|
||||
self.updated_messages.clear()
|
||||
|
||||
if self.radar_off_can or (self.rcp is None):
|
||||
return super().update(None)
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -6,16 +6,23 @@ from opendbc.car import ACCELERATION_DUE_TO_GRAVITY, Bus, CarSpecs, DbcDict, Pla
|
||||
from opendbc.car.lateral import AngleSteeringLimits, ISO_LATERAL_ACCEL, ISO_LATERAL_JERK
|
||||
from opendbc.car.common.conversions import Conversions as CV
|
||||
from opendbc.car.structs import CarParams
|
||||
from opendbc.car.docs_definitions import CarHarness, CarDocs, CarParts
|
||||
from opendbc.car.docs_definitions import CarHarness, CarDocs, CarParts, SupportType
|
||||
from opendbc.car.fw_query_definitions import FwQueryConfig, Request, p16
|
||||
|
||||
Ecu = CarParams.Ecu
|
||||
AVERAGE_ROAD_ROLL = 0.06 # conservative roll margin used by Hyundai CAN-FD angle steering safety
|
||||
SPORTAGE_HEV_2026_MAX_LATERAL_ACCEL = 3.6
|
||||
SPORTAGE_HEV_2026_BASE_LATERAL_JERK = 3.25
|
||||
SPORTAGE_HEV_2026_LOW_SPEED_JERK_BOOST = 0.55
|
||||
SPORTAGE_HEV_2026_LOW_SPEED_JERK_SPEED = 11.0
|
||||
SPORTAGE_HEV_2026_LOW_SPEED_JERK_WIDTH = 5.0
|
||||
SPORTAGE_HEV_2026_MAX_ANGLE_RATE = 6.5
|
||||
SPORTAGE_HEV_2026_STEER_ANGLE_MAX = 220.0
|
||||
|
||||
|
||||
class CarControllerParams:
|
||||
ACCEL_MIN = -3.5 # m/s
|
||||
ACCEL_MAX = 2.0 # m/s
|
||||
ACCEL_MAX = 3.5 # m/s
|
||||
ANGLE_LIMITS: AngleSteeringLimits = AngleSteeringLimits(
|
||||
180,
|
||||
([], []),
|
||||
@@ -53,17 +60,26 @@ class CarControllerParams:
|
||||
if CP.flags & HyundaiFlags.CANFD_ANGLE_STEERING:
|
||||
self.STEER_THRESHOLD = 175
|
||||
|
||||
# The Sportage angle port is rough at low speed on the higher global jerk limit.
|
||||
# Keep the branch-wide higher limit for other cars, but restore the older calmer
|
||||
# jerk ceiling on this port only.
|
||||
# The Sportage angle port still needs more authority in real turns than the
|
||||
# fully calmed branch-wide ceiling allows, but the old low-speed jerk boost
|
||||
# made the 3-20 degree band angry and ping-pongy as the car slowed down.
|
||||
# Split the difference:
|
||||
# - keep a calmer low-speed boost that fades out earlier
|
||||
# - give the car a little more true turn headroom through accel/rate limits
|
||||
if CP.carFingerprint == CAR.KIA_SPORTAGE_HEV_2026:
|
||||
sportage_low_speed_weight = min(max((SPORTAGE_HEV_2026_LOW_SPEED_JERK_SPEED - vEgoRaw) / SPORTAGE_HEV_2026_LOW_SPEED_JERK_WIDTH, 0.0), 1.0)
|
||||
sportage_lateral_jerk = SPORTAGE_HEV_2026_BASE_LATERAL_JERK + (SPORTAGE_HEV_2026_LOW_SPEED_JERK_BOOST * sportage_low_speed_weight)
|
||||
self.ANGLE_LIMITS = replace(self.ANGLE_LIMITS,
|
||||
MAX_LATERAL_JERK=3.0 + (ACCELERATION_DUE_TO_GRAVITY * AVERAGE_ROAD_ROLL))
|
||||
STEER_ANGLE_MAX=SPORTAGE_HEV_2026_STEER_ANGLE_MAX,
|
||||
MAX_LATERAL_ACCEL=SPORTAGE_HEV_2026_MAX_LATERAL_ACCEL + (ACCELERATION_DUE_TO_GRAVITY * AVERAGE_ROAD_ROLL),
|
||||
MAX_LATERAL_JERK=sportage_lateral_jerk + (ACCELERATION_DUE_TO_GRAVITY * AVERAGE_ROAD_ROLL),
|
||||
MAX_ANGLE_RATE=SPORTAGE_HEV_2026_MAX_ANGLE_RATE)
|
||||
|
||||
# To determine the limit for your car, find the maximum value that the stock LKAS will request.
|
||||
# If the max stock LKAS request is <384, add your car to this list.
|
||||
elif CP.carFingerprint in (CAR.GENESIS_G80, CAR.HYUNDAI_ELANTRA, CAR.HYUNDAI_ELANTRA_GT_I30, CAR.HYUNDAI_IONIQ,
|
||||
CAR.HYUNDAI_IONIQ_EV_LTD, CAR.HYUNDAI_SANTA_FE_PHEV_2022, CAR.HYUNDAI_SONATA_LF, CAR.KIA_FORTE, CAR.KIA_NIRO_PHEV,
|
||||
CAR.KIA_FORTE_2019_NON_SCC, CAR.KIA_FORTE_2021_NON_SCC,
|
||||
CAR.KIA_OPTIMA_H, CAR.KIA_OPTIMA_H_G4_FL, CAR.KIA_SORENTO):
|
||||
self.STEER_MAX = 255
|
||||
|
||||
@@ -78,6 +94,13 @@ class CarControllerParams:
|
||||
self.STEER_DELTA_UP = 2
|
||||
self.STEER_DELTA_DOWN = 3
|
||||
|
||||
elif CP.flags & HyundaiFlags.CAN_CANFD_BLENDED:
|
||||
self.STEER_MAX = 404
|
||||
self.STEER_DRIVER_ALLOWANCE = 50
|
||||
self.STEER_THRESHOLD = 150
|
||||
self.STEER_DELTA_UP = 2
|
||||
self.STEER_DELTA_DOWN = 3
|
||||
|
||||
# Default for most HKG
|
||||
else:
|
||||
self.STEER_MAX = 384
|
||||
@@ -95,6 +118,9 @@ class HyundaiSafetyFlags(IntFlag):
|
||||
FCEV_GAS = 256
|
||||
ALT_LIMITS_2 = 512
|
||||
CANFD_ANGLE_STEERING = 1024
|
||||
NON_SCC = 4096
|
||||
CAN_CANFD_BLENDED = 8192
|
||||
CANCEL_BTN_ENABLE = 16384
|
||||
|
||||
|
||||
class HyundaiStarPilotSafetyFlags(IntFlag):
|
||||
@@ -170,12 +196,26 @@ class HyundaiFlags(IntFlag):
|
||||
# Hyundai CAN-FD angle-based steering path used on newer ADAS platforms.
|
||||
CANFD_ANGLE_STEERING = 2 ** 28
|
||||
|
||||
# Palisade/Telluride 2023+ uses CAN routing with CAN-FD-style checksums.
|
||||
CAN_CANFD_BLENDED = 2 ** 29
|
||||
|
||||
# Non-SCC platforms do not all share the same FCA source or availability.
|
||||
NON_SCC_NO_FCA = 2 ** 30
|
||||
NON_SCC_RADAR_FCA = 2 ** 31
|
||||
|
||||
|
||||
@dataclass
|
||||
class HyundaiCarDocs(CarDocs):
|
||||
package: str = "Smart Cruise Control (SCC)"
|
||||
|
||||
|
||||
@dataclass
|
||||
class HyundaiNonSccCarDocs(CarDocs):
|
||||
package: str = "No Smart Cruise Control (Non-SCC)"
|
||||
support_type: SupportType = SupportType.COMMUNITY
|
||||
support_link: str = "community"
|
||||
|
||||
|
||||
@dataclass
|
||||
class HyundaiPlatformConfig(PlatformConfig):
|
||||
dbc_dict: DbcDict = field(default_factory=lambda: {Bus.pt: "hyundai_kia_generic"})
|
||||
@@ -187,6 +227,9 @@ class HyundaiPlatformConfig(PlatformConfig):
|
||||
if self.flags & HyundaiFlags.MIN_STEER_32_MPH:
|
||||
self.specs = self.specs.override(minSteerSpeed=32 * CV.MPH_TO_MS)
|
||||
|
||||
if self.flags & HyundaiFlags.CAN_CANFD_BLENDED:
|
||||
self.dbc_dict = {Bus.pt: "hyundai_palisade_2023_generated"}
|
||||
|
||||
|
||||
@dataclass
|
||||
class HyundaiCanFDPlatformConfig(PlatformConfig):
|
||||
@@ -196,6 +239,17 @@ class HyundaiCanFDPlatformConfig(PlatformConfig):
|
||||
self.flags |= HyundaiFlags.CANFD
|
||||
|
||||
|
||||
@dataclass
|
||||
class HyundaiNonSccPlatformConfig(PlatformConfig):
|
||||
dbc_dict: DbcDict = field(default_factory=lambda: {Bus.pt: "hyundai_kia_generic"})
|
||||
|
||||
def init(self):
|
||||
self.flags |= HyundaiFlags.NON_SCC
|
||||
|
||||
if self.flags & HyundaiFlags.MIN_STEER_32_MPH:
|
||||
self.specs = self.specs.override(minSteerSpeed=32 * CV.MPH_TO_MS)
|
||||
|
||||
|
||||
class CAR(Platforms):
|
||||
# Hyundai
|
||||
HYUNDAI_AZERA_6TH_GEN = HyundaiPlatformConfig(
|
||||
@@ -367,6 +421,16 @@ class CAR(Platforms):
|
||||
CarSpecs(mass=1999, wheelbase=2.9, steerRatio=15.6 * 1.15, tireStiffnessFactor=0.63),
|
||||
flags=HyundaiFlags.MANDO_RADAR | HyundaiFlags.CHECKSUM_CRC8,
|
||||
)
|
||||
HYUNDAI_PALISADE_2023 = HyundaiPlatformConfig(
|
||||
[
|
||||
HyundaiCarDocs("Hyundai Palisade (without HDA II) 2023-25", "Highway Driving Assist",
|
||||
car_parts=CarParts.common([CarHarness.hyundai_a])),
|
||||
HyundaiCarDocs("Kia Telluride (without HDA II) 2023-25", "Highway Driving Assist",
|
||||
car_parts=CarParts.common([CarHarness.hyundai_l])),
|
||||
],
|
||||
HYUNDAI_PALISADE.specs,
|
||||
flags=HyundaiFlags.CHECKSUM_CRC8 | HyundaiFlags.CAN_CANFD_BLENDED | HyundaiFlags.RADAR_SCC,
|
||||
)
|
||||
HYUNDAI_VELOSTER = HyundaiPlatformConfig(
|
||||
[HyundaiCarDocs("Hyundai Veloster 2019-20", min_enable_speed=5. * CV.MPH_TO_MS, car_parts=CarParts.common([CarHarness.hyundai_e]))],
|
||||
CarSpecs(mass=2917 * CV.LB_TO_KG, wheelbase=2.8, steerRatio=13.75 * 1.15, tireStiffnessFactor=0.5),
|
||||
@@ -389,7 +453,7 @@ class CAR(Platforms):
|
||||
HYUNDAI_IONIQ_6 = HyundaiCanFDPlatformConfig(
|
||||
[HyundaiCarDocs("Hyundai Ioniq 6 (with HDA II) 2023-24", "Highway Driving Assist II", car_parts=CarParts.common([CarHarness.hyundai_p]))],
|
||||
HYUNDAI_IONIQ_5.specs,
|
||||
flags=HyundaiFlags.EV | HyundaiFlags.CANFD_LKA_STEERING | HyundaiFlags.CANFD_LKA_STEERING_ALT | HyundaiFlags.CANFD_NO_RADAR_DISABLE,
|
||||
flags=HyundaiFlags.EV | HyundaiFlags.CANFD_NO_RADAR_DISABLE,
|
||||
)
|
||||
HYUNDAI_TUCSON_4TH_GEN = HyundaiCanFDPlatformConfig(
|
||||
[
|
||||
@@ -635,6 +699,57 @@ class CAR(Platforms):
|
||||
flags=HyundaiFlags.RADAR_SCC,
|
||||
)
|
||||
|
||||
# Hyundai non-SCC extensions
|
||||
HYUNDAI_BAYON_1ST_GEN_NON_SCC = HyundaiNonSccPlatformConfig(
|
||||
[HyundaiNonSccCarDocs("Hyundai Bayon Non-SCC 2021", car_parts=CarParts.common([CarHarness.hyundai_n]))],
|
||||
CarSpecs(mass=1150, wheelbase=2.58, steerRatio=13.27 * 1.15),
|
||||
flags=HyundaiFlags.CHECKSUM_CRC8,
|
||||
)
|
||||
HYUNDAI_ELANTRA_2022_NON_SCC = HyundaiNonSccPlatformConfig(
|
||||
[HyundaiNonSccCarDocs("Hyundai Elantra Non-SCC 2022", car_parts=CarParts.common([CarHarness.hyundai_k]))],
|
||||
HYUNDAI_ELANTRA_2021.specs,
|
||||
flags=HyundaiFlags.CHECKSUM_CRC8,
|
||||
)
|
||||
HYUNDAI_ELANTRA_HEV_2022_NON_SCC = HyundaiNonSccPlatformConfig(
|
||||
[HyundaiNonSccCarDocs("Hyundai Elantra Hybrid Non-SCC 2022", car_parts=CarParts.common([CarHarness.hyundai_k]))],
|
||||
HYUNDAI_ELANTRA_HEV_2021.specs,
|
||||
flags=HyundaiFlags.CHECKSUM_CRC8 | HyundaiFlags.HYBRID,
|
||||
)
|
||||
HYUNDAI_KONA_NON_SCC = HyundaiNonSccPlatformConfig(
|
||||
[HyundaiNonSccCarDocs("Hyundai Kona Non-SCC 2019", car_parts=CarParts.common([CarHarness.hyundai_b]))],
|
||||
HYUNDAI_KONA.specs,
|
||||
flags=HyundaiFlags.ALT_LIMITS,
|
||||
)
|
||||
HYUNDAI_KONA_EV_NON_SCC = HyundaiNonSccPlatformConfig(
|
||||
[HyundaiNonSccCarDocs("Hyundai Kona Electric Non-SCC 2019", car_parts=CarParts.common([CarHarness.hyundai_g]))],
|
||||
HYUNDAI_KONA_EV.specs,
|
||||
flags=HyundaiFlags.EV | HyundaiFlags.ALT_LIMITS,
|
||||
)
|
||||
KIA_CEED_PHEV_2022_NON_SCC = HyundaiNonSccPlatformConfig(
|
||||
[HyundaiNonSccCarDocs("Kia Ceed Plug-in Hybrid Non-SCC 2022", car_parts=CarParts.common([CarHarness.hyundai_i]))],
|
||||
CarSpecs(mass=1650, wheelbase=2.65, steerRatio=13.75, tireStiffnessFactor=0.5),
|
||||
flags=HyundaiFlags.HYBRID,
|
||||
)
|
||||
KIA_FORTE_2019_NON_SCC = HyundaiNonSccPlatformConfig(
|
||||
[HyundaiNonSccCarDocs("Kia Forte Non-SCC 2019", car_parts=CarParts.common([CarHarness.hyundai_g]))],
|
||||
KIA_FORTE.specs,
|
||||
flags=HyundaiFlags.NON_SCC_NO_FCA,
|
||||
)
|
||||
KIA_FORTE_2021_NON_SCC = HyundaiNonSccPlatformConfig(
|
||||
[HyundaiNonSccCarDocs("Kia Forte Non-SCC 2021", car_parts=CarParts.common([CarHarness.hyundai_g]))],
|
||||
KIA_FORTE.specs,
|
||||
)
|
||||
KIA_SELTOS_2023_NON_SCC = HyundaiNonSccPlatformConfig(
|
||||
[HyundaiNonSccCarDocs("Kia Seltos Non-SCC 2023-24", car_parts=CarParts.common([CarHarness.hyundai_l]))],
|
||||
KIA_SELTOS.specs,
|
||||
flags=HyundaiFlags.CHECKSUM_CRC8,
|
||||
)
|
||||
GENESIS_G70_2021_NON_SCC = HyundaiNonSccPlatformConfig(
|
||||
[HyundaiNonSccCarDocs("Genesis G70 Non-SCC 2021", car_parts=CarParts.common([CarHarness.hyundai_f]))],
|
||||
GENESIS_G70_2020.specs,
|
||||
flags=HyundaiFlags.CHECKSUM_CRC8 | HyundaiFlags.NON_SCC_RADAR_FCA,
|
||||
)
|
||||
|
||||
|
||||
class Buttons:
|
||||
NONE = 0
|
||||
@@ -644,6 +759,15 @@ class Buttons:
|
||||
CANCEL = 4 # on newer models, this is a pause/resume button
|
||||
|
||||
|
||||
CANCEL_BUTTON_ENABLE_CARS = frozenset({
|
||||
CAR.HYUNDAI_PALISADE_2023,
|
||||
})
|
||||
|
||||
|
||||
def hyundai_cancel_button_enables_cruise(car_fingerprint) -> bool:
|
||||
return car_fingerprint in CANCEL_BUTTON_ENABLE_CARS
|
||||
|
||||
|
||||
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)
|
||||
@@ -798,8 +922,6 @@ FW_QUERY_CONFIG = FwQueryConfig(
|
||||
# We lose these ECUs without the comma power on these cars.
|
||||
# Note that we still attempt to match with them when they are present
|
||||
non_essential_ecus={
|
||||
# Some Forte trims are lateral-only and omit the SCC radar entirely.
|
||||
Ecu.fwdRadar: [CAR.KIA_FORTE],
|
||||
Ecu.abs: [CAR.HYUNDAI_PALISADE, CAR.HYUNDAI_SONATA, CAR.HYUNDAI_SANTA_FE_2022, CAR.KIA_K5_2021, CAR.HYUNDAI_ELANTRA_2021,
|
||||
CAR.HYUNDAI_SANTA_FE, CAR.HYUNDAI_KONA_EV_2022, CAR.HYUNDAI_KONA_EV, CAR.HYUNDAI_CUSTIN_1ST_GEN, CAR.KIA_SORENTO,
|
||||
CAR.KIA_CEED, CAR.KIA_SELTOS],
|
||||
@@ -841,6 +963,8 @@ EV_CAR = CAR.with_flags(HyundaiFlags.EV)
|
||||
|
||||
LEGACY_SAFETY_MODE_CAR = CAR.with_flags(HyundaiFlags.LEGACY)
|
||||
|
||||
NON_SCC_CAR = CAR.with_flags(HyundaiFlags.NON_SCC)
|
||||
|
||||
# TODO: another PR with (HyundaiFlags.LEGACY | HyundaiFlags.UNSUPPORTED_LONGITUDINAL | HyundaiFlags.CAMERA_SCC |
|
||||
# HyundaiFlags.CANFD_RADAR_SCC | HyundaiFlags.CANFD_NO_RADAR_DISABLE | )
|
||||
UNSUPPORTED_LONGITUDINAL_CAR = CAR.with_flags(HyundaiFlags.LEGACY) | CAR.with_flags(HyundaiFlags.UNSUPPORTED_LONGITUDINAL)
|
||||
|
||||
@@ -18,7 +18,7 @@ from opendbc.car.common.basedir import BASEDIR
|
||||
from opendbc.car.common.conversions import Conversions as CV
|
||||
from opendbc.car.common.simple_kalman import KF1D, get_kalman_gain
|
||||
from opendbc.car.gm.values import CAR as GM
|
||||
from opendbc.car.honda.values import CAR as HONDA, HONDA_BOSCH, HONDA_CAMERA_MESSAGE_CARS, HondaSafetyFlags, HondaStarPilotFlags
|
||||
from opendbc.car.honda.values import CAR as HONDA, HONDA_BOSCH, HondaFlags, HondaSafetyFlags, HondaStarPilotFlags
|
||||
from opendbc.car.hyundai.hyundaicanfd import CanBus
|
||||
from opendbc.car.hyundai.values import CAR as HYUNDAI, CANFD_CAR, HyundaiFlags, HyundaiStarPilotFlags, HyundaiStarPilotSafetyFlags
|
||||
from opendbc.car.mock.values import CAR as MOCK
|
||||
@@ -26,6 +26,7 @@ from opendbc.car.toyota.values import CAR as TOYOTA, NO_DSU_CAR, TSS2_CAR, UNSUP
|
||||
from opendbc.car.values import PLATFORMS
|
||||
from opendbc.can import CANParser
|
||||
from openpilot.common.params import Params
|
||||
from openpilot.starpilot.common.testing_grounds import testing_ground
|
||||
|
||||
GearShifter = structs.CarState.GearShifter
|
||||
ButtonType = structs.CarState.ButtonEvent.Type
|
||||
@@ -182,7 +183,14 @@ class CarInterfaceBase(ABC):
|
||||
ret.tireStiffnessFront, ret.tireStiffnessRear = scale_tire_stiffness(ret.mass, ret.wheelbase, ret.centerToFront, ret.tireStiffnessFactor)
|
||||
|
||||
toggles_to_check = ("force_torque_controller", "nnff", "nnff_lite")
|
||||
if ret.steerControlType != structs.CarParams.SteerControlType.angle and any(getattr(starpilot_toggles, toggle, False) for toggle in toggles_to_check):
|
||||
modified_civic_force_torque = (
|
||||
candidate == HONDA.HONDA_CIVIC_BOSCH and
|
||||
bool(ret.flags & HondaFlags.EPS_MODIFIED)
|
||||
)
|
||||
if ret.steerControlType != structs.CarParams.SteerControlType.angle and (
|
||||
any(getattr(starpilot_toggles, toggle, False) for toggle in toggles_to_check) or
|
||||
modified_civic_force_torque
|
||||
):
|
||||
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
|
||||
|
||||
return ret
|
||||
@@ -190,6 +198,7 @@ class CarInterfaceBase(ABC):
|
||||
@classmethod
|
||||
def get_starpilot_params(cls, candidate: str, fingerprint: dict[int, dict[int, int]], car_fw: list[structs.CarParams.CarFw], CP: structs.CarParams, starpilot_toggles: SimpleNamespace):
|
||||
fp_ret = custom.StarPilotCarParams.new_message()
|
||||
fp_ret.pcmCruiseSpeed = True
|
||||
|
||||
platform = PLATFORMS[candidate]
|
||||
|
||||
@@ -207,8 +216,7 @@ class CarInterfaceBase(ABC):
|
||||
|
||||
elif platform in HONDA:
|
||||
fp_ret.canUsePedal = candidate not in HONDA_BOSCH
|
||||
# Only enable TSR parsing on Hondas confirmed to publish CAMERA_MESSAGES.
|
||||
if candidate in HONDA_CAMERA_MESSAGE_CARS:
|
||||
if any(0x35E in bus_fingerprint for bus_fingerprint in fingerprint.values()):
|
||||
fp_ret.flags |= int(HondaStarPilotFlags.HAS_CAMERA_MESSAGES)
|
||||
|
||||
elif platform in HYUNDAI:
|
||||
@@ -220,11 +228,15 @@ class CarInterfaceBase(ABC):
|
||||
if 0x1FA in fingerprint[CAN.ECAN]:
|
||||
fp_ret.flags |= HyundaiStarPilotFlags.SPEED_LIMIT_AVAILABLE.value
|
||||
|
||||
fp_ret.redneckCruiseAvailable = not bool(CP.flags & HyundaiFlags.CANFD_ALT_BUTTONS)
|
||||
if fp_ret.redneckCruiseAvailable and Params(return_defaults=True).get_bool("RedneckCruise") and \
|
||||
not CP.openpilotLongitudinalControl:
|
||||
fp_ret.pcmCruiseSpeed = False
|
||||
|
||||
if CP.flags & HyundaiFlags.HAS_LDA_BUTTON:
|
||||
fp_ret.safetyConfigs[-1].safetyParam |= HyundaiStarPilotSafetyFlags.HAS_LDA_BUTTON.value
|
||||
if starpilot_toggles.always_on_lateral_lkas:
|
||||
fp_ret.safetyConfigs[-1].safetyParam |= HyundaiStarPilotSafetyFlags.AOL_LKAS_ON_ENGAGE.value
|
||||
|
||||
elif platform in TOYOTA:
|
||||
fp_ret.canUsePedal = not CP.autoResumeSng
|
||||
fp_ret.canUseSDSU = candidate not in UNSUPPORTED_DSU_CAR and candidate not in TSS2_CAR
|
||||
@@ -239,6 +251,9 @@ class CarInterfaceBase(ABC):
|
||||
if 0x23 in fingerprint[0]:
|
||||
fp_ret.flags |= ToyotaStarPilotFlags.ZSS.value
|
||||
|
||||
elif platform.config.platform_str == "TESLA_MODEL_S_PREAP":
|
||||
fp_ret.canUsePedal = True
|
||||
|
||||
return fp_ret
|
||||
|
||||
@staticmethod
|
||||
|
||||
@@ -118,11 +118,13 @@ class CarState(CarStateBase):
|
||||
|
||||
fp_ret = custom.StarPilotCarState.new_message()
|
||||
|
||||
fp_ret.dashboardStopSign = 1 if cp_cam.vl["CAM_TRAFFIC_SIGNS"]["STOP_SIGN"] == 9 else 0
|
||||
|
||||
return ret, fp_ret
|
||||
|
||||
@staticmethod
|
||||
def get_can_parsers(CP):
|
||||
return {
|
||||
Bus.pt: CANParser(DBC[CP.carFingerprint][Bus.pt], [], 0),
|
||||
Bus.cam: CANParser(DBC[CP.carFingerprint][Bus.pt], [], 2),
|
||||
Bus.cam: CANParser(DBC[CP.carFingerprint][Bus.pt], [("CAM_TRAFFIC_SIGNS", 0)], 2),
|
||||
}
|
||||
|
||||
@@ -4,15 +4,14 @@ from opendbc.car import Bus
|
||||
from opendbc.car.lateral import apply_steer_angle_limits_vm
|
||||
from opendbc.car.interfaces import CarControllerBase
|
||||
from opendbc.car.tesla.teslacan import TeslaCAN
|
||||
from opendbc.car.tesla.values import CarControllerParams
|
||||
from opendbc.car.tesla.preap.carcontroller import PreAPLongController, init_preap_can
|
||||
from opendbc.car.tesla.preap.stock_cc_spoofer import StockCCSpoofer
|
||||
from opendbc.car.tesla.values import CANBUS, CAR, CarControllerParams
|
||||
from opendbc.car.vehicle_model import VehicleModel
|
||||
|
||||
|
||||
def get_safety_CP():
|
||||
# We use the TESLA_MODEL_Y platform for lateral limiting to match safety
|
||||
# A Model 3 at 40 m/s using the Model Y limits sees a <0.3% difference in max angle (from curvature factor)
|
||||
from opendbc.car.tesla.interface import CarInterface
|
||||
return CarInterface.get_non_essential_params("TESLA_MODEL_Y")
|
||||
return CarInterface.get_non_essential_params(CAR.TESLA_MODEL_Y)
|
||||
|
||||
|
||||
class CarController(CarControllerBase):
|
||||
@@ -21,11 +20,23 @@ class CarController(CarControllerBase):
|
||||
self.apply_angle_last = 0
|
||||
self.packer = CANPacker(dbc_names[Bus.party])
|
||||
self.tesla_can = TeslaCAN(self.packer)
|
||||
self.preap_long = None
|
||||
self.stock_cc = None
|
||||
|
||||
# Vehicle model used for lateral limiting
|
||||
self.VM = VehicleModel(get_safety_CP())
|
||||
|
||||
if CP.carFingerprint == CAR.TESLA_MODEL_S_PREAP:
|
||||
self.tesla_can = init_preap_can(dbc_names)
|
||||
self.preap_long = PreAPLongController()
|
||||
self.stock_cc = StockCCSpoofer()
|
||||
from opendbc.car.tesla.interface import CarInterface
|
||||
self.VM = VehicleModel(CarInterface.get_non_essential_params(CAR.TESLA_MODEL_S_PREAP))
|
||||
|
||||
def update(self, CC, CS, now_nanos, starpilot_toggles):
|
||||
if self.CP.carFingerprint == CAR.TESLA_MODEL_S_PREAP:
|
||||
return self._update_preap(CC, CS)
|
||||
|
||||
actuators = CC.actuators
|
||||
can_sends = []
|
||||
|
||||
@@ -64,3 +75,45 @@ class CarController(CarControllerBase):
|
||||
|
||||
self.frame += 1
|
||||
return new_actuators, can_sends
|
||||
|
||||
def _update_preap(self, CC, CS):
|
||||
actuators = CC.actuators
|
||||
can_sends = []
|
||||
lat_active = CC.latActive and CS.hands_on_level < 3
|
||||
|
||||
if CC.cruiseControl.cancel and CS.cruiseEnabled:
|
||||
CS.cruiseEnabled = False
|
||||
CS.enableLongControl = False
|
||||
CS.enableJustCC = False
|
||||
CS.pedal_speed_kph = 0.0
|
||||
CS.preap_cc_cancel_needed = True
|
||||
if hasattr(CS, "engagement"):
|
||||
CS.engagement.cruiseEnabled = False
|
||||
CS.engagement.enableLongControl = False
|
||||
CS.engagement.enableJustCC = False
|
||||
CS.engagement.pending_enable = False
|
||||
CS.engagement.pedal_speed_kph = 0.0
|
||||
|
||||
if self.frame % 2 == 0:
|
||||
self.apply_angle_last = apply_steer_angle_limits_vm(
|
||||
actuators.steeringAngleDeg, self.apply_angle_last, CS.out.vEgoRaw, CS.out.steeringAngleDeg,
|
||||
lat_active, CarControllerParams, self.VM,
|
||||
)
|
||||
cntr = (self.frame // 2) % 16
|
||||
can_sends.append(self.tesla_can.create_steering_control(cntr, self.apply_angle_last, lat_active))
|
||||
can_sends.append(self.tesla_can.create_epas_control(cntr, 1))
|
||||
|
||||
CS.pccEvent = None
|
||||
if self.CP.openpilotLongitudinalControl and self.preap_long is not None:
|
||||
can_sends.extend(self.preap_long.update(CC, CS, self.frame, self.tesla_can, CANBUS.party))
|
||||
|
||||
if self.stock_cc is not None:
|
||||
can_sends.extend(self.stock_cc.update(CS, self.frame, self.tesla_can, CANBUS.party))
|
||||
if self.stock_cc.pcc_event:
|
||||
CS.pccEvent = self.stock_cc.pcc_event
|
||||
|
||||
new_actuators = actuators.as_builder()
|
||||
new_actuators.steeringAngleDeg = self.apply_angle_last
|
||||
|
||||
self.frame += 1
|
||||
return new_actuators, can_sends
|
||||
|
||||
@@ -5,6 +5,10 @@ from opendbc.car import Bus, structs
|
||||
from opendbc.car.common.conversions import Conversions as CV
|
||||
from opendbc.car.interfaces import CarStateBase
|
||||
from opendbc.car.tesla.values import DBC, CANBUS, GEAR_MAP, STEER_THRESHOLD, CAR
|
||||
from opendbc.car.tesla.preap.carstate import get_preap_can_parsers, update_preap
|
||||
from opendbc.car.tesla.preap.engagement import PreAPEngagement
|
||||
from opendbc.car.tesla.preap.nap_conf import nap_conf
|
||||
from opendbc.car.tesla.preap.pedal_feedback import PedalFeedback
|
||||
|
||||
ButtonType = structs.CarState.ButtonEvent.Type
|
||||
|
||||
@@ -13,7 +17,8 @@ class CarState(CarStateBase):
|
||||
def __init__(self, CP, FPCP):
|
||||
super().__init__(CP, FPCP)
|
||||
self.can_define = CANDefine(DBC[CP.carFingerprint][Bus.party])
|
||||
self.shifter_values = self.can_define.dv["DI_systemStatus"]["DI_gear"]
|
||||
self.shifter_values = self.can_define.dv["DI_systemStatus"]["DI_gear"] if CP.carFingerprint != CAR.TESLA_MODEL_S_PREAP else \
|
||||
self.can_define.dv["DI_torque2"]["DI_gear"]
|
||||
|
||||
self.autopark = False
|
||||
self.autopark_prev = False
|
||||
@@ -21,6 +26,25 @@ class CarState(CarStateBase):
|
||||
|
||||
self.hands_on_level = 0
|
||||
self.das_control = None
|
||||
self.cruise_buttons = 0
|
||||
self.prev_cruise_buttons = 0
|
||||
self.msg_stw_actn_req = None
|
||||
self.speed_units = "MPH"
|
||||
|
||||
if CP.carFingerprint == CAR.TESLA_MODEL_S_PREAP:
|
||||
self.engagement = PreAPEngagement(nap_conf.double_pull_enabled, nap_conf.double_pull_window_ms)
|
||||
self.cruiseEnabled = False
|
||||
self.enableLongControl = False
|
||||
self.enableJustCC = False
|
||||
self.pedal_speed_kph = 0.0
|
||||
self.prev_stalk_follow = 0
|
||||
self.pccEvent = None
|
||||
self.preap_cc_cancel_needed = False
|
||||
self.preap_cc_engage_needed = False
|
||||
self.di_cruise_state = "OFF"
|
||||
self.pedal = PedalFeedback()
|
||||
self.pedal_interceptor_value = 0.0
|
||||
self.pedal_timeout = True
|
||||
|
||||
def update_autopark_state(self, autopark_state: str, cruise_enabled: bool):
|
||||
autopark_now = autopark_state in ("ACTIVE", "COMPLETE", "SELFPARK_STARTED")
|
||||
@@ -31,7 +55,15 @@ class CarState(CarStateBase):
|
||||
self.autopark_prev = autopark_now
|
||||
self.cruise_enabled_prev = cruise_enabled
|
||||
|
||||
def update_button_enable(self, buttonEvents: list[structs.CarState.ButtonEvent]):
|
||||
if self.CP.carFingerprint == CAR.TESLA_MODEL_S_PREAP:
|
||||
return False
|
||||
return super().update_button_enable(buttonEvents)
|
||||
|
||||
def update(self, can_parsers, starpilot_toggles) -> structs.CarState:
|
||||
if self.CP.carFingerprint == CAR.TESLA_MODEL_S_PREAP:
|
||||
return update_preap(self, can_parsers)
|
||||
|
||||
cp_party = can_parsers[Bus.party]
|
||||
cp_ap_party = can_parsers[Bus.ap_party]
|
||||
ret = structs.CarState()
|
||||
@@ -124,6 +156,8 @@ class CarState(CarStateBase):
|
||||
|
||||
@staticmethod
|
||||
def get_can_parsers(CP):
|
||||
if CP.carFingerprint == CAR.TESLA_MODEL_S_PREAP:
|
||||
return get_preap_can_parsers(CP)
|
||||
return {
|
||||
Bus.party: CANParser(DBC[CP.carFingerprint][Bus.party], [], CANBUS.party),
|
||||
Bus.ap_party: CANParser(DBC[CP.carFingerprint][Bus.party], [], CANBUS.autopilot_party)
|
||||
|
||||
@@ -45,3 +45,22 @@ FW_VERSIONS = {
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
FINGERPRINTS = {
|
||||
CAR.TESLA_MODEL_S_PREAP: [
|
||||
{
|
||||
1: 8, 3: 8, 14: 8, 21: 4, 69: 8, 109: 4, 257: 3, 264: 8, 277: 6, 280: 6, 293: 4, 296: 4,
|
||||
309: 5, 325: 8, 336: 8, 341: 8, 360: 7, 373: 8, 389: 8, 415: 8, 513: 5, 516: 8, 520: 4,
|
||||
522: 8, 524: 8, 527: 8, 536: 8, 551: 4, 552: 2, 556: 8, 568: 8, 582: 5, 638: 8, 643: 8,
|
||||
693: 8, 696: 8, 712: 8, 728: 8, 744: 8, 760: 8, 771: 2, 772: 8, 775: 8, 776: 8, 778: 8,
|
||||
780: 2, 783: 8, 785: 8, 787: 8, 788: 8, 791: 8, 792: 8, 796: 2, 799: 8, 804: 8, 805: 8,
|
||||
807: 8, 808: 1, 812: 8, 815: 8, 820: 8, 823: 8, 824: 8, 831: 8, 836: 8, 840: 8, 856: 4,
|
||||
863: 8, 872: 8, 880: 8, 888: 8, 896: 8, 901: 6, 904: 3, 920: 8, 936: 8, 949: 8, 952: 8,
|
||||
953: 6, 968: 8, 984: 8, 1000: 8, 1006: 8, 1026: 8, 1028: 8, 1029: 8, 1030: 8, 1032: 1,
|
||||
1034: 8, 1048: 1, 1064: 8, 1080: 8, 1281: 8, 1285: 8, 1332: 8, 1335: 8, 1362: 6, 1368: 8,
|
||||
1412: 8, 1436: 8, 1456: 8, 1463: 8, 1476: 8, 1524: 8, 1527: 8, 1601: 8, 1605: 8, 1617: 8,
|
||||
1621: 8, 1800: 4, 1804: 8, 1812: 8, 1815: 8, 1816: 8, 1828: 8, 1831: 8, 1832: 8, 1840: 8,
|
||||
1848: 8, 1864: 8, 1880: 8, 1892: 8, 1896: 8, 1912: 8, 1960: 8, 1992: 8, 2008: 3, 2043: 5,
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
@@ -2,17 +2,29 @@ from opendbc.car import get_safety_config, structs
|
||||
from opendbc.car.interfaces import CarInterfaceBase
|
||||
from opendbc.car.tesla.carcontroller import CarController
|
||||
from opendbc.car.tesla.carstate import CarState
|
||||
from opendbc.car.tesla.radar_interface import RadarInterface
|
||||
from opendbc.car.tesla.values import TeslaSafetyFlags, CAR
|
||||
from opendbc.car.tesla.preap.interface import get_preap_accel_limits, get_preap_params
|
||||
|
||||
|
||||
class CarInterface(CarInterfaceBase):
|
||||
CarState = CarState
|
||||
CarController = CarController
|
||||
RadarInterface = RadarInterface
|
||||
|
||||
@staticmethod
|
||||
def get_pid_accel_limits(CP, current_speed, cruise_speed):
|
||||
if CP.carFingerprint == CAR.TESLA_MODEL_S_PREAP:
|
||||
return get_preap_accel_limits(current_speed)
|
||||
return CarInterfaceBase.get_pid_accel_limits(CP, current_speed, cruise_speed)
|
||||
|
||||
@staticmethod
|
||||
def _get_params(ret: structs.CarParams, candidate, fingerprint, car_fw, alpha_long, is_release, docs) -> structs.CarParams:
|
||||
ret.brand = "tesla"
|
||||
|
||||
if candidate == CAR.TESLA_MODEL_S_PREAP:
|
||||
return get_preap_params(ret)
|
||||
|
||||
ret.safetyConfigs = [get_safety_config(structs.CarParams.SafetyModel.tesla)]
|
||||
|
||||
ret.steerLimitTimer = 0.4
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
from numpy import clip, interp
|
||||
|
||||
from opendbc.car.common.conversions import Conversions as CV
|
||||
from opendbc.car.tesla.preap.nap_conf import (
|
||||
ACCEL_MAX,
|
||||
PEDAL_BP,
|
||||
PEDAL_DI_MIN,
|
||||
PEDAL_DI_ZERO,
|
||||
PEDAL_MAX_VALUES,
|
||||
REGEN_MAX,
|
||||
nap_conf,
|
||||
)
|
||||
|
||||
PEDAL_RAMP_RATE_UP = 5.0
|
||||
PEDAL_RAMP_RATE_DOWN = 2.5
|
||||
ACCEL_DEADBAND = 0.15
|
||||
PEDAL_HYST_GAP = 1.0
|
||||
TORQUE_LEVEL_ACC = 0.0
|
||||
TORQUE_LEVEL_DECEL = -30.0
|
||||
ZERO_TORQUE_MIN_SPEED = 10.0 * CV.MPH_TO_MS
|
||||
|
||||
|
||||
class PedalZeroTorque:
|
||||
def __init__(self):
|
||||
self.value = PEDAL_DI_ZERO
|
||||
self._best_torque = TORQUE_LEVEL_DECEL
|
||||
|
||||
def update(self, torque_level: float, current_pedal_di: float, v_ego: float) -> None:
|
||||
if v_ego < ZERO_TORQUE_MIN_SPEED:
|
||||
return
|
||||
|
||||
if TORQUE_LEVEL_DECEL < torque_level < TORQUE_LEVEL_ACC and abs(torque_level) < abs(self._best_torque):
|
||||
self.value = current_pedal_di
|
||||
self._best_torque = torque_level
|
||||
|
||||
def get(self, v_ego: float) -> float:
|
||||
if v_ego < 5.0 * CV.MPH_TO_MS:
|
||||
return PEDAL_DI_ZERO
|
||||
return self.value
|
||||
|
||||
|
||||
_zero_torque = PedalZeroTorque()
|
||||
|
||||
|
||||
def get_zero_torque() -> PedalZeroTorque:
|
||||
return _zero_torque
|
||||
|
||||
|
||||
def compute_pedal_command(accel_request: float, v_ego: float, prev_pedal_di: float) -> tuple[float, float]:
|
||||
max_pedal_value = float(interp(v_ego, PEDAL_BP, PEDAL_MAX_VALUES))
|
||||
zero_torque_di = _zero_torque.get(v_ego)
|
||||
|
||||
if abs(accel_request) < ACCEL_DEADBAND:
|
||||
accel_request = 0.0
|
||||
|
||||
pedal_di = float(interp(accel_request, [REGEN_MAX, 0.0, ACCEL_MAX], [PEDAL_DI_MIN, zero_torque_di, max_pedal_value]))
|
||||
pedal_di = float(clip(pedal_di, PEDAL_DI_MIN, max_pedal_value))
|
||||
pedal_di = float(clip(pedal_di, prev_pedal_di - PEDAL_RAMP_RATE_DOWN, prev_pedal_di + PEDAL_RAMP_RATE_UP))
|
||||
|
||||
if abs(pedal_di - prev_pedal_di) < PEDAL_HYST_GAP:
|
||||
pedal_di = prev_pedal_di
|
||||
|
||||
return nap_conf.di_to_pedal(pedal_di), pedal_di
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
import numpy as np
|
||||
|
||||
from opendbc.can import CANPacker
|
||||
from opendbc.car import Bus
|
||||
from opendbc.car.carlog import carlog
|
||||
from opendbc.car.tesla.pedal.controller import get_zero_torque
|
||||
from opendbc.car.tesla.preap.interface import get_preap_accel_limits
|
||||
from opendbc.car.tesla.preap.nap_conf import PEDAL_DI_MIN, PEDAL_DI_ZERO, nap_conf
|
||||
from opendbc.car.tesla.preap.teslacan import TeslaCANPreAP
|
||||
from opendbc.car.tesla.preap.virtual_das import VirtualDAS
|
||||
from opendbc.car.tesla.values import CANBUS, CruiseButtons
|
||||
|
||||
ENGAGE_GRACE_FRAMES = 50
|
||||
|
||||
|
||||
def init_preap_can(dbc_names):
|
||||
tesla_can = TeslaCANPreAP(CANPacker(dbc_names[Bus.party]))
|
||||
tesla_can.pedal_can_bus = nap_conf.pedal_can_bus
|
||||
return tesla_can
|
||||
|
||||
|
||||
class PreAPLongController:
|
||||
def __init__(self):
|
||||
self.prev_pedal_di = 0.0
|
||||
self.prev_requested_long = False
|
||||
self.preap_long_engage_frame = -1_000_000
|
||||
self.engage_a_max = 0.0
|
||||
self.vdas = VirtualDAS(dt=0.02)
|
||||
|
||||
def update(self, CC, CS, frame: int, tesla_can, can_bus_party: int):
|
||||
can_sends = []
|
||||
actuators = CC.actuators
|
||||
requested_long = CS.cruiseEnabled and CS.enableLongControl
|
||||
long_active = requested_long and CC.longActive
|
||||
use_pedal = nap_conf.use_pedal and self._pedal_ready()
|
||||
|
||||
if (not self.prev_requested_long) and requested_long:
|
||||
self.preap_long_engage_frame = frame
|
||||
zero_torque_di = get_zero_torque().get(CS.out.vEgo)
|
||||
self.prev_pedal_di = max(CS.pedal_interceptor_value, zero_torque_di)
|
||||
self.vdas.reset(a_init=0.0, pedal_di_init=self.prev_pedal_di)
|
||||
_, self.engage_a_max = get_preap_accel_limits(CS.out.vEgo)
|
||||
|
||||
if use_pedal:
|
||||
pedal_button_press = CS.cruise_buttons != CS.prev_cruise_buttons and CS.cruise_buttons != CruiseButtons.IDLE
|
||||
if ((not self.prev_requested_long) and requested_long) or (self.prev_requested_long and not requested_long) or pedal_button_press:
|
||||
CS.preap_cc_cancel_needed = True
|
||||
|
||||
self.prev_requested_long = requested_long
|
||||
|
||||
if frame % 2 == 0:
|
||||
if use_pedal:
|
||||
get_zero_torque().update(CS.pedal.torque_level, self.prev_pedal_di, CS.out.vEgo)
|
||||
|
||||
if requested_long and use_pedal:
|
||||
try:
|
||||
if CS.out.gasPressed:
|
||||
self.prev_pedal_di = max(CS.pedal_interceptor_value, PEDAL_DI_ZERO)
|
||||
can_sends.append(tesla_can.create_pedal_command(0, enable=0))
|
||||
elif long_active:
|
||||
accel_request = float(actuators.accel)
|
||||
engage_elapsed_frames = frame - self.preap_long_engage_frame
|
||||
in_engage_grace = engage_elapsed_frames < ENGAGE_GRACE_FRAMES
|
||||
if in_engage_grace:
|
||||
accel_request = max(0.0, min(accel_request, (engage_elapsed_frames / ENGAGE_GRACE_FRAMES) * self.engage_a_max))
|
||||
|
||||
self.prev_pedal_di = self.vdas.update(
|
||||
accel_request, CS.out.vEgo, self.prev_pedal_di, a_ego=CS.out.aEgo,
|
||||
freeze_integrator=in_engage_grace, orientation_ned=list(CC.orientationNED),
|
||||
)
|
||||
can_sends.append(tesla_can.create_pedal_command(nap_conf.di_to_pedal(self.prev_pedal_di), enable=1))
|
||||
if self.prev_pedal_di <= 0.95 * PEDAL_DI_MIN and not in_engage_grace:
|
||||
CS.pccEvent = "pedalMaxRegen"
|
||||
else:
|
||||
zero_torque_di = get_zero_torque().get(CS.out.vEgo)
|
||||
self.prev_pedal_di = zero_torque_di
|
||||
can_sends.append(tesla_can.create_pedal_command(nap_conf.di_to_pedal(zero_torque_di), enable=1))
|
||||
except Exception:
|
||||
carlog.exception("Pre-AP pedal command failed; sending disabled")
|
||||
can_sends.append(tesla_can.create_pedal_command(nap_conf.di_to_pedal(PEDAL_DI_ZERO), enable=0))
|
||||
self.prev_pedal_di = 0.0
|
||||
else:
|
||||
if nap_conf.use_pedal:
|
||||
can_sends.append(tesla_can.create_pedal_command(nap_conf.di_to_pedal(PEDAL_DI_ZERO), enable=0))
|
||||
self.prev_pedal_di = 0.0
|
||||
|
||||
return can_sends
|
||||
|
||||
@staticmethod
|
||||
def _pedal_ready() -> bool:
|
||||
return nap_conf.pedal_calibrated and abs(nap_conf.pedal_factor) > 1e-6
|
||||
@@ -0,0 +1,154 @@
|
||||
import copy
|
||||
import math
|
||||
import time
|
||||
|
||||
from cereal import custom
|
||||
from opendbc.can import CANParser
|
||||
from opendbc.car import Bus, structs
|
||||
from opendbc.car.common.conversions import Conversions as CV
|
||||
from opendbc.car.tesla.preap.nap_params import NAPParamKeys
|
||||
from opendbc.car.tesla.preap.engagement import PreAPEngagement
|
||||
from opendbc.car.tesla.preap.nap_conf import PEDAL_DI_PRESSED, nap_conf
|
||||
from opendbc.car.tesla.preap.pedal_feedback import PedalFeedback
|
||||
from opendbc.car.tesla.values import CANBUS, DBC, GEAR_MAP, STEER_THRESHOLD
|
||||
|
||||
try:
|
||||
from openpilot.common.params import Params as _NAPParams
|
||||
_nap_params = _NAPParams()
|
||||
except ImportError:
|
||||
_nap_params = None
|
||||
|
||||
_DOORS = ("DOOR_STATE_FL", "DOOR_STATE_FR", "DOOR_STATE_RL", "DOOR_STATE_RR", "DOOR_STATE_FrontTrunk", "BOOT_STATE")
|
||||
|
||||
|
||||
def _current_time_millis() -> int:
|
||||
return int(round(time.time() * 1000))
|
||||
|
||||
|
||||
def update_preap(cs, can_parsers):
|
||||
cp_ap_party = can_parsers[Bus.ap_party]
|
||||
cp_pt = can_parsers[Bus.pt]
|
||||
cp_chassis = can_parsers[Bus.chassis]
|
||||
ret = structs.CarState()
|
||||
fp_ret = custom.StarPilotCarState.new_message()
|
||||
|
||||
ret.vEgoRaw = cp_chassis.vl["ESP_B"]["ESP_vehicleSpeed"] * CV.KPH_TO_MS
|
||||
ret.vEgo, ret.aEgo = cs.update_speed_kf(ret.vEgoRaw)
|
||||
ret.gasPressed = cp_pt.vl["DI_torque1"]["DI_pedalPos"] > PEDAL_DI_PRESSED
|
||||
real_brake_pressed = cp_chassis.vl["BrakeMessage"]["driverBrakeStatus"] == 2
|
||||
ret.brake = 0
|
||||
ret.brakePressed = real_brake_pressed
|
||||
|
||||
epas_status = cp_chassis.vl["EPAS_sysStatus"]
|
||||
cs.hands_on_level = epas_status["EPAS_handsOnLevel"]
|
||||
ret.steeringAngleDeg = -epas_status["EPAS_internalSAS"]
|
||||
ret.steeringRateDeg = -cp_chassis.vl["STW_ANGLHP_STAT"]["StW_AnglHP_Spd"]
|
||||
ret.steeringTorque = -epas_status["EPAS_torsionBarTorque"]
|
||||
ret.steeringPressed = cs.update_steering_pressed(abs(ret.steeringTorque) > STEER_THRESHOLD, 5)
|
||||
|
||||
eac_status = cs.can_define.dv["EPAS_sysStatus"]["EPAS_eacStatus"].get(int(epas_status["EPAS_eacStatus"]), None)
|
||||
ret.steerFaultPermanent = eac_status == "EAC_FAULT"
|
||||
ret.steerFaultTemporary = False
|
||||
|
||||
eac_error_code = cs.can_define.dv["EPAS_sysStatus"]["EPAS_eacErrorCode"].get(int(epas_status["EPAS_eacErrorCode"]), None)
|
||||
ret.steeringDisengage = cs.hands_on_level >= 3 or (eac_status == "EAC_INHIBITED" and eac_error_code in (
|
||||
"EAC_ERROR_HIGH_ANGLE_REQ", "EAC_ERROR_HIGH_ANGLE_RATE_REQ", "EAC_ERROR_HIGH_ANGLE_SAFETY", "EAC_ERROR_HIGH_ANGLE_RATE_SAFETY",
|
||||
))
|
||||
cs.engagement.handle_steering_disengage(ret.steeringDisengage)
|
||||
|
||||
cruise_state = cs.can_define.dv["DI_state"]["DI_cruiseState"].get(int(cp_chassis.vl["DI_state"]["DI_cruiseState"]), None)
|
||||
cs.di_cruise_state = cruise_state or "OFF"
|
||||
speed_units = cs.can_define.dv["DI_state"]["DI_speedUnits"].get(int(cp_chassis.vl["DI_state"]["DI_speedUnits"]), None)
|
||||
if speed_units is not None:
|
||||
cs.speed_units = speed_units
|
||||
|
||||
pedal_transform_valid = math.isfinite(nap_conf.pedal_factor) and abs(nap_conf.pedal_factor) > 1e-6
|
||||
use_pedal = nap_conf.use_pedal and cs.CP.openpilotLongitudinalControl and pedal_transform_valid
|
||||
pedal_long_allowed = use_pedal
|
||||
long_control_allowed = True if not use_pedal else pedal_long_allowed
|
||||
|
||||
ret.cruiseState.available = True
|
||||
if cs.enableLongControl and use_pedal:
|
||||
ret.cruiseState.speed = cs.pedal_speed_kph * CV.KPH_TO_MS
|
||||
elif speed_units == "KPH":
|
||||
ret.cruiseState.speed = max(cp_chassis.vl["DI_state"]["DI_digitalSpeed"] * CV.KPH_TO_MS, 1e-3)
|
||||
elif speed_units == "MPH":
|
||||
ret.cruiseState.speed = max(cp_chassis.vl["DI_state"]["DI_digitalSpeed"] * CV.MPH_TO_MS, 1e-3)
|
||||
ret.cruiseState.standstill = False
|
||||
ret.standstill = cruise_state == "STANDSTILL"
|
||||
ret.accFaulted = cruise_state == "FAULT"
|
||||
|
||||
ret.gearShifter = GEAR_MAP[cs.can_define.dv["DI_torque2"]["DI_gear"].get(int(cp_chassis.vl["DI_torque2"]["DI_gear"]), "DI_GEAR_INVALID")]
|
||||
ret.doorOpen = any((cs.can_define.dv["GTW_carState"][door].get(int(cp_chassis.vl["GTW_carState"][door]), "OPEN") == "OPEN") for door in _DOORS)
|
||||
ret.leftBlinker = cp_chassis.vl["GTW_carState"]["BC_indicatorLStatus"] == 1
|
||||
ret.rightBlinker = cp_chassis.vl["GTW_carState"]["BC_indicatorRStatus"] == 1
|
||||
ret.seatbeltUnlatched = False
|
||||
ret.stockAeb = False
|
||||
ret.stockLkas = False
|
||||
|
||||
cs.prev_cruise_buttons = cs.cruise_buttons
|
||||
cs.cruise_buttons = int(cp_chassis.vl["STW_ACTN_RQ"]["SpdCtrlLvr_Stat"])
|
||||
cs.msg_stw_actn_req = copy.copy(cp_chassis.vl["STW_ACTN_RQ"])
|
||||
|
||||
if _nap_params is not None:
|
||||
dtr_dist = int(cp_chassis.vl["STW_ACTN_RQ"]["DTR_Dist_Rq"])
|
||||
if dtr_dist != 255:
|
||||
stalk_follow = min((dtr_dist // 33) + 1, 7)
|
||||
if stalk_follow != cs.prev_stalk_follow:
|
||||
_nap_params.put(NAPParamKeys.FOLLOW_DISTANCE, str(stalk_follow))
|
||||
cs.prev_stalk_follow = stalk_follow
|
||||
|
||||
curr_time_ms = _current_time_millis()
|
||||
ret.buttonEvents = cs.engagement.process_buttons(
|
||||
cs.cruise_buttons, cs.prev_cruise_buttons, curr_time_ms, ret.vEgo, cs.speed_units,
|
||||
use_pedal, pedal_long_allowed, long_control_allowed, real_brake_pressed, cs.di_cruise_state,
|
||||
)
|
||||
ret.brakePressed = False
|
||||
|
||||
can_engage = cs.engagement.check_can_engage(ret.doorOpen, ret.gearShifter, ret.seatbeltUnlatched)
|
||||
ret.cruiseState.enabled = cs.engagement.cruiseEnabled and can_engage
|
||||
|
||||
cs.cruiseEnabled = cs.engagement.cruiseEnabled
|
||||
cs.enableLongControl = cs.engagement.enableLongControl
|
||||
cs.enableJustCC = cs.engagement.enableJustCC
|
||||
cs.pedal_speed_kph = cs.engagement.pedal_speed_kph
|
||||
cs.preap_cc_cancel_needed = cs.engagement.preap_cc_cancel_needed
|
||||
cs.preap_cc_engage_needed = cs.engagement.preap_cc_engage_needed
|
||||
|
||||
if nap_conf.use_pedal:
|
||||
gas_sensor = cp_ap_party.vl.get("GAS_SENSOR", {})
|
||||
cs.pedal.update(gas_sensor, curr_time_ms)
|
||||
cs.pedal.update_torque(cp_pt.vl.get("DI_torque1", {}))
|
||||
cs.pedal_interceptor_value = cs.pedal.interceptor_value
|
||||
cs.pedal_timeout = cs.pedal.timeout
|
||||
if use_pedal:
|
||||
ret.gasPressed = cs.pedal.gas_pressed
|
||||
|
||||
cs.das_control = None
|
||||
cs.cruise_enabled_prev = ret.cruiseState.enabled
|
||||
ret.pedalMaxRegen = cs.pccEvent == "pedalMaxRegen"
|
||||
ret.teslaCCEngaged = cs.pccEvent == "teslaCCEngaged"
|
||||
ret.teslaCCDisengaged = cs.pccEvent == "teslaCCDisengaged"
|
||||
ret.teslaCCNotArmed = (
|
||||
not nap_conf.use_pedal and
|
||||
cs.cruiseEnabled and
|
||||
cs.enableLongControl and
|
||||
cs.di_cruise_state not in ("STANDBY", "ENABLED")
|
||||
)
|
||||
ret.pedalLongActive = cs.enableLongControl and nap_conf.use_pedal
|
||||
return ret, fp_ret
|
||||
|
||||
|
||||
def get_preap_can_parsers(CP):
|
||||
chassis_messages = [
|
||||
("ESP_B", 0), ("BrakeMessage", 0), ("DI_state", 0), ("DI_torque2", 0),
|
||||
("GTW_carState", 0), ("GTW_epasControl", 0), ("STW_ANGLHP_STAT", 0), ("EPAS_sysStatus", 0), ("STW_ACTN_RQ", 0),
|
||||
]
|
||||
pt_messages = [("DI_torque1", 0)]
|
||||
pedal_messages = [("GAS_SENSOR", 50)] if nap_conf.use_pedal else []
|
||||
return {
|
||||
Bus.party: CANParser(DBC[CP.carFingerprint][Bus.party], [], CANBUS.party),
|
||||
Bus.ap_party: CANParser(DBC[CP.carFingerprint][Bus.party], pedal_messages, nap_conf.pedal_can_bus),
|
||||
Bus.pt: CANParser(DBC[CP.carFingerprint][Bus.pt], pt_messages, CANBUS.party),
|
||||
Bus.chassis: CANParser(DBC[CP.carFingerprint][Bus.chassis], chassis_messages, CANBUS.party),
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
ACCEL_PREAP_BP = [0.0, 1.3, 7.5, 15.0, 25.0, 40.0]
|
||||
ACCEL_PREAP_FOLLOW = [0.35, 0.55, 0.80, 0.75, 0.65, 0.50]
|
||||
ACCEL_PREAP_PROFILES = {
|
||||
0: [0.3, 0.8, 1.1, 1.0, 0.85, 0.7],
|
||||
1: [0.3, 0.7, 1.0, 0.9, 0.8, 0.65],
|
||||
2: [0.3, 0.6, 0.9, 0.8, 0.7, 0.55],
|
||||
}
|
||||
PEDAL_LONG_K_BP = [0.0, 3.0, 6.0, 35.0]
|
||||
PEDAL_LONG_KP_V = [0.0, 0.0, 0.0, 0.0]
|
||||
PEDAL_LONG_KI_V = [0.05, 0.08, 0.10, 0.15]
|
||||
VDAS_INNER_K_BP = [0.0, 5.0, 35.0]
|
||||
VDAS_INNER_KP_V = [0.0, 0.0, 0.0]
|
||||
VDAS_INNER_KI_V = [0.3, 0.2, 0.15]
|
||||
VDAS_FUTURE_T_BP = [2.0, 5.0]
|
||||
VDAS_FUTURE_T_V = [0.30, 0.55]
|
||||
VDAS_AEGO_FILTER_RC = 0.25
|
||||
@@ -0,0 +1,148 @@
|
||||
from opendbc.car import structs
|
||||
from opendbc.car.carlog import carlog
|
||||
from opendbc.car.common.conversions import Conversions as CV
|
||||
from opendbc.car.tesla.values import CruiseButtons
|
||||
|
||||
ButtonType = structs.CarState.ButtonEvent.Type
|
||||
CANCEL_ECHO_WINDOW_MS = 600
|
||||
SPOOF_ECHO_WINDOW_MS = 300
|
||||
|
||||
|
||||
class PreAPEngagement:
|
||||
def __init__(self, double_pull_enabled: bool, double_pull_window_ms: int):
|
||||
self.enableDoublePull = double_pull_enabled
|
||||
self.double_pull_window_ms = double_pull_window_ms
|
||||
self.cruiseEnabled = False
|
||||
self.enableLongControl = False
|
||||
self.enableJustCC = False
|
||||
self.pending_enable = False
|
||||
self.stalk_pull_time_ms = 0
|
||||
self.prev_stalk_pull_time_ms = -1000
|
||||
self.pedal_speed_kph = 0.0
|
||||
self.preap_cc_cancel_needed = False
|
||||
self.preap_cc_engage_needed = False
|
||||
self.preap_last_cc_spoof_ms = 0
|
||||
self.preap_brake_pressed_prev = False
|
||||
self.last_stalk_non_cancel_ms = -10000
|
||||
self.prev_steering_disengage = False
|
||||
|
||||
def handle_steering_disengage(self, steering_disengage: bool) -> None:
|
||||
if steering_disengage and not self.prev_steering_disengage:
|
||||
self.cruiseEnabled = False
|
||||
self.enableLongControl = False
|
||||
self.enableJustCC = False
|
||||
self.pending_enable = False
|
||||
self.pedal_speed_kph = 0.0
|
||||
self.stalk_pull_time_ms = 0
|
||||
self.prev_stalk_pull_time_ms = -1000
|
||||
self.prev_steering_disengage = steering_disengage
|
||||
|
||||
def process_buttons(self, cruise_buttons: int, prev_cruise_buttons: int, curr_time_ms: int, v_ego: float, speed_units: str,
|
||||
use_pedal: bool, pedal_long_allowed: bool, long_control_allowed: bool, real_brake_pressed: bool,
|
||||
di_cruise_state: str = "OFF") -> list[structs.CarState.ButtonEvent]:
|
||||
self.preap_cc_cancel_needed = False
|
||||
self.preap_cc_engage_needed = False
|
||||
button_events: list[structs.CarState.ButtonEvent] = []
|
||||
|
||||
if cruise_buttons == CruiseButtons.MAIN and prev_cruise_buttons != CruiseButtons.MAIN:
|
||||
if self.enableDoublePull:
|
||||
self._handle_double_pull(curr_time_ms, v_ego, speed_units, use_pedal, pedal_long_allowed, long_control_allowed, di_cruise_state)
|
||||
else:
|
||||
self.cruiseEnabled = True
|
||||
self.enableLongControl = long_control_allowed
|
||||
self.enableJustCC = not long_control_allowed
|
||||
self.pedal_speed_kph = self._capture_target_speed(v_ego, speed_units) if pedal_long_allowed else 0.0
|
||||
if not use_pedal and di_cruise_state == "STANDBY":
|
||||
self.preap_cc_engage_needed = True
|
||||
self.preap_last_cc_spoof_ms = curr_time_ms
|
||||
|
||||
if cruise_buttons != prev_cruise_buttons:
|
||||
button_events.append(self._make_button_event(cruise_buttons, prev_cruise_buttons, curr_time_ms, v_ego, speed_units, use_pedal))
|
||||
|
||||
if self.pending_enable and (curr_time_ms - self.stalk_pull_time_ms > self.double_pull_window_ms):
|
||||
self.pending_enable = False
|
||||
|
||||
brake_rising_edge = real_brake_pressed and not self.preap_brake_pressed_prev
|
||||
if use_pedal and brake_rising_edge and self.cruiseEnabled and self.enableLongControl:
|
||||
self.enableLongControl = False
|
||||
self.enableJustCC = True
|
||||
self.pending_enable = False
|
||||
self.pedal_speed_kph = 0.0
|
||||
self.preap_brake_pressed_prev = real_brake_pressed
|
||||
|
||||
return button_events
|
||||
|
||||
def check_can_engage(self, door_open: bool, gear_shifter, seatbelt_unlatched: bool) -> bool:
|
||||
can_engage = not door_open and gear_shifter == structs.CarState.GearShifter.drive and not seatbelt_unlatched
|
||||
if not can_engage:
|
||||
self.cruiseEnabled = False
|
||||
self.enableLongControl = False
|
||||
self.enableJustCC = False
|
||||
self.pending_enable = False
|
||||
return can_engage
|
||||
|
||||
def _handle_double_pull(self, curr_time_ms: int, v_ego: float, speed_units: str, use_pedal: bool,
|
||||
pedal_long_allowed: bool, long_control_allowed: bool, di_cruise_state: str) -> None:
|
||||
self.prev_stalk_pull_time_ms = self.stalk_pull_time_ms
|
||||
self.stalk_pull_time_ms = curr_time_ms
|
||||
double_pull = (self.stalk_pull_time_ms - self.prev_stalk_pull_time_ms) < self.double_pull_window_ms
|
||||
|
||||
self.cruiseEnabled = True
|
||||
self.pending_enable = False
|
||||
self.enableLongControl = long_control_allowed if double_pull else False
|
||||
self.enableJustCC = not self.enableLongControl
|
||||
self.pedal_speed_kph = self._capture_target_speed(v_ego, speed_units) if pedal_long_allowed and double_pull else 0.0
|
||||
|
||||
if double_pull:
|
||||
if not use_pedal:
|
||||
self.preap_cc_engage_needed = True
|
||||
self.preap_last_cc_spoof_ms = curr_time_ms
|
||||
else:
|
||||
self.pending_enable = True
|
||||
if not use_pedal:
|
||||
self.preap_cc_cancel_needed = True
|
||||
self.preap_last_cc_spoof_ms = curr_time_ms
|
||||
|
||||
def _make_button_event(self, cruise_buttons: int, prev_cruise_buttons: int, curr_time_ms: int,
|
||||
v_ego: float, speed_units: str, use_pedal: bool) -> structs.CarState.ButtonEvent:
|
||||
be = structs.CarState.ButtonEvent()
|
||||
be.pressed = cruise_buttons != CruiseButtons.IDLE
|
||||
state = cruise_buttons if be.pressed else prev_cruise_buttons
|
||||
|
||||
if state == CruiseButtons.MAIN:
|
||||
be.type = ButtonType.setCruise
|
||||
if be.pressed:
|
||||
self.last_stalk_non_cancel_ms = curr_time_ms
|
||||
elif state == CruiseButtons.CANCEL:
|
||||
is_echo = (self.cruiseEnabled and (curr_time_ms - self.last_stalk_non_cancel_ms) < CANCEL_ECHO_WINDOW_MS) or \
|
||||
((curr_time_ms - self.preap_last_cc_spoof_ms) < SPOOF_ECHO_WINDOW_MS)
|
||||
be.type = ButtonType.unknown if is_echo else ButtonType.cancel
|
||||
if not is_echo:
|
||||
self.cruiseEnabled = False
|
||||
self.enableLongControl = False
|
||||
self.enableJustCC = False
|
||||
self.pending_enable = False
|
||||
self.pedal_speed_kph = 0.0
|
||||
self.stalk_pull_time_ms = 0
|
||||
self.prev_stalk_pull_time_ms = -1000
|
||||
elif CruiseButtons.is_accel(state):
|
||||
be.type = ButtonType.accelCruise
|
||||
if be.pressed and use_pedal and self.enableLongControl:
|
||||
speed_uom_kph = CV.MPH_TO_KPH if speed_units == "MPH" else 1.0
|
||||
actual_kph = int(v_ego * CV.MS_TO_KPH / speed_uom_kph + 0.5) * speed_uom_kph
|
||||
self.pedal_speed_kph = min(max(self.pedal_speed_kph, actual_kph) + (5 * speed_uom_kph if state == CruiseButtons.RES_ACCEL_2ND else speed_uom_kph), 270.0)
|
||||
elif CruiseButtons.is_decel(state):
|
||||
be.type = ButtonType.decelCruise
|
||||
if be.pressed and use_pedal and self.enableLongControl:
|
||||
speed_uom_kph = CV.MPH_TO_KPH if speed_units == "MPH" else 1.0
|
||||
self.pedal_speed_kph = max(self.pedal_speed_kph - (5 * speed_uom_kph if state == CruiseButtons.DECEL_2ND else speed_uom_kph), 0.0)
|
||||
else:
|
||||
be.type = ButtonType.unknown
|
||||
|
||||
return be
|
||||
|
||||
@staticmethod
|
||||
def _capture_target_speed(v_ego: float, speed_units: str) -> float:
|
||||
speed_uom_kph = CV.MPH_TO_KPH if speed_units == "MPH" else 1.0
|
||||
return max(int(v_ego * CV.MS_TO_KPH / speed_uom_kph + 0.5) * speed_uom_kph, 0.0)
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
SPEED_BP = [0.0, 5.0, 12.0, 20.0, 30.0, 40.0]
|
||||
ACCEL_BP = [-1.5, -1.0, -0.5, 0.0, 0.5, 1.0, 1.5, 2.0, 2.5]
|
||||
DEFAULT_TABLE = [
|
||||
[-5.00, -3.33, -1.67, 0.00, 10.00, 20.00, 30.00, 40.00, 50.00],
|
||||
[-5.00, -3.33, -1.67, 0.00, 11.60, 23.20, 34.80, 46.40, 58.00],
|
||||
[-5.00, -3.33, -1.67, 0.00, 13.20, 26.40, 39.60, 52.80, 66.00],
|
||||
[-5.00, -3.33, -1.67, 0.00, 14.80, 29.60, 44.40, 59.20, 74.00],
|
||||
[-5.00, -3.33, -1.67, 0.00, 16.40, 32.80, 49.20, 65.60, 82.00],
|
||||
[-5.00, -3.33, -1.67, 0.00, 18.00, 36.00, 54.00, 72.00, 90.00],
|
||||
]
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
import numpy as np
|
||||
|
||||
from opendbc.car import get_safety_config, structs
|
||||
from opendbc.car.tesla.preap.constants import ACCEL_PREAP_BP, ACCEL_PREAP_PROFILES, PEDAL_LONG_K_BP, PEDAL_LONG_KI_V, PEDAL_LONG_KP_V
|
||||
from opendbc.car.tesla.preap.nap_conf import nap_conf
|
||||
|
||||
PREAP_FLAG_ENABLE_PEDAL = 1
|
||||
PREAP_FLAG_RADAR_EMULATION = 2
|
||||
PREAP_FLAG_RADAR_BEHIND_NOSECONE = 4
|
||||
|
||||
|
||||
def get_preap_accel_limits(current_speed: float) -> tuple[float, float]:
|
||||
try:
|
||||
from openpilot.common.params import Params
|
||||
personality = Params().get_int("LongitudinalPersonality")
|
||||
except Exception:
|
||||
personality = 1
|
||||
profile = ACCEL_PREAP_PROFILES.get(personality, ACCEL_PREAP_PROFILES[1])
|
||||
return -1.5, float(np.interp(current_speed, ACCEL_PREAP_BP, profile))
|
||||
|
||||
|
||||
def get_preap_params(ret: structs.CarParams) -> structs.CarParams:
|
||||
safety_flags = 0
|
||||
if nap_conf.use_pedal:
|
||||
safety_flags |= PREAP_FLAG_ENABLE_PEDAL
|
||||
if nap_conf.radar_enabled:
|
||||
safety_flags |= PREAP_FLAG_RADAR_EMULATION
|
||||
if nap_conf.radar_behind_nosecone:
|
||||
safety_flags |= PREAP_FLAG_RADAR_BEHIND_NOSECONE
|
||||
|
||||
use_pedal = nap_conf.use_pedal and nap_conf.pedal_calibrated
|
||||
ret.safetyConfigs = [get_safety_config(structs.CarParams.SafetyModel.teslaPreap, safety_flags)]
|
||||
ret.radarUnavailable = not nap_conf.radar_enabled
|
||||
ret.steerControlType = structs.CarParams.SteerControlType.angle
|
||||
ret.openpilotLongitudinalControl = use_pedal
|
||||
ret.pcmCruise = not use_pedal
|
||||
ret.alphaLongitudinalAvailable = False
|
||||
ret.steerLimitTimer = 0.4
|
||||
ret.steerActuatorDelay = 0.1
|
||||
ret.steerAtStandstill = True
|
||||
ret.vEgoStopping = 0.1
|
||||
ret.vEgoStarting = 0.1
|
||||
ret.stoppingDecelRate = 1.0
|
||||
|
||||
if use_pedal:
|
||||
ret.longitudinalTuning.kpBP = PEDAL_LONG_K_BP
|
||||
ret.longitudinalTuning.kpV = PEDAL_LONG_KP_V
|
||||
ret.longitudinalTuning.kiBP = PEDAL_LONG_K_BP
|
||||
ret.longitudinalTuning.kiV = PEDAL_LONG_KI_V
|
||||
try:
|
||||
ret.longitudinalTuning.kf = 1.0
|
||||
except AttributeError:
|
||||
pass
|
||||
ret.longitudinalActuatorDelay = 0.4
|
||||
|
||||
return ret
|
||||
@@ -0,0 +1,89 @@
|
||||
from openpilot.common.params import Params
|
||||
|
||||
from opendbc.car.tesla.preap.nap_params import NAPParamKeys
|
||||
|
||||
_params = Params()
|
||||
|
||||
PEDAL_DI_MIN = -5.0
|
||||
PEDAL_DI_ZERO = 0.0
|
||||
PEDAL_DI_PRESSED = 2.0
|
||||
ACCEL_MAX = 2.5
|
||||
REGEN_MAX = -1.5
|
||||
PEDAL_BP = [0.0, 5.0, 12.0, 20.0, 30.0, 40.0]
|
||||
PEDAL_MAX_VALUES = [50.0, 58.0, 66.0, 74.0, 82.0, 90.0]
|
||||
|
||||
|
||||
def transform_di_to_pedal(val: float, pedal_zero: float, pedal_factor: float) -> float:
|
||||
return pedal_zero + (val - PEDAL_DI_ZERO) / (pedal_factor or 1.0)
|
||||
|
||||
|
||||
def transform_pedal_to_di(val: float, pedal_zero: float, pedal_factor: float) -> float:
|
||||
return PEDAL_DI_ZERO + (val - pedal_zero) * (pedal_factor or 1.0)
|
||||
|
||||
|
||||
class NAPConf:
|
||||
@property
|
||||
def adaptive_accel(self) -> bool:
|
||||
return _params.get_bool(NAPParamKeys.ADAPTIVE_ACCEL)
|
||||
|
||||
@property
|
||||
def follow_distance(self) -> int:
|
||||
return max(1, min(7, _params.get_int(NAPParamKeys.FOLLOW_DISTANCE)))
|
||||
|
||||
@property
|
||||
def use_pedal(self) -> bool:
|
||||
return _params.get_bool(NAPParamKeys.PEDAL_ENABLED)
|
||||
|
||||
@property
|
||||
def pedal_calibrated(self) -> bool:
|
||||
if not _params.get_bool(NAPParamKeys.PEDAL_CALIB_DONE):
|
||||
return False
|
||||
return abs(self.pedal_factor) > 1e-6
|
||||
|
||||
@property
|
||||
def pedal_can_zero(self) -> bool:
|
||||
return _params.get_int(NAPParamKeys.PEDAL_CAN_BUS) == 0
|
||||
|
||||
@property
|
||||
def pedal_can_bus(self) -> int:
|
||||
return 0 if self.pedal_can_zero else 2
|
||||
|
||||
@property
|
||||
def pedal_factor(self) -> float:
|
||||
return _params.get_float(NAPParamKeys.PEDAL_CALIB_FACTOR)
|
||||
|
||||
@property
|
||||
def pedal_zero(self) -> float:
|
||||
return _params.get_float(NAPParamKeys.PEDAL_CALIB_ZERO)
|
||||
|
||||
@property
|
||||
def radar_enabled(self) -> bool:
|
||||
return _params.get_bool(NAPParamKeys.RADAR_ENABLED)
|
||||
|
||||
@property
|
||||
def radar_behind_nosecone(self) -> bool:
|
||||
return _params.get_bool(NAPParamKeys.RADAR_BEHIND_NOSECONE)
|
||||
|
||||
@property
|
||||
def radar_offset(self) -> float:
|
||||
return _params.get_float(NAPParamKeys.RADAR_OFFSET)
|
||||
|
||||
@property
|
||||
def double_pull_enabled(self) -> bool:
|
||||
return True
|
||||
|
||||
@property
|
||||
def double_pull_window_ms(self) -> int:
|
||||
return 500
|
||||
|
||||
def get_pedal_profile_values(self) -> list[float]:
|
||||
return PEDAL_MAX_VALUES
|
||||
|
||||
def di_to_pedal(self, val: float) -> float:
|
||||
return transform_di_to_pedal(val, self.pedal_zero, self.pedal_factor)
|
||||
|
||||
def pedal_to_di(self, val: float) -> float:
|
||||
return transform_pedal_to_di(val, self.pedal_zero, self.pedal_factor)
|
||||
|
||||
|
||||
nap_conf = NAPConf()
|
||||
@@ -0,0 +1,15 @@
|
||||
class NAPParamKeys:
|
||||
ADAPTIVE_ACCEL = "NAPAdaptiveAccel"
|
||||
FOLLOW_DISTANCE = "NAPFollowDistance"
|
||||
FORCE_PRE_AP = "NAPForcePreAP"
|
||||
PEDAL_ENABLED = "NAPPedalEnabled"
|
||||
PEDAL_PROFILE = "NAPPedalProfile"
|
||||
PEDAL_CAN_BUS = "NAPPedalCanBus"
|
||||
PEDAL_CALIB_DONE = "NAPPedalCalibDone"
|
||||
PEDAL_CALIB_MIN = "NAPPedalCalibMin"
|
||||
PEDAL_CALIB_MAX = "NAPPedalCalibMax"
|
||||
PEDAL_CALIB_FACTOR = "NAPPedalCalibFactor"
|
||||
PEDAL_CALIB_ZERO = "NAPPedalCalibZero"
|
||||
RADAR_ENABLED = "NAPRadarEnabled"
|
||||
RADAR_BEHIND_NOSECONE = "NAPRadarBehindNosecone"
|
||||
RADAR_OFFSET = "NAPRadarOffset"
|
||||
@@ -0,0 +1,41 @@
|
||||
from opendbc.car.tesla.preap.nap_conf import PEDAL_DI_PRESSED, nap_conf
|
||||
|
||||
PEDAL_TIMEOUT_MS = 500
|
||||
|
||||
|
||||
class PedalFeedback:
|
||||
def __init__(self):
|
||||
self.interceptor_value = 0.0
|
||||
self.interceptor_value2 = 0.0
|
||||
self.interceptor_state = 0
|
||||
self.idx = 0
|
||||
self.prev_idx = 0
|
||||
self.last_seen_ms = 0
|
||||
self.available = False
|
||||
self.timeout = True
|
||||
self.torque_level = 0.0
|
||||
|
||||
def update(self, gas_sensor_msg, curr_time_ms: int) -> bool:
|
||||
if not gas_sensor_msg:
|
||||
return False
|
||||
|
||||
self.prev_idx = self.idx
|
||||
self.interceptor_value = float(nap_conf.pedal_to_di(float(gas_sensor_msg.get("INTERCEPTOR_GAS", 0.0))))
|
||||
self.interceptor_value2 = float(nap_conf.pedal_to_di(float(gas_sensor_msg.get("INTERCEPTOR_GAS2", 0.0))))
|
||||
self.interceptor_state = int(gas_sensor_msg.get("STATE", 0))
|
||||
self.idx = int(gas_sensor_msg.get("IDX", 0))
|
||||
|
||||
if self.idx != self.prev_idx:
|
||||
self.last_seen_ms = curr_time_ms
|
||||
|
||||
self.timeout = (curr_time_ms - self.last_seen_ms) > PEDAL_TIMEOUT_MS
|
||||
self.available = (not self.timeout) and (self.interceptor_state == 0)
|
||||
return True
|
||||
|
||||
def update_torque(self, di_torque1_msg) -> None:
|
||||
self.torque_level = di_torque1_msg.get("DI_torqueMotor", 0.0)
|
||||
|
||||
@property
|
||||
def gas_pressed(self) -> bool:
|
||||
return self.interceptor_value > PEDAL_DI_PRESSED
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
from opendbc.car.tesla.values import CruiseButtons
|
||||
|
||||
_PHASE_IDLE = 0
|
||||
_PHASE_ENGAGING = 1
|
||||
CANCEL_DELAY_FRAMES = 10
|
||||
CC_ENGAGE_TIMEOUT_FRAMES = 50
|
||||
|
||||
|
||||
class StockCCSpoofer:
|
||||
def __init__(self):
|
||||
self.cc_engage_phase = _PHASE_IDLE
|
||||
self.cc_engage_start_frame = 0
|
||||
self.cancel_pending = False
|
||||
self.cancel_frame = -1_000_000
|
||||
self.prev_di_cc_engaged = False
|
||||
self.pcc_event = None
|
||||
|
||||
def update(self, CS, frame: int, tesla_can, can_bus_party: int):
|
||||
can_sends = []
|
||||
|
||||
if getattr(CS, "preap_cc_cancel_needed", False):
|
||||
self.cancel_pending = True
|
||||
self.cancel_frame = frame
|
||||
self.cc_engage_phase = _PHASE_IDLE
|
||||
CS.preap_cc_cancel_needed = False
|
||||
|
||||
if getattr(CS, "preap_cc_engage_needed", False) and self.cc_engage_phase == _PHASE_IDLE:
|
||||
self.cc_engage_phase = _PHASE_ENGAGING
|
||||
self.cc_engage_start_frame = frame
|
||||
CS.preap_cc_engage_needed = False
|
||||
|
||||
if self.cancel_pending and (frame - self.cancel_frame) >= CANCEL_DELAY_FRAMES and frame % 10 == 0:
|
||||
sent = self._send(CS, tesla_can, can_bus_party, CruiseButtons.CANCEL)
|
||||
if sent is not None:
|
||||
can_sends.append(sent)
|
||||
self.cancel_pending = False
|
||||
elif self.cc_engage_phase == _PHASE_ENGAGING and frame % 10 == 0:
|
||||
if (frame - self.cc_engage_start_frame) >= CC_ENGAGE_TIMEOUT_FRAMES or getattr(CS, "di_cruise_state", "OFF") == "ENABLED":
|
||||
self.cc_engage_phase = _PHASE_IDLE
|
||||
else:
|
||||
sent = self._send(CS, tesla_can, can_bus_party, CruiseButtons.SET_ACCEL)
|
||||
if sent is not None:
|
||||
can_sends.append(sent)
|
||||
|
||||
di_cc_engaged = getattr(CS, "di_cruise_state", "OFF") == "ENABLED"
|
||||
if di_cc_engaged and not self.prev_di_cc_engaged:
|
||||
self.pcc_event = "teslaCCEngaged"
|
||||
elif not di_cc_engaged and self.prev_di_cc_engaged:
|
||||
self.pcc_event = "teslaCCDisengaged"
|
||||
else:
|
||||
self.pcc_event = None
|
||||
self.prev_di_cc_engaged = di_cc_engaged
|
||||
|
||||
return can_sends
|
||||
|
||||
@staticmethod
|
||||
def _send(CS, tesla_can, can_bus_party: int, button: int):
|
||||
msg_stw = getattr(CS, "msg_stw_actn_req", None)
|
||||
if msg_stw is None:
|
||||
return None
|
||||
counter = (int(msg_stw.get("MC_STW_ACTN_RQ", 0)) + 1) % 16
|
||||
return tesla_can.create_action_request(button, can_bus_party, counter, msg_stw)
|
||||
@@ -0,0 +1,96 @@
|
||||
import struct
|
||||
from ctypes import create_string_buffer
|
||||
|
||||
from opendbc.car.tesla.values import CANBUS
|
||||
|
||||
PEDAL_M1 = 0.050796813
|
||||
PEDAL_M2 = 0.101593626
|
||||
PEDAL_D = -22.85856576
|
||||
GAS_COMMAND_ID = 0x551
|
||||
_STW_CRC_POLY = 0x1D
|
||||
_STW_DEFAULTS = {
|
||||
"VSL_Enbl_Rq": 1, "DTR_Dist_Rq": 0, "TurnIndLvr_Stat": 0,
|
||||
"HiBmLvr_Stat": 0, "WprWashSw_Psd": 0, "WprWash_R_Sw_Posn_V2": 0,
|
||||
"StW_Lvr_Stat": 0, "StW_Cond_Flt": 0, "StW_Cond_Psd": 0,
|
||||
"HrnSw_Psd": 0, "StW_Sw00_Psd": 0, "StW_Sw01_Psd": 0,
|
||||
"StW_Sw02_Psd": 0, "StW_Sw03_Psd": 0, "StW_Sw04_Psd": 0,
|
||||
"StW_Sw05_Psd": 0, "StW_Sw06_Psd": 0,
|
||||
"WprSw6Posn": 0,
|
||||
}
|
||||
|
||||
|
||||
def _crc8_stw(data: bytes) -> int:
|
||||
crc = 0xFF
|
||||
for b in data:
|
||||
crc ^= b
|
||||
for _ in range(8):
|
||||
crc = ((crc << 1) ^ _STW_CRC_POLY) & 0xFF if (crc & 0x80) else (crc << 1) & 0xFF
|
||||
return crc ^ 0xFF
|
||||
|
||||
|
||||
class TeslaCANPreAP:
|
||||
def __init__(self, packer):
|
||||
self.packer = packer
|
||||
self.pedal_can_bus = 2
|
||||
self.pedal_idx = 0
|
||||
|
||||
@staticmethod
|
||||
def checksum(msg_id: int, dat: bytes) -> int:
|
||||
return ((msg_id & 0xFF) + ((msg_id >> 8) & 0xFF) + sum(dat)) & 0xFF
|
||||
|
||||
def create_steering_control(self, counter: int, angle: float, enabled: bool):
|
||||
values = {
|
||||
"DAS_steeringControlCounter": counter,
|
||||
"DAS_steeringAngleRequest": -angle,
|
||||
"DAS_steeringHapticRequest": 0,
|
||||
"DAS_steeringControlType": 1 if enabled else 0,
|
||||
}
|
||||
data = self.packer.make_can_msg("DAS_steeringControl", CANBUS.party, values)[1]
|
||||
values["DAS_steeringControlChecksum"] = self.checksum(0x488, data[:3])
|
||||
return self.packer.make_can_msg("DAS_steeringControl", CANBUS.party, values)
|
||||
|
||||
def create_epas_control(self, counter: int, mode: int):
|
||||
values = {
|
||||
"EPB_epasEACAllow": mode,
|
||||
"EPB_epasControlCounter": counter,
|
||||
"EPB_epasControlChecksum": 0,
|
||||
}
|
||||
data = self.packer.make_can_msg("EPB_epasControl", CANBUS.party, values)[1]
|
||||
values["EPB_epasControlChecksum"] = self.checksum(0x214, data)
|
||||
return self.packer.make_can_msg("EPB_epasControl", CANBUS.party, values)
|
||||
|
||||
def create_pedal_command(self, accel_command: float, enable: int = 1, pedal_can_bus: int | None = None):
|
||||
if pedal_can_bus is None:
|
||||
pedal_can_bus = self.pedal_can_bus
|
||||
|
||||
idx = self.pedal_idx
|
||||
self.pedal_idx = (self.pedal_idx + 1) % 16
|
||||
if enable == 1:
|
||||
int_cmd1 = max(0, min(65534, int((accel_command - PEDAL_D) / PEDAL_M1)))
|
||||
int_cmd2 = max(0, min(65534, int((accel_command - PEDAL_D) / PEDAL_M2)))
|
||||
else:
|
||||
int_cmd1 = 0
|
||||
int_cmd2 = 0
|
||||
|
||||
msg = create_string_buffer(6)
|
||||
struct.pack_into("BBBBB", msg, 0,
|
||||
(int_cmd1 >> 8) & 0xFF, int_cmd1 & 0xFF,
|
||||
(int_cmd2 >> 8) & 0xFF, int_cmd2 & 0xFF,
|
||||
((enable << 7) + idx) & 0xFF)
|
||||
struct.pack_into("B", msg, 5, self.checksum(GAS_COMMAND_ID, msg.raw))
|
||||
return GAS_COMMAND_ID, bytes(msg.raw), pedal_can_bus
|
||||
|
||||
def create_action_request(self, button_to_press: int, bus: int, counter: int, msg_stw=None):
|
||||
values = {"MC_STW_ACTN_RQ": counter, "CRC_STW_ACTN_RQ": 0, "SpdCtrlLvr_Stat": button_to_press}
|
||||
if msg_stw is not None:
|
||||
for key, default in _STW_DEFAULTS.items():
|
||||
values[key] = msg_stw.get(key, default)
|
||||
else:
|
||||
values.update(_STW_DEFAULTS)
|
||||
|
||||
# Preserve the live stalk layout, but force VSL enable on engage/resume spoofs.
|
||||
values["VSL_Enbl_Rq"] = 0 if button_to_press == 1 else 1
|
||||
|
||||
data = self.packer.make_can_msg("STW_ACTN_RQ", bus, values)[1]
|
||||
values["CRC_STW_ACTN_RQ"] = _crc8_stw(data[:7])
|
||||
return self.packer.make_can_msg("STW_ACTN_RQ", bus, values)
|
||||
@@ -0,0 +1,139 @@
|
||||
import math
|
||||
|
||||
from numpy import clip, interp
|
||||
|
||||
from opendbc.car.common.filter_simple import FirstOrderFilter, HighPassFilter
|
||||
from opendbc.car.common.pid import PIDController
|
||||
from opendbc.car.tesla.pedal.controller import PEDAL_RAMP_RATE_DOWN, PEDAL_RAMP_RATE_UP, get_zero_torque
|
||||
from opendbc.car.tesla.preap.constants import (
|
||||
VDAS_AEGO_FILTER_RC,
|
||||
VDAS_FUTURE_T_BP,
|
||||
VDAS_FUTURE_T_V,
|
||||
VDAS_INNER_K_BP,
|
||||
VDAS_INNER_KI_V,
|
||||
VDAS_INNER_KP_V,
|
||||
)
|
||||
from opendbc.car.tesla.preap.ff_table_default import ACCEL_BP, DEFAULT_TABLE, SPEED_BP
|
||||
from opendbc.car.tesla.preap.nap_conf import ACCEL_MAX, PEDAL_BP, PEDAL_DI_MIN, PEDAL_MAX_VALUES, REGEN_MAX
|
||||
|
||||
PID_ERROR_DEADBAND = 0.1
|
||||
GRAVITY = 9.81
|
||||
PITCH_LP_RC = 0.5
|
||||
PITCH_HP_RC1 = 0.1
|
||||
PITCH_HP_RC2 = 1.0
|
||||
MAX_PITCH_COMPENSATION = 1.5
|
||||
|
||||
|
||||
class GradeEstimator:
|
||||
def __init__(self, dt: float = 0.02):
|
||||
self.pitch_lp = FirstOrderFilter(0.0, PITCH_LP_RC, dt)
|
||||
self.pitch_hp = HighPassFilter(0.0, PITCH_HP_RC1, PITCH_HP_RC2, dt)
|
||||
|
||||
def update(self, orientation_ned: list[float]) -> tuple[float, float]:
|
||||
if len(orientation_ned) < 2:
|
||||
return 0.0, 0.0
|
||||
pitch = orientation_ned[1]
|
||||
self.pitch_lp.update(pitch)
|
||||
self.pitch_hp.update(pitch)
|
||||
grade_accel = math.sin(self.pitch_lp.x) * GRAVITY
|
||||
pitch_comp = float(clip(math.sin(self.pitch_hp.x) * GRAVITY, -MAX_PITCH_COMPENSATION, MAX_PITCH_COMPENSATION))
|
||||
return grade_accel, pitch_comp
|
||||
|
||||
def reset(self) -> None:
|
||||
self.pitch_lp.x = 0.0
|
||||
self.pitch_hp.x = 0.0
|
||||
self.pitch_hp._f1.x = 0.0
|
||||
self.pitch_hp._f2.x = 0.0
|
||||
|
||||
|
||||
class JerkLimiter:
|
||||
def __init__(self, j_max: float = 2.5, dt: float = 0.02):
|
||||
self.j_max = j_max
|
||||
self.dt = dt
|
||||
self.a_limited = 0.0
|
||||
|
||||
def update(self, a_cmd: float) -> float:
|
||||
da_max = self.j_max * self.dt
|
||||
self.a_limited += float(clip(a_cmd - self.a_limited, -da_max, da_max))
|
||||
return self.a_limited
|
||||
|
||||
def reset(self, a_init: float = 0.0) -> None:
|
||||
self.a_limited = a_init
|
||||
|
||||
|
||||
class FeedforwardModel:
|
||||
def __init__(self):
|
||||
self.speed_bp = list(SPEED_BP)
|
||||
self.accel_bp = list(ACCEL_BP)
|
||||
self.table = [list(row) for row in DEFAULT_TABLE]
|
||||
|
||||
def get(self, a_cmd: float, v_ego: float, zero_torque_di: float) -> float:
|
||||
si = float(interp(v_ego, self.speed_bp, range(len(self.speed_bp))))
|
||||
si_lo = int(si)
|
||||
si_hi = min(si_lo + 1, len(self.speed_bp) - 1)
|
||||
sf = si - si_lo
|
||||
di_lo = float(interp(a_cmd, self.accel_bp, self.table[si_lo]))
|
||||
di_hi = float(interp(a_cmd, self.accel_bp, self.table[si_hi]))
|
||||
base_di = di_lo + sf * (di_hi - di_lo)
|
||||
|
||||
if a_cmd < 0:
|
||||
blend = float(clip((a_cmd - REGEN_MAX) / (0.0 - REGEN_MAX), 0.0, 1.0))
|
||||
else:
|
||||
blend = float(1.0 - a_cmd / ACCEL_MAX)
|
||||
return base_di + zero_torque_di * blend
|
||||
|
||||
|
||||
class VirtualDAS:
|
||||
def __init__(self, dt: float = 0.02):
|
||||
self.dt = dt
|
||||
self.jerk_limiter = JerkLimiter(dt=dt)
|
||||
self.ff_model = FeedforwardModel()
|
||||
self.grade_estimator = GradeEstimator(dt=dt)
|
||||
self.inner_pid = PIDController(
|
||||
k_p=(VDAS_INNER_K_BP, VDAS_INNER_KP_V),
|
||||
k_i=(VDAS_INNER_K_BP, VDAS_INNER_KI_V),
|
||||
k_f=0.0,
|
||||
pos_limit=PEDAL_RAMP_RATE_UP,
|
||||
neg_limit=-PEDAL_RAMP_RATE_DOWN,
|
||||
rate=1.0 / dt,
|
||||
)
|
||||
self.a_ego_filter = FirstOrderFilter(0.0, VDAS_AEGO_FILTER_RC, dt)
|
||||
self.prev_a_ego_filtered = 0.0
|
||||
self.prev_pedal_di = 0.0
|
||||
|
||||
def update(self, a_cmd: float, v_ego: float, prev_pedal_di: float, a_ego: float = 0.0,
|
||||
freeze_integrator: bool = False, orientation_ned: list[float] | None = None) -> float:
|
||||
a_limited = self.jerk_limiter.update(a_cmd)
|
||||
grade_accel, pitch_comp = self.grade_estimator.update(orientation_ned or [])
|
||||
|
||||
ff_di = self._feedforward(a_limited, v_ego) + pitch_comp
|
||||
a_ego_corrected = a_ego - grade_accel
|
||||
a_ego_filtered = self.a_ego_filter.update(a_ego_corrected)
|
||||
j_ego = (a_ego_filtered - self.prev_a_ego_filtered) / self.dt
|
||||
self.prev_a_ego_filtered = a_ego_filtered
|
||||
|
||||
future_t = float(interp(v_ego, VDAS_FUTURE_T_BP, VDAS_FUTURE_T_V))
|
||||
error = a_limited - (a_ego_filtered + j_ego * future_t)
|
||||
if abs(error) < PID_ERROR_DEADBAND:
|
||||
error = 0.0
|
||||
|
||||
pedal_di = ff_di + float(self.inner_pid.update(error, speed=v_ego, freeze_integrator=freeze_integrator))
|
||||
max_pedal_value = float(interp(v_ego, PEDAL_BP, PEDAL_MAX_VALUES))
|
||||
pedal_di = float(clip(pedal_di, PEDAL_DI_MIN, max_pedal_value))
|
||||
pedal_di = float(clip(pedal_di, prev_pedal_di - PEDAL_RAMP_RATE_DOWN, prev_pedal_di + PEDAL_RAMP_RATE_UP))
|
||||
self.prev_pedal_di = pedal_di
|
||||
return pedal_di
|
||||
|
||||
def reset(self, a_init: float = 0.0, pedal_di_init: float = 0.0) -> None:
|
||||
self.jerk_limiter.reset(a_init)
|
||||
self.inner_pid.reset()
|
||||
self.grade_estimator.reset()
|
||||
self.a_ego_filter.x = 0.0
|
||||
self.prev_a_ego_filtered = 0.0
|
||||
self.prev_pedal_di = pedal_di_init
|
||||
|
||||
def _feedforward(self, a_cmd: float, v_ego: float) -> float:
|
||||
zero_torque_di = get_zero_torque().get(v_ego)
|
||||
max_pedal_value = float(interp(v_ego, PEDAL_BP, PEDAL_MAX_VALUES))
|
||||
return float(clip(self.ff_model.get(a_cmd, v_ego, zero_torque_di), PEDAL_DI_MIN, max_pedal_value))
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
from opendbc.can import CANParser
|
||||
from opendbc.car import Bus, structs
|
||||
from opendbc.car.interfaces import RadarInterfaceBase
|
||||
from opendbc.car.tesla.preap.nap_conf import nap_conf
|
||||
from opendbc.car.tesla.values import CANBUS, DBC, CAR
|
||||
|
||||
_BOSCH_RADAR_STATUS_MSG = 769
|
||||
_BOSCH_RADAR_POINT_A_BASE = 784
|
||||
_BOSCH_RADAR_POINT_B_BASE = 785
|
||||
_BOSCH_RADAR_POINT_STRIDE = 3
|
||||
_BOSCH_RADAR_POINTS = 32
|
||||
_BOSCH_TRIGGER_MSG = _BOSCH_RADAR_POINT_B_BASE + ((_BOSCH_RADAR_POINTS - 1) * _BOSCH_RADAR_POINT_STRIDE)
|
||||
|
||||
|
||||
class RadarInterface(RadarInterfaceBase):
|
||||
def __init__(self, CP):
|
||||
super().__init__(CP)
|
||||
|
||||
self.radar_off_can = CP.radarUnavailable or CP.carFingerprint != CAR.TESLA_MODEL_S_PREAP
|
||||
self.updated_messages: set[int] = set()
|
||||
self.track_id = 0
|
||||
self.radar_offset = float(nap_conf.radar_offset) if CP.carFingerprint == CAR.TESLA_MODEL_S_PREAP else 0.0
|
||||
|
||||
if self.radar_off_can:
|
||||
self.rcp = None
|
||||
else:
|
||||
messages = [(_BOSCH_RADAR_STATUS_MSG, 8)]
|
||||
for i in range(_BOSCH_RADAR_POINTS):
|
||||
messages.append((_BOSCH_RADAR_POINT_A_BASE + (i * _BOSCH_RADAR_POINT_STRIDE), 8))
|
||||
messages.append((_BOSCH_RADAR_POINT_B_BASE + (i * _BOSCH_RADAR_POINT_STRIDE), 8))
|
||||
self.rcp = CANParser(DBC[CP.carFingerprint][Bus.radar], messages, CANBUS.radar)
|
||||
|
||||
self.trigger_msg = _BOSCH_TRIGGER_MSG
|
||||
|
||||
def update(self, can_strings):
|
||||
if self.radar_off_can or self.rcp is None:
|
||||
return super().update(None)
|
||||
|
||||
vls = self.rcp.update(can_strings)
|
||||
self.updated_messages.update(vls)
|
||||
|
||||
if self.trigger_msg not in self.updated_messages:
|
||||
return None
|
||||
|
||||
ret = structs.RadarData()
|
||||
if not self.rcp.can_valid:
|
||||
ret.errors.canError = True
|
||||
|
||||
radar_status = self.rcp.vl[_BOSCH_RADAR_STATUS_MSG]
|
||||
ret.errors.radarFault = bool(radar_status["RADC_HWFail"])
|
||||
ret.errors.radarUnavailableTemporary = False
|
||||
|
||||
current_points: set[int] = set()
|
||||
for i in range(_BOSCH_RADAR_POINTS):
|
||||
msg_a_id = _BOSCH_RADAR_POINT_A_BASE + (i * _BOSCH_RADAR_POINT_STRIDE)
|
||||
msg_b_id = _BOSCH_RADAR_POINT_B_BASE + (i * _BOSCH_RADAR_POINT_STRIDE)
|
||||
|
||||
msg_a = self.rcp.vl[msg_a_id]
|
||||
msg_b = self.rcp.vl[msg_b_id]
|
||||
if msg_a["Index"] != msg_b["Index2"]:
|
||||
continue
|
||||
|
||||
if not msg_a["Tracked"] or msg_a["LongDist"] <= 0.0 or msg_a["LongDist"] > 250.0 or msg_a["ProbExist"] < 50.0:
|
||||
self.pts.pop(i, None)
|
||||
continue
|
||||
|
||||
current_points.add(i)
|
||||
if i not in self.pts:
|
||||
self.pts[i] = structs.RadarData.RadarPoint()
|
||||
self.pts[i].trackId = self.track_id
|
||||
self.track_id += 1
|
||||
|
||||
point = self.pts[i]
|
||||
point.dRel = msg_a["LongDist"]
|
||||
point.yRel = msg_a["LatDist"] + self.radar_offset
|
||||
point.vRel = msg_a["LongSpeed"]
|
||||
point.aRel = msg_a["LongAccel"]
|
||||
point.yvRel = msg_b["LatSpeed"]
|
||||
point.measured = bool(msg_a["Meas"])
|
||||
|
||||
for point_id in list(self.pts.keys()):
|
||||
if point_id not in current_points:
|
||||
del self.pts[point_id]
|
||||
|
||||
ret.points = list(self.pts.values())
|
||||
self.updated_messages.clear()
|
||||
return ret
|
||||
@@ -60,6 +60,16 @@ class CAR(Platforms):
|
||||
[TeslaCarDocsHW4("Tesla Model X (with HW4) 2024")],
|
||||
CarSpecs(mass=2495., wheelbase=2.960, steerRatio=12.0),
|
||||
)
|
||||
TESLA_MODEL_S_PREAP = TeslaPlatformConfig(
|
||||
[CarDocs("Tesla Model S (Pre-AP) 2012-14", "All")],
|
||||
CarSpecs(mass=2100., wheelbase=2.960, steerRatio=15.0),
|
||||
{
|
||||
Bus.party: 'tesla_can',
|
||||
Bus.pt: 'tesla_can',
|
||||
Bus.chassis: 'tesla_can',
|
||||
Bus.radar: 'tesla_radar_bosch_generated',
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
FW_QUERY_CONFIG = FwQueryConfig(
|
||||
@@ -75,7 +85,8 @@ FW_QUERY_CONFIG = FwQueryConfig(
|
||||
|
||||
class CANBUS:
|
||||
party = 0
|
||||
vehicle = 1
|
||||
radar = 1
|
||||
vehicle = radar
|
||||
autopilot_party = 2
|
||||
|
||||
|
||||
@@ -125,6 +136,25 @@ class TeslaFlags(IntFlag):
|
||||
LONG_CONTROL = 1
|
||||
|
||||
|
||||
class CruiseButtons:
|
||||
IDLE = 0
|
||||
CANCEL = 1
|
||||
MAIN = 2
|
||||
RES_ACCEL_2ND = 4
|
||||
DECEL_2ND = 8
|
||||
SET_ACCEL = 16
|
||||
RES_ACCEL = 16
|
||||
DECEL_SET = 32
|
||||
|
||||
@classmethod
|
||||
def is_accel(cls, btn: int) -> bool:
|
||||
return btn in (cls.RES_ACCEL, cls.RES_ACCEL_2ND)
|
||||
|
||||
@classmethod
|
||||
def is_decel(cls, btn: int) -> bool:
|
||||
return btn in (cls.DECEL_SET, cls.DECEL_2ND)
|
||||
|
||||
|
||||
DBC = CAR.create_dbc_map()
|
||||
|
||||
STEER_THRESHOLD = 1
|
||||
|
||||
@@ -9,7 +9,7 @@ from typing import Any
|
||||
|
||||
from cereal import custom
|
||||
from opendbc.car import DT_CTRL, CanData, structs
|
||||
from opendbc.car.car_helpers import interfaces
|
||||
from opendbc.car.car_helpers import _apply_disable_openpilot_long, interfaces
|
||||
from opendbc.car.chrysler.carstate import CarState as ChryslerCarState
|
||||
from opendbc.car.fingerprints import FW_VERSIONS
|
||||
from opendbc.car.fw_versions import FW_QUERY_CONFIGS
|
||||
@@ -18,6 +18,7 @@ from opendbc.car.hyundai.values import CAR as HYUNDAI_CAR, HyundaiStarPilotSafet
|
||||
from opendbc.car.gm.values import CAR as GM_CAR, CanBus as GMCanBus, GMSafetyFlags
|
||||
from opendbc.car.interfaces import CarInterfaceBase, CarStateBase, get_interface_attr
|
||||
from opendbc.car.mock.values import CAR as MOCK
|
||||
from opendbc.car.toyota.values import CAR as TOYOTA_CAR, ToyotaSafetyFlags
|
||||
from opendbc.car.values import PLATFORMS
|
||||
|
||||
DrawType = Callable[[st.SearchStrategy], Any]
|
||||
@@ -213,6 +214,40 @@ class TestCarInterfaces:
|
||||
)
|
||||
assert fp_car_params.safetyConfigs[-1].safetyParam & HyundaiStarPilotSafetyFlags.AOL_LKAS_ON_ENGAGE.value
|
||||
|
||||
def test_toyota_disable_openpilot_long_sets_stock_long_safety_flag(self):
|
||||
CarInterface = interfaces[TOYOTA_CAR.TOYOTA_PRIUS_TSS2]
|
||||
fingerprint = {bus: {} for bus in range(8)}
|
||||
toggles = get_test_starpilot_toggles()
|
||||
|
||||
car_params = CarInterface.get_params(
|
||||
TOYOTA_CAR.TOYOTA_PRIUS_TSS2,
|
||||
fingerprint,
|
||||
[],
|
||||
alpha_long=False,
|
||||
is_release=False,
|
||||
docs=False,
|
||||
starpilot_toggles=toggles,
|
||||
)
|
||||
assert car_params.openpilotLongitudinalControl
|
||||
assert not car_params.alphaLongitudinalAvailable
|
||||
assert not (car_params.safetyConfigs[0].safetyParam & ToyotaSafetyFlags.STOCK_LONGITUDINAL.value)
|
||||
|
||||
fp_car_params = CarInterface.get_starpilot_params(
|
||||
TOYOTA_CAR.TOYOTA_PRIUS_TSS2,
|
||||
fingerprint,
|
||||
[],
|
||||
car_params,
|
||||
toggles,
|
||||
)
|
||||
|
||||
_apply_disable_openpilot_long(car_params, fp_car_params)
|
||||
|
||||
assert not car_params.openpilotLongitudinalControl
|
||||
assert car_params.pcmCruise
|
||||
assert fp_car_params.openpilotLongitudinalControlDisabled
|
||||
assert car_params.safetyConfigs[0].safetyParam & ToyotaSafetyFlags.STOCK_LONGITUDINAL.value
|
||||
assert fp_car_params.safetyConfigs[0].safetyParam & ToyotaSafetyFlags.STOCK_LONGITUDINAL.value
|
||||
|
||||
# FIXME: Due to the lists used in carParams, Phase.target is very slow and will cause
|
||||
# many generated examples to overrun when max_examples > ~20, don't use it
|
||||
@pytest.mark.parametrize("car_name", sorted(PLATFORMS))
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
from opendbc.car.chrysler.values import CAR as CHRYSLER_CAR, pacifica_hybrid_aol_stock_acc_mode
|
||||
from opendbc.car.values import PLATFORMS
|
||||
|
||||
|
||||
@@ -15,3 +16,13 @@ class TestPlatformConfigs:
|
||||
assert name == platform.config.platform_str
|
||||
|
||||
assert platform.config.specs is not None
|
||||
|
||||
def test_pacifica_hybrid_aol_stock_acc_mode_requires_pr_conditions(self):
|
||||
assert pacifica_hybrid_aol_stock_acc_mode(CHRYSLER_CAR.CHRYSLER_PACIFICA_2019_HYBRID, True, False, True)
|
||||
assert not pacifica_hybrid_aol_stock_acc_mode(CHRYSLER_CAR.CHRYSLER_PACIFICA_2019_HYBRID, True, True, True)
|
||||
assert not pacifica_hybrid_aol_stock_acc_mode(CHRYSLER_CAR.CHRYSLER_PACIFICA_2019_HYBRID, True, False, False)
|
||||
assert not pacifica_hybrid_aol_stock_acc_mode(CHRYSLER_CAR.CHRYSLER_PACIFICA_2019_HYBRID, False, False, True)
|
||||
|
||||
def test_pacifica_hybrid_aol_stock_acc_mode_stays_narrow(self):
|
||||
assert not pacifica_hybrid_aol_stock_acc_mode(CHRYSLER_CAR.CHRYSLER_PACIFICA_2018_HYBRID, True, False, True)
|
||||
assert not pacifica_hybrid_aol_stock_acc_mode(CHRYSLER_CAR.CHRYSLER_PACIFICA_2020, True, False, True)
|
||||
|
||||
@@ -33,6 +33,7 @@ legend = ["LAT_ACCEL_FACTOR", "MAX_LAT_ACCEL_MEASURED", "FRICTION"]
|
||||
"HYUNDAI_KONA_2022" = "HYUNDAI_KONA_EV"
|
||||
"HYUNDAI_KONA_HEV" = "HYUNDAI_KONA_EV"
|
||||
"HYUNDAI_KONA_EV_2022" = "HYUNDAI_KONA_EV"
|
||||
"HYUNDAI_PALISADE_2023" = "HYUNDAI_PALISADE"
|
||||
"HYUNDAI_IONIQ" = "HYUNDAI_IONIQ_PHEV_2019"
|
||||
"HYUNDAI_IONIQ_HEV_2022" = "HYUNDAI_IONIQ_PHEV_2019"
|
||||
"HYUNDAI_IONIQ_EV_2020" = "HYUNDAI_IONIQ_PHEV_2019"
|
||||
@@ -46,8 +47,19 @@ legend = ["LAT_ACCEL_FACTOR", "MAX_LAT_ACCEL_MEASURED", "FRICTION"]
|
||||
"GENESIS_G90" = "GENESIS_G70"
|
||||
"GENESIS_G80" = "GENESIS_G70"
|
||||
"GENESIS_G70_2020" = "HYUNDAI_SONATA"
|
||||
"KIA_CEED_PHEV_2022_NON_SCC" = "HYUNDAI_SONATA"
|
||||
"HYUNDAI_KONA_EV_NON_SCC" = "HYUNDAI_KONA_EV"
|
||||
"KIA_FORTE_2019_NON_SCC" = "HYUNDAI_SONATA"
|
||||
"KIA_FORTE_2021_NON_SCC" = "HYUNDAI_SONATA"
|
||||
"KIA_SELTOS_2023_NON_SCC" = "HYUNDAI_SONATA"
|
||||
"HYUNDAI_KONA_NON_SCC" = "HYUNDAI_KONA_EV"
|
||||
"HYUNDAI_ELANTRA_2022_NON_SCC" = "HYUNDAI_ELANTRA_2021"
|
||||
"HYUNDAI_ELANTRA_HEV_2022_NON_SCC" = "HYUNDAI_ELANTRA_HEV_2021"
|
||||
"GENESIS_G70_2021_NON_SCC" = "HYUNDAI_SONATA"
|
||||
"HYUNDAI_BAYON_1ST_GEN_NON_SCC" = "HYUNDAI_SONATA"
|
||||
|
||||
"HONDA_FREED" = "HONDA_ODYSSEY"
|
||||
"HONDA_CLARITY" = "HONDA_ACCORD"
|
||||
"HONDA_CRV_EU" = "HONDA_CRV"
|
||||
"HONDA_CIVIC_BOSCH_DIESEL" = "HONDA_CIVIC_BOSCH"
|
||||
"HONDA_E" = "HONDA_CIVIC_BOSCH"
|
||||
@@ -87,6 +99,8 @@ legend = ["LAT_ACCEL_FACTOR", "MAX_LAT_ACCEL_MEASURED", "FRICTION"]
|
||||
"SUBARU_FORESTER_HYBRID" = "SUBARU_IMPREZA_2020"
|
||||
"SUBARU_LEGACY" = "SUBARU_OUTBACK"
|
||||
|
||||
"TESLA_MODEL_S_PREAP" = "TESLA_MODEL_3"
|
||||
|
||||
# Old subarus don't have much data guessing it's like low torque impreza"
|
||||
"SUBARU_OUTBACK_PREGLOBAL_2018" = "SUBARU_IMPREZA"
|
||||
"SUBARU_OUTBACK_PREGLOBAL" = "SUBARU_IMPREZA"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import math
|
||||
import numpy as np
|
||||
from opendbc.car import Bus, make_tester_present_msg, rate_limit, structs, ACCELERATION_DUE_TO_GRAVITY, DT_CTRL
|
||||
from opendbc.car import Bus, create_gas_interceptor_command, make_tester_present_msg, rate_limit, structs, ACCELERATION_DUE_TO_GRAVITY, DT_CTRL
|
||||
from opendbc.car.lateral import apply_meas_steer_torque_limits, apply_std_steer_angle_limits, common_fault_avoidance
|
||||
from opendbc.car.can_definitions import CanData
|
||||
from opendbc.car.carlog import carlog
|
||||
@@ -9,7 +9,7 @@ from opendbc.car.common.pid import PIDController
|
||||
from opendbc.car.secoc import add_mac, build_sync_mac
|
||||
from opendbc.car.interfaces import CarControllerBase
|
||||
from opendbc.car.toyota import toyotacan
|
||||
from opendbc.car.toyota.values import CAR, NO_STOP_TIMER_CAR, TSS2_CAR, \
|
||||
from opendbc.car.toyota.values import CAR, MIN_ACC_SPEED, NO_STOP_TIMER_CAR, PEDAL_TRANSITION, TSS2_CAR, \
|
||||
CarControllerParams, ToyotaFlags, \
|
||||
UNSUPPORTED_DSU_CAR
|
||||
from opendbc.can import CANPacker
|
||||
@@ -28,6 +28,11 @@ PRIUS_INTEGRAL_MISMATCH_UNWIND = 8.0
|
||||
PRIUS_POSITIVE_FEEDFORWARD_SCALE = 0.5
|
||||
|
||||
MAX_PITCH_COMPENSATION = 1.5 # m/s^2
|
||||
TOYOTA_COAST_BRAKE_MIN_SPEED = 15.0 # m/s
|
||||
TOYOTA_COAST_BRAKE_ENABLE_ACCEL = -0.10 # m/s^2
|
||||
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
|
||||
|
||||
# LKA limits
|
||||
# EPS faults if you apply torque while the steering rate is above 100 deg/s for too long
|
||||
@@ -60,6 +65,83 @@ def get_long_tune(CP, params):
|
||||
rate=1 / (DT_CTRL * 3))
|
||||
|
||||
|
||||
def update_permit_braking(current: bool, net_acceleration_request_min: float, stopping: bool,
|
||||
long_active: bool, v_ego: float, lead_visible: bool) -> bool:
|
||||
if stopping or not long_active:
|
||||
return True
|
||||
|
||||
if not lead_visible and v_ego >= TOYOTA_COAST_BRAKE_MIN_SPEED:
|
||||
return net_acceleration_request_min <= TOYOTA_NO_LEAD_COAST_BRAKE_ACCEL
|
||||
|
||||
# At cruising speeds, some Toyota platforms turn tiny negative accel corrections
|
||||
# into noticeable brake taps. Keep a small coast-only band so mild follow/cruise
|
||||
# trims stay off the brakes while still allowing real negative requests through.
|
||||
if v_ego >= TOYOTA_COAST_BRAKE_MIN_SPEED:
|
||||
if net_acceleration_request_min <= TOYOTA_COAST_BRAKE_ENABLE_ACCEL:
|
||||
return True
|
||||
if net_acceleration_request_min >= TOYOTA_COAST_BRAKE_DISABLE_ACCEL:
|
||||
return False
|
||||
return current
|
||||
|
||||
if net_acceleration_request_min < 0.2:
|
||||
return True
|
||||
if net_acceleration_request_min > 0.3:
|
||||
return False
|
||||
return current
|
||||
|
||||
|
||||
def limit_interceptor_pcm_accel(pcm_accel_cmd: float, target_accel: float, stopping: bool, v_ego: float) -> float:
|
||||
if stopping:
|
||||
return pcm_accel_cmd
|
||||
|
||||
# The Toyota long path should not cross the accel sign against a strong planner
|
||||
# request on pedal/SDSU cars during ordinary driving.
|
||||
if target_accel > 0.15 and pcm_accel_cmd < 0.0:
|
||||
positive_floor = float(np.interp(v_ego, [0.0, 5.0, 15.0], [0.12, 0.08, 0.0]))
|
||||
pcm_accel_cmd = max(pcm_accel_cmd, positive_floor)
|
||||
elif target_accel < -0.15 and pcm_accel_cmd > 0.0:
|
||||
negative_floor = float(np.interp(v_ego, [0.0, 5.0, 15.0], [-0.12, -0.08, 0.0]))
|
||||
pcm_accel_cmd = min(pcm_accel_cmd, negative_floor)
|
||||
|
||||
if target_accel <= -1.6:
|
||||
return pcm_accel_cmd
|
||||
|
||||
if abs(target_accel) > TOYOTA_INTERCEPTOR_COMFORT_TARGET_ACCEL:
|
||||
return pcm_accel_cmd
|
||||
|
||||
lower_delta = float(np.interp(v_ego, [0.0, 5.0, 15.0], [0.20, 0.25, 0.35]))
|
||||
upper_delta = float(np.interp(v_ego, [0.0, 5.0, 15.0], [0.25, 0.30, 0.40]))
|
||||
limited = float(np.clip(pcm_accel_cmd, target_accel - lower_delta, target_accel + upper_delta))
|
||||
|
||||
# Pedal/SDSU cars can feel especially surgey if the Toyota longitudinal controller
|
||||
# swings far away from the planner target. Bias comfort-zone requests back toward
|
||||
# the planner while still allowing some controller correction.
|
||||
target_weight = float(np.interp(v_ego, [0.0, 5.0, 15.0], [0.8, 0.65, 0.5]))
|
||||
limited = (target_weight * target_accel) + ((1.0 - target_weight) * limited)
|
||||
|
||||
if target_accel > 0.15:
|
||||
limited = max(limited, float(np.interp(v_ego, [0.0, 5.0, 15.0], [0.10, 0.05, 0.0])))
|
||||
elif target_accel < -0.15:
|
||||
limited = min(limited, float(np.interp(v_ego, [0.0, 5.0, 15.0], [-0.10, -0.05, 0.0])))
|
||||
|
||||
return limited
|
||||
|
||||
|
||||
def limit_interceptor_stopping_accel(pcm_accel_cmd: float, target_accel: float, stopping: bool, v_ego: float, lead_visible: bool) -> float:
|
||||
if not stopping or lead_visible or pcm_accel_cmd >= 0.0 or v_ego >= 2.5:
|
||||
return pcm_accel_cmd
|
||||
|
||||
# Pedal/SDSU Toyotas can feel abrupt in the last few feet of a no-lead stop
|
||||
# because stopping state can hold onto a stale strong negative command even
|
||||
# after the planner target has already softened. Keep real lead stops
|
||||
# untouched, but let the last 2-3 mph of a no-lead stop unwind toward the
|
||||
# current planner target instead of shoving through zero.
|
||||
stop_floor = float(np.interp(v_ego, [0.0, 0.2, 0.5, 0.9, 1.5, 2.5], [-0.72, -0.76, -0.82, -0.92, -1.05, -1.20]))
|
||||
target_buffer = float(np.interp(v_ego, [0.0, 0.5, 1.5, 2.5], [0.08, 0.10, 0.15, 0.20]))
|
||||
planner_floor = float(target_accel) - target_buffer
|
||||
return max(pcm_accel_cmd, max(stop_floor, planner_floor))
|
||||
|
||||
|
||||
class CarController(CarControllerBase):
|
||||
def __init__(self, dbc_names, CP):
|
||||
super().__init__(dbc_names, CP)
|
||||
@@ -92,6 +174,50 @@ class CarController(CarControllerBase):
|
||||
|
||||
self.doors_locked = False
|
||||
|
||||
def _compute_interceptor_gas_cmd(self, CC, CS):
|
||||
if not (self.CP.enableGasInterceptorDEPRECATED and self.CP.openpilotLongitudinalControl and CC.longActive):
|
||||
return 0.0
|
||||
|
||||
if self.CP.minEnableSpeed < 0.0:
|
||||
return 0.12 if CS.out.standstill and self.accel > 0.0 else 0.0
|
||||
|
||||
max_interceptor_gas = 0.5
|
||||
if self.CP.carFingerprint == CAR.TOYOTA_RAV4:
|
||||
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 == CAR.TOYOTA_COROLLA:
|
||||
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:
|
||||
pedal_scale = float(np.interp(CS.out.vEgo, [0.0, MIN_ACC_SPEED, MIN_ACC_SPEED + PEDAL_TRANSITION], [0.4, 0.5, 0.0]))
|
||||
|
||||
pedal_offset = float(np.interp(CS.out.vEgo, [0.0, 2.3, MIN_ACC_SPEED + PEDAL_TRANSITION], [-0.4, 0.0, 0.2]))
|
||||
pedal_command = pedal_scale * (self.accel + pedal_offset)
|
||||
return float(np.clip(pedal_command, 0.0, max_interceptor_gas))
|
||||
|
||||
def _update_standstill_request(self, CC, CS, actuators, starpilot_toggles):
|
||||
# Older TSS-P platforms need a standstill latch pulse, then an explicit release to move again.
|
||||
if self.CP.carFingerprint not in NO_STOP_TIMER_CAR:
|
||||
if CS.out.standstill and not self.last_standstill and not starpilot_toggles.sng_hack:
|
||||
self.standstill_req = True
|
||||
|
||||
if CS.pcm_acc_status != 8 or CC.cruiseControl.resume or starpilot_toggles.sng_hack:
|
||||
# Clear once the PCM has latched the stop, when planner wants to move,
|
||||
# or when the SNG hack is forcing the latch open.
|
||||
self.standstill_req = False
|
||||
|
||||
else:
|
||||
# if user engages at a stop with foot on brake, PCM starts in a special cruise standstill mode. on resume press,
|
||||
# brakes can take a while to ramp up causing a lurch forward. prevent resume press until planner wants to move.
|
||||
# don't use CC.cruiseControl.resume since it is gated on CS.cruiseState.standstill which goes false for 3s after resume press
|
||||
# TODO: hybrids do not have this issue and can stay stopped after resume press, whitelist them
|
||||
should_resume = actuators.accel > 0 or starpilot_toggles.sng_hack
|
||||
if should_resume:
|
||||
self.standstill_req = False
|
||||
|
||||
if not should_resume and CS.out.cruiseState.standstill:
|
||||
self.standstill_req = True
|
||||
|
||||
self.last_standstill = CS.out.standstill
|
||||
|
||||
def update(self, CC, CS, now_nanos, starpilot_toggles):
|
||||
actuators = CC.actuators
|
||||
stopping = actuators.longControlState == LongCtrlState.stopping
|
||||
@@ -184,33 +310,18 @@ class CarController(CarControllerBase):
|
||||
|
||||
# *** gas and brake ***
|
||||
|
||||
# on entering standstill, send standstill request for older TSS-P cars that aren't designed to stay engaged at a stop
|
||||
if self.CP.carFingerprint not in NO_STOP_TIMER_CAR:
|
||||
if CS.out.standstill and not self.last_standstill and not starpilot_toggles.sng_hack:
|
||||
self.standstill_req = True
|
||||
if CS.pcm_acc_status != 8:
|
||||
# pcm entered standstill or it's disabled
|
||||
self.standstill_req = False
|
||||
|
||||
else:
|
||||
# if user engages at a stop with foot on brake, PCM starts in a special cruise standstill mode. on resume press,
|
||||
# brakes can take a while to ramp up causing a lurch forward. prevent resume press until planner wants to move.
|
||||
# don't use CC.cruiseControl.resume since it is gated on CS.cruiseState.standstill which goes false for 3s after resume press
|
||||
# TODO: hybrids do not have this issue and can stay stopped after resume press, whitelist them
|
||||
should_resume = actuators.accel > 0 or starpilot_toggles.sng_hack
|
||||
if should_resume:
|
||||
self.standstill_req = False
|
||||
|
||||
if not should_resume and CS.out.cruiseState.standstill:
|
||||
self.standstill_req = True
|
||||
|
||||
self.last_standstill = CS.out.standstill
|
||||
self._update_standstill_request(CC, CS, actuators, starpilot_toggles)
|
||||
interceptor_gas_cmd = self._compute_interceptor_gas_cmd(CC, CS)
|
||||
|
||||
# handle UI messages
|
||||
fcw_alert = hud_control.visualAlert == VisualAlert.fcw
|
||||
steer_alert = hud_control.visualAlert in (VisualAlert.steerRequired, VisualAlert.ldw)
|
||||
lead = hud_control.leadVisible or CS.out.vEgo < 12. # at low speed we always assume the lead is present so ACC can be engaged
|
||||
|
||||
if self.frame % 2 == 0 and self.CP.enableGasInterceptorDEPRECATED and self.CP.openpilotLongitudinalControl:
|
||||
# Send an explicit zero when off so the interceptor does not rescale around a stale command.
|
||||
can_sends.append(create_gas_interceptor_command(self.packer, interceptor_gas_cmd, self.frame // 2))
|
||||
|
||||
if self.CP.openpilotLongitudinalControl:
|
||||
if self.frame % 3 == 0:
|
||||
# Press distance button until we are at the correct bar length. Only change while enabled to avoid skipping startup popup
|
||||
@@ -251,41 +362,54 @@ class CarController(CarControllerBase):
|
||||
a_ego_future = a_ego_blended + j_ego * future_t
|
||||
|
||||
if CC.longActive:
|
||||
# constantly slowly unwind integral to recover from large temporary errors
|
||||
unwind_rate = ACCEL_PID_UNWIND
|
||||
if self.CP.carFingerprint == CAR.TOYOTA_PRIUS and pcm_accel_cmd * self.long_pid.i < 0.0:
|
||||
unwind_rate *= PRIUS_INTEGRAL_MISMATCH_UNWIND
|
||||
self.long_pid.i -= unwind_rate * float(np.sign(self.long_pid.i))
|
||||
if self.CP.enableGasInterceptorDEPRECATED:
|
||||
# Pedal/SDSU Toyotas have shown better behavior when we trust the planner
|
||||
# target directly instead of letting the Toyota longitudinal PID swing it
|
||||
# around. Keep the shared rate limits above, but bypass the extra
|
||||
# correction layer that causes rev/release oscillation.
|
||||
self.long_pid.reset()
|
||||
else:
|
||||
# constantly slowly unwind integral to recover from large temporary errors
|
||||
unwind_rate = ACCEL_PID_UNWIND
|
||||
if self.CP.carFingerprint == CAR.TOYOTA_PRIUS and pcm_accel_cmd * self.long_pid.i < 0.0:
|
||||
unwind_rate *= PRIUS_INTEGRAL_MISMATCH_UNWIND
|
||||
self.long_pid.i -= unwind_rate * float(np.sign(self.long_pid.i))
|
||||
|
||||
error_future = pcm_accel_cmd - a_ego_future
|
||||
error_future = pcm_accel_cmd - a_ego_future
|
||||
|
||||
if not stopping:
|
||||
# Toyota's PCM slowly responds to changes in pitch. On change, we amplify our
|
||||
# acceleration request to compensate for the undershoot and following overshoot
|
||||
pitch_compensation = float(np.clip(math.sin(self.pitch_hp.x) * ACCELERATION_DUE_TO_GRAVITY,
|
||||
-MAX_PITCH_COMPENSATION, MAX_PITCH_COMPENSATION))
|
||||
pcm_accel_cmd += pitch_compensation
|
||||
if not stopping:
|
||||
# Toyota's PCM slowly responds to changes in pitch. On change, we amplify our
|
||||
# acceleration request to compensate for the undershoot and following overshoot
|
||||
pitch_compensation = float(np.clip(math.sin(self.pitch_hp.x) * ACCELERATION_DUE_TO_GRAVITY,
|
||||
-MAX_PITCH_COMPENSATION, MAX_PITCH_COMPENSATION))
|
||||
pcm_accel_cmd += pitch_compensation
|
||||
|
||||
feedforward = pcm_accel_cmd
|
||||
if self.CP.carFingerprint == CAR.TOYOTA_PRIUS:
|
||||
# Keep Prius positive handoffs softer than the stock tune, while restoring some launch authority.
|
||||
if feedforward > 0.0:
|
||||
feedforward *= PRIUS_POSITIVE_FEEDFORWARD_SCALE
|
||||
feedforward = pcm_accel_cmd
|
||||
if self.CP.carFingerprint == CAR.TOYOTA_PRIUS:
|
||||
# Keep Prius positive handoffs softer than the stock tune, while restoring some launch authority.
|
||||
if feedforward > 0.0:
|
||||
feedforward *= PRIUS_POSITIVE_FEEDFORWARD_SCALE
|
||||
|
||||
pcm_accel_cmd = self.long_pid.update(error_future,
|
||||
speed=CS.out.vEgo,
|
||||
feedforward=feedforward,
|
||||
freeze_integrator=actuators.longControlState != LongCtrlState.pid)
|
||||
pcm_accel_cmd = self.long_pid.update(error_future,
|
||||
speed=CS.out.vEgo,
|
||||
feedforward=feedforward,
|
||||
freeze_integrator=actuators.longControlState != LongCtrlState.pid)
|
||||
else:
|
||||
self.long_pid.reset()
|
||||
|
||||
# Along with rate limiting positive jerk above, this greatly improves gas response time
|
||||
# Consider the net acceleration request that the PCM should be applying (pitch included)
|
||||
net_acceleration_request_min = min(actuators.accel + accel_due_to_pitch, net_acceleration_request)
|
||||
if net_acceleration_request_min < 0.2 or stopping or not CC.longActive:
|
||||
self.permit_braking = True
|
||||
elif net_acceleration_request_min > 0.3:
|
||||
self.permit_braking = False
|
||||
self.permit_braking = update_permit_braking(self.permit_braking,
|
||||
net_acceleration_request_min,
|
||||
stopping,
|
||||
CC.longActive,
|
||||
CS.out.vEgo,
|
||||
lead)
|
||||
|
||||
if self.CP.enableGasInterceptorDEPRECATED:
|
||||
pcm_accel_cmd = limit_interceptor_pcm_accel(pcm_accel_cmd, actuators.accel, stopping, CS.out.vEgo)
|
||||
pcm_accel_cmd = limit_interceptor_stopping_accel(pcm_accel_cmd, actuators.accel, stopping, CS.out.vEgo, bool(hud_control.leadVisible))
|
||||
|
||||
pcm_accel_cmd = float(np.clip(pcm_accel_cmd, self.params.ACCEL_MIN, self.params.ACCEL_MAX))
|
||||
|
||||
@@ -329,7 +453,7 @@ class CarController(CarControllerBase):
|
||||
if self.frame % 20 == 0 or send_ui:
|
||||
can_sends.append(toyotacan.create_ui_command(self.packer, steer_alert, pcm_cancel_cmd, hud_control.leftLaneVisible,
|
||||
hud_control.rightLaneVisible, hud_control.leftLaneDepart,
|
||||
hud_control.rightLaneDepart, CC.enabled, CS.lkas_hud))
|
||||
hud_control.rightLaneDepart, CS.lkas_hud, lat_active))
|
||||
|
||||
if (self.frame % 100 == 0 or send_ui) and self.CP.flags & ToyotaFlags.DISABLE_RADAR.value:
|
||||
can_sends.append(toyotacan.create_fcw_command(self.packer, fcw_alert))
|
||||
|
||||
@@ -38,6 +38,11 @@ def calculate_speed_limit(cp_cam):
|
||||
return 0
|
||||
|
||||
|
||||
def calculate_interceptor_gas_pressed(cp) -> bool:
|
||||
interceptor_gas = (cp.vl["GAS_SENSOR"]["INTERCEPTOR_GAS"] + cp.vl["GAS_SENSOR"]["INTERCEPTOR_GAS2"]) / 2
|
||||
return interceptor_gas > 805
|
||||
|
||||
|
||||
class CarState(CarStateBase):
|
||||
def __init__(self, CP, FPCP):
|
||||
super().__init__(CP, FPCP)
|
||||
@@ -99,7 +104,10 @@ class CarState(CarStateBase):
|
||||
ret.gasPressed = cp.vl["GAS_PEDAL"]["GAS_PEDAL_USER"] > 0
|
||||
can_gear = int(cp.vl["GEAR_PACKET_HYBRID"]["GEAR"])
|
||||
else:
|
||||
ret.gasPressed = cp.vl["PCM_CRUISE"]["GAS_RELEASED"] == 0 # TODO: these also have GAS_PEDAL, come back and unify
|
||||
if self.CP.enableGasInterceptorDEPRECATED:
|
||||
ret.gasPressed = calculate_interceptor_gas_pressed(cp)
|
||||
else:
|
||||
ret.gasPressed = cp.vl["PCM_CRUISE"]["GAS_RELEASED"] == 0 # TODO: these also have GAS_PEDAL, come back and unify
|
||||
can_gear = int(cp.vl["GEAR_PACKET"]["GEAR"])
|
||||
if not self.CP.flags & ToyotaFlags.DISABLE_RADAR.value:
|
||||
ret.stockAeb = bool(cp_acc.vl["PRE_COLLISION"]["PRECOLLISION_ACTIVE"] and cp_acc.vl["PRE_COLLISION"]["FORCE"] < -1e-5)
|
||||
@@ -269,6 +277,9 @@ class CarState(CarStateBase):
|
||||
("BLINKERS_STATE", float('nan')),
|
||||
]
|
||||
|
||||
if CP.enableGasInterceptorDEPRECATED:
|
||||
pt_messages.append(("GAS_SENSOR", 50))
|
||||
|
||||
return {
|
||||
Bus.pt: CANParser(DBC[CP.carFingerprint][Bus.pt], pt_messages, 0),
|
||||
Bus.cam: CANParser(DBC[CP.carFingerprint][Bus.pt], [], 2),
|
||||
|
||||
@@ -54,6 +54,9 @@ class CarInterface(CarInterfaceBase):
|
||||
# openpilot to send it. 0x2AA is sent by a similar device which intercepts
|
||||
# the radar instead of DSU on NO_DSU_CARs.
|
||||
use_sdsu = bool(0x2FF in fingerprint[0] or (0x2AA in fingerprint[0] and candidate in NO_DSU_CAR))
|
||||
if use_sdsu:
|
||||
# sDSU / radar filter hardware needs the Toyota safety long-filter TX set.
|
||||
ret.safetyConfigs[0].safetyParam |= ToyotaSafetyFlags.LONG_FILTER.value
|
||||
|
||||
# In TSS2 cars, the camera does long control
|
||||
found_ecus = [fw.ecu for fw in car_fw]
|
||||
@@ -133,15 +136,16 @@ class CarInterface(CarInterfaceBase):
|
||||
bool(ret.flags & ToyotaFlags.DISABLE_RADAR.value))
|
||||
|
||||
ret.autoResumeSng = ret.openpilotLongitudinalControl and candidate in NO_STOP_TIMER_CAR
|
||||
ret.enableGasInterceptorDEPRECATED = 0x201 in fingerprint[0] and ret.openpilotLongitudinalControl
|
||||
|
||||
if not ret.openpilotLongitudinalControl:
|
||||
ret.safetyConfigs[0].safetyParam |= ToyotaSafetyFlags.STOCK_LONGITUDINAL.value
|
||||
|
||||
# min speed to enable ACC. if car can do stop and go, then set enabling speed
|
||||
# to a negative value, so it won't matter.
|
||||
ret.minEnableSpeed = -1. if stop_and_go else MIN_ACC_SPEED
|
||||
ret.minEnableSpeed = -1. if (stop_and_go or ret.enableGasInterceptorDEPRECATED) else MIN_ACC_SPEED
|
||||
|
||||
if candidate in TSS2_CAR:
|
||||
if candidate in TSS2_CAR or ret.enableGasInterceptorDEPRECATED:
|
||||
ret.flags |= ToyotaFlags.RAISED_ACCEL_LIMIT.value
|
||||
|
||||
ret.vEgoStopping = 0.25
|
||||
@@ -152,6 +156,11 @@ class CarInterface(CarInterfaceBase):
|
||||
if ret.flags & ToyotaFlags.HYBRID.value:
|
||||
ret.longitudinalActuatorDelay = 0.05
|
||||
|
||||
if ret.enableGasInterceptorDEPRECATED:
|
||||
# Pedal/SDSU Toyotas feel best with a softer final stop clamp.
|
||||
ret.longitudinalActuatorDelay = max(ret.longitudinalActuatorDelay, 0.2)
|
||||
ret.stopAccel = -1.5
|
||||
|
||||
return ret
|
||||
|
||||
@staticmethod
|
||||
|
||||
@@ -1,9 +1,16 @@
|
||||
from types import SimpleNamespace
|
||||
|
||||
from hypothesis import given, settings, strategies as st
|
||||
|
||||
from opendbc.car import Bus
|
||||
from opendbc.can import CANPacker, CANParser
|
||||
from opendbc.car.structs import CarParams
|
||||
from opendbc.car.fw_versions import build_fw_dict
|
||||
from opendbc.car.toyota import toyotacan
|
||||
from opendbc.car.toyota.carcontroller import CarController, limit_interceptor_pcm_accel, limit_interceptor_stopping_accel, update_permit_braking
|
||||
from opendbc.car.toyota.carstate import calculate_interceptor_gas_pressed
|
||||
from opendbc.car.toyota.fingerprints import FW_VERSIONS
|
||||
from opendbc.car.toyota.interface import CarInterface
|
||||
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, \
|
||||
get_platform_codes
|
||||
@@ -165,3 +172,244 @@ class TestToyotaFingerprint:
|
||||
platforms_with_shared_codes |= {str(platform), *matches}
|
||||
|
||||
assert platforms_with_shared_codes == FUZZY_EXCLUDED_PLATFORMS, (len(platforms_with_shared_codes), len(FW_VERSIONS))
|
||||
|
||||
|
||||
class TestToyotaCarController:
|
||||
@staticmethod
|
||||
def _make_controller(*, standstill_req=False, last_standstill=False):
|
||||
controller = CarController.__new__(CarController)
|
||||
controller.CP = SimpleNamespace(
|
||||
carFingerprint=CAR.TOYOTA_PRIUS,
|
||||
enableGasInterceptorDEPRECATED=False,
|
||||
openpilotLongitudinalControl=True,
|
||||
minEnableSpeed=-1.0,
|
||||
)
|
||||
controller.standstill_req = standstill_req
|
||||
controller.last_standstill = last_standstill
|
||||
controller.accel = 0.0
|
||||
return controller
|
||||
|
||||
@staticmethod
|
||||
def _make_cc(*, resume=False):
|
||||
return SimpleNamespace(cruiseControl=SimpleNamespace(resume=resume))
|
||||
|
||||
@staticmethod
|
||||
def _make_cs(*, standstill=True, cruise_standstill=True, pcm_acc_status=8):
|
||||
return SimpleNamespace(
|
||||
out=SimpleNamespace(
|
||||
standstill=standstill,
|
||||
cruiseState=SimpleNamespace(standstill=cruise_standstill),
|
||||
),
|
||||
pcm_acc_status=pcm_acc_status,
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _make_toggles(*, sng_hack=False):
|
||||
return SimpleNamespace(sng_hack=sng_hack)
|
||||
|
||||
def test_prius_standstill_request_latches_on_entry(self):
|
||||
controller = self._make_controller()
|
||||
|
||||
controller._update_standstill_request(
|
||||
self._make_cc(),
|
||||
self._make_cs(),
|
||||
SimpleNamespace(accel=0.0),
|
||||
self._make_toggles(),
|
||||
)
|
||||
|
||||
assert controller.standstill_req is True
|
||||
|
||||
def test_prius_resume_request_releases_standstill_latch(self):
|
||||
controller = self._make_controller(standstill_req=True, last_standstill=True)
|
||||
|
||||
controller._update_standstill_request(
|
||||
self._make_cc(resume=True),
|
||||
self._make_cs(),
|
||||
SimpleNamespace(accel=0.0),
|
||||
self._make_toggles(),
|
||||
)
|
||||
|
||||
assert controller.standstill_req is False
|
||||
|
||||
def test_permit_braking_high_speed_coasts_for_tiny_decel(self):
|
||||
assert update_permit_braking(True, -0.05, False, True, 25.0, True) is False
|
||||
assert update_permit_braking(False, -0.05, False, True, 25.0, True) is False
|
||||
|
||||
def test_permit_braking_high_speed_brakes_for_meaningful_decel_with_lead(self):
|
||||
assert update_permit_braking(False, -0.15, False, True, 25.0, True) is True
|
||||
|
||||
def test_permit_braking_high_speed_no_lead_coasts_for_mild_decel(self):
|
||||
assert update_permit_braking(True, -0.25, False, True, 25.0, False) is False
|
||||
assert update_permit_braking(False, -0.25, False, True, 25.0, False) is False
|
||||
|
||||
def test_permit_braking_high_speed_no_lead_brakes_for_stronger_decel(self):
|
||||
assert update_permit_braking(False, -0.35, False, True, 25.0, False) is True
|
||||
|
||||
def test_permit_braking_low_speed_keeps_legacy_behavior(self):
|
||||
assert update_permit_braking(False, -0.05, False, True, 10.0, False) is True
|
||||
|
||||
def test_permit_braking_forces_on_when_stopping_or_inactive(self):
|
||||
assert update_permit_braking(False, 0.10, True, True, 25.0, False) is True
|
||||
assert update_permit_braking(False, 0.10, False, False, 25.0, False) is True
|
||||
|
||||
def test_sng_hack_clears_existing_standstill_latch(self):
|
||||
controller = self._make_controller(standstill_req=True, last_standstill=True)
|
||||
|
||||
controller._update_standstill_request(
|
||||
self._make_cc(),
|
||||
self._make_cs(),
|
||||
SimpleNamespace(accel=0.0),
|
||||
self._make_toggles(sng_hack=True),
|
||||
)
|
||||
|
||||
assert controller.standstill_req is False
|
||||
|
||||
def test_ui_command_shows_aol_bars_when_lateral_active(self):
|
||||
packer = CANPacker(DBC[CAR.TOYOTA_HIGHLANDER_TSS2][Bus.pt])
|
||||
parser = CANParser(DBC[CAR.TOYOTA_HIGHLANDER_TSS2][Bus.pt], [("LKAS_HUD", 0)], 0)
|
||||
|
||||
msg = toyotacan.create_ui_command(packer, False, False, True, True, False, False, {}, True)
|
||||
parser.update([(1, [msg])])
|
||||
|
||||
assert parser.can_valid
|
||||
assert parser.vl["LKAS_HUD"]["BARRIERS"] == 1
|
||||
assert parser.vl["LKAS_HUD"]["LEFT_LINE"] == 1
|
||||
assert parser.vl["LKAS_HUD"]["RIGHT_LINE"] == 1
|
||||
|
||||
def test_ui_command_hides_lane_markers_when_lateral_inactive(self):
|
||||
packer = CANPacker(DBC[CAR.TOYOTA_HIGHLANDER_TSS2][Bus.pt])
|
||||
parser = CANParser(DBC[CAR.TOYOTA_HIGHLANDER_TSS2][Bus.pt], [("LKAS_HUD", 0)], 0)
|
||||
|
||||
msg = toyotacan.create_ui_command(packer, False, False, True, True, False, False, {}, False)
|
||||
parser.update([(1, [msg])])
|
||||
|
||||
assert parser.can_valid
|
||||
assert parser.vl["LKAS_HUD"]["BARRIERS"] == 0
|
||||
assert parser.vl["LKAS_HUD"]["LEFT_LINE"] == 0
|
||||
assert parser.vl["LKAS_HUD"]["RIGHT_LINE"] == 0
|
||||
|
||||
def test_interceptor_stop_and_go_holds_small_launch_at_standstill(self):
|
||||
controller = self._make_controller()
|
||||
controller.CP.enableGasInterceptorDEPRECATED = True
|
||||
controller.accel = 0.3
|
||||
|
||||
gas_cmd = controller._compute_interceptor_gas_cmd(
|
||||
SimpleNamespace(longActive=True),
|
||||
SimpleNamespace(out=SimpleNamespace(standstill=True, vEgo=0.0)),
|
||||
)
|
||||
|
||||
assert gas_cmd == 0.12
|
||||
|
||||
def test_interceptor_non_stop_and_go_scales_with_accel_request(self):
|
||||
controller = self._make_controller()
|
||||
controller.CP.enableGasInterceptorDEPRECATED = True
|
||||
controller.CP.carFingerprint = CAR.TOYOTA_AVALON_2019
|
||||
controller.CP.minEnableSpeed = 8.5
|
||||
controller.accel = 0.8
|
||||
|
||||
gas_cmd = controller._compute_interceptor_gas_cmd(
|
||||
SimpleNamespace(longActive=True),
|
||||
SimpleNamespace(out=SimpleNamespace(standstill=False, vEgo=8.0)),
|
||||
)
|
||||
|
||||
assert 0.0 < gas_cmd <= 0.5
|
||||
|
||||
def test_interceptor_disabled_returns_zero(self):
|
||||
controller = self._make_controller()
|
||||
controller.accel = 1.0
|
||||
|
||||
gas_cmd = controller._compute_interceptor_gas_cmd(
|
||||
SimpleNamespace(longActive=True),
|
||||
SimpleNamespace(out=SimpleNamespace(standstill=False, vEgo=8.0)),
|
||||
)
|
||||
|
||||
assert gas_cmd == 0.0
|
||||
|
||||
def test_interceptor_comfort_limit_keeps_positive_target_out_of_coast(self):
|
||||
limited = limit_interceptor_pcm_accel(-0.30, 0.70, False, 8.5)
|
||||
|
||||
assert limited > 0.0
|
||||
assert limited < 0.70
|
||||
|
||||
def test_interceptor_comfort_limit_keeps_mild_brake_request_negative(self):
|
||||
limited = limit_interceptor_pcm_accel(0.25, -0.35, False, 8.5)
|
||||
|
||||
assert limited < 0.0
|
||||
assert limited > -0.35
|
||||
|
||||
def test_interceptor_comfort_limit_bypasses_harder_braking(self):
|
||||
original = -1.80
|
||||
limited = limit_interceptor_pcm_accel(original, -1.80, False, 8.5)
|
||||
|
||||
assert limited == original
|
||||
|
||||
def test_interceptor_comfort_limit_prevents_positive_target_from_crossing_negative(self):
|
||||
limited = limit_interceptor_pcm_accel(-2.0, 1.7, False, 7.5)
|
||||
|
||||
assert limited >= 0.0
|
||||
|
||||
def test_interceptor_comfort_limit_prevents_negative_target_from_crossing_positive(self):
|
||||
limited = limit_interceptor_pcm_accel(0.8, -0.7, False, 7.5)
|
||||
|
||||
assert limited <= 0.0
|
||||
|
||||
def test_interceptor_stopping_limit_softens_no_lead_final_crawl(self):
|
||||
limited = limit_interceptor_stopping_accel(-1.48, -1.48, True, 0.5, False)
|
||||
|
||||
assert limited > -1.48
|
||||
assert limited == -0.82
|
||||
|
||||
def test_interceptor_stopping_limit_tracks_softer_target_near_standstill(self):
|
||||
limited = limit_interceptor_stopping_accel(-1.86, -0.63, True, 0.5, False)
|
||||
|
||||
assert limited > -1.0
|
||||
assert limited == -0.73
|
||||
|
||||
def test_interceptor_stopping_limit_keeps_visible_lead_stop_untouched(self):
|
||||
limited = limit_interceptor_stopping_accel(-1.48, -0.63, True, 0.5, True)
|
||||
|
||||
assert limited == -1.48
|
||||
|
||||
def test_interceptor_stopping_limit_keeps_higher_speed_stop_untouched(self):
|
||||
limited = limit_interceptor_stopping_accel(-1.48, -0.63, True, 3.0, False)
|
||||
|
||||
assert limited == -1.48
|
||||
|
||||
def test_interceptor_stopping_limit_softens_low_speed_no_lead_stop_before_final_crawl(self):
|
||||
limited = limit_interceptor_stopping_accel(-1.55, -0.66, True, 1.45, False)
|
||||
|
||||
assert abs(limited - (-0.8075)) < 1e-6
|
||||
|
||||
def test_avalon_pedal_params_raise_delay_and_soften_stop(self):
|
||||
CP = CarInterface.get_params(
|
||||
CAR.TOYOTA_AVALON_2019,
|
||||
{0: {0x2FF: 8, 0x201: 8}},
|
||||
[],
|
||||
True,
|
||||
False,
|
||||
False,
|
||||
None,
|
||||
)
|
||||
|
||||
assert CP.enableGasInterceptorDEPRECATED
|
||||
assert abs(CP.longitudinalActuatorDelay - 0.2) < 1e-6
|
||||
assert CP.stopAccel == -1.5
|
||||
|
||||
|
||||
class TestToyotaCarState:
|
||||
def test_interceptor_gas_pressed_threshold(self):
|
||||
cp = SimpleNamespace(vl={
|
||||
"GAS_SENSOR": {
|
||||
"INTERCEPTOR_GAS": 900,
|
||||
"INTERCEPTOR_GAS2": 910,
|
||||
}
|
||||
})
|
||||
assert calculate_interceptor_gas_pressed(cp) is True
|
||||
|
||||
cp = SimpleNamespace(vl={
|
||||
"GAS_SENSOR": {
|
||||
"INTERCEPTOR_GAS": 700,
|
||||
"INTERCEPTOR_GAS2": 710,
|
||||
}
|
||||
})
|
||||
assert calculate_interceptor_gas_pressed(cp) is False
|
||||
|
||||
@@ -110,13 +110,13 @@ def create_fcw_command(packer, fcw):
|
||||
return packer.make_can_msg("PCS_HUD", 0, values)
|
||||
|
||||
|
||||
def create_ui_command(packer, steer, chime, left_line, right_line, left_lane_depart, right_lane_depart, enabled, stock_lkas_hud):
|
||||
def create_ui_command(packer, steer, chime, left_line, right_line, left_lane_depart, right_lane_depart, stock_lkas_hud, lat_active):
|
||||
values = {
|
||||
"TWO_BEEPS": chime,
|
||||
"LDA_ALERT": steer,
|
||||
"RIGHT_LINE": 3 if right_lane_depart else 1 if right_line else 2,
|
||||
"LEFT_LINE": 3 if left_lane_depart else 1 if left_line else 2,
|
||||
"BARRIERS": 1 if enabled else 0,
|
||||
"RIGHT_LINE": 0 if not lat_active else 3 if right_lane_depart else 1 if right_line else 2,
|
||||
"LEFT_LINE": 0 if not lat_active else 3 if left_lane_depart else 1 if left_line else 2,
|
||||
"BARRIERS": 1 if lat_active else 0,
|
||||
|
||||
# static signals
|
||||
"SET_ME_X02": 2,
|
||||
|
||||
@@ -56,6 +56,7 @@ class ToyotaSafetyFlags(IntFlag):
|
||||
STOCK_LONGITUDINAL = (2 << 8)
|
||||
LTA = (4 << 8)
|
||||
SECOC = (8 << 8)
|
||||
LONG_FILTER = (16 << 8)
|
||||
|
||||
|
||||
class ToyotaFlags(IntFlag):
|
||||
|
||||
@@ -393,16 +393,30 @@ BO_ 437 FR_CMR_03_50ms: 32 FR_CMR
|
||||
BO_ 442 BLINDSPOTS_REAR_CORNERS: 24 XXX
|
||||
SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX
|
||||
SG_ COUNTER : 16|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ LEFT_BLOCKED : 24|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ LEFT_MB : 30|1@0+ (1,0) [0|3] "" XXX
|
||||
SG_ MORE_LEFT_PROB : 32|1@1+ (1,0) [0|3] "" XXX
|
||||
SG_ BCW_Sta : 24|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ BCW_OnOffEquipSta : 28|2@1+ (1,0) [0|3] "" XXX
|
||||
SG_ BCW_LtIndSta : 31|2@0+ (1,0) [0|3] "" XXX
|
||||
SG_ BCW_RtIndSta : 32|2@1+ (1,0) [0|3] "" XXX
|
||||
SG_ BCW_LtSndWrngSta : 34|2@1+ (1,0) [0|3] "" XXX
|
||||
SG_ BCW_RtSndWrngSta : 36|10@1+ (1,0) [0|1023] "" XXX
|
||||
SG_ FL_INDICATOR : 46|6@0+ (1,0) [0|1] "" XXX
|
||||
SG_ FR_INDICATOR : 54|6@0+ (1,0) [0|63] "" XXX
|
||||
SG_ RIGHT_BLOCKED : 64|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ COLLISION_AVOIDANCE_ACTIVE : 68|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ NEW_SIGNAL_2 : 96|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ FL_INDICATOR_ALT : 138|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ FR_INDICATOR_ALT : 141|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ FR_INDICATOR : 54|2@0+ (1,0) [0|3] "" XXX
|
||||
SG_ BCW_SnstvtyModRetVal : 56|3@1+ (1,0) [0|7] "" XXX
|
||||
SG_ BCW_IndSta : 59|3@1+ (1,0) [0|7] "" XXX
|
||||
SG_ BCA_OnOffEquip2Sta : 62|2@1+ (1,0) [0|3] "" XXX
|
||||
SG_ BCA_Sta : 64|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ BCA_OnOffEquipSta : 68|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ BCA_DRV_WarnSta : 76|4@1+ (1,0) [0|15] "" XXX
|
||||
SG_ BCA_Plus_Deccel_Req : 80|4@1+ (1,0) [0|15] "" XXX
|
||||
SG_ BCA_Plus_BrkCmdSta : 84|2@1+ (1,0) [0|3] "" XXX
|
||||
SG_ BCA_Plus_LtWrngSta : 86|2@1+ (1,0) [0|3] "" XXX
|
||||
SG_ BCA_Plus_RtWrngSta : 88|5@1+ (1,0) [0|31] "" XXX
|
||||
SG_ BCA_Plus_FuncStat : 93|3@1+ (1,0) [0|7] "" XXX
|
||||
SG_ BCA_Plus_Sta : 96|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ Brake_Control_RL : 110|2@1+ (1,0) [0|3] "" XXX
|
||||
SG_ Brake_Control_RR : 118|2@1+ (1,0) [0|3] "" XXX
|
||||
SG_ OSMrrLamp_LtIndSta : 128|2@1+ (1,0) [0|3] "" XXX
|
||||
SG_ OSMrrLamp_RtIndSta : 130|6@1+ (1,0) [0|63] "" XXX
|
||||
|
||||
BO_ 463 CRUISE_BUTTONS: 8 XXX
|
||||
SG_ _CHECKSUM : 0|8@1+ (1,0) [0|65535] "" XXX
|
||||
@@ -622,6 +636,7 @@ BO_ 866 CAM_0x362: 32 CAMERA
|
||||
BO_ 874 BLINDSPOTS_FRONT_CORNER_2: 16 XXX
|
||||
SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX
|
||||
SG_ COUNTER : 16|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ SIDE_DETECT_STATE : 24|8@1+ (1,0) [0|255] "" XXX
|
||||
|
||||
BO_ 896 ADAS_0x380: 24 XXX
|
||||
SG_ STOP_SIGN : 83|1@1+ (1,0) [0|1] "" XXX
|
||||
|
||||
@@ -188,6 +188,50 @@ BO_ 640 EMS13: 8 XXX
|
||||
SG_ TCO : 24|8@1+ (0.75,-48) [0|0] "" XXX
|
||||
SG_ TIA : 48|8@1+ (0.75,-48) [0|0] "" XXX
|
||||
|
||||
BO_ 683 ESCC: 8 XXX
|
||||
SG_ FCA_CmdAct : 0|1@1+ (1,0) [0|1] "" ESC
|
||||
SG_ CF_VSM_Warn_FCA11 : 1|2@1+ (1,0) [0|3] "" ACU,CLU,ESC
|
||||
SG_ AEB_CmdAct : 3|1@1+ (1,0) [0|1] "" ESC
|
||||
SG_ CF_VSM_Warn_SCC12 : 4|2@1+ (1,0) [0|3] "" CLU,ESC,IAP
|
||||
SG_ CF_VSM_DecCmdAct_SCC12 : 6|1@1+ (1,0) [0|1] "" ESC
|
||||
SG_ CF_VSM_DecCmdAct_FCA11 : 7|1@1+ (1,0) [0|1] "" ESC
|
||||
SG_ CR_VSM_DecCmd_SCC12 : 8|8@1+ (0.01,0) [0|2.55] "g" ESC
|
||||
SG_ ObjValid : 16|1@1+ (1,0) [0|1] "" CLU,ESC,TCU
|
||||
SG_ ACC_ObjStatus : 17|2@1+ (1,0) [0|3] "" ABS,ESC
|
||||
SG_ ACC_ObjRelSpd : 40|9@1+ (0.1,-16.4) [0|0] "m/s" ABS,ESC
|
||||
SG_ ACC_ObjDist : 24|11@1+ (0.1,0) [0|204.7] "m" ABS,ESC
|
||||
SG_ CR_VSM_DecCmd_FCA11 : 56|8@1+ (0.01,0) [0|2.55] "g" ESC
|
||||
|
||||
BO_ 2016 ESCC_DEBUG: 8 XXX
|
||||
SG_ safety_mode : 0|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ watchdog_fails : 8|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ fwd_hook_count : 16|16@1+ (1,0) [0|255] "" XXX
|
||||
SG_ fifo_overrun : 32|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ fifo_overrun_count : 40|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ can3_queue : 48|16@1+ (1,0) [0|255] "" XXX
|
||||
|
||||
BO_ 2017 ESCC_DEBUG2: 8 XXX
|
||||
SG_ CAN1_TME0 : 26|1@1+ (1,0) [0|1] "" XXX
|
||||
SG_ CAN1_TME1 : 27|1@1+ (1,0) [0|1] "" XXX
|
||||
SG_ CAN1_TME2 : 28|1@1+ (1,0) [0|1] "" XXX
|
||||
SG_ CAN1_ALST0 : 2|1@1+ (1,0) [0|1] "" XXX
|
||||
SG_ CAN1_ALST1 : 10|1@1+ (1,0) [0|1] "" XXX
|
||||
SG_ CAN1_ALST2 : 18|1@1+ (1,0) [0|1] "" XXX
|
||||
SG_ CAN1_TERR0 : 1|1@1+ (1,0) [0|1] "" XXX
|
||||
SG_ CAN1_TERR1 : 9|1@1+ (1,0) [0|1] "" XXX
|
||||
SG_ CAN1_TERR2 : 17|1@1+ (1,0) [0|1] "" XXX
|
||||
SG_ CAN1_RQCP0 : 0|1@1+ (1,0) [0|1] "" XXX
|
||||
SG_ CAN1_RQCP1 : 8|1@1+ (1,0) [0|1] "" XXX
|
||||
SG_ CAN1_RQCP2 : 16|1@1+ (1,0) [0|1] "" XXX
|
||||
SG_ CAN1_TX_ERR_CNT : 32|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ CAN1_RX_ERR_CNT : 40|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ CAN1_EWGF : 48|1@1+ (1,0) [0|1] "" XXX
|
||||
SG_ CAN1_EPVF : 49|1@1+ (1,0) [0|1] "" XXX
|
||||
SG_ CAN1_BOFF : 50|1@1+ (1,0) [0|1] "" XXX
|
||||
SG_ CAN1_LEC : 56|3@1+ (1,0) [0|7] "" XXX
|
||||
|
||||
VAL_ 2017 CAN1_LEC 0 "No Error" 1 "Stuff Error" 2 "Form Error" 3 "Ack Error" 4 "Bit Recessive Error" 5 "Bit Dominant Error" 6 "CRC Error" 7 "Set by Software" ;
|
||||
|
||||
BO_ 688 SAS11: 5 XXX
|
||||
SG_ CheckSum : 36|4@1+ (1,0) [0|0] "" XXX
|
||||
SG_ MsgCount : 32|4@1+ (1,0) [0|0] "" XXX
|
||||
@@ -256,7 +300,7 @@ BO_ 854 M_356: 8 XXX
|
||||
SG_ PAINT4 : 38|1@0+ (1,0) [0|0] "" XXX
|
||||
|
||||
BO_ 867 RADAR_0x363: 8 XXX
|
||||
SG_ _CHECKSUM : 0|8@1+ (1,0) [0|0] "" XXX
|
||||
SG_ CHECKSUM : 0|8@1+ (1,0) [0|0] "" XXX
|
||||
SG_ COUNTER : 12|4@1+ (1,0) [0|0] "" XXX
|
||||
SG_ FCA_ESA : 8|2@1+ (1,0) [0|3] "" XXX
|
||||
SG_ BYTE2 : 16|8@1+ (1,0) [0|0] "" XXX
|
||||
@@ -266,6 +310,17 @@ BO_ 867 RADAR_0x363: 8 XXX
|
||||
SG_ BYTE6 : 48|8@1+ (1,0) [0|0] "" XXX
|
||||
SG_ BYTE7 : 56|8@1+ (1,0) [0|0] "" XXX
|
||||
|
||||
BO_ 868 ALERTS_364: 8 XXX
|
||||
SG_ CHECKSUM : 0|8@1+ (1,0) [0|0] "" XXX
|
||||
SG_ COUNTER : 12|4@1+ (1,0) [0|0] "" XXX
|
||||
SG_ BYTE2 : 16|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ BYTE3 : 24|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ DAW_Status : 35|3@1+ (1,0) [0|0] "" XXX
|
||||
SG_ DAW_Warning : 39|1@1+ (1,0) [0|7] "" XXX
|
||||
SG_ BYTE5 : 40|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ BYTE6 : 48|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ BYTE7 : 56|8@1+ (1,0) [0|255] "" XXX
|
||||
|
||||
BO_ 870 EMS_366: 8 XXX
|
||||
SG_ N : 8|16@1+ (0.25,0) [0|0] "" XXX
|
||||
SG_ SWI_IGK : 48|1@0+ (1,0) [0|0] "" XXX
|
||||
@@ -275,6 +330,7 @@ BO_ 870 EMS_366: 8 XXX
|
||||
|
||||
BO_ 871 LVR12: 8 XXX
|
||||
SG_ CF_Lvr_CruiseSet : 0|8@1+ (1,0) [0|0] "" XXX
|
||||
SG_ AUTO_START_STOP_STATUS : 8|2@1+ (1,0) [0|3] "" XXX
|
||||
SG_ CF_Lvr_Gear : 32|4@1+ (1,0) [0|0] "" XXX
|
||||
|
||||
BO_ 872 LVR11: 8 XXX
|
||||
@@ -329,26 +385,29 @@ BO_ 903 WHL_PUL11: 6 XXX
|
||||
SG_ WHL_PUL_RR : 24|8@1+ (0.5,0) [0|0] "" XXX
|
||||
|
||||
BO_ 905 SCC14: 8 XXX
|
||||
SG_ ACC_ObjLatPos : 16|9@1+ (0.1,-20) [-20|31.1] "m" ABS,ESC
|
||||
SG_ _CHECKSUM : 0|8@1+ (1,0) [0|0] "" XXX
|
||||
SG_ ACC_ObjRelSpd : 16|9@1+ (0.1,-20) [-20|31.1] "m/s" ABS,ESC
|
||||
SG_ CHECKSUM : 0|8@1+ (1,0) [0|0] "" XXX
|
||||
SG_ COUNTER : 12|4@1+ (1,0) [0|0] "" XXX
|
||||
SG_ ObjGap : 51|3@1+ (1,0) [0|255] "" CLU,HUD,ESC
|
||||
SG_ ObjValid : 27|1@1+ (1,0) [0|1] "" CLU,ESC,TCU
|
||||
SG_ ObjStatus : 51|3@1+ (1,0) [0|7] "" CLU,HUD,ESC
|
||||
|
||||
BO_ 909 FCA11: 8 XXX
|
||||
SG_ CR_FCA_ChkSum : 0|8@1+ (1,0) [0|0] "" XXX
|
||||
SG_ NEW_SIGNAL_1 : 8|4@1+ (1,0) [0|0] "" XXX
|
||||
SG_ CR_FCA_Alive : 12|4@1+ (1,0) [0|0] "" XXX
|
||||
SG_ BYTE2 : 16|8@1+ (1,0) [0|0] "" XXX
|
||||
SG_ BYTE3 : 24|8@1+ (1,0) [0|0] "" XXX
|
||||
SG_ BYTE4 : 32|8@1+ (1,0) [0|0] "" XXX
|
||||
SG_ BYTE5 : 40|8@1+ (1,0) [0|0] "" XXX
|
||||
SG_ BYTE6 : 48|8@1+ (1,0) [0|0] "" XXX
|
||||
SG_ BYTE7 : 56|8@1+ (1,0) [0|0] "" XXX
|
||||
SG_ CHECKSUM : 0|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ COUNTER : 12|4@1+ (1,0) [0|0] "" XXX
|
||||
SG_ aeb_cmd_act : 16|1@1+ (1,0) [0|1] "" XXX
|
||||
SG_ fca_cmd_act : 19|1@1+ (1,0) [0|1] "" XXX
|
||||
SG_ fca_fault : 32|2@1+ (1,0) [0|3] "" XXX
|
||||
SG_ cr_vsm_deccmd : 38|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ NEW_SIGNAL_2 : 47|1@1+ (1,0) [0|1] "" XXX
|
||||
SG_ cf_vsm_deccmdact : 48|7@1+ (1,-127) [0|15] "" XXX
|
||||
SG_ cf_vsm_warn_1 : 55|1@1+ (1,0) [0|0] "" XXX
|
||||
SG_ cf_vsm_warn_2 : 57|1@1+ (1,0) [0|1] "" XXX
|
||||
SG_ cf_vsm_warn_3 : 62|1@1+ (1,0) [0|0] "" XXX
|
||||
|
||||
BO_ 913 BCM_PO_11: 8 XXX
|
||||
SG_ BCM_Door_Dri_Status : 5|1@0+ (1,0) [0|0] "" XXX
|
||||
SG_ BCM_Shift_R_MT_SW_Status : 39|2@0+ (1,0) [0|0] "" XXX
|
||||
SG_ LFA_Pressed : 4|1@0+ (1,0) [0|0] "" XXX
|
||||
SG_ LDA_BTN : 4|1@0+ (1,0) [0|0] "" XXX
|
||||
|
||||
BO_ 916 TCS13: 8 XXX
|
||||
SG_ ACCEL_REF_ACC : 32|11@1+ (0.01,-10.23) [0|0] "" XXX
|
||||
@@ -359,7 +418,7 @@ BO_ 916 TCS13: 8 XXX
|
||||
SG_ AliveCounterTCS : 13|3@1+ (1,0) [0|0] "" XXX
|
||||
SG_ BrakeLight : 11|1@1+ (1,0) [0|0] "" XXX
|
||||
SG_ CF_DriBkeStat : 60|1@1+ (1,0) [0|0] "" XXX
|
||||
SG_ CF_VSM_Avail : 57|2@1+ (1,0) [0|0] "" XXX
|
||||
SG_ CF_FCW_AEB_Status : 57|2@1+ (1,0) [0|0] "" XXX
|
||||
SG_ CF_VSM_Coded : 56|1@1+ (1,0) [0|0] "" XXX
|
||||
SG_ CF_VSM_ConfSwi : 61|2@1+ (1,0) [0|0] "" XXX
|
||||
SG_ CF_VSM_Handshake : 59|1@1+ (1,0) [0|0] "" XXX
|
||||
@@ -378,7 +437,7 @@ BO_ 916 TCS13: 8 XXX
|
||||
SG_ aBasis : 0|11@1+ (0.01,-10.23) [0|0] "" XXX
|
||||
|
||||
BO_ 920 RADAR_0x398: 8 XXX
|
||||
SG_ _CHECKSUM : 0|8@1+ (1,0) [0|0] "" XXX
|
||||
SG_ CHECKSUM : 0|8@1+ (1,0) [0|0] "" XXX
|
||||
SG_ NEW_SIGNAL_1 : 8|4@1+ (1,0) [0|0] "" XXX
|
||||
SG_ COUNTER : 12|4@1+ (1,0) [0|0] "" XXX
|
||||
SG_ BYTE2 : 16|8@1+ (1,0) [0|0] "" XXX
|
||||
@@ -389,7 +448,7 @@ BO_ 920 RADAR_0x398: 8 XXX
|
||||
SG_ BYTE7 : 56|8@1+ (1,0) [0|0] "" XXX
|
||||
|
||||
BO_ 921 RADAR_0x399: 8 XXX
|
||||
SG_ _CHECKSUM : 0|8@1+ (1,0) [0|0] "" XXX
|
||||
SG_ CHECKSUM : 0|8@1+ (1,0) [0|0] "" XXX
|
||||
SG_ NEW_SIGNAL_1 : 8|4@1+ (1,0) [0|0] "" XXX
|
||||
SG_ COUNTER : 12|4@1+ (1,0) [0|0] "" XXX
|
||||
SG_ BYTE2 : 16|8@1+ (1,0) [0|0] "" XXX
|
||||
@@ -400,7 +459,7 @@ BO_ 921 RADAR_0x399: 8 XXX
|
||||
SG_ BYTE7 : 56|8@1+ (1,0) [0|0] "" XXX
|
||||
|
||||
BO_ 922 RADAR_0x39a: 8 XXX
|
||||
SG_ _CHECKSUM : 0|8@1+ (1,0) [0|0] "" XXX
|
||||
SG_ CHECKSUM : 0|8@1+ (1,0) [0|0] "" XXX
|
||||
SG_ NEW_SIGNAL_1 : 8|4@1+ (1,0) [0|0] "" XXX
|
||||
SG_ COUNTER : 12|4@1+ (1,0) [0|0] "" XXX
|
||||
SG_ BYTE2 : 16|8@1+ (1,0) [0|0] "" XXX
|
||||
@@ -411,7 +470,7 @@ BO_ 922 RADAR_0x39a: 8 XXX
|
||||
SG_ BYTE7 : 56|8@1+ (1,0) [0|0] "" XXX
|
||||
|
||||
BO_ 923 RADAR_0x39b: 8 XXX
|
||||
SG_ _CHECKSUM : 0|8@1+ (1,0) [0|0] "" XXX
|
||||
SG_ CHECKSUM : 0|8@1+ (1,0) [0|0] "" XXX
|
||||
SG_ NEW_SIGNAL_1 : 8|4@1+ (1,0) [0|0] "" XXX
|
||||
SG_ COUNTER : 12|4@1+ (1,0) [0|0] "" XXX
|
||||
SG_ BYTE2 : 16|8@1+ (1,0) [0|0] "" XXX
|
||||
@@ -422,7 +481,7 @@ BO_ 923 RADAR_0x39b: 8 XXX
|
||||
SG_ BYTE7 : 56|8@1+ (1,0) [0|0] "" XXX
|
||||
|
||||
BO_ 924 RADAR_0x39c: 8 XXX
|
||||
SG_ _CHECKSUM : 0|8@1+ (1,0) [0|0] "" XXX
|
||||
SG_ CHECKSUM : 0|8@1+ (1,0) [0|0] "" XXX
|
||||
SG_ NEW_SIGNAL_1 : 8|4@1+ (1,0) [0|0] "" XXX
|
||||
SG_ COUNTER : 12|4@1+ (1,0) [0|0] "" XXX
|
||||
SG_ BYTE2 : 16|8@1+ (1,0) [0|0] "" XXX
|
||||
@@ -470,25 +529,27 @@ BO_ 1042 ICM_412h: 8 XXX
|
||||
SG_ WarningSoundOutput_3Group : 7|1@0+ (1,0) [0|0] "" XXX
|
||||
|
||||
BO_ 1056 SCC11: 8 XXX
|
||||
SG_ _CHECKSUM : 0|8@1+ (1,0) [0|0] "" XXX
|
||||
SG_ AliveCounterACC : 12|4@1+ (1,0) [0|15] "" CLU,EMS,ESC,TCU
|
||||
SG_ JerkLowerLimit : 50|7@1+ (0.1,0) [0|12.7] "m/s^3" ESC
|
||||
SG_ JerkUpperLimit : 43|7@1+ (0.1,0) [0|12.7] "m/s^3" ESC
|
||||
SG_ ObjValid : 59|1@1+ (1,0) [0|1] "" CLU,ESC,TCU
|
||||
SG_ aReqRaw : 27|11@1+ (0.01,-10.23) [-10.23|10.24] "m/s^2" Vector__XXX
|
||||
SG_ CHECKSUM : 0|8@1+ (1,0) [0|0] "" XXX
|
||||
SG_ COUNTER : 12|4@1+ (1,0) [0|15] "" CLU,EMS,ESC,TCU
|
||||
SG_ aReqValue : 16|11@1+ (0.01,-10.23) [-10.23|10.24] "m/s^2" Vector__XXX
|
||||
SG_ aReqRaw : 27|11@1+ (0.01,-10.23) [-10.23|10.24] "m/s^2" Vector__XXX
|
||||
SG_ JerkUpperLimit : 38|7@1+ (0.1,0) [0|12.7] "m/s^3" ESC
|
||||
SG_ JerkLowerLimit : 45|7@1+ (0.1,0) [0|12.7] "m/s^3" ESC
|
||||
SG_ ComfortBandUpper : 52|6@1+ (0.02,0) [0|1.26] "m/s^2" ESC
|
||||
SG_ ComfortBandLower : 58|6@1+ (0.02,0) [0|1.26] "m/s^2" ESC
|
||||
|
||||
BO_ 1057 SCC12: 8 XXX
|
||||
SG_ ACCMode : 28|2@1+ (1,0) [0|3] "" CLU,HUD,LDWS_LKAS,ESC
|
||||
SG_ ACCMode_Inactive : 30|1@1+ (1,0) [0|1] "" CLU,HUD,LDWS_LKAS,ESC
|
||||
SG_ ACC_ObjDist : 16|11@1+ (0.1,0) [0|204.7] "m" ABS,ESC
|
||||
SG_ CR_VSM_ChkSum : 0|8@1+ (1,0) [0|0] "" XXX
|
||||
SG_ CR_VSM_Alive : 12|4@1+ (1,0) [0|15] "" ESC,PSB
|
||||
SG_ CHECKSUM : 0|8@1+ (1,0) [0|0] "" XXX
|
||||
SG_ COUNTER : 12|4@1+ (1,0) [0|15] "" ESC,PSB
|
||||
SG_ MainMode_ACC : 27|1@1+ (1,0) [0|1] "" CLU,EMS,ESC
|
||||
SG_ SCCInfoDisplay : 32|3@1+ (1,0) [0|7] "" CLU,ESC
|
||||
SG_ TauGapSet : 37|3@1+ (1,0) [0|7] "" CLU,ESC,TCU
|
||||
SG_ VSetDis : 41|8@1+ (1,0) [0|255] "km/h or MPH" CLU,ESC,TCU
|
||||
SG_ StopReq : 49|1@1+ (1,0) [0|1] "" EPB,ESC
|
||||
SG_ ACC_ObjDist_Ref : 50|11@1+ (0.1,0) [0|204.7] "" CLU,HUD,LDWS_LKAS,ESC
|
||||
|
||||
BO_ 1064 _4WD11: 8 XXX
|
||||
SG_ AUTO_ACT : 43|1@1+ (1,0) [0|0] "" XXX
|
||||
@@ -519,7 +580,7 @@ BO_ 1078 PAS11: 4 XXX
|
||||
SG_ CF_Gway_PASSystemOn : 24|2@1+ (1,0) [0|0] "" XXX
|
||||
|
||||
BO_ 1082 RADAR_0x43a: 8 XXX
|
||||
SG_ _CHECKSUM : 0|8@1+ (1,0) [0|0] "" XXX
|
||||
SG_ CHECKSUM : 0|8@1+ (1,0) [0|0] "" XXX
|
||||
SG_ NEW_SIGNAL_1 : 8|4@1+ (1,0) [0|0] "" XXX
|
||||
SG_ COUNTER : 12|4@1+ (1,0) [0|0] "" XXX
|
||||
SG_ BYTE2 : 16|8@1+ (1,0) [0|0] "" XXX
|
||||
@@ -529,6 +590,11 @@ BO_ 1082 RADAR_0x43a: 8 XXX
|
||||
SG_ BYTE6 : 48|8@1+ (1,0) [0|0] "" XXX
|
||||
SG_ BYTE7 : 56|8@1+ (1,0) [0|0] "" XXX
|
||||
|
||||
BO_ 1123 MSG_463: 8 XXX
|
||||
SG_ DRV_MODE_PRIMARY_STATUS : 0|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ DRV_MODE_SECONDARY_STATUS : 8|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ DRV_MODE_EXT_IND : 16|8@1+ (1,0) [0|255] "" XXX
|
||||
|
||||
BO_ 1136 P_STS: 8 XXX
|
||||
SG_ Checksum : 62|2@1+ (1,0) [0|0] "" XXX
|
||||
SG_ Counter : 58|4@1+ (1,0) [0|0] "" XXX
|
||||
@@ -549,7 +615,7 @@ BO_ 1151 ESP11: 6 XXX
|
||||
SG_ _4WD_TQC_LIM : 16|16@1+ (1,0) [0|0] "" XXX
|
||||
|
||||
BO_ 1157 LFAHDA_MFC: 8 XXX
|
||||
SG_ _CHECKSUM : 0|8@1+ (1,0) [0|0] "" XXX
|
||||
SG_ CHECKSUM : 0|8@1+ (1,0) [0|0] "" XXX
|
||||
SG_ NEW_SIGNAL_1 : 8|4@1+ (1,0) [0|0] "" XXX
|
||||
SG_ COUNTER : 12|4@1+ (1,0) [0|0] "" XXX
|
||||
SG_ NEW_SIGNAL_2 : 16|1@0+ (1,0) [0|0] "" XXX
|
||||
@@ -602,15 +668,15 @@ BO_ 1170 EMS19: 8 XXX
|
||||
BO_ 1173 YRS13: 8 XXX
|
||||
SG_ YRS_SeralNo : 16|48@1+ (1,0) [0|0] "" XXX
|
||||
|
||||
BO_ 1186 FRT_RADAR11: 2 XXX
|
||||
SG_ BYTE0 : 0|8@1+ (1,0) [0|0] "" XXX
|
||||
SG_ BYTE1 : 8|8@1+ (1,0) [0|0] "" XXX
|
||||
SG_ BYTE2 : 16|8@1+ (1,0) [0|0] "" XXX
|
||||
SG_ BYTE3 : 24|8@1+ (1,0) [0|0] "" XXX
|
||||
SG_ BYTE4 : 32|8@1+ (1,0) [0|0] "" XXX
|
||||
SG_ BYTE5 : 40|8@1+ (1,0) [0|0] "" XXX
|
||||
SG_ BYTE6 : 48|8@1+ (1,0) [0|0] "" XXX
|
||||
SG_ BYTE7 : 56|8@1+ (1,0) [0|0] "" XXX
|
||||
BO_ 1186 FRT_RADAR11: 8 FCA
|
||||
SG_ BYTE0 : 0|8@1+ (1,0) [0|7] "" LDWS_LKAS,LDW_LKA,ESC
|
||||
SG_ BYTE1 : 8|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ BYTE2 : 16|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ BYTE3 : 24|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ BYTE4 : 32|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ BYTE5 : 40|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ BYTE6 : 48|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ BYTE7 : 56|8@1+ (1,0) [0|255] "" XXX
|
||||
|
||||
BO_ 1265 CLU11: 4 XXX
|
||||
SG_ CF_Clu_AliveCnt1 : 28|4@1+ (1,0) [0|0] "" XXX
|
||||
@@ -686,13 +752,15 @@ BO_ 1322 CLU15: 8 XXX
|
||||
|
||||
BO_ 1342 LKAS12: 8 XXX
|
||||
SG_ CF_LkasDawStatus : 40|3@1+ (1,0) [0|0] "" XXX
|
||||
SG_ CF_LkasTsrSpeed_Display_Navi : 24|8@1+ (1,0) [0|0] "" XXX
|
||||
SG_ CF_Lkas_Daw_USM : 37|3@1+ (1,0) [0|0] "" XXX
|
||||
SG_ CF_Lkas_TsrAddinfo_Display : 32|2@1+ (1,0) [0|0] "" XXX
|
||||
SG_ CF_LkasTsrSpeed_Display_Navi : 32|8@1+ (1,0) [0|0] "" XXX
|
||||
SG_ CF_Lkas_TsrSpeed_Display_Clu : 24|8@1+ (1,0) [0|0] "" XXX
|
||||
SG_ CF_Lkas_TsrSlifOpt : 10|2@1+ (1,0) [0|0] "" XXX
|
||||
SG_ CF_Lkas_TsrSpeed_Display_Clu : 16|8@1+ (1,0) [0|0] "" XXX
|
||||
SG_ _CHECKSUM : 0|8@1+ (1,0) [0|0] "" XXX
|
||||
SG_ CHECKSUM : 0|8@1+ (1,0) [0|0] "" XXX
|
||||
SG_ COUNTER : 12|4@1+ (1,0) [0|0] "" XXX
|
||||
SG_ CF_SLA_Avail : 48|2@1+ (1,0) [0|3] "" XXX
|
||||
SG_ CF_SLA_AdjDisp : 51|2@1+ (1,0) [0|3] "" XXX
|
||||
SG_ Sign_detected : 56|5@1+ (1,0) [0|255] "" XXX
|
||||
SG_ CF_SLA_ACT : 61|1@0+ (1,0) [0|1] "" XXX
|
||||
|
||||
BO_ 1345 CGW1: 8 XXX
|
||||
SG_ CF_Gway_ALightStat : 37|1@1+ (1,0) [0|0] "" XXX
|
||||
@@ -852,14 +920,26 @@ BO_ 1456 CLU12: 4 XXX
|
||||
SG_ CF_Clu_Odometer : 0|24@1+ (0.1,0) [0|0] "" XXX
|
||||
|
||||
|
||||
CM_ SG_ 1123 DRV_MODE_PRIMARY_STATUS "Indicates the current drive mode selected by the driver (Smart, Sport, Comfort, ECO, Snow). This is a status-only signal, not writable.";
|
||||
CM_ SG_ 1123 DRV_MODE_SECONDARY_STATUS "Indicates a secondary status modifier, often used in combination with PRIMARY to disambiguate ECO vs Snow. Status-only.";
|
||||
CM_ SG_ 1123 DRV_MODE_EXT_IND "Drive mode extension indicator (e.g. Snow Mode flag). Status-only signal.";
|
||||
CM_ SG_ 1348 SpeedLim_Nav_Clu "Speed limit displayed on Nav, Cluster and HUD";
|
||||
CM_ SG_ 1342 CF_SLA_ACT "Speed limit assist active flag";
|
||||
CM_ SG_ 1342 CF_SLA_AdjDisp "Display of the automatically adjusting speed message";
|
||||
CM_ SG_ 1342 CF_SLA_Avail "Up or Down arrow showing speed limit assist available";
|
||||
|
||||
VAL_ 871 CF_Lvr_Gear 12 "T" 5 "D" 8 "S" 6 "N" 7 "R" 0 "P";
|
||||
VAL_ 871 AUTO_START_STOP_STATUS 3 "Off" 1 "On";
|
||||
VAL_ 909 CF_VSM_Warn 2 "FCW" 3 "AEB";
|
||||
VAL_ 916 ACCEnable 0 "SCC ready" 1 "SCC temp fault" 2 "SCC permanent fault" 3 "SCC permanent fault, communication issue";
|
||||
VAL_ 916 CF_FCW_AEB_Status 0 "Normal" 3 "Fault";
|
||||
VAL_ 1057 ACCMode 0 "off" 1 "enabled" 2 "driver_override" 3 "off_maybe_fault" 4 "cancelled";
|
||||
VAL_ 1123 DRV_MODE_PRIMARY_STATUS 49 "Smart" 33 "Sport" 1 "Comfort" 17 "ECO_or_Snow";
|
||||
VAL_ 1123 DRV_MODE_SECONDARY_STATUS 1 "Normal" 17 "Snow";
|
||||
VAL_ 1123 DRV_MODE_EXT_IND 0 "Standard" 16 "SnowFlag";
|
||||
VAL_ 1157 HDA_Icon_State 0 "no_hda" 1 "white_hda" 2 "green_hda";
|
||||
VAL_ 1157 LFA_SysWarning 0 "no_message" 1 "switching_to_hda" 2 "switching_to_scc" 3 "lfa_error" 4 "check_hda" 5 "keep_hands_on_wheel_orange" 6 "keep_hands_on_wheel_red";
|
||||
VAL_ 1157 LFA_Icon_State 0 "no_wheel" 1 "white_wheel" 2 "green_wheel" 3 "green_wheel_blink";
|
||||
VAL_ 1157 HDA_SysWarning 0 "no_message" 1 "driving_convenience_systems_cancelled" 2 "highway_drive_assist_system_cancelled";
|
||||
VAL_ 1322 CF_Clu_Gear 1 "P" 2 "R" 4 "N" 8 "D";
|
||||
VAL_ 1342 Sign_detected 1 "Slow/Caution" 8 "Stop" 9 "Yield"
|
||||
@@ -630,16 +630,30 @@ BO_ 437 FR_CMR_03_50ms: 32 FR_CMR
|
||||
BO_ 442 BLINDSPOTS_REAR_CORNERS: 24 XXX
|
||||
SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX
|
||||
SG_ COUNTER : 16|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ LEFT_BLOCKED : 24|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ LEFT_MB : 30|1@0+ (1,0) [0|3] "" XXX
|
||||
SG_ MORE_LEFT_PROB : 32|1@1+ (1,0) [0|3] "" XXX
|
||||
SG_ BCW_Sta : 24|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ BCW_OnOffEquipSta : 28|2@1+ (1,0) [0|3] "" XXX
|
||||
SG_ BCW_LtIndSta : 31|2@0+ (1,0) [0|3] "" XXX
|
||||
SG_ BCW_RtIndSta : 32|2@1+ (1,0) [0|3] "" XXX
|
||||
SG_ BCW_LtSndWrngSta : 34|2@1+ (1,0) [0|3] "" XXX
|
||||
SG_ BCW_RtSndWrngSta : 36|10@1+ (1,0) [0|1023] "" XXX
|
||||
SG_ FL_INDICATOR : 46|6@0+ (1,0) [0|1] "" XXX
|
||||
SG_ FR_INDICATOR : 54|6@0+ (1,0) [0|63] "" XXX
|
||||
SG_ RIGHT_BLOCKED : 64|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ COLLISION_AVOIDANCE_ACTIVE : 68|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ NEW_SIGNAL_2 : 96|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ FL_INDICATOR_ALT : 138|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ FR_INDICATOR_ALT : 141|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ FR_INDICATOR : 54|2@0+ (1,0) [0|3] "" XXX
|
||||
SG_ BCW_SnstvtyModRetVal : 56|3@1+ (1,0) [0|7] "" XXX
|
||||
SG_ BCW_IndSta : 59|3@1+ (1,0) [0|7] "" XXX
|
||||
SG_ BCA_OnOffEquip2Sta : 62|2@1+ (1,0) [0|3] "" XXX
|
||||
SG_ BCA_Sta : 64|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ BCA_OnOffEquipSta : 68|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ BCA_DRV_WarnSta : 76|4@1+ (1,0) [0|15] "" XXX
|
||||
SG_ BCA_Plus_Deccel_Req : 80|4@1+ (1,0) [0|15] "" XXX
|
||||
SG_ BCA_Plus_BrkCmdSta : 84|2@1+ (1,0) [0|3] "" XXX
|
||||
SG_ BCA_Plus_LtWrngSta : 86|2@1+ (1,0) [0|3] "" XXX
|
||||
SG_ BCA_Plus_RtWrngSta : 88|5@1+ (1,0) [0|31] "" XXX
|
||||
SG_ BCA_Plus_FuncStat : 93|3@1+ (1,0) [0|7] "" XXX
|
||||
SG_ BCA_Plus_Sta : 96|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ Brake_Control_RL : 110|2@1+ (1,0) [0|3] "" XXX
|
||||
SG_ Brake_Control_RR : 118|2@1+ (1,0) [0|3] "" XXX
|
||||
SG_ OSMrrLamp_LtIndSta : 128|2@1+ (1,0) [0|3] "" XXX
|
||||
SG_ OSMrrLamp_RtIndSta : 130|6@1+ (1,0) [0|63] "" XXX
|
||||
|
||||
BO_ 463 CRUISE_BUTTONS: 8 XXX
|
||||
SG_ _CHECKSUM : 0|8@1+ (1,0) [0|65535] "" XXX
|
||||
@@ -859,6 +873,7 @@ BO_ 866 CAM_0x362: 32 CAMERA
|
||||
BO_ 874 BLINDSPOTS_FRONT_CORNER_2: 16 XXX
|
||||
SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX
|
||||
SG_ COUNTER : 16|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ SIDE_DETECT_STATE : 24|8@1+ (1,0) [0|255] "" XXX
|
||||
|
||||
BO_ 896 ADAS_0x380: 24 XXX
|
||||
SG_ STOP_SIGN : 83|1@1+ (1,0) [0|1] "" XXX
|
||||
|
||||
@@ -1536,6 +1536,10 @@ BO_ 881 E_EMS11: 8 XXX
|
||||
BO_ 1355 EV_PC6: 8 CGW
|
||||
SG_ CF_Vcu_SbwWarnMsg : 16|3@1+ (1,0) [0|7] "" Vector__XXX
|
||||
|
||||
BO_ 1429 E_CRUISE_CONTROL: 8 XXX
|
||||
SG_ CRUISE_LAMP_M : 50|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ CRUISE_LAMP_S : 51|1@0+ (1,0) [0|1] "" XXX
|
||||
|
||||
BO_ 1430 EV_PC2: 8 CGW
|
||||
SG_ CR_Ldc_ActVol_LS_V : 32|8@1+ (0.1,0) [0|0] "V" Vector__XXX
|
||||
|
||||
|
||||
@@ -425,6 +425,50 @@ BO_ 640 EMS13: 8 XXX
|
||||
SG_ TCO : 24|8@1+ (0.75,-48) [0|0] "" XXX
|
||||
SG_ TIA : 48|8@1+ (0.75,-48) [0|0] "" XXX
|
||||
|
||||
BO_ 683 ESCC: 8 XXX
|
||||
SG_ FCA_CmdAct : 0|1@1+ (1,0) [0|1] "" ESC
|
||||
SG_ CF_VSM_Warn_FCA11 : 1|2@1+ (1,0) [0|3] "" ACU,CLU,ESC
|
||||
SG_ AEB_CmdAct : 3|1@1+ (1,0) [0|1] "" ESC
|
||||
SG_ CF_VSM_Warn_SCC12 : 4|2@1+ (1,0) [0|3] "" CLU,ESC,IAP
|
||||
SG_ CF_VSM_DecCmdAct_SCC12 : 6|1@1+ (1,0) [0|1] "" ESC
|
||||
SG_ CF_VSM_DecCmdAct_FCA11 : 7|1@1+ (1,0) [0|1] "" ESC
|
||||
SG_ CR_VSM_DecCmd_SCC12 : 8|8@1+ (0.01,0) [0|2.55] "g" ESC
|
||||
SG_ ObjValid : 16|1@1+ (1,0) [0|1] "" CLU,ESC,TCU
|
||||
SG_ ACC_ObjStatus : 17|2@1+ (1,0) [0|3] "" ABS,ESC
|
||||
SG_ ACC_ObjRelSpd : 40|9@1+ (0.1,-16.4) [0|0] "m/s" ABS,ESC
|
||||
SG_ ACC_ObjDist : 24|11@1+ (0.1,0) [0|204.7] "m" ABS,ESC
|
||||
SG_ CR_VSM_DecCmd_FCA11 : 56|8@1+ (0.01,0) [0|2.55] "g" ESC
|
||||
|
||||
BO_ 2016 ESCC_DEBUG: 8 XXX
|
||||
SG_ safety_mode : 0|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ watchdog_fails : 8|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ fwd_hook_count : 16|16@1+ (1,0) [0|255] "" XXX
|
||||
SG_ fifo_overrun : 32|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ fifo_overrun_count : 40|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ can3_queue : 48|16@1+ (1,0) [0|255] "" XXX
|
||||
|
||||
BO_ 2017 ESCC_DEBUG2: 8 XXX
|
||||
SG_ CAN1_TME0 : 26|1@1+ (1,0) [0|1] "" XXX
|
||||
SG_ CAN1_TME1 : 27|1@1+ (1,0) [0|1] "" XXX
|
||||
SG_ CAN1_TME2 : 28|1@1+ (1,0) [0|1] "" XXX
|
||||
SG_ CAN1_ALST0 : 2|1@1+ (1,0) [0|1] "" XXX
|
||||
SG_ CAN1_ALST1 : 10|1@1+ (1,0) [0|1] "" XXX
|
||||
SG_ CAN1_ALST2 : 18|1@1+ (1,0) [0|1] "" XXX
|
||||
SG_ CAN1_TERR0 : 1|1@1+ (1,0) [0|1] "" XXX
|
||||
SG_ CAN1_TERR1 : 9|1@1+ (1,0) [0|1] "" XXX
|
||||
SG_ CAN1_TERR2 : 17|1@1+ (1,0) [0|1] "" XXX
|
||||
SG_ CAN1_RQCP0 : 0|1@1+ (1,0) [0|1] "" XXX
|
||||
SG_ CAN1_RQCP1 : 8|1@1+ (1,0) [0|1] "" XXX
|
||||
SG_ CAN1_RQCP2 : 16|1@1+ (1,0) [0|1] "" XXX
|
||||
SG_ CAN1_TX_ERR_CNT : 32|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ CAN1_RX_ERR_CNT : 40|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ CAN1_EWGF : 48|1@1+ (1,0) [0|1] "" XXX
|
||||
SG_ CAN1_EPVF : 49|1@1+ (1,0) [0|1] "" XXX
|
||||
SG_ CAN1_BOFF : 50|1@1+ (1,0) [0|1] "" XXX
|
||||
SG_ CAN1_LEC : 56|3@1+ (1,0) [0|7] "" XXX
|
||||
|
||||
VAL_ 2017 CAN1_LEC 0 "No Error" 1 "Stuff Error" 2 "Form Error" 3 "Ack Error" 4 "Bit Recessive Error" 5 "Bit Dominant Error" 6 "CRC Error" 7 "Set by Software" ;
|
||||
|
||||
BO_ 688 SAS11: 5 XXX
|
||||
SG_ CheckSum : 36|4@1+ (1,0) [0|0] "" XXX
|
||||
SG_ MsgCount : 32|4@1+ (1,0) [0|0] "" XXX
|
||||
@@ -493,7 +537,7 @@ BO_ 854 M_356: 8 XXX
|
||||
SG_ PAINT4 : 38|1@0+ (1,0) [0|0] "" XXX
|
||||
|
||||
BO_ 867 RADAR_0x363: 8 XXX
|
||||
SG_ _CHECKSUM : 0|8@1+ (1,0) [0|0] "" XXX
|
||||
SG_ CHECKSUM : 0|8@1+ (1,0) [0|0] "" XXX
|
||||
SG_ COUNTER : 12|4@1+ (1,0) [0|0] "" XXX
|
||||
SG_ FCA_ESA : 8|2@1+ (1,0) [0|3] "" XXX
|
||||
SG_ BYTE2 : 16|8@1+ (1,0) [0|0] "" XXX
|
||||
@@ -503,6 +547,17 @@ BO_ 867 RADAR_0x363: 8 XXX
|
||||
SG_ BYTE6 : 48|8@1+ (1,0) [0|0] "" XXX
|
||||
SG_ BYTE7 : 56|8@1+ (1,0) [0|0] "" XXX
|
||||
|
||||
BO_ 868 ALERTS_364: 8 XXX
|
||||
SG_ CHECKSUM : 0|8@1+ (1,0) [0|0] "" XXX
|
||||
SG_ COUNTER : 12|4@1+ (1,0) [0|0] "" XXX
|
||||
SG_ BYTE2 : 16|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ BYTE3 : 24|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ DAW_Status : 35|3@1+ (1,0) [0|0] "" XXX
|
||||
SG_ DAW_Warning : 39|1@1+ (1,0) [0|7] "" XXX
|
||||
SG_ BYTE5 : 40|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ BYTE6 : 48|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ BYTE7 : 56|8@1+ (1,0) [0|255] "" XXX
|
||||
|
||||
BO_ 870 EMS_366: 8 XXX
|
||||
SG_ N : 8|16@1+ (0.25,0) [0|0] "" XXX
|
||||
SG_ SWI_IGK : 48|1@0+ (1,0) [0|0] "" XXX
|
||||
@@ -512,6 +567,7 @@ BO_ 870 EMS_366: 8 XXX
|
||||
|
||||
BO_ 871 LVR12: 8 XXX
|
||||
SG_ CF_Lvr_CruiseSet : 0|8@1+ (1,0) [0|0] "" XXX
|
||||
SG_ AUTO_START_STOP_STATUS : 8|2@1+ (1,0) [0|3] "" XXX
|
||||
SG_ CF_Lvr_Gear : 32|4@1+ (1,0) [0|0] "" XXX
|
||||
|
||||
BO_ 872 LVR11: 8 XXX
|
||||
@@ -566,26 +622,29 @@ BO_ 903 WHL_PUL11: 6 XXX
|
||||
SG_ WHL_PUL_RR : 24|8@1+ (0.5,0) [0|0] "" XXX
|
||||
|
||||
BO_ 905 SCC14: 8 XXX
|
||||
SG_ ACC_ObjLatPos : 16|9@1+ (0.1,-20) [-20|31.1] "m" ABS,ESC
|
||||
SG_ _CHECKSUM : 0|8@1+ (1,0) [0|0] "" XXX
|
||||
SG_ ACC_ObjRelSpd : 16|9@1+ (0.1,-20) [-20|31.1] "m/s" ABS,ESC
|
||||
SG_ CHECKSUM : 0|8@1+ (1,0) [0|0] "" XXX
|
||||
SG_ COUNTER : 12|4@1+ (1,0) [0|0] "" XXX
|
||||
SG_ ObjGap : 51|3@1+ (1,0) [0|255] "" CLU,HUD,ESC
|
||||
SG_ ObjValid : 27|1@1+ (1,0) [0|1] "" CLU,ESC,TCU
|
||||
SG_ ObjStatus : 51|3@1+ (1,0) [0|7] "" CLU,HUD,ESC
|
||||
|
||||
BO_ 909 FCA11: 8 XXX
|
||||
SG_ CR_FCA_ChkSum : 0|8@1+ (1,0) [0|0] "" XXX
|
||||
SG_ NEW_SIGNAL_1 : 8|4@1+ (1,0) [0|0] "" XXX
|
||||
SG_ CR_FCA_Alive : 12|4@1+ (1,0) [0|0] "" XXX
|
||||
SG_ BYTE2 : 16|8@1+ (1,0) [0|0] "" XXX
|
||||
SG_ BYTE3 : 24|8@1+ (1,0) [0|0] "" XXX
|
||||
SG_ BYTE4 : 32|8@1+ (1,0) [0|0] "" XXX
|
||||
SG_ BYTE5 : 40|8@1+ (1,0) [0|0] "" XXX
|
||||
SG_ BYTE6 : 48|8@1+ (1,0) [0|0] "" XXX
|
||||
SG_ BYTE7 : 56|8@1+ (1,0) [0|0] "" XXX
|
||||
SG_ CHECKSUM : 0|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ COUNTER : 12|4@1+ (1,0) [0|0] "" XXX
|
||||
SG_ aeb_cmd_act : 16|1@1+ (1,0) [0|1] "" XXX
|
||||
SG_ fca_cmd_act : 19|1@1+ (1,0) [0|1] "" XXX
|
||||
SG_ fca_fault : 32|2@1+ (1,0) [0|3] "" XXX
|
||||
SG_ cr_vsm_deccmd : 38|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ NEW_SIGNAL_2 : 47|1@1+ (1,0) [0|1] "" XXX
|
||||
SG_ cf_vsm_deccmdact : 48|7@1+ (1,-127) [0|15] "" XXX
|
||||
SG_ cf_vsm_warn_1 : 55|1@1+ (1,0) [0|0] "" XXX
|
||||
SG_ cf_vsm_warn_2 : 57|1@1+ (1,0) [0|1] "" XXX
|
||||
SG_ cf_vsm_warn_3 : 62|1@1+ (1,0) [0|0] "" XXX
|
||||
|
||||
BO_ 913 BCM_PO_11: 8 XXX
|
||||
SG_ BCM_Door_Dri_Status : 5|1@0+ (1,0) [0|0] "" XXX
|
||||
SG_ BCM_Shift_R_MT_SW_Status : 39|2@0+ (1,0) [0|0] "" XXX
|
||||
SG_ LFA_Pressed : 4|1@0+ (1,0) [0|0] "" XXX
|
||||
SG_ LDA_BTN : 4|1@0+ (1,0) [0|0] "" XXX
|
||||
|
||||
BO_ 916 TCS13: 8 XXX
|
||||
SG_ ACCEL_REF_ACC : 32|11@1+ (0.01,-10.23) [0|0] "" XXX
|
||||
@@ -596,7 +655,7 @@ BO_ 916 TCS13: 8 XXX
|
||||
SG_ AliveCounterTCS : 13|3@1+ (1,0) [0|0] "" XXX
|
||||
SG_ BrakeLight : 11|1@1+ (1,0) [0|0] "" XXX
|
||||
SG_ CF_DriBkeStat : 60|1@1+ (1,0) [0|0] "" XXX
|
||||
SG_ CF_VSM_Avail : 57|2@1+ (1,0) [0|0] "" XXX
|
||||
SG_ CF_FCW_AEB_Status : 57|2@1+ (1,0) [0|0] "" XXX
|
||||
SG_ CF_VSM_Coded : 56|1@1+ (1,0) [0|0] "" XXX
|
||||
SG_ CF_VSM_ConfSwi : 61|2@1+ (1,0) [0|0] "" XXX
|
||||
SG_ CF_VSM_Handshake : 59|1@1+ (1,0) [0|0] "" XXX
|
||||
@@ -615,7 +674,7 @@ BO_ 916 TCS13: 8 XXX
|
||||
SG_ aBasis : 0|11@1+ (0.01,-10.23) [0|0] "" XXX
|
||||
|
||||
BO_ 920 RADAR_0x398: 8 XXX
|
||||
SG_ _CHECKSUM : 0|8@1+ (1,0) [0|0] "" XXX
|
||||
SG_ CHECKSUM : 0|8@1+ (1,0) [0|0] "" XXX
|
||||
SG_ NEW_SIGNAL_1 : 8|4@1+ (1,0) [0|0] "" XXX
|
||||
SG_ COUNTER : 12|4@1+ (1,0) [0|0] "" XXX
|
||||
SG_ BYTE2 : 16|8@1+ (1,0) [0|0] "" XXX
|
||||
@@ -626,7 +685,7 @@ BO_ 920 RADAR_0x398: 8 XXX
|
||||
SG_ BYTE7 : 56|8@1+ (1,0) [0|0] "" XXX
|
||||
|
||||
BO_ 921 RADAR_0x399: 8 XXX
|
||||
SG_ _CHECKSUM : 0|8@1+ (1,0) [0|0] "" XXX
|
||||
SG_ CHECKSUM : 0|8@1+ (1,0) [0|0] "" XXX
|
||||
SG_ NEW_SIGNAL_1 : 8|4@1+ (1,0) [0|0] "" XXX
|
||||
SG_ COUNTER : 12|4@1+ (1,0) [0|0] "" XXX
|
||||
SG_ BYTE2 : 16|8@1+ (1,0) [0|0] "" XXX
|
||||
@@ -637,7 +696,7 @@ BO_ 921 RADAR_0x399: 8 XXX
|
||||
SG_ BYTE7 : 56|8@1+ (1,0) [0|0] "" XXX
|
||||
|
||||
BO_ 922 RADAR_0x39a: 8 XXX
|
||||
SG_ _CHECKSUM : 0|8@1+ (1,0) [0|0] "" XXX
|
||||
SG_ CHECKSUM : 0|8@1+ (1,0) [0|0] "" XXX
|
||||
SG_ NEW_SIGNAL_1 : 8|4@1+ (1,0) [0|0] "" XXX
|
||||
SG_ COUNTER : 12|4@1+ (1,0) [0|0] "" XXX
|
||||
SG_ BYTE2 : 16|8@1+ (1,0) [0|0] "" XXX
|
||||
@@ -648,7 +707,7 @@ BO_ 922 RADAR_0x39a: 8 XXX
|
||||
SG_ BYTE7 : 56|8@1+ (1,0) [0|0] "" XXX
|
||||
|
||||
BO_ 923 RADAR_0x39b: 8 XXX
|
||||
SG_ _CHECKSUM : 0|8@1+ (1,0) [0|0] "" XXX
|
||||
SG_ CHECKSUM : 0|8@1+ (1,0) [0|0] "" XXX
|
||||
SG_ NEW_SIGNAL_1 : 8|4@1+ (1,0) [0|0] "" XXX
|
||||
SG_ COUNTER : 12|4@1+ (1,0) [0|0] "" XXX
|
||||
SG_ BYTE2 : 16|8@1+ (1,0) [0|0] "" XXX
|
||||
@@ -659,7 +718,7 @@ BO_ 923 RADAR_0x39b: 8 XXX
|
||||
SG_ BYTE7 : 56|8@1+ (1,0) [0|0] "" XXX
|
||||
|
||||
BO_ 924 RADAR_0x39c: 8 XXX
|
||||
SG_ _CHECKSUM : 0|8@1+ (1,0) [0|0] "" XXX
|
||||
SG_ CHECKSUM : 0|8@1+ (1,0) [0|0] "" XXX
|
||||
SG_ NEW_SIGNAL_1 : 8|4@1+ (1,0) [0|0] "" XXX
|
||||
SG_ COUNTER : 12|4@1+ (1,0) [0|0] "" XXX
|
||||
SG_ BYTE2 : 16|8@1+ (1,0) [0|0] "" XXX
|
||||
@@ -707,25 +766,27 @@ BO_ 1042 ICM_412h: 8 XXX
|
||||
SG_ WarningSoundOutput_3Group : 7|1@0+ (1,0) [0|0] "" XXX
|
||||
|
||||
BO_ 1056 SCC11: 8 XXX
|
||||
SG_ _CHECKSUM : 0|8@1+ (1,0) [0|0] "" XXX
|
||||
SG_ AliveCounterACC : 12|4@1+ (1,0) [0|15] "" CLU,EMS,ESC,TCU
|
||||
SG_ JerkLowerLimit : 50|7@1+ (0.1,0) [0|12.7] "m/s^3" ESC
|
||||
SG_ JerkUpperLimit : 43|7@1+ (0.1,0) [0|12.7] "m/s^3" ESC
|
||||
SG_ ObjValid : 59|1@1+ (1,0) [0|1] "" CLU,ESC,TCU
|
||||
SG_ aReqRaw : 27|11@1+ (0.01,-10.23) [-10.23|10.24] "m/s^2" Vector__XXX
|
||||
SG_ CHECKSUM : 0|8@1+ (1,0) [0|0] "" XXX
|
||||
SG_ COUNTER : 12|4@1+ (1,0) [0|15] "" CLU,EMS,ESC,TCU
|
||||
SG_ aReqValue : 16|11@1+ (0.01,-10.23) [-10.23|10.24] "m/s^2" Vector__XXX
|
||||
SG_ aReqRaw : 27|11@1+ (0.01,-10.23) [-10.23|10.24] "m/s^2" Vector__XXX
|
||||
SG_ JerkUpperLimit : 38|7@1+ (0.1,0) [0|12.7] "m/s^3" ESC
|
||||
SG_ JerkLowerLimit : 45|7@1+ (0.1,0) [0|12.7] "m/s^3" ESC
|
||||
SG_ ComfortBandUpper : 52|6@1+ (0.02,0) [0|1.26] "m/s^2" ESC
|
||||
SG_ ComfortBandLower : 58|6@1+ (0.02,0) [0|1.26] "m/s^2" ESC
|
||||
|
||||
BO_ 1057 SCC12: 8 XXX
|
||||
SG_ ACCMode : 28|2@1+ (1,0) [0|3] "" CLU,HUD,LDWS_LKAS,ESC
|
||||
SG_ ACCMode_Inactive : 30|1@1+ (1,0) [0|1] "" CLU,HUD,LDWS_LKAS,ESC
|
||||
SG_ ACC_ObjDist : 16|11@1+ (0.1,0) [0|204.7] "m" ABS,ESC
|
||||
SG_ CR_VSM_ChkSum : 0|8@1+ (1,0) [0|0] "" XXX
|
||||
SG_ CR_VSM_Alive : 12|4@1+ (1,0) [0|15] "" ESC,PSB
|
||||
SG_ CHECKSUM : 0|8@1+ (1,0) [0|0] "" XXX
|
||||
SG_ COUNTER : 12|4@1+ (1,0) [0|15] "" ESC,PSB
|
||||
SG_ MainMode_ACC : 27|1@1+ (1,0) [0|1] "" CLU,EMS,ESC
|
||||
SG_ SCCInfoDisplay : 32|3@1+ (1,0) [0|7] "" CLU,ESC
|
||||
SG_ TauGapSet : 37|3@1+ (1,0) [0|7] "" CLU,ESC,TCU
|
||||
SG_ VSetDis : 41|8@1+ (1,0) [0|255] "km/h or MPH" CLU,ESC,TCU
|
||||
SG_ StopReq : 49|1@1+ (1,0) [0|1] "" EPB,ESC
|
||||
SG_ ACC_ObjDist_Ref : 50|11@1+ (0.1,0) [0|204.7] "" CLU,HUD,LDWS_LKAS,ESC
|
||||
|
||||
BO_ 1064 _4WD11: 8 XXX
|
||||
SG_ AUTO_ACT : 43|1@1+ (1,0) [0|0] "" XXX
|
||||
@@ -756,7 +817,7 @@ BO_ 1078 PAS11: 4 XXX
|
||||
SG_ CF_Gway_PASSystemOn : 24|2@1+ (1,0) [0|0] "" XXX
|
||||
|
||||
BO_ 1082 RADAR_0x43a: 8 XXX
|
||||
SG_ _CHECKSUM : 0|8@1+ (1,0) [0|0] "" XXX
|
||||
SG_ CHECKSUM : 0|8@1+ (1,0) [0|0] "" XXX
|
||||
SG_ NEW_SIGNAL_1 : 8|4@1+ (1,0) [0|0] "" XXX
|
||||
SG_ COUNTER : 12|4@1+ (1,0) [0|0] "" XXX
|
||||
SG_ BYTE2 : 16|8@1+ (1,0) [0|0] "" XXX
|
||||
@@ -766,6 +827,11 @@ BO_ 1082 RADAR_0x43a: 8 XXX
|
||||
SG_ BYTE6 : 48|8@1+ (1,0) [0|0] "" XXX
|
||||
SG_ BYTE7 : 56|8@1+ (1,0) [0|0] "" XXX
|
||||
|
||||
BO_ 1123 MSG_463: 8 XXX
|
||||
SG_ DRV_MODE_PRIMARY_STATUS : 0|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ DRV_MODE_SECONDARY_STATUS : 8|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ DRV_MODE_EXT_IND : 16|8@1+ (1,0) [0|255] "" XXX
|
||||
|
||||
BO_ 1136 P_STS: 8 XXX
|
||||
SG_ Checksum : 62|2@1+ (1,0) [0|0] "" XXX
|
||||
SG_ Counter : 58|4@1+ (1,0) [0|0] "" XXX
|
||||
@@ -786,7 +852,7 @@ BO_ 1151 ESP11: 6 XXX
|
||||
SG_ _4WD_TQC_LIM : 16|16@1+ (1,0) [0|0] "" XXX
|
||||
|
||||
BO_ 1157 LFAHDA_MFC: 8 XXX
|
||||
SG_ _CHECKSUM : 0|8@1+ (1,0) [0|0] "" XXX
|
||||
SG_ CHECKSUM : 0|8@1+ (1,0) [0|0] "" XXX
|
||||
SG_ NEW_SIGNAL_1 : 8|4@1+ (1,0) [0|0] "" XXX
|
||||
SG_ COUNTER : 12|4@1+ (1,0) [0|0] "" XXX
|
||||
SG_ NEW_SIGNAL_2 : 16|1@0+ (1,0) [0|0] "" XXX
|
||||
@@ -839,15 +905,15 @@ BO_ 1170 EMS19: 8 XXX
|
||||
BO_ 1173 YRS13: 8 XXX
|
||||
SG_ YRS_SeralNo : 16|48@1+ (1,0) [0|0] "" XXX
|
||||
|
||||
BO_ 1186 FRT_RADAR11: 2 XXX
|
||||
SG_ BYTE0 : 0|8@1+ (1,0) [0|0] "" XXX
|
||||
SG_ BYTE1 : 8|8@1+ (1,0) [0|0] "" XXX
|
||||
SG_ BYTE2 : 16|8@1+ (1,0) [0|0] "" XXX
|
||||
SG_ BYTE3 : 24|8@1+ (1,0) [0|0] "" XXX
|
||||
SG_ BYTE4 : 32|8@1+ (1,0) [0|0] "" XXX
|
||||
SG_ BYTE5 : 40|8@1+ (1,0) [0|0] "" XXX
|
||||
SG_ BYTE6 : 48|8@1+ (1,0) [0|0] "" XXX
|
||||
SG_ BYTE7 : 56|8@1+ (1,0) [0|0] "" XXX
|
||||
BO_ 1186 FRT_RADAR11: 8 FCA
|
||||
SG_ BYTE0 : 0|8@1+ (1,0) [0|7] "" LDWS_LKAS,LDW_LKA,ESC
|
||||
SG_ BYTE1 : 8|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ BYTE2 : 16|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ BYTE3 : 24|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ BYTE4 : 32|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ BYTE5 : 40|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ BYTE6 : 48|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ BYTE7 : 56|8@1+ (1,0) [0|255] "" XXX
|
||||
|
||||
BO_ 1265 CLU11: 4 XXX
|
||||
SG_ CF_Clu_AliveCnt1 : 28|4@1+ (1,0) [0|0] "" XXX
|
||||
@@ -923,13 +989,15 @@ BO_ 1322 CLU15: 8 XXX
|
||||
|
||||
BO_ 1342 LKAS12: 8 XXX
|
||||
SG_ CF_LkasDawStatus : 40|3@1+ (1,0) [0|0] "" XXX
|
||||
SG_ CF_LkasTsrSpeed_Display_Navi : 24|8@1+ (1,0) [0|0] "" XXX
|
||||
SG_ CF_Lkas_Daw_USM : 37|3@1+ (1,0) [0|0] "" XXX
|
||||
SG_ CF_Lkas_TsrAddinfo_Display : 32|2@1+ (1,0) [0|0] "" XXX
|
||||
SG_ CF_LkasTsrSpeed_Display_Navi : 32|8@1+ (1,0) [0|0] "" XXX
|
||||
SG_ CF_Lkas_TsrSpeed_Display_Clu : 24|8@1+ (1,0) [0|0] "" XXX
|
||||
SG_ CF_Lkas_TsrSlifOpt : 10|2@1+ (1,0) [0|0] "" XXX
|
||||
SG_ CF_Lkas_TsrSpeed_Display_Clu : 16|8@1+ (1,0) [0|0] "" XXX
|
||||
SG_ _CHECKSUM : 0|8@1+ (1,0) [0|0] "" XXX
|
||||
SG_ CHECKSUM : 0|8@1+ (1,0) [0|0] "" XXX
|
||||
SG_ COUNTER : 12|4@1+ (1,0) [0|0] "" XXX
|
||||
SG_ CF_SLA_Avail : 48|2@1+ (1,0) [0|3] "" XXX
|
||||
SG_ CF_SLA_AdjDisp : 51|2@1+ (1,0) [0|3] "" XXX
|
||||
SG_ Sign_detected : 56|5@1+ (1,0) [0|255] "" XXX
|
||||
SG_ CF_SLA_ACT : 61|1@0+ (1,0) [0|1] "" XXX
|
||||
|
||||
BO_ 1345 CGW1: 8 XXX
|
||||
SG_ CF_Gway_ALightStat : 37|1@1+ (1,0) [0|0] "" XXX
|
||||
@@ -1089,14 +1157,26 @@ BO_ 1456 CLU12: 4 XXX
|
||||
SG_ CF_Clu_Odometer : 0|24@1+ (0.1,0) [0|0] "" XXX
|
||||
|
||||
|
||||
CM_ SG_ 1123 DRV_MODE_PRIMARY_STATUS "Indicates the current drive mode selected by the driver (Smart, Sport, Comfort, ECO, Snow). This is a status-only signal, not writable.";
|
||||
CM_ SG_ 1123 DRV_MODE_SECONDARY_STATUS "Indicates a secondary status modifier, often used in combination with PRIMARY to disambiguate ECO vs Snow. Status-only.";
|
||||
CM_ SG_ 1123 DRV_MODE_EXT_IND "Drive mode extension indicator (e.g. Snow Mode flag). Status-only signal.";
|
||||
CM_ SG_ 1348 SpeedLim_Nav_Clu "Speed limit displayed on Nav, Cluster and HUD";
|
||||
CM_ SG_ 1342 CF_SLA_ACT "Speed limit assist active flag";
|
||||
CM_ SG_ 1342 CF_SLA_AdjDisp "Display of the automatically adjusting speed message";
|
||||
CM_ SG_ 1342 CF_SLA_Avail "Up or Down arrow showing speed limit assist available";
|
||||
|
||||
VAL_ 871 CF_Lvr_Gear 12 "T" 5 "D" 8 "S" 6 "N" 7 "R" 0 "P";
|
||||
VAL_ 871 AUTO_START_STOP_STATUS 3 "Off" 1 "On";
|
||||
VAL_ 909 CF_VSM_Warn 2 "FCW" 3 "AEB";
|
||||
VAL_ 916 ACCEnable 0 "SCC ready" 1 "SCC temp fault" 2 "SCC permanent fault" 3 "SCC permanent fault, communication issue";
|
||||
VAL_ 916 CF_FCW_AEB_Status 0 "Normal" 3 "Fault";
|
||||
VAL_ 1057 ACCMode 0 "off" 1 "enabled" 2 "driver_override" 3 "off_maybe_fault" 4 "cancelled";
|
||||
VAL_ 1123 DRV_MODE_PRIMARY_STATUS 49 "Smart" 33 "Sport" 1 "Comfort" 17 "ECO_or_Snow";
|
||||
VAL_ 1123 DRV_MODE_SECONDARY_STATUS 1 "Normal" 17 "Snow";
|
||||
VAL_ 1123 DRV_MODE_EXT_IND 0 "Standard" 16 "SnowFlag";
|
||||
VAL_ 1157 HDA_Icon_State 0 "no_hda" 1 "white_hda" 2 "green_hda";
|
||||
VAL_ 1157 LFA_SysWarning 0 "no_message" 1 "switching_to_hda" 2 "switching_to_scc" 3 "lfa_error" 4 "check_hda" 5 "keep_hands_on_wheel_orange" 6 "keep_hands_on_wheel_red";
|
||||
VAL_ 1157 LFA_Icon_State 0 "no_wheel" 1 "white_wheel" 2 "green_wheel" 3 "green_wheel_blink";
|
||||
VAL_ 1157 HDA_SysWarning 0 "no_message" 1 "driving_convenience_systems_cancelled" 2 "highway_drive_assist_system_cancelled";
|
||||
VAL_ 1322 CF_Clu_Gear 1 "P" 2 "R" 4 "N" 8 "D";
|
||||
VAL_ 1342 Sign_detected 1 "Slow/Caution" 8 "Stop" 9 "Yield"
|
||||
@@ -725,6 +725,15 @@ BO_ 780 DriverSeat: 8 XXX
|
||||
SG_ occupancyStatus : 16|3@1+ (1,0) [0|7] "" XXX
|
||||
SG_ buckleStatus : 19|2@1+ (1,0) [0|3] "" XXX
|
||||
|
||||
BO_ 1362 GAS_SENSOR: 6 INTERCEPTOR
|
||||
SG_ INTERCEPTOR_GAS : 7|16@0+ (0.0507968128,-22.85856576) [0|1] "" EON
|
||||
SG_ INTERCEPTOR_GAS2 : 23|16@0+ (0.1015936256,-22.85856576) [0|1] "" EON
|
||||
SG_ STATE : 35|4@0+ (1,0) [0|15] "" EON
|
||||
SG_ IDX : 39|4@0+ (1,0) [0|15] "" EON
|
||||
SG_ CHECKSUM : 47|8@0+ (1,0) [0|255] "" EON
|
||||
|
||||
VAL_ 1362 STATE 5 "FAULT_TIMEOUT" 4 "FAULT_STARTUP" 3 "FAULT_SCE" 2 "FAULT_SEND" 1 "FAULT_BAD_CHECKSUM" 0 "NO_FAULT" ;
|
||||
|
||||
VAL_ 3 StW_Angl 16383 "SNA" ;
|
||||
VAL_ 3 StW_AnglSens_Id 2 "MUST" 0 "PSBL" 1 "SELF" ;
|
||||
VAL_ 3 StW_AnglSens_Stat 2 "ERR" 3 "ERR_INI" 1 "INI" 0 "OK" ;
|
||||
@@ -898,4 +907,3 @@ VAL_ 1160 DAS_steeringAngleRequest 16384 "ZERO_ANGLE" ;
|
||||
VAL_ 1160 DAS_steeringControlType 1 "ANGLE_CONTROL" 3 "DISABLED" 0 "NONE" 2 "RESERVED" ;
|
||||
VAL_ 1160 DAS_steeringHapticRequest 1 "ACTIVE" 0 "IDLE" ;
|
||||
|
||||
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
#define SAFETY_PSA 31U
|
||||
#define SAFETY_RIVIAN 33U
|
||||
#define SAFETY_VOLKSWAGEN_MEB 34U
|
||||
#define SAFETY_TESLA_PREAP 35U
|
||||
|
||||
#define GET_BIT(msg, b) ((bool)!!(((msg)->data[((b) / 8U)] >> ((b) % 8U)) & 0x1U))
|
||||
#define GET_FLAG(value, mask) (((value) & (mask)) == (mask))
|
||||
@@ -215,6 +216,7 @@ typedef bool (*fwd_hook)(int bus_num, int addr); // returns true if the mes
|
||||
typedef struct {
|
||||
safety_hook_init init;
|
||||
rx_hook rx;
|
||||
rx_hook rx_all;
|
||||
tx_hook tx;
|
||||
fwd_hook fwd;
|
||||
get_checksum_t get_checksum;
|
||||
@@ -230,9 +232,7 @@ void update_sample(struct sample_t *sample, int sample_new);
|
||||
bool get_longitudinal_allowed(void);
|
||||
int ROUND(float val);
|
||||
void gen_crc_lookup_table_8(uint8_t poly, uint8_t crc_lut[]);
|
||||
#ifdef CANFD
|
||||
void gen_crc_lookup_table_16(uint16_t poly, uint16_t crc_lut[]);
|
||||
#endif
|
||||
bool steer_torque_cmd_checks(int desired_torque, int steer_req, const TorqueSteeringLimits limits);
|
||||
bool steer_angle_cmd_checks(int desired_angle, bool steer_control_enabled, const AngleSteeringLimits limits);
|
||||
bool steer_angle_cmd_checks_vm(int desired_angle, bool steer_control_enabled, const AngleSteeringLimits limits,
|
||||
@@ -357,3 +357,4 @@ extern const safety_hooks volkswagen_mqb_hooks;
|
||||
extern const safety_hooks volkswagen_pq_hooks;
|
||||
extern const safety_hooks rivian_hooks;
|
||||
extern const safety_hooks psa_hooks;
|
||||
extern const safety_hooks tesla_preap_hooks;
|
||||
|
||||
@@ -59,6 +59,8 @@ static bool gm_force_brake_c9 = false;
|
||||
static bool gm_panda_3d1_sched = false;
|
||||
static bool gm_panda_paddle_sched = false;
|
||||
static bool gm_bolt_2022_pedal = false;
|
||||
static bool gm_alt_brake = false;
|
||||
static bool gm_volt_auto_hold = false;
|
||||
|
||||
static bool gm_cc_long = false;
|
||||
static bool gm_has_acc = true;
|
||||
@@ -95,6 +97,7 @@ static const uint32_t GM_PADDLE_TX_OFFSET_US = 0U;
|
||||
static const uint32_t GM_PADDLE_LOCK_TOLERANCE_US = 5000U;
|
||||
static const uint32_t GM_PADDLE_STALE_US = 100000U;
|
||||
static const uint32_t GM_PADDLE_FEED_STALE_US = 100000U;
|
||||
static const int GM_VOLT_AUTO_HOLD_MAX_BRAKE = 240;
|
||||
|
||||
void can_send(CANPacket_t *to_push, uint8_t bus_number, bool skip_tx_hook);
|
||||
void can_set_checksum(CANPacket_t *packet);
|
||||
@@ -222,8 +225,8 @@ static void gm_rx_hook(const CANPacket_t *msg) {
|
||||
// Malibu Hybrid pedal-long frames use byte3 bit0 in combinations that can alias
|
||||
// CANCEL semantics; don't force a safety disengage on those RX edges.
|
||||
bool malibu_cancel_passthrough = remap_cancel_to_distance && gm_bolt_2022_pedal && gm_pedal_long && ((msg->data[3] & 0x1U) != 0U);
|
||||
// Gen1 Bolt pedal-long uses CANCEL as an auxiliary input path.
|
||||
bool bolt_cancel_passthrough = remap_cancel_to_distance && gm_pedal_long && !gm_has_acc && !gm_bolt_2022_pedal;
|
||||
// No-ACC pedal-long Bolt paths use CANCEL as an auxiliary input path.
|
||||
bool bolt_cancel_passthrough = remap_cancel_to_distance && gm_pedal_long && !gm_has_acc;
|
||||
|
||||
// enter controls on falling edge of set or rising edge of resume (avoids fault)
|
||||
bool set = (button != GM_BTN_SET) && (cruise_button_prev == GM_BTN_SET);
|
||||
@@ -246,10 +249,14 @@ static void gm_rx_hook(const CANPacket_t *msg) {
|
||||
brake_pressed = GET_BIT(msg, 40U);
|
||||
}
|
||||
|
||||
if ((msg->addr == 0xBEU) && ((gm_hw == GM_ASCM) || gm_sdgm || gm_ascm_int)) {
|
||||
if ((msg->addr == 0xBEU) && (((gm_hw == GM_ASCM) && !gm_alt_brake) || gm_sdgm || gm_ascm_int)) {
|
||||
brake_pressed = msg->data[1] >= 8U;
|
||||
}
|
||||
|
||||
if ((msg->addr == 0xF1U) && gm_alt_brake) {
|
||||
brake_pressed = msg->data[1] >= 6U;
|
||||
}
|
||||
|
||||
if ((msg->addr == 0xC9U) && (gm_hw == GM_CAM) && !gm_force_brake_c9) {
|
||||
brake_pressed = GET_BIT(msg, 40U);
|
||||
}
|
||||
@@ -358,7 +365,15 @@ static bool gm_tx_hook(const CANPacket_t *msg) {
|
||||
if (msg->addr == 0x315U) {
|
||||
int brake = ((msg->data[0] & 0xFU) << 8) + msg->data[1];
|
||||
brake = (0x1000 - brake) & 0xFFF;
|
||||
if (longitudinal_brake_checks(brake, *gm_long_limits)) {
|
||||
bool stock_auto_hold_brake_allowed = gm_volt_auto_hold && !vehicle_moving && !gas_pressed_prev;
|
||||
bool violation = false;
|
||||
violation |= !(get_longitudinal_allowed() || stock_auto_hold_brake_allowed) && (brake != 0);
|
||||
if (stock_auto_hold_brake_allowed && !get_longitudinal_allowed()) {
|
||||
violation |= brake > GM_VOLT_AUTO_HOLD_MAX_BRAKE;
|
||||
} else {
|
||||
violation |= brake > gm_long_limits->max_brake;
|
||||
}
|
||||
if (violation) {
|
||||
tx = false;
|
||||
}
|
||||
}
|
||||
@@ -546,6 +561,12 @@ static safety_config gm_init(uint16_t param) {
|
||||
{0x1E1, 0, 7, .check_relay = false},
|
||||
{0xBD, 0, 7, .check_relay = false},
|
||||
{0x1F5, 0, 8, .check_relay = false}}; // pt bus
|
||||
static const CanMsg GM_ASCM_ALT_BRAKE_TX_MSGS[] = {{0x180, 0, 4, .check_relay = true}, {0x409, 0, 7, .check_relay = false}, {0x40A, 0, 7, .check_relay = false}, {0x2CB, 0, 8, .check_relay = true}, {0x370, 0, 6, .check_relay = false}, {0x315, 0, 5, .check_relay = true}, // pt bus
|
||||
{0xA1, 1, 7, .check_relay = false}, {0x306, 1, 8, .check_relay = false}, {0x308, 1, 7, .check_relay = false}, {0x310, 1, 2, .check_relay = false}, // obs bus
|
||||
{0x200, 0, 6, .check_relay = false},
|
||||
{0x1E1, 0, 7, .check_relay = false},
|
||||
{0xBD, 0, 7, .check_relay = false},
|
||||
{0x1F5, 0, 8, .check_relay = false}}; // pt bus
|
||||
|
||||
|
||||
static const LongitudinalLimits GM_CAM_LONG_LIMITS = {
|
||||
@@ -591,12 +612,22 @@ static safety_config gm_init(uint16_t param) {
|
||||
{0x200, 0, 6, .check_relay = false},
|
||||
{0x1E1, 0, 7, .check_relay = false},
|
||||
{0xBD, 0, 7, .check_relay = false}, {0x1F5, 0, 8, .check_relay = false}}; // pt bus
|
||||
static const CanMsg GM_CAM_VOLT_AUTO_HOLD_TX_MSGS[] = {{0x180, 0, 4, .check_relay = true}, {0x370, 0, 6, .check_relay = false}, {0x3D1, 0, 8, .check_relay = false}, {0x315, 0, 5, .check_relay = true}, // pt bus
|
||||
{0x1E1, 2, 7, .check_relay = false}, {0x184, 2, 8, .check_relay = true}, // camera bus
|
||||
{0x200, 0, 6, .check_relay = false},
|
||||
{0x1E1, 0, 7, .check_relay = false},
|
||||
{0xBD, 0, 7, .check_relay = false}, {0x1F5, 0, 8, .check_relay = false}}; // pt bus
|
||||
|
||||
static const CanMsg GM_SDGM_TX_MSGS[] = {{0x180, 0, 4, .check_relay = true}, {0x370, 0, 6, .check_relay = false}, {0x3D1, 0, 8, .check_relay = false}, // pt bus
|
||||
{0x1E1, 2, 7, .check_relay = false}, {0x184, 2, 8, .check_relay = false}, // camera bus
|
||||
{0x200, 0, 6, .check_relay = false},
|
||||
{0x1E1, 0, 7, .check_relay = false},
|
||||
{0xBD, 0, 7, .check_relay = false}, {0x1F5, 0, 8, .check_relay = false}}; // pt bus
|
||||
static const CanMsg GM_SDGM_VOLT_AUTO_HOLD_TX_MSGS[] = {{0x180, 0, 4, .check_relay = true}, {0x370, 0, 6, .check_relay = false}, {0x3D1, 0, 8, .check_relay = false}, // pt bus
|
||||
{0x1E1, 2, 7, .check_relay = false}, {0x184, 2, 8, .check_relay = false}, {0x315, 2, 5, .check_relay = false}, // camera bus
|
||||
{0x200, 0, 6, .check_relay = false},
|
||||
{0x1E1, 0, 7, .check_relay = false},
|
||||
{0xBD, 0, 7, .check_relay = false}, {0x1F5, 0, 8, .check_relay = false}}; // pt bus
|
||||
|
||||
static const CanMsg GM_CAM_NO_CAMERA_TX_MSGS[] = {{0x180, 0, 4, .check_relay = false}, {0x370, 0, 6, .check_relay = false}, {0x3D1, 0, 8, .check_relay = false}, // pt bus
|
||||
{0x409, 0, 7, .check_relay = false}, {0x40A, 0, 7, .check_relay = false},
|
||||
@@ -604,6 +635,12 @@ static safety_config gm_init(uint16_t param) {
|
||||
{0x200, 0, 6, .check_relay = false},
|
||||
{0x1E1, 0, 7, .check_relay = false},
|
||||
{0xBD, 0, 7, .check_relay = false}, {0x1F5, 0, 8, .check_relay = false}}; // pt bus
|
||||
static const CanMsg GM_CAM_NO_CAMERA_VOLT_AUTO_HOLD_TX_MSGS[] = {{0x180, 0, 4, .check_relay = false}, {0x370, 0, 6, .check_relay = false}, {0x3D1, 0, 8, .check_relay = false}, {0x315, 0, 5, .check_relay = false}, // pt bus
|
||||
{0x409, 0, 7, .check_relay = false}, {0x40A, 0, 7, .check_relay = false},
|
||||
{0x1E1, 2, 7, .check_relay = false}, {0x184, 2, 8, .check_relay = false}, // camera bus
|
||||
{0x200, 0, 6, .check_relay = false},
|
||||
{0x1E1, 0, 7, .check_relay = false},
|
||||
{0xBD, 0, 7, .check_relay = false}, {0x1F5, 0, 8, .check_relay = false}}; // pt bus
|
||||
|
||||
static RxCheck gm_no_acc_rx_checks[] = {
|
||||
GM_COMMON_RX_CHECKS
|
||||
@@ -660,6 +697,9 @@ static safety_config gm_init(uint16_t param) {
|
||||
gm_remote_start_boots_comma = GET_FLAG(param, GM_PARAM_REMOTE_START_BOOTS_COMMA);
|
||||
gm_panda_3d1_sched = GET_FLAG(param, GM_PARAM_PANDA_3D1_SCHED) && gm_pedal_long && !gm_has_acc && !gm_bolt_2022_pedal;
|
||||
gm_panda_paddle_sched = GET_FLAG(param, GM_PARAM_PANDA_PADDLE_SCHED) && gm_pedal_long && enable_gas_interceptor;
|
||||
// Reuse the paddle-scheduler bit as a stock-Volt auto-hold marker on non-pedal ACC paths.
|
||||
gm_volt_auto_hold = GET_FLAG(param, GM_PARAM_PANDA_PADDLE_SCHED) && !gm_pedal_long && !gm_cc_long && gm_has_acc;
|
||||
gm_alt_brake = GET_FLAG(param, GM_PARAM_NO_CAMERA) && (gm_hw == GM_ASCM) && !gm_sdgm && !gm_ascm_int;
|
||||
|
||||
gm_3d1_spoof_valid = false;
|
||||
gm_3d1_internal_tx = false;
|
||||
@@ -702,7 +742,11 @@ static safety_config gm_init(uint16_t param) {
|
||||
const bool gm_sdgm_stock = gm_sdgm && !gm_cc_long && !gm_cam_long && !gm_no_camera;
|
||||
// SDGM behaves like a forwarding camera path for whitelist/forwarding purposes.
|
||||
if (gm_sdgm_stock) {
|
||||
ret = BUILD_SAFETY_CFG(gm_rx_checks, GM_SDGM_TX_MSGS);
|
||||
if (gm_volt_auto_hold) {
|
||||
ret = BUILD_SAFETY_CFG(gm_rx_checks, GM_SDGM_VOLT_AUTO_HOLD_TX_MSGS);
|
||||
} else {
|
||||
ret = BUILD_SAFETY_CFG(gm_rx_checks, GM_SDGM_TX_MSGS);
|
||||
}
|
||||
} else if ((gm_hw == GM_CAM) || gm_sdgm) {
|
||||
// FIXME: cppcheck thinks that gm_cam_long is always false. This is not true
|
||||
// if ALLOW_DEBUG is defined but cppcheck is run without ALLOW_DEBUG
|
||||
@@ -720,12 +764,24 @@ static safety_config gm_init(uint16_t param) {
|
||||
ret = BUILD_SAFETY_CFG(gm_rx_checks, GM_CAM_LONG_TX_MSGS);
|
||||
}
|
||||
} else {
|
||||
if (gm_no_camera) {
|
||||
ret = BUILD_SAFETY_CFG(gm_rx_checks, GM_CAM_NO_CAMERA_TX_MSGS);
|
||||
if (gm_volt_auto_hold && gm_sdgm) {
|
||||
ret = BUILD_SAFETY_CFG(gm_rx_checks, GM_SDGM_VOLT_AUTO_HOLD_TX_MSGS);
|
||||
} else if (gm_no_camera) {
|
||||
if (gm_volt_auto_hold) {
|
||||
ret = BUILD_SAFETY_CFG(gm_rx_checks, GM_CAM_NO_CAMERA_VOLT_AUTO_HOLD_TX_MSGS);
|
||||
} else {
|
||||
ret = BUILD_SAFETY_CFG(gm_rx_checks, GM_CAM_NO_CAMERA_TX_MSGS);
|
||||
}
|
||||
} else {
|
||||
ret = BUILD_SAFETY_CFG(gm_rx_checks, GM_CAM_TX_MSGS);
|
||||
if (gm_volt_auto_hold) {
|
||||
ret = BUILD_SAFETY_CFG(gm_rx_checks, GM_CAM_VOLT_AUTO_HOLD_TX_MSGS);
|
||||
} else {
|
||||
ret = BUILD_SAFETY_CFG(gm_rx_checks, GM_CAM_TX_MSGS);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (gm_alt_brake) {
|
||||
ret = BUILD_SAFETY_CFG(gm_rx_checks, GM_ASCM_ALT_BRAKE_TX_MSGS);
|
||||
} else {
|
||||
ret = BUILD_SAFETY_CFG(gm_rx_checks, GM_ASCM_TX_MSGS);
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user