Compare commits

..

3 Commits

Author SHA1 Message Date
firestar5683 0cb46873f9 build 2026-04-29 13:48:18 -05:00
firestar5683 7d3723a1db build 2026-04-29 13:48:17 -05:00
firestar5683 ceadca17a3 plexy 2026-04-29 13:48:17 -05:00
1590 changed files with 229990 additions and 265184 deletions
-9
View File
@@ -1,9 +0,0 @@
{
"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\")"
]
}
}
+52
View File
@@ -0,0 +1,52 @@
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
+37
View File
@@ -0,0 +1,37 @@
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
+101
View File
@@ -0,0 +1,101 @@
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
+17
View File
@@ -0,0 +1,17 @@
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 }}
-215
View File
@@ -1,215 +0,0 @@
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_can_ignition_only.bin.signed"
"panda/board/obj/panda_h7_can_ignition_only.bin.signed"
"panda/board/obj/panda_remote_can_ignition_only.bin.signed"
"panda/board/obj/panda_h7_remote_can_ignition_only.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}"
+205 -81
View File
@@ -3,113 +3,237 @@ name: Compile StarPilot
on:
workflow_dispatch:
inputs:
target_ref:
description: "Branch to build and publish back to"
not_vetted:
description: "This branch is not vetted"
type: boolean
default: false
required: false
publish_custom_branch:
description: "Push to custom branch:"
type: string
default: "master"
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"
required: true
jobs:
description: "Optional parallel job count passed to ./build"
type: string
default: "12"
update_translations:
description: "Update missing/outdated translations"
type: boolean
default: false
required: false
vet_existing_translations:
description: "Vet existing translations"
type: boolean
default: false
required: false
concurrency:
group: compile-starpilot-${{ 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 }}
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 }}
jobs:
build_and_push:
runs-on: [self-hosted, truenas, starpilot-build]
timeout-minutes: 720
get_branch:
runs-on: [self-hosted, "${{ inputs.runner }}"]
outputs:
branch: ${{ steps.get_branch.outputs.branch }}
python_version: ${{ steps.get_python_version.outputs.python_version }}
steps:
- name: Validate target branch
shell: bash
- name: Get Current Branch
id: get_branch
run: |
set -euo pipefail
cd "$BUILD_DIR"
echo "branch=$(git rev-parse --abbrev-ref HEAD)" >> "$GITHUB_OUTPUT"
if [[ -z "${TARGET_REF}" ]]; then
echo "target_ref is required." >&2
exit 1
fi
- name: Get Python Version
id: get_python_version
run: |
echo "python_version=$(tr -d '[:space:]' < "$BUILD_DIR/.python-version")" >> $GITHUB_OUTPUT
if [[ "${TARGET_REF}" == refs/heads/* ]]; then
echo "BUILD_BRANCH=${TARGET_REF#refs/heads/}" >> "$GITHUB_ENV"
else
echo "BUILD_BRANCH=${TARGET_REF}" >> "$GITHUB_ENV"
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"
- name: Checkout target branch
- name: Checkout Required Files
uses: actions/checkout@v4
with:
ref: ${{ env.BUILD_BRANCH }}
fetch-depth: 0
submodules: recursive
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
- name: Configure git identity
shell: bash
- name: Set Up Python
uses: actions/setup-python@v4
with:
cache: "pip"
python-version: ${{ needs.get_branch.outputs.python_version }}
- name: Install Dependencies
run: |
set -euo pipefail
git config user.name "$GIT_NAME"
git config user.email "$GIT_EMAIL"
git config http.postBuffer 104857600
pip install requests
sudo apt-get update && sudo apt-get install -y --no-install-recommends qttools5-dev-tools
- name: Ensure branch exists on origin
shell: bash
- name: Update Translations
run: |
set -euo pipefail
git ls-remote --exit-code --heads origin "refs/heads/${BUILD_BRANCH}" >/dev/null
python selfdrive/ui/update_translations.py --vanish
- name: Link persistent sysroot
shell: bash
- name: Update Missing Translations
continue-on-error: true
timeout-minutes: 300
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
python selfdrive/ui/translations/auto_translate.py --all-files
- name: Run StarPilot device build
shell: bash
- name: Vet Existing Translations
if: inputs.vet_existing_translations
continue-on-error: true
timeout-minutes: 300
run: |
set -euo pipefail
python selfdrive/ui/translations/auto_translate.py --all-files --vet-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
- name: Commit and Push Translations
run: |
set -euo pipefail
if [[ -z "$(git status --porcelain --untracked-files=no)" ]]; then
echo "No build output changes detected."
if git diff --quiet selfdrive/ui/translations/*.ts; then
echo "No translation updates detected."
exit 0
fi
git add -u
git commit -m "build"
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 }}"
- name: Push build commit
shell: bash
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
run: |
set -euo pipefail
git push origin "HEAD:refs/heads/${BUILD_BRANCH}"
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
+65
View File
@@ -0,0 +1,65 @@
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
+59
View File
@@ -0,0 +1,59 @@
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,
});
@@ -0,0 +1,151 @@
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 }}
+42
View File
@@ -0,0 +1,42 @@
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 }}
+39
View File
@@ -0,0 +1,39 @@
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"
@@ -1,35 +0,0 @@
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 }}
+175
View File
@@ -0,0 +1,175 @@
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 }}
+42
View File
@@ -0,0 +1,42 @@
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
+72
View File
@@ -0,0 +1,72 @@
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
+71
View File
@@ -0,0 +1,71 @@
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
+52
View File
@@ -0,0 +1,52 @@
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
+294
View File
@@ -0,0 +1,294 @@
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
@@ -0,0 +1,107 @@
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
+95
View File
@@ -0,0 +1,95 @@
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"
-1
View File
@@ -68,7 +68,6 @@ cppcheck_report.txt
comma*.sh
selfdrive/modeld/models/*.pkl
!selfdrive/modeld/models/driving_tinygrad.pkl
!selfdrive/modeld/models/driving_vision_tinygrad.pkl
!selfdrive/modeld/models/driving_policy_tinygrad.pkl
!selfdrive/modeld/models/driving_vision_metadata.pkl
-202
View File
@@ -1,202 +0,0 @@
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 -11
View File
@@ -1,13 +1,14 @@
# StarPilot
[![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/firestar5683/StarPilot)
[![Discord](https://img.shields.io/discord/1387432184121393333?label=Discord)](https://firestar.link/discord)
[![Discord](https://img.shields.io/discord/1137853399715549214?label=Discord)](https://firestar.link/discord)
[![Last Updated](https://img.shields.io/github/last-commit/firestar5683/StarPilot/StarPilot)](https://github.com/firestar5683/StarPilot)
[![Wiki](https://img.shields.io/badge/Wiki-StarPilot-blue?logo=wiki)](https://wiki.firestar.link)
**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
@@ -15,11 +16,11 @@ Openpilot provides
* Lane Change Assist
* Driver Monitoring *without wheel nags*
StarPilot was formerly a GM targeted fork,
but [has expanded to offer Quality-Of-Life improvements for all](#features)!
StarPilot adds support for many GM vehicles along with improved tuning,
especially for radar-less (camera only) vehicles.
StarPilot is built off of [FrogPilot](https://github.com/FrogAi/FrogPilot)
and supports the major features FrogPilot offers.
StarPilot is built off of [StarPilot](https://github.com/FrogAi/StarPilot)
and supports the major features StarPilot offers.
StarPilot has a vibrant, welcoming community [discord](https://firestar.link/discord).
Stop by to chat or ask questions!
@@ -32,9 +33,9 @@ installation guides, and software configuration.
## Features
* Full support for Comma C3, C3X, and C4
* C4 is currently in release testing. Join our fleet of C4 testers!
* Model switcher with all of comma's tinygrad driving models
* Special longitudinal planner tuning for VoACC (visual only, radar-less) vehicles
* Custom-tuned torque controllers for an expanding list of cars.
* Galaxy: StarPilot's portal to configure your comma device using your phone from anywhere.
Download models, change settings, update software, visualize live model outputs for tuning.
* Always On Lateral (full time steering assist)*
@@ -49,9 +50,8 @@ Download models, change settings, update software, visualize live model outputs
* ZSS support*
* High quality dashcam recordings*
* Enhanced tuning for CEM (dynamic experimental mode switching)
* And more!
\* [Inherited from FrogPilot](https://github.com/FrogAi/FrogPilot#openpilot-vs-frogpilot)
\* [Inherited from StarPilot](https://github.com/FrogAi/StarPilot#openpilot-vs-starpilot)
## GM-only Features
@@ -75,6 +75,3 @@ 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.
+9 -17
View File
@@ -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", "PARAMS_ROOT"):
for key in ("HOME", "TMPDIR", "XDG_CACHE_HOME", "CACHEDB"):
if key in os.environ:
lenv[key] = os.environ[key]
@@ -404,22 +404,12 @@ 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 "SP_QT_HOST_MOC" in os.environ:
qt_env['QT3_MOC'] = qt_host_moc
elif os.path.isfile(qt_arm_moc):
if os.path.isfile(qt_arm_moc):
qt_env['QT3_MOC'] = qt_arm_moc
if "SP_QT_HOST_UIC" in os.environ:
qt_env['QT3_UIC'] = qt_host_uic
elif os.path.isfile(qt_arm_uic):
if os.path.isfile(qt_arm_uic):
qt_env['QT3_UIC'] = qt_arm_uic
if "SP_QT_HOST_RCC" in os.environ:
qt_env['SP_QT_RCC'] = qt_host_rcc
elif os.path.isfile(qt_arm_rcc):
if 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")
@@ -427,17 +417,19 @@ 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_env['QT3_MOC'] = qt_host_moc
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"))
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_env['QT3_UIC'] = qt_host_uic
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"))
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'] = qt_host_rcc
qt_env['SP_QT_RCC'] = os.environ.get("SP_QT_HOST_RCC", "rcc")
qt_env['CPPPATH'] += qt_dirs + ["#third_party/qrcode"]
qt_flags = [
+2 -29
View File
@@ -59,8 +59,6 @@ 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;
@@ -87,17 +85,6 @@ 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;
pedalMaxRegen @23 :Bool; # pedal at max regen, driver should use brake for more decel
pedalLongActive @24 :Bool; # Pre-AP pedal longitudinal mode is active (enableLongControl)
teslaCCEngaged @25 :Bool; # rising edge of stock Tesla CC engaging (no-pedal mode)
teslaCCDisengaged @26 :Bool; # falling edge of stock Tesla CC
teslaCCNotArmed @27 :Bool; # lateral engaged but DI_cruiseState != STANDBY/ENABLED
accelHardCruise @28 :Bool; # current/releasing accel cruise button came from GM hard-press signal
decelHardCruise @29 :Bool; # current/releasing decel cruise button came from GM hard-press signal
}
struct StarPilotDeviceState @0xda96579883444c35 {
@@ -115,11 +102,7 @@ struct StarPilotModelDataV2 @0x80ae746ee2596b11 {
}
}
struct StarPilotOnroadEvents @0xa5cd762cd951a455 {
events @0 :List(StarPilotOnroadEvent);
}
struct StarPilotOnroadEvent @0xe344718567f9ce71 {
struct StarPilotOnroadEvent @0xa5cd762cd951a455 {
name @0 :EventName;
enable @1 :Bool;
@@ -169,14 +152,6 @@ struct StarPilotOnroadEvent @0xe344718567f9ce71 {
switchbackModeInactive @30;
lkasEnable @31;
lkasDisable @32;
lateralManeuver @33;
pedalCruiseEnabled @34;
pedalCruiseDisabled @35;
pedalMaxRegen @36;
teslaCCEngaged @37;
teslaCCDisengaged @38;
teslaCCNotArmed @39;
pedalNotCalibrated @40;
}
}
@@ -218,7 +193,6 @@ struct StarPilotPlan @0xf98d843bfd7004a3 {
weatherId @34 :Int16;
disableThrottle @35 :Bool;
trackingLead @36 :Bool;
stopSignConfirmed @37 :Bool;
}
struct StarPilotRadarState @0xb86e6369214c01c8 {
@@ -278,8 +252,7 @@ struct CustomReserved9 @0xa1680744031fdb2d {
wallTimeNanos @5 :UInt64;
}
struct StarPilotLateralManeuverPlanDEPRECATED @0xcb9fd56c7057593a {
desiredCurvature @0 :Float32; # 1/m
struct CustomReserved10 @0xcb9fd56c7057593a {
}
struct CustomReserved11 @0xc2243c65e0340384 {
Binary file not shown.
+32 -123
View File
@@ -68,12 +68,12 @@ struct OnroadEvent @0xc4fa6047f024e718 {
longitudinalManeuver @30;
steerTempUnavailableSilent @31;
resumeRequired @32;
driverDistracted1 @33;
driverDistracted2 @34;
driverDistracted3 @35;
driverUnresponsive1 @36;
driverUnresponsive2 @37;
driverUnresponsive3 @38;
preDriverDistracted @33;
promptDriverDistracted @34;
driverDistracted @35;
preDriverUnresponsive @36;
promptDriverUnresponsive @37;
driverUnresponsive @38;
belowSteerSpeed @39;
lowBattery @40;
accFaulted @41;
@@ -130,6 +130,7 @@ struct OnroadEvent @0xc4fa6047f024e718 {
userBookmark @95;
excessiveActuation @96;
audioFeedback @97;
lateralManeuver @98;
soundsUnavailableDEPRECATED @47;
}
@@ -825,30 +826,13 @@ struct SelfdriveState {
alertStatus @5 :AlertStatus;
alertSize @6 :AlertSize;
alertType @7 :Text;
alertSound @13 :AudibleAlert;
alertSound @8 :Car.CarControl.HUDControl.AudibleAlert;
alertHudVisual @12 :Car.CarControl.HUDControl.VisualAlert;
# configurable driving settings
experimentalMode @10 :Bool;
personality @11 :LongitudinalPersonality;
enum AudibleAlert {
none @0;
engage @1;
disengage @2;
refuse @3;
warningSoft @4;
warningImmediate @5;
prompt @6;
promptRepeat @7;
promptDistracted @8;
preAlert @9;
}
enum OpenpilotState @0xdbe58b96d2d1ac61 {
disabled @0;
preEnabled @1;
@@ -869,10 +853,6 @@ struct SelfdriveState {
mid @2;
full @3;
}
deprecated :group {
alertSound @8 :Car.CarControl.HUDControl.AudibleAlert;
}
}
struct ControlsState @0x97ff69c53601abf1 {
@@ -1108,7 +1088,7 @@ struct ModelDataV2 {
confidence @23: ConfidenceClass;
# Model perceived motion
temporalPoseDEPRECATED @21 :Pose;
temporalPose @21 :Pose;
# e2e lateral planner
action @26: Action;
@@ -1282,6 +1262,12 @@ 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;
@@ -2186,12 +2172,14 @@ struct DriverStateV2 {
facePosition @2 :List(Float32);
facePositionStd @3 :List(Float32);
faceProb @4 :Float32;
leftEyeProb @5 :Float32;
rightEyeProb @6 :Float32;
leftBlinkProb @7 :Float32;
rightBlinkProb @8 :Float32;
sunglassesProb @9 :Float32;
eyesVisibleProb @14 :Float32;
eyesClosedProb @15 :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);
@@ -2233,7 +2221,7 @@ struct DriverStateDEPRECATED @0xb83c6cc593ed0a00 {
stdDEPRECATED @2 :Float32;
}
struct DriverMonitoringStateDEPRECATED @0xb83cda094a1da284 {
struct DriverMonitoringState @0xb83cda094a1da284 {
events @18 :List(OnroadEvent);
faceDetected @1 :Bool;
isDistracted @2 :Bool;
@@ -2251,90 +2239,12 @@ struct DriverMonitoringStateDEPRECATED @0xb83cda094a1da284 {
isActiveMode @16 :Bool;
isRHD @4 :Bool;
uncertainCount @19 :UInt32;
phoneProbOffset @20 :Float32;
phoneProbValidCount @21 :UInt32;
deprecated :group {
phoneProbOffset @20 :Float32;
phoneProbValidCount @21 :UInt32;
isPreview @15 :Bool;
rhdChecked @5 :Bool;
events @0 :List(Car.OnroadEventDEPRECATED);
}
}
struct DriverMonitoringState {
lockout @0 :Bool;
lockoutRecoveryPercent @11 :Int8;
alert3Count @12 :Int8;
noResponseCount @13 :Int8;
noResponseForceDecel @14 :Bool;
alwaysOn @3 :Bool;
alwaysOnLockout @4 :Bool;
alertLevel @5 :AlertLevel;
activePolicy @6 :MonitoringPolicy;
isRHD @7 :Bool;
rhdCalibration @8 :CalibrationState;
visionPolicyState @9 :VisionPolicyState;
wheeltouchPolicyState @10 :WheeltouchPolicyState;
enum AlertLevel {
# ordinal must match the name to prevent bugs
# comparing against the raw ordinal value
none @0;
one @1;
two @2;
three @3;
}
enum MonitoringPolicy {
wheeltouch @0;
vision @1;
}
struct VisionPolicyState {
awarenessPercent @0 :Int8;
awarenessStep @1 :Float32;
isDistracted @2 :Bool;
distractedTypes @3 :DistractedTypes;
faceDetected @4 :Bool;
pose @5 :Pose;
wheeltouchFallbackPercent @6 :Int8;
uncertainOffroadAlertPercent @7 :Int8;
struct DistractedTypes {
pose @0: Bool;
eye @1: Bool;
phone @2: Bool;
}
struct Pose {
pitch @0 :Float32;
yaw @1 :Float32;
pitchCalib @2 :CalibrationState;
yawCalib @3 :CalibrationState;
calibrated @4 :Bool;
uncertainty @5 :Float32;
}
}
struct WheeltouchPolicyState {
awarenessPercent @0 :Int8;
awarenessStep @1 :Float32;
driverInteracting @2 :Bool;
}
struct CalibrationState {
calibratedPercent @0 :Int8;
offset @1 :Float32;
}
deprecated :group {
alertCountLockoutPercent @1 :Int8;
alertTimeLockoutPercent @2 :Int8;
}
isPreviewDEPRECATED @15 :Bool;
rhdCheckedDEPRECATED @5 :Bool;
eventsDEPRECATED @0 :List(Car.OnroadEventDEPRECATED);
}
struct Boot {
@@ -2655,7 +2565,7 @@ struct Event {
thumbnail @66: Thumbnail;
onroadEvents @134: List(OnroadEvent);
carParams @69: Car.CarParams;
driverMonitoringState @151 :DriverMonitoringState;
driverMonitoringState @71: DriverMonitoringState;
livePose @129 :LivePose;
modelV2 @75 :ModelDataV2;
drivingModelData @128 :DrivingModelData;
@@ -2704,8 +2614,8 @@ struct Event {
userBookmark @93 :UserBookmark;
bookmarkButton @148 :UserBookmark;
audioFeedback @149 :AudioFeedback;
lateralManeuverPlan @150 :LateralManeuverPlan;
# *********** debug ***********
testJoystick @52 :Joystick;
roadEncodeData @86 :EncodeData;
@@ -2734,12 +2644,12 @@ struct Event {
starpilotCarState @109 :Custom.StarPilotCarState;
starpilotDeviceState @110 :Custom.StarPilotDeviceState;
starpilotModelV2 @111 :Custom.StarPilotModelDataV2;
starpilotOnroadEvents @112 :Custom.StarPilotOnroadEvents;
starpilotOnroadEvents @112 :List(Custom.StarPilotOnroadEvent);
starpilotPlan @113 :Custom.StarPilotPlan;
starpilotRadarState @114 :Custom.StarPilotRadarState;
starpilotSelfdriveState @115 :Custom.StarPilotSelfdriveState;
customReserved9 @116 :Custom.CustomReserved9;
starpilotLateralManeuverPlanDEPRECATED @136 :Custom.StarPilotLateralManeuverPlanDEPRECATED;
customReserved10 @136 :Custom.CustomReserved10;
customReserved11 @137 :Custom.CustomReserved11;
customReserved12 @138 :Custom.CustomReserved12;
customReserved13 @139 :Custom.CustomReserved13;
@@ -2797,6 +2707,5 @@ struct Event {
gyroscope2DEPRECATED @100 :SensorEventData;
accelerometer2DEPRECATED @101 :SensorEventData;
temperatureSensor2DEPRECATED @123 :SensorEventData;
driverMonitoringStateDEPRECATED @71 :DriverMonitoringStateDEPRECATED;
}
}
Binary file not shown.
+58 -243
View File
@@ -1,255 +1,70 @@
from __future__ import annotations
from openpilot.common.params_pyx import Params as _Params, ParamKeyFlag, ParamKeyType, UnknownKeyName
assert _Params
assert ParamKeyFlag
assert ParamKeyType
assert UnknownKeyName
from enum import IntEnum, IntFlag
from pathlib import Path
import tempfile
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
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 int(float(value))
except (TypeError, ValueError):
return default
return value.decode(encoding)
except Exception:
return value.decode("utf-8", errors="replace")
return value
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_bool(self, key, block=False, default=False):
try:
return super().get_bool(key, block=block)
except UnknownKeyName:
return bool(default)
def put(self, key, dat):
key = self.check_key(key)
self._store[key] = self._coerce_value(key, dat)
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_bool(self, key, val: bool):
self.put(key, bool(val))
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_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))
def put_float(self, key, val):
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 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)))
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
+29 -102
View File
@@ -11,6 +11,7 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
{"AlwaysAllowUploads", {PERSISTENT, BOOL, "0"}},
{"AlwaysOnDM", {PERSISTENT, BOOL}},
{"ApiCache_Device", {PERSISTENT, STRING}},
{"ApiCache_FirehoseStats", {PERSISTENT, JSON}},
{"AssistNowToken", {PERSISTENT, STRING}},
{"AthenadPid", {PERSISTENT, INT}},
{"AthenadUploadQueue", {PERSISTENT, JSON}},
@@ -42,9 +43,7 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
{"ExperimentalLongitudinalEnabled", {PERSISTENT, BOOL}},
{"ExperimentalMode", {PERSISTENT, BOOL}},
{"ExperimentalModeConfirmed", {PERSISTENT, BOOL}},
{"PersistChillState", {PERSISTENT, BOOL, "0", "0", 1}},
{"PersistExperimentalState", {PERSISTENT, BOOL, "0", "0", 1}},
{"PersistedCCStatus", {PERSISTENT, INT, "0", "0"}},
{"PersistedCEStatus", {PERSISTENT, INT, "0", "0"}},
{"FirmwareQueryDone", {CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION, BOOL}},
{"ForcePowerDown", {PERSISTENT, BOOL}},
@@ -60,8 +59,6 @@ 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}},
@@ -69,9 +66,7 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
{"IsMetric", {PERSISTENT, BOOL}},
{"IsOffroad", {CLEAR_ON_MANAGER_START, BOOL}},
{"IsOnroad", {PERSISTENT, BOOL}},
{"IsRHD", {PERSISTENT, BOOL}},
{"IsRhdDetected", {PERSISTENT, BOOL}},
{"IsRHDOverride", {PERSISTENT, BOOL}},
{"IsReleaseBranch", {CLEAR_ON_MANAGER_START, BOOL}},
{"IsTakingSnapshot", {CLEAR_ON_MANAGER_START, BOOL}},
{"IsTestedBranch", {CLEAR_ON_MANAGER_START, BOOL}},
@@ -116,7 +111,6 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
{"PandaSomResetTriggered", {CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION, BOOL}},
{"PandaSignatures", {CLEAR_ON_MANAGER_START, BYTES}},
{"PrimeType", {PERSISTENT, INT}},
{"PriusClusterOffsetMigrated", {PERSISTENT, BOOL, "0", "0"}},
{"RecordAudio", {PERSISTENT, BOOL}},
{"RecordAudioFeedback", {PERSISTENT, BOOL, "0"}},
{"RecordFront", {PERSISTENT, BOOL}},
@@ -148,7 +142,7 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
// StarPilot variables
{"AccelerationPath", {PERSISTENT, BOOL, "1", "0", 2}},
{"AccelerationProfile", {PERSISTENT, INT, "0", "0", 0}},
{"AccelerationProfile", {PERSISTENT, INT, "2", "0", 0}},
{"AdjacentLeadsUI", {PERSISTENT, BOOL, "1", "0", 3}},
{"AdjacentPath", {PERSISTENT, BOOL, "0", "0", 3}},
{"AdjacentPathMetrics", {PERSISTENT, BOOL, "0", "0", 3}},
@@ -156,14 +150,13 @@ 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.0", "1.0", 2}},
{"AggressiveFollowHigh", {PERSISTENT, FLOAT, "1.25", "1.25", 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}},
{"AggressiveJerkSpeed", {PERSISTENT, FLOAT, "50.0", "50.0", 3}},
{"AggressiveJerkSpeedDecrease", {PERSISTENT, FLOAT, "50.0", "50.0", 3}},
{"AlertVolumeControl", {PERSISTENT, BOOL, "0", "0", 2}},
{"AllowImpossibleAcceleration", {PERSISTENT, BOOL, "0", "0", 3}},
{"AlwaysOnLateral", {PERSISTENT, BOOL, "1", "0", 0}},
{"AlwaysOnLateralLKAS", {PERSISTENT, BOOL, "1", "0", 2}},
{"ApiCache_DriveStats", {PERSISTENT, JSON, "{}", "{}"}},
@@ -172,7 +165,6 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
{"AvailableModelNames", {PERSISTENT, STRING, "", "", 1}},
{"AvailableModelSeries", {PERSISTENT, STRING, "", "", 1}},
{"AvailableModels", {PERSISTENT, STRING, "", "", 1}},
{"AvailableModelArtifactFormats", {PERSISTENT, STRING, "", "", 1}},
{"BlacklistedModels", {PERSISTENT, STRING, "", "", 2}},
{"BootLogo", {PERSISTENT, STRING, "starpilot", "stock", 0}},
{"BuildMetadata", {PERSISTENT, STRING, "", "", 0}},
@@ -183,10 +175,8 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
{"BorderWidth", {PERSISTENT, FLOAT, "100.0", "100.0", 2}},
{"CalibratedLateralAcceleration", {PERSISTENT, FLOAT, "2.0", "2.0", 2}},
{"CalibrationProgress", {PERSISTENT, FLOAT, "0.0", "0.0", 3}},
{"CameraOffset", {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}},
@@ -201,22 +191,15 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
{"CESlowerLead", {PERSISTENT, BOOL, "1", "0", 1}},
{"CESpeed", {PERSISTENT, FLOAT, "0.0", "0.0", 1}},
{"CESpeedLead", {PERSISTENT, FLOAT, "0.0", "0.0", 1}},
{"CCMLead", {PERSISTENT, BOOL, "1", "0", 1}},
{"CCMLaunchAssist", {PERSISTENT, BOOL, "0", "0", 1}},
{"CCMSetSpeedMargin", {PERSISTENT, FLOAT, "3.0", "0.0", 1}},
{"CCMSpeed", {PERSISTENT, FLOAT, "45.0", "0.0", 1}},
{"CCMSpeedLead", {PERSISTENT, FLOAT, "35.0", "0.0", 1}},
{"CCStatus", {CLEAR_ON_OFFROAD_TRANSITION, INT, "0", "0"}},
{"CEStatus", {CLEAR_ON_OFFROAD_TRANSITION, INT, "0", "0"}},
{"CEStopLights", {PERSISTENT, BOOL, "1", "0", 1}},
{"CEStoppedLead", {PERSISTENT, BOOL, "0", "0", 1}},
{"CEStoppedLead", {PERSISTENT, BOOL, "1", "0", 1}},
{"ClusterOffset", {PERSISTENT, FLOAT, "1.0", "1.0", 2}},
{"ColorScheme", {PERSISTENT, STRING, "stock", "stock", 0}},
{"ColorScheme", {PERSISTENT, STRING, "frog", "stock", 0}},
{"ColorToDownload", {CLEAR_ON_MANAGER_START, STRING, "", ""}},
{"BootLogoToDownload", {CLEAR_ON_MANAGER_START, STRING, "", ""}},
{"Compass", {PERSISTENT, BOOL, "0", "0", 1}},
{"CommunityFavorites", {PERSISTENT, STRING, "", "", 1}},
{"ConditionalChill", {PERSISTENT, BOOL, "0", "0", 1}},
{"ConditionalExperimental", {PERSISTENT, BOOL, "1", "0", 1}},
{"CurvatureData", {PERSISTENT | DONT_LOG, JSON, "{}", "{}"}},
{"CurveSpeedController", {PERSISTENT, BOOL, "1", "0", 1}},
@@ -233,14 +216,11 @@ 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"}},
{"AOLLKASMigratedToButtonControl", {PERSISTENT, BOOL, "0", "0"}},
{"TrafficPersonalityProfile", {PERSISTENT, BOOL, "1", "1", 2}},
{"AggressivePersonalityProfile", {PERSISTENT, BOOL, "1", "1", 2}},
{"StandardPersonalityProfile", {PERSISTENT, BOOL, "1", "1", 2}},
{"RelaxedPersonalityProfile", {PERSISTENT, BOOL, "1", "1", 2}},
{"CustomThemes", {PERSISTENT, BOOL, "0", "0", 0}},
{"CustomThemes", {PERSISTENT, BOOL, "1", "0", 0}},
{"CustomUI", {PERSISTENT, BOOL, "1", "0", 1}},
{"DebugMode", {CLEAR_ON_OFFROAD_TRANSITION, BOOL, "0", "0"}},
{"DecelerationProfile", {PERSISTENT, INT, "1", "0", 2}},
@@ -287,40 +267,22 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
{"FlashPanda", {CLEAR_ON_MANAGER_START, BOOL, "0", "0"}},
{"GMDashSpoofOffsets", {PERSISTENT, BOOL, "0", "0", 2}},
{"GMPedalLongitudinal", {PERSISTENT, BOOL, "1", "1", 2}},
{"IgnoreIgnitionLine", {PERSISTENT, BOOL, "0", "0"}},
{"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, "1", "0", 2}},
{"ForceStopDistanceOffset", {PERSISTENT, INT, "0", "0", 2}},
{"ForceStops", {PERSISTENT, BOOL, "0", "0", 2}},
{"ForceStandstill", {PERSISTENT, BOOL, "0", "0", 2}},
{"ForceTorqueController", {PERSISTENT, BOOL, "0", "0", 3}},
{"FPSCounter", {PERSISTENT, BOOL, "1", "0", 3}},
{"GalaxyDashboardStats", {PERSISTENT | DONT_LOG, JSON, "{}", "{}"}},
{"StarPilotApiToken", {PERSISTENT | DONT_LOG, STRING, "", "", 0}},
{"StarPilotCarParams", {CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION, BYTES, "", ""}},
{"StarPilotCarParamsPersistent", {PERSISTENT, BYTES, "", ""}},
{"StarPilotDongleId", {PERSISTENT | DONT_LOG, STRING, "", "", 0}},
{"StarPilotFavoriteSlots", {PERSISTENT, JSON, "[]", "[]", 1}},
{"StarPilotStats", {PERSISTENT | DONT_LOG, JSON, "{}", "{}"}},
{"StarPilotTogglesUpdated", {CLEAR_ON_MANAGER_START, BOOL, "0", "0"}},
{"FrogsGoMoosTweak", {PERSISTENT, BOOL, "1", "0", 2}},
@@ -328,29 +290,23 @@ 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}},
{"HideSpeedLimit", {PERSISTENT, BOOL, "0", "0", 2}},
{"HideSteeringWheel", {PERSISTENT, BOOL, "0", "0", 2}},
{"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}},
{"PrioritizeSmoothFollowing", {PERSISTENT, BOOL, "0", "0", 2}},
{"HumanLaneChanges", {PERSISTENT, BOOL, "0", "0", 2}},
{"IconPack", {PERSISTENT, STRING, "stock", "stock", 0}},
{"IconPack", {PERSISTENT, STRING, "frog-animated", "stock", 0}},
{"IconToDownload", {CLEAR_ON_MANAGER_START, STRING, "", ""}},
{"IncreasedStoppedDistance", {PERSISTENT, FLOAT, "0.0", "0.0", 1}},
{"IncreasedStoppedDistanceLowVisibility", {PERSISTENT, FLOAT, "0.0", "0.0", 2}},
{"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}},
@@ -374,7 +330,6 @@ 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}},
@@ -385,14 +340,8 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
{"LongitudinalManeuverStatus", {CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION, JSON, "{}", "{}"}},
{"LongitudinalTune", {PERSISTENT, BOOL, "1", "0", 0}},
{"LoudBlindspotAlert", {PERSISTENT, BOOL, "0", "0", 0}},
{"LoudBlindspotAlertWhenDisengaged", {PERSISTENT, BOOL, "0", "0", 0}},
{"LowVoltageShutdown", {PERSISTENT, FLOAT, "11.8", "11.8", 3}},
{"MainCruiseButtonControl", {PERSISTENT, INT, "0", "0", 2}},
{"ManualUpdateInitiated", {CLEAR_ON_MANAGER_START, BOOL, "0", "0"}},
{"AMapKey1", {PERSISTENT | DONT_LOG, STRING, "", "", 0}},
{"AMapKey2", {PERSISTENT | DONT_LOG, STRING, "", "", 0}},
{"ApiCache_NavDestinations", {PERSISTENT, JSON, "[]", "[]"}},
{"FavoriteDestinations", {PERSISTENT, JSON, "[]", "[]"}},
{"MapAcceleration", {PERSISTENT, BOOL, "0", "0", 1}},
{"MapboxPublicKey", {PERSISTENT | DONT_LOG, STRING, "", "", 0}},
{"MapBoxRequests", {PERSISTENT, JSON, "{}", "{}"}},
@@ -402,12 +351,6 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
{"MapGears", {PERSISTENT, BOOL, "0", "0", 2}},
{"MapsSelected", {PERSISTENT, STRING, "", "", 0}},
{"MapSpeedLimit", {CLEAR_ON_MANAGER_START, FLOAT, "0.0", "0.0"}},
{"NavDesiresAllowed", {PERSISTENT, BOOL, "1", "0", 2}},
{"NavLongitudinalAllowed", {PERSISTENT, BOOL, "1", "0", 2}},
{"ClearNavOnOffroad", {PERSISTENT, BOOL, "1", "1", 2}},
{"NavDestination", {PERSISTENT | CLEAR_ON_MANAGER_START, STRING, "", ""}},
{"NavInstructionCollapsed", {CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION, BOOL, "0", "0"}},
{"NavInstructionState", {CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION, JSON, "{}", "{}"}},
{"NextMapSpeedLimit", {CLEAR_ON_MANAGER_START, JSON, "{}", "{}"}},
{"VisionSpeedLimit", {CLEAR_ON_MANAGER_START, FLOAT, "0.0", "0.0"}},
{"VisionSpeedLimitConfidence", {CLEAR_ON_MANAGER_START, FLOAT, "0.0", "0.0"}},
@@ -428,7 +371,6 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
{"ModelToDownload", {CLEAR_ON_MANAGER_START, STRING, "", ""}},
{"ModelUI", {PERSISTENT, BOOL, "1", "0", 2}},
{"ModelVersions", {PERSISTENT, STRING, "", "", 1}},
{"ModelManifestVersion", {PERSISTENT, STRING, "", "", 1}},
{"NavigationUI", {PERSISTENT, BOOL, "1", "0", 1}},
{"NNFF", {PERSISTENT, BOOL, "0", "0", 2}},
{"NNFFLite", {PERSISTENT, BOOL, "0", "0", 2}},
@@ -436,8 +378,7 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
{"NNFFModelName", {CLEAR_ON_MANAGER_START, STRING, "", "", 0}},
{"NoLogging", {PERSISTENT, BOOL, "0", "0", 2}},
{"NoUploads", {PERSISTENT, BOOL, "0", "0", 2}},
{"NudgelessLaneChange", {PERSISTENT, BOOL, "0", "0", 0}},
{"NudgelessLaneChangeOnlyWhenEngaged", {PERSISTENT, BOOL, "0", "0", 1}},
{"NudgelessLaneChange", {PERSISTENT, BOOL, "1", "0", 0}},
{"NumericalTemp", {PERSISTENT, BOOL, "1", "0", 3}},
{"Offset1", {PERSISTENT, FLOAT, "5.0", "0.0", 0}},
{"Offset2", {PERSISTENT, FLOAT, "5.0", "0.0", 0}},
@@ -459,10 +400,9 @@ 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}},
{"GalaxyPaired", {PERSISTENT, BOOL, "0", "0", 0}},
{"GalaxyUploadPending", {PERSISTENT, BOOL, "0", "0", 0}},
{"PondPaired", {PERSISTENT, BOOL, "0", "0", 0}},
{"PondUploadPending", {PERSISTENT, BOOL, "0", "0", 0}},
{"PreferredSchedule", {PERSISTENT, INT, "2", "0", 0}},
{"PreviousSpeedLimit", {PERSISTENT, FLOAT, "0.0", "0.0"}},
{"PromptDistractedVolume", {PERSISTENT, INT, "101", "101", 2}},
@@ -470,7 +410,6 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
{"QOLLateral", {PERSISTENT, BOOL, "1", "0", 1}},
{"QOLLongitudinal", {PERSISTENT, BOOL, "1", "0", 1}},
{"QOLVisuals", {PERSISTENT, BOOL, "1", "0", 0}},
{"RadarTakeoffs", {PERSISTENT, BOOL, "0", "0", 2}},
{"RadarTracksUI", {PERSISTENT, BOOL, "0", "0", 3}},
{"RainbowPath", {PERSISTENT, BOOL, "0", "0", 1}},
{"RandomEvents", {PERSISTENT, BOOL, "0", "0", 1}},
@@ -485,13 +424,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.6", "1.6", 2}},
{"RelaxedFollowHigh", {PERSISTENT, FLOAT, "1.4", "1.4", 2}},
{"RelaxedJerkAcceleration", {PERSISTENT, FLOAT, "100.0", "100.0", 3}},
{"RelaxedFollow", {PERSISTENT, FLOAT, "1.75", "1.75", 2}},
{"RelaxedFollowHigh", {PERSISTENT, FLOAT, "1.75", "1.75", 2}},
{"RelaxedJerkAcceleration", {PERSISTENT, FLOAT, "50.0", "50.0", 3}},
{"RelaxedJerkDanger", {PERSISTENT, FLOAT, "100.0", "100.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}},
{"RelaxedJerkDeceleration", {PERSISTENT, FLOAT, "50.0", "50.0", 3}},
{"RelaxedJerkSpeed", {PERSISTENT, FLOAT, "50.0", "50.0", 3}},
{"RelaxedJerkSpeedDecrease", {PERSISTENT, FLOAT, "50.0", "50.0", 3}},
{"ReverseCruise", {PERSISTENT, BOOL, "0", "0", 1}},
{"RecoveryPower", {PERSISTENT, FLOAT, "1.0", "1.0", 2}},
{"RoadEdgesWidth", {PERSISTENT, FLOAT, "2.0", "2.0", 2}},
@@ -509,13 +448,11 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
{"SetSpeedLimit", {PERSISTENT, BOOL, "0", "0", 1}},
{"SetSpeedOffset", {PERSISTENT, FLOAT, "0.0", "0.0", 2}},
{"ShowCEMStatus", {PERSISTENT, BOOL, "1", "0", 2}},
{"ShowCCMStatus", {PERSISTENT, BOOL, "0", "0", 2}},
{"ShowCPU", {PERSISTENT, BOOL, "1", "0", 3}},
{"ShowCSCStatus", {PERSISTENT, BOOL, "1", "0", 2}},
{"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}},
@@ -526,12 +463,10 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
{"ShowStorageUsed", {PERSISTENT, BOOL, "0", "0", 3}},
{"SidebarMetrics", {PERSISTENT, BOOL, "1", "0", 3}},
{"SidebarOpen", {PERSISTENT, BOOL, "0", "0", 0}},
{"SignalAnimation", {PERSISTENT, STRING, "stock", "stock", 0}},
{"SignalAnimation", {PERSISTENT, STRING, "frog", "stock", 0}},
{"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}},
@@ -544,10 +479,8 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
{"SLCPriority1", {PERSISTENT, STRING, "Map Data", "Map Data", 2}},
{"SLCPriority2", {PERSISTENT, STRING, "Dashboard", "Dashboard", 2}},
{"SNGHack", {PERSISTENT, BOOL, "1", "0", 2}},
{"SoundPack", {PERSISTENT, STRING, "stock", "stock", 0}},
{"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}},
@@ -560,17 +493,17 @@ 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.2", "1.2", 2}},
{"StandardJerkAcceleration", {PERSISTENT, FLOAT, "100.0", "100.0", 3}},
{"StandardFollowHigh", {PERSISTENT, FLOAT, "1.45", "1.45", 2}},
{"StandardJerkAcceleration", {PERSISTENT, FLOAT, "50.0", "50.0", 3}},
{"StandardJerkDanger", {PERSISTENT, FLOAT, "100.0", "100.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}},
{"StandardJerkDeceleration", {PERSISTENT, FLOAT, "50.0", "50.0", 3}},
{"StandardJerkSpeed", {PERSISTENT, FLOAT, "50.0", "50.0", 3}},
{"StandardJerkSpeedDecrease", {PERSISTENT, FLOAT, "50.0", "50.0", 3}},
{"StandbyMode", {PERSISTENT, BOOL, "0", "0", 1}},
{"StartAccel", {PERSISTENT, FLOAT, "0.0", "0.0", 3}},
{"StartAccelStock", {PERSISTENT, FLOAT, "0.0", "0.0", 3}},
{"StartupMessageBottom", {PERSISTENT, STRING, "Always keep hands on wheel and eyes on road", "Always keep hands on wheel and eyes on road", 0}},
{"StartupMessageTop", {PERSISTENT, STRING, "Be ready to take over at any time", "Be ready to take over at any time", 0}},
{"StartupMessageBottom", {PERSISTENT, STRING, "Human-tested, frog-approved 🐸", "Always keep hands on wheel and eyes on road", 0}},
{"StartupMessageTop", {PERSISTENT, STRING, "Hop in and buckle up!", "Be ready to take over at any time", 0}},
{"StaticPedalsOnUI", {PERSISTENT, BOOL, "0", "0", 1}},
{"SteerDelay", {PERSISTENT, FLOAT, "0.0", "0.0", 3}},
{"SteerDelayStock", {PERSISTENT, FLOAT, "0.0", "0.0", 3}},
@@ -584,7 +517,6 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
{"SteerOffsetStock", {PERSISTENT, FLOAT, "0.0", "0.0", 3}},
{"SteerRatio", {PERSISTENT, FLOAT, "0.0", "0.0", 3}},
{"SteerRatioStock", {PERSISTENT, FLOAT, "0.0", "0.0", 3}},
{"StockConfidenceBallWidget", {PERSISTENT, BOOL, "0", "0", 0}},
{"StockDongleId", {PERSISTENT, STRING, "", ""}},
{"StopAccel", {PERSISTENT, FLOAT, "0.0", "0.0", 3}},
{"StopAccelStock", {PERSISTENT, FLOAT, "0.0", "0.0", 3}},
@@ -596,8 +528,8 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
{"SwitchbackModeCooldown", {PERSISTENT, INT, "5", "0", 2}},
{"SwitchbackModeEnabled", {CLEAR_ON_OFFROAD_TRANSITION, BOOL, "0", "0"}},
{"SubaruSNG", {PERSISTENT, BOOL, "1", "0", 2}},
{"SubaruSNGManualParkingBrake", {PERSISTENT, BOOL, "0", "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, "", ""}},
@@ -605,7 +537,6 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
{"Timezone", {PERSISTENT, STRING, "", ""}},
{"TinygradUpdateAvailable", {PERSISTENT, BOOL, "0", "0", 1}},
{"ToyotaDoors", {PERSISTENT, BOOL, "1", "0", 0}},
{"TrailerLoad", {PERSISTENT, INT, "0", "0", 2}},
{"TrafficFollow", {PERSISTENT, FLOAT, "0.5", "0.5", 2}},
{"TrafficJerkAcceleration", {PERSISTENT, FLOAT, "50.0", "50.0", 3}},
{"TrafficJerkDanger", {PERSISTENT, FLOAT, "100.0", "100.0", 3}},
@@ -631,20 +562,16 @@ 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}},
{"VoltOnePedalMode", {PERSISTENT, BOOL, "0", "0", 2}},
{"ToyotaAutoHold", {PERSISTENT, BOOL, "0", "0", 2}},
{"WarningImmediateVolume", {PERSISTENT, INT, "101", "101", 2}},
{"WarningSoftVolume", {PERSISTENT, INT, "101", "101", 2}},
{"WeatherPresets", {PERSISTENT, BOOL, "0", "0", 2}},
{"WeatherToken", {PERSISTENT | DONT_LOG, STRING, "", "", 2}},
{"WheelControls", {PERSISTENT, STRING, "", "", 2}},
{"WheelIcon", {PERSISTENT, STRING, "stock", "stock", 0}},
{"WheelIcon", {PERSISTENT, STRING, "frog", "stock", 0}},
{"WheelSpeed", {PERSISTENT, BOOL, "0", "0", 2}},
{"WheelToDownload", {CLEAR_ON_MANAGER_START, STRING, "", ""}},
};
Binary file not shown.
+2 -2
View File
@@ -133,8 +133,8 @@ class TestParams:
def test_params_get_type(self):
# json
self.params.put("ApiCache_DriveStats", {"a": 0})
assert self.params.get("ApiCache_DriveStats") == {"a": 0}
self.params.put("ApiCache_FirehoseStats", {"a": 0})
assert self.params.get("ApiCache_FirehoseStats") == {"a": 0}
# int
self.params.put("BootCount", 1441)
-32
View File
@@ -1,40 +1,8 @@
import contextlib
import gc
import os
import platform
import sys
from pathlib import Path
import pytest
def _prepend_host_pytest_runtime() -> None:
if platform.system() != "Darwin" or os.getenv("SP_DISABLE_HOST_PYTEST_REDIRECT") == "1":
return
root_dir = Path(__file__).resolve().parent
work_dir = root_dir / ".host_runtime" / "darwin" / "worktree"
required_extension = work_dir / "msgq_repo" / "msgq" / "ipc_pyx.so"
if not required_extension.exists():
return
extra_paths = [work_dir, work_dir / "starpilot" / "third_party"]
extra_paths.extend(sorted(work_dir.glob("*_repo")))
acados_dir = work_dir / "third_party" / "acados"
if acados_dir.is_dir():
extra_paths.append(acados_dir)
existing = set(sys.path)
insert_at = 0
for path in [str(p) for p in extra_paths if p.exists()]:
if path in existing:
continue
sys.path.insert(insert_at, path)
insert_at += 1
_prepend_host_pytest_runtime()
from openpilot.common.prefix import OpenpilotPrefix
from openpilot.system.manager import manager
from openpilot.system.hardware import TICI, HARDWARE
+1 -2
View File
@@ -4,7 +4,7 @@
A supported vehicle is one that just works when you install a comma device. All supported cars provide a better experience than any stock system. Supported vehicles reference the US market unless otherwise specified.
# 326 Supported Cars
# 325 Supported Cars
|Make|Model|Supported Package|ACC|No ACC accel below|No ALC below|Steering Torque|Resume from stop|<a href="##"><img width=2000></a>Hardware Needed<br>&nbsp;|Video|Setup Video|
|---|---|---|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|
@@ -110,7 +110,6 @@ A supported vehicle is one that just works when you install a comma device. All
|Hyundai|Azera 2022|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Hyundai K connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Hyundai Azera 2022">Buy Here</a></sub></details>|||
|Hyundai|Azera Hybrid 2019|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Hyundai C connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Hyundai Azera Hybrid 2019">Buy Here</a></sub></details>|||
|Hyundai|Azera Hybrid 2020|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Hyundai K connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Hyundai Azera Hybrid 2020">Buy Here</a></sub></details>|||
|Hyundai|Azera Hybrid (with HDA II & LFA2) 2025|Highway Driving Assist II & Lane Follow Assist 2|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Hyundai S connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Hyundai Azera Hybrid (with HDA II & LFA2) 2025">Buy Here</a></sub></details>|||
|Hyundai|Custin 2023|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Hyundai K connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Hyundai Custin 2023">Buy Here</a></sub></details>|||
|Hyundai|Elantra 2017-18|Smart Cruise Control (SCC)|Stock|19 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|<details><summary>Parts</summary><sub>- 1 Hyundai B connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Hyundai Elantra 2017-18">Buy Here</a></sub></details>|||
|Hyundai|Elantra 2019|Smart Cruise Control (SCC)|Stock|19 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|<details><summary>Parts</summary><sub>- 1 Hyundai G connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Hyundai Elantra 2019">Buy Here</a></sub></details>|||
-152
View File
@@ -1,152 +0,0 @@
# StarPilot Unified Model Rebuild
This workflow rebuilds StarPilot driving and driver-monitoring artifacts for the vendored tinygrad revision. Driving-model behavior versions remain manifest metadata; every runtime driving artifact uses the `tinygrad_single_v1` layout.
## Safety
- The supported build device is `comma@192.168.3.110`.
- Never run these commands against `192.168.3.109`.
- Do not compile normal and big-GPU artifacts together. This workflow builds normal QCOM artifacts only.
- Keep source ONNX files and compiled PKLs on the T5 workspace, not the comma.
## Workspace
The default workspace is:
```text
/Volumes/T5/StarPilot-Model-Rebuild-2026-06-22/
```
Important directories:
- `onnx/<model-id>/`: ID-prefixed source ONNX files.
- `compiled/`: completed unified driving PKLs.
- `driver-monitoring/`: DM ONNX, model PKL, metadata, and camera warps.
- `ready-for-resources/`: flat repository-upload handoff.
- Oversized models are represented by repository-safe `.p00`, `.p01`, and `.sha256` files in `ready-for-resources/`.
- `logs/`: one remote compilation log per model.
- `results/`: source and artifact checksum records.
- `manifests/`: generated `model_names_v22.json`.
## Initialize And Extract
```bash
python3 scripts/model_rebuild_pipeline.py init
python3 scripts/model_rebuild_pipeline.py extract \
--base-manifest /path/to/model_names_v21.json
```
Extraction streams Git blobs directly to disk. LFS pointers are resolved from the local object cache or fetched by object ID, then checked against the pointer SHA-256 and size. Binary ONNX data is never stored in a shell variable.
To retry one source:
```bash
python3 scripts/model_rebuild_pipeline.py extract \
--model pop22 \
--base-manifest /path/to/model_names_v21.json
```
Source commits are defined in `scripts/model_source_map_v22.json`.
## Compile
Compile one model:
```bash
python3 scripts/model_rebuild_pipeline.py compile \
--model pop22 \
--base-manifest /path/to/model_names_v21.json
```
Compile or resume the full catalog:
```bash
python3 scripts/model_rebuild_pipeline.py compile \
--base-manifest /path/to/model_names_v21.json
```
Existing artifacts are skipped unless `--force` is passed. Each model is staged in its own remote input directory, compiled on `.110`, copied back to the T5, hashed, and copied into `ready-for-resources/`. Failures are written to `results/<id>_failure.json`; rerunning the same command resumes incomplete models.
Validate one or all completed artifacts with synthetic camera inputs on QCOM:
```bash
python3 scripts/model_rebuild_pipeline.py validate \
--model pop22 \
--base-manifest /path/to/model_names_v21.json
```
The lower-level device compiler also supports direct use:
```bash
./models --model pop22 --input-format split --version v11
./models --model deeprl3v2 --input-format supercombo --version v15
```
`--version` records behavioral semantics only. It does not change artifact layout.
If the compiled PKL exceeds 100 MiB, `./models` automatically keeps the full
local PKL and creates 95 MiB upload parts beside it:
```text
deeprl3v2_driving_tinygrad.pkl
deeprl3v2_driving_tinygrad.pkl.p00
deeprl3v2_driving_tinygrad.pkl.p01
deeprl3v2_driving_tinygrad.pkl.sha256
```
To split an already compiled artifact:
```bash
./models --split-artifact /path/to/deeprl3v2_driving_tinygrad.pkl \
--output-dir /path/to/upload-ready
```
Upload only the numbered parts and checksum when the full PKL exceeds the
repository limit. The downloader reassembles into a temporary file, verifies
the companion SHA-256, and atomically installs the final PKL. No manifest field
is required for multipart artifacts.
## Driver Monitoring
Stage the current DM ONNX in `uncompiledmodels`, then run:
```bash
./models --dm \
--input-dir /data/openpilot/uncompiledmodels \
--output-dir /tmp/dm_artifacts
```
This builds:
- `dmonitoring_model_tinygrad.pkl`
- `dmonitoring_model_metadata.pkl`
- `dm_warp_1928x1208_tinygrad.pkl`
- `dm_warp_1344x760_tinygrad.pkl`
All four files must be updated together.
## Manifest
Generate v22 after compilation:
```bash
python3 scripts/model_rebuild_pipeline.py manifest \
--base-manifest /path/to/model_names_v21.json
```
The generator preserves existing IDs and behavioral metadata and adds
`deeprl3v2`. Manifest v22 implies the unified single-PKL runtime layout.
Repository-hosted multipart files are discovered by naming convention, so no
size, hash, format, or part-count metadata is required.
## Runtime Verification
Compilation validates JIT capture/replay, pickle round-trip, finite outputs, metadata slices, and both camera warps. Before release:
1. Select representative v8, v11, v12, v15, and supercombo models.
2. Confirm `modeld` stays running.
3. Confirm finite `modelV2` path, lane-line, lead, pose, and action data.
4. Confirm `driverStateV2` on both supported camera resolutions.
5. Test download, selection, deletion, randomization, migration, and fallback in QT, raylib/mici, and Galaxy.
The built-in South Carolina artifact is `selfdrive/modeld/models/driving_tinygrad.pkl`. If migration cannot download the selected v22 artifact, StarPilot switches to that built-in model.
-113
View File
@@ -1,113 +0,0 @@
# 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.
+63 -4
View File
@@ -33,7 +33,64 @@ function agnos_init {
# StarPilot variables
sudo chmod 0777 /cache
sudo rm -f /data/misc/display/color_cal/color_cal /data/misc/display/color_cal/source.sha256
# Weston loads display color correction from /data/misc/display/color_cal/color_cal.
# Prefer a factory /persist/comma/color_cal blob when present. Otherwise, derive a
# Weston-compatible calibration blob from the device's legacy dwo gamma tables.
COLOR_CAL_SRC="/persist/comma/color_cal"
DWO_GAMMA_SRC="/persist/comma/dwo_gamma_curves"
COLOR_CAL_DST_DIR="/data/misc/display/color_cal"
COLOR_CAL_DST="${COLOR_CAL_DST_DIR}/color_cal"
COLOR_CAL_HASH_PATH="${COLOR_CAL_DST_DIR}/source.sha256"
DWO_REFERENCE="$DIR/tools/reference_dwo_gamma_curves.txt"
DWO_GENERATOR="$DIR/tools/generate_color_cal_from_dwo.py"
COLOR_CAL_UPDATED=0
COLOR_CAL_TMP=""
DWO_SOURCE_HASH=""
if [ -f "$COLOR_CAL_SRC" ]; then
sudo mkdir -p "$COLOR_CAL_DST_DIR"
if [ ! -f "$COLOR_CAL_DST" ] || ! cmp -s "$COLOR_CAL_SRC" "$COLOR_CAL_DST"; then
sudo cp "$COLOR_CAL_SRC" "$COLOR_CAL_DST"
sudo chown -R comma:comma "$COLOR_CAL_DST_DIR"
sudo chmod 664 "$COLOR_CAL_DST"
sudo rm -f "$COLOR_CAL_HASH_PATH"
COLOR_CAL_UPDATED=1
fi
elif [ -f "$DWO_GAMMA_SRC" ] && [ -f "$DWO_REFERENCE" ] && [ -f "$DWO_GENERATOR" ]; then
DWO_SOURCE_HASH="$(cat "$DWO_GAMMA_SRC" "$DWO_REFERENCE" "$DWO_GENERATOR" | sha256sum | awk '{print $1}')"
if [ ! -f "$COLOR_CAL_DST" ] || [ ! -f "$COLOR_CAL_HASH_PATH" ] || [ "$(cat "$COLOR_CAL_HASH_PATH" 2>/dev/null)" != "$DWO_SOURCE_HASH" ]; then
COLOR_CAL_TMP="$(mktemp)"
if python3 "$DWO_GENERATOR" --reference "$DWO_REFERENCE" --input "$DWO_GAMMA_SRC" --output "$COLOR_CAL_TMP"; then
sudo mkdir -p "$COLOR_CAL_DST_DIR"
if [ ! -f "$COLOR_CAL_DST" ] || ! cmp -s "$COLOR_CAL_TMP" "$COLOR_CAL_DST"; then
sudo cp "$COLOR_CAL_TMP" "$COLOR_CAL_DST"
COLOR_CAL_UPDATED=1
fi
printf '%s' "$DWO_SOURCE_HASH" | sudo tee "$COLOR_CAL_HASH_PATH" >/dev/null
sudo chown -R comma:comma "$COLOR_CAL_DST_DIR"
sudo chmod 664 "$COLOR_CAL_DST" "$COLOR_CAL_HASH_PATH"
fi
rm -f "$COLOR_CAL_TMP"
fi
fi
if [ "$COLOR_CAL_UPDATED" = "1" ] && systemctl is-active --quiet weston.service; then
sudo systemctl restart weston.service
# Weston can recreate wayland-0 as root on service restart before weston-ready
# fixes ownership. Repair it here so the Qt UI can always reconnect.
if [ -d /var/tmp/weston ]; then
for _ in $(seq 1 50); do
if [ -S /var/tmp/weston/wayland-0 ]; then
sudo chown -R comma:comma /var/tmp/weston 2>/dev/null || true
sudo chmod -R 700 /var/tmp/weston 2>/dev/null || true
SOCKET_OWNER="$(stat -c '%U:%G' /var/tmp/weston/wayland-0 2>/dev/null || true)"
[ "$SOCKET_OWNER" = "comma:comma" ] && break
fi
sleep 0.1
done
fi
fi
# Check if AGNOS update is required
if [ $(< /VERSION) != "$AGNOS_VERSION" ]; then
@@ -122,6 +179,7 @@ mods = [
"msgq.ipc_pyx",
"msgq.visionipc.visionipc_pyx",
"openpilot.common.transformations.transformations",
"openpilot.selfdrive.modeld.models.commonmodel_pyx",
"openpilot.selfdrive.pandad.pandad_api_impl",
"openpilot.selfdrive.controls.lib.lateral_mpc_lib.c_generated_code.acados_ocp_solver_pyx",
"openpilot.selfdrive.controls.lib.longitudinal_mpc_lib.c_generated_code.acados_ocp_solver_pyx",
@@ -136,11 +194,12 @@ for mod in mods:
repo_root = Path.cwd().parents[1]
required_files = [
repo_root / "selfdrive/modeld/models/driving_tinygrad.pkl",
repo_root / "selfdrive/modeld/models/driving_vision_metadata.pkl",
repo_root / "selfdrive/modeld/models/driving_policy_metadata.pkl",
repo_root / "selfdrive/modeld/models/driving_vision_tinygrad.pkl",
repo_root / "selfdrive/modeld/models/driving_policy_tinygrad.pkl",
repo_root / "selfdrive/modeld/models/dmonitoring_model_metadata.pkl",
repo_root / "selfdrive/modeld/models/dmonitoring_model_tinygrad.pkl",
repo_root / "selfdrive/modeld/models/dm_warp_1928x1208_tinygrad.pkl",
repo_root / "selfdrive/modeld/models/dm_warp_1344x760_tinygrad.pkl",
repo_root / "selfdrive/pandad/pandad_api_impl.so",
repo_root / "selfdrive/controls/lib/lateral_mpc_lib/c_generated_code/acados_ocp_solver_pyx.so",
repo_root / "selfdrive/controls/lib/lateral_mpc_lib/c_generated_code/libacados_ocp_solver_lat.so",
+1 -1
View File
@@ -21,7 +21,7 @@ fi
export QCOM_PRIORITY=12
if [ -z "$AGNOS_VERSION" ]; then
export AGNOS_VERSION="12.8.17"
export AGNOS_VERSION="12.8.16"
fi
export STAGING_ROOT="/data/safe_staging"
-600
View File
@@ -1,600 +0,0 @@
#!/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
from openpilot.tools.lib.auth_config import DEFAULT_API_HOST, KONIK_API_HOST, get_token, normalize_api_host
API_HOST = normalize_api_host(os.getenv("COMMA_API_HOST") or os.getenv("API_HOST") or DEFAULT_API_HOST)
API_HOSTS = [API_HOST] if os.getenv("COMMA_API_HOST") or os.getenv("API_HOST") else [DEFAULT_API_HOST, KONIK_API_HOST]
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, api_host: str) -> str:
return f"{api_host}/v1/route/{quote(route.canonical_name, safe='')}/"
def route_files_url(route: RouteId, api_host: str) -> 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 api_headers(api_host: str) -> dict[str, str] | None:
token = get_token(api_host)
return {"Authorization": f"JWT {token}"} if token else None
def fetch_json(session: requests.Session, url: str, timeout: float, api_host: str | None = None) -> Any:
response = session.get(url, timeout=timeout, allow_redirects=True, headers=api_headers(api_host) if api_host else None)
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]:
route_meta = None
files_payload = None
api_host = None
not_found_hosts = []
for candidate_host in API_HOSTS:
try:
route_meta = fetch_json(session, route_url(route, candidate_host), timeout, candidate_host)
except requests.HTTPError as exc:
status_code = exc.response.status_code if exc.response is not None else "unknown"
if status_code == 404 and len(API_HOSTS) > 1:
not_found_hosts.append(candidate_host)
continue
raise ValidationError(route, [f"route is not publicly accessible from {candidate_host} (HTTP {status_code})."]) from exc
try:
files_payload = fetch_json(session, route_files_url(route, candidate_host), timeout, candidate_host)
except requests.HTTPError as exc:
status_code = exc.response.status_code if exc.response is not None else "unknown"
if status_code == 404 and len(API_HOSTS) > 1:
not_found_hosts.append(candidate_host)
continue
raise ValidationError(route, [f"public route files could not be fetched from {candidate_host} (HTTP {status_code})."]) from exc
api_host = candidate_host
break
if route_meta is None or files_payload is None or api_host is None:
raise ValidationError(route, [f"route was not found on: {', '.join(not_found_hosts) or ', '.join(API_HOSTS)}."])
if not route_meta.get("is_public", False):
raise ValidationError(route, ["route metadata loaded, but `is_public` was false."])
expected_segments = expected_segment_count(route_meta, files_payload)
if expected_segments <= 0:
raise ValidationError(route, ["could not determine any route segments from the route API."])
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 {
"api_host": api_host,
"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" route API: {validation['api_host']}")
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())
+2 -2
View File
@@ -106,7 +106,7 @@ def make_tester_present_msg(addr, bus, subaddr=None, suppress_response=False):
return CanData(addr, bytes(dat), bus)
def get_safety_config(safety_model, safety_param: int = None) -> structs.CarParams.SafetyConfig:
def get_safety_config(safety_model: structs.CarParams.SafetyModel, safety_param: int = None) -> structs.CarParams.SafetyConfig:
ret = structs.CarParams.SafetyConfig()
ret.safetyModel = safety_model
if safety_param is not None:
@@ -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 = max(1, len(CP.safetyConfigs))
num = len(CP.safetyConfigs)
self.offset = 4 * (num - 1)
+2 -24
View File
@@ -11,7 +11,6 @@ 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
@@ -109,20 +108,6 @@ 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
@@ -211,11 +196,6 @@ 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 \
@@ -334,14 +314,12 @@ def get_car(can_recv: CanRecvCallable, can_send: CanSendCallable, set_obd_multip
CP.carFw = car_fw
CP.fingerprintSource = source
CP.fuzzyFingerprint = not exact_match
post_fingerprint_params = getattr(CarInterface, "apply_post_fingerprint_params", None)
if post_fingerprint_params is not None:
post_fingerprint_params(CP, candidate, fingerprints, car_fw)
FPCP: StarPilotCarParams = CarInterface.get_starpilot_params(candidate, fingerprints, car_fw, CP, starpilot_toggles)
if not CP.alphaLongitudinalAvailable and starpilot_toggles.disable_openpilot_long:
_apply_disable_openpilot_long(CP, FPCP)
CP.openpilotLongitudinalControl = False
FPCP.openpilotLongitudinalControlDisabled = True
return interfaces[CP.carFingerprint](CP, FPCP)
@@ -2,7 +2,7 @@ from opendbc.can import CANPacker
from opendbc.car import Bus, DT_CTRL
from opendbc.car.lateral import apply_meas_steer_torque_limits
from opendbc.car.chrysler import chryslercan
from opendbc.car.chrysler.values import RAM_CARS, RAM_DT, CarControllerParams, ChryslerFlags, ChryslerStarPilotFlags
from opendbc.car.chrysler.values import RAM_CARS, RAM_DT, CarControllerParams, ChryslerFlags
from opendbc.car.interfaces import CarControllerBase
@@ -50,9 +50,7 @@ class CarController(CarControllerBase):
# TODO: can we make this more sane? why is it different for all the cars?
lkas_control_bit = self.lkas_control_bit_prev
if self.FPCP is not None and self.FPCP.flags & ChryslerStarPilotFlags.NO_MIN_STEERING_SPEED:
lkas_control_bit = CC.latActive
elif self.CP.carFingerprint in RAM_DT:
if self.CP.carFingerprint in RAM_DT:
if self.CP.minEnableSpeed <= CS.out.vEgo <= self.CP.minEnableSpeed + 0.5:
lkas_control_bit = True
if (self.CP.minEnableSpeed >= 14.5) and (CS.out.gearShifter != 2):
@@ -21,7 +21,6 @@ class ChryslerFlags(IntFlag):
class ChryslerStarPilotFlags(IntFlag):
RAM_HD_ALT_BUTTONS = 1
NO_MIN_STEERING_SPEED = 2
@dataclass
@@ -98,25 +97,6 @@ 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
+1 -18
View File
@@ -5,8 +5,6 @@ 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"
@@ -23,7 +21,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, reset=False):
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):
"""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.
@@ -31,15 +29,6 @@ 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:
@@ -61,7 +50,6 @@ 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)
@@ -71,7 +59,6 @@ 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",
@@ -86,10 +73,6 @@ 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
+1 -3
View File
@@ -1,7 +1,6 @@
#!/usr/bin/env python3
import argparse
import os
from types import SimpleNamespace
from typing import get_args
from collections import defaultdict
@@ -33,8 +32,7 @@ def get_params_for_docs(platform) -> CarParams:
cp_platform = platform if platform in interfaces else MOCK.MOCK
CP: CarParams = interfaces[cp_platform].get_params(cp_platform, fingerprint=gen_empty_fingerprint(),
car_fw=[CarParams.CarFw(ecu=CarParams.Ecu.unknown)],
alpha_long=True, is_release=False, docs=True,
starpilot_toggles=SimpleNamespace())
alpha_long=True, is_release=False, docs=True)
return CP
@@ -131,7 +131,6 @@ class CarHarness(EnumBase):
hyundai_p = BaseCarHarness("Hyundai P connector")
hyundai_q = BaseCarHarness("Hyundai Q connector")
hyundai_r = BaseCarHarness("Hyundai R connector")
hyundai_s = BaseCarHarness("Hyundai S connector")
custom = BaseCarHarness("Developer connector")
obd_ii = BaseCarHarness("OBD-II connector", parts=[Cable.long_obdc_cable, Cable.usbc_coupler], has_connector=False)
gm = BaseCarHarness("GM connector", parts=[Accessory.harness_box])
-23
View File
@@ -72,7 +72,6 @@ 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,
@@ -193,43 +192,28 @@ MIGRATION = {
"HYUNDAI IONIQ PLUG-IN HYBRID 2019": HYUNDAI.HYUNDAI_IONIQ_PHEV_2019,
"HYUNDAI IONIQ PHEV 2020": HYUNDAI.HYUNDAI_IONIQ_PHEV,
"HYUNDAI KONA 2020": HYUNDAI.HYUNDAI_KONA,
"HYUNDAI KONA 2ND GEN": HYUNDAI.HYUNDAI_KONA_2ND_GEN,
"HYUNDAI KONA ELECTRIC 2019": HYUNDAI.HYUNDAI_KONA_EV,
"HYUNDAI KONA ELECTRIC 2022": HYUNDAI.HYUNDAI_KONA_EV_2022,
"HYUNDAI KONA ELECTRIC 2ND GEN": HYUNDAI.HYUNDAI_KONA_EV_2ND_GEN,
"HYUNDAI KONA HYBRID 2020": HYUNDAI.HYUNDAI_KONA_HEV,
"HYUNDAI KONA HYBRID 2ND GEN": HYUNDAI.HYUNDAI_KONA_HEV_2ND_GEN,
"HYUNDAI SANTA FE 2019": HYUNDAI.HYUNDAI_SANTA_FE,
"HYUNDAI SANTA FE 2022": HYUNDAI.HYUNDAI_SANTA_FE_2022,
"HYUNDAI SANTA FE HYBRID 2022": HYUNDAI.HYUNDAI_SANTA_FE_HEV_2022,
"HYUNDAI SANTA FE HYBRID 5TH GEN": HYUNDAI.HYUNDAI_SANTA_FE_HEV_5TH_GEN,
"HYUNDAI SANTA FE PlUG-IN HYBRID 2022": HYUNDAI.HYUNDAI_SANTA_FE_PHEV_2022,
"HYUNDAI SONATA 2020": HYUNDAI.HYUNDAI_SONATA,
"HYUNDAI SONATA 2024": HYUNDAI.HYUNDAI_SONATA_2024,
"HYUNDAI SONATA 2019": HYUNDAI.HYUNDAI_SONATA_LF,
"HYUNDAI STARIA 4TH GEN": HYUNDAI.HYUNDAI_STARIA_4TH_GEN,
"HYUNDAI TUCSON 2019": HYUNDAI.HYUNDAI_TUCSON,
"HYUNDAI TUCSON 2025": HYUNDAI.HYUNDAI_TUCSON_2025,
"HYUNDAI TUCSON HYBRID 2025": HYUNDAI.HYUNDAI_TUCSON_HEV_2025,
"HYUNDAI TUCSON PLUG-IN HYBRID 2025": HYUNDAI.HYUNDAI_TUCSON_PHEV_2025,
"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 SONATA HYBRID 2024": HYUNDAI.HYUNDAI_SONATA_HEV_2024,
"HYUNDAI IONIQ 5 2022": HYUNDAI.HYUNDAI_IONIQ_5,
"HYUNDAI IONIQ 5 2025": HYUNDAI.HYUNDAI_IONIQ_5_PE,
"HYUNDAI IONIQ 5 N 2024": HYUNDAI.HYUNDAI_IONIQ_5_N,
"HYUNDAI IONIQ 6 2023": HYUNDAI.HYUNDAI_IONIQ_6,
"HYUNDAI IONIQ 9 2025": HYUNDAI.HYUNDAI_IONIQ_9,
"HYUNDAI TUCSON 4TH GEN": HYUNDAI.HYUNDAI_TUCSON_4TH_GEN,
"HYUNDAI SANTA CRUZ 1ST GEN": HYUNDAI.HYUNDAI_SANTA_CRUZ_1ST_GEN,
"HYUNDAI SANTA CRUZ 2025": HYUNDAI.HYUNDAI_SANTA_CRUZ_2025,
"HYUNDAI CUSTIN 1ST GEN": HYUNDAI.HYUNDAI_CUSTIN_1ST_GEN,
"KIA FORTE E 2018 & GT 2021": HYUNDAI.KIA_FORTE,
"KIA K4 2025": HYUNDAI.KIA_K4_2025,
"KIA K5 2021": HYUNDAI.KIA_K5_2021,
"KIA K5 2025": HYUNDAI.KIA_K5_2025,
"KIA K5 HYBRID 2020": HYUNDAI.KIA_K5_HEV_2020,
"KIA K8 HYBRID 1ST GEN": HYUNDAI.KIA_K8_HEV_1ST_GEN,
"KIA NIRO EV 2020": HYUNDAI.KIA_NIRO_EV,
@@ -244,29 +228,22 @@ MIGRATION = {
"KIA OPTIMA HYBRID 4TH GEN FACELIFT": HYUNDAI.KIA_OPTIMA_H_G4_FL,
"KIA SELTOS 2021": HYUNDAI.KIA_SELTOS,
"KIA SPORTAGE 5TH GEN": HYUNDAI.KIA_SPORTAGE_5TH_GEN,
"KIA SPORTAGE 2026": HYUNDAI.KIA_SPORTAGE_2026,
"KIA SPORTAGE HYBRID 2026": HYUNDAI.KIA_SPORTAGE_HEV_2026,
"KIA SORENTO GT LINE 2018": HYUNDAI.KIA_SORENTO,
"KIA SORENTO 4TH GEN": HYUNDAI.KIA_SORENTO_4TH_GEN,
"KIA SORENTO 2024": HYUNDAI.KIA_SORENTO_2024,
"KIA SORENTO HYBRID 4TH GEN": HYUNDAI.KIA_SORENTO_HEV_4TH_GEN,
"KIA SORENTO HYBRID 4TH GEN LFA2": HYUNDAI.KIA_SORENTO_HEV_4TH_GEN_LFA2,
"KIA STINGER GT2 2018": HYUNDAI.KIA_STINGER,
"KIA STINGER 2022": HYUNDAI.KIA_STINGER_2022,
"KIA CEED INTRO ED 2019": HYUNDAI.KIA_CEED,
"KIA EV6 2022": HYUNDAI.KIA_EV6,
"KIA EV6 2025": HYUNDAI.KIA_EV6_2025,
"KIA EV9 2025": HYUNDAI.KIA_EV9,
"KIA CARNIVAL 4TH GEN": HYUNDAI.KIA_CARNIVAL_4TH_GEN,
"GENESIS GV60 ELECTRIC 1ST GEN": HYUNDAI.GENESIS_GV60_EV_1ST_GEN,
"GENESIS G70 2018": HYUNDAI.GENESIS_G70,
"GENESIS G70 2020": HYUNDAI.GENESIS_G70_2020,
"GENESIS GV70 1ST GEN": HYUNDAI.GENESIS_GV70_1ST_GEN,
"GENESIS GV70 ELECTRIFIED 2026": HYUNDAI.GENESIS_GV70_ELECTRIFIED_2ND_GEN,
"GENESIS G80 2017": HYUNDAI.GENESIS_G80,
"GENESIS G90 2017": HYUNDAI.GENESIS_G90,
"GENESIS GV80 2023": HYUNDAI.GENESIS_GV80,
"GENESIS GV80 2025": HYUNDAI.GENESIS_GV80_2025,
"MAZDA CX-5": MAZDA.MAZDA_CX5,
"MAZDA CX-9": MAZDA.MAZDA_CX9,
"MAZDA 3": MAZDA.MAZDA_3,
@@ -1,8 +0,0 @@
from opendbc.car.gm.values import AccState, CAR
def get_stock_cc_active_for_cancel(CP, CS):
stock_cc_active = CS.out.cruiseState.enabled or CS.pcm_acc_status != AccState.OFF
if CP.carFingerprint == CAR.CHEVROLET_BOLT_ACC_2022_2023_PEDAL:
return CS.out.cruiseState.enabled
return stock_cc_active
+75 -599
View File
@@ -7,87 +7,53 @@ 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, GMSafetyFlags,
CruiseButtons, GMFlags,
)
from opendbc.car.interfaces import CarControllerBase
from openpilot.common.pid import PIDController
from openpilot.common.params import Params, UnknownKeyName
from openpilot.starpilot.common.testing_grounds import testing_ground
VisualAlert = structs.CarControl.HUDControl.VisualAlert
NetworkLocation = structs.CarParams.NetworkLocation
TransmissionType = structs.CarParams.TransmissionType
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
AUTO_HOLD_STOPPED_SPEED = 0.02
AUTO_HOLD_2019_MIN_BRAKE = 100
BOLT_ACC_PEDAL_FRICTION_RELEASE_FRAMES = 5
BOLT_PEDAL_LONG_ACCEL_LIMIT_BP = [0.0, 1.5, 4.0, 8.0, 15.0, 30.0]
BOLT_PEDAL_LONG_ACCEL_LIMIT_V = [-0.93, -1.28, -1.98, -2.58, -2.86, -2.95]
VOLT_ONE_PEDAL_DECEL_BP = [0.5 * CV.MPH_TO_MS, 6.0 * CV.MPH_TO_MS]
VOLT_ONE_PEDAL_DECEL_V = [-1.0, -1.1]
VOLT_ONE_PEDAL_REGEN_PADDLE_DECEL_V = [-1.5, -1.6]
VOLT_ONE_PEDAL_MAX_DECEL = min((*VOLT_ONE_PEDAL_DECEL_V, *VOLT_ONE_PEDAL_REGEN_PADDLE_DECEL_V)) - 0.5
VOLT_ONE_PEDAL_PID_NEG_LIMIT = -3.5
VOLT_ONE_PEDAL_SPEED_ERROR_FACTOR_BP = [1.5, 20.0]
VOLT_ONE_PEDAL_SPEED_ERROR_FACTOR_V = [0.4, 0.2]
VOLT_ONE_PEDAL_DECEL_RATE_LIMIT_SPEED_FACTOR_BP = [0.0, 10.0 * CV.MPH_TO_MS]
VOLT_ONE_PEDAL_DECEL_RATE_LIMIT_SPEED_FACTOR_V = [0.2, 1.0]
VOLT_ONE_PEDAL_DECEL_RATE_LIMIT_STEER_FACTOR_BP = [20.0, 120.0]
VOLT_ONE_PEDAL_DECEL_RATE_LIMIT_STEER_FACTOR_V = [1.0, 0.2]
VOLT_ONE_PEDAL_DECEL_RATE_LIMIT_UP = 0.8 * DT_CTRL * 4
VOLT_ONE_PEDAL_DECEL_RATE_LIMIT_DOWN = 0.8 * DT_CTRL * 4
VOLT_ONE_PEDAL_ACCEL_PITCH_FACTOR_BP = [4.0, 8.0]
VOLT_ONE_PEDAL_ACCEL_PITCH_FACTOR_V = [0.4, 1.0]
VOLT_ONE_PEDAL_ACCEL_PITCH_FACTOR_INCLINE_V = [0.2, 1.0]
VOLT_ONE_PEDAL_LIFT_BRAKE_BP = [0.0, CarControllerParams.NEAR_STOP_BRAKE_PHASE, 2.0 * CV.MPH_TO_MS]
VOLT_ONE_PEDAL_LIFT_BRAKE_V = [AUTO_HOLD_MIN_BRAKE, AUTO_HOLD_MIN_BRAKE, 20.0]
VOLT_ONE_PEDAL_LIFT_BRAKE_FRAMES = 8
TRUCK_LONG_SMOOTH_CARS = {
CAR.CHEVROLET_SILVERADO,
CAR.CHEVROLET_SILVERADO_CC,
}
def get_lka_steering_cmd_counter(counter, CS):
if CS.loopback_lka_steering_cmd_updated:
return (CS.loopback_lka_steering_cmd_counter + 1) % 4
if CS.loopback_lka_steering_cmd_ts_nanos == 0 and counter < 0:
return (CS.pt_lka_steering_cmd_counter + 1) % 4
return counter % 4
def get_stock_cc_active_for_cancel(CP, CS):
if CS.out.accFaulted:
return False
stock_cc_active = CS.out.cruiseState.enabled or CS.pcm_acc_status != AccState.OFF
if CP.carFingerprint == CAR.CHEVROLET_BOLT_ACC_2022_2023_PEDAL:
return CS.out.cruiseState.enabled
return stock_cc_active
def should_send_stock_long_cancel(cancel_counter, CS):
return cancel_counter > CAMERA_CANCEL_DELAY_FRAMES and not CS.out.accFaulted
def use_interceptor_sng_launch(CP, CS, maneuver_mode=False):
# Restrict the fixed standstill-launch gas to actual near-zero motion
# so higher accel requests can take over once the car has started moving.
launch_speed = max(CP.vEgoStarting, 0.3)
if maneuver_mode:
launch_speed = max(launch_speed, 2.0)
near_stop = CS.out.standstill or CS.out.vEgo < launch_speed
if (
getattr(CP, "carFingerprint", None) == CAR.CHEVROLET_BOLT_ACC_2022_2023_PEDAL and
getattr(CP, "enableGasInterceptorDEPRECATED", False)
):
return near_stop
return CS.out.cruiseState.standstill and near_stop
return CS.out.cruiseState.standstill and (CS.out.standstill or CS.out.vEgo < launch_speed)
def should_spoof_dash_speed(CP, starpilot_toggles):
@@ -113,28 +79,6 @@ 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_status_active(CP, CC):
if CC.enabled:
return True
return CP.carFingerprint == CAR.BUICK_LACROSSE_ASCM and CC.latActive
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,
@@ -170,245 +114,10 @@ def get_adas_keepalive_step(CP, is_kaofui_car):
return None
def should_send_adas_status(CP, is_kaofui_car):
if CP.radarUnavailable:
return False
if not is_kaofui_car:
return True
if CP.carFingerprint in ASCM_INT:
return False
return CP.networkLocation != NetworkLocation.fwdCamera and CP.carFingerprint not in SDGM_CAR
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 shape_truck_positive_accel(accel: float, v_ego: float, enabled: bool) -> float:
if not enabled or accel <= 0.0 or v_ego < 12.0:
return accel
low_scale = float(np.interp(v_ego, [12.0, 18.0, 25.0, 35.0], [0.95, 0.88, 0.82, 0.76]))
mid_scale = float(np.interp(v_ego, [12.0, 18.0, 25.0, 35.0], [0.98, 0.94, 0.89, 0.84]))
if accel <= 0.12:
return accel * low_scale
if accel <= 0.35:
return float(np.interp(accel, [0.12, 0.35], [0.12 * low_scale, 0.35 * mid_scale]))
if accel <= 0.65:
return float(np.interp(accel, [0.35, 0.65], [0.35 * mid_scale, 0.65]))
return accel
def get_lka_steering_cmd_counter(next_counter: int, CS) -> int:
if getattr(CS, "loopback_lka_steering_cmd_updated", False):
return (getattr(CS, "loopback_lka_steering_cmd_counter", next_counter) + 1) % 4
if next_counter < 0 and getattr(CS, "loopback_lka_steering_cmd_ts_nanos", 0) == 0:
return (getattr(CS, "pt_lka_steering_cmd_counter", next_counter) + 1) % 4
return next_counter
def should_send_stock_long_cancel(cancel_counter: int, CS) -> bool:
cs_out = getattr(CS, "out", None)
return cancel_counter > CAMERA_CANCEL_DELAY_FRAMES and not bool(getattr(cs_out, "accFaulted", False))
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 = 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 supports_volt_one_pedal(CP, one_pedal_enabled: bool):
safety_cfg = getattr(CP, "safetyConfigs", ())
safety_param = safety_cfg[0].safetyParam if safety_cfg else 0
stock_hold_safety_ready = bool(safety_param & GMSafetyFlags.FLAG_GM_PANDA_PADDLE_SCHED.value)
return (
one_pedal_enabled and
stock_hold_safety_ready and
getattr(CP, "transmissionType", None) == TransmissionType.direct and
CP.carFingerprint in AUTO_HOLD_VOLT_CARS
)
def estimate_auto_hold_brake(driver_brake: float, op_brake: float, CP=None) -> 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))
min_brake = AUTO_HOLD_2019_MIN_BRAKE if getattr(CP, "carFingerprint", None) == CAR.CHEVROLET_VOLT_2019 else AUTO_HOLD_MIN_BRAKE
return int(round(np.clip(hold_brake, min_brake, AUTO_HOLD_MAX_BRAKE)))
def get_auto_hold_stop_threshold(CP, auto_hold_engaged: bool) -> float:
if auto_hold_engaged and getattr(CP, "carFingerprint", None) == CAR.CHEVROLET_VOLT_2019:
return CarControllerParams.NEAR_STOP_BRAKE_PHASE
return AUTO_HOLD_STOPPED_SPEED
def get_volt_one_pedal_target_decel(v_ego: float) -> float:
return float(np.interp(v_ego, VOLT_ONE_PEDAL_DECEL_BP, VOLT_ONE_PEDAL_DECEL_V))
def get_volt_one_pedal_lift_brake(v_ego: float) -> int:
if v_ego > VOLT_ONE_PEDAL_LIFT_BRAKE_BP[-1]:
return 0
return int(round(np.interp(v_ego, VOLT_ONE_PEDAL_LIFT_BRAKE_BP, VOLT_ONE_PEDAL_LIFT_BRAKE_V)))
def should_activate_volt_one_pedal(one_pedal_ready: bool, cruise_main: bool, long_active: bool,
gas_pressed: bool, brake_pressed: bool, regen_braking: bool,
single_pedal_mode: bool, gear_shifter, drive_time_s: float) -> bool:
# Volt rear wheel direction bits can falsely report reverse while stopping in L.
return (
one_pedal_ready and
cruise_main and
single_pedal_mode and
gear_shifter in AUTO_HOLD_DRIVE_GEARS and
drive_time_s >= AUTO_HOLD_MIN_DRIVE_TIME_S and
not long_active and
not gas_pressed and
not brake_pressed and
not regen_braking
)
def should_activate_auto_hold(hold_ready: bool, auto_hold_armed: bool, auto_hold_engaged: bool,
brake_pressed: bool, gas_pressed: bool, standstill: bool, long_active: bool,
regen_braking: bool, v_ego: float, stop_speed_threshold: float=AUTO_HOLD_STOPPED_SPEED) -> bool:
stopped = standstill or v_ego < stop_speed_threshold
return (
hold_ready and
(auto_hold_armed or auto_hold_engaged or brake_pressed) and
not gas_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
def supports_bolt_acc_pedal_friction_experiment(CP) -> bool:
return (
CP.carFingerprint == CAR.CHEVROLET_BOLT_ACC_2022_2023_PEDAL and
CP.openpilotLongitudinalControl and
CP.enableGasInterceptorDEPRECATED and
bool(CP.flags & GMFlags.PEDAL_LONG.value)
)
def get_bolt_acc_pedal_friction_brake(apply_brake, full_brake_accel, v_ego, params) -> int:
if apply_brake <= 0:
return 0
full_brake_accel = min(full_brake_accel, -0.1)
legacy_full_scale = max(-params.ACCEL_MIN, 0.1)
corrected_scale = legacy_full_scale / max(-full_brake_accel, 0.1)
speed_gain = float(np.interp(v_ego, [0.0, 8.0, 15.0, 25.0], [1.0, 1.08, 1.2, 1.35]))
onset_gain = float(np.interp(
apply_brake,
[0.0, 5.0, 20.0, 60.0, 120.0, 240.0, params.MAX_BRAKE],
[0.0, 1.8, 1.65, 1.4, 1.22, 1.08, 1.0],
))
shaped_brake = apply_brake * corrected_scale * speed_gain * onset_gain
minimum_brake = float(np.interp(v_ego, [0.0, 6.0, 8.0, 12.0, 18.0, 25.0], [0.0, 0.0, 4.0, 10.0, 20.0, 28.0]))
shaped_brake = max(shaped_brake, minimum_brake)
return int(round(np.clip(shaped_brake, 0, params.MAX_BRAKE)))
def shape_bolt_acc_pedal_low_speed_friction(apply_brake: int, v_ego: float, stopping: bool, active: bool):
if apply_brake <= 0:
return 0, False
engage_threshold = float(np.interp(v_ego, [0.0, 1.5, 3.0, 5.0, 8.0], [40.0, 20.0, 12.0, 10.0, 0.0]))
release_threshold = float(np.interp(v_ego, [0.0, 1.5, 3.0, 5.0, 8.0], [0.0, 8.0, 6.0, 4.0, 0.0]))
if not active:
if apply_brake < engage_threshold:
return 0, False
active = True
elif apply_brake < release_threshold:
return 0, False
if stopping:
stop_fade = float(np.interp(v_ego, [0.0, 0.6, 0.9, 1.2, 1.8, 2.8], [0.0, 0.0, 0.05, 0.12, 0.32, 0.78]))
apply_brake = int(round(apply_brake * stop_fade))
if apply_brake <= 0 or apply_brake < release_threshold:
return 0, False
return apply_brake, active
def get_bolt_pedal_long_accel_limit(v_ego: float) -> float:
return float(np.interp(v_ego, BOLT_PEDAL_LONG_ACCEL_LIMIT_BP, BOLT_PEDAL_LONG_ACCEL_LIMIT_V))
def get_bolt_acc_pedal_planner_brake_switch(v_ego: float, params, tire_radius: float, mass: float,
coeff_drag: float, frontal_area: float, air_density: float) -> int:
planner_accel_limit = get_bolt_pedal_long_accel_limit(v_ego)
aero_drag_force = 0.5 * coeff_drag * frontal_area * air_density * v_ego ** 2
planner_torque = tire_radius * ((mass * planner_accel_limit) + aero_drag_force)
return int(round(planner_torque + params.ZERO_GAS))
def get_bolt_acc_pedal_effective_brake_switch(stock_switch: int, planner_switch: int) -> int:
return max(stock_switch, planner_switch)
def get_bolt_acc_pedal_friction_command_state(apply_brake: int, cruise_main_on: bool, release_frames: int):
command_brake = apply_brake if cruise_main_on else 0
if command_brake > 0:
release_frames = BOLT_ACC_PEDAL_FRICTION_RELEASE_FRAMES
elif release_frames > 0:
release_frames -= 1
should_send = cruise_main_on or release_frames > 0
return command_brake, release_frames, should_send
def get_interceptor_sng_gas_cmd(CP, interceptor_gas_cmd: float, accel: float, params, maneuver_mode: bool) -> float:
if maneuver_mode:
return max(interceptor_gas_cmd, float(np.interp(accel, [0.0, 1.0, 2.0], [params.SNG_INTERCEPTOR_GAS, 0.11, 0.16])))
if supports_bolt_acc_pedal_friction_experiment(CP):
return max(interceptor_gas_cmd, params.SNG_INTERCEPTOR_GAS)
return params.SNG_INTERCEPTOR_GAS
def should_use_fixed_stopping_brake(CP, near_stop: bool, stopping: bool, resume: bool) -> bool:
if not (near_stop and stopping and not resume):
return False
return not supports_bolt_acc_pedal_friction_experiment(CP)
class CarController(CarControllerBase):
def __init__(self, dbc_names, CP):
super().__init__(dbc_names, CP)
@@ -420,7 +129,7 @@ class CarController(CarControllerBase):
self.last_button_frame = 0
self.cancel_counter = 0
self.lka_steering_cmd_counter = 0
self.lka_steering_cmd_counter = -1
self.lka_icon_status_last = (False, False)
self.params = CarControllerParams(self.CP)
@@ -461,60 +170,6 @@ 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
self.volt_one_pedal_pid = PIDController(
(CP.longitudinalTuning.kpBP, CP.longitudinalTuning.kpV),
(CP.longitudinalTuning.kiBP, CP.longitudinalTuning.kiV),
rate=1 / (DT_CTRL * 4),
pos_limit=0.0,
neg_limit=VOLT_ONE_PEDAL_PID_NEG_LIMIT,
)
self.volt_one_pedal_decel = 0.0
self.volt_one_pedal_brake = 0
self.volt_one_pedal_lift_frames = 0
self.volt_one_pedal_gas_pressed_last = False
try:
self.gm_auto_hold_enabled = self.params_.get_bool("GMAutoHold")
except UnknownKeyName:
self.gm_auto_hold_enabled = False
self.bolt_acc_pedal_friction_release_frames = 0
self.bolt_acc_pedal_friction_low_speed_active = False
def _reset_volt_one_pedal(self):
self.volt_one_pedal_pid.reset()
self.volt_one_pedal_decel = min(0.0, float(self.aego))
self.volt_one_pedal_brake = 0
self.volt_one_pedal_lift_frames = 0
def _update_volt_one_pedal_brake(self, CC, CS):
pitch_accel = 0.0
if len(CC.orientationNED) == 3 and CS.out.vEgo > self.CP.vEgoStopping:
pitch_accel = math.sin(CC.orientationNED[1]) * ACCELERATION_DUE_TO_GRAVITY
pitch_factor_values = VOLT_ONE_PEDAL_ACCEL_PITCH_FACTOR_V if pitch_accel <= 0.0 else VOLT_ONE_PEDAL_ACCEL_PITCH_FACTOR_INCLINE_V
pitch_accel *= float(np.interp(CS.out.vEgo, VOLT_ONE_PEDAL_ACCEL_PITCH_FACTOR_BP, pitch_factor_values))
target_decel = get_volt_one_pedal_target_decel(CS.out.vEgo)
measured_decel = min(0.0, CS.out.aEgo + pitch_accel)
error_factor = float(np.interp(CS.out.vEgo, VOLT_ONE_PEDAL_SPEED_ERROR_FACTOR_BP, VOLT_ONE_PEDAL_SPEED_ERROR_FACTOR_V))
error = (target_decel - measured_decel) * error_factor
raw_decel = float(self.volt_one_pedal_pid.update(error, speed=CS.out.vEgo, feedforward=target_decel))
rate_limit_factor = min(
float(np.interp(CS.out.vEgo, VOLT_ONE_PEDAL_DECEL_RATE_LIMIT_SPEED_FACTOR_BP, VOLT_ONE_PEDAL_DECEL_RATE_LIMIT_SPEED_FACTOR_V)),
float(np.interp(abs(CS.out.steeringAngleDeg), VOLT_ONE_PEDAL_DECEL_RATE_LIMIT_STEER_FACTOR_BP, VOLT_ONE_PEDAL_DECEL_RATE_LIMIT_STEER_FACTOR_V)),
)
lower = min(self.volt_one_pedal_decel, measured_decel) - VOLT_ONE_PEDAL_DECEL_RATE_LIMIT_UP * rate_limit_factor
upper = max(self.volt_one_pedal_decel, measured_decel) + VOLT_ONE_PEDAL_DECEL_RATE_LIMIT_DOWN + rate_limit_factor
self.volt_one_pedal_decel = float(np.clip(raw_decel, lower, upper))
self.volt_one_pedal_decel = max(self.volt_one_pedal_decel, VOLT_ONE_PEDAL_MAX_DECEL)
self.volt_one_pedal_brake = int(round(np.clip(
np.interp(self.volt_one_pedal_decel, self.params.BRAKE_LOOKUP_BP, self.params.BRAKE_LOOKUP_V),
0,
self.params.MAX_BRAKE,
)))
if self.volt_one_pedal_lift_frames > 0:
self.volt_one_pedal_brake = max(self.volt_one_pedal_brake, get_volt_one_pedal_lift_brake(CS.out.vEgo))
self.volt_one_pedal_lift_frames -= 1
def calc_pedal_command(self, accel: float, long_active: bool, v_ego: float):
if not long_active:
@@ -637,9 +292,6 @@ 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
@@ -667,64 +319,8 @@ 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)
volt_one_pedal_supported = supports_volt_one_pedal(
self.CP, bool(getattr(starpilot_toggles, "volt_one_pedal_mode", False))
)
volt_one_pedal_active = should_activate_volt_one_pedal(
volt_one_pedal_supported,
CS.out.cruiseState.available,
CC.longActive,
CS.out.gasPressed,
CS.out.brakePressed,
CS.out.regenBraking,
bool(getattr(CS, "single_pedal_mode", False)),
CS.out.gearShifter,
float(getattr(CS, "one_pedal_drive_time", 0.0)),
)
if volt_one_pedal_active and self.volt_one_pedal_gas_pressed_last and not CS.out.gasPressed:
if CS.out.vEgo < VOLT_ONE_PEDAL_LIFT_BRAKE_BP[-1]:
self.volt_one_pedal_lift_frames = VOLT_ONE_PEDAL_LIFT_BRAKE_FRAMES
elif CS.out.gasPressed or not volt_one_pedal_active:
self.volt_one_pedal_lift_frames = 0
if self.frame % 4 == 0:
if volt_one_pedal_active:
self._update_volt_one_pedal_brake(CC, CS)
else:
self._reset_volt_one_pedal()
if not self.CP.openpilotLongitudinalControl:
self.apply_gas = 0
self.apply_brake = self.volt_one_pedal_brake if volt_one_pedal_active else 0
self.volt_one_pedal_gas_pressed_last = CS.out.gasPressed
stock_hold_apply_brake = max(self.apply_brake if self.CP.openpilotLongitudinalControl else 0, self.volt_one_pedal_brake)
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
if CS.out.gasPressed:
CS.auto_hold_engaged = 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, self.CP)
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:
@@ -804,29 +400,6 @@ 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.gasPressed,
CS.out.standstill,
CC.longActive,
CS.out.regenBraking,
CS.out.vEgo,
get_auto_hold_stop_threshold(self.CP, CS.auto_hold_engaged),
)
bolt_acc_pedal_friction_experiment = supports_bolt_acc_pedal_friction_experiment(self.CP)
bolt_acc_pedal_friction_main_on = bolt_acc_pedal_friction_experiment and CS.out.cruiseState.available
if not bolt_acc_pedal_friction_main_on:
self.bolt_acc_pedal_friction_low_speed_active = False
volt_one_pedal_braking = volt_one_pedal_active and self.volt_one_pedal_brake > 0
volt_one_pedal_hold_active = (
volt_one_pedal_braking and
not auto_hold_active and
CS.one_pedal_drive_time >= AUTO_HOLD_MIN_DRIVE_TIME_S and
(CS.out.standstill or CS.out.vEgo < 0.02)
)
# Steering (Active: 50Hz, inactive: 10Hz)
steer_step = self.params.STEER_STEP if CC.latActive else self.params.INACTIVE_STEER_STEP
@@ -840,16 +413,12 @@ class CarController(CarControllerBase):
if CS.loopback_lka_steering_cmd_ts_nanos == 0 or out_of_sync:
steer_step = self.params.STEER_STEP
self.lka_steering_cmd_counter += 1 if CS.loopback_lka_steering_cmd_updated else 0
self.lka_steering_cmd_counter = get_lka_steering_cmd_counter(self.lka_steering_cmd_counter, CS)
# Avoid GM EPS faults when transmitting messages too close together: skip this transmit if we
# received the ASCMLKASteeringCmd loopback confirmation too recently
last_lka_steer_msg_ms = (now_nanos - CS.loopback_lka_steering_cmd_ts_nanos) * 1e-6
if (self.frame - self.last_steer_frame) >= steer_step and last_lka_steer_msg_ms > MIN_STEER_MSG_INTERVAL_MS:
# Initialize ASCMLKASteeringCmd counter using the camera until we get a msg on the bus
if CS.loopback_lka_steering_cmd_ts_nanos == 0:
self.lka_steering_cmd_counter = CS.pt_lka_steering_cmd_counter + 1
if CC.latActive:
new_torque = int(round(actuators.torque * self.params.STEER_MAX))
apply_torque = apply_driver_steer_torque_limits(new_torque, self.apply_torque_last, CS.out.steeringTorque, self.params)
@@ -862,8 +431,10 @@ class CarController(CarControllerBase):
self.last_steer_frame = self.frame
self.apply_torque_last = apply_torque
idx = self.lka_steering_cmd_counter % 4
idx = self.lka_steering_cmd_counter
can_sends.append(gmcan.create_steering_control(self.packer_pt, CanBus.POWERTRAIN, apply_torque, idx, CC.latActive))
# Keep the counter moving even if panda stops returning loopback confirmations.
self.lka_steering_cmd_counter = (idx + 1) % 4
if should_spoof_ecm_cruise_status(self.CP) and self.frame % 4 == 0:
can_sends.append(gmcan.create_ecm_cruise_control_command(
@@ -880,7 +451,6 @@ class CarController(CarControllerBase):
# ASCM sends max regen when not enabled
self.apply_gas = self.params.INACTIVE_REGEN
self.apply_brake = 0
self.bolt_acc_pedal_friction_low_speed_active = False
self.planner_regen_hold = False
self.regen_paddle_pressed = False
self.regen_paddle_timer = 0
@@ -888,7 +458,7 @@ class CarController(CarControllerBase):
self.regen_release_counter = 0
self.regen_min_on_frames = 0
self.regen_min_off_frames = 0
elif should_use_fixed_stopping_brake(self.CP, near_stop, stopping, CC.cruiseControl.resume):
elif near_stop and stopping and not CC.cruiseControl.resume:
stop_accel = getattr(starpilot_toggles, "stopAccel", self.CP.stopAccel)
self.apply_gas = self.params.INACTIVE_REGEN
self.apply_brake = int(min(-100 * stop_accel, self.params.MAX_BRAKE))
@@ -932,16 +502,7 @@ class CarController(CarControllerBase):
if testing_ground.use_1:
accel_max = min(accel_max, np.interp(CS.out.vEgo, [0.0, 4.0, 12.0], [1.25, 1.6, self.params.ACCEL_MAX]))
accel_input = actuators.accel + accel_due_to_pitch
if (
getattr(starpilot_toggles, "truck_tuning", False) and
self.CP.carFingerprint in TRUCK_LONG_SMOOTH_CARS and
getattr(self.CP, "transmissionType", None) == TransmissionType.automatic and
not self.CP.enableGasInterceptorDEPRECATED
):
accel_input = shape_truck_positive_accel(accel_input, CS.out.vEgo, True)
accel_cmd = float(np.clip(accel_input, self.params.ACCEL_MIN, accel_max))
accel_cmd = float(np.clip(actuators.accel + accel_due_to_pitch, self.params.ACCEL_MIN, accel_max))
torque = self.tireRadius * ((self.mass * accel_cmd) + (0.5 * self.coeffDrag * self.frontalArea * self.airDensity * CS.out.vEgo ** 2))
scaled_torque = torque + self.params.ZERO_GAS
apply_gas_torque = np.clip(scaled_torque, self.params.MAX_ACC_REGEN, gas_max)
@@ -949,27 +510,9 @@ class CarController(CarControllerBase):
if testing_ground.use_1:
brake_switch_bias = get_testing_ground_1_brake_switch_bias(CS.out.vEgo)
brake_switch = min(self.params.ZERO_GAS, brake_switch + brake_switch_bias)
if bolt_acc_pedal_friction_main_on:
planner_brake_switch = get_bolt_acc_pedal_planner_brake_switch(
CS.out.vEgo, self.params, self.tireRadius, self.mass, self.coeffDrag, self.frontalArea, self.airDensity,
)
brake_switch = get_bolt_acc_pedal_effective_brake_switch(brake_switch, planner_brake_switch)
brake_accel = min((scaled_torque - brake_switch) / (self.tireRadius * self.mass), 0)
self.apply_gas = int(round(apply_gas_torque))
self.apply_brake = int(round(np.interp(brake_accel, self.params.BRAKE_LOOKUP_BP, self.params.BRAKE_LOOKUP_V)))
if bolt_acc_pedal_friction_main_on:
if self.apply_brake > 0:
full_brake_accel = min(
self.params.ACCEL_MIN + (0.5 * self.coeffDrag * self.frontalArea * self.airDensity * CS.out.vEgo ** 2) / self.mass +
(self.params.ZERO_GAS - brake_switch) / (self.tireRadius * self.mass),
-0.1,
)
self.apply_brake = get_bolt_acc_pedal_friction_brake(
self.apply_brake, full_brake_accel, CS.out.vEgo, self.params,
)
self.apply_brake, self.bolt_acc_pedal_friction_low_speed_active = shape_bolt_acc_pedal_low_speed_friction(
self.apply_brake, CS.out.vEgo, stopping, self.bolt_acc_pedal_friction_low_speed_active,
)
if self.apply_brake > 0:
self.apply_gas = self.params.INACTIVE_REGEN
@@ -980,19 +523,15 @@ class CarController(CarControllerBase):
# gas interceptor only used for full long control on cars without ACC
interceptor_gas_cmd, press_regen_paddle = self.calc_pedal_command(actuators.accel, CC.longActive, CS.out.vEgo)
if volt_one_pedal_braking:
self.apply_gas = self.params.INACTIVE_REGEN
self.apply_brake = max(self.apply_brake, self.volt_one_pedal_brake)
maneuver_sng_launch = self.longitudinal_maneuver_mode and self.is_volt
if (
self.CP.enableGasInterceptorDEPRECATED and
self.apply_gas > self.params.INACTIVE_REGEN and
use_interceptor_sng_launch(self.CP, CS, maneuver_sng_launch)
):
interceptor_gas_cmd = get_interceptor_sng_gas_cmd(
self.CP, interceptor_gas_cmd, actuators.accel, self.params, maneuver_sng_launch,
)
interceptor_gas_cmd = self.params.SNG_INTERCEPTOR_GAS
if maneuver_sng_launch:
interceptor_gas_cmd = max(interceptor_gas_cmd, float(np.interp(actuators.accel, [0.0, 1.0, 2.0], [self.params.SNG_INTERCEPTOR_GAS, 0.11, 0.16])))
self.apply_brake = 0
self.apply_gas = self.params.INACTIVE_REGEN
@@ -1026,33 +565,20 @@ class CarController(CarControllerBase):
can_sends.append(gmcan.create_buttons(self.packer_pt, CanBus.POWERTRAIN, (CS.buttons_counter + 1) % 4, CruiseButtons.DECEL_SET))
if self.CP.enableGasInterceptorDEPRECATED:
can_sends.append(create_gas_interceptor_command(self.packer_pt, interceptor_gas_cmd, idx))
if bolt_acc_pedal_friction_experiment:
friction_brake_bus = get_friction_brake_bus(self.CP)
if self.CP.networkLocation == NetworkLocation.fwdCamera:
at_full_stop = at_full_stop and stopping
experiment_brake, self.bolt_acc_pedal_friction_release_frames, should_send_bolt_acc_pedal_friction = \
get_bolt_acc_pedal_friction_command_state(
self.apply_brake,
bolt_acc_pedal_friction_main_on,
self.bolt_acc_pedal_friction_release_frames,
)
# This fingerprint is routed through the CC-only pedal path, so it
# does not fall through to the normal friction-brake sender below.
# Never apply stock friction with cruise main off, but do send a short
# explicit zero-brake unwind so the last nonzero stock-EBCM command
# cannot linger after a disengage or main-off event.
if should_send_bolt_acc_pedal_friction:
can_sends.append(gmcan.create_friction_brake_command(
self.packer_ch, friction_brake_bus, experiment_brake, idx, bolt_acc_pedal_friction_main_on,
near_stop, at_full_stop, self.CP))
if self.CP.carFingerprint not in CC_ONLY_CAR:
friction_brake_bus = get_friction_brake_bus(self.CP)
volt_gateway_alt_brake = (
self.CP.carFingerprint == CAR.CHEVROLET_VOLT and
self.CP.networkLocation == NetworkLocation.gateway and
bool(self.CP.flags & GMFlags.NO_ACCELERATOR_POS_MSG.value)
)
friction_brake_bus = CanBus.POWERTRAIN if volt_gateway_alt_brake else CanBus.CHASSIS
# 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
@@ -1063,66 +589,47 @@ class CarController(CarControllerBase):
else:
acc_engaged = CC.enabled
if auto_hold_active:
hold_brake = max(self.volt_one_pedal_brake, self.auto_hold_brake or estimate_auto_hold_brake(CS.out.brake, self.apply_brake, self.CP))
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, allow_near_stop_mode=True))
CS.auto_hold_engaged = True
CS.auto_hold_fault_suppression_timer = 1.0
elif volt_one_pedal_hold_active:
hold_brake = max(self.volt_one_pedal_brake, self.auto_hold_brake or estimate_auto_hold_brake(0.0, self.volt_one_pedal_brake, self.CP))
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, allow_near_stop_mode=True))
CS.auto_hold_engaged = True
CS.auto_hold_fault_suppression_timer = 1.0
else:
if volt_one_pedal_braking:
at_full_stop = at_full_stop or CS.pcm_acc_status == AccState.STANDSTILL
near_stop = near_stop or (CS.out.vEgo < self.params.NEAR_STOP_BRAKE_PHASE)
# 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,
allow_near_stop_mode=volt_one_pedal_braking))
CS.auto_hold_engaged = False
# 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 should_send_acc_dashboard_status(self.CP, dash_speed_spoof_active):
fcw_alert = get_acc_dashboard_fcw_alert(hud_alert, CS)
acc_dashboard_status_active = get_acc_dashboard_status_active(self.CP, CC)
can_sends.append(gmcan.create_acc_dashboard_command(self.packer_pt, CanBus.POWERTRAIN, acc_dashboard_status_active,
hud_v_cruise * CV.MS_TO_KPH, hud_control, fcw_alert))
send_fcw = hud_alert == VisualAlert.fcw
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))
# Radar needs to know current speed and yaw rate (50hz),
# and that ADAS is alive (10hz)
if should_send_adas_status(self.CP, self.CP.carFingerprint in kaofui_cars):
tt = self.frame * DT_CTRL
if not self.CP.radarUnavailable:
send_adas = True
if self.CP.carFingerprint in kaofui_cars:
time_and_headlights_step = 10
speed_and_accelerometer_step = 2
if self.frame % time_and_headlights_step == 0:
idx = (self.frame // time_and_headlights_step) % 4
can_sends.append(gmcan.create_adas_time_status(CanBus.OBSTACLE, int((tt - self.start_time) * 60), idx))
can_sends.append(gmcan.create_adas_headlights_status(self.packer_obj, CanBus.OBSTACLE))
if self.frame % speed_and_accelerometer_step == 0:
idx = (self.frame // speed_and_accelerometer_step) % 4
can_sends.append(gmcan.create_adas_steering_status(CanBus.OBSTACLE, idx))
can_sends.append(gmcan.create_adas_accelerometer_speed_status(CanBus.OBSTACLE, CS.out.vEgo, idx))
else:
time_and_headlights_step = 20
if self.frame % time_and_headlights_step == 0:
idx = (self.frame // time_and_headlights_step) % 4
can_sends.append(gmcan.create_adas_time_status(CanBus.OBSTACLE, int((tt - self.start_time) * 60), idx))
can_sends.append(gmcan.create_adas_headlights_status(self.packer_obj, CanBus.OBSTACLE))
can_sends.append(gmcan.create_adas_steering_status(CanBus.OBSTACLE, idx))
can_sends.append(gmcan.create_adas_accelerometer_speed_status(CanBus.OBSTACLE, CS.out.vEgo, idx))
if self.CP.carFingerprint not in ASCM_INT:
send_adas = (self.CP.networkLocation != NetworkLocation.fwdCamera) and (self.CP.carFingerprint not in SDGM_CAR)
if send_adas:
tt = self.frame * DT_CTRL
if self.CP.carFingerprint in kaofui_cars:
time_and_headlights_step = 10
speed_and_accelerometer_step = 2
if self.frame % time_and_headlights_step == 0:
idx = (self.frame // time_and_headlights_step) % 4
can_sends.append(gmcan.create_adas_time_status(CanBus.OBSTACLE, int((tt - self.start_time) * 60), idx))
can_sends.append(gmcan.create_adas_headlights_status(self.packer_obj, CanBus.OBSTACLE))
if self.frame % speed_and_accelerometer_step == 0:
idx = (self.frame // speed_and_accelerometer_step) % 4
can_sends.append(gmcan.create_adas_steering_status(CanBus.OBSTACLE, idx))
can_sends.append(gmcan.create_adas_accelerometer_speed_status(CanBus.OBSTACLE, CS.out.vEgo, idx))
else:
time_and_headlights_step = 20
if self.frame % time_and_headlights_step == 0:
idx = (self.frame // time_and_headlights_step) % 4
can_sends.append(gmcan.create_adas_time_status(CanBus.OBSTACLE, int((tt - self.start_time) * 60), idx))
can_sends.append(gmcan.create_adas_headlights_status(self.packer_obj, CanBus.OBSTACLE))
can_sends.append(gmcan.create_adas_steering_status(CanBus.OBSTACLE, idx))
can_sends.append(gmcan.create_adas_accelerometer_speed_status(CanBus.OBSTACLE, CS.out.vEgo, idx))
keepalive_step = get_adas_keepalive_step(self.CP, self.CP.carFingerprint in kaofui_cars)
if keepalive_step is not None and self.frame % keepalive_step == 0:
@@ -1146,46 +653,15 @@ 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 = max(self.volt_one_pedal_brake, self.auto_hold_brake or estimate_auto_hold_brake(CS.out.brake, stock_hold_apply_brake, self.CP))
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, allow_near_stop_mode=True))
CS.auto_hold_engaged = True
CS.auto_hold_fault_suppression_timer = 1.0
elif self.frame % 4 == 0 and volt_one_pedal_hold_active:
idx = (self.frame // 4) % 4
hold_brake = max(self.volt_one_pedal_brake, self.auto_hold_brake or estimate_auto_hold_brake(0.0, self.volt_one_pedal_brake, self.CP))
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, allow_near_stop_mode=True))
CS.auto_hold_engaged = True
CS.auto_hold_fault_suppression_timer = 1.0
elif self.frame % 4 == 0 and volt_one_pedal_braking:
idx = (self.frame // 4) % 4
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), self.volt_one_pedal_brake, idx, False, near_stop, False,
self.CP, allow_near_stop_mode=True))
CS.auto_hold_engaged = False
elif self.frame % 4 == 0:
self.apply_brake = 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
# Stock longitudinal, integrated at camera
if self.CP.carFingerprint == CAR.CHEVROLET_MALIBU_HYBRID_CC and self.cancel_counter > CAMERA_CANCEL_DELAY_FRAMES:
if self.CP.carFingerprint == CAR.CHEVROLET_MALIBU_HYBRID_CC and should_send_stock_long_cancel(self.cancel_counter, CS):
malibu_cancel_requested = True
elif (self.frame - self.last_button_frame) * DT_CTRL > 0.04:
if self.cancel_counter > CAMERA_CANCEL_DELAY_FRAMES:
if should_send_stock_long_cancel(self.cancel_counter, CS):
self.last_button_frame = self.frame
sdgm_stock_cancel_pt = (
self.CP.carFingerprint in SDGM_CAR and
+41 -120
View File
@@ -2,7 +2,6 @@ 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 (
@@ -28,41 +27,15 @@ 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}
HARD_BUTTONS_DICT = {CruiseButtons.RES_ACCEL: ButtonType.accelCruise, CruiseButtons.DECEL_SET: ButtonType.decelCruise}
NORMAL_CRUISE_BUTTONS = (CruiseButtons.RES_ACCEL, CruiseButtons.DECEL_SET)
def get_hard_cruise_buttons(steering_button_msg: dict) -> int:
return steering_button_msg.get("ACCButtonsHard", CruiseButtons.INIT)
GearShifter = structs.CarState.GearShifter
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,
}
def update_auto_hold_drive_timers(in_drive_for_hold: bool, moving_for_hold: bool,
auto_hold_drive_time: float, one_pedal_drive_time: float) -> tuple[float, float]:
if in_drive_for_hold:
if moving_for_hold:
auto_hold_drive_time = min(auto_hold_drive_time + DT_CTRL, AUTO_HOLD_MIN_DRIVE_TIME_S)
one_pedal_drive_time = min(one_pedal_drive_time + DT_CTRL, AUTO_HOLD_MIN_DRIVE_TIME_S)
else:
auto_hold_drive_time = 0.0
one_pedal_drive_time = 0.0
return auto_hold_drive_time, one_pedal_drive_time
class CarState(CarStateBase):
@@ -74,6 +47,7 @@ class CarState(CarStateBase):
self.cluster_min_speed = CV.KPH_TO_MS / 2.
self.loopback_lka_steering_cmd_updated = False
self.loopback_lka_steering_cmd_counter = 0
self.loopback_lka_steering_cmd_ts_nanos = 0
self.pt_lka_steering_cmd_counter = 0
self.cam_lka_steering_cmd_counter = 0
@@ -84,17 +58,8 @@ class CarState(CarStateBase):
self.prev_distance_button = 0
self.distance_button = 0
self.hard_cruise_buttons = CruiseButtons.INIT
self.force_reset_cruise_buttons = False
self.single_pedal_mode = False
self.auto_hold_armed = False
self.auto_hold_engaged = False
self.auto_hold_drive_time = 0.0
self.one_pedal_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
@@ -103,7 +68,6 @@ 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:
@@ -136,42 +100,28 @@ class CarState(CarStateBase):
sdgm_non_volt = self.CP.carFingerprint in SDGM_CAR and self.CP.carFingerprint not in kaofui_state_cars
prev_cruise_buttons = self.cruise_buttons
prev_hard_cruise_buttons = self.hard_cruise_buttons
prev_distance_button = self.distance_button
if not sdgm_non_volt:
steering_button_msg = pt_cp.vl["ASCMSteeringButton"]
self.cruise_buttons = steering_button_msg["ACCButtons"]
self.hard_cruise_buttons = get_hard_cruise_buttons(steering_button_msg)
self.distance_button = steering_button_msg["DistanceButton"]
self.buttons_counter = steering_button_msg["RollingCounter"]
self.steering_button_checksum = steering_button_msg["SteeringButtonChecksum"]
self.cruise_buttons = pt_cp.vl["ASCMSteeringButton"]["ACCButtons"]
self.distance_button = pt_cp.vl["ASCMSteeringButton"]["DistanceButton"]
self.buttons_counter = pt_cp.vl["ASCMSteeringButton"]["RollingCounter"]
self.steering_button_checksum = pt_cp.vl["ASCMSteeringButton"]["SteeringButtonChecksum"]
self.steering_button_ts_nanos = pt_cp.ts_nanos["ASCMSteeringButton"]["ACCButtons"]
acc_always_one = steering_button_msg["ACCAlwaysOne"]
acc_hidden_bit = steering_button_msg.get("ACCHiddenBit", 0)
acc_always_one = pt_cp.vl["ASCMSteeringButton"]["ACCAlwaysOne"]
acc_hidden_bit = pt_cp.vl["ASCMSteeringButton"].get("ACCHiddenBit", 0)
self.steering_button_prefix = (int(acc_always_one) & 1) | ((int(acc_hidden_bit) & 1) << 6)
else:
steering_button_msg = cam_cp.vl["ASCMSteeringButton"]
self.cruise_buttons = steering_button_msg["ACCButtons"]
self.hard_cruise_buttons = get_hard_cruise_buttons(steering_button_msg)
self.distance_button = steering_button_msg["DistanceButton"]
self.buttons_counter = steering_button_msg["RollingCounter"]
self.cruise_buttons = cam_cp.vl["ASCMSteeringButton"]["ACCButtons"]
self.distance_button = cam_cp.vl["ASCMSteeringButton"]["DistanceButton"]
self.buttons_counter = cam_cp.vl["ASCMSteeringButton"]["RollingCounter"]
self.steering_button_ts_nanos = cam_cp.ts_nanos["ASCMSteeringButton"]["ACCButtons"]
# A GM hard press keeps the normal cruise button signal active too. Suppress
# the normal button until the wheel reports a different normal state.
if self.hard_cruise_buttons != CruiseButtons.INIT and self.cruise_buttons in NORMAL_CRUISE_BUTTONS:
self.force_reset_cruise_buttons = True
if self.force_reset_cruise_buttons and self.cruise_buttons in NORMAL_CRUISE_BUTTONS:
self.cruise_buttons = CruiseButtons.UNPRESS
elif self.force_reset_cruise_buttons and self.cruise_buttons not in NORMAL_CRUISE_BUTTONS:
self.force_reset_cruise_buttons = False
self.pscm_status = copy.copy(pt_cp.vl["PSCMStatus"])
self.moving_backward = (pt_cp.vl["EBCMWheelSpdRear"]["RLWheelDir"] == 2) or (pt_cp.vl["EBCMWheelSpdRear"]["RRWheelDir"] == 2)
# Variables used for avoiding LKAS faults
self.loopback_lka_steering_cmd_updated = len(loopback_cp.vl_all["ASCMLKASteeringCmd"]["RollingCounter"]) > 0
if self.loopback_lka_steering_cmd_updated:
self.loopback_lka_steering_cmd_counter = loopback_cp.vl["ASCMLKASteeringCmd"]["RollingCounter"]
self.loopback_lka_steering_cmd_ts_nanos = loopback_cp.ts_nanos["ASCMLKASteeringCmd"]["RollingCounter"]
if self.CP.networkLocation == NetworkLocation.fwdCamera and not self.CP.flags & GMFlags.NO_CAMERA.value:
self.pt_lka_steering_cmd_counter = pt_cp.vl["ASCMLKASteeringCmd"]["RollingCounter"]
@@ -210,10 +160,7 @@ class CarState(CarStateBase):
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 not in (SDGM_CAR | ASCM_INT | {CAR.CHEVROLET_BLAZER})
):
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
else:
# Some Volt 2016-17 have loose brake pedal push rod retainers which causes the ECM to believe
@@ -223,19 +170,9 @@ 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)
self.auto_hold_drive_time, self.one_pedal_drive_time = update_auto_hold_drive_timers(
in_drive_for_hold, ret.vEgo > 0.1, self.auto_hold_drive_time, self.one_pedal_drive_time
)
if not in_drive_for_hold:
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
@@ -244,10 +181,6 @@ 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.
@@ -304,20 +237,11 @@ 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
ret.stockFcw = False
if self.CP.networkLocation == NetworkLocation.fwdCamera and not self.CP.flags & GMFlags.NO_CAMERA.value:
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"])
ret.stockFcw = self.stock_fcw_alert != 0
if self.CP.carFingerprint not in CC_ONLY_CAR:
ret.cruiseState.speed = cam_cp.vl["ASCMActiveCruiseControlStatus"]["ACCSpeedSetpoint"] * CV.KPH_TO_MS
if self.CP.carFingerprint not in SDGM_CAR:
if self.CP.carFingerprint not in (SDGM_CAR | ASCM_INT):
ret.stockAeb = cam_cp.vl["AEBCmd"]["AEBCmdActive"] != 0
else:
ret.stockAeb = False
@@ -345,10 +269,6 @@ 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
@@ -363,34 +283,46 @@ 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_button = (
bolt_cancel_personality = (
remap_cancel_to_distance and
self.CP.carFingerprint in BOLT_CANCEL_BUTTON_CARS and
self.CP.carFingerprint in BOLT_GEN1_CANCEL_PERSONALITY_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_button:
if malibu_cancel_passthrough or bolt_cancel_personality:
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_lkas_conflict and (
suppress_bolt_cancel_lkas = bolt_cancel_personality and (
self.cruise_buttons == CruiseButtons.CANCEL or
prev_cruise_buttons == CruiseButtons.CANCEL
)
@@ -400,28 +332,23 @@ class CarState(CarStateBase):
lkas_events = [] if (suppress_malibu_side_buttons or suppress_bolt_cancel_lkas) else create_button_events(
self.lkas_enabled, self.lkas_previously_enabled, {1: ButtonType.lkas}
)
hard_cruise_events = create_button_events(
self.hard_cruise_buttons, prev_hard_cruise_buttons, HARD_BUTTONS_DICT, unpressed_btn=CruiseButtons.INIT
)
# Don't add events if transitioning from INIT, unless it's to an actual button.
if (self.cruise_buttons != CruiseButtons.UNPRESS or prev_cruise_buttons != CruiseButtons.INIT or
self.hard_cruise_buttons != CruiseButtons.INIT or prev_hard_cruise_buttons != CruiseButtons.INIT):
if self.cruise_buttons != CruiseButtons.UNPRESS or prev_cruise_buttons != CruiseButtons.INIT:
ret.buttonEvents = [
*cruise_events,
*cancel_gap_events,
*distance_events,
*lkas_events,
*hard_cruise_events,
]
if ret.vEgo < self.CP.minSteerSpeed:
ret.lowSpeedAlert = True
fp_ret = custom.StarPilotCarState.new_message()
fp_ret.accelHardCruise = self.hard_cruise_buttons == CruiseButtons.RES_ACCEL or prev_hard_cruise_buttons == CruiseButtons.RES_ACCEL
fp_ret.decelHardCruise = self.hard_cruise_buttons == CruiseButtons.DECEL_SET or prev_hard_cruise_buttons == CruiseButtons.DECEL_SET
if bolt_cancel_button and self.cruise_buttons == CruiseButtons.CANCEL:
fp_ret.cancelPressed = True
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
fp_ret.sportGear = pt_cp.vl["SportMode"]["SportMode"] == 1
return ret, fp_ret
@@ -471,7 +398,7 @@ class CarState(CarStateBase):
("ASCMSteeringButton", 33),
]
if CP.enableBsm:
pt_messages.append(("BCMBlindSpotMonitor", 0))
pt_messages.append(("BCMBlindSpotMonitor", 10))
if CP.flags & GMFlags.NO_ACCELERATOR_POS_MSG.value:
if ("ECMAcceleratorPos", 80) in pt_messages:
@@ -513,14 +440,8 @@ class CarState(CarStateBase):
("ASCMSteeringButton", 33),
]
if CP.enableBsm:
cam_messages.append(("BCMBlindSpotMonitor", 0))
elif CP.carFingerprint in ASCM_INT:
# Volt/ASCM-int variants don't reliably have AEBCmd present at startup,
# but when it appears we still want to surface OEM AEB state.
cam_messages += [
("AEBCmd", 0),
]
elif CP.carFingerprint not in SDGM_CAR:
cam_messages.append(("BCMBlindSpotMonitor", 10))
elif CP.carFingerprint not in (SDGM_CAR | ASCM_INT):
cam_messages += [
("AEBCmd", 10),
]
@@ -7,7 +7,6 @@ from opendbc.car.gm.values import CAR
CAMERA_DIAGNOSTIC_ADDRESS = 0x24b
CAMERA_DIAGNOSTIC_RX_ADDRESS = 0x64b
SASCM_ADDRESS = 0x2FF
FINGERPRINTS = {
@@ -211,16 +210,12 @@ FINGERPRINTS.update({
CAR.GMC_ACADIA_ASCM: FINGERPRINTS[CAR.GMC_ACADIA],
CAR.CHEVROLET_MALIBU_ASCM: FINGERPRINTS[CAR.CHEVROLET_MALIBU],
CAR.CADILLAC_ESCALADE_ASCM: FINGERPRINTS[CAR.CADILLAC_ESCALADE],
CAR.CADILLAC_ESCALADE_ESV_2019_ASCM: [{**fp, SASCM_ADDRESS: 8} for fp in FINGERPRINTS[CAR.CADILLAC_ESCALADE_ESV_2019]],
CAR.CHEVROLET_SUBURBAN: FINGERPRINTS[CAR.CHEVROLET_SUBURBAN_CC],
CAR.GMC_YUKON_CC: FINGERPRINTS[CAR.GMC_YUKON],
CAR.CADILLAC_XT6: FINGERPRINTS[CAR.CHEVROLET_TRAVERSE],
CAR.CADILLAC_XT5: FINGERPRINTS[CAR.CHEVROLET_TRAVERSE],
CAR.CHEVROLET_BLAZER: FINGERPRINTS[CAR.CHEVROLET_TRAVERSE],
CAR.CHEVROLET_MALIBU_SDGM: FINGERPRINTS[CAR.CHEVROLET_MALIBU_CC],
CAR.BUICK_BABYENCLAVE: FINGERPRINTS[CAR.CHEVROLET_TRAVERSE],
CAR.CHEVROLET_SILVERADO_CC: FINGERPRINTS[CAR.CHEVROLET_SILVERADO],
CAR.BUICK_LACROSSE_ASCM: FINGERPRINTS[CAR.BUICK_LACROSSE],
})
FW_VERSIONS: dict[str, dict[tuple, list[bytes]]] = {
+8 -14
View File
@@ -179,28 +179,22 @@ def create_ecm_cruise_control_command(packer, bus, enabled, target_speed_kph):
return CanData(0x3D1, bytes(dat), bus)
def get_friction_brake_mode(apply_brake, enabled, near_stop, at_full_stop, CP, allow_near_stop_mode=False):
def create_friction_brake_command(packer, bus, apply_brake, idx, enabled, near_stop, at_full_stop, CP):
mode = 0x1
# TODO: Understand this better. Volts and ICE Camera ACC cars are 0x1 when enabled with no brake
if enabled and CP.carFingerprint in (CAR.CHEVROLET_BOLT_ACC_2022_2023, CAR.CHEVROLET_BOLT_ACC_2022_2023_PEDAL):
if enabled and CP.carFingerprint in (CAR.CHEVROLET_BOLT_ACC_2022_2023,):
mode = 0x9
if apply_brake > 0:
mode = 0xa
if at_full_stop:
mode = 0xd
elif allow_near_stop_mode and near_stop:
# Stock Volt auto hold can run with cruise main on but ACC inactive, so
# there is no stock STANDSTILL state to promote 0xa -> 0xd. Restore the
# older near-stop hold mode only for that path.
mode = 0xb
return mode
def create_friction_brake_command(packer, bus, apply_brake, idx, enabled, near_stop, at_full_stop, CP, allow_near_stop_mode=False):
mode = get_friction_brake_mode(apply_brake, enabled, near_stop, at_full_stop, CP, allow_near_stop_mode)
# TODO: this is to have GM bringing the car to complete stop,
# but currently it conflicts with OP controls, so turned off. Not set by all cars
#elif near_stop:
# mode = 0xb
brake = (0x1000 - apply_brake) & 0xfff
checksum = (0x10000 - (mode << 12) - brake - idx) & 0xffff
@@ -215,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_alert):
def create_acc_dashboard_command(packer, bus, enabled, target_speed_kph, hud_control, fcw):
target_speed = min(target_speed_kph, 255)
values = {
@@ -226,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": int(fcw_alert) & 0x3,
"FCWAlert": 0x3 if fcw else 0
}
return packer.make_can_msg("ASCMActiveCruiseControlStatus", bus, values)
+11 -98
View File
@@ -58,18 +58,6 @@ NON_LINEAR_TORQUE_PARAMS = {
"left": [3.8, 0.81, 0.24, 0.0465122],
"right": [3.8, 0.81, 0.24, 0.0465122],
},
CAR.CHEVROLET_SILVERADO_CC: {
"left": [3.8, 0.81, 0.24, 0.0465122],
"right": [3.8, 0.81, 0.24, 0.0465122],
},
CAR.CADILLAC_XT4: {
"left": [2.4, 0.95, 0.28, 0.0],
"right": [2.4, 0.95, 0.28, 0.0],
},
CAR.CHEVROLET_VOLT: {
"left": [1.525, 1.05, 0.155, 0.0],
"right": [1.525, 0.95, 0.150, 0.0],
},
}
PEDAL_MSG = 0x201
@@ -97,14 +85,6 @@ VOLT_LONG_TEST_TUNE_CARS = {
CAR.CHEVROLET_VOLT_CC,
}
VOLT_BSM_CARS = {
CAR.CHEVROLET_VOLT,
CAR.CHEVROLET_VOLT_2019,
CAR.CHEVROLET_VOLT_ASCM,
CAR.CHEVROLET_VOLT_CAMERA,
CAR.CHEVROLET_VOLT_CC,
}
BOLT_PEDAL_LONG_CARS = {
CAR.CHEVROLET_BOLT_CC_2017,
CAR.CHEVROLET_BOLT_CC_2018_2021,
@@ -113,16 +93,12 @@ BOLT_PEDAL_LONG_CARS = {
CAR.CHEVROLET_MALIBU_HYBRID_CC,
}
# Cancel-button remap support uses the same safety path on all pedal-long Bolts,
# but only gen1 needs extra LKAS suppression in CarState.
# Cancel-to-personality mapping target: gen1 Bolt pedal-long paths only.
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 | {
CAR.CHEVROLET_BOLT_ACC_2022_2023_PEDAL,
CAR.CHEVROLET_BOLT_CC_2022_2023,
}
CANCEL_REMAP_DISTANCE_CARS = BOLT_GEN1_CANCEL_PERSONALITY_CARS
class CarInterface(CarInterfaceBase):
@@ -133,11 +109,7 @@ class CarInterface(CarInterfaceBase):
@staticmethod
def get_pid_accel_limits(CP, current_speed, cruise_speed):
if CP.enableGasInterceptorDEPRECATED and bool(CP.flags & GMFlags.PEDAL_LONG.value):
if CP.carFingerprint == CAR.CHEVROLET_BOLT_ACC_2022_2023_PEDAL:
accel_min = CarControllerParams.ACCEL_MIN
accel_max = np.interp(current_speed, [0.0, 1.5, 4.0, 8.0, 15.0],
[0.54, 0.74, 1.03, 1.46, CarControllerParams.ACCEL_MAX])
elif CP.carFingerprint in BOLT_PEDAL_LONG_CARS:
if CP.carFingerprint in BOLT_PEDAL_LONG_CARS:
accel_min = np.interp(current_speed, [0.0, 1.5, 4.0, 8.0, 15.0, 30.0],
[-0.93, -1.28, -1.98, -2.58, -2.86, -2.95])
accel_max = np.interp(current_speed, [0.0, 1.5, 4.0, 8.0, 15.0],
@@ -217,20 +189,11 @@ 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
try:
volt_one_pedal_mode = params.get_bool("VoltOnePedalMode")
except UnknownKeyName:
volt_one_pedal_mode = False
ret.brand = "gm"
ret.safetyConfigs = [get_safety_config(structs.CarParams.SafetyModel.gm)]
ret.autoResumeSng = False
# Some Volt installs don't expose the BSM frame during startup fingerprinting.
ret.enableBsm = 0x142 in fingerprint[CanBus.POWERTRAIN] or candidate in VOLT_BSM_CARS
ret.enableBsm = 0x142 in fingerprint[CanBus.POWERTRAIN]
has_sascm = 0x2FF in fingerprint[CanBus.POWERTRAIN]
if has_sascm:
ret.flags |= GMFlags.SASCM.value
@@ -422,8 +385,8 @@ class CarInterface(CarInterfaceBase):
ret.steerActuatorDelay = 0.2
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
elif candidate in (CAR.BUICK_LACROSSE, CAR.BUICK_LACROSSE_ASCM):
CarInterfaceBase.configure_torque_tune(CAR.BUICK_LACROSSE, ret.lateralTuning)
elif candidate == CAR.BUICK_LACROSSE:
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
elif candidate == CAR.CADILLAC_ESCALADE:
ret.minEnableSpeed = -1. # engage speed is decided by pcm
@@ -432,7 +395,7 @@ class CarInterface(CarInterfaceBase):
elif candidate == CAR.CADILLAC_ESCALADE_ASCM:
CarInterfaceBase.configure_torque_tune(CAR.CADILLAC_ESCALADE, ret.lateralTuning)
elif candidate in (CAR.CADILLAC_ESCALADE_ESV, CAR.CADILLAC_ESCALADE_ESV_2019, CAR.CADILLAC_ESCALADE_ESV_2019_ASCM):
elif candidate in (CAR.CADILLAC_ESCALADE_ESV, CAR.CADILLAC_ESCALADE_ESV_2019):
ret.minEnableSpeed = -1. # engage speed is decided by pcm
if candidate == CAR.CADILLAC_ESCALADE_ESV:
@@ -441,8 +404,7 @@ class CarInterface(CarInterfaceBase):
ret.lateralTuning.pid.kf = 0.000045
else:
ret.steerActuatorDelay = 0.2
torque_candidate = CAR.CADILLAC_ESCALADE_ESV_2019 if candidate == CAR.CADILLAC_ESCALADE_ESV_2019_ASCM else candidate
CarInterfaceBase.configure_torque_tune(torque_candidate, ret.lateralTuning)
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
elif candidate in (
CAR.CHEVROLET_BOLT_ACC_2022_2023,
@@ -482,7 +444,7 @@ class CarInterface(CarInterfaceBase):
# ACC Bolts use pedal for full longitudinal control, not just SNG.
ret.flags |= GMFlags.PEDAL_LONG.value
elif candidate in (CAR.CHEVROLET_SILVERADO, CAR.CHEVROLET_SILVERADO_CC):
elif candidate == CAR.CHEVROLET_SILVERADO:
# On the Bolt, the ECM and camera independently check that you are either above 5 kph or at a stop
# with foot on brake to allow engagement, but this platform only has that check in the camera.
# TODO: check if this is split by EV/ICE with more platforms in the future
@@ -519,7 +481,7 @@ class CarInterface(CarInterfaceBase):
ret.minSteerSpeed = 7 * CV.MPH_TO_MS
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
elif candidate in (CAR.CADILLAC_XT5, CAR.CADILLAC_XT5_CC):
elif candidate == CAR.CADILLAC_XT5_CC:
ret.steerActuatorDelay = 0.2
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
@@ -528,19 +490,7 @@ class CarInterface(CarInterfaceBase):
if not ret.openpilotLongitudinalControl:
ret.minEnableSpeed = -1.
if candidate == CAR.CHEVROLET_BLAZER:
# The Blazer builds brake torque noticeably later than the rest of the GM set.
# A slightly larger planner delay estimate starts the request earlier and keeps
# stopped-lead approaches from turning into a late, harsh max-brake catch-up.
ret.longitudinalActuatorDelay = 0.7
ret.longitudinalTuning.kpBP = [0.0, 4.0, 12.0, 35.0]
ret.longitudinalTuning.kpV = [0.09, 0.075, 0.055, 0.040]
ret.longitudinalTuning.kiBP = [0.0, 4.0, 12.0, 35.0]
ret.longitudinalTuning.kiV = [0.03, 0.04, 0.055, 0.07]
ret.minEnableSpeed = 5 * CV.KPH_TO_MS
ret.stoppingDecelRate = 1.0
ret.vEgoStopping = 0.35
ret.vEgoStarting = 0.35
ret.stopAccel = -0.30
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
elif candidate == CAR.BUICK_BABYENCLAVE:
@@ -634,12 +584,6 @@ class CarInterface(CarInterfaceBase):
ret.startAccel = 1.15
ret.vEgoStarting = max(ret.vEgoStarting, 0.35)
if ret.openpilotLongitudinalControl and candidate in (CAR.CHEVROLET_SILVERADO, CAR.CHEVROLET_SILVERADO_CC) and not ret.enableGasInterceptorDEPRECATED:
ret.longitudinalTuning.kpBP = [0.0, 5.0, 15.0, 35.0]
ret.longitudinalTuning.kpV = [0.02, 0.03, 0.028, 0.022]
ret.longitudinalTuning.kiBP = [0.0, 5.0, 15.0, 35.0]
ret.longitudinalTuning.kiV = [0.28, 0.26, 0.20, 0.16]
elif candidate in CC_ONLY_CAR and not ret.enableGasInterceptorDEPRECATED:
ret.flags |= GMFlags.CC_LONG.value
ret.alphaLongitudinalAvailable = False
@@ -670,7 +614,7 @@ class CarInterface(CarInterfaceBase):
# Exception for flashed cars, or cars whose camera was removed.
missing_camera_msg = CAM_MSG not in fingerprint.get(CanBus.CAMERA, {})
if (ret.networkLocation == NetworkLocation.fwdCamera or candidate in CC_ONLY_CAR) and missing_camera_msg and candidate not in (ASCM_INT | SDGM_CAR):
if (ret.networkLocation == NetworkLocation.fwdCamera or candidate in CC_ONLY_CAR) and missing_camera_msg and candidate not in SDGM_CAR:
ret.flags |= GMFlags.NO_CAMERA.value
ret.safetyConfigs[0].safetyParam |= GMSafetyFlags.FLAG_GM_NO_CAMERA.value
@@ -688,37 +632,6 @@ class CarInterface(CarInterfaceBase):
if remote_start_boots_comma:
ret.safetyConfigs[0].safetyParam |= GMSafetyFlags.FLAG_GM_REMOTE_START_BOOTS_COMMA.value
volt_stock_friction_brake_safety = (
(gm_auto_hold or volt_one_pedal_mode) and
candidate in {
CAR.CHEVROLET_VOLT,
CAR.CHEVROLET_VOLT_2019,
CAR.CHEVROLET_VOLT_ASCM,
CAR.CHEVROLET_VOLT_CAMERA,
}
)
if volt_stock_friction_brake_safety:
# Reuse the paddle-scheduler safety bit as a Volt stock friction-brake
# marker on non-pedal paths. Both auto hold and one-pedal can run while
# OP longitudinal is configured but not currently active, so the bit must
# be present regardless of the current long-control mode.
ret.safetyConfigs[0].safetyParam |= GMSafetyFlags.FLAG_GM_PANDA_PADDLE_SCHED.value
volt_stock_one_pedal_safety = (
volt_one_pedal_mode and
candidate in {
CAR.CHEVROLET_VOLT,
CAR.CHEVROLET_VOLT_2019,
CAR.CHEVROLET_VOLT_ASCM,
CAR.CHEVROLET_VOLT_CAMERA,
}
)
if volt_stock_one_pedal_safety:
# Reuse the 3D1 scheduler bit as a Volt one-pedal marker on non-pedal
# ACC paths. The bit is ignored by the actual 3D1 scheduler unless the
# car is on a pedal-long CC-only path, so this stays isolated from Bolt.
ret.safetyConfigs[0].safetyParam |= GMSafetyFlags.FLAG_GM_PANDA_3D1_SCHED.value
use_panda_3d1_sched = (
ret.openpilotLongitudinalControl and
ret.enableGasInterceptorDEPRECATED and
@@ -1,923 +0,0 @@
import sys
import types
from types import SimpleNamespace
import numpy as np
import pytest
from opendbc.car import structs
fake_interfaces = types.ModuleType("opendbc.car.interfaces")
class _FakeCarControllerBase:
def __init__(self, dbc_names=None, CP=None):
self.CP = CP
fake_interfaces.CarControllerBase = _FakeCarControllerBase
sys.modules.setdefault("opendbc.car.interfaces", fake_interfaces)
fake_params = types.ModuleType("openpilot.common.params")
class _FakeParams:
pass
class _FakeUnknownKeyName(Exception):
pass
fake_params.Params = _FakeParams
fake_params.UnknownKeyName = _FakeUnknownKeyName
sys.modules.setdefault("openpilot.common.params", fake_params)
fake_testing_grounds = types.ModuleType("openpilot.starpilot.common.testing_grounds")
fake_testing_grounds.testing_ground = SimpleNamespace(use_1=False)
sys.modules.setdefault("openpilot.starpilot.common.testing_grounds", fake_testing_grounds)
from opendbc.car.gm.carcontroller import (
AUTO_HOLD_DRIVE_GEARS,
CarController,
estimate_auto_hold_brake,
get_adas_keepalive_step,
get_auto_hold_stop_threshold,
get_bolt_acc_pedal_friction_brake,
get_bolt_acc_pedal_friction_command_state,
get_bolt_acc_pedal_effective_brake_switch,
get_bolt_acc_pedal_planner_brake_switch,
get_bolt_pedal_long_accel_limit,
get_interceptor_sng_gas_cmd,
get_lka_steering_cmd_counter,
get_volt_one_pedal_target_decel,
get_testing_ground_1_brake_switch_bias,
get_acc_dashboard_status_active,
get_stock_cc_active_for_cancel,
shape_bolt_acc_pedal_low_speed_friction,
shape_truck_positive_accel,
should_use_fixed_stopping_brake,
should_activate_auto_hold,
should_activate_volt_one_pedal,
should_send_adas_status,
should_send_stock_long_cancel,
should_spoof_dash_speed,
should_spoof_ecm_cruise_status,
supports_bolt_acc_pedal_friction_experiment,
supports_volt_auto_hold,
supports_volt_one_pedal,
use_interceptor_sng_launch,
)
from opendbc.car.gm.gmcan import get_friction_brake_mode
from opendbc.car.gm.values import AccState, CAR, CarControllerParams, GMFlags
from opendbc.car.structs import CarParams
from opendbc.car.common.conversions import Conversions as CV
def _cs(enabled, pcm_acc_status):
return SimpleNamespace(
out=SimpleNamespace(cruiseState=SimpleNamespace(enabled=enabled), accFaulted=False),
pcm_acc_status=pcm_acc_status,
)
def _sng_cs(v_ego, standstill, cruise_standstill):
return SimpleNamespace(
out=SimpleNamespace(
vEgo=v_ego,
standstill=standstill,
cruiseState=SimpleNamespace(standstill=cruise_standstill),
),
)
def _controller(car_fingerprint=CAR.CHEVROLET_BOLT_CC_2018_2021):
controller = CarController.__new__(CarController)
controller.CP = SimpleNamespace(carFingerprint=car_fingerprint)
controller.planner_regen_hold = False
controller.regen_paddle_pressed = False
controller.regen_paddle_timer = 0
controller.regen_press_counter = 0
controller.regen_release_counter = 0
controller.regen_min_on_frames = 0
controller.regen_min_off_frames = 0
controller.pedal_active_last = False
controller.pedal_steady = 0.0
controller.aego = 0.0
controller.maneuver_paddle_mode = "auto"
controller.bolt_acc_pedal_friction_low_speed_active = False
return controller
def test_gen1_bolt_pedal_cancel_uses_pcm_acc_status():
CP = SimpleNamespace(carFingerprint=CAR.CHEVROLET_BOLT_CC_2018_2021)
assert get_stock_cc_active_for_cancel(CP, _cs(False, AccState.ACTIVE))
def test_gen2_bolt_acc_pedal_cancel_uses_enabled_only():
CP = SimpleNamespace(carFingerprint=CAR.CHEVROLET_BOLT_ACC_2022_2023_PEDAL)
assert not get_stock_cc_active_for_cancel(CP, _cs(False, AccState.ACTIVE))
def test_bolt_acc_pedal_friction_experiment_is_single_fingerprint_only():
assert supports_bolt_acc_pedal_friction_experiment(SimpleNamespace(
carFingerprint=CAR.CHEVROLET_BOLT_ACC_2022_2023_PEDAL,
openpilotLongitudinalControl=True,
enableGasInterceptorDEPRECATED=True,
flags=GMFlags.PEDAL_LONG.value,
))
assert not supports_bolt_acc_pedal_friction_experiment(SimpleNamespace(
carFingerprint=CAR.CHEVROLET_MALIBU_HYBRID_CC,
openpilotLongitudinalControl=True,
enableGasInterceptorDEPRECATED=True,
flags=GMFlags.PEDAL_LONG.value,
))
assert not supports_bolt_acc_pedal_friction_experiment(SimpleNamespace(
carFingerprint=CAR.CHEVROLET_BOLT_ACC_2022_2023_PEDAL,
openpilotLongitudinalControl=False,
enableGasInterceptorDEPRECATED=True,
flags=GMFlags.PEDAL_LONG.value,
))
def test_bolt_acc_pedal_friction_blend_preserves_zero_before_crossover():
params = SimpleNamespace(ACCEL_MIN=-4.0, MAX_BRAKE=400)
assert get_bolt_acc_pedal_friction_brake(0, -2.8, 20.0, params) == 0
def test_bolt_acc_pedal_friction_blend_uses_full_brake_range_after_regen():
params = SimpleNamespace(ACCEL_MIN=-4.0, MAX_BRAKE=400)
# Legacy mapping tops out early once regen has already consumed part of the
# decel request. The experiment remaps that reduced span back to full scale.
assert get_bolt_acc_pedal_friction_brake(286, -2.86, 20.0, params) == 400
def test_bolt_acc_pedal_friction_blend_biases_small_commands_upward_at_speed():
params = SimpleNamespace(ACCEL_MIN=-4.0, MAX_BRAKE=400)
low_speed = get_bolt_acc_pedal_friction_brake(31, -2.86, 0.0, params)
high_speed = get_bolt_acc_pedal_friction_brake(31, -2.86, 20.0, params)
assert low_speed > 31
assert high_speed > low_speed
def test_bolt_acc_pedal_friction_blend_applies_a_minimum_pre_stop_command_at_speed():
params = SimpleNamespace(ACCEL_MIN=-4.0, MAX_BRAKE=400)
assert get_bolt_acc_pedal_friction_brake(2, -2.86, 17.0, params) >= 18
def test_bolt_acc_pedal_friction_blend_boosts_midrange_commands_before_stopping_phase():
params = SimpleNamespace(ACCEL_MIN=-4.0, MAX_BRAKE=400)
assert get_bolt_acc_pedal_friction_brake(40, -2.86, 15.0, params) >= 80
def test_bolt_acc_pedal_low_speed_friction_ignores_tiny_inactive_brake_requests():
apply_brake, active = shape_bolt_acc_pedal_low_speed_friction(9, 5.0, False, False)
assert apply_brake == 0
assert not active
def test_bolt_acc_pedal_low_speed_friction_uses_hysteresis_once_active():
apply_brake, active = shape_bolt_acc_pedal_low_speed_friction(24, 3.0, False, False)
assert apply_brake == 24
assert active
apply_brake, active = shape_bolt_acc_pedal_low_speed_friction(5, 3.0, False, active)
assert apply_brake == 0
assert not active
def test_bolt_acc_pedal_low_speed_friction_fades_out_at_standstill():
apply_brake, active = shape_bolt_acc_pedal_low_speed_friction(80, 0.2, True, True)
assert apply_brake == 0
assert not active
def test_bolt_acc_pedal_low_speed_friction_preserves_rolling_stop_authority():
apply_brake, active = shape_bolt_acc_pedal_low_speed_friction(80, 1.2, True, True)
assert 0 < apply_brake < 80
assert active
def test_bolt_acc_pedal_low_speed_friction_drops_out_before_two_clamp():
apply_brake, active = shape_bolt_acc_pedal_low_speed_friction(80, 0.9, True, True)
assert apply_brake == 0
assert not active
def test_bolt_pedal_long_accel_limit_matches_planner_regen_envelope():
assert get_bolt_pedal_long_accel_limit(6.66) == pytest.approx(-2.379, abs=1e-3)
assert get_bolt_pedal_long_accel_limit(3.0) == pytest.approx(-1.70, abs=1e-3)
def test_bolt_acc_pedal_planner_brake_switch_is_lower_than_stock_switch():
params = SimpleNamespace(ZERO_GAS=6150, BRAKE_SWITCH_LOOKUP_BP=[0.5, 10.0], BRAKE_SWITCH_LOOKUP_V=[6150, 5500])
v_ego = 6.66
stock_switch = int(round(np.interp(v_ego, params.BRAKE_SWITCH_LOOKUP_BP, params.BRAKE_SWITCH_LOOKUP_V)))
planner_switch = get_bolt_acc_pedal_planner_brake_switch(
v_ego, params, tire_radius=0.336, mass=1832.0, coeff_drag=0.30, frontal_area=2.35, air_density=1.225,
)
assert planner_switch < stock_switch
def test_bolt_acc_pedal_effective_brake_switch_never_suppresses_stock_friction():
params = SimpleNamespace(ZERO_GAS=6150, BRAKE_SWITCH_LOOKUP_BP=[0.5, 10.0], BRAKE_SWITCH_LOOKUP_V=[6150, 5500])
v_ego = 5.434
mass = 1805.0
tire_radius = 0.075 * 2.63779 + 0.1453
frontal_area = 1.05 * 2.63779 + 0.0679
coeff_drag = 0.30
air_density = 1.225
accel_cmd = -1.399
aero_drag_force = 0.5 * coeff_drag * frontal_area * air_density * v_ego ** 2
torque = tire_radius * ((mass * accel_cmd) + aero_drag_force)
scaled_torque = torque + params.ZERO_GAS
stock_switch = int(round(np.interp(v_ego, params.BRAKE_SWITCH_LOOKUP_BP, params.BRAKE_SWITCH_LOOKUP_V)))
planner_switch = get_bolt_acc_pedal_planner_brake_switch(
v_ego, params, tire_radius=tire_radius, mass=mass,
coeff_drag=coeff_drag, frontal_area=frontal_area, air_density=air_density,
)
effective_switch = get_bolt_acc_pedal_effective_brake_switch(stock_switch, planner_switch)
stock_brake_accel = min((scaled_torque - stock_switch) / (tire_radius * mass), 0)
effective_brake_accel = min((scaled_torque - effective_switch) / (tire_radius * mass), 0)
assert planner_switch < stock_switch
assert effective_switch == stock_switch
assert stock_brake_accel < 0
assert effective_brake_accel == stock_brake_accel
def test_bolt_acc_pedal_friction_command_state_requires_cruise_main_for_positive_brake():
command_brake, release_frames, should_send = get_bolt_acc_pedal_friction_command_state(120, False, 0)
assert command_brake == 0
assert release_frames == 0
assert not should_send
def test_bolt_acc_pedal_friction_command_state_sends_zero_unwind_after_main_off():
command_brake, release_frames, should_send = get_bolt_acc_pedal_friction_command_state(120, True, 0)
assert command_brake == 120
assert release_frames > 0
assert should_send
command_brake, release_frames, should_send = get_bolt_acc_pedal_friction_command_state(0, False, release_frames)
assert command_brake == 0
assert release_frames >= 0
assert should_send
def test_fixed_stopping_brake_is_disabled_for_bolt_acc_pedal_experiment():
CP = SimpleNamespace(
carFingerprint=CAR.CHEVROLET_BOLT_ACC_2022_2023_PEDAL,
openpilotLongitudinalControl=True,
enableGasInterceptorDEPRECATED=True,
flags=GMFlags.PEDAL_LONG.value,
)
assert not should_use_fixed_stopping_brake(CP, True, True, False)
def test_fixed_stopping_brake_stays_enabled_for_normal_acc_path():
CP = SimpleNamespace(
carFingerprint=CAR.CHEVROLET_BOLT_ACC_2022_2023,
openpilotLongitudinalControl=True,
enableGasInterceptorDEPRECATED=False,
flags=0,
)
assert should_use_fixed_stopping_brake(CP, True, True, False)
assert not should_use_fixed_stopping_brake(CP, False, True, False)
assert not should_use_fixed_stopping_brake(CP, True, False, False)
assert not should_use_fixed_stopping_brake(CP, True, True, True)
def test_stock_cancel_is_suppressed_when_acc_is_faulted():
CP = SimpleNamespace(carFingerprint=CAR.CHEVROLET_VOLT_CAMERA)
cs = _cs(True, AccState.FAULTED)
cs.out.accFaulted = True
assert get_stock_cc_active_for_cancel(CP, cs)
assert not should_send_stock_long_cancel(11, cs)
def test_stock_cancel_requires_delay_and_no_acc_fault():
cs = _cs(True, AccState.ACTIVE)
assert not should_send_stock_long_cancel(10, cs)
assert should_send_stock_long_cancel(11, cs)
def test_gen1_bolt_pedal_ecm_cruise_spoof_is_not_gated_by_dash_speed_toggle():
CP = SimpleNamespace(
carFingerprint=CAR.CHEVROLET_BOLT_CC_2018_2021,
enableGasInterceptorDEPRECATED=True,
flags=GMFlags.PEDAL_LONG.value,
openpilotLongitudinalControl=True,
)
assert not should_spoof_dash_speed(CP, SimpleNamespace(disable_openpilot_long=True, gm_pedal_longitudinal=True))
assert should_spoof_ecm_cruise_status(CP)
def test_gateway_keepalive_uses_gateway_cadence():
cp = SimpleNamespace(networkLocation=CarParams.NetworkLocation.gateway, flags=0)
assert get_adas_keepalive_step(cp, is_kaofui_car=True) == 100
assert get_adas_keepalive_step(cp, is_kaofui_car=False) == 200
def test_removed_camera_keepalive_uses_camera_cadence():
cp = SimpleNamespace(networkLocation=CarParams.NetworkLocation.fwdCamera, flags=GMFlags.NO_CAMERA.value)
assert get_adas_keepalive_step(cp, is_kaofui_car=True) == 100
def test_live_camera_path_does_not_send_pt_keepalive():
cp = SimpleNamespace(networkLocation=CarParams.NetworkLocation.fwdCamera, flags=0)
assert get_adas_keepalive_step(cp, is_kaofui_car=True) is None
def test_ascm_int_cars_do_not_send_radar_status():
common = {
"networkLocation": CarParams.NetworkLocation.fwdCamera,
"radarUnavailable": False,
}
assert not should_send_adas_status(SimpleNamespace(carFingerprint=CAR.BUICK_LACROSSE_ASCM, **common), is_kaofui_car=True)
assert not should_send_adas_status(SimpleNamespace(carFingerprint=CAR.CHEVROLET_VOLT_ASCM, **common), is_kaofui_car=True)
def test_lacrosse_ascm_marks_acc_dashboard_active_for_aol_only():
cc = SimpleNamespace(enabled=False, latActive=True)
assert get_acc_dashboard_status_active(SimpleNamespace(carFingerprint=CAR.BUICK_LACROSSE_ASCM), cc)
assert not get_acc_dashboard_status_active(SimpleNamespace(carFingerprint=CAR.CHEVROLET_VOLT_ASCM), cc)
assert not get_acc_dashboard_status_active(SimpleNamespace(carFingerprint=CAR.CHEVROLET_BOLT_ACC_2022_2023), cc)
def test_acc_dashboard_status_active_for_normal_enabled_cars():
cc = SimpleNamespace(enabled=True, latActive=False)
assert get_acc_dashboard_status_active(SimpleNamespace(carFingerprint=CAR.CHEVROLET_VOLT_ASCM), cc)
def test_volt_auto_hold_requires_toggle_supported_non_cc_only_volt_and_stock_safety():
stock_safety = [SimpleNamespace(safetyParam=0x8000)]
no_safety = [SimpleNamespace(safetyParam=0)]
assert not supports_volt_auto_hold(
SimpleNamespace(
carFingerprint=CAR.CHEVROLET_VOLT_CAMERA,
openpilotLongitudinalControl=True,
networkLocation=CarParams.NetworkLocation.fwdCamera,
safetyConfigs=no_safety,
),
True,
)
assert not supports_volt_auto_hold(
SimpleNamespace(
carFingerprint=CAR.CHEVROLET_VOLT_CC,
openpilotLongitudinalControl=True,
networkLocation=CarParams.NetworkLocation.fwdCamera,
safetyConfigs=no_safety,
),
True,
)
assert supports_volt_auto_hold(
SimpleNamespace(
carFingerprint=CAR.CHEVROLET_VOLT_CAMERA,
openpilotLongitudinalControl=True,
networkLocation=CarParams.NetworkLocation.fwdCamera,
safetyConfigs=stock_safety,
),
True,
)
assert supports_volt_auto_hold(
SimpleNamespace(
carFingerprint=CAR.CHEVROLET_VOLT,
openpilotLongitudinalControl=False,
networkLocation=CarParams.NetworkLocation.gateway,
safetyConfigs=stock_safety,
),
True,
)
assert supports_volt_auto_hold(
SimpleNamespace(
carFingerprint=CAR.CHEVROLET_VOLT_2019,
openpilotLongitudinalControl=False,
networkLocation=CarParams.NetworkLocation.fwdCamera,
safetyConfigs=stock_safety,
),
True,
)
assert not supports_volt_auto_hold(
SimpleNamespace(
carFingerprint=CAR.CHEVROLET_VOLT_2019,
openpilotLongitudinalControl=False,
networkLocation=CarParams.NetworkLocation.fwdCamera,
safetyConfigs=no_safety,
),
True,
)
assert not supports_volt_auto_hold(
SimpleNamespace(
carFingerprint=CAR.CHEVROLET_VOLT_CAMERA,
openpilotLongitudinalControl=True,
networkLocation=CarParams.NetworkLocation.fwdCamera,
safetyConfigs=no_safety,
),
False,
)
def test_auto_hold_brake_estimate_uses_driver_or_op_brake_and_clamps():
assert estimate_auto_hold_brake(0.0, 20.0) == 80
assert estimate_auto_hold_brake(20.0, 40.0) == 110
assert estimate_auto_hold_brake(20.0, 160.0) == 160
assert estimate_auto_hold_brake(100.0, 400.0) == 240
assert estimate_auto_hold_brake(7.0, 0.0, SimpleNamespace(carFingerprint=CAR.CHEVROLET_VOLT_2019)) == 100
def test_volt_one_pedal_requires_toggle_supported_volt_stock_safety_and_ev_transmission():
stock_safety = [SimpleNamespace(safetyParam=0x8000)]
no_safety = [SimpleNamespace(safetyParam=0)]
assert supports_volt_one_pedal(
SimpleNamespace(
carFingerprint=CAR.CHEVROLET_VOLT_CAMERA,
safetyConfigs=stock_safety,
transmissionType=structs.CarParams.TransmissionType.direct,
),
True,
)
assert not supports_volt_one_pedal(
SimpleNamespace(
carFingerprint=CAR.CHEVROLET_VOLT_CAMERA,
safetyConfigs=no_safety,
transmissionType=structs.CarParams.TransmissionType.direct,
),
True,
)
assert not supports_volt_one_pedal(
SimpleNamespace(
carFingerprint=CAR.CHEVROLET_VOLT_CC,
safetyConfigs=stock_safety,
transmissionType=structs.CarParams.TransmissionType.direct,
),
True,
)
assert not supports_volt_one_pedal(
SimpleNamespace(
carFingerprint=CAR.CHEVROLET_VOLT_CAMERA,
safetyConfigs=stock_safety,
transmissionType=structs.CarParams.TransmissionType.automatic,
),
True,
)
assert not supports_volt_one_pedal(
SimpleNamespace(
carFingerprint=CAR.CHEVROLET_VOLT_CAMERA,
safetyConfigs=stock_safety,
transmissionType=structs.CarParams.TransmissionType.direct,
),
False,
)
def test_auto_hold_drive_gears_accept_capnp_dynamic_enum_membership():
msg = structs.CarState.new_message()
msg.gearShifter = structs.CarState.GearShifter.drive
assert msg.gearShifter in AUTO_HOLD_DRIVE_GEARS
def test_auto_hold_activation_allows_direct_entry_from_stopped_brake_press():
assert should_activate_auto_hold(
True,
False,
False,
True,
False,
True,
False,
False,
0.01,
)
def test_auto_hold_activation_stays_latched_after_brake_release():
assert should_activate_auto_hold(
True,
False,
True,
False,
False,
True,
False,
False,
0.0,
)
def test_volt_2019_auto_hold_engaged_uses_near_stop_creep_hysteresis():
CP = SimpleNamespace(carFingerprint=CAR.CHEVROLET_VOLT_2019)
assert get_auto_hold_stop_threshold(CP, True) == CarControllerParams.NEAR_STOP_BRAKE_PHASE
assert should_activate_auto_hold(
True,
True,
True,
False,
False,
False,
False,
False,
0.05,
get_auto_hold_stop_threshold(CP, True),
)
assert not should_activate_auto_hold(
True,
True,
True,
False,
False,
False,
False,
False,
0.05,
)
def test_auto_hold_activation_blocks_when_long_is_active_or_motion_is_above_threshold():
assert not should_activate_auto_hold(
True,
True,
False,
True,
False,
True,
True,
False,
0.0,
)
assert not should_activate_auto_hold(
True,
True,
False,
False,
False,
False,
False,
False,
0.03,
)
def test_auto_hold_activation_allows_standstill_even_if_speed_filter_is_slightly_above_threshold():
assert should_activate_auto_hold(
True,
True,
False,
False,
False,
True,
False,
False,
0.05,
)
def test_auto_hold_activation_releases_immediately_on_gas_press():
assert not should_activate_auto_hold(
True,
True,
True,
False,
True,
True,
False,
False,
0.0,
)
def test_volt_one_pedal_activation_requires_main_l_mode_and_no_driver_input():
assert should_activate_volt_one_pedal(
True,
True,
False,
False,
False,
False,
True,
structs.CarState.GearShifter.low,
3.0,
)
assert not should_activate_volt_one_pedal(
True,
False,
False,
False,
False,
False,
True,
structs.CarState.GearShifter.low,
3.0,
)
assert not should_activate_volt_one_pedal(
True,
True,
True,
False,
False,
False,
True,
structs.CarState.GearShifter.low,
3.0,
)
assert not should_activate_volt_one_pedal(
True,
True,
False,
True,
False,
False,
True,
structs.CarState.GearShifter.low,
3.0,
)
assert not should_activate_volt_one_pedal(
True,
True,
False,
False,
False,
True,
True,
structs.CarState.GearShifter.low,
3.0,
)
assert not should_activate_volt_one_pedal(
True,
True,
False,
False,
False,
False,
False,
structs.CarState.GearShifter.drive,
3.0,
)
def test_volt_one_pedal_target_decel_stays_active_above_low_speed_band():
assert get_volt_one_pedal_target_decel(0.5 * CV.MPH_TO_MS) == -1.0
assert get_volt_one_pedal_target_decel(6.0 * CV.MPH_TO_MS) == -1.1
assert get_volt_one_pedal_target_decel(20.0 * CV.MPH_TO_MS) == -1.1
def test_volt_one_pedal_regression_ignores_noisy_wheel_direction_bits():
assert should_activate_volt_one_pedal(
True,
True,
False,
False,
False,
False,
True,
structs.CarState.GearShifter.low,
3.0,
)
def test_volt_one_pedal_requires_time_in_drive_before_arming():
assert not should_activate_volt_one_pedal(
True,
True,
False,
False,
False,
False,
True,
structs.CarState.GearShifter.low,
2.5,
)
def test_friction_brake_mode_keeps_near_stop_disabled_for_regular_long_braking():
CP = SimpleNamespace(carFingerprint=CAR.CHEVROLET_VOLT_ASCM)
assert get_friction_brake_mode(120, False, True, False, CP) == 0xa
def test_friction_brake_mode_uses_near_stop_hold_mode_for_volt_auto_hold():
CP = SimpleNamespace(carFingerprint=CAR.CHEVROLET_VOLT_ASCM)
assert get_friction_brake_mode(120, False, True, False, CP, allow_near_stop_mode=True) == 0xb
assert get_friction_brake_mode(120, False, True, True, CP, allow_near_stop_mode=True) == 0xd
def test_friction_brake_mode_uses_stock_bolt_unwind_for_pedal_print_when_enabled():
CP = SimpleNamespace(carFingerprint=CAR.CHEVROLET_BOLT_ACC_2022_2023_PEDAL)
assert get_friction_brake_mode(0, False, True, False, CP) == 0x1
assert get_friction_brake_mode(0, True, True, False, CP) == 0x9
def test_friction_brake_mode_keeps_bolt_pedal_braking_mode_unchanged():
CP = SimpleNamespace(carFingerprint=CAR.CHEVROLET_BOLT_ACC_2022_2023_PEDAL)
assert get_friction_brake_mode(120, True, False, False, CP) == 0xa
assert get_friction_brake_mode(120, True, True, True, CP) == 0xd
def test_calc_pedal_command_small_accel_deadband_keeps_creep_target_stable():
pos_controller = _controller()
neg_controller = _controller()
pos_pedal, pos_regen = pos_controller.calc_pedal_command(0.02, True, 0.3)
neg_pedal, neg_regen = neg_controller.calc_pedal_command(-0.02, True, 0.3)
assert not pos_regen
assert not neg_regen
assert pos_pedal == neg_pedal
def test_calc_pedal_command_creep_switch_does_not_snap_to_target():
controller = _controller()
controller.pedal_active_last = True
controller.pedal_steady = 0.18
controller.regen_press_counter = 20
pedal_gas, press_regen = controller.calc_pedal_command(-1.0, True, 0.5)
assert press_regen
assert pedal_gas > 0.15
def test_calc_pedal_command_softens_small_positive_follow_ramp_at_road_speed():
controller = _controller()
controller.pedal_active_last = True
controller.pedal_steady = 0.18
pedal_gas, press_regen = controller.calc_pedal_command(0.2, True, 18.0)
assert not press_regen
assert pedal_gas - 0.18 < 0.026
def test_calc_pedal_command_keeps_strong_positive_requests_responsive():
controller = _controller()
controller.pedal_active_last = True
controller.pedal_steady = 0.18
pedal_gas, press_regen = controller.calc_pedal_command(1.4, True, 18.0)
assert not press_regen
assert pedal_gas - 0.18 > 0.04
def test_shape_truck_positive_accel_softens_small_highway_requests():
shaped = shape_truck_positive_accel(0.12, 26.0, True)
assert 0.09 < shaped < 0.10
def test_shape_truck_positive_accel_keeps_mid_follow_requests_available():
shaped = shape_truck_positive_accel(0.45, 13.5, True)
assert 0.43 < shaped < 0.45
def test_shape_truck_positive_accel_leaves_large_requests_alone():
assert shape_truck_positive_accel(1.0, 26.0, True) == 1.0
def test_shape_truck_positive_accel_is_inactive_when_disabled_or_low_speed():
assert shape_truck_positive_accel(0.12, 26.0, False) == 0.12
assert shape_truck_positive_accel(0.12, 6.0, True) == 0.12
def test_use_interceptor_sng_launch_requires_actual_near_stop():
CP = SimpleNamespace(vEgoStarting=0.25)
assert use_interceptor_sng_launch(CP, _sng_cs(0.0, True, True))
assert use_interceptor_sng_launch(CP, _sng_cs(0.2, False, True))
assert not use_interceptor_sng_launch(CP, _sng_cs(1.2, False, True))
assert not use_interceptor_sng_launch(CP, _sng_cs(0.0, True, False))
def test_bolt_acc_pedal_sng_launch_uses_physical_standstill_without_stock_acc_bit():
CP = SimpleNamespace(
vEgoStarting=0.25,
carFingerprint=CAR.CHEVROLET_BOLT_ACC_2022_2023_PEDAL,
enableGasInterceptorDEPRECATED=True,
)
assert use_interceptor_sng_launch(CP, _sng_cs(0.0, True, False))
assert use_interceptor_sng_launch(CP, _sng_cs(0.2, False, False))
assert not use_interceptor_sng_launch(CP, _sng_cs(1.2, False, False))
def test_bolt_acc_pedal_sng_launch_preserves_stronger_computed_pedal():
CP = SimpleNamespace(
carFingerprint=CAR.CHEVROLET_BOLT_ACC_2022_2023_PEDAL,
openpilotLongitudinalControl=True,
enableGasInterceptorDEPRECATED=True,
flags=GMFlags.PEDAL_LONG.value,
)
params = SimpleNamespace(SNG_INTERCEPTOR_GAS=18. / 255.)
assert get_interceptor_sng_gas_cmd(CP, 0.2, 0.54, params, False) == pytest.approx(0.2)
def test_other_pedal_sng_launch_keeps_fixed_floor_behavior():
CP = SimpleNamespace(
carFingerprint=CAR.CHEVROLET_BOLT_CC_2018_2021,
openpilotLongitudinalControl=True,
enableGasInterceptorDEPRECATED=True,
flags=GMFlags.PEDAL_LONG.value,
)
params = SimpleNamespace(SNG_INTERCEPTOR_GAS=18. / 255.)
assert get_interceptor_sng_gas_cmd(CP, 0.2, 0.54, params, False) == pytest.approx(18. / 255.)
def test_use_interceptor_sng_launch_extends_for_maneuver_mode():
CP = SimpleNamespace(vEgoStarting=0.25)
assert use_interceptor_sng_launch(CP, _sng_cs(1.2, False, True), maneuver_mode=True)
assert not use_interceptor_sng_launch(CP, _sng_cs(2.2, False, True), maneuver_mode=True)
def test_testing_ground_1_brake_switch_bias_is_softened_but_still_speed_scaled():
assert get_testing_ground_1_brake_switch_bias(0.0) == 40
assert get_testing_ground_1_brake_switch_bias(6.0) == 85
assert get_testing_ground_1_brake_switch_bias(15.0) == 130
assert get_testing_ground_1_brake_switch_bias(30.0) == 170
def test_lka_counter_uses_returned_loopback_counter():
cs = SimpleNamespace(
loopback_lka_steering_cmd_updated=True,
loopback_lka_steering_cmd_counter=2,
loopback_lka_steering_cmd_ts_nanos=1,
pt_lka_steering_cmd_counter=0,
)
assert get_lka_steering_cmd_counter(0, cs) == 3
def test_lka_counter_keeps_advancing_without_loopback_updates():
cs = SimpleNamespace(
loopback_lka_steering_cmd_updated=False,
loopback_lka_steering_cmd_counter=0,
loopback_lka_steering_cmd_ts_nanos=1,
pt_lka_steering_cmd_counter=0,
)
next_counter = 1
sent = []
for _ in range(6):
idx = get_lka_steering_cmd_counter(next_counter, cs)
sent.append(idx)
next_counter = (idx + 1) % 4
assert sent == [1, 2, 3, 0, 1, 2]
def test_lka_counter_only_seeds_from_pt_counter_once_without_loopback():
cs = SimpleNamespace(
loopback_lka_steering_cmd_updated=False,
loopback_lka_steering_cmd_counter=0,
loopback_lka_steering_cmd_ts_nanos=0,
pt_lka_steering_cmd_counter=0,
)
next_counter = -1
sent = []
for _ in range(6):
idx = get_lka_steering_cmd_counter(next_counter, cs)
sent.append(idx)
next_counter = (idx + 1) % 4
assert sent == [1, 2, 3, 0, 1, 2]
+12 -356
View File
@@ -1,28 +1,16 @@
import pytest
import numpy as np
from types import SimpleNamespace
from parameterized import parameterized
from cereal import custom
from opendbc.can import CANPacker, CANParser
from opendbc.car import Bus, DT_CTRL, structs
from opendbc.can import CANPacker
from opendbc.car import Bus, DT_CTRL
from opendbc.car.car_helpers import interfaces
from opendbc.car.gm import gmcan
from opendbc.car.gm.carstate import CarState as GMCarState, get_hard_cruise_buttons, update_auto_hold_drive_timers
from opendbc.car.gm.carcontroller import (
VisualAlert,
get_acc_dashboard_fcw_alert,
get_volt_one_pedal_lift_brake,
should_send_acc_dashboard_status,
should_send_cc_button_spam,
should_spoof_dash_speed,
)
from opendbc.car.gm.carcontroller import 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 ASCM_INT, CAMERA_ACC_CAR, CAR, CC_ONLY_CAR, DBC, GM_RX_OFFSET, CarControllerParams, CruiseButtons, GMFlags, GMSafetyFlags
from opendbc.safety import ALTERNATIVE_EXPERIENCE
from openpilot.common.params import Params
from opendbc.car.gm.values import CAMERA_ACC_CAR, CAR, CC_ONLY_CAR, DBC, GM_RX_OFFSET, GMFlags, GMSafetyFlags
CAMERA_DIAGNOSTIC_ADDRESS = 0x24b
VOLT_CARS = (
@@ -44,7 +32,6 @@ 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,
)
@@ -65,52 +52,6 @@ class TestGMFingerprint:
class TestGMInterface:
def test_bolt_acc_pedal_pid_accel_limits_keep_full_negative_authority(self):
cp = SimpleNamespace(
enableGasInterceptorDEPRECATED=True,
flags=GMFlags.PEDAL_LONG.value,
carFingerprint=CAR.CHEVROLET_BOLT_ACC_2022_2023_PEDAL,
)
accel_min, accel_max = gm_interface.CarInterface.get_pid_accel_limits(cp, 4.73, 0.0)
assert accel_min == pytest.approx(CarControllerParams.ACCEL_MIN)
assert accel_max == pytest.approx(np.interp(4.73, [0.0, 1.5, 4.0, 8.0, 15.0],
[0.54, 0.74, 1.03, 1.46, CarControllerParams.ACCEL_MAX]))
def test_bolt_cc_pedal_pid_accel_limits_remain_regen_limited(self):
cp = SimpleNamespace(
enableGasInterceptorDEPRECATED=True,
flags=GMFlags.PEDAL_LONG.value,
carFingerprint=CAR.CHEVROLET_BOLT_CC_2022_2023,
)
accel_min, _ = gm_interface.CarInterface.get_pid_accel_limits(cp, 4.73, 0.0)
assert accel_min == pytest.approx(np.interp(4.73, [0.0, 1.5, 4.0, 8.0, 15.0, 30.0],
[-0.93, -1.28, -1.98, -2.58, -2.86, -2.95]))
def test_missing_hard_cruise_signal_defaults_to_init(self):
assert get_hard_cruise_buttons({"ACCButtons": CruiseButtons.RES_ACCEL}) == CruiseButtons.INIT
assert get_hard_cruise_buttons({"ACCButtonsHard": CruiseButtons.DECEL_SET}) == CruiseButtons.DECEL_SET
def test_volt_auto_hold_drive_timer_requires_motion_before_startup_arming(self):
auto_hold_time, one_pedal_time = update_auto_hold_drive_timers(True, False, 0.0, 0.0)
assert auto_hold_time == 0.0
assert one_pedal_time == 0.0
def test_volt_auto_hold_drive_timer_accumulates_only_while_moving(self):
auto_hold_time, one_pedal_time = update_auto_hold_drive_timers(True, True, 0.0, 0.0)
assert auto_hold_time == pytest.approx(DT_CTRL)
assert one_pedal_time == pytest.approx(DT_CTRL)
auto_hold_time, one_pedal_time = update_auto_hold_drive_timers(True, False, auto_hold_time, one_pedal_time)
assert auto_hold_time == pytest.approx(DT_CTRL)
assert one_pedal_time == pytest.approx(DT_CTRL)
@parameterized.expand(VOLT_CARS)
def test_volt_min_steer_speed_is_7_mph(self, car_model):
CarInterface = interfaces[car_model]
@@ -123,37 +64,22 @@ class TestGMInterface:
("interceptor", True),
("ascm_int", False),
])
def test_volt_testing_ground_tune_sets_nonzero_p_and_starting_state(self, _name, pedal_present):
def test_volt_testing_ground_tune_sets_nonzero_p_and_starting_state(self, _name, pedal_present, monkeypatch):
CarInterface = interfaces[CAR.CHEVROLET_VOLT_ASCM]
fingerprint = _empty_fingerprint()
if pedal_present:
fingerprint[0][0x201] = 8 # pedal detected
fingerprint[0][0x2FF] = 8 # SASCM detected
old_testing_ground = gm_interface.testing_ground
gm_interface.testing_ground = SimpleNamespace(use_2=True)
params = Params()
params.put_bool("GMPedalLongitudinal", True)
monkeypatch.setattr(gm_interface.testing_ground, "use_2", True, raising=False)
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
params.remove("GMPedalLongitudinal")
car_params = CarInterface.get_params(CAR.CHEVROLET_VOLT_ASCM, fingerprint, [], alpha_long=False, is_release=False, docs=False,
starpilot_toggles=_test_starpilot_toggles())
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)
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)
def test_volt_cc_sparse_fingerprint_without_camera_sets_no_camera(self):
CarInterface = interfaces[CAR.CHEVROLET_VOLT_CC]
@@ -168,42 +94,6 @@ class TestGMInterface:
assert car_params.flags & GMFlags.NO_CAMERA.value
assert car_params.safetyConfigs[0].safetyParam & GMSafetyFlags.FLAG_GM_NO_CAMERA.value
def test_silverado_alpha_long_uses_trimmed_longitudinal_tune(self):
CarInterface = interfaces[CAR.CHEVROLET_SILVERADO]
fingerprint = _empty_fingerprint()
fingerprint[0] = FINGERPRINTS[CAR.CHEVROLET_SILVERADO][0].copy()
car_params = CarInterface.get_params(CAR.CHEVROLET_SILVERADO, fingerprint, [], alpha_long=True, is_release=False,
docs=False, starpilot_toggles=_test_starpilot_toggles())
assert car_params.openpilotLongitudinalControl
assert not car_params.enableGasInterceptorDEPRECATED
assert list(car_params.longitudinalTuning.kpBP) == pytest.approx([0.0, 5.0, 15.0, 35.0])
assert list(car_params.longitudinalTuning.kpV) == pytest.approx([0.02, 0.03, 0.028, 0.022])
assert list(car_params.longitudinalTuning.kiBP) == pytest.approx([0.0, 5.0, 15.0, 35.0])
assert list(car_params.longitudinalTuning.kiV) == pytest.approx([0.28, 0.26, 0.20, 0.16])
def test_blazer_uses_softer_low_speed_stop_hold_tune(self):
CarInterface = interfaces[CAR.CHEVROLET_BLAZER]
fingerprint = _empty_fingerprint()
fingerprint[0] = FINGERPRINTS[CAR.CHEVROLET_BLAZER][0].copy()
fingerprint[0][0x2FF] = 8 # SASCM present so alpha-long can enable on this platform
car_params = CarInterface.get_params(CAR.CHEVROLET_BLAZER, fingerprint, [], alpha_long=True, is_release=False,
docs=False, starpilot_toggles=_test_starpilot_toggles())
assert car_params.openpilotLongitudinalControl
assert list(car_params.longitudinalTuning.kpBP) == pytest.approx([0.0, 4.0, 12.0, 35.0])
assert list(car_params.longitudinalTuning.kpV) == pytest.approx([0.09, 0.075, 0.055, 0.04])
assert list(car_params.longitudinalTuning.kiBP) == pytest.approx([0.0, 4.0, 12.0, 35.0])
assert list(car_params.longitudinalTuning.kiV) == pytest.approx([0.03, 0.04, 0.055, 0.07])
assert car_params.longitudinalActuatorDelay == pytest.approx(0.7)
assert car_params.minEnableSpeed == pytest.approx(5 * CV.KPH_TO_MS)
assert car_params.stoppingDecelRate == pytest.approx(1.0)
assert car_params.vEgoStopping == pytest.approx(0.35)
assert car_params.vEgoStarting == pytest.approx(0.35)
assert car_params.stopAccel == pytest.approx(-0.30)
def test_volt_gateway_without_accel_pos_uses_brake_pedal_message(self):
CarInterface = interfaces[CAR.CHEVROLET_VOLT]
fingerprint = _empty_fingerprint()
@@ -219,166 +109,6 @@ class TestGMInterface:
assert "ECMAcceleratorPos" not in pt_parser.vl
assert "EBCMBrakePedalPosition" in pt_parser.vl
def test_volt_auto_hold_sets_stock_hold_safety_bit_with_op_long_enabled(self):
CarInterface = interfaces[CAR.CHEVROLET_VOLT_ASCM]
fingerprint = _empty_fingerprint()
fingerprint[0][0x2FF] = 8
params = Params()
try:
params.put_bool("GMAutoHold", True)
car_params = CarInterface.get_params(CAR.CHEVROLET_VOLT_ASCM, fingerprint, [], alpha_long=True, is_release=False,
docs=False, starpilot_toggles=_test_starpilot_toggles())
finally:
params.remove("GMAutoHold")
assert car_params.openpilotLongitudinalControl
assert car_params.safetyConfigs[0].safetyParam & GMSafetyFlags.FLAG_GM_PANDA_PADDLE_SCHED.value
def test_volt_one_pedal_sets_stock_hold_safety_bit_without_auto_hold(self):
CarInterface = interfaces[CAR.CHEVROLET_VOLT_ASCM]
fingerprint = _empty_fingerprint()
fingerprint[0][0x2FF] = 8
params = Params()
try:
params.put_bool("GMAutoHold", False)
params.put_bool("VoltOnePedalMode", True)
car_params = CarInterface.get_params(CAR.CHEVROLET_VOLT_ASCM, fingerprint, [], alpha_long=True, is_release=False,
docs=False, starpilot_toggles=_test_starpilot_toggles())
finally:
params.remove("GMAutoHold")
params.remove("VoltOnePedalMode")
assert car_params.openpilotLongitudinalControl
assert car_params.safetyConfigs[0].safetyParam & GMSafetyFlags.FLAG_GM_PANDA_PADDLE_SCHED.value
assert car_params.safetyConfigs[0].safetyParam & GMSafetyFlags.FLAG_GM_PANDA_3D1_SCHED.value
@parameterized.expand(VOLT_CARS)
def test_volt_bsm_is_enabled_without_fingerprint_match(self, car_model):
CarInterface = interfaces[car_model]
car_params = CarInterface.get_params(car_model, _empty_fingerprint(), [], alpha_long=False, is_release=False, docs=False,
starpilot_toggles=_test_starpilot_toggles())
assert car_params.enableBsm
def test_volt_bsm_parser_is_optional(self):
cp = SimpleNamespace(
carFingerprint=CAR.CHEVROLET_VOLT_ASCM,
networkLocation=structs.CarParams.NetworkLocation.fwdCamera,
flags=0,
transmissionType=structs.CarParams.TransmissionType.direct,
enableGasInterceptorDEPRECATED=False,
enableBsm=True,
)
pt_parser = GMCarState.get_can_parsers(cp)[Bus.pt]
bsm_addr = pt_parser.dbc.name_to_msg["BCMBlindSpotMonitor"].address
assert "BCMBlindSpotMonitor" in pt_parser.vl
assert pt_parser.message_states[bsm_addr].ignore_alive
def test_volt_ascm_cam_parser_includes_optional_aeb_cmd(self):
cam_parser = GMCarState.get_can_parsers(SimpleNamespace(
carFingerprint=CAR.CHEVROLET_VOLT_ASCM,
networkLocation=structs.CarParams.NetworkLocation.fwdCamera,
flags=0,
transmissionType=structs.CarParams.TransmissionType.direct,
enableGasInterceptorDEPRECATED=False,
enableBsm=False,
))[Bus.cam]
aeb_addr = cam_parser.dbc.name_to_msg["AEBCmd"].address
assert "AEBCmd" in cam_parser.vl
assert cam_parser.message_states[aeb_addr].ignore_alive
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("GMPedalLongitudinal", True)
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("GMPedalLongitudinal")
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
assert car_params.safetyConfigs[0].safetyParam & GMSafetyFlags.FLAG_GM_PANDA_PADDLE_SCHED.value
def test_cadillac_xt5_sdgm_sascm_gates_alpha_long(self):
CarInterface = interfaces[CAR.CADILLAC_XT5]
fingerprint = _empty_fingerprint()
fingerprint[0][0xBE] = 6
stock_params = CarInterface.get_params(CAR.CADILLAC_XT5, fingerprint, [], alpha_long=True, is_release=False,
docs=False, starpilot_toggles=_test_starpilot_toggles())
assert stock_params.networkLocation == structs.CarParams.NetworkLocation.fwdCamera
assert stock_params.pcmCruise
assert stock_params.alphaLongitudinalAvailable is False
assert stock_params.openpilotLongitudinalControl is False
assert stock_params.safetyConfigs[0].safetyParam & GMSafetyFlags.HW_SDGM.value
fingerprint[0][0x2FF] = 8
sascm_params = CarInterface.get_params(CAR.CADILLAC_XT5, fingerprint, [], alpha_long=True, is_release=False,
docs=False, starpilot_toggles=_test_starpilot_toggles())
assert sascm_params.flags & GMFlags.SASCM.value
assert sascm_params.alphaLongitudinalAvailable
assert sascm_params.openpilotLongitudinalControl
assert not sascm_params.pcmCruise
assert sascm_params.safetyConfigs[0].safetyParam & GMSafetyFlags.HW_CAM_LONG.value
def test_cadillac_escalade_esv_2019_ascm_uses_sascm_and_2019_tune(self):
base_fingerprint = FINGERPRINTS[CAR.CADILLAC_ESCALADE_ESV_2019][0]
ascm_fingerprint = FINGERPRINTS[CAR.CADILLAC_ESCALADE_ESV_2019_ASCM][0]
assert CAR.CADILLAC_ESCALADE_ESV_2019_ASCM in ASCM_INT
assert ascm_fingerprint[0x2FF] == 8
assert {addr: length for addr, length in ascm_fingerprint.items() if addr != 0x2FF} == base_fingerprint
CarInterface = interfaces[CAR.CADILLAC_ESCALADE_ESV_2019_ASCM]
fingerprint = _empty_fingerprint()
fingerprint[0] = ascm_fingerprint.copy()
car_params = CarInterface.get_params(CAR.CADILLAC_ESCALADE_ESV_2019_ASCM, fingerprint, [], alpha_long=True, is_release=False,
docs=False, starpilot_toggles=_test_starpilot_toggles())
assert car_params.flags & GMFlags.SASCM.value
assert car_params.networkLocation == structs.CarParams.NetworkLocation.fwdCamera
assert car_params.openpilotLongitudinalControl
assert not car_params.pcmCruise
assert car_params.safetyConfigs[0].safetyParam & GMSafetyFlags.HW_ASCM_INT.value
assert car_params.safetyConfigs[0].safetyParam & GMSafetyFlags.HW_CAM_LONG.value
assert car_params.lateralTuning.torque.latAccelFactor == pytest.approx(1.15)
assert car_params.lateralTuning.torque.friction == pytest.approx(0.2)
def test_cadillac_xt4_uses_nonlinear_torque_curve_with_center_boost(self):
CarInterface = interfaces[CAR.CADILLAC_XT4]
car_params = CarInterface.get_non_essential_params(CAR.CADILLAC_XT4)
ci = CarInterface(car_params, custom.StarPilotCarParams.new_message())
torque_from_lataccel = ci.torque_from_lateral_accel()
low_lataccel = 0.2
high_lataccel = 1.0
low_torque = torque_from_lataccel(low_lataccel, car_params.lateralTuning.torque)
high_torque = torque_from_lataccel(high_lataccel, car_params.lateralTuning.torque)
linear_low_torque = low_lataccel / car_params.lateralTuning.torque.latAccelFactor
linear_high_torque = high_lataccel / car_params.lateralTuning.torque.latAccelFactor
assert low_torque > linear_low_torque * 1.15
assert low_torque < linear_low_torque * 1.30
assert high_torque == pytest.approx(linear_high_torque, rel=0.03)
assert torque_from_lataccel(-low_lataccel, car_params.lateralTuning.torque) == pytest.approx(-low_torque, rel=1e-6)
class TestGMCarController:
def test_dash_speed_spoof_respects_live_stock_acc_toggles(self):
@@ -393,11 +123,6 @@ class TestGMCarController:
assert should_spoof_dash_speed(cp, SimpleNamespace(disable_openpilot_long=False))
def test_volt_one_pedal_lift_brake_seeds_low_speed_braking(self):
assert get_volt_one_pedal_lift_brake(2.1 * CV.MPH_TO_MS) == 0
assert get_volt_one_pedal_lift_brake(2.0 * CV.MPH_TO_MS) == 20
assert get_volt_one_pedal_lift_brake(0.10) == 80
def test_volt_camera_no_camera_sends_acc_dashboard_without_dash_spoof(self):
cp = SimpleNamespace(carFingerprint=CAR.CHEVROLET_VOLT_CAMERA, flags=GMFlags.NO_CAMERA.value)
@@ -489,72 +214,3 @@ 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_command_uses_openpilot_hud_when_disengaged(self):
packer = CANPacker(DBC[CAR.CHEVROLET_VOLT_ASCM][Bus.pt])
parser = CANParser(DBC[CAR.CHEVROLET_VOLT_ASCM][Bus.pt], [("ASCMActiveCruiseControlStatus", 0)], 0)
msg = gmcan.create_acc_dashboard_command(
packer,
0,
False,
50,
SimpleNamespace(leadDistanceBars=2, leadVisible=True),
0x3,
)
parser.update([0, [msg]])
values = parser.vl["ASCMActiveCruiseControlStatus"]
assert values["ACCSpeedSetpoint"] == 50
assert values["ACCGapLevel"] == 0
assert values["ACCCmdActive"] == 0
assert values["ACCLeadCar"] == 1
assert values["FCWAlert"] == 3
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
def test_acc_dashboard_fcw_alert_falls_back_to_stock_fcw_event(self):
cs = SimpleNamespace(
stock_fcw_alert=0,
out=SimpleNamespace(stockAeb=False, stockFcw=True),
)
assert get_acc_dashboard_fcw_alert(VisualAlert.none, cs) == 0x3
@@ -1,79 +0,0 @@
from types import SimpleNamespace
from opendbc.can import CANPacker
from opendbc.car.gm import gmcan
from opendbc.car.gm.values import CAR, DBC
class TestGMCan:
def setup_method(self):
self.packer = CANPacker(DBC[CAR.CHEVROLET_BOLT_ACC_2022_2023]["pt"])
def test_gas_regen_command_matches_starpilot_bolt_acc(self):
addr, dat, bus = gmcan.create_gas_regen_command(self.packer, 0, 5000, 1, True, False)
assert addr == 0x2CB
assert bus == 0
assert dat.hex() == "41429c4000bd63bf"
def test_gas_regen_command_preserves_always_one3_layout(self):
_, dat, _ = gmcan.create_gas_regen_command(self.packer, 0, 0, 1, True, False, include_always_one3=True)
assert dat.hex() == "4142800000bd7fff"
def test_gas_regen_command_encodes_high_bit_above_8191(self):
_, dat, _ = gmcan.create_gas_regen_command(self.packer, 0, 8848, 1, True, False)
decoded = ((dat[1] & 0x1) << 13) | (dat[2] << 5) | ((dat[3] & 0xF8) >> 3)
assert dat[1] & 0x1
assert decoded == 8848
def test_prndl2_command_matches_bolt_gen2_regen_paddle_spoof(self):
CP = SimpleNamespace(carFingerprint=CAR.CHEVROLET_BOLT_ACC_2022_2023_PEDAL)
addr, dat, bus = gmcan.create_prndl2_command(self.packer, 0, False, CP)
assert addr == 0x1F5
assert bus == 0
assert dat.hex() == "0c0c000600000100"
addr, dat, bus = gmcan.create_prndl2_command(self.packer, 0, True, CP)
assert addr == 0x1F5
assert bus == 0
assert dat.hex() == "0c0c000500020100"
def test_prndl2_command_matches_bolt_gen1_regen_paddle_spoof(self):
CP = SimpleNamespace(carFingerprint=CAR.CHEVROLET_BOLT_CC_2018_2021)
addr, dat, bus = gmcan.create_prndl2_command(self.packer, 0, True, CP)
assert addr == 0x1F5
assert bus == 0
assert dat.hex() == "0c0c000700020100"
def test_regen_paddle_command_matches_bolt_spoof(self):
addr, dat, bus = gmcan.create_regen_paddle_command(self.packer, 0, False)
assert addr == 0xBD
assert bus == 0
assert dat.hex() == "00000000000000"
addr, dat, bus = gmcan.create_regen_paddle_command(self.packer, 0, True)
assert addr == 0xBD
assert bus == 0
assert dat.hex() == "20000000000000"
def test_gas_regen_command_matches_starpilot_volt_2019(self):
packer = CANPacker(DBC[CAR.CHEVROLET_VOLT_2019]["pt"])
addr, dat, bus = gmcan.create_gas_regen_command(packer, 0, 5000, 1, True, False, include_always_one3=True, use_volt_layout=True)
assert addr == 0x2CB
assert bus == 0
assert dat.hex() == "41429c4000bd63bf"
def test_gas_regen_command_matches_starpilot_volt_ascm(self):
packer = CANPacker(DBC[CAR.CHEVROLET_VOLT_ASCM]["pt"])
addr, dat, bus = gmcan.create_gas_regen_command(packer, 0, 5000, 1, True, False, include_always_one3=True, use_volt_layout=True)
assert addr == 0x2CB
assert bus == 0
assert dat.hex() == "41429c4000bd63bf"
+3 -31
View File
@@ -233,7 +233,7 @@ class CAR(Platforms):
)
CHEVROLET_VOLT = GMASCMPlatformConfig(
[GMCarDocs("Chevrolet Volt 2017-18", min_enable_speed=0, video="https://youtu.be/QeMCN_4TFfQ")],
GMCarSpecs(mass=1607, wheelbase=2.69, steerRatio=15.7, centerToFrontRatio=0.45, tireStiffnessFactor=1.0, minEnableSpeed=-1), #tire stiffness factor hasn't been updated since 2018 and every other gm is on 1.0
GMCarSpecs(mass=1607, wheelbase=2.69, steerRatio=15.7, centerToFrontRatio=0.45, tireStiffnessFactor=0.469, minEnableSpeed=-1),
dbc_dict={
Bus.pt: "gm_global_a_powertrain_volt",
Bus.radar: "gm_global_a_object",
@@ -279,10 +279,6 @@ class CAR(Platforms):
[GMCarDocs("Buick LaCrosse 2017-19", "Driver Confidence Package 2")],
GMCarSpecs(mass=1712, wheelbase=2.91, steerRatio=15.8, centerToFrontRatio=0.4),
)
BUICK_LACROSSE_ASCM = GMPlatformConfig(
[GMCarDocs("Buick LaCrosse 2017-19 ASCM Harness")],
BUICK_LACROSSE.specs,
)
BUICK_REGAL = GMASCMPlatformConfig(
[GMCarDocs("Buick Regal Essence 2018")],
GMCarSpecs(mass=1714, wheelbase=2.83, steerRatio=14.4, centerToFrontRatio=0.4),
@@ -303,10 +299,6 @@ class CAR(Platforms):
[GMCarDocs("Cadillac Escalade ESV 2019", "Adaptive Cruise Control (ACC) & LKAS")],
CADILLAC_ESCALADE_ESV.specs,
)
CADILLAC_ESCALADE_ESV_2019_ASCM = GMPlatformConfig(
[GMCarDocs("Cadillac Escalade ESV Platinum 2019 ASCM Harness", "Adaptive Cruise Control (ACC) & LKAS")],
CADILLAC_ESCALADE_ESV_2019.specs,
)
CHEVROLET_BOLT_ACC_2022_2023 = GMPlatformConfig(
[
GMCarDocs("Chevrolet Bolt ACC 2022-23", "Premier or Premier Redline Trim without Super Cruise Package", video="https://youtu.be/xvwzGMUA210"),
@@ -337,13 +329,6 @@ class CAR(Platforms):
],
GMCarSpecs(mass=2994, wheelbase=3.75, steerRatio=16.3, tireStiffnessFactor=1.0),
)
CHEVROLET_SILVERADO_CC = GMPlatformConfig(
[
GMCarDocs("Chevrolet Silverado 1500 - No-ACC"),
GMCarDocs("GMC Sierra 1500 - No-ACC"),
],
CHEVROLET_SILVERADO.specs,
)
CHEVROLET_EQUINOX = GMPlatformConfig(
[GMCarDocs("Chevrolet Equinox 2019-22")],
GMCarSpecs(mass=1588, wheelbase=2.72, steerRatio=14.4, centerToFrontRatio=0.4),
@@ -368,10 +353,6 @@ class CAR(Platforms):
[GMCarDocs("Cadillac XT4 2023", "Driver Assist Package")],
GMCarSpecs(mass=1660, wheelbase=2.78, steerRatio=14.4, centerToFrontRatio=0.4),
)
CADILLAC_XT5 = GMSDGMPlatformConfig(
[GMCarDocs("Cadillac XT5 2022", "Driver Assist Package")],
CarSpecs(mass=1810, wheelbase=2.86, steerRatio=16.34, centerToFrontRatio=0.5),
)
CADILLAC_XT6 = GMPlatformConfig(
[GMCarDocs("Cadillac XT6 2020", "Driver Assist Package")],
GMCarSpecs(mass=2050, wheelbase=2.86, steerRatio=16.5, centerToFrontRatio=0.4),
@@ -402,7 +383,7 @@ class CAR(Platforms):
)
CADILLAC_XT5_CC = GMPlatformConfig(
[GMCarDocs("Cadillac XT5 - No-ACC")],
CADILLAC_XT5.specs,
CarSpecs(mass=1810, wheelbase=2.86, steerRatio=16.34, centerToFrontRatio=0.5),
)
CHEVROLET_EQUINOX_CC = GMPlatformConfig(
[GMCarDocs("Chevrolet Equinox 2019-22 - No-ACC")],
@@ -546,7 +527,6 @@ ALT_ACCS = {CAR.GMC_YUKON, CAR.GMC_YUKON_CC}
# We're integrated at the Safety Data Gateway Module on these cars
SDGM_CAR = {
CAR.CADILLAC_XT4,
CAR.CADILLAC_XT5,
CAR.CADILLAC_XT6,
CAR.CHEVROLET_TRAVERSE,
CAR.CHEVROLET_BLAZER,
@@ -569,7 +549,6 @@ CC_ONLY_CAR = {
CAR.CADILLAC_XT5_CC,
CAR.CHEVROLET_MALIBU_CC,
CAR.CHEVROLET_MALIBU_HYBRID_CC,
CAR.CHEVROLET_SILVERADO_CC,
}
CC_REGEN_PADDLE_CAR = {
CAR.CHEVROLET_BOLT_CC_2018_2021,
@@ -580,14 +559,7 @@ CC_REGEN_PADDLE_CAR = {
CAMERA_ACC_CAR.update(CC_ONLY_CAR)
# ASCM-INT paths are only enabled when SASCM (0x2FF) is detected at runtime
ASCM_INT = {
CAR.CHEVROLET_VOLT_ASCM,
CAR.GMC_ACADIA_ASCM,
CAR.CHEVROLET_MALIBU_ASCM,
CAR.CADILLAC_ESCALADE_ASCM,
CAR.CADILLAC_ESCALADE_ESV_2019_ASCM,
CAR.BUICK_LACROSSE_ASCM,
}
ASCM_INT = {CAR.CHEVROLET_VOLT_ASCM, CAR.GMC_ACADIA_ASCM, CAR.CHEVROLET_MALIBU_ASCM, CAR.CADILLAC_ESCALADE_ASCM}
STEER_THRESHOLD = 1.0
+47 -281
View File
@@ -1,130 +1,16 @@
import math
import numpy as np
from opendbc.can import CANPacker
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 import Bus, DT_CTRL, 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,
HondaFlags,
)
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.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
@@ -150,18 +36,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.0
braking = brake > 0.0
brake = 0.
braking = brake > 0.
# for small brake oscillations within brake_hyst_gap, don't change the brake command
if brake == 0.0:
brake_steady = 0.0
if brake == 0.:
brake_steady = 0.
elif brake > brake_steady + brake_hyst_gap:
brake_steady = brake - brake_hyst_gap
elif brake < brake_steady - brake_hyst_gap:
@@ -178,7 +64,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.0 and apply_brake > 0):
if apply_brake > apply_brake_last or (ts - last_pump_ts > 20. and apply_brake > 0):
last_pump_ts = ts
# once the pump is on, run it for at least 0.2s
@@ -207,15 +93,14 @@ 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.0
self.brake_last = 0.0
self.brake_steady = 0.
self.brake_last = 0.
self.apply_brake_last = 0
self.last_pump_ts = 0.0
self.last_pump_ts = 0.
self.stopping_counter = 0
self.accel = 0.0
@@ -223,41 +108,12 @@ 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
@@ -266,36 +122,17 @@ 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(torque_cmd, self.last_torque, -self.params.STEER_DELTA_DOWN * DT_CTRL, self.params.STEER_DELTA_UP * DT_CTRL)
limited_torque = rate_limit(actuators.torque, 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.0, DT_CTRL)
self.brake_last = rate_limit(pre_limit_brake, self.brake_last, -2., DT_CTRL)
# vehicle hud display, wait for one update from 10Hz 0x304 msg
alert_fcw, alert_steer_required = process_hud_alert(hud_control.visualAlert)
@@ -303,7 +140,8 @@ 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 = []
@@ -311,29 +149,37 @@ 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, self.CAN.pt, suppress_response=True))
can_sends.append(make_tester_present_msg(0x18DAB0F1, 1, 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 = 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)
wind_brake = np.interp(CS.out.vEgo, [0.0, 2.3, 35.0], [0.001, 0.002, 0.15])
# 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 self.CP.enableGasInterceptorDEPRECATED or not CC.longActive:
if 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)
@@ -353,108 +199,35 @@ 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))
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
self.gas = float(np.interp(accel, self.params.BOSCH_GAS_LOOKUP_BP, self.params.BOSCH_GAS_LOOKUP_V))
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 = 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
)
)
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))
if self.CP.openpilotLongitudinalControl:
# TODO: combining with create_acc_hud block above will change message order and will need replay logs regenerated
@@ -464,14 +237,7 @@ 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
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)
self.gas = pcm_accel / self.params.NIDEC_GAS_MAX
new_actuators = actuators.as_builder()
new_actuators.speed = self.speed
+22 -52
View File
@@ -3,11 +3,11 @@ from collections import defaultdict
from cereal import custom
from opendbc.can import CANDefine, CANParser
from opendbc.car import Bus, DT_CTRL, create_button_events, structs
from opendbc.car import Bus, 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_BOSCH_TJA_CONTROL, \
HONDA_NIDEC_ALT_SCM_MESSAGES, HONDA_BOSCH_RADARLESS, \
HondaFlags, CruiseButtons, CruiseSettings, GearShifter, CarControllerParams, HondaStarPilotFlags
from opendbc.car.interfaces import CarStateBase
@@ -54,19 +54,13 @@ 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_TJA_CONTROL | HONDA_BOSCH_CANFD)
self.dynamic_v_cruise_units = self.CP.carFingerprint in (HONDA_BOSCH_RADARLESS | HONDA_BOSCH_ALT_RADAR | 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]
@@ -87,19 +81,18 @@ class CarState(CarStateBase):
self.cruise_buttons = cp.vl["SCM_BUTTONS"]["CRUISE_BUTTONS"]
# used for car hud message
self.is_metric = self.CP.carFingerprint in (CAR.HONDA_ODYSSEY_TWN,) or not cp.vl["CAR_SPEED"]["IMPERIAL_UNIT"]
self.is_metric = 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) * lowspeed_source * CV.KPH_TO_MS * self.CP.wheelSpeedFactor + v_weight * v_wheel
ret.vEgoRaw = (1. - v_weight) * cp.vl["ENGINE_DATA"]["XMISSION_SPEED"] * CV.KPH_TO_MS * self.CP.wheelSpeedFactor + v_weight * v_wheel
ret.vEgo, ret.aEgo = self.update_speed_kf(ret.vEgoRaw)
ret.standstill = lowspeed_source < 1e-5
ret.standstill = cp.vl["ENGINE_DATA"]["XMISSION_SPEED"] < 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
@@ -114,8 +107,7 @@ 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", "TJA_LOW_SPEED_LOCKOUT",
"TMP_FAULT")
ret.steerFaultPermanent = steer_status not in ("NORMAL", "NO_TORQUE_ALERT_1", "NO_TORQUE_ALERT_2", "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)
@@ -125,11 +117,7 @@ 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", "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
ret.steerFaultTemporary = steer_status not in ("NORMAL", "LOW_SPEED_LOCKOUT", "NO_TORQUE_ALERT_2")
# 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
@@ -140,23 +128,22 @@ class CarState(CarStateBase):
self.low_speed_alert = False
ret.lowSpeedAlert = self.low_speed_alert
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:
if self.CP.carFingerprint in HONDA_BOSCH_RADARLESS:
ret.accFaulted = bool(cp.vl["CRUISE_FAULT_STATUS"]["CRUISE_FAULT"])
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:
else:
if self.CP.openpilotLongitudinalControl:
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
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
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"]
@@ -167,16 +154,12 @@ 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[self.car_state_scm_msg]["REVERSE_LIGHT"]) else GearShifter.drive
ret.gearShifter = GearShifter.reverse if bool(cp.vl["SCM_FEEDBACK"]["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)
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.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)
@@ -217,16 +200,6 @@ 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:
@@ -245,7 +218,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 | HONDA_BOSCH_CANFD):
if self.CP.carFingerprint in HONDA_BOSCH_RADARLESS:
self.lkas_hud = cp_cam.vl["LKAS_HUD"]
if self.CP.enableBsm:
@@ -262,9 +235,6 @@ 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,7 +84,6 @@ 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',
@@ -143,29 +142,6 @@ 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',
@@ -187,7 +163,6 @@ 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',
@@ -255,14 +230,11 @@ 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',
@@ -396,7 +368,6 @@ 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',
+4 -5
View File
@@ -167,19 +167,18 @@ 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': lanes_visible,
'SOLID_LANES': hud_control.lanesVisible,
'BEEP': 0,
}
if CP.carFingerprint in (HONDA_BOSCH_RADARLESS | HONDA_BOSCH_CANFD):
lkas_hud_values['LANE_LINES'] = 3
lkas_hud_values['DASHED_LANES'] = lanes_visible
lkas_hud_values['DASHED_LANES'] = hud_control.lanesVisible
# car likely needs to see LKAS_PROBLEM fall within a specific time frame, so forward from camera
# TODO: needed for Bosch CAN FD?
@@ -193,8 +192,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'] = bool(steering_available or lanes_visible)
lkas_hud_values['SOLID_LANES'] = lanes_visible
lkas_hud_values['DASHED_LANES'] = steering_available
lkas_hud_values['SOLID_LANES'] = lat_active
lkas_hud_values['LKAS_PROBLEM'] = lat_active and reduced_steering
if CP.flags & HondaFlags.BOSCH_EXT_HUD and not CP.openpilotLongitudinalControl:
+17 -139
View File
@@ -23,8 +23,6 @@ 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
@@ -48,17 +46,15 @@ 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
ret.alphaLongitudinalAvailable = True
ret.openpilotLongitudinalControl = alpha_long
# 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.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]
@@ -84,7 +80,6 @@ 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
@@ -95,32 +90,14 @@ class CarInterface(CarInterfaceBase):
ret.longitudinalTuning.kiBP = [0., 5., 35.]
ret.longitudinalTuning.kiV = [1.2, 0.8, 0.5]
eps_modified = False
# Disable control if EPS mod detected
for fw in car_fw:
if fw.ecu == "eps" and b"," in fw.fwVersion:
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
ret.dashcamOnly = True
if candidate == CAR.HONDA_CIVIC:
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]]
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
@@ -135,17 +112,7 @@ 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
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)
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.6], [0.18]]
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
@@ -157,15 +124,8 @@ class CarInterface(CarInterfaceBase):
ret.wheelSpeedFactor = 1.025
elif candidate == CAR.HONDA_CRV_5G:
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.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:
@@ -173,13 +133,6 @@ 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]]
@@ -195,52 +148,22 @@ 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, 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
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.2], [0.06]]
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_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):
elif candidate == CAR.HONDA_PILOT:
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.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)
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.38], [0.11]]
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
@@ -250,7 +173,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 in (CAR.HONDA_E, CAR.HONDA_E_ADVANCE):
elif candidate == CAR.HONDA_E:
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
@@ -266,48 +189,19 @@ 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, CAR.ACURA_RDX_3G, CAR.ACURA_MDX_4G,
CAR.ACURA_ADX, *HONDA_BOSCH_CANFD):
if 0x1BE in fingerprint[CAN.pt] and candidate in (CAR.HONDA_ACCORD, CAR.HONDA_HRV_3G, *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:
@@ -318,24 +212,8 @@ 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
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.autoResumeSng = candidate in (HONDA_BOSCH | {CAR.HONDA_CIVIC})
ret.minEnableSpeed = -1. if ret.autoResumeSng else 25.51 * CV.MPH_TO_MS
ret.steerLimitTimer = 0.8
ret.radarDelay = 0.1
@@ -1,44 +1,12 @@
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 CAR, DBC, HONDA_BOSCH, HONDA_BOSCH_TJA_CONTROL, CarControllerParams, HondaFlags, HondaSafetyFlags, \
HondaStarPilotFlags
from opendbc.car.honda.values import HONDA_BOSCH, HONDA_BOSCH_TJA_CONTROL
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)
HONDA_FW_VERSION_RE = br"[A-Z0-9]{5}-[A-Z0-9]{3}(-|,)[A-Z0-9]{4}(\x00){2}$"
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():
@@ -48,230 +16,3 @@ 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)
+2 -114
View File
@@ -56,7 +56,6 @@ class HondaSafetyFlags(IntFlag):
NIDEC_ALT = 4
RADARLESS = 8
BOSCH_CANFD = 16
GAS_INTERCEPTOR = 32
class HondaFlags(IntFlag):
@@ -80,7 +79,6 @@ class HondaFlags(IntFlag):
ALLOW_MANUAL_TRANS = 1024
HYBRID = 2048
BOSCH_TJA_CONTROL = 4096
EPS_MODIFIED = 8192
# Car button codes
@@ -248,12 +246,6 @@ 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
@@ -264,11 +256,6 @@ 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),
@@ -277,12 +264,6 @@ 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")],
@@ -300,34 +281,6 @@ 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(
@@ -351,12 +304,6 @@ 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),
@@ -375,27 +322,12 @@ 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
@@ -423,43 +355,6 @@ 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)
@@ -494,11 +389,6 @@ 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,
}
@@ -543,11 +433,9 @@ 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, 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.eps: [CAR.ACURA_RDX_3G, CAR.HONDA_ACCORD, CAR.HONDA_E, *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_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],
CAR.HONDA_E, CAR.HONDA_INSIGHT, CAR.HONDA_NBOX_2G, *HONDA_BOSCH_ALT_RADAR, *HONDA_BOSCH_RADARLESS, *HONDA_BOSCH_CANFD],
},
extra_ecus=[
(Ecu.combinationMeter, 0x18da60f1, None),
+35 -518
View File
@@ -1,15 +1,11 @@
from dataclasses import dataclass
import numpy as np
from opendbc.can import CANPacker
from opendbc.car import Bus, DT_CTRL, make_tester_present_msg, rate_limit, structs
from opendbc.car.common.filter_simple import FirstOrderFilter
from opendbc.car import Bus, DT_CTRL, make_tester_present_msg, structs
from opendbc.car.lateral import apply_driver_steer_torque_limits, apply_steer_angle_limits_vm, common_fault_avoidance
from opendbc.car.common.conversions import Conversions as CV
from opendbc.car.hyundai import hyundaicanfd, hyundaican
from opendbc.car.hyundai.hyundaicanfd import CanBus
from opendbc.car.hyundai.values import HyundaiFlags, Buttons, CarControllerParams, CAR, CANFD_RADAR_LIVE_LONGITUDINAL_CAR, \
kia_ev6_gt_line_longitudinal_tuning
from opendbc.car.hyundai.values import HyundaiFlags, Buttons, CarControllerParams, CAR
from opendbc.car.interfaces import CarControllerBase
from opendbc.car.vehicle_model import VehicleModel
from openpilot.common.params import Params
@@ -22,236 +18,6 @@ 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]
ANGLE_SAFETY_BASELINE_MODEL = str(CAR.KIA_SPORTAGE_HEV_2026)
DEFAULT_ANGLE_SMOOTHING_VEGO_BP = [5.0, 10.0, 20.0]
DEFAULT_ANGLE_SMOOTHING_ALPHA_V = [0.2, 0.1, 0.0]
EV9_HIGH_ANGLE_GAIN_BP = [70.0, 120.0, 220.0, 320.0]
EV9_HIGH_ANGLE_GAIN_CAP_V = [0.85, 0.55, 0.30, 0.16]
EV9_HIGH_ANGLE_GAIN_MIN = 0.004
def egmp_dynamic_longitudinal_tuning(CP) -> bool:
return CP.carFingerprint == CAR.HYUNDAI_IONIQ_6 or \
kia_ev6_gt_line_longitudinal_tuning(CP.carFingerprint, getattr(CP, "carVin", ""))
def should_reset_ev6_gt_line_longitudinal_tuning(CP, long_control_state: LongCtrlState) -> bool:
return kia_ev6_gt_line_longitudinal_tuning(CP.carFingerprint, getattr(CP, "carVin", "")) and \
long_control_state == LongCtrlState.off
@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
def reset_ev6_gt_line_longitudinal_tuning(state: Ioniq6LongitudinalTuningState, CP,
long_control_state: LongCtrlState) -> Ioniq6LongitudinalTuningState:
if should_reset_ev6_gt_line_longitudinal_tuning(CP, long_control_state):
return Ioniq6LongitudinalTuningState(long_control_state_last=long_control_state)
return state
@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 get_baseline_safety_cp():
from opendbc.car.hyundai.interface import CarInterface
return CarInterface.get_non_essential_params(ANGLE_SAFETY_BASELINE_MODEL)
def get_angle_smoothing_alpha(CP, v_ego: float) -> float:
return float(np.interp(v_ego, DEFAULT_ANGLE_SMOOTHING_VEGO_BP, DEFAULT_ANGLE_SMOOTHING_ALPHA_V))
def compute_torque_reduction_gain(steering_torque, v_ego, lat_active, last_gain):
if lat_active:
ceiling = np.interp(v_ego, [0.5, 1.5], [1.0, 0.85])
shelf = np.interp(v_ego, [2.0, 11.0], [0.45, 0.6])
floor = np.interp(v_ego, [2.0, 22.0], [0.1, 0.3])
bp1 = np.interp(v_ego, [2.0, 11.0], [75.0, 125.0])
bp2 = np.interp(v_ego, [2.0, 11.0], [125.0, 150.0])
bp3 = np.interp(v_ego, [2.0, 11.0], [175.0, 275.0])
bp4 = np.interp(v_ego, [2.0, 22.0], [400.0, 700.0])
target = np.interp(abs(steering_torque), [bp1, bp2, bp3, bp4], [ceiling, shelf, shelf, floor])
else:
target = 0.0
gain = rate_limit(target, last_gain, -0.014, 0.004)
return round(gain / 0.004) * 0.004
def apply_ev9_high_angle_gain_cap(CP, gain: float, steering_angle_deg: float, lat_active: bool) -> float:
if CP.carFingerprint != CAR.KIA_EV9 or not CP.flags & HyundaiFlags.CANFD_ANGLE_STEERING or not lat_active:
return gain
cap = float(np.interp(abs(steering_angle_deg), EV9_HIGH_ANGLE_GAIN_BP, EV9_HIGH_ANGLE_GAIN_CAP_V))
return max(EV9_HIGH_ANGLE_GAIN_MIN, min(gain, cap))
def process_hud_alert(enabled, fingerprint, hud_control):
@@ -286,145 +52,41 @@ class CarController(CarControllerBase):
self.packer = CANPacker(dbc_names[Bus.pt])
self.angle_limit_counter = 0
self.VM = VehicleModel(CP)
self.BASELINE_VM = VehicleModel(get_baseline_safety_cp()) if CP.flags & HyundaiFlags.CANFD_ANGLE_STEERING else self.VM
self.angle_filter = FirstOrderFilter(0.0, 0.2, DT_CTRL)
self.accel_last = 0
self.apply_torque_last = 0
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
CS.redneck_last_sent_button = getattr(CS, "redneck_send_button", 0)
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)
]
CS.redneck_last_sent_button = getattr(CS, "redneck_send_button", 0)
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)
if not self.CP.flags & HyundaiFlags.CANFD_ANGLE_STEERING:
self.params = CarControllerParams(self.CP, CS.out.vEgoRaw)
self.params = CarControllerParams(self.CP, CS.out.vEgoRaw)
apply_angle = CS.out.steeringAngleDeg
if self.CP.flags & HyundaiFlags.CANFD_ANGLE_STEERING:
v_ego_raw = CS.out.vEgoRaw
desired_angle = float(np.clip(actuators.steeringAngleDeg,
-self.params.ANGLE_LIMITS.STEER_ANGLE_MAX,
self.params.ANGLE_LIMITS.STEER_ANGLE_MAX))
self.angle_filter.update_alpha(get_angle_smoothing_alpha(self.CP, CS.out.vEgo))
desired_angle = self.angle_filter.update(desired_angle)
apply_angle = apply_steer_angle_limits_vm(desired_angle, self.apply_angle_last, v_ego_raw,
apply_angle = apply_steer_angle_limits_vm(desired_angle, self.apply_angle_last, CS.out.vEgoRaw,
CS.out.steeringAngleDeg, CC.latActive, self.params, self.VM)
if str(self.CP.carFingerprint) != ANGLE_SAFETY_BASELINE_MODEL:
apply_angle = apply_steer_angle_limits_vm(apply_angle or desired_angle, self.apply_angle_last, v_ego_raw,
CS.out.steeringAngleDeg, CC.latActive, self.params, self.BASELINE_VM)
if CS.out.steeringPressed and abs(CS.out.steeringTorque) > self.params.STEER_THRESHOLD:
apply_torque = self.params.ANGLE_MIN_TORQUE_REDUCTION_GAIN
elif CC.latActive and CS.out.vEgoRaw < 0.3:
apply_torque = self.params.ANGLE_ACTIVE_TORQUE_REDUCTION_GAIN
else:
apply_torque = self.params.ANGLE_MAX_TORQUE_REDUCTION_GAIN if CC.latActive else 0.0
apply_torque = compute_torque_reduction_gain(CS.out.steeringTorque, v_ego_raw, CC.latActive, self.apply_torque_last)
apply_torque = apply_ev9_high_angle_gain_cap(self.CP, apply_torque, CS.out.steeringAngleDeg, CC.latActive)
apply_steer_req = CC.latActive and apply_torque != 0.0
apply_steer_req = CC.latActive and apply_torque > 0.0
torque_fault = False
if apply_angle is None:
apply_torque = 0
apply_angle = CS.out.steeringAngleDeg
apply_steer_req = False
self.apply_angle_last = apply_angle
if not CC.latActive:
self.apply_angle_last = float(np.clip(CS.out.steeringAngleDeg,
-self.params.ANGLE_LIMITS.STEER_ANGLE_MAX,
self.params.ANGLE_LIMITS.STEER_ANGLE_MAX))
self.angle_filter.x = self.apply_angle_last
else:
# steering torque
new_torque = int(round(actuators.torque * self.params.STEER_MAX))
@@ -445,11 +107,9 @@ class CarController(CarControllerBase):
self.apply_torque_last = apply_torque
# accel + longitudinal
accel_cmd = float(np.clip(actuators.accel, CarControllerParams.ACCEL_MIN, CarControllerParams.ACCEL_MAX))
accel = accel_cmd
accel = float(np.clip(actuators.accel, CarControllerParams.ACCEL_MIN, CarControllerParams.ACCEL_MAX))
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)
CS.redneck_last_sent_button = 0
can_sends = []
@@ -462,49 +122,12 @@ 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_egmp_dynamic_long_tuning = egmp_dynamic_longitudinal_tuning(self.CP) and self.long_active_ecu and \
actuators.longControlState in (LongCtrlState.starting, LongCtrlState.pid, LongCtrlState.stopping)
if should_reset_ev6_gt_line_longitudinal_tuning(self.CP, actuators.longControlState):
self._ioniq_6_long_tuning = reset_ev6_gt_line_longitudinal_tuning(self._ioniq_6_long_tuning, self.CP,
actuators.longControlState)
elif use_egmp_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_egmp_smoothed_accel = use_egmp_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 use_egmp_dynamic_long_tuning:
if use_egmp_smoothed_accel:
accel = self._ioniq_6_long_tuning.actual_accel
stopping = self._ioniq_6_long_tuning.stopping
else:
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 | HyundaiFlags.CAN_CANFD_BLENDED) else 0
addr, bus = 0x7d0, self.CAN.ECAN if self.CP.flags & HyundaiFlags.CANFD 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))
@@ -515,11 +138,11 @@ class CarController(CarControllerBase):
# *** CAN/CAN FD specific ***
if self.CP.flags & HyundaiFlags.CANFD:
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))
can_sends.extend(self.create_canfd_msgs(apply_steer_req, apply_torque, apply_angle, set_speed_in_units, accel,
stopping, hud_control, CS, CC))
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, lfa_icon))
stopping, hud_control, actuators, CS, CC))
new_actuators = actuators.as_builder()
if self.CP.flags & HyundaiFlags.CANFD_ANGLE_STEERING:
@@ -534,24 +157,17 @@ 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, lfa_icon):
def create_can_msgs(self, apply_steer_req, apply_torque, torque_fault, set_speed_in_units, accel, stopping, hud_control, actuators, CS, CC):
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)
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))
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:
@@ -564,163 +180,66 @@ 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
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))
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, self.frame, self.CP, lfa_icon))
can_sends.append(hyundaican.create_lfahda_mfc(self.packer, CC.enabled))
# 5 Hz ACC options
if self.frame % 20 == 0 and self.long_active_ecu and not can_canfd_blended:
if self.frame % 20 == 0 and self.long_active_ecu:
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 and not can_canfd_blended:
if self.frame % 50 == 0 and self.long_active_ecu:
can_sends.append(hyundaican.create_frt_radar_opt(self.packer))
return can_sends
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):
def create_canfd_msgs(self, apply_steer_req, apply_torque, apply_angle, set_speed_in_units, accel, stopping, hud_control, CS, CC):
can_sends = []
lka_steering = self.CP.flags & HyundaiFlags.CANFD_LKA_STEERING
lka_steering_long = lka_steering and self.long_active_ecu
ccnc_non_hda2 = self.CP.flags & HyundaiFlags.CCNC and not lka_steering
use_egmp_dynamic_long_tuning = egmp_dynamic_longitudinal_tuning(self.CP) and self.long_active_ecu and \
CC.actuators.longControlState in (LongCtrlState.starting, LongCtrlState.pid, LongCtrlState.stopping)
use_egmp_smoothed_accel = use_egmp_dynamic_long_tuning and (
CC.actuators.accel >= self._ioniq_6_long_tuning.actual_accel or
self._ioniq_6_long_tuning.launch_active or
self._ioniq_6_long_tuning.stopping
)
# steering control
preserve_stock_lkas = bool(self.CP.flags & HyundaiFlags.CANFD_LKA_STEERING) and not self.long_active_ecu
steering_msg_active = apply_steer_req
if self.CP.carFingerprint == CAR.KIA_EV9 and self.CP.flags & HyundaiFlags.CANFD_ANGLE_STEERING:
# EV9 faults if the angle-steering status drops inactive during torque limiting.
# Hold the angle status active while lateral is active; VM/safety limits handle actuation.
steering_msg_active = CC.latActive
can_sends.extend(hyundaicanfd.create_steering_messages(self.packer, self.CP, self.CAN, CC.enabled,
steering_msg_active, apply_torque, apply_angle,
CS.stock_lfa_msg,
CS.stock_lkas_msg if preserve_stock_lkas else None,
lka_icon=lka_icon))
apply_steer_req, apply_torque, apply_angle))
# prevent LFA from activating on LKA steering cars by sending "no lane lines detected" to ADAS ECU
suppress_lfa = bool(lka_steering)
if self.frame % 5 == 0 and suppress_lfa:
if self.frame % 5 == 0 and lka_steering:
can_sends.append(hyundaicanfd.create_suppress_lfa(self.packer, self.CAN, CS.lfa_block_msg,
self.CP.flags & HyundaiFlags.CANFD_LKA_STEERING_ALT))
# LFA and HDA icons
if self.frame % 5 == 0 and (not lka_steering or lka_steering_long):
if ccnc_non_hda2:
can_sends.extend(hyundaicanfd.create_ccnc(self.packer, self.CAN, self.long_active_ecu, CC.enabled, CC.hudControl,
CC.leftBlinker, CC.rightBlinker, CS.msg_161, CS.msg_162, CS.msg_1b5,
CS.is_metric, CS.out, CS.out.cruiseState.available, lfa_icon))
else:
can_sends.append(hyundaicanfd.create_lfahda_cluster(self.packer, self.CAN, CC.enabled, CS.stock_lfahda_cluster_msg,
lfa_icon=lfa_icon))
can_sends.append(hyundaicanfd.create_lfahda_cluster(self.packer, self.CAN, CC.enabled))
# 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))
# Ioniq 5/6: front radar treats ADAS_DRV's 0x100 broadcast as its host heartbeat
# and stops publishing object tracks when it disappears. Spoof it periodically on
# PT bus so the radar keeps tracking.
if self.CP.carFingerprint in CANFD_RADAR_LIVE_LONGITUDINAL_CAR and self.frame % 4 == 0:
can_sends.append(hyundaicanfd.create_accelerator_brake_alt_spoof(0, self.frame // 4, CS.out.brakePressed,
CS.out.gasPressed, self.CP.carFingerprint))
elif not ccnc_non_hda2:
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 use_egmp_dynamic_long_tuning:
if use_egmp_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, cruise_info=CS.cruise_info if ccnc_non_hda2 else None,
**acc_kwargs))
set_speed_in_units, hud_control))
self.accel_last = accel
else:
# button presses
if (self.frame - self.last_button_frame) * DT_CTRL > 0.25:
# 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:
# cruise cancel
if CC.cruiseControl.cancel:
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
@@ -738,7 +257,5 @@ 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
+35 -274
View File
@@ -7,8 +7,7 @@ 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, HyundaiStarPilotSafetyFlags, CAR, DBC, Buttons, CarControllerParams, \
hyundai_cancel_button_enables_cruise, ALT_BUS_LDA_BUTTON_CARS, ALT_BUS_LDA_BUTTON_SWL_STAT_CARS
from opendbc.car.hyundai.values import HyundaiFlags, HyundaiStarPilotFlags, CAR, DBC, Buttons, CarControllerParams
from opendbc.car.interfaces import CarStateBase
ButtonType = structs.CarState.ButtonEvent.Type
@@ -22,19 +21,6 @@ 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
ALT_BUS_LDA_BUTTON_BURST_DEBOUNCE_NS = int(1.3e9)
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):
@@ -48,19 +34,6 @@ 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]:
@@ -75,22 +48,9 @@ class CarState(CarStateBase):
self.cruise_buttons: deque = deque([Buttons.NONE] * PREV_BUTTON_SAMPLES, maxlen=PREV_BUTTON_SAMPLES)
self.main_buttons: deque = deque([Buttons.NONE] * PREV_BUTTON_SAMPLES, maxlen=PREV_BUTTON_SAMPLES)
self.lda_button = 0
self.sonata_hybrid_lkas_source = None
self.sonata_hybrid_lkas_sources = {
"bcm": 0,
"clu13": 0,
"swl_stat": 0,
}
self.lda_button_raw = 0
self.lda_button_raw_initialized = False
self.lda_button_last_raw_rise_ts_nanos = 0
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 \
@@ -118,24 +78,6 @@ class CarState(CarStateBase):
self.buttons_counter = 0
self.cruise_info = {}
self.msg_161 = {}
self.msg_162 = {}
self.msg_1b5 = {}
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
@@ -149,109 +91,9 @@ 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 get_alt_bus_lda_button_raw_state(self, cp_source: CANParser) -> tuple[int, int]:
if self.CP.carFingerprint in ALT_BUS_LDA_BUTTON_SWL_STAT_CARS:
return int(cp_source.vl["CLU13"]["CF_Clu_SWL_Stat"] == 4), cp_source.ts_nanos["CLU13"]["CF_Clu_SWL_Stat"]
return int(cp_source.vl["CLU13"]["CF_Clu_LdwsLkasSW"]), cp_source.ts_nanos["CLU13"]["CF_Clu_LdwsLkasSW"]
def create_alt_bus_lda_button_events(self, cp_source: CANParser) -> list[structs.CarState.ButtonEvent]:
raw_lda_button, raw_lda_button_ts_nanos = self.get_alt_bus_lda_button_raw_state(cp_source)
button_events: list[structs.CarState.ButtonEvent] = []
if not self.lda_button_raw_initialized:
self.lda_button_raw_initialized = True
self.lda_button_raw = raw_lda_button
return button_events
# Some alt-bus LKAS button layouts pulse several times per physical press burst.
# Collapse each burst into a single synthetic press/release pair.
if raw_lda_button and not self.lda_button_raw:
if self.lda_button_last_raw_rise_ts_nanos == 0 or \
raw_lda_button_ts_nanos - self.lda_button_last_raw_rise_ts_nanos > ALT_BUS_LDA_BUTTON_BURST_DEBOUNCE_NS:
button_events = [
structs.CarState.ButtonEvent(pressed=True, type=ButtonType.lkas),
structs.CarState.ButtonEvent(pressed=False, type=ButtonType.lkas),
]
self.lda_button_last_raw_rise_ts_nanos = raw_lda_button_ts_nanos
self.lda_button_raw = raw_lda_button
return button_events
def create_lkas_button_events(self, cp: CANParser, prev_lda_button: int) -> list[structs.CarState.ButtonEvent]:
if self.CP.carFingerprint == CAR.HYUNDAI_SONATA_HYBRID:
self.lda_button = self.get_sonata_hybrid_lkas_button_state(cp)
# Some classic HKG platforms publish the LKAS button on the cluster bus instead of BCM_PO_11.
elif cp.ts_nanos["CLU13"]["CF_Clu_LdwsLkasSW"] > 0:
self.lda_button = int(cp.vl["CLU13"]["CF_Clu_LdwsLkasSW"])
elif cp.ts_nanos["BCM_PO_11"]["LDA_BTN"] > 0:
self.lda_button = int(cp.vl["BCM_PO_11"]["LDA_BTN"])
else:
self.lda_button = 0
return create_button_events(self.lda_button, prev_lda_button, {1: ButtonType.lkas})
def get_sonata_hybrid_lkas_button_state(self, cp: CANParser) -> int:
source_states = {
"bcm": int(cp.vl["BCM_PO_11"]["LDA_BTN"]) if cp.ts_nanos["BCM_PO_11"]["LDA_BTN"] > 0 else 0,
"clu13": int(cp.vl["CLU13"]["CF_Clu_LdwsLkasSW"]) if cp.ts_nanos["CLU13"]["CF_Clu_LdwsLkasSW"] > 0 else 0,
"swl_stat": int(cp.vl["CLU13"]["CF_Clu_SWL_Stat"] == 4) if cp.ts_nanos["CLU13"]["CF_Clu_SWL_Stat"] > 0 else 0,
}
changed_sources = [source for source, state in source_states.items() if state != self.sonata_hybrid_lkas_sources[source]]
active_sources = [source for source, state in source_states.items() if state]
selected_source = None
if self.sonata_hybrid_lkas_source in changed_sources:
selected_source = self.sonata_hybrid_lkas_source
elif active_sources:
selected_source = active_sources[0]
elif changed_sources:
selected_source = changed_sources[0]
elif self.sonata_hybrid_lkas_source is not None:
selected_source = self.sonata_hybrid_lkas_source
self.sonata_hybrid_lkas_sources.update(source_states)
if selected_source is not None:
self.sonata_hybrid_lkas_source = selected_source
return source_states[selected_source]
return 0
def update(self, can_parsers, starpilot_toggles) -> structs.CarState:
cp = can_parsers[Bus.pt]
cp_cam = can_parsers[Bus.cam]
cp_alt = can_parsers.get(Bus.alt)
if self.CP.flags & HyundaiFlags.CANFD:
return self.update_canfd(can_parsers)
@@ -305,22 +147,22 @@ class CarState(CarStateBase):
ret.cruiseState.standstill = False
ret.cruiseState.nonAdaptive = False
elif no_scc:
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
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
ret.cruiseState.standstill = False
ret.cruiseState.nonAdaptive = False
ret.cruiseState.speed = cp.vl[cruise_speed_msg][cruise_speed_sig] * speed_conv
if 0 < cruise_set_speed < 255:
ret.cruiseState.speed = cruise_set_speed * speed_conv
else:
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.available = cp_cruise.vl["SCC11"]["MainMode_ACC"] == 1
ret.cruiseState.enabled = cp_cruise.vl["SCC12"]["ACCMode"] != 0
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"])
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
# TODO: Find brake pressure
ret.brake = 0
@@ -356,23 +198,14 @@ 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) 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 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 self.CP.enableBsm:
ret.leftBlindspot = cp.vl["LCA11"]["CF_Lca_IndLeft"] != 0
@@ -385,17 +218,14 @@ class CarState(CarStateBase):
prev_cruise_buttons = self.cruise_buttons[-1]
prev_main_buttons = self.main_buttons[-1]
prev_lda_button = self.lda_button
lkas_button_events = []
self.cruise_buttons.extend(cp.vl_all["CLU11"]["CF_Clu_CruiseSwState"])
self.main_buttons.extend(cp.vl_all["CLU11"]["CF_Clu_CruiseSwMain"])
if self.CP.carFingerprint in ALT_BUS_LDA_BUTTON_CARS and cp_alt is not None and self.get_alt_bus_lda_button_raw_state(cp_alt)[1] > 0:
lkas_button_events = self.create_alt_bus_lda_button_events(cp_alt)
else:
lkas_button_events = self.create_lkas_button_events(cp, prev_lda_button)
if self.CP.flags & HyundaiFlags.HAS_LDA_BUTTON:
self.lda_button = cp.vl["BCM_PO_11"]["LDA_BTN"]
ret.buttonEvents = [*self.create_cruise_button_events(self.cruise_buttons[-1], prev_cruise_buttons),
ret.buttonEvents = [*create_button_events(self.cruise_buttons[-1], prev_cruise_buttons, BUTTONS_DICT),
*create_button_events(self.main_buttons[-1], prev_main_buttons, {1: ButtonType.mainCruise}),
*lkas_button_events]
*create_button_events(self.lda_button, prev_lda_button, {1: ButtonType.lkas})]
ret.blockPcmEnable = not self.recent_button_interaction()
@@ -449,41 +279,17 @@ class CarState(CarStateBase):
ret.steeringPressed = self.update_steering_pressed(abs(ret.steeringTorque) > self.params.STEER_THRESHOLD, 5)
ret.steerFaultTemporary = cp.vl["MDPS"]["LKA_FAULT"] != 0
ccnc_non_hda2 = self.CP.flags & HyundaiFlags.CCNC and not self.CP.flags & HyundaiFlags.CANFD_LKA_STEERING
if ccnc_non_hda2:
self.msg_161 = copy.copy(cp_cam.vl["CCNC_0x161"])
self.msg_162 = copy.copy(cp_cam.vl["CCNC_0x162"])
self.msg_1b5 = copy.copy(cp_cam.vl["FR_CMR_03_50ms"])
cp_cruise_info = cp_cam if self.CP.flags & HyundaiFlags.CANFD_CAMERA_SCC else cp
self.cruise_info = copy.copy(cp_cruise_info.vl["SCC_CONTROL"])
use_alt_lamp = cp.vl["BLINKERS"]["USE_ALT_LAMP"] == 1 or bool(self.CP.flags & HyundaiFlags.CCNC)
left_blinker_sig, right_blinker_sig = self.get_canfd_blinker_sig_names(self.CP.carFingerprint, use_alt_lamp)
left_blinker_sig, right_blinker_sig = self.get_canfd_blinker_sig_names(self.CP.carFingerprint,
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 = (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)
elif self.CP.flags & HyundaiFlags.CCNC:
ret.leftBlindspot = bool(cp.vl["BLINDSPOTS_REAR_CORNERS"]["BCW_LtIndSta"])
ret.rightBlindspot = bool(cp.vl["BLINDSPOTS_REAR_CORNERS"]["BCW_RtIndSta"])
ret.leftBlindspot = cp.vl["BLINDSPOTS_REAR_CORNERS"]["LEFT_MB"] != 0
ret.rightBlindspot = cp.vl["BLINDSPOTS_REAR_CORNERS"]["MORE_LEFT_PROB"] != 0
else:
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"]
ret.leftBlindspot = cp.vl["BLINDSPOTS_REAR_CORNERS"]["FL_INDICATOR"] != 0
ret.rightBlindspot = cp.vl["BLINDSPOTS_REAR_CORNERS"]["FR_INDICATOR"] != 0
# cruise state
# CAN FD cars enable on main button press, set available if no TCS faults preventing engagement
@@ -506,6 +312,7 @@ 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
@@ -515,32 +322,15 @@ 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 = [*self.create_cruise_button_events(self.cruise_buttons[-1], prev_cruise_buttons),
ret.buttonEvents = [*create_button_events(self.cruise_buttons[-1], prev_cruise_buttons, BUTTONS_DICT),
*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})]
@@ -560,10 +350,6 @@ 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):
@@ -577,26 +363,11 @@ 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
if CP.flags & HyundaiFlags.CCNC:
cam_msgs += [
("CCNC_0x161", 0),
("CCNC_0x162", 0),
]
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),
@@ -606,17 +377,7 @@ class CarState(CarStateBase):
if CP.flags & HyundaiFlags.CANFD:
return self.get_can_parsers_canfd(CP)
msgs = [
("BCM_PO_11", 0),
("CLU13", 0),
]
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
parsers = {
Bus.pt: CANParser(DBC[CP.carFingerprint][Bus.pt], msgs, 0),
return {
Bus.pt: CANParser(DBC[CP.carFingerprint][Bus.pt], [], 0),
Bus.cam: CANParser(DBC[CP.carFingerprint][Bus.pt], [], 2),
}
if CP.carFingerprint in ALT_BUS_LDA_BUTTON_CARS:
parsers[Bus.alt] = CANParser(DBC[CP.carFingerprint][Bus.pt], [("CLU13", 0)], 1)
return parsers
@@ -38,14 +38,6 @@ FW_VERSIONS = {
b'\xf1\x00IGhe SCC FHCUP 1.00 1.02 99110-M9000 ',
],
},
CAR.HYUNDAI_AZERA_HEV_7TH_GEN: {
(Ecu.fwdCamera, 0x7c4, None): [
b'\xf1\x00GN7HMFC AT KOR LHD 1.00 1.01 99211-N1110 240423',
],
(Ecu.fwdRadar, 0x7d0, None): [
b'\xf1\x00GN7_ RDR ----- 1.00 1.00 99110-N1100 ',
],
},
CAR.HYUNDAI_GENESIS: {
(Ecu.fwdCamera, 0x7c4, None): [
b'\xf1\x00DH LKAS 1.1 -150210',
@@ -475,33 +467,6 @@ 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 ',
@@ -615,28 +580,15 @@ FW_VERSIONS = {
b'\xf1\x00CD ESC \x0b 101 \x10\x03 58910-J7AC0',
],
},
CAR.KIA_XCEED_PHEV: {
(Ecu.fwdRadar, 0x7d0, None): [
b'\xf1\x00CDph SCC F-CUP 1.00 1.01 99110-CR100 ',
],
(Ecu.eps, 0x7d4, None): [
b'\xf1\x00CDe MDPS C 1.00 1.01 56310-XX000 4CDHC101',
],
(Ecu.fwdCamera, 0x7c4, None): [
b'\xf1\x00CD2 LKAS AT EUR LHD 1.00 1.01 99211-CR010 621',
],
},
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',
],
@@ -760,27 +712,6 @@ FW_VERSIONS = {
],
(Ecu.fwdCamera, 0x7c4, None): [
b'\xf1\x00SX2EMFC AT KOR LHD 1.00 1.00 99211-BF000 230410',
b'\xf1\x00SX2EMFC AT USA LHD 1.00 1.02 99211-BF000 230823',
],
},
CAR.HYUNDAI_KONA_2ND_GEN: {
(Ecu.fwdCamera, 0x7c4, None): [
b'\xf1\x00SX2 MFC AT USA LHD 1.00 1.03 99211-BE000 230517',
b'\xf1\x00SX2 MFC AT USA LHD 1.00 1.07 99211-BE000 240611',
],
(Ecu.fwdRadar, 0x7d0, None): [
b'\xf1\x00SX2_ RDR ----- 1.00 1.02 99110-BE000 ',
b'\xf1\x00SX2_ RDR ----- 1.00 1.02 99110-BE500 ',
],
},
CAR.HYUNDAI_KONA_HEV_2ND_GEN: {
(Ecu.fwdCamera, 0x7c4, None): [
b'\xf1\x00SX2HMFC AT AUS RHD 1.00 1.00 99211-BE001 241015',
b'\xf1\x00SX2HMFC AT EUR RHD 1.00 1.04 99211-BE000 231010',
],
(Ecu.fwdRadar, 0x7d0, None): [
b'\xf1\x00SX2_ RDR ----- 1.00 1.02 99110-BE000 ',
b'\xf1\x00SX2_ RDR ----- 1.00 1.02 99110-BE500 ',
],
},
CAR.KIA_NIRO_EV: {
@@ -1135,7 +1066,6 @@ 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: {
@@ -1199,186 +1129,6 @@ FW_VERSIONS = {
b'\xf1\x00NQ5__ 1.00 1.04 99110CH100 ',
],
},
CAR.HYUNDAI_SANTA_FE_HEV_5TH_GEN: {
(Ecu.fwdCamera, 0x7c4, None): [
b'\xf1\x00MX5HMFC AT CAN LHD 1.00 1.07 99211-P6000 231218',
b'\xf1\x00MX5HMFC AT KOR LHD 1.00 1.07 99211-P6000 231218',
b'\xf1\x00MX5HMFC AT USA LHD 1.00 1.06 99211-R6000 231218',
b'\xf1\x00MX5HMFC AT USA LHD 1.00 1.09 99211-R6200 250519',
],
(Ecu.fwdRadar, 0x7d0, None): [
b'\xf1\x00MX5_ RDR ----- 1.00 1.01 99110-P6000 ',
b'\xf1\x00MX5_ RDR ----- 1.00 1.01 99110-R6000 ',
],
},
CAR.HYUNDAI_SONATA_2024: {
(Ecu.fwdCamera, 0x7c4, None): [
b'\xf1\x00DN8 MFC AT KOR LHD 1.00 1.01 99211-L1800 230512',
b'\xf1\x00DN8 MFC AT USA LHD 1.00 1.01 99211-L1800 230512',
],
(Ecu.fwdRadar, 0x7d0, None): [
b'\xf1\x00DN8_ RDR ----- 1.00 1.00 99110-L1800 ',
],
},
CAR.HYUNDAI_SONATA_HEV_2024: {
(Ecu.fwdCamera, 0x7c4, None): [
b'\xf1\x00DN8HMFC AT KOR LHD 1.00 1.01 99211-L1800 230512',
b'\xf1\x00DN8HMFC AT USA LHD 1.00 1.01 99211-L1800 230512',
],
(Ecu.fwdRadar, 0x7d0, None): [
b'\xf1\x00DN8_ RDR ----- 1.00 1.00 99110-L1800 ',
],
},
CAR.HYUNDAI_IONIQ_5_PE: {
(Ecu.fwdRadar, 0x7d0, None): [
b'\xf1\x00NE__ RDR ----- 1.00 1.00 99110-PI000 ',
b'\xf1\x00NE__ RDR ----- 1.00 1.01 99110-GI500 ',
],
(Ecu.fwdCamera, 0x7c4, None): [
b'\xf1\x00NE MFC AT USA LHD 1.00 1.01 99211-PI000 240905',
b'\xf1\x00NE MFC AT EUR LHD 1.00 1.03 99211-GI500 240809',
b'\xf1\x00NE MFC AT USA LHD 1.00 1.00 99211-PI010 250407',
b'\xf1\x00NE MFC AT EUR LHD 1.00 1.00 99211-GI510 250513',
],
},
CAR.HYUNDAI_IONIQ_5_N: {
(Ecu.fwdRadar, 0x7d0, None): [
b'\xf1\x00NE1N RDR ----- 1.00 1.00 99110-NI000 ',
],
(Ecu.fwdCamera, 0x7c4, None): [
b'\xf1\x00NE1NMFC AT KOR LHD 1.00 1.04 99211-NI000 231219',
b'\xf1\x00NE1NMFC AT KOR LHD 1.00 1.00 99211-NI010 240712',
b'\xf1\x00NE1NMFC AT USA LHD 1.00 1.04 99211-NI000 231219',
],
},
CAR.HYUNDAI_IONIQ_9: {
(Ecu.fwdRadar, 0x7d0, None): [
b'\xf1\x00MEev RDR ----- 1.00 1.00 99110-GO000 ',
],
(Ecu.fwdCamera, 0x7c4, None): [
b'\xf1\x00ME MFC AT KOR LHD 1.00 1.00 99211-GO000 241007',
b'\xf1\x00ME MFC AT KOR LHD 1.00 1.01 99211-GO000 250103',
],
},
CAR.HYUNDAI_TUCSON_2025: {
(Ecu.fwdCamera, 0x7c4, None): [
b'\xf1\x00NX4 FR_CMR AT USA LHD 1.00 1.01 99211-N7050 C5A',
],
(Ecu.fwdRadar, 0x7d0, None): [
b'\xf1\x00NX4__ 1.00 1.03 99110N7100 ',
],
},
CAR.HYUNDAI_TUCSON_HEV_2025: {
(Ecu.fwdCamera, 0x7c4, None): [
b'\xf1\x00NX4 FR_CMR AT USA LHD 1.00 1.00 99211-N7030 C55',
b'\xf1\x00NX4 FR_CMR AT EUR LHD 1.00 1.00 99211-N7030 C55',
],
(Ecu.fwdRadar, 0x7d0, None): [
b'\xf1\x00NX4__ 1.00 1.02 99110N7000 ',
b'\xf1\x00NX4__ 1.00 1.02 99110N7100 ',
b'\xf1\x00NX4__ 1.00 1.03 99110N7100 ',
],
},
CAR.HYUNDAI_TUCSON_PHEV_2025: {
(Ecu.fwdCamera, 0x7c4, None): [
b'\xf1\x00NX4 FR_CMR AT CAN LHD 1.00 1.00 99211-N7030 C55',
],
(Ecu.fwdRadar, 0x7d0, None): [
b'\xf1\x00NX4__ 1.00 1.02 99110N7100 ',
],
},
CAR.HYUNDAI_SANTA_CRUZ_2025: {
(Ecu.fwdCamera, 0x7c4, None): [
b'\xf1\x00NX4 FR_CMR AT USA LHD 1.00 1.00 99211-N7030 C55',
],
(Ecu.fwdRadar, 0x7d0, None): [
b'\xf1\x00NX4__ 1.00 1.00 99110K5500 ',
],
},
CAR.KIA_K4_2025: {
(Ecu.fwdCamera, 0x7c4, None): [
b'\xf1\x00CL4 MFC AT CAN LHD 1.00 1.02 99210-GG000 240708',
b'\xf1\x00CL4 MFC AT USA LHD 1.00 1.02 99210-GG000 240708',
],
(Ecu.fwdRadar, 0x7d0, None): [
b'\xf1\x00CL4_ RDR ----- 1.00 1.01 99110-GG000 ',
b'\xf1\x00CL4_ RDR ----- 1.00 1.01 99110-GG100 ',
],
},
CAR.KIA_K5_2025: {
(Ecu.fwdCamera, 0x7c4, None): [
b'\xf1\x00DL3 MFC AT USA LHD 1.00 1.04 99210-L2500 240117',
],
(Ecu.fwdRadar, 0x7d0, None): [
b'\xf1\x00DL3_ RDR ----- 1.00 1.01 99110-L2500 ',
],
},
CAR.KIA_SPORTAGE_2026: {
(Ecu.fwdCamera, 0x7c4, None): [
b'\xf1\x00NQ51.011.021.012551000HKP_NQ524_50509099211P1110',
],
(Ecu.fwdRadar, 0x7d0, None): [
b'\xf1\x00NQ5__ 1.00 1.04 99110P1100 ',
],
},
CAR.KIA_SORENTO_2024: {
(Ecu.fwdCamera, 0x7c4, None): [
b'\xf1\x00MQ4 MFC AT AUS RHD 1.01 1.04 99210-P2550 231127',
],
(Ecu.fwdRadar, 0x7d0, None): [
b'\xf1\x00MQ4_ RDR ----- 1.00 1.01 99110-P2500 ',
],
},
CAR.KIA_SORENTO_HEV_4TH_GEN_LFA2: {
(Ecu.fwdCamera, 0x7c4, None): [
b'\xf1\x00MQ4HMFC AT USA LHD 1.00 1.00 99210-P2600 250617',
],
(Ecu.fwdRadar, 0x7d0, None): [
b'\xf1\x00MQ4_ RDR ----- 1.00 1.01 99110-P2500 ',
],
},
CAR.KIA_EV6_2025: {
(Ecu.fwdRadar, 0x7d0, None): [
b'\xf1\x00CV__ RDR ----- 1.00 1.00 99110-XG500 ',
b'\xf1\x00CV__ RDR ----- 1.00 1.01 99110-CV500 ',
],
(Ecu.fwdCamera, 0x7c4, None): [
b'\xf1\x00CV MFC AT KOR LHD 1.00 1.01 99210-CV500 240405',
b'\xf1\x00CV MFC AT USA LHD 1.00 1.02 99210-XG500 241223',
],
},
CAR.KIA_EV9: {
(Ecu.fwdRadar, 0x7d0, None): [
b'\xf1\x00MV__ RDR ----- 1.00 1.02 99110-DO000 ',
b'\xf1\x00MV__ RDR ----- 1.00 1.03 99110-DO000 ',
b'\xf1\x00MV__ RDR ----- 1.00 1.04 99110-DO000 ',
b'\xf1\x00MV__ RDR ----- 1.00 1.02 99110-DO700 ',
],
(Ecu.fwdCamera, 0x7c4, None): [
b'\xf1\x00MV MFC AT KOR LHD 1.00 1.01 99211-DO000 230419',
b'\xf1\x00MV MFC AT USA LHD 1.00 1.02 99211-DO000 230616',
b'\xf1\x00MV MFC AT EUR LHD 1.00 1.02 99211-DO000 230616',
b'\xf1\x00MV MFC AT CAN LHD 1.00 1.00 99211-DO100 240403',
b'\xf1\x00MV MFC AT USA LHD 1.00 1.01 99211-XA000 241023',
b'\xf1\x00MV MFC AT CAN LHD 1.00 1.01 99211-DO100 241023',
],
},
CAR.GENESIS_GV70_ELECTRIFIED_2ND_GEN: {
(Ecu.fwdCamera, 0x7c4, None): [
b'\xf1\x00JK MFC AT USA LHD 1.00 1.03 99211-DS600 241125',
],
(Ecu.fwdRadar, 0x7d0, None): [
b'\xf1\x00JK__ RDR ----- 1.00 1.01 99110-DS500 ',
],
},
CAR.GENESIS_GV80_2025: {
(Ecu.fwdRadar, 0x7d0, None): [
b'\xf1\x00JX__ RDR ----- 1.00 1.03 99110-T6500 ',
],
(Ecu.fwdCamera, 0x7c4, None): [
b'\xf1\x00JX MFC AT USA LHD 1.00 1.03 99211-T6510 240124',
],
},
CAR.GENESIS_GV70_1ST_GEN: {
(Ecu.fwdCamera, 0x7c4, None): [
b'\xf1\x00JK1 MFC AT CAN LHD 1.00 1.02 99211-IY000 230627',
@@ -1532,109 +1282,4 @@ 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',
b'\xf1\x00OS MDPS C 1.00 1.05 56310/J9500 4OSDC105',
],
(Ecu.fwdCamera, 0x7c4, None): [
b'\xf1\x00OS9 LKAS AT USA LHD 1.00 1.00 95740-J9200 g30',
b'\xf1\x00OS9 LKAS AT AUS RHD 1.00 1.00 95740-J9200 g30',
],
(Ecu.fwdRadar, 0x7d0, None): [
b'\xf1\x00OS__ FCA --CUP 1.00 1.00 95655-J9100 ',
],
(Ecu.transmission, 0x7e1, None): [
b'\xf1\x006T6J0_C2\x00\x006T6K1051\x00\x00TOS4N20NS2\x00\x00\x00\x00',
b'\xf1\x006U2V0_C2\x00\x006U2V1051\x00\x00DOS4T16AS2\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',
],
},
}
+10 -133
View File
@@ -1,5 +1,4 @@
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)
@@ -39,7 +38,7 @@ def create_lkas11(packer, frame, CP, apply_torque, steer_req,
CAR.HYUNDAI_IONIQ_EV_2020, CAR.HYUNDAI_IONIQ_PHEV, CAR.KIA_SELTOS, CAR.HYUNDAI_ELANTRA_2021, CAR.GENESIS_G70_2020,
CAR.HYUNDAI_ELANTRA_HEV_2021, CAR.HYUNDAI_SONATA_HYBRID, CAR.HYUNDAI_KONA_EV, CAR.HYUNDAI_KONA_HEV, CAR.HYUNDAI_KONA_EV_2022,
CAR.HYUNDAI_SANTA_FE_2022, CAR.KIA_K5_2021, CAR.HYUNDAI_IONIQ_HEV_2022, CAR.HYUNDAI_SANTA_FE_HEV_2022,
CAR.HYUNDAI_SANTA_FE_PHEV_2022, CAR.KIA_STINGER_2022, CAR.KIA_K5_HEV_2020, CAR.KIA_CEED, CAR.KIA_XCEED_PHEV,
CAR.HYUNDAI_SANTA_FE_PHEV_2022, CAR.KIA_STINGER_2022, CAR.KIA_K5_HEV_2020, CAR.KIA_CEED,
CAR.HYUNDAI_AZERA_6TH_GEN, CAR.HYUNDAI_AZERA_HEV_6TH_GEN, CAR.HYUNDAI_CUSTIN_1ST_GEN, CAR.HYUNDAI_KONA_2022):
values["CF_Lkas_LdwsActivemode"] = int(left_lane) + (int(right_lane) << 1)
values["CF_Lkas_LdwsOpt_USM"] = 2
@@ -97,47 +96,6 @@ 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",
@@ -156,80 +114,15 @@ 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
if CP.flags & HyundaiFlags.CAMERA_SCC:
bus = 2
elif CP.flags & HyundaiFlags.CAN_CANFD_BLENDED:
bus = CanBus(CP).ECAN
else:
bus = 0
bus = 2 if CP.flags & HyundaiFlags.CAMERA_SCC else 0
return packer.make_can_msg("CLU11", bus, values)
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
def create_lfahda_mfc(packer, enabled):
values = {
"LFA_Icon_State": lfa_icon,
"LFA_Icon_State": 2 if enabled else 0,
}
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
return packer.make_can_msg("LFAHDA_MFC", 0, values)
def create_acc_commands(packer, enabled, accel, upper_jerk, idx, hud_control, set_speed, stopping, long_override, use_fca, CP):
@@ -256,10 +149,11 @@ def create_acc_commands(packer, enabled, accel, upper_jerk, idx, hud_control, se
"CR_VSM_Alive": idx % 0xF,
}
# Keep ESC/TCS happy on non-FCA cars without explicitly showing the disabled AEB icon.
# 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
if not use_fca:
scc12_values["CF_VSM_ConfMode"] = 1
scc12_values["AEB_Status"] = 2
scc12_values["AEB_Status"] = 1 # AEB disabled
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
@@ -285,7 +179,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": 2,
"FCA_Status": 1, # AEB disabled
}
fca11_dat = packer.make_can_msg("FCA11", 0, fca11_values)[1]
fca11_values["CR_FCA_ChkSum"] = hyundai_checksum(fca11_dat[:7])
@@ -309,7 +203,7 @@ def create_acc_opt(packer, CP):
if not (CP.flags & HyundaiFlags.CAMERA_SCC):
fca12_values = {
"FCA_DrvSetState": 2,
"FCA_USM": 2,
"FCA_USM": 1, # AEB disabled
}
commands.append(packer.make_can_msg("FCA12", 0, fca12_values))
@@ -321,20 +215,3 @@ 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
+25 -474
View File
@@ -1,7 +1,6 @@
import copy
import numpy as np
from opendbc.car import CanBusBase, CanData
from opendbc.car.common.conversions import Conversions as CV
from opendbc.car import CanBusBase
from opendbc.car.crc import CRC16_XMODEM
from opendbc.car.hyundai.values import HyundaiFlags
@@ -92,86 +91,29 @@ 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,
lfa_base_values=None, lkas_base_values=None, lka_icon=None):
if lka_icon is None:
lka_icon = 2 if enabled else 1
ev9_angle_lkas_alt = str(CP.carFingerprint) == "KIA_EV9" and CP.flags & HyundaiFlags.CANFD_ANGLE_STEERING and \
CP.flags & HyundaiFlags.CANFD_LKA_STEERING_ALT
control_values = {
def create_steering_messages(packer, CP, CAN, enabled, lat_active, apply_torque, apply_angle):
common_values = {
"LKA_MODE": 2,
"LKA_ICON": lka_icon,
"LKA_ICON": 2 if enabled else 1,
"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]
}
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
lkas_values = copy.copy(common_values)
lkas_values["LKA_AVAILABLE"] = 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]
lfa_values = copy.copy(common_values)
lfa_values["NEW_SIGNAL_1"] = 0
if CP.flags & HyundaiFlags.CANFD_ANGLE_STEERING and CP.flags & HyundaiFlags.CANFD_LKA_STEERING_ALT:
lkas_values["ADAS_StrAnglReqVal"] = apply_angle
lkas_values["LKAS_ANGLE_ACTIVE"] = 2 if lat_active else 1
lkas_values["ADAS_ACIAnglTqRedcGainVal"] = apply_torque if lat_active else 0.0
if ev9_angle_lkas_alt:
if lat_active:
lkas_values.update({
"LKA_MODE": 0,
"LKA_AVAILABLE": 3,
"LKA_WARNING": 0,
"LKA_ICON": lka_icon,
"FCA_SYSWARN": 0,
"TORQUE_REQUEST": 0,
"STEER_REQ": 0,
"LFA_BUTTON": 0,
"LKA_ASSIST": 0,
"DAMP_FACTOR": 100,
"HAS_LANE_SAFETY": 0,
})
elif lkas_base_values:
for signal in ("LKA_MODE", "LKA_AVAILABLE", "LKA_WARNING", "LKA_ICON", "FCA_SYSWARN",
"LFA_BUTTON", "LKA_ASSIST", "DAMP_FACTOR", "HAS_LANE_SAFETY"):
if signal in lkas_base_values:
lkas_values[signal] = lkas_base_values[signal]
lkas_values.update({
"TORQUE_REQUEST": 0,
"STEER_REQ": 0,
})
else:
lkas_values.update({
"LKA_MODE": 0,
"LKA_AVAILABLE": 0,
"LKA_WARNING": 0,
"LKA_ICON": lka_icon,
"FCA_SYSWARN": 0,
"TORQUE_REQUEST": 0,
"STEER_REQ": 0,
"LFA_BUTTON": 0,
"LKA_ASSIST": 0,
"DAMP_FACTOR": 100,
"HAS_LANE_SAFETY": 0,
})
# These signals overlap DAMP_FACTOR in the local DBC naming; omitting them
# preserves the stock angle-steering damping byte expected by the ADAS ECU.
lkas_values.pop("STEER_MODE", None)
lkas_values.pop("NEW_SIGNAL_2", None)
ret = []
if CP.flags & HyundaiFlags.CANFD_LKA_STEERING:
@@ -182,9 +124,6 @@ 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))
@@ -207,15 +146,12 @@ 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=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({
def create_buttons(packer, CP, CAN, cnt, btn):
values = {
"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)
@@ -252,403 +188,41 @@ 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, 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({
def create_lfahda_cluster(packer, CAN, enabled):
values = {
"HDA_ICON": 1 if enabled else 0,
"LFA_ICON": lfa_icon,
})
"LFA_ICON": 2 if enabled else 0,
}
return packer.make_can_msg("LFAHDA_CLUSTER", CAN.ECAN, values)
def create_ccnc(packer, CAN, openpilot_longitudinal, enabled, hud, left_blinker, right_blinker, msg_161, msg_162, msg_1b5,
is_metric, out, main_cruise_enabled, lfa_icon):
for fault in ("FAULT_LSS", "FAULT_HDA", "FAULT_DAS", "FAULT_LFA", "FAULT_DAW", "FAULT_ESS"):
msg_162[fault] = 0
if msg_161["ALERTS_2"] == 5:
msg_161.update({"ALERTS_2": 0, "SOUNDS_2": 0})
if msg_161["ALERTS_3"] == 17:
msg_161["ALERTS_3"] = 0
if msg_161["ALERTS_5"] in (2, 5):
msg_161["ALERTS_5"] = 0
if msg_161["SOUNDS_4"] == 2 and msg_161["LFA_ICON"] in (3, 0):
msg_161["SOUNDS_4"] = 0
lane_change_speed_min = 8.9408
any_blinker = left_blinker or right_blinker
curvature = {i: (31 if i == -1 else 13 - abs(i + 15)) if i < 0 else 15 + i for i in range(-15, 16)}
msg_161.update({
"DAW_ICON": 0,
"LKA_ICON": 0,
"LFA_ICON": 2 if lfa_icon else 0,
"CENTERLINE": 1 if lfa_icon else 0,
"LANELINE_CURVATURE": curvature.get(max(-15, min(int(out.steeringAngleDeg / 4.5), 15)), 14) if lfa_icon and not any_blinker else 15,
"LANELINE_LEFT": 0 if not lfa_icon else 1 if not hud.leftLaneVisible else 4 if hud.leftLaneDepart else 6 if any_blinker else 2,
"LANELINE_RIGHT": 0 if not lfa_icon else 1 if not hud.rightLaneVisible else 4 if hud.rightLaneDepart else 6 if any_blinker else 2,
"LCA_LEFT_ICON": 0 if not lfa_icon or out.vEgo < lane_change_speed_min else 1 if out.leftBlindspot else 2 if any_blinker else 4,
"LCA_RIGHT_ICON": 0 if not lfa_icon or out.vEgo < lane_change_speed_min else 1 if out.rightBlindspot else 2 if any_blinker else 4,
"LCA_LEFT_ARROW": 2 if left_blinker else 0,
"LCA_RIGHT_ARROW": 2 if right_blinker else 0,
})
if lfa_icon and any_blinker:
left_lane_raw = msg_1b5["Info_LftLnPosVal"]
right_lane_raw = msg_1b5["Info_RtLnPosVal"]
scale_per_m = 15 / 1.7
left_lane = abs(int(round(15 + (left_lane_raw - 1.7) * scale_per_m)))
right_lane = abs(int(round(15 + (right_lane_raw - 1.7) * scale_per_m)))
if msg_1b5["Info_LftLnQualSta"] not in (2, 3):
left_lane = 0
if msg_1b5["Info_RtLnQualSta"] not in (2, 3):
right_lane = 0
if left_lane_raw == -2.0248375:
left_lane = 30 - right_lane
if right_lane_raw == 2.0248375:
right_lane = 30 - left_lane
if left_lane_raw == right_lane_raw == 0:
left_lane = right_lane = 15
elif left_lane_raw == 0:
left_lane = 30 - right_lane
elif right_lane_raw == 0:
right_lane = 30 - left_lane
total = left_lane + right_lane
if total == 0:
left_lane = right_lane = 15
else:
left_lane = round((left_lane / total) * 30)
right_lane = 30 - left_lane
msg_161["LANELINE_LEFT_POSITION"] = left_lane
msg_161["LANELINE_RIGHT_POSITION"] = right_lane
if hud.leftLaneDepart or hud.rightLaneDepart:
msg_162["VIBRATE"] = 1
if openpilot_longitudinal:
if msg_161["ALERTS_3"] in (1, 2, 3, 4, 7, 8, 9, 10):
msg_161["ALERTS_3"] = 0
if msg_161["ALERTS_5"] == 4:
msg_161["ALERTS_5"] = 0
if msg_161["SOUNDS_3"] == 5:
msg_161["SOUNDS_3"] = 0
cruise_speed = round(out.vCruiseCluster * (1 if is_metric else CV.KPH_TO_MPH))
msg_161.update({
"SETSPEED": 3 if enabled else 1,
"SETSPEED_HUD": 0 if not main_cruise_enabled else 2 if enabled else 1,
"SETSPEED_SPEED": 255 if not main_cruise_enabled else (40 if is_metric else 25) if cruise_speed > (145 if is_metric else 90) else cruise_speed,
"DISTANCE": hud.leadDistanceBars,
"DISTANCE_SPACING": 0 if not main_cruise_enabled else 1 if enabled else 3,
"DISTANCE_LEAD": 0 if not main_cruise_enabled else 2 if enabled and hud.leadVisible else 1 if hud.leadVisible else 0,
"DISTANCE_CAR": 0 if not main_cruise_enabled else 2 if enabled else 1,
"SLA_ICON": 0,
"NAV_ICON": 0,
"TARGET": 0,
})
msg_162["LEAD"] = 0 if not main_cruise_enabled else 2 if enabled else 1
msg_162["LEAD_DISTANCE"] = msg_1b5["Longitudinal_Distance"]
return [packer.make_can_msg(msg, CAN.ECAN, values) for msg, values in (("CCNC_0x161", msg_161), ("CCNC_0x162", msg_162))]
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, cruise_info=None):
def create_acc_control(packer, CAN, enabled, accel_last, accel, stopping, gas_override, set_speed, hud_control):
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": main_mode_acc,
"MainMode_ACC": 1,
"StopReq": 1 if stopping else 0,
"aReqValue": a_val,
"aReqRaw": a_raw,
"VSetDis": set_speed,
"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,
"JerkLowerLimit": jerk if enabled else 1,
"JerkUpperLimit": 3.0,
"ACC_ObjDist": acc_obj_dist,
"ACC_ObjRelSpd": acc_obj_rel_spd,
"ObjValid": obj_valid,
"OBJ_STATUS": obj_status,
"ACC_ObjDist": 1,
"ObjValid": 0,
"OBJ_STATUS": 2,
"SET_ME_2": 0x4,
"SET_ME_3": 0x3,
"SET_ME_TMP_64": 0x64,
"DISTANCE_SETTING": hud_control.leadDistanceBars,
}
if cruise_info:
values.update({s: cruise_info[s] for s in ("ACC_ObjDist", "ACC_ObjRelSpd")})
return packer.make_can_msg("SCC_CONTROL", CAN.ECAN, values)
@@ -746,26 +320,3 @@ def hkg_can_fd_checksum(address: int, sig, d: bytearray) -> int:
elif len(d) == 32:
crc ^= 0x9F5B
return crc
# Ioniq 5/6 / HKG LKA-steering: ADAS_DRV broadcasts ACCELERATOR_BRAKE_ALT (0x100) on bus 0.
# The front radar uses this as its "host alive" heartbeat. When we disable ADAS_DRV the
# radar stops publishing real object tracks. Spoof this message ourselves with valid CRC
# and current pedal state so the radar keeps tracking.
# Length is 24 bytes on Ioniq 6 (DBC declares 32 for ICE Hyundais, but EV firmware uses 24).
# Byte templates captured from real ADAS broadcasts; only checksum, counter,
# brake, and accelerator bits are updated for the radar heartbeat.
_ACCEL_BRAKE_ALT_TEMPLATE = bytes.fromhex("000000020000fcff000000000020000055ff000068000000")
_KIA_EV9_ACCEL_BRAKE_ALT_TEMPLATE = bytes.fromhex("00000000ff006f00e80400001201030055ffff0000000000")
def create_accelerator_brake_alt_spoof(bus: int, counter: int, brake_pressed: bool, accelerator_pressed: bool,
car_fingerprint=None) -> CanData:
template = _KIA_EV9_ACCEL_BRAKE_ALT_TEMPLATE if str(car_fingerprint) == "KIA_EV9" else _ACCEL_BRAKE_ALT_TEMPLATE
d = bytearray(template)
d[2] = counter & 0xFF # COUNTER (bit 16, 8-bit)
d[4] = (d[4] & ~0x01) | (0x01 if brake_pressed else 0x00) # BRAKE_PRESSED (bit 32)
d[22] = (d[22] & ~0x01) | (0x01 if accelerator_pressed else 0x00) # ACCELERATOR_PEDAL_PRESSED (bit 176)
crc = hkg_can_fd_checksum(0x100, None, d)
d[0] = crc & 0xFF
d[1] = (crc >> 8) & 0xFF
return CanData(0x100, bytes(d), bus)
+33 -112
View File
@@ -1,19 +1,12 @@
import time
from opendbc.car import get_safety_config, structs, uds
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, CarControllerParams, \
from opendbc.car.hyundai.values import HyundaiFlags, CAR, DBC, \
CANFD_UNSUPPORTED_LONGITUDINAL_CAR, \
CANFD_SECURITYACCESS_CAR, \
CANFD_ANGLE_LONGITUDINAL_CAR, \
CANFD_RADAR_LIVE_LONGITUDINAL_CAR, \
RADAR_LIVE_LONGITUDINAL_CAR, \
UNSUPPORTED_LONGITUDINAL_CAR, HyundaiSafetyFlags, \
LEGACY_LONGITUDINAL_CAR, \
HyundaiStarPilotSafetyFlags, \
hyundai_cancel_button_enables_cruise, \
kia_ev6_gt_line_longitudinal_tuning
from opendbc.car.hyundai.radar_interface import get_radar_track_config, radar_tracks_available
from opendbc.car.interfaces import CarInterfaceBase, ACCEL_MIN
UNSUPPORTED_LONGITUDINAL_CAR, HyundaiSafetyFlags
from opendbc.car.hyundai.radar_interface import RADAR_START_ADDR
from opendbc.car.interfaces import CarInterfaceBase
from opendbc.car.disable_ecu import disable_ecu, ecu_log
from opendbc.car.hyundai.carcontroller import CarController
from opendbc.car.hyundai.carstate import CarState
@@ -27,44 +20,6 @@ ENABLE_BUTTONS = (ButtonType.accelCruise, ButtonType.decelCruise, ButtonType.can
# Track when ECU disable happened - used to permanently suppress CAN errors from disabled ECU
ECU_DISABLE_TIMESTAMP = 0.0
KONA_NON_SCC_FCA_RADAR_ADDR = 0x602
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_kia_ev6_gt_line_longitudinal_params(ret: structs.CarParams) -> None:
ret.startAccel = 1.4
ret.longitudinalActuatorDelay = 0.35
ret.vEgoStarting = 0.5
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
def detect_kona_non_scc_radar_fca(candidate, fingerprint, car_fw) -> bool:
if candidate != CAR.HYUNDAI_KONA_NON_SCC:
return False
if any(fw.ecu == Ecu.fwdRadar for fw in car_fw):
return True
# Some non-SCC Kona trims have FCA radar tracks without SCC. Use PT FCA11
# status on those cars; camera-bus FCA11 is not continuously published.
return KONA_NON_SCC_FCA_RADAR_ADDR in fingerprint[1]
class CarInterface(CarInterfaceBase):
@@ -72,15 +27,6 @@ class CarInterface(CarInterfaceBase):
CarController = CarController
RadarInterface = RadarInterface
@staticmethod
def get_pid_accel_limits(CP, current_speed, cruise_speed):
return ACCEL_MIN, CarControllerParams.ACCEL_MAX
@staticmethod
def apply_post_fingerprint_params(CP: structs.CarParams, candidate, fingerprint, car_fw) -> None:
if kia_ev6_gt_line_longitudinal_tuning(CP.carFingerprint, CP.carVin):
apply_kia_ev6_gt_line_longitudinal_params(CP)
@staticmethod
def _get_params(ret: structs.CarParams, candidate, fingerprint, car_fw, alpha_long, is_release, docs) -> structs.CarParams:
ret.brand = "hyundai"
@@ -100,9 +46,6 @@ class CarInterface(CarInterfaceBase):
# this needs to be figured out for cars without an ADAS ECU
# Cars in CANFD_SECURITYACCESS_CAR are known to have ADAS ECUs that work with SecurityAccess
ret.alphaLongitudinalAvailable = False
if lka_steering and ret.flags & HyundaiFlags.CANFD_ANGLE_STEERING and candidate not in CANFD_ANGLE_LONGITUDINAL_CAR:
# Most angle-steering LKA platforms still need stock longitudinal validation.
ret.alphaLongitudinalAvailable = False
ret.enableBsm = 0x1ba in fingerprint[CAN.ECAN]
@@ -148,23 +91,19 @@ class CarInterface(CarInterfaceBase):
if ret.flags & HyundaiFlags.CANFD_ANGLE_STEERING:
ret.steerControlType = structs.CarParams.SteerControlType.angle
ret.safetyConfigs[-1].safetyParam |= HyundaiSafetyFlags.CANFD_ANGLE_STEERING.value
if ret.flags & HyundaiFlags.CCNC and not ret.flags & HyundaiFlags.CANFD_LKA_STEERING:
ret.safetyConfigs[-1].safetyParam |= HyundaiSafetyFlags.CCNC.value
else:
# Shared configuration for non CAN-FD cars
ret.alphaLongitudinalAvailable = candidate not in UNSUPPORTED_LONGITUDINAL_CAR or candidate in LEGACY_LONGITUDINAL_CAR
ret.enableBsm = 0x58b in fingerprint[CAN.ECAN]
ret.alphaLongitudinalAvailable = candidate not in UNSUPPORTED_LONGITUDINAL_CAR
ret.enableBsm = 0x58b in fingerprint[0]
# Send LFA message on cars with HDA
if 0x485 in fingerprint[CAN.CAM]:
if 0x485 in fingerprint[2]:
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[CAN.ECAN] or 0x38d in fingerprint[CAN.CAM]:
if 0x38d in fingerprint[0] or 0x38d in fingerprint[2]:
ret.flags |= HyundaiFlags.USE_FCA.value
if detect_kona_non_scc_radar_fca(candidate, fingerprint, car_fw):
ret.flags |= HyundaiFlags.NON_SCC_RADAR_FCA.value
if ret.flags & HyundaiFlags.LEGACY:
# these cars require a special panda safety mode due to missing counters and checksums in the messages
@@ -175,13 +114,15 @@ class CarInterface(CarInterfaceBase):
if ret.flags & HyundaiFlags.CAMERA_SCC:
ret.safetyConfigs[0].safetyParam |= HyundaiSafetyFlags.CAMERA_SCC.value
# These cars expose an LKAS/LFA steering-wheel button that StarPilot can customize.
if 0x391 in fingerprint[0] or ret.flags & HyundaiFlags.CAN_CANFD_BLENDED:
ret.safetyConfigs[-1].safetyParam |= HyundaiStarPilotSafetyFlags.HAS_LDA_BUTTON.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
# These cars have the LFA button on the steering wheel
if 0x391 in fingerprint[0]:
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
# Common lateral control setup
@@ -200,21 +141,21 @@ 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
radar_config = get_radar_track_config(ret.carFingerprint, ret.flags)
radar_available = radar_tracks_available(radar_config, fingerprint)
ret.radarUnavailable = not radar_available
ret.radarUnavailable = RADAR_START_ADDR not in fingerprint[1] or Bus.radar not in DBC[ret.carFingerprint]
if ret.flags & HyundaiFlags.NON_SCC:
ret.alphaLongitudinalAvailable = False
ret.openpilotLongitudinalControl = alpha_long and ret.alphaLongitudinalAvailable
if ret.openpilotLongitudinalControl and not (candidate in RADAR_LIVE_LONGITUDINAL_CAR and radar_available):
# When longitudinal is enabled, we disable the ADAS ECU which stops radar messages
# Force radarUnavailable to prevent CAN Error from missing radar messages
if ret.openpilotLongitudinalControl:
ret.radarUnavailable = True
ret.pcmCruise = not ret.openpilotLongitudinalControl
apply_platform_longitudinal_params(ret)
ret.startingState = True
ret.vEgoStarting = 0.1
ret.startAccel = 1.0
ret.longitudinalActuatorDelay = 0.5
if ret.openpilotLongitudinalControl:
ret.safetyConfigs[-1].safetyParam |= HyundaiSafetyFlags.LONG.value
@@ -227,25 +168,6 @@ 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
@@ -263,7 +185,7 @@ class CarInterface(CarInterfaceBase):
params = Params()
if communication_control is None:
if CP.carFingerprint in CANFD_RADAR_LIVE_LONGITUDINAL_CAR:
if CP.carFingerprint == CAR.HYUNDAI_IONIQ_6:
# Don't use 0x80 suppress bit so we can read the ECU response.
# Use ENABLE_RX_DISABLE_TX (0x01) so the ECU can still receive from rear radars for BSM
# while blocking SCC TX.
@@ -275,7 +197,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 | HyundaiFlags.CAN_CANFD_BLENDED) else 0
addr, bus = 0x7d0, CanBus(CP).ECAN if CP.flags & HyundaiFlags.CANFD else 0
if CP.flags & HyundaiFlags.CANFD_LKA_STEERING.value:
addr, bus = 0x730, CanBus(CP).ECAN
@@ -283,8 +205,7 @@ 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,
reset=bool(CP.flags & HyundaiFlags.CAN_CANFD_BLENDED))
ecu_disabled = disable_ecu(can_recv, can_send, bus=bus, addr=addr, com_cont_req=communication_control)
if CP.carFingerprint == CAR.HYUNDAI_IONIQ_6:
# Ioniq 6: track success/failure to auto-switch between openpilot long and stock ACC
@@ -294,15 +215,15 @@ class CarInterface(CarInterfaceBase):
params.put_bool("ExperimentalMode", True)
ecu_log("=== ECU DISABLE SUCCESS - Longitudinal + Experimental ENABLED ===")
else:
apply_ecu_disable_failure_fallback(CP, params)
params.put_bool("EcuDisableFailed", True)
CP.safetyConfigs[-1].safetyParam &= ~HyundaiSafetyFlags.LONG.value
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:
apply_ecu_disable_failure_fallback(CP, params)
ecu_log(f"=== ECU DISABLE FAILED - safetyParam stripped to {CP.safetyConfigs[-1].safetyParam}, lateral-only mode ===")
ecu_log("=== ECU DISABLE FAILED ===")
# for blinkers
if CP.flags & HyundaiFlags.ENABLE_BLINKERS:
@@ -1,141 +1,40 @@
import math
from dataclasses import dataclass, replace
from opendbc.can import CANParser
from opendbc.can.dbc import DBC as DBCReader
from opendbc.can.parser import get_raw_value
from opendbc.car import Bus, structs
from opendbc.car.interfaces import RadarInterfaceBase
from opendbc.car.hyundai.values import CAR, DBC, HyundaiFlags, HYUNDAI_MANDO_FRONT_RADAR_DBC, HYUNDAI_MRREVO14F_RADAR_DBC, \
HYUNDAI_MRR30_RADAR_DBC, HYUNDAI_MRR35_RADAR_DBC
from openpilot.common.swaglog import cloudlog
from opendbc.car.hyundai.values import DBC
RADAR_START_ADDR = 0x500
RADAR_MSG_COUNT = 32
G90_RADAR_MSG_COUNT = 64
MRREVO14F_RADAR_START_ADDR = 0x602
MRREVO14F_RADAR_MSG_COUNT = 16
MRR30_RADAR_START_ADDR = 0x210
MRR30_RADAR_MSG_COUNT = 16
MRR35_RADAR_START_ADDR = 0x3A5
MRR35_RADAR_MSG_COUNT = 32
@dataclass(frozen=True)
class RadarTrackConfig:
start_addr: int
msg_count: int
radar_type: str
bus: int = 1
frequency: int = 50
parser_msg_count: int | None = None
expected_length: int | None = None
@property
def can_parser_msg_count(self) -> int:
return self.parser_msg_count if self.parser_msg_count is not None else self.msg_count
RADAR_TRACK_CONFIGS = {
HYUNDAI_MANDO_FRONT_RADAR_DBC: RadarTrackConfig(RADAR_START_ADDR, RADAR_MSG_COUNT, "mando"),
HYUNDAI_MRREVO14F_RADAR_DBC: RadarTrackConfig(MRREVO14F_RADAR_START_ADDR, MRREVO14F_RADAR_MSG_COUNT, "mrrevo14f"),
HYUNDAI_MRR30_RADAR_DBC: RadarTrackConfig(MRR30_RADAR_START_ADDR, MRR30_RADAR_MSG_COUNT, "mrr30", bus=0, expected_length=32),
HYUNDAI_MRR35_RADAR_DBC: RadarTrackConfig(MRR35_RADAR_START_ADDR, MRR35_RADAR_MSG_COUNT, "mrr35", bus=0, frequency=20, expected_length=24),
}
# POC for parsing corner radars: https://github.com/commaai/openpilot/pull/24221/
def get_radar_track_config(car_fingerprint, flags: int = 0) -> RadarTrackConfig | None:
radar_dbc = DBC[car_fingerprint].get(Bus.radar)
if car_fingerprint == CAR.GENESIS_G90 and radar_dbc == HYUNDAI_MANDO_FRONT_RADAR_DBC:
return RadarTrackConfig(RADAR_START_ADDR, G90_RADAR_MSG_COUNT, "mando", parser_msg_count=RADAR_MSG_COUNT)
radar_config = RADAR_TRACK_CONFIGS.get(radar_dbc)
if radar_config is None:
def get_radar_can_parser(CP):
if Bus.radar not in DBC[CP.carFingerprint]:
return None
if car_fingerprint == CAR.HYUNDAI_IONIQ_6 and flags & HyundaiFlags.CANFD_CAMERA_SCC:
return replace(radar_config, bus=1)
return radar_config
def radar_tracks_available(radar_config: RadarTrackConfig | None, fingerprint) -> bool:
if radar_config is None:
return False
msg_len = fingerprint[radar_config.bus].get(radar_config.start_addr)
if msg_len is None:
return False
return radar_config.expected_length is None or msg_len == radar_config.expected_length
def get_radar_can_parser(CP, radar_config):
if radar_config is None:
return None
messages = [(f"RADAR_TRACK_{addr:x}", radar_config.frequency)
for addr in range(radar_config.start_addr, radar_config.start_addr + radar_config.can_parser_msg_count)]
return CANParser(DBC[CP.carFingerprint][Bus.radar], messages, radar_config.bus)
messages = [(f"RADAR_TRACK_{addr:x}", 50) for addr in range(RADAR_START_ADDR, RADAR_START_ADDR + RADAR_MSG_COUNT)]
return CANParser(DBC[CP.carFingerprint][Bus.radar], messages, 1)
class RadarInterface(RadarInterfaceBase):
def __init__(self, CP):
super().__init__(CP)
self.radar_config = get_radar_track_config(CP.carFingerprint, CP.flags)
self.updated_messages = set()
self.trigger_msg = (self.radar_config.start_addr + self.radar_config.can_parser_msg_count - 1
if self.radar_config is not None else RADAR_START_ADDR)
self.trigger_msg = RADAR_START_ADDR + RADAR_MSG_COUNT - 1
self.track_id = 0
self.g90_extended_mando = (CP.carFingerprint == CAR.GENESIS_G90 and self.radar_config is not None and
self.radar_config.msg_count > self.radar_config.can_parser_msg_count)
self.g90_mando_signals = []
if self.g90_extended_mando:
radar_dbc = DBCReader(DBC[CP.carFingerprint][Bus.radar])
self.g90_mando_signals = list(radar_dbc.addr_to_msg[RADAR_START_ADDR].sigs.values())
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, self.radar_config)
# Precompute (addr, "RADAR_TRACK_xxx") pairs once. _update runs on the
# CAN-driven card loop (core 4, shared with controlsd/selfdrived), so avoid
# rebuilding 32 f-strings per radar frame.
self.track_addrs: list[tuple[int, str]] = []
if self.radar_config is not None:
self.track_addrs = [(addr, f"RADAR_TRACK_{addr:x}")
for addr in range(self.radar_config.start_addr,
self.radar_config.start_addr + self.radar_config.can_parser_msg_count)]
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)
vls = self.rcp.update(can_strings)
self.updated_messages.update(vls)
if self.g90_extended_mando:
self._update_g90_extended_mando_tracks(can_strings)
if self.trigger_msg not in self.updated_messages:
return None
@@ -145,46 +44,6 @@ class RadarInterface(RadarInterfaceBase):
return rr
def _decode_g90_mando_values(self, dat: bytes):
vals = {}
for sig in self.g90_mando_signals:
raw = get_raw_value(dat, sig)
if sig.is_signed:
raw -= ((raw >> (sig.size - 1)) & 1) * (1 << sig.size)
vals[sig.name] = raw * sig.factor + sig.offset
return vals
def _update_g90_extended_mando_tracks(self, can_strings):
if self.radar_config is None:
return
start_addr = self.radar_config.start_addr + self.radar_config.can_parser_msg_count
end_addr = self.radar_config.start_addr + self.radar_config.msg_count
for _, frames in can_strings:
for address, dat, src in frames:
if src != self.radar_config.bus or not (start_addr <= address < end_addr) or len(dat) < 8:
continue
self.updated_messages.add(address)
msg = self._decode_g90_mando_values(dat)
valid = msg["STATE"] in (3, 4)
if valid:
if address not in self.pts:
self.pts[address] = structs.RadarData.RadarPoint()
self.pts[address].trackId = self.track_id
self.track_id += 1
azimuth = math.radians(msg["AZIMUTH"])
self.pts[address].measured = True
self.pts[address].dRel = math.cos(azimuth) * msg["LONG_DIST"]
self.pts[address].yRel = 0.5 * -math.sin(azimuth) * msg["LONG_DIST"]
self.pts[address].vRel = msg["REL_SPEED"]
self.pts[address].aRel = msg["REL_ACCEL"]
self.pts[address].yvRel = float("nan")
elif address in self.pts:
del self.pts[address]
def _update(self, updated_messages):
ret = structs.RadarData()
if self.rcp is None:
@@ -193,77 +52,8 @@ class RadarInterface(RadarInterfaceBase):
if not self.rcp.can_valid:
ret.errors.canError = True
if self.radar_config is None:
return ret
radar_type = self.radar_config.radar_type
vl = self.rcp.vl
for addr, track_name in self.track_addrs:
msg = vl[track_name]
if radar_type == "mrr30":
for i in ("1", "2"):
track_key = addr * 2 + int(i) - 1
if track_key not in self.pts:
self.pts[track_key] = structs.RadarData.RadarPoint()
self.pts[track_key].trackId = self.track_id
self.track_id += 1
valid = msg[f"{i}_STATE"] in (3, 4)
if valid:
pt = self.pts[track_key]
pt.measured = True
pt.dRel = msg[f"{i}_LONG_DIST"]
pt.yRel = msg[f"{i}_LAT_DIST"]
pt.vRel = msg[f"{i}_REL_SPEED"]
pt.aRel = float("nan")
pt.yvRel = float("nan")
else:
del self.pts[track_key]
continue
if radar_type == "mrrevo14f":
for i in ("1", "2"):
track_key = addr * 2 + int(i) - 1
valid = msg[f"{i}_DISTANCE"] != 255.75
if valid:
pt = self.pts.get(track_key)
if pt is None:
pt = structs.RadarData.RadarPoint()
pt.trackId = self.track_id
self.track_id += 1
self.pts[track_key] = pt
pt.measured = True
pt.dRel = msg[f"{i}_DISTANCE"]
pt.yRel = msg[f"{i}_LATERAL"]
pt.vRel = msg[f"{i}_SPEED"]
pt.aRel = float("nan")
pt.yvRel = float("nan")
elif track_key in self.pts:
del self.pts[track_key]
continue
if radar_type == "mrr35":
# Most of the 32 channels are empty each frame. Only allocate a point
# when the channel is valid; drop it otherwise. Avoids the per-frame
# alloc-then-delete churn on the ~27 idle channels.
if msg["STATE"] in (3, 4):
pt = self.pts.get(addr)
if pt is None:
pt = structs.RadarData.RadarPoint()
pt.trackId = self.track_id
self.track_id += 1
self.pts[addr] = pt
pt.measured = True
pt.dRel = msg["LONG_DIST"]
pt.yRel = msg["LAT_DIST"]
pt.vRel = msg["REL_SPEED"]
pt.aRel = msg["REL_ACCEL"]
pt.yvRel = float("nan")
elif addr in self.pts:
del self.pts[addr]
continue
for addr in range(RADAR_START_ADDR, RADAR_START_ADDR + RADAR_MSG_COUNT):
msg = self.rcp.vl[f"RADAR_TRACK_{addr:x}"]
if addr not in self.pts:
self.pts[addr] = structs.RadarData.RadarPoint()
File diff suppressed because it is too large Load Diff
+34 -356
View File
@@ -1,33 +1,32 @@
import re
from dataclasses import dataclass, field
from dataclasses import dataclass, field, replace
from enum import IntFlag
from opendbc.car import ACCELERATION_DUE_TO_GRAVITY, Bus, CarSpecs, DbcDict, PlatformConfig, Platforms, uds
from opendbc.car.lateral import AngleSteeringLimits, ISO_LATERAL_ACCEL
from opendbc.car.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, SupportType
from opendbc.car.docs_definitions import CarHarness, CarDocs, CarParts
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
HYUNDAI_MANDO_FRONT_RADAR_DBC = "hyundai_kia_mando_front_radar_generated"
HYUNDAI_MRREVO14F_RADAR_DBC = "hyundai_mrrevo14f_radar_generated"
HYUNDAI_MRR30_RADAR_DBC = "hyundai_mrr30_radar_generated"
HYUNDAI_MRR35_RADAR_DBC = "hyundai_mrr35_radar_generated"
class CarControllerParams:
ACCEL_MIN = -3.5 # m/s
ACCEL_MAX = 3.5 # m/s
ACCEL_MAX = 2.0 # m/s
ANGLE_LIMITS: AngleSteeringLimits = AngleSteeringLimits(
360,
180,
([], []),
([], []),
MAX_LATERAL_ACCEL=ISO_LATERAL_ACCEL + (ACCELERATION_DUE_TO_GRAVITY * AVERAGE_ROAD_ROLL),
MAX_LATERAL_JERK=3.0 + (ACCELERATION_DUE_TO_GRAVITY * AVERAGE_ROAD_ROLL),
MAX_LATERAL_JERK=ISO_LATERAL_JERK + (ACCELERATION_DUE_TO_GRAVITY * AVERAGE_ROAD_ROLL),
MAX_ANGLE_RATE=5,
)
ANGLE_MAX_TORQUE_REDUCTION_GAIN = 1.0
ANGLE_MIN_TORQUE_REDUCTION_GAIN = 0.6
ANGLE_ACTIVE_TORQUE_REDUCTION_GAIN = 0.6
def __init__(self, CP, vEgoRaw=100.):
self.ANGLE_LIMITS = self.ANGLE_LIMITS
@@ -54,14 +53,17 @@ class CarControllerParams:
if CP.flags & HyundaiFlags.CANFD_ANGLE_STEERING:
self.STEER_THRESHOLD = 175
elif CP.flags & HyundaiFlags.CANFD:
pass
# 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.
if CP.carFingerprint == CAR.KIA_SPORTAGE_HEV_2026:
self.ANGLE_LIMITS = replace(self.ANGLE_LIMITS,
MAX_LATERAL_JERK=3.0 + (ACCELERATION_DUE_TO_GRAVITY * AVERAGE_ROAD_ROLL))
# 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
@@ -76,13 +78,6 @@ 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
@@ -100,10 +95,6 @@ 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
CCNC = 32768
class HyundaiStarPilotSafetyFlags(IntFlag):
@@ -167,6 +158,8 @@ class HyundaiFlags(IntFlag):
MIN_STEER_32_MPH = 2 ** 23
HAS_LDA_BUTTON = 2 ** 24
FCEV = 2 ** 25
ALT_LIMITS_2 = 2 ** 26
@@ -177,71 +170,30 @@ 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
# Connected Car Navigation Cockpit CAN-FD platforms.
CCNC = 2 ** 24
@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"})
radar_dbc: str | None = None
def init(self):
if self.radar_dbc is not None:
self.dbc_dict = {Bus.pt: "hyundai_kia_generic", Bus.radar: self.radar_dbc}
elif self.flags & HyundaiFlags.MANDO_RADAR:
self.dbc_dict = {Bus.pt: "hyundai_kia_generic", Bus.radar: HYUNDAI_MANDO_FRONT_RADAR_DBC}
if self.flags & HyundaiFlags.MANDO_RADAR:
self.dbc_dict = {Bus.pt: "hyundai_kia_generic", Bus.radar: 'hyundai_kia_mando_front_radar_generated'}
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):
dbc_dict: DbcDict = field(default_factory=lambda: {Bus.pt: "hyundai_canfd_generated"})
radar_dbc: str | None = None
def init(self):
self.flags |= HyundaiFlags.CANFD
if self.radar_dbc is not None:
self.dbc_dict = {Bus.pt: "hyundai_canfd_generated", Bus.radar: self.radar_dbc}
@dataclass
class HyundaiNonSccPlatformConfig(PlatformConfig):
dbc_dict: DbcDict = field(default_factory=lambda: {Bus.pt: "hyundai_kia_generic"})
radar_dbc: str | None = None
def init(self):
if self.radar_dbc is not None:
self.dbc_dict = {Bus.pt: "hyundai_kia_generic", Bus.radar: self.radar_dbc}
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):
@@ -258,14 +210,6 @@ class CAR(Platforms):
CarSpecs(mass=1675, wheelbase=2.885, steerRatio=14.5),
flags=HyundaiFlags.HYBRID,
)
HYUNDAI_AZERA_HEV_7TH_GEN = HyundaiCanFDPlatformConfig(
[
HyundaiCarDocs("Hyundai Azera Hybrid (with HDA II & LFA2) 2025", "Highway Driving Assist II & Lane Follow Assist 2",
car_parts=CarParts.common([CarHarness.hyundai_s])),
],
CarSpecs(mass=1720, wheelbase=2.895, steerRatio=13.5),
flags=HyundaiFlags.CANFD_ANGLE_STEERING,
)
HYUNDAI_ELANTRA = HyundaiPlatformConfig(
[
# TODO: 2017-18 could be Hyundai G
@@ -307,7 +251,7 @@ class CAR(Platforms):
HYUNDAI_IONIQ = HyundaiPlatformConfig(
[HyundaiCarDocs("Hyundai Ioniq Hybrid 2017-19", car_parts=CarParts.common([CarHarness.hyundai_c]))],
CarSpecs(mass=1490, wheelbase=2.7, steerRatio=13.73, tireStiffnessFactor=0.385),
flags=HyundaiFlags.HYBRID | HyundaiFlags.MIN_STEER_32_MPH | HyundaiFlags.MANDO_RADAR,
flags=HyundaiFlags.HYBRID | HyundaiFlags.MIN_STEER_32_MPH,
)
HYUNDAI_IONIQ_HEV_2022 = HyundaiPlatformConfig(
[HyundaiCarDocs("Hyundai Ioniq Hybrid 2020-22", car_parts=CarParts.common([CarHarness.hyundai_h]))],
@@ -344,16 +288,6 @@ class CAR(Platforms):
CarSpecs(mass=1491, wheelbase=2.6, steerRatio=13.42, tireStiffnessFactor=0.385),
flags=HyundaiFlags.CAMERA_SCC | HyundaiFlags.ALT_LIMITS_2,
)
HYUNDAI_KONA_2ND_GEN = HyundaiCanFDPlatformConfig(
[HyundaiCarDocs("Hyundai Kona (without HDA II) 2024-25", car_parts=CarParts.common([CarHarness.hyundai_l]))],
CarSpecs(mass=1590, wheelbase=2.66, steerRatio=13.6, tireStiffnessFactor=0.385),
flags=HyundaiFlags.CCNC,
)
HYUNDAI_KONA_HEV_2ND_GEN = HyundaiCanFDPlatformConfig(
[HyundaiCarDocs("Hyundai Kona Hybrid (without HDA II) 2024", car_parts=CarParts.common([CarHarness.hyundai_l]))],
CarSpecs(mass=1590, wheelbase=2.66, steerRatio=13.6, tireStiffnessFactor=0.385),
flags=HyundaiFlags.CCNC,
)
HYUNDAI_KONA_EV = HyundaiPlatformConfig(
[HyundaiCarDocs("Hyundai Kona Electric 2018-21", car_parts=CarParts.common([CarHarness.hyundai_g]))],
CarSpecs(mass=1685, wheelbase=2.6, steerRatio=13.42, tireStiffnessFactor=0.385),
@@ -363,17 +297,12 @@ class CAR(Platforms):
[HyundaiCarDocs("Hyundai Kona Electric 2022-23", car_parts=CarParts.common([CarHarness.hyundai_o]))],
CarSpecs(mass=1743, wheelbase=2.6, steerRatio=13.42, tireStiffnessFactor=0.385),
flags=HyundaiFlags.CAMERA_SCC | HyundaiFlags.EV | HyundaiFlags.ALT_LIMITS,
radar_dbc=HYUNDAI_MRREVO14F_RADAR_DBC,
)
HYUNDAI_KONA_EV_2ND_GEN = HyundaiCanFDPlatformConfig(
[
HyundaiCarDocs("Hyundai Kona Electric (with HDA II, Korea only) 2023", video="https://www.youtube.com/watch?v=U2fOCmcQ8hw",
car_parts=CarParts.common([CarHarness.hyundai_r])),
HyundaiCarDocs("Hyundai Kona Electric (without HDA II) 2024", car_parts=CarParts.common([CarHarness.hyundai_a])),
],
[HyundaiCarDocs("Hyundai Kona Electric (with HDA II, Korea only) 2023", video="https://www.youtube.com/watch?v=U2fOCmcQ8hw",
car_parts=CarParts.common([CarHarness.hyundai_r]))],
CarSpecs(mass=1740, wheelbase=2.66, steerRatio=13.6, tireStiffnessFactor=0.385),
flags=HyundaiFlags.EV | HyundaiFlags.CANFD_NO_RADAR_DISABLE | HyundaiFlags.CCNC,
radar_dbc=HYUNDAI_MRR35_RADAR_DBC,
flags=HyundaiFlags.EV | HyundaiFlags.CANFD_NO_RADAR_DISABLE,
)
HYUNDAI_KONA_HEV = HyundaiPlatformConfig(
[HyundaiCarDocs("Hyundai Kona Hybrid 2020", car_parts=CarParts.common([CarHarness.hyundai_i]))], # TODO: check packages,
@@ -395,27 +324,17 @@ class CAR(Platforms):
[HyundaiCarDocs("Hyundai Santa Fe 2021-23", "All", video="https://youtu.be/VnHzSTygTS4",
car_parts=CarParts.common([CarHarness.hyundai_l]))],
HYUNDAI_SANTA_FE.specs,
flags=HyundaiFlags.MANDO_RADAR | HyundaiFlags.CHECKSUM_CRC8,
flags=HyundaiFlags.CHECKSUM_CRC8,
)
HYUNDAI_SANTA_FE_HEV_2022 = HyundaiPlatformConfig(
[HyundaiCarDocs("Hyundai Santa Fe Hybrid 2022-23", "All", car_parts=CarParts.common([CarHarness.hyundai_l]))],
HYUNDAI_SANTA_FE.specs,
flags=HyundaiFlags.MANDO_RADAR | HyundaiFlags.CHECKSUM_CRC8 | HyundaiFlags.HYBRID,
flags=HyundaiFlags.CHECKSUM_CRC8 | HyundaiFlags.HYBRID,
)
HYUNDAI_SANTA_FE_PHEV_2022 = HyundaiPlatformConfig(
[HyundaiCarDocs("Hyundai Santa Fe Plug-in Hybrid 2022-23", "All", car_parts=CarParts.common([CarHarness.hyundai_l]))],
HYUNDAI_SANTA_FE.specs,
flags=HyundaiFlags.MANDO_RADAR | HyundaiFlags.CHECKSUM_CRC8 | HyundaiFlags.HYBRID,
)
HYUNDAI_SANTA_FE_HEV_5TH_GEN = HyundaiCanFDPlatformConfig(
[
HyundaiCarDocs("Hyundai Santa Fe Hybrid (with HDA II & LFA2) 2024-25", "Highway Driving Assist II & Lane Follow Assist 2",
car_parts=CarParts.common([CarHarness.hyundai_p])),
HyundaiCarDocs("Hyundai Santa Fe Hybrid (without HDA II, LFA2) 2025-26", "Lane Follow Assist 2",
car_parts=CarParts.common([CarHarness.hyundai_l])),
],
CarSpecs(mass=2035, wheelbase=2.81, steerRatio=13.72),
flags=HyundaiFlags.CANFD_ANGLE_STEERING | HyundaiFlags.CCNC,
flags=HyundaiFlags.CHECKSUM_CRC8 | HyundaiFlags.HYBRID,
)
HYUNDAI_SONATA = HyundaiPlatformConfig(
[HyundaiCarDocs("Hyundai Sonata 2020-23", "All", video="https://www.youtube.com/watch?v=ix63r9kE3Fw",
@@ -423,11 +342,6 @@ class CAR(Platforms):
CarSpecs(mass=1513, wheelbase=2.84, steerRatio=13.27 * 1.15, tireStiffnessFactor=0.65), # 15% higher at the center seems reasonable
flags=HyundaiFlags.MANDO_RADAR | HyundaiFlags.CHECKSUM_CRC8,
)
HYUNDAI_SONATA_2024 = HyundaiCanFDPlatformConfig(
[HyundaiCarDocs("Hyundai Sonata (without HDA II) 2024-25", car_parts=CarParts.common([CarHarness.hyundai_a]))],
CarSpecs(mass=1556, wheelbase=2.84, steerRatio=12.81),
flags=HyundaiFlags.CCNC,
)
HYUNDAI_SONATA_LF = HyundaiPlatformConfig(
[HyundaiCarDocs("Hyundai Sonata 2018-19", car_parts=CarParts.common([CarHarness.hyundai_e]))],
CarSpecs(mass=1536, wheelbase=2.804, steerRatio=13.27 * 1.15), # 15% higher at the center seems reasonable
@@ -453,16 +367,6 @@ 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),
@@ -473,11 +377,6 @@ class CAR(Platforms):
HYUNDAI_SONATA.specs,
flags=HyundaiFlags.MANDO_RADAR | HyundaiFlags.CHECKSUM_CRC8 | HyundaiFlags.HYBRID,
)
HYUNDAI_SONATA_HEV_2024 = HyundaiCanFDPlatformConfig(
[HyundaiCarDocs("Hyundai Sonata Hybrid (without HDA II) 2024-25", car_parts=CarParts.common([CarHarness.hyundai_a]))],
CarSpecs(mass=1616, wheelbase=2.84, steerRatio=13.27),
flags=HyundaiFlags.CCNC,
)
HYUNDAI_IONIQ_5 = HyundaiCanFDPlatformConfig(
[
HyundaiCarDocs("Hyundai Ioniq 5 (Southeast Asia and Europe only) 2022-24", "All", car_parts=CarParts.common([CarHarness.hyundai_q])),
@@ -486,37 +385,11 @@ class CAR(Platforms):
],
CarSpecs(mass=1948, wheelbase=2.97, steerRatio=14.26, tireStiffnessFactor=0.65),
flags=HyundaiFlags.EV,
radar_dbc=HYUNDAI_MRR30_RADAR_DBC,
)
HYUNDAI_IONIQ_5_PE = HyundaiCanFDPlatformConfig(
[
HyundaiCarDocs("Hyundai Ioniq 5 PE (with HDA II & LFA2) 2025-26", "Highway Driving Assist II & Lane Follow Assist 2",
car_parts=CarParts.common([CarHarness.hyundai_q]))
],
HYUNDAI_IONIQ_5.specs,
flags=HyundaiFlags.EV | HyundaiFlags.CANFD_ANGLE_STEERING,
radar_dbc=HYUNDAI_MRR30_RADAR_DBC,
)
HYUNDAI_IONIQ_5_N = HyundaiCanFDPlatformConfig(
[HyundaiCarDocs("Hyundai Ioniq 5 N (with HDA II) 2024", car_parts=CarParts.common([CarHarness.hyundai_s]))],
CarSpecs(mass=2205, wheelbase=3.00, steerRatio=14.26, tireStiffnessFactor=1.3),
flags=HyundaiFlags.EV | HyundaiFlags.CCNC,
radar_dbc=HYUNDAI_MRR30_RADAR_DBC,
)
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_NO_RADAR_DISABLE,
radar_dbc=HYUNDAI_MRR35_RADAR_DBC,
)
HYUNDAI_IONIQ_9 = HyundaiCanFDPlatformConfig(
[
HyundaiCarDocs("Hyundai Ioniq 9 (with HDA II & LFA2) 2025-26", "Highway Driving Assist II & Lane Follow Assist 2",
car_parts=CarParts.common([CarHarness.hyundai_m]))
],
CarSpecs(mass=2700, wheelbase=3.13, steerRatio=16.02),
flags=HyundaiFlags.EV | HyundaiFlags.CANFD_ANGLE_STEERING,
radar_dbc=HYUNDAI_MRR35_RADAR_DBC,
flags=HyundaiFlags.EV | HyundaiFlags.CANFD_LKA_STEERING | HyundaiFlags.CANFD_LKA_STEERING_ALT | HyundaiFlags.CANFD_NO_RADAR_DISABLE,
)
HYUNDAI_TUCSON_4TH_GEN = HyundaiCanFDPlatformConfig(
[
@@ -527,31 +400,11 @@ class CAR(Platforms):
],
CarSpecs(mass=1630, wheelbase=2.756, steerRatio=13.7, tireStiffnessFactor=0.385),
)
HYUNDAI_TUCSON_2025 = HyundaiCanFDPlatformConfig(
[HyundaiCarDocs("Hyundai Tucson (without HDA II) 2025-26", car_parts=CarParts.common([CarHarness.hyundai_n]))],
CarSpecs(mass=1630, wheelbase=2.756, steerRatio=13.7, tireStiffnessFactor=0.385),
flags=HyundaiFlags.CCNC,
)
HYUNDAI_TUCSON_HEV_2025 = HyundaiCanFDPlatformConfig(
[HyundaiCarDocs("Hyundai Tucson Hybrid (without HDA II) 2025-26", car_parts=CarParts.common([CarHarness.hyundai_n]))],
CarSpecs(mass=1630, wheelbase=2.756, steerRatio=13.7, tireStiffnessFactor=0.385),
flags=HyundaiFlags.CCNC,
)
HYUNDAI_TUCSON_PHEV_2025 = HyundaiCanFDPlatformConfig(
[HyundaiCarDocs("Hyundai Tucson Plug-in Hybrid (without HDA II) 2025", car_parts=CarParts.common([CarHarness.hyundai_n]))],
CarSpecs(mass=1630, wheelbase=2.756, steerRatio=13.7, tireStiffnessFactor=0.385),
flags=HyundaiFlags.CCNC,
)
HYUNDAI_SANTA_CRUZ_1ST_GEN = HyundaiCanFDPlatformConfig(
[HyundaiCarDocs("Hyundai Santa Cruz 2022-24", car_parts=CarParts.common([CarHarness.hyundai_n]))],
# weight from Limited trim - the only supported trim, steering ratio according to Hyundai News https://www.hyundainews.com/assets/documents/original/48035-2022SantaCruzProductGuideSpecsv2081521.pdf
CarSpecs(mass=1870, wheelbase=3, steerRatio=14.2),
)
HYUNDAI_SANTA_CRUZ_2025 = HyundaiCanFDPlatformConfig(
[HyundaiCarDocs("Hyundai Santa Cruz (without HDA II) 2025", car_parts=CarParts.common([CarHarness.hyundai_n]))],
CarSpecs(mass=1920, wheelbase=3, steerRatio=14.2),
flags=HyundaiFlags.CCNC,
)
HYUNDAI_CUSTIN_1ST_GEN = HyundaiPlatformConfig(
[HyundaiCarDocs("Hyundai Custin 2023", "All", car_parts=CarParts.common([CarHarness.hyundai_k]))],
CarSpecs(mass=1690, wheelbase=3.055, steerRatio=17), # mass: from https://www.hyundai-motor.com.tw/clicktobuy/custin#spec_0, steerRatio: from learner
@@ -566,24 +419,11 @@ class CAR(Platforms):
],
CarSpecs(mass=2878 * CV.LB_TO_KG, wheelbase=2.8, steerRatio=13.75, tireStiffnessFactor=0.5)
)
KIA_K4_2025 = HyundaiCanFDPlatformConfig(
[
HyundaiCarDocs("Kia K4 (without HDA II) 2025", car_parts=CarParts.common([CarHarness.hyundai_a])),
HyundaiCarDocs("Kia K4 (with HDA II) 2025", car_parts=CarParts.common([CarHarness.hyundai_r])),
],
CarSpecs(mass=2987 * CV.LB_TO_KG, wheelbase=2.72, steerRatio=13.4),
flags=HyundaiFlags.CCNC,
)
KIA_K5_2021 = HyundaiPlatformConfig(
[HyundaiCarDocs("Kia K5 2021-24", car_parts=CarParts.common([CarHarness.hyundai_a]))],
CarSpecs(mass=3381 * CV.LB_TO_KG, wheelbase=2.85, steerRatio=13.27, tireStiffnessFactor=0.5), # 2021 Kia K5 Steering Ratio (all trims)
flags=HyundaiFlags.CHECKSUM_CRC8,
)
KIA_K5_2025 = HyundaiCanFDPlatformConfig(
[HyundaiCarDocs("Kia K5 (without HDA II) 2025", car_parts=CarParts.common([CarHarness.hyundai_m]))],
CarSpecs(mass=3230 * CV.LB_TO_KG, wheelbase=2.85, steerRatio=13.27),
flags=HyundaiFlags.CCNC,
)
KIA_K5_HEV_2020 = HyundaiPlatformConfig(
[HyundaiCarDocs("Kia K5 Hybrid 2020-22", car_parts=CarParts.common([CarHarness.hyundai_a]))],
KIA_K5_2021.specs,
@@ -676,13 +516,6 @@ class CAR(Platforms):
# weight from SX and above trims, average of FWD and AWD version, steering ratio according to Kia News https://www.kiamedia.com/us/en/models/sportage/2023/specifications
CarSpecs(mass=1725, wheelbase=2.756, steerRatio=13.6),
)
KIA_SPORTAGE_2026 = HyundaiCanFDPlatformConfig(
[
HyundaiCarDocs("Kia Sportage (without HDA II) 2026", car_parts=CarParts.common([CarHarness.hyundai_n])),
],
CarSpecs(mass=1735, wheelbase=2.756, steerRatio=13.7),
flags=HyundaiFlags.CANFD_ANGLE_STEERING | HyundaiFlags.CCNC,
)
KIA_SPORTAGE_HEV_2026 = HyundaiCanFDPlatformConfig(
[HyundaiCarDocs("Kia Sportage Hybrid 2026", car_parts=CarParts.common([CarHarness.hyundai_n]))],
CarSpecs(mass=1812, wheelbase=2.756, steerRatio=13.7),
@@ -702,11 +535,6 @@ class CAR(Platforms):
CarSpecs(mass=3957 * CV.LB_TO_KG, wheelbase=2.81, steerRatio=13.5), # average of the platforms
flags=HyundaiFlags.RADAR_SCC,
)
KIA_SORENTO_2024 = HyundaiCanFDPlatformConfig(
[HyundaiCarDocs("Kia Sorento (without HDA II) 2024-25", car_parts=CarParts.common([CarHarness.hyundai_a]))],
CarSpecs(mass=3957 * CV.LB_TO_KG, wheelbase=2.81, steerRatio=13.5),
flags=HyundaiFlags.CCNC,
)
KIA_SORENTO_HEV_4TH_GEN = HyundaiCanFDPlatformConfig(
[
HyundaiCarDocs("Kia Sorento Hybrid 2021-23", "All", car_parts=CarParts.common([CarHarness.hyundai_a])),
@@ -715,13 +543,6 @@ class CAR(Platforms):
CarSpecs(mass=4395 * CV.LB_TO_KG, wheelbase=2.81, steerRatio=13.5), # average of the platforms
flags=HyundaiFlags.RADAR_SCC,
)
KIA_SORENTO_HEV_4TH_GEN_LFA2 = HyundaiCanFDPlatformConfig(
[
HyundaiCarDocs("Kia Sorento Hybrid 2026", "All", car_parts=CarParts.common([CarHarness.hyundai_q])),
],
CarSpecs(mass=1970, wheelbase=2.814, steerRatio=13.27, tireStiffnessFactor=0.65),
flags=HyundaiFlags.CANFD_ANGLE_STEERING,
)
KIA_STINGER = HyundaiPlatformConfig(
[HyundaiCarDocs("Kia Stinger 2018-20", video="https://www.youtube.com/watch?v=MJ94qoofYw0",
car_parts=CarParts.common([CarHarness.hyundai_c]))],
@@ -736,36 +557,14 @@ class CAR(Platforms):
CarSpecs(mass=1450, wheelbase=2.65, steerRatio=13.75, tireStiffnessFactor=0.5),
flags=HyundaiFlags.LEGACY,
)
KIA_XCEED_PHEV = HyundaiPlatformConfig(
[HyundaiCarDocs("Kia XCeed Plug-in Hybrid 2021", car_parts=CarParts.common([CarHarness.hyundai_b]))],
CarSpecs(mass=1650, wheelbase=2.65, steerRatio=13.75, tireStiffnessFactor=0.5),
flags=HyundaiFlags.LEGACY | HyundaiFlags.HYBRID | HyundaiFlags.MANDO_RADAR,
)
KIA_EV6 = HyundaiCanFDPlatformConfig(
[
HyundaiCarDocs("Kia EV6 (Southeast Asia only) 2022-24", "All", car_parts=CarParts.common([CarHarness.hyundai_p])),
HyundaiCarDocs("Kia EV6 (without HDA II) 2022-24", "Highway Driving Assist", car_parts=CarParts.common([CarHarness.hyundai_l])),
HyundaiCarDocs("Kia EV6 (with HDA II) 2022-24", "Highway Driving Assist II", car_parts=CarParts.common([CarHarness.hyundai_p]))
],
CarSpecs(mass=2055, wheelbase=2.9, steerRatio=14.25, tireStiffnessFactor=0.65),
CarSpecs(mass=2055, wheelbase=2.9, steerRatio=16, tireStiffnessFactor=0.65),
flags=HyundaiFlags.EV,
radar_dbc=HYUNDAI_MRR30_RADAR_DBC,
)
KIA_EV6_2025 = HyundaiCanFDPlatformConfig(
[
HyundaiCarDocs("Kia EV6 (with HDA I) 2025", "Highway Driving Assist I", car_parts=CarParts.common([CarHarness.hyundai_p]))
],
CarSpecs(mass=2055, wheelbase=2.9, steerRatio=14.26, tireStiffnessFactor=0.65),
flags=HyundaiFlags.EV | HyundaiFlags.CANFD_ANGLE_STEERING,
radar_dbc=HYUNDAI_MRR30_RADAR_DBC,
)
KIA_EV9 = HyundaiCanFDPlatformConfig(
[
HyundaiCarDocs("Kia EV9 2025-26", car_parts=CarParts.common([CarHarness.hyundai_r]))
],
CarSpecs(mass=2664, wheelbase=3.1, steerRatio=16),
flags=HyundaiFlags.EV | HyundaiFlags.CANFD_ANGLE_STEERING,
radar_dbc=HYUNDAI_MRR35_RADAR_DBC,
)
KIA_CARNIVAL_4TH_GEN = HyundaiCanFDPlatformConfig(
[
@@ -784,7 +583,6 @@ class CAR(Platforms):
],
CarSpecs(mass=2205, wheelbase=2.9, steerRatio=17.6),
flags=HyundaiFlags.EV,
radar_dbc=HYUNDAI_MRR30_RADAR_DBC,
)
GENESIS_G70 = HyundaiPlatformConfig(
[HyundaiCarDocs("Genesis G70 2018", "All", car_parts=CarParts.common([CarHarness.hyundai_f]))],
@@ -818,14 +616,6 @@ class CAR(Platforms):
CarSpecs(mass=2260, wheelbase=2.87, steerRatio=17.1),
flags=HyundaiFlags.EV,
)
GENESIS_GV70_ELECTRIFIED_2ND_GEN = HyundaiCanFDPlatformConfig(
[
HyundaiCarDocs("Genesis GV70 Electrified 2026", "All", car_parts=CarParts.common([CarHarness.hyundai_m])),
],
GENESIS_GV70_ELECTRIFIED_1ST_GEN.specs,
flags=HyundaiFlags.EV | HyundaiFlags.CANFD_ANGLE_STEERING,
radar_dbc=HYUNDAI_MRR35_RADAR_DBC,
)
GENESIS_G80 = HyundaiPlatformConfig(
[HyundaiCarDocs("Genesis G80 2018-19", "All", car_parts=CarParts.common([CarHarness.hyundai_h]))],
CarSpecs(mass=2060, wheelbase=3.01, steerRatio=16.5),
@@ -838,76 +628,12 @@ class CAR(Platforms):
GENESIS_G90 = HyundaiPlatformConfig(
[HyundaiCarDocs("Genesis G90 2017-20", "All", car_parts=CarParts.common([CarHarness.hyundai_c]))],
CarSpecs(mass=2200, wheelbase=3.15, steerRatio=12.069),
flags=HyundaiFlags.MANDO_RADAR,
)
GENESIS_GV80 = HyundaiCanFDPlatformConfig(
[HyundaiCarDocs("Genesis GV80 2023", "All", car_parts=CarParts.common([CarHarness.hyundai_m]))],
CarSpecs(mass=2258, wheelbase=2.95, steerRatio=14.14),
flags=HyundaiFlags.RADAR_SCC,
)
GENESIS_GV80_2025 = HyundaiCanFDPlatformConfig(
[
HyundaiCarDocs("Genesis GV80 (3.5T Prestige Trim, with HDA II & LFA2) 2025", "Highway Driving Assist II & Lane Follow Assist 2",
car_parts=CarParts.common([CarHarness.hyundai_q])),
HyundaiCarDocs("Genesis GV80 Coupe (with HDA II & LFA2) 2025", "Highway Driving Assist II & Lane Follow Assist 2",
car_parts=CarParts.common([CarHarness.hyundai_q])),
],
GENESIS_GV80.specs,
flags=HyundaiFlags.CANFD_ANGLE_STEERING,
radar_dbc=HYUNDAI_MRR35_RADAR_DBC,
)
# 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,
radar_dbc=HYUNDAI_MRREVO14F_RADAR_DBC,
)
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:
@@ -918,38 +644,6 @@ class Buttons:
CANCEL = 4 # on newer models, this is a pause/resume button
CANCEL_BUTTON_ENABLE_CARS = frozenset({
CAR.HYUNDAI_PALISADE_2023,
})
KIA_EV6_GT_LINE_LONG_TUNING_VDS_PREFIXES = frozenset({
"C4DLC",
})
# These classic HKG platforms publish the LKAS button on CLU13 over the alt bus.
# Keep G90 excluded until its alt-bus path is route-proven without the recent
# engage/disengage regression.
ALT_BUS_LDA_BUTTON_CARS = frozenset({
CAR.HYUNDAI_SONATA,
})
# On these Sonata layouts the alt-bus LKAS button pulses through the CLU13
# steering-wheel-status field instead of the dedicated LKAS bit.
ALT_BUS_LDA_BUTTON_SWL_STAT_CARS = frozenset({
CAR.HYUNDAI_SONATA,
})
def hyundai_cancel_button_enables_cruise(car_fingerprint) -> bool:
return car_fingerprint in CANCEL_BUTTON_ENABLE_CARS
def kia_ev6_gt_line_longitudinal_tuning(car_fingerprint, vin: str) -> bool:
return car_fingerprint == CAR.KIA_EV6 and isinstance(vin, str) and \
len(vin) == 17 and vin[3:8] in KIA_EV6_GT_LINE_LONG_TUNING_VDS_PREFIXES
def get_platform_codes(fw_versions: list[bytes]) -> set[tuple[bytes, bytes | None]]:
# Returns unique, platform-specific identification codes for a set of versions
codes = set() # (code-Optional[part], date)
@@ -1039,7 +733,7 @@ PART_NUMBER_FW_PATTERN = re.compile(b'(?<=[0-9][.,][0-9]{2} )([0-9]{5}[-/]?[A-Z]
# We've seen both ICE and hybrid for these platforms, and they have hybrid descriptors (e.g. MQ4 vs MQ4H)
CANFD_FUZZY_WHITELIST = {CAR.KIA_SORENTO_4TH_GEN, CAR.KIA_SORENTO_HEV_4TH_GEN, CAR.KIA_K8_HEV_1ST_GEN,
# TODO: the hybrid variant is not out yet
CAR.KIA_CARNIVAL_4TH_GEN, CAR.KIA_SORENTO_HEV_4TH_GEN_LFA2}
CAR.KIA_CARNIVAL_4TH_GEN}
# List of ECUs expected to have platform codes, camera and radar should exist on all cars
# TODO: use abs, it has the platform code and part number on many platforms
@@ -1104,10 +798,11 @@ 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_XCEED_PHEV, CAR.KIA_SELTOS],
Ecu.fwdRadar: [CAR.HYUNDAI_KONA_NON_SCC],
CAR.KIA_CEED, CAR.KIA_SELTOS],
},
extra_ecus=[
(Ecu.adas, 0x730, None), # ADAS Driving ECU on platforms with LKA steering
@@ -1134,22 +829,9 @@ CAN_GEARS = {
CANFD_CAR = CAR.with_flags(HyundaiFlags.CANFD)
CANFD_RADAR_SCC_CAR = CAR.with_flags(HyundaiFlags.RADAR_SCC) # TODO: merge with UNSUPPORTED_LONGITUDINAL_CAR
# CAN-FD cars with ADAS ECUs that work with the communication-control path.
CANFD_SECURITYACCESS_CAR = {CAR.HYUNDAI_IONIQ_5, CAR.HYUNDAI_IONIQ_6, CAR.HYUNDAI_KONA_EV_2ND_GEN}
# Cars with CANFD_NO_RADAR_DISABLE that now work with SecurityAccess handshake
CANFD_SECURITYACCESS_CAR = {CAR.HYUNDAI_IONIQ_6, CAR.HYUNDAI_KONA_EV_2ND_GEN}
CANFD_UNSUPPORTED_LONGITUDINAL_CAR = CAR.with_flags(HyundaiFlags.CANFD_NO_RADAR_DISABLE) - CANFD_SECURITYACCESS_CAR # TODO: merge with UNSUPPORTED_LONGITUDINAL_CAR
CANFD_ANGLE_LONGITUDINAL_CAR = set()
CANFD_RADAR_LIVE_LONGITUDINAL_CAR = {CAR.HYUNDAI_IONIQ_5, CAR.HYUNDAI_IONIQ_6, CAR.KIA_EV6, CAR.GENESIS_GV60_EV_1ST_GEN}
RADAR_LIVE_LONGITUDINAL_CAR = CANFD_RADAR_LIVE_LONGITUDINAL_CAR | {
CAR.HYUNDAI_IONIQ,
CAR.HYUNDAI_KONA_EV_2022,
CAR.HYUNDAI_SANTA_FE_2022,
CAR.HYUNDAI_SANTA_FE_HEV_2022,
CAR.HYUNDAI_SANTA_FE_PHEV_2022,
CAR.HYUNDAI_SONATA,
CAR.HYUNDAI_SONATA_HYBRID,
CAR.KIA_XCEED_PHEV,
CAR.GENESIS_G90,
}
CAMERA_SCC_CAR = CAR.with_flags(HyundaiFlags.CAMERA_SCC)
@@ -1159,12 +841,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)
LEGACY_LONGITUDINAL_CAR = {CAR.KIA_XCEED_PHEV}
DBC = CAR.create_dbc_map()
+8 -45
View File
@@ -18,16 +18,14 @@ 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, HondaFlags, HondaSafetyFlags, HondaStarPilotFlags
from opendbc.car.honda.values import CAR as HONDA, HONDA_BOSCH, HONDA_CAMERA_MESSAGE_CARS, HondaSafetyFlags, HondaStarPilotFlags
from opendbc.car.hyundai.hyundaicanfd import CanBus
from opendbc.car.hyundai.values import CAR as HYUNDAI, CANFD_CAR, HyundaiFlags, HyundaiStarPilotFlags, HyundaiStarPilotSafetyFlags, ALT_BUS_LDA_BUTTON_CARS
from opendbc.car.hyundai.values import CAR as HYUNDAI, CANFD_CAR, HyundaiFlags, HyundaiStarPilotFlags, HyundaiStarPilotSafetyFlags
from opendbc.car.mock.values import CAR as MOCK
from opendbc.car.subaru.values import CAR as SUBARU, SubaruSafetyFlags
from opendbc.car.toyota.values import CAR as TOYOTA, NO_DSU_CAR, TSS2_CAR, UNSUPPORTED_DSU_CAR, ToyotaStarPilotFlags, ToyotaSafetyFlags
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
@@ -134,7 +132,6 @@ class CarInterfaceBase(ABC):
dbc_names = {bus: cp.dbc_name for bus, cp in self.can_parsers.items()}
self.CC: CarControllerBase = self.CarController(dbc_names, CP)
self.CC.FPCP = FPCP
self.FPCP = FPCP
@@ -176,11 +173,8 @@ class CarInterfaceBase(ABC):
ret = cls._get_params(ret, candidate, fingerprint, car_fw, alpha_long, is_release, docs)
trailer_load_kg = float(np.clip(getattr(starpilot_toggles, "trailer_load_kg", 0.0) or 0.0, 0.0, 15000.0 * CV.LB_TO_KG))
# Vehicle mass is published curb weight plus assumed payload such as a human driver; notCars have no assumed payload
if not ret.notCar:
ret.mass = ret.mass + trailer_load_kg
ret.mass = ret.mass + STD_CARGO_KG
# Set params dependent on values set by the car interface
@@ -188,14 +182,7 @@ 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")
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
):
if ret.steerControlType != structs.CarParams.SteerControlType.angle and any(getattr(starpilot_toggles, toggle, False) for toggle in toggles_to_check):
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
return ret
@@ -203,8 +190,6 @@ 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
params = Params(return_defaults=True)
platform = PLATFORMS[candidate]
@@ -216,16 +201,14 @@ class CarInterfaceBase(ABC):
if candidate == CHRYSLER.RAM_HD_5TH_GEN:
if 570 not in fingerprint[0]:
fp_ret.flags |= ChryslerStarPilotFlags.RAM_HD_ALT_BUTTONS.value
if 0x4FF in fingerprint[0]:
fp_ret.flags |= ChryslerStarPilotFlags.NO_MIN_STEERING_SPEED.value
CP.minSteerSpeed = 0.
elif platform in GM:
fp_ret.canUsePedal = True
elif platform in HONDA:
fp_ret.canUsePedal = candidate not in HONDA_BOSCH
if any(0x35E in bus_fingerprint for bus_fingerprint in fingerprint.values()):
# Only enable TSR parsing on Hondas confirmed to publish CAMERA_MESSAGES.
if candidate in HONDA_CAMERA_MESSAGE_CARS:
fp_ret.flags |= int(HondaStarPilotFlags.HAS_CAMERA_MESSAGES)
elif platform in HYUNDAI:
@@ -237,23 +220,11 @@ class CarInterfaceBase(ABC):
if 0x1FA in fingerprint[CAN.ECAN]:
fp_ret.flags |= HyundaiStarPilotFlags.SPEED_LIMIT_AVAILABLE.value
fp_ret.redneckCruiseAvailable = bool(CP.flags & HyundaiFlags.NON_SCC) and not bool(CP.flags & HyundaiFlags.CANFD_ALT_BUTTONS)
if fp_ret.redneckCruiseAvailable and params.get_bool("RedneckCruise") and \
not CP.openpilotLongitudinalControl:
fp_ret.pcmCruiseSpeed = False
hyundai_has_lda_button = (
0x391 in fingerprint[0] or
0x50C in fingerprint[0] or
candidate in ALT_BUS_LDA_BUTTON_CARS or
bool(CP.flags & HyundaiFlags.CAN_CANFD_BLENDED)
)
if hyundai_has_lda_button:
if CP.flags & HyundaiFlags.HAS_LDA_BUTTON:
fp_ret.safetyConfigs[-1].safetyParam |= HyundaiStarPilotSafetyFlags.HAS_LDA_BUTTON.value
# LKASButtonControl == 9 means BUTTON_FUNCTIONS["AOL_TOGGLE"] in starpilot_variables.
if params.get_bool("AlwaysOnLateral") and params.get_int("LKASButtonControl") == 9:
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
@@ -268,13 +239,6 @@ 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
elif platform in SUBARU:
if getattr(starpilot_toggles, "subaru_sng", False):
fp_ret.safetyConfigs[-1].safetyParam |= SubaruSafetyFlags.STOP_AND_GO.value
return fp_ret
@staticmethod
@@ -509,7 +473,6 @@ class CarStateBase(ABC):
class CarControllerBase(ABC):
def __init__(self, dbc_names: dict[StrEnum, str], CP: structs.CarParams):
self.CP = CP
self.FPCP: custom.StarPilotCarParams | None = None
self.frame = 0
self.secoc_key: bytes = b"00" * 16
+1 -3
View File
@@ -118,13 +118,11 @@ 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], [("CAM_TRAFFIC_SIGNS", 0)], 2),
Bus.cam: CANParser(DBC[CP.carFingerprint][Bus.pt], [], 2),
}
@@ -1,6 +1,6 @@
import numpy as np
from opendbc.can import CANPacker
from opendbc.car import Bus, DT_CTRL, make_tester_present_msg
from opendbc.car import Bus, make_tester_present_msg
from opendbc.car.lateral import apply_driver_steer_torque_limits, common_fault_avoidance
from opendbc.car.interfaces import CarControllerBase
from opendbc.car.subaru import subarucan
@@ -26,9 +26,14 @@ class CarController(CarControllerBase):
self.p = CarControllerParams(CP)
self.packer = CANPacker(DBC[CP.carFingerprint][Bus.pt])
self.manual_hold = False
self.prev_standstill = False
self.sng_acc_resume = False
self.prev_close_distance = 0
self.epb_resume_frames_remaining = -1
self.last_standstill_frame = 0
self.prev_cruise_state = 0
self.sng_acc_resume_cnt = 0
self.standstill_start = 0
def update(self, CC, CS, now_nanos, starpilot_toggles):
actuators = CC.actuators
@@ -65,9 +70,8 @@ class CarController(CarControllerBase):
self.apply_torque_last = apply_torque
# *** stop and go ***
subaru_sng_manual_parking_brake = getattr(starpilot_toggles, "subaru_sng_manual_parking_brake", False)
if starpilot_toggles.subaru_sng:
throttle_cmd, speed_cmd = self.stop_and_go(CC, CS, subaru_sng_manual_parking_brake)
throttle_cmd, speed_cmd = self.stop_and_go(CC, CS)
# *** longitudinal ***
@@ -106,11 +110,7 @@ class CarController(CarControllerBase):
can_sends.append(subarucan.create_preglobal_es_distance(self.packer, cruise_button, CS.es_distance_msg))
if starpilot_toggles.subaru_sng:
can_sends.append(subarucan.create_preglobal_throttle(self.packer, CS.throttle_msg["COUNTER"] + 1, CS.throttle_msg,
throttle_cmd))
if self.frame % 2 == 0:
can_sends.append(subarucan.create_preglobal_brake_pedal(self.packer, CS.brake_pedal_msg,
speed_cmd))
can_sends.append(subarucan.create_preglobal_throttle(self.packer, CS.throttle_msg["COUNTER"] + 1, CS.throttle_msg, throttle_cmd))
else:
if self.frame % 10 == 0:
can_sends.append(subarucan.create_es_dashstatus(self.packer, self.frame // 10, CS.es_dashstatus_msg, CC.enabled,
@@ -124,11 +124,9 @@ class CarController(CarControllerBase):
can_sends.append(subarucan.create_es_infotainment(self.packer, self.frame // 10, CS.es_infotainment_msg, hud_control.visualAlert))
if starpilot_toggles.subaru_sng:
can_sends.append(subarucan.create_throttle(self.packer, CS.throttle_msg["COUNTER"] + 1, CS.throttle_msg,
throttle_cmd))
can_sends.append(subarucan.create_throttle(self.packer, CS.throttle_msg["COUNTER"] + 1, CS.throttle_msg, throttle_cmd))
if self.frame % 2 == 0:
can_sends.append(subarucan.create_brake_pedal(self.packer, self.frame // 2, CS.brake_pedal_msg,
speed_cmd, pcm_cancel_cmd))
can_sends.append(subarucan.create_brake_pedal(self.packer, self.frame // 2, CS.brake_pedal_msg, speed_cmd, pcm_cancel_cmd))
if self.CP.openpilotLongitudinalControl:
if self.frame % 5 == 0:
@@ -168,37 +166,49 @@ class CarController(CarControllerBase):
self.frame += 1
return new_actuators, can_sends
def stop_and_go(self, CC, CS, manual_parking_brake=False):
throttle_cmd = False
speed_cmd = False
def stop_and_go(self, CC, CS, speed_cmd=False, throttle_cmd=False):
if self.CP.flags & SubaruFlags.PREGLOBAL:
trigger_resume = CC.enabled
trigger_resume &= CS.car_follow == 1
trigger_resume &= CS.close_distance > self.prev_close_distance
trigger_resume &= CS.out.standstill
trigger_resume &= _SNG_ACC_MIN_DIST < CS.close_distance < _SNG_ACC_MAX_DIST
if not CC.enabled or not CC.hudControl.leadVisible:
return throttle_cmd, speed_cmd
if trigger_resume:
self.sng_acc_resume = True
else:
if CS.car_follow == 0 and CS.cruise_state == 3 and CS.out.standstill and self.prev_cruise_state == 1:
self.manual_hold = True
close_distance = CS.close_distance
if not CS.out.standstill:
self.last_standstill_frame = self.frame
if not CS.out.standstill:
self.manual_hold = False
standstill_timers = (0.75, 0.8) if self.CP.flags & SubaruFlags.PREGLOBAL else (0.5, 0.55)
standstill_duration = (self.frame - self.last_standstill_frame) * DT_CTRL
in_standstill_hold = standstill_duration > standstill_timers[0]
if standstill_duration >= standstill_timers[1]:
self.last_standstill_frame = self.frame
trigger_resume = CC.enabled
trigger_resume &= CS.car_follow == 1
trigger_resume &= CS.close_distance > self.prev_close_distance
trigger_resume &= CS.cruise_state == 3
trigger_resume &= not self.manual_hold
trigger_resume &= _SNG_ACC_MIN_DIST < CS.close_distance < _SNG_ACC_MAX_DIST
if manual_parking_brake or not (self.CP.flags & SubaruFlags.PREGLOBAL):
speed_cmd = in_standstill_hold
if trigger_resume:
self.sng_acc_resume = True
should_resume = (
CS.out.standstill and
_SNG_ACC_MIN_DIST < close_distance < _SNG_ACC_MAX_DIST and
close_distance > self.prev_close_distance
)
if should_resume:
self.epb_resume_frames_remaining = 15
if CC.enabled and CS.car_follow == 1 and CS.out.standstill and self.frame > self.standstill_start + 50:
speed_cmd = True
throttle_cmd = self.epb_resume_frames_remaining > 0
if self.epb_resume_frames_remaining > 0:
self.epb_resume_frames_remaining -= 1
if CS.out.standstill and not self.prev_standstill:
self.standstill_start = self.frame
self.prev_close_distance = close_distance
self.prev_standstill = CS.out.standstill
self.prev_cruise_state = CS.cruise_state
if self.sng_acc_resume:
if self.sng_acc_resume_cnt < 5:
throttle_cmd = True
self.sng_acc_resume_cnt += 1
else:
self.sng_acc_resume = False
self.sng_acc_resume_cnt = -1
self.prev_close_distance = CS.close_distance
return throttle_cmd, speed_cmd
@@ -358,19 +358,6 @@ def create_brake_pedal(packer, frame, brake_pedal_msg, speed_cmd, brake_cmd):
return packer.make_can_msg("Brake_Pedal", CanBus.camera, values)
def create_preglobal_brake_pedal(packer, brake_pedal_msg, speed_cmd):
values = {s: brake_pedal_msg[s] for s in sorted([
"Brake_Pedal",
"Signal1",
"Speed",
])}
if speed_cmd:
values["Speed"] = 1
return packer.make_can_msg("Brake_Pedal", CanBus.camera, values)
def create_throttle(packer, frame, throttle_msg, throttle_cmd):
values = {s: throttle_msg[s] for s in sorted([
"CHECKSUM",
@@ -1,57 +1,4 @@
from types import SimpleNamespace
from opendbc.car.subaru.carcontroller import CarController
from opendbc.car.subaru.fingerprints import FW_VERSIONS
from opendbc.car.subaru.values import SubaruFlags
def make_sng_controller(flags=0, prev_close_distance=4.0):
controller = object.__new__(CarController)
controller.CP = SimpleNamespace(flags=flags)
controller.frame = 60
controller.last_standstill_frame = 0
controller.prev_close_distance = prev_close_distance
controller.epb_resume_frames_remaining = -1
return controller
def make_sng_state(close_distance=4.0, standstill=True):
cc = SimpleNamespace(enabled=True, hudControl=SimpleNamespace(leadVisible=True))
cs = SimpleNamespace(
close_distance=close_distance,
out=SimpleNamespace(standstill=standstill),
)
return cc, cs
def test_global_sng_keeps_standstill_alive_without_manual_parking_brake_toggle():
controller = make_sng_controller()
cc, cs = make_sng_state()
throttle_cmd, speed_cmd = controller.stop_and_go(cc, cs, manual_parking_brake=False)
assert throttle_cmd is False
assert speed_cmd is True
def test_manual_parking_brake_sng_still_sends_resume_throttle():
controller = make_sng_controller(prev_close_distance=3.9)
cc, cs = make_sng_state(close_distance=4.0)
throttle_cmd, speed_cmd = controller.stop_and_go(cc, cs, manual_parking_brake=True)
assert throttle_cmd is True
assert speed_cmd is True
def test_preglobal_sng_does_not_send_standstill_keepalive_without_manual_toggle():
controller = make_sng_controller(flags=SubaruFlags.PREGLOBAL)
cc, cs = make_sng_state()
throttle_cmd, speed_cmd = controller.stop_and_go(cc, cs, manual_parking_brake=False)
assert throttle_cmd is False
assert speed_cmd is False
class TestSubaruFingerprint:
@@ -59,7 +59,6 @@ class SubaruSafetyFlags(IntFlag):
GEN2 = 1
LONG = 2
PREGLOBAL_REVERSED_DRIVER_TORQUE = 4
STOP_AND_GO = 8
class SubaruFlags(IntFlag):
@@ -4,14 +4,15 @@ 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.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.tesla.values import 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(CAR.TESLA_MODEL_Y)
return CarInterface.get_non_essential_params("TESLA_MODEL_Y")
class CarController(CarControllerBase):
@@ -20,23 +21,11 @@ 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 = []
@@ -75,45 +64,3 @@ 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
+1 -35
View File
@@ -5,10 +5,6 @@ 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
@@ -17,8 +13,7 @@ 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"] if CP.carFingerprint != CAR.TESLA_MODEL_S_PREAP else \
self.can_define.dv["DI_torque2"]["DI_gear"]
self.shifter_values = self.can_define.dv["DI_systemStatus"]["DI_gear"]
self.autopark = False
self.autopark_prev = False
@@ -26,25 +21,6 @@ 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")
@@ -55,15 +31,7 @@ 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()
@@ -156,8 +124,6 @@ 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,22 +45,3 @@ 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,29 +2,17 @@ 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
@@ -1 +0,0 @@
@@ -1,64 +0,0 @@
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
@@ -1 +0,0 @@
@@ -1,91 +0,0 @@
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
@@ -1,154 +0,0 @@
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
fp_ret.pedalMaxRegen = cs.pccEvent == "pedalMaxRegen"
fp_ret.teslaCCEngaged = cs.pccEvent == "teslaCCEngaged"
fp_ret.teslaCCDisengaged = cs.pccEvent == "teslaCCDisengaged"
fp_ret.teslaCCNotArmed = (
not nap_conf.use_pedal and
cs.cruiseEnabled and
cs.enableLongControl and
cs.di_cruise_state not in ("STANDBY", "ENABLED")
)
fp_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),
}
@@ -1,16 +0,0 @@
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
@@ -1,148 +0,0 @@
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)
@@ -1,11 +0,0 @@
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],
]
@@ -1,57 +0,0 @@
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
SAFETY_TESLA_PREAP = 35
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(SAFETY_TESLA_PREAP, 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
@@ -1,89 +0,0 @@
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()
@@ -1,15 +0,0 @@
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"
@@ -1,41 +0,0 @@
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
@@ -1,62 +0,0 @@
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)
@@ -1,96 +0,0 @@
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)
@@ -1,139 +0,0 @@
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))
@@ -1,87 +0,0 @@
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

Some files were not shown because too many files have changed in this diff Show More