Merge branch 'upstream/openpilot/master' into sync-20240216

# Conflicts:
#	cereal
#	opendbc
#	panda
#	selfdrive/car/hyundai/interface.py
#	selfdrive/controls/controlsd.py
#	selfdrive/monitoring/dmonitoringd.py
This commit is contained in:
Jason Wen
2024-02-16 08:27:39 -05:00
131 changed files with 1706 additions and 1183 deletions
+1 -1
View File
@@ -47,7 +47,7 @@ jobs:
export PYTHONPATH=$TARGET_DIR
cd $TARGET_DIR
scons -j$(nproc)
selfdrive/car/tests/test_car_interfaces.py
pytest -n logical selfdrive/car/tests/test_car_interfaces.py
- name: Push master-ci
run: |
unset TARGET_DIR
+3 -1
View File
@@ -2,7 +2,7 @@ name: repo maintenance
on:
schedule:
- cron: "0 12 * * 1" # every Monday at 12am UTC (4am PST)
- cron: "0 14 * * 1" # every Monday at 2am UTC (6am PST)
workflow_dispatch:
jobs:
@@ -11,6 +11,7 @@ jobs:
runs-on: ubuntu-20.04
container:
image: ghcr.io/commaai/openpilot-base:latest
if: github.repository == 'commaai/openpilot'
steps:
- uses: actions/checkout@v4
with:
@@ -36,6 +37,7 @@ jobs:
runs-on: ubuntu-20.04
container:
image: ghcr.io/commaai/openpilot-base:latest
if: github.repository == 'commaai/openpilot'
steps:
- uses: actions/checkout@v4
- name: poetry lock
+6 -21
View File
@@ -14,7 +14,6 @@ concurrency:
env:
PYTHONWARNINGS: error
BASE_IMAGE: openpilot-base
CL_BASE_IMAGE: openpilot-base-cl
AZURE_TOKEN: ${{ secrets.AZURE_COMMADATACI_OPENPILOTCI_TOKEN }}
DOCKER_LOGIN: docker login ghcr.io -u ${{ github.actor }} -p ${{ secrets.GITHUB_TOKEN }}
@@ -22,11 +21,7 @@ env:
RUN: docker run --shm-size 1G -v $PWD:/tmp/openpilot -w /tmp/openpilot -e CI=1 -e PRE_COMMIT_HOME=/tmp/pre-commit -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/pre-commit:/tmp/pre-commit -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
BUILD_CL: selfdrive/test/docker_build.sh cl
RUN_CL: docker run --shm-size 1G -v $PWD:/tmp/openpilot -w /tmp/openpilot -e CI=1 -e PYTHONWARNINGS=error -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 $CL_BASE_IMAGE /bin/bash -c
PYTEST: pytest --continue-on-collection-errors --cov --cov-report=xml --cov-append --durations=0 --durations-min=5 --hypothesis-seed 0
PYTEST: pytest --continue-on-collection-errors --cov --cov-report=xml --cov-append --durations=0 --durations-min=5 --hypothesis-seed 0 -n logical
jobs:
build_release:
@@ -106,11 +101,6 @@ jobs:
- uses: ./.github/workflows/setup-with-retry
with:
docker_hub_pat: ${{ secrets.DOCKER_HUB_PAT }}
- name: Build and push CL Docker image
if: matrix.arch == 'x86_64'
run: |
unset TARGET_ARCHITECTURE
eval "$BUILD_CL"
docker_push_multiarch:
name: docker push multiarch tag
@@ -182,7 +172,7 @@ jobs:
run: |
${{ env.RUN }} "source selfdrive/test/setup_xvfb.sh && \
export MAPBOX_TOKEN='pk.eyJ1Ijoiam5ld2IiLCJhIjoiY2xxNW8zZXprMGw1ZzJwbzZneHd2NHljbSJ9.gV7VPRfbXFetD-1OVF0XZg' && \
$PYTEST --timeout 60 -m 'not slow' -n $(nproc) && \
$PYTEST --timeout 60 -m 'not slow' && \
./selfdrive/ui/tests/create_test_translations.sh && \
QT_QPA_PLATFORM=offscreen ./selfdrive/ui/tests/test_translations && \
./selfdrive/ui/tests/test_translations.py"
@@ -258,15 +248,13 @@ jobs:
key: regen-${{ hashFiles('.github/workflows/selfdrive_tests.yaml', 'selfdrive/test/process_replay/test_regen.py') }}
- name: Build base Docker image
run: eval "$BUILD"
- name: Build Docker image
run: eval "$BUILD_CL"
- name: Build openpilot
run: |
${{ env.RUN }} "scons -j$(nproc)"
- name: Run regen
timeout-minutes: 30
run: |
${{ env.RUN_CL }} "ONNXCPU=1 $PYTEST selfdrive/test/process_replay/test_regen.py && \
${{ env.RUN }} "ONNXCPU=1 $PYTEST selfdrive/test/process_replay/test_regen.py && \
chmod -R 777 /tmp/comma_download_cache"
test_modeld:
@@ -279,9 +267,6 @@ jobs:
- uses: ./.github/workflows/setup-with-retry
- name: Build base Docker image
run: eval "$BUILD"
- name: Build Docker image
# Sim docker is needed to get the OpenCL drivers
run: eval "$BUILD_CL"
- name: Build openpilot
run: |
${{ env.RUN }} "scons -j$(nproc)"
@@ -289,14 +274,14 @@ jobs:
- name: Run model replay with ONNX
timeout-minutes: 4
run: |
${{ env.RUN_CL }} "unset PYTHONWARNINGS && \
${{ env.RUN }} "unset PYTHONWARNINGS && \
ONNXCPU=1 NO_NAV=1 coverage run selfdrive/test/process_replay/model_replay.py && \
coverage combine && \
coverage xml"
- name: Run unit tests
timeout-minutes: 4
run: |
${{ env.RUN_CL }} "unset PYTHONWARNINGS && \
${{ env.RUN }} "unset PYTHONWARNINGS && \
$PYTEST selfdrive/modeld"
- name: "Upload coverage to Codecov"
uses: codecov/codecov-action@v3
@@ -328,7 +313,7 @@ jobs:
- name: Build openpilot
run: ${{ env.RUN }} "scons -j$(nproc)"
- name: Test car models
timeout-minutes: 25
timeout-minutes: 10
run: |
${{ env.RUN }} "$PYTEST selfdrive/car/tests/test_models.py && \
chmod -R 777 /tmp/comma_download_cache"
+1 -8
View File
@@ -12,17 +12,12 @@ concurrency:
env:
BASE_IMAGE: openpilot-base
CL_BASE_IMAGE: openpilot-base-cl
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 1G -v $GITHUB_WORKSPACE:/tmp/openpilot -w /tmp/openpilot -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
BUILD_CL: selfdrive/test/docker_build.sh cl
RUN_CL: docker run --shm-size 1G -v $GITHUB_WORKSPACE:/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 $CL_BASE_IMAGE /bin/bash -c
jobs:
plotjuggler:
@@ -52,8 +47,6 @@ jobs:
with:
submodules: true
- uses: ./.github/workflows/setup-with-retry
- name: Build base cl image
run: eval "$BUILD_CL"
- name: Setup to push to repo
if: github.ref == 'refs/heads/master' && github.repository == 'commaai/openpilot'
run: |
@@ -104,6 +97,6 @@ jobs:
timeout-minutes: ${{ ((steps.restore-scons-cache.outputs.cache-hit == 'true') && 10 || 30) }} # allow more time when we missed the scons cache
run: ${{ env.RUN }} "scons -j$(nproc)"
- name: Test notebooks
timeout-minutes: 2
timeout-minutes: 3
run: |
${{ env.RUN }} "pip install nbmake && pytest --nbmake tools/car_porting/examples/"
+2 -2
View File
@@ -44,7 +44,7 @@ repos:
- --explicit-package-bases
exclude: '^(third_party/)|(cereal/)|(opendbc/)|(panda/)|(rednose/)|(rednose_repo/)|(tinygrad/)|(tinygrad_repo/)|(teleoprtc/)|(teleoprtc_repo/)|(xx/)'
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.1.14
rev: v0.2.1
hooks:
- id: ruff
exclude: '^(third_party/)|(cereal/)|(panda/)|(rednose/)|(rednose_repo/)|(tinygrad/)|(tinygrad_repo/)|(teleoprtc/)|(teleoprtc_repo/)'
@@ -90,6 +90,6 @@ repos:
args:
- --lock
- repo: https://github.com/python-jsonschema/check-jsonschema
rev: 0.27.3
rev: 0.28.0
hooks:
- id: check-github-workflows
+38
View File
@@ -22,6 +22,44 @@ RUN cd /tmp && \
rm -rf arm/ && \
rm -rf thumb/nofp thumb/v6* thumb/v8* thumb/v7+fp thumb/v7-r+fp.sp
# Add OpenCL
RUN apt-get update && apt-get install -y --no-install-recommends \
apt-utils \
alien \
unzip \
tar \
curl \
xz-utils \
dbus \
gcc-arm-none-eabi \
tmux \
vim \
lsb-core \
libx11-6 \
&& rm -rf /var/lib/apt/lists/*
ARG INTEL_DRIVER=l_opencl_p_18.1.0.015.tgz
ARG INTEL_DRIVER_URL=https://registrationcenter-download.intel.com/akdlm/irc_nas/vcp/15532
RUN mkdir -p /tmp/opencl-driver-intel
RUN cd /tmp/opencl-driver-intel && \
echo INTEL_DRIVER is $INTEL_DRIVER && \
curl -O $INTEL_DRIVER_URL/$INTEL_DRIVER && \
tar -xzf $INTEL_DRIVER && \
for i in $(basename $INTEL_DRIVER .tgz)/rpm/*.rpm; do alien --to-deb $i; done && \
dpkg -i *.deb && \
rm -rf $INTEL_DRIVER $(basename $INTEL_DRIVER .tgz) *.deb && \
mkdir -p /etc/OpenCL/vendors && \
echo /opt/intel/opencl_compilers_and_libraries_18.1.0.015/linux/compiler/lib/intel64_lin/libintelocl.so > /etc/OpenCL/vendors/intel.icd && \
cd / && \
rm -rf /tmp/opencl-driver-intel
ENV NVIDIA_VISIBLE_DEVICES all
ENV NVIDIA_DRIVER_CAPABILITIES graphics,utility,compute
ENV QTWEBENGINE_DISABLE_SANDBOX 1
RUN dbus-uuidgen > /etc/machine-id
ARG USER=batman
ARG USER_UID=1000
RUN useradd -m -s /bin/bash -u $USER_UID $USER
-37
View File
@@ -1,37 +0,0 @@
FROM ghcr.io/commaai/openpilot-base:latest
RUN apt-get update && apt-get install -y --no-install-recommends\
apt-utils \
alien \
unzip \
tar \
curl \
xz-utils \
dbus \
gcc-arm-none-eabi \
tmux \
vim \
lsb-core \
libx11-6 \
&& rm -rf /var/lib/apt/lists/*
# Intel OpenCL driver
ARG INTEL_DRIVER=l_opencl_p_18.1.0.015.tgz
ARG INTEL_DRIVER_URL=https://registrationcenter-download.intel.com/akdlm/irc_nas/vcp/15532
RUN mkdir -p /tmp/opencl-driver-intel
WORKDIR /tmp/opencl-driver-intel
RUN echo INTEL_DRIVER is $INTEL_DRIVER && \
curl -O $INTEL_DRIVER_URL/$INTEL_DRIVER && \
tar -xzf $INTEL_DRIVER && \
for i in $(basename $INTEL_DRIVER .tgz)/rpm/*.rpm; do alien --to-deb $i; done && \
dpkg -i *.deb && \
rm -rf $INTEL_DRIVER $(basename $INTEL_DRIVER .tgz) *.deb && \
mkdir -p /etc/OpenCL/vendors && \
echo /opt/intel/opencl_compilers_and_libraries_18.1.0.015/linux/compiler/lib/intel64_lin/libintelocl.so > /etc/OpenCL/vendors/intel.icd && \
rm -rf /tmp/opencl-driver-intel
ENV NVIDIA_VISIBLE_DEVICES all
ENV NVIDIA_DRIVER_CAPABILITIES graphics,utility,compute
ENV QTWEBENGINE_DISABLE_SANDBOX 1
RUN dbus-uuidgen > /etc/machine-id
Vendored
+13 -47
View File
@@ -12,10 +12,12 @@ def retryWithDelay(int maxRetries, int delay, Closure body) {
def device(String ip, String step_label, String cmd) {
withCredentials([file(credentialsId: 'id_rsa', variable: 'key_file')]) {
def ssh_cmd = """
ssh -tt -o ConnectTimeout=30 -o ServerAliveInterval=30 -o ServerAliveCountMax=3 -o BatchMode=yes -o StrictHostKeyChecking=no -i ${key_file} 'comma@${ip}' /usr/bin/bash <<'END'
ssh -tt -o ConnectTimeout=5 -o ServerAliveInterval=5 -o ServerAliveCountMax=2 -o BatchMode=yes -o StrictHostKeyChecking=no -i ${key_file} 'comma@${ip}' /usr/bin/bash <<'END'
set -e
shopt -s huponexit # kill all child processes when the shell exits
export CI=1
export PYTHONWARNINGS=error
export LOGPRINT=debug
@@ -78,17 +80,13 @@ def deviceStage(String stageName, String deviceType, List extra_env, def steps)
def extra = extra_env.collect { "export ${it}" }.join('\n');
def branch = env.BRANCH_NAME ?: 'master';
docker.image('ghcr.io/commaai/alpine-ssh').inside('--user=root') {
lock(resource: "", label: deviceType, inversePrecedence: true, variable: 'device_ip', quantity: 1, resourceSelectStrategy: 'random') {
lock(resource: "", label: deviceType, inversePrecedence: true, variable: 'device_ip', quantity: 1, resourceSelectStrategy: 'random') {
docker.image('ghcr.io/commaai/alpine-ssh').inside('--user=root') {
timeout(time: 20, unit: 'MINUTES') {
retry (3) {
device(device_ip, "git checkout", extra + "\n" + readFile("selfdrive/test/setup_device_ci.sh"))
}
steps.each { item ->
if (branch != "master" && item.size() == 3 && !hasDirectoryChanged(item[2])) {
println "Skipped '${item[0]}', no relevant changes were detected."
return;
}
device(device_ip, item[0], item[1])
}
}
@@ -106,7 +104,7 @@ def pcStage(String stageName, Closure body) {
checkout scm
def dockerArgs = "--user=batman -v /tmp/comma_download_cache:/tmp/comma_download_cache -v /tmp/scons_cache:/tmp/scons_cache -e PYTHONPATH=${env.WORKSPACE}";
def dockerArgs = "--user=batman -v /tmp/comma_download_cache:/tmp/comma_download_cache -v /tmp/scons_cache:/tmp/scons_cache -e PYTHONPATH=${env.WORKSPACE} --cpus=8 --memory 16g -e PYTEST_ADDOPTS='-n8'";
def openpilot_base = retryWithDelay (3, 15) {
return docker.build("openpilot-base:build-${env.GIT_COMMIT}", "-f Dockerfile.openpilot_base .")
@@ -143,20 +141,6 @@ def setupCredentials() {
}
}
def hasDirectoryChanged(List<String> paths) {
for (change in currentBuild.changeSets) {
for (item in change.items) {
for (affectedPath in item.affectedPaths) {
for (path in paths) {
if (affectedPath.startsWith(path)) {
return true
}
}
}
}
}
return false
}
node {
env.CI = "1"
@@ -205,17 +189,17 @@ node {
])
},
'HW + Unit Tests': {
deviceStage("tici", "tici-common", ["UNSAFE=1"], [
deviceStage("tici-hardware", "tici-common", ["UNSAFE=1"], [
["build", "cd selfdrive/manager && ./build.py"],
["test pandad", "pytest selfdrive/boardd/tests/test_pandad.py", ["panda/", "selfdrive/boardd/"]],
["test power draw", "./system/hardware/tici/tests/test_power_draw.py"],
["test pandad", "pytest selfdrive/boardd/tests/test_pandad.py"],
["test power draw", "pytest -s system/hardware/tici/tests/test_power_draw.py"],
["test encoder", "LD_LIBRARY_PATH=/usr/local/lib pytest system/loggerd/tests/test_encoder.py"],
["test pigeond", "pytest system/sensord/tests/test_pigeond.py"],
["test manager", "pytest selfdrive/manager/test/test_manager.py"],
])
},
'loopback': {
deviceStage("tici", "tici-loopback", ["UNSAFE=1"], [
deviceStage("loopback", "tici-loopback", ["UNSAFE=1"], [
["build openpilot", "cd selfdrive/manager && ./build.py"],
["test boardd loopback", "pytest selfdrive/boardd/tests/test_boardd_loopback.py"],
])
@@ -243,7 +227,7 @@ node {
])
},
'replay': {
deviceStage("tici", "tici-replay", ["UNSAFE=1"], [
deviceStage("model-replay", "tici-replay", ["UNSAFE=1"], [
["build", "cd selfdrive/manager && ./build.py"],
["model replay", "selfdrive/test/process_replay/model_replay.py"],
])
@@ -252,31 +236,13 @@ node {
deviceStage("tizi", "tizi", ["UNSAFE=1"], [
["build openpilot", "cd selfdrive/manager && ./build.py"],
["test boardd loopback", "SINGLE_PANDA=1 pytest selfdrive/boardd/tests/test_boardd_loopback.py"],
["test pandad", "pytest selfdrive/boardd/tests/test_pandad.py", ["panda/", "selfdrive/boardd/"]],
["test pandad", "pytest selfdrive/boardd/tests/test_pandad.py"],
["test amp", "pytest system/hardware/tici/tests/test_amplifier.py"],
["test hw", "pytest system/hardware/tici/tests/test_hardware.py"],
["test qcomgpsd", "pytest system/qcomgpsd/tests/test_qcomgpsd.py", ["system/qcomgpsd/"]],
["test qcomgpsd", "pytest system/qcomgpsd/tests/test_qcomgpsd.py"],
])
},
// *** PC tests ***
'PC tests': {
pcStage("PC tests") {
// tests that our build system's dependencies are configured properly,
// needs a machine with lots of cores
sh label: "test multi-threaded build",
script: '''#!/bin/bash
scons --no-cache --random -j$(nproc)'''
}
},
'car tests': {
pcStage("car tests") {
sh label: "build", script: "selfdrive/manager/build.py"
sh label: "run car tests", script: "cd selfdrive/car/tests && MAX_EXAMPLES=300 INTERNAL_SEG_CNT=300 FILEREADER_CACHE=1 \
INTERNAL_SEG_LIST=selfdrive/car/tests/test_models_segs.txt pytest test_models.py test_car_interfaces.py"
}
},
)
}
} catch (Exception e) {
+4 -1
View File
@@ -1,4 +1,4 @@
Version 0.9.6 (2024-02-XX)
Version 0.9.6 (2024-02-22)
========================
* New driving model
* Vision model trained on more data
@@ -9,8 +9,11 @@ Version 0.9.6 (2024-02-XX)
* AGNOS 9
* comma body streaming and controls over WebRTC
* Improved fuzzy fingerprinting for many makes and models
* Chevrolet Equinox 2019-22 support thanks to JasonJShuler and nworb-cire!
* Dodge Duranago 2020-21 support
* Hyundai Staria 2023 support thanks to sunnyhaibin!
* Kia Niro Plug-in Hybrid 2022 support thanks to sunnyhaibin!
* Lexus LC 2024 support thanks to nelsonjchen!
* Toyota RAV4 2023-24 support
* Toyota RAV4 Hybrid 2023-24 support
+1 -1
Submodule cereal updated: d6beb2482e...8e00786e2a
+9
View File
@@ -41,6 +41,15 @@ public:
if (char* dongle_id = getenv("DONGLE_ID")) {
ctx_j["dongle_id"] = dongle_id;
}
if (char* git_origin = getenv("GIT_ORIGIN")) {
ctx_j["origin"] = git_origin;
}
if (char* git_branch = getenv("GIT_BRANCH")) {
ctx_j["branch"] = git_branch;
}
if (char* git_commit = getenv("GIT_COMMIT")) {
ctx_j["commit"] = git_commit;
}
if (char* daemon_name = getenv("MANAGER_DAEMON")) {
ctx_j["daemon"] = daemon_name;
}
+20 -3
View File
@@ -1,10 +1,11 @@
import gc
import os
import pytest
import random
from openpilot.common.prefix import OpenpilotPrefix
from openpilot.selfdrive.manager import manager
from openpilot.system.hardware import TICI
from openpilot.system.hardware import TICI, HARDWARE
def pytest_sessionstart(session):
@@ -45,6 +46,11 @@ def openpilot_function_fixture(request):
# cleanup any started processes
manager.manager_cleanup()
# some processes disable gc for performance, re-enable here
if not gc.isenabled():
gc.enable()
gc.collect()
# If you use setUpClass, the environment variables won't be cleared properly,
# so we need to hook both the function and class pytest fixtures
@pytest.fixture(scope="class", autouse=True)
@@ -57,12 +63,23 @@ def openpilot_class_fixture():
os.environ.update(starting_env)
@pytest.fixture(scope="function")
def tici_setup_fixture(openpilot_function_fixture):
"""Ensure a consistent state for tests on-device. Needs the openpilot function fixture to run first."""
HARDWARE.initialize_hardware()
HARDWARE.set_power_save(False)
os.system("pkill -9 -f athena")
@pytest.hookimpl(tryfirst=True)
def pytest_collection_modifyitems(config, items):
skipper = pytest.mark.skip(reason="Skipping tici test on PC")
for item in items:
if not TICI and "tici" in item.keywords:
item.add_marker(skipper)
if "tici" in item.keywords:
if not TICI:
item.add_marker(skipper)
else:
item.fixturenames.append('tici_setup_fixture')
if "xdist_group_class_property" in item.keywords:
class_property_name = item.get_closest_marker('xdist_group_class_property').args[0]
+8 -4
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.
# 275 Supported Cars
# 279 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|
|---|---|---|:---:|:---:|:---:|:---:|:---:|:---:|:---:|
@@ -23,6 +23,7 @@ A supported vehicle is one that just works when you install a comma device. All
|Cadillac|Escalade ESV 2019[<sup>4</sup>](#footnotes)|Adaptive Cruise Control (ACC) & LKAS|openpilot|0 mph|7 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|<details><summary>Parts</summary><sub>- 1 OBD-II connector<br>- 1 comma 3X<br>- 2 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Cadillac&model=Escalade ESV 2019">Buy Here</a></sub></details>||
|Chevrolet|Bolt EUV 2022-23|Premier or Premier Redline Trim without Super Cruise Package|openpilot available[<sup>1</sup>](#footnotes)|3 mph|6 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|<details><summary>Parts</summary><sub>- 1 GM connector<br>- 1 comma 3X<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Chevrolet&model=Bolt EUV 2022-23">Buy Here</a></sub></details>|<a href="https://youtu.be/xvwzGMUA210" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|Chevrolet|Bolt EV 2022-23|2LT Trim with Adaptive Cruise Control Package|openpilot available[<sup>1</sup>](#footnotes)|3 mph|6 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|<details><summary>Parts</summary><sub>- 1 GM connector<br>- 1 comma 3X<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Chevrolet&model=Bolt EV 2022-23">Buy Here</a></sub></details>||
|Chevrolet|Equinox 2019-22|Adaptive Cruise Control (ACC)|openpilot available[<sup>1</sup>](#footnotes)|3 mph|6 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|<details><summary>Parts</summary><sub>- 1 GM connector<br>- 1 comma 3X<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Chevrolet&model=Equinox 2019-22">Buy Here</a></sub></details>||
|Chevrolet|Silverado 1500 2020-21|Safety Package II|openpilot available[<sup>1</sup>](#footnotes)|0 mph|6 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|<details><summary>Parts</summary><sub>- 1 GM connector<br>- 1 comma 3X<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Chevrolet&model=Silverado 1500 2020-21">Buy Here</a></sub></details>||
|Chevrolet|Trailblazer 2021-22|Adaptive Cruise Control (ACC)|openpilot available[<sup>1</sup>](#footnotes)|3 mph|6 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|<details><summary>Parts</summary><sub>- 1 GM connector<br>- 1 comma 3X<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Chevrolet&model=Trailblazer 2021-22">Buy Here</a></sub></details>||
|Chevrolet|Volt 2017-18[<sup>4</sup>](#footnotes)|Adaptive Cruise Control (ACC)|openpilot|0 mph|7 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|<details><summary>Parts</summary><sub>- 1 OBD-II connector<br>- 1 comma 3X<br>- 2 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Chevrolet&model=Volt 2017-18">Buy Here</a></sub></details>|<a href="https://youtu.be/QeMCN_4TFfQ" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
@@ -33,6 +34,7 @@ A supported vehicle is one that just works when you install a comma device. All
|Chrysler|Pacifica Hybrid 2018|Adaptive Cruise Control (ACC)|Stock|0 mph|9 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 FCA connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Chrysler&model=Pacifica Hybrid 2018">Buy Here</a></sub></details>||
|Chrysler|Pacifica Hybrid 2019-23|Adaptive Cruise Control (ACC)|Stock|0 mph|39 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 FCA connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Chrysler&model=Pacifica Hybrid 2019-23">Buy Here</a></sub></details>||
|comma|body|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|None||
|Dodge|Durango 2020-21|Adaptive Cruise Control (ACC)|Stock|0 mph|39 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 FCA connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Dodge&model=Durango 2020-21">Buy Here</a></sub></details>||
|Ford|Bronco Sport 2021-22|Co-Pilot360 Assist+|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 Ford Q3 connector<br>- 1 RJ45 cable (7 ft)<br>- 1 angled mount (8 degrees)<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Ford&model=Bronco Sport 2021-22">Buy Here</a></sub></details>||
|Ford|Escape 2020-22|Co-Pilot360 Assist+|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 Ford Q3 connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Ford&model=Escape 2020-22">Buy Here</a></sub></details>||
|Ford|Explorer 2020-23|Co-Pilot360 Assist+|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 Ford Q3 connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Ford&model=Explorer 2020-23">Buy Here</a></sub></details>||
@@ -72,7 +74,7 @@ A supported vehicle is one that just works when you install a comma device. All
|Honda|Odyssey 2018-20|Honda Sensing|openpilot|25 mph|0 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|<details><summary>Parts</summary><sub>- 1 Honda Nidec connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Honda&model=Odyssey 2018-20">Buy Here</a></sub></details>||
|Honda|Passport 2019-23|All|openpilot|25 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|<details><summary>Parts</summary><sub>- 1 Honda Nidec connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Honda&model=Passport 2019-23">Buy Here</a></sub></details>||
|Honda|Pilot 2016-22|Honda Sensing|openpilot|25 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|<details><summary>Parts</summary><sub>- 1 Honda Nidec connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Honda&model=Pilot 2016-22">Buy Here</a></sub></details>||
|Honda|Ridgeline 2017-23|Honda Sensing|openpilot|25 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|<details><summary>Parts</summary><sub>- 1 Honda Nidec connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Honda&model=Ridgeline 2017-23">Buy Here</a></sub></details>||
|Honda|Ridgeline 2017-24|Honda Sensing|openpilot|25 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|<details><summary>Parts</summary><sub>- 1 Honda Nidec connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Honda&model=Ridgeline 2017-24">Buy Here</a></sub></details>||
|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 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Hyundai&model=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 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Hyundai&model=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 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Hyundai&model=Azera Hybrid 2020">Buy Here</a></sub></details>||
@@ -138,6 +140,7 @@ A supported vehicle is one that just works when you install a comma device. All
|Kia|Niro Hybrid 2023[<sup>6</sup>](#footnotes)|Smart Cruise Control (SCC)|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 A connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Kia&model=Niro Hybrid 2023">Buy Here</a></sub></details>||
|Kia|Niro Plug-in Hybrid 2018-19|All|Stock|10 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Hyundai C connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Kia&model=Niro Plug-in Hybrid 2018-19">Buy Here</a></sub></details>||
|Kia|Niro Plug-in Hybrid 2020|All|Stock|0 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Hyundai D connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Kia&model=Niro Plug-in Hybrid 2020">Buy Here</a></sub></details>||
|Kia|Niro Plug-in Hybrid 2021|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 D connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Kia&model=Niro Plug-in Hybrid 2021">Buy Here</a></sub></details>||
|Kia|Niro Plug-in Hybrid 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 F connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Kia&model=Niro Plug-in Hybrid 2022">Buy Here</a></sub></details>||
|Kia|Optima 2017|Advanced Smart Cruise Control|Stock|0 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Hyundai B connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Kia&model=Optima 2017">Buy Here</a></sub></details>||
|Kia|Optima 2019-20|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Hyundai G connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Kia&model=Optima 2019-20">Buy Here</a></sub></details>||
@@ -161,6 +164,7 @@ A supported vehicle is one that just works when you install a comma device. All
|Lexus|GS F 2016|All|Stock|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|<details><summary>Parts</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Toyota A connector<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Lexus&model=GS F 2016">Buy Here</a></sub></details>||
|Lexus|IS 2017-19|All|Stock|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|<details><summary>Parts</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Toyota A connector<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Lexus&model=IS 2017-19">Buy Here</a></sub></details>||
|Lexus|IS 2022-23|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Toyota A connector<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Lexus&model=IS 2022-23">Buy Here</a></sub></details>||
|Lexus|LC 2024|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Toyota A connector<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Lexus&model=LC 2024">Buy Here</a></sub></details>||
|Lexus|NX 2018-19|All|openpilot available[<sup>2</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|<details><summary>Parts</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Toyota A connector<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Lexus&model=NX 2018-19">Buy Here</a></sub></details>||
|Lexus|NX 2020-21|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Toyota A connector<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Lexus&model=NX 2020-21">Buy Here</a></sub></details>||
|Lexus|NX Hybrid 2018-19|All|openpilot available[<sup>2</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|<details><summary>Parts</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Toyota A connector<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Lexus&model=NX Hybrid 2018-19">Buy Here</a></sub></details>||
@@ -267,8 +271,8 @@ A supported vehicle is one that just works when you install a comma device. All
|Volkswagen|Golf R 2015-19|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,13</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Volkswagen&model=Golf R 2015-19">Buy Here</a></sub></details>||
|Volkswagen|Golf SportsVan 2015-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,13</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Volkswagen&model=Golf SportsVan 2015-20">Buy Here</a></sub></details>||
|Volkswagen|Grand California 2019-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,13</sup>](#footnotes)|0 mph|31 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 angled mount (8 degrees)<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Volkswagen&model=Grand California 2019-23">Buy Here</a></sub></details>|<a href="https://youtu.be/4100gLeabmo" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|Volkswagen|Jetta 2018-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,13</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Volkswagen&model=Jetta 2018-22">Buy Here</a></sub></details>||
|Volkswagen|Jetta GLI 2021-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,13</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Volkswagen&model=Jetta GLI 2021-22">Buy Here</a></sub></details>||
|Volkswagen|Jetta 2018-24|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,13</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Volkswagen&model=Jetta 2018-24">Buy Here</a></sub></details>||
|Volkswagen|Jetta GLI 2021-24|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,13</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Volkswagen&model=Jetta GLI 2021-24">Buy Here</a></sub></details>||
|Volkswagen|Passat 2015-22[<sup>11</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,13</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Volkswagen&model=Passat 2015-22">Buy Here</a></sub></details>||
|Volkswagen|Passat Alltrack 2015-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,13</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Volkswagen&model=Passat Alltrack 2015-22">Buy Here</a></sub></details>||
|Volkswagen|Passat GTE 2015-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,13</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Volkswagen&model=Passat GTE 2015-22">Buy Here</a></sub></details>||
+1 -1
View File
@@ -7,7 +7,7 @@ export OPENBLAS_NUM_THREADS=1
export VECLIB_MAXIMUM_THREADS=1
if [ -z "$AGNOS_VERSION" ]; then
export AGNOS_VERSION="9.3"
export AGNOS_VERSION="9.6"
fi
export STAGING_ROOT="/data/safe_staging"
+1 -1
Submodule opendbc updated: aff840356a...4d004e78dd
+1 -1
Submodule panda updated: ea01b5148a...a48b419df4
Generated
+413 -393
View File
File diff suppressed because it is too large Load Diff
+5 -5
View File
@@ -126,7 +126,7 @@ inputs = "*"
Jinja2 = "*"
lru-dict = "*"
matplotlib = "*"
metadrive-simulator = { version = "0.4.2.2", markers = "platform_machine != 'aarch64'" } # no linux/aarch64 wheels for certain dependencies
metadrive-simulator = { version = "0.4.2.3", markers = "platform_machine != 'aarch64'" } # no linux/aarch64 wheels for certain dependencies
mpld3 = "*"
mypy = "*"
myst-parser = "*"
@@ -167,8 +167,8 @@ build-backend = "poetry.core.masonry.api"
# https://beta.ruff.rs/docs/configuration/#using-pyprojecttoml
[tool.ruff]
select = ["E", "F", "W", "PIE", "C4", "ISC", "RUF008", "RUF100", "A", "B", "TID251"]
ignore = ["E741", "E402", "C408", "ISC003", "B027", "B024"]
lint.select = ["E", "F", "W", "PIE", "C4", "ISC", "RUF008", "RUF100", "A", "B", "TID251"]
lint.ignore = ["E741", "E402", "C408", "ISC003", "B027", "B024"]
line-length = 160
target-version="py311"
exclude = [
@@ -180,8 +180,8 @@ exclude = [
"teleoprtc_repo",
"third_party",
]
flake8-implicit-str-concat.allow-multiline=false
[tool.ruff.flake8-tidy-imports.banned-api]
lint.flake8-implicit-str-concat.allow-multiline=false
[tool.ruff.lint.flake8-tidy-imports.banned-api]
"selfdrive".msg = "Use openpilot.selfdrive"
"common".msg = "Use openpilot.common"
"system".msg = "Use openpilot.system"
+1 -1
View File
@@ -95,7 +95,7 @@ selfdrive/car/ecu_addrs.py
selfdrive/car/isotp_parallel_query.py
selfdrive/car/tests/__init__.py
selfdrive/car/tests/test_car_interfaces.py
selfdrive/car/torque_data/*.toml
selfdrive/car/torque_data/*
selfdrive/car/body/*.py
selfdrive/car/chrysler/*.py
+4 -4
View File
@@ -36,7 +36,7 @@ from openpilot.common.realtime import set_core_affinity
from openpilot.system.hardware import HARDWARE, PC
from openpilot.system.loggerd.xattr_cache import getxattr, setxattr
from openpilot.common.swaglog import cloudlog
from openpilot.system.version import get_commit, get_origin, get_short_branch, get_version
from openpilot.system.version import get_commit, get_normalized_origin, get_short_branch, get_version
from openpilot.system.hardware.hw import Paths
@@ -316,9 +316,9 @@ def getMessage(service: str, timeout: int = 1000) -> dict:
def getVersion() -> Dict[str, str]:
return {
"version": get_version(),
"remote": get_origin(''),
"branch": get_short_branch(''),
"commit": get_commit(default=''),
"remote": get_normalized_origin(),
"branch": get_short_branch(),
"commit": get_commit(),
}
+9 -2
View File
@@ -6,7 +6,8 @@ from multiprocessing import Process
from openpilot.common.params import Params
from openpilot.selfdrive.manager.process import launcher
from openpilot.common.swaglog import cloudlog
from openpilot.system.version import get_version, is_dirty
from openpilot.system.hardware import HARDWARE
from openpilot.system.version import get_version, get_normalized_origin, get_short_branch, get_commit, is_dirty
ATHENA_MGR_PID_PARAM = "AthenadPid"
@@ -14,7 +15,13 @@ ATHENA_MGR_PID_PARAM = "AthenadPid"
def main():
params = Params()
dongle_id = params.get("DongleId").decode('utf-8')
cloudlog.bind_global(dongle_id=dongle_id, version=get_version(), dirty=is_dirty())
cloudlog.bind_global(dongle_id=dongle_id,
version=get_version(),
origin=get_normalized_origin(),
branch=get_short_branch(),
commit=get_commit(),
dirty=is_dirty(),
device=HARDWARE.get_device_type())
try:
while 1:
@@ -32,7 +32,7 @@ class TestBoardd(unittest.TestCase):
with Timeout(90, "boardd didn't start"):
sm = messaging.SubMaster(['pandaStates'])
while sm.rcv_frame['pandaStates'] < 1 or len(sm['pandaStates']) == 0 or \
while sm.recv_frame['pandaStates'] < 1 or len(sm['pandaStates']) == 0 or \
any(ps.pandaType == log.PandaState.PandaType.unknown for ps in sm['pandaStates']):
sm.update(1000)
+5 -4
View File
@@ -141,9 +141,10 @@ def fingerprint(logcan, sendcan, num_pandas):
cached = True
else:
cloudlog.warning("Getting VIN & FW versions")
# enable OBD multiplexing for Vin query, also allows time for sendcan subscriber to connect
# enable OBD multiplexing for VIN query
# NOTE: this takes ~0.1s and is relied on to allow sendcan subscriber to connect in time
set_obd_multiplexing(params, True)
# Vin query only reliably works through OBDII
# VIN query only reliably works through OBDII
vin_rx_addr, vin_rx_bus, vin = get_vin(logcan, sendcan, (0, 1))
ecu_rx_addrs = get_present_ecus(logcan, sendcan, num_pandas=num_pandas)
car_fw = get_fw_versions_ordered(logcan, sendcan, ecu_rx_addrs, num_pandas=num_pandas)
@@ -187,7 +188,7 @@ def fingerprint(logcan, sendcan, num_pandas):
cloudlog.event("fingerprinted", car_fingerprint=car_fingerprint, source=source, fuzzy=not exact_match, cached=cached,
fw_count=len(car_fw), ecu_responses=list(ecu_rx_addrs), vin_rx_addr=vin_rx_addr, vin_rx_bus=vin_rx_bus,
fingerprints=finger, fw_query_time=fw_query_time, error=True)
fingerprints=repr(finger), fw_query_time=fw_query_time, error=True)
return car_fingerprint, finger, vin, car_fw, source, exact_match
@@ -195,7 +196,7 @@ def get_car(logcan, sendcan, experimental_long_allowed, num_pandas=1):
candidate, fingerprints, vin, car_fw, source, exact_match = fingerprint(logcan, sendcan, num_pandas)
if candidate is None:
cloudlog.event("car doesn't match any fingerprints", fingerprints=fingerprints, error=True)
cloudlog.event("car doesn't match any fingerprints", fingerprints=repr(fingerprints), error=True)
candidate = "mock"
CarInterface, CarController, CarState = interfaces[candidate]
+39
View File
@@ -67,6 +67,7 @@ FW_VERSIONS = {
(Ecu.engine, 0x7e0, None): [
b'68267018AO ',
b'68267020AJ ',
b'68303534AJ ',
b'68340762AD ',
b'68340764AD ',
b'68352652AE ',
@@ -299,6 +300,7 @@ FW_VERSIONS = {
CAR.JEEP_GRAND_CHEROKEE_2019: {
(Ecu.combinationMeter, 0x742, None): [
b'68402703AB',
b'68402704AB',
b'68402708AB',
b'68402971AD',
b'68454144AD',
@@ -326,6 +328,7 @@ FW_VERSIONS = {
(Ecu.eps, 0x75a, None): [
b'68417279AA',
b'68417280AA',
b'68417281AA',
b'68453431AA',
b'68453433AA',
b'68453435AA',
@@ -336,6 +339,7 @@ FW_VERSIONS = {
(Ecu.engine, 0x7e0, None): [
b'05035674AB ',
b'68412635AG ',
b'68412660AD ',
b'68422860AB',
b'68449435AE ',
b'68496223AA ',
@@ -346,6 +350,7 @@ FW_VERSIONS = {
(Ecu.transmission, 0x7e1, None): [
b'05035707AA',
b'68419672AC',
b'68419678AB',
b'68423905AB',
b'68449258AC',
b'68495807AA',
@@ -359,6 +364,7 @@ FW_VERSIONS = {
b'68294051AG',
b'68294051AI',
b'68294052AG',
b'68294052AH',
b'68294063AG',
b'68294063AH',
b'68294063AI',
@@ -474,14 +480,17 @@ FW_VERSIONS = {
b'05149591AD ',
b'05149591AE ',
b'05149592AE ',
b'05149599AE ',
b'05149600AD ',
b'05149605AE ',
b'05149846AA ',
b'05149848AA ',
b'05149848AC ',
b'05190341AD',
b'68378695AJ ',
b'68378696AJ ',
b'68378701AI ',
b'68378702AI ',
b'68378710AL ',
b'68378748AL ',
b'68378758AM ',
@@ -603,4 +612,34 @@ FW_VERSIONS = {
b'M2421132MB',
],
},
CAR.DODGE_DURANGO: {
(Ecu.combinationMeter, 0x742, None): [
b'68454261AD',
b'68471535AE',
],
(Ecu.srs, 0x744, None): [
b'68355362AB',
b'68492238AD',
],
(Ecu.abs, 0x747, None): [
b'68408639AD',
b'68499978AB',
],
(Ecu.fwdRadar, 0x753, None): [
b'68440581AE',
b'68456722AC',
],
(Ecu.eps, 0x75a, None): [
b'68453435AA',
b'68498477AA',
],
(Ecu.engine, 0x7e0, None): [
b'05035786AE ',
b'68449476AE ',
],
(Ecu.transmission, 0x7e1, None): [
b'05035826AC',
b'68449265AC',
],
},
}
+3 -2
View File
@@ -28,13 +28,13 @@ class CarInterface(CarInterfaceBase):
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
if candidate not in RAM_CARS:
# Newer FW versions standard on the following platforms, or flashed by a dealer onto older platforms have a higher minimum steering speed.
new_eps_platform = candidate in (CAR.PACIFICA_2019_HYBRID, CAR.PACIFICA_2020, CAR.JEEP_GRAND_CHEROKEE_2019)
new_eps_platform = candidate in (CAR.PACIFICA_2019_HYBRID, CAR.PACIFICA_2020, CAR.JEEP_GRAND_CHEROKEE_2019, CAR.DODGE_DURANGO)
new_eps_firmware = any(fw.ecu == 'eps' and fw.fwVersion[:4] >= b"6841" for fw in car_fw)
if new_eps_platform or new_eps_firmware:
ret.flags |= ChryslerFlags.HIGHER_MIN_STEERING_SPEED.value
# Chrysler
if candidate in (CAR.PACIFICA_2017_HYBRID, CAR.PACIFICA_2018, CAR.PACIFICA_2018_HYBRID, CAR.PACIFICA_2019_HYBRID, CAR.PACIFICA_2020):
if candidate in (CAR.PACIFICA_2017_HYBRID, CAR.PACIFICA_2018, CAR.PACIFICA_2018_HYBRID, CAR.PACIFICA_2019_HYBRID, CAR.PACIFICA_2020, CAR.DODGE_DURANGO):
ret.mass = 2242.
ret.wheelbase = 3.089
ret.steerRatio = 16.2 # Pacifica Hybrid 2017
@@ -80,6 +80,7 @@ class CarInterface(CarInterfaceBase):
if ret.flags & ChryslerFlags.HIGHER_MIN_STEERING_SPEED:
# TODO: allow these cars to steer down to 13 m/s if already engaged.
# TODO: Durango 2020 may be able to steer to zero once above 38 kph
ret.minSteerSpeed = 17.5 # m/s 17 on the way up, 13 on the way down once engaged.
ret.centerToFront = ret.wheelbase * 0.44
+5
View File
@@ -23,6 +23,9 @@ class CAR(StrEnum):
PACIFICA_2018 = "CHRYSLER PACIFICA 2018"
PACIFICA_2020 = "CHRYSLER PACIFICA 2020"
# Dodge
DODGE_DURANGO = "DODGE DURANGO 2021"
# Jeep
JEEP_GRAND_CHEROKEE = "JEEP GRAND CHEROKEE V6 2018" # includes 2017 Trailhawk
JEEP_GRAND_CHEROKEE_2019 = "JEEP GRAND CHEROKEE 2019" # includes 2020 Trailhawk
@@ -74,6 +77,7 @@ CAR_INFO: Dict[str, Optional[Union[ChryslerCarInfo, List[ChryslerCarInfo]]]] = {
],
CAR.JEEP_GRAND_CHEROKEE: ChryslerCarInfo("Jeep Grand Cherokee 2016-18", video_link="https://www.youtube.com/watch?v=eLR9o2JkuRk"),
CAR.JEEP_GRAND_CHEROKEE_2019: ChryslerCarInfo("Jeep Grand Cherokee 2019-21", video_link="https://www.youtube.com/watch?v=jBe4lWnRSu4"),
CAR.DODGE_DURANGO: ChryslerCarInfo("Dodge Durango 2020-21"),
CAR.RAM_1500: ChryslerCarInfo("Ram 1500 2019-24", car_parts=CarParts.common([CarHarness.ram])),
CAR.RAM_HD: [
ChryslerCarInfo("Ram 2500 2020-24", car_parts=CarParts.common([CarHarness.ram])),
@@ -128,6 +132,7 @@ DBC = {
CAR.PACIFICA_2020: dbc_dict('chrysler_pacifica_2017_hybrid_generated', 'chrysler_pacifica_2017_hybrid_private_fusion'),
CAR.PACIFICA_2018_HYBRID: dbc_dict('chrysler_pacifica_2017_hybrid_generated', 'chrysler_pacifica_2017_hybrid_private_fusion'),
CAR.PACIFICA_2019_HYBRID: dbc_dict('chrysler_pacifica_2017_hybrid_generated', 'chrysler_pacifica_2017_hybrid_private_fusion'),
CAR.DODGE_DURANGO: dbc_dict('chrysler_pacifica_2017_hybrid_generated', 'chrysler_pacifica_2017_hybrid_private_fusion'),
CAR.JEEP_GRAND_CHEROKEE: dbc_dict('chrysler_pacifica_2017_hybrid_generated', 'chrysler_pacifica_2017_hybrid_private_fusion'),
CAR.JEEP_GRAND_CHEROKEE_2019: dbc_dict('chrysler_pacifica_2017_hybrid_generated', 'chrysler_pacifica_2017_hybrid_private_fusion'),
CAR.RAM_1500: dbc_dict('chrysler_ram_dt_generated', None),
+1 -1
View File
@@ -54,7 +54,7 @@ class CarState(CarStateBase):
ret.steeringPressed = self.update_steering_pressed(abs(ret.steeringTorque) > CarControllerParams.STEER_DRIVER_ALLOWANCE, 5)
ret.steerFaultTemporary = cp.vl["EPAS_INFO"]["EPAS_Failure"] == 1
ret.steerFaultPermanent = cp.vl["EPAS_INFO"]["EPAS_Failure"] in (2, 3)
# ret.espDisabled = False # TODO: find traction control signal
ret.espDisabled = cp.vl["Cluster_Info1_FD1"]["DrvSlipCtlMde_D_Rq"] != 0 # 0 is default mode
if self.CP.carFingerprint in CANFD_CAR:
# this signal is always 0 on non-CAN FD cars
+1
View File
@@ -24,6 +24,7 @@ FW_VERSIONS = {
(Ecu.eps, 0x730, None): [
b'LX6C-14D003-AF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
b'LX6C-14D003-AH\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
b'LX6C-14D003-AK\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
b'LX6C-14D003-AL\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
],
(Ecu.abs, 0x760, None): [
+1
View File
@@ -119,6 +119,7 @@ FW_QUERY_CONFIG = FwQueryConfig(
),
],
extra_ecus=[
# We are unlikely to get a response from the PCM from behind the gateway
(Ecu.engine, 0x7e0, None),
(Ecu.shiftByWire, 0x732, None),
],
+4 -1
View File
@@ -14,7 +14,10 @@ EcuAddrSubAddr = Tuple[int, int, Optional[int]]
LiveFwVersions = Dict[AddrType, Set[bytes]]
OfflineFwVersions = Dict[str, Dict[EcuAddrSubAddr, List[bytes]]]
STANDARD_VIN_ADDRS = [0x7e0, 0x7e2, 0x18da10f1, 0x18da0ef1] # engine, VMCU, 29-bit engine, PGM-FI
# A global list of addresses we will only ever consider for VIN responses
# engine, hybrid controller, Ford abs, Hyundai CAN FD cluster, 29-bit engine, PGM-FI
# TODO: move these to each brand's FW query config
STANDARD_VIN_ADDRS = [0x7e0, 0x7e2, 0x760, 0x7c4, 0x18da10f1, 0x18da0ef1]
def p16(val):
+4 -2
View File
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
from collections import defaultdict
from typing import Any, DefaultDict, Dict, List, Optional, Set
from typing import Any, DefaultDict, Dict, Iterator, List, Optional, Set, TypeVar
from tqdm import tqdm
import capnp
@@ -24,8 +24,10 @@ VERSIONS = get_interface_attr('FW_VERSIONS', ignore_none=True)
MODEL_TO_BRAND = {c: b for b, e in VERSIONS.items() for c in e}
REQUESTS = [(brand, config, r) for brand, config in FW_QUERY_CONFIGS.items() for r in config.requests]
T = TypeVar('T')
def chunks(l, n=128):
def chunks(l: List[T], n: int = 128) -> Iterator[List[T]]:
for i in range(0, len(l), n):
yield l[i:i + n]
+4
View File
@@ -2,6 +2,7 @@
from openpilot.selfdrive.car.gm.values import CAR
# Trailblazer also matches as a SILVERADO, TODO: split with fw versions
# FIXME: There are Equinox users with different message lengths, specifically 304 and 320
FINGERPRINTS = {
@@ -52,6 +53,9 @@ FINGERPRINTS = {
}],
CAR.EQUINOX: [{
190: 6, 193: 8, 197: 8, 201: 8, 209: 7, 211: 2, 241: 6, 249: 8, 257: 8, 288: 5, 289: 8, 298: 8, 304: 1, 309: 8, 311: 8, 313: 8, 320: 3, 328: 1, 352: 5, 381: 8, 384: 4, 386: 8, 388: 8, 413: 8, 451: 8, 452: 8, 453: 6, 455: 7, 463: 3, 479: 3, 481: 7, 485: 8, 489: 8, 497: 8, 500: 6, 501: 8, 510: 8, 528: 5, 532: 6, 560: 8, 562: 8, 563: 5, 565: 5, 587: 8, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 707: 8, 715: 8, 717: 5, 753: 5, 761: 7, 789: 5, 800: 6, 810: 8, 840: 5, 842: 5, 844: 8, 869: 4, 880: 6, 977: 8, 1001: 8, 1011: 6, 1017: 8, 1020: 8, 1033: 7, 1034: 7, 1217: 8, 1221: 5, 1233: 8, 1249: 8, 1259: 8, 1261: 7, 1263: 4, 1265: 8, 1267: 1, 1271: 8, 1280: 4, 1296: 4, 1300: 8, 1611: 8, 1930: 7
},
{
190: 6, 201: 8, 211: 2, 717: 5, 241: 6, 451: 8, 298: 8, 452: 8, 453: 6, 479: 3, 485: 8, 249: 8, 500: 6, 587: 8, 1611: 8, 289: 8, 481: 7, 193: 8, 197: 8, 209: 7, 455: 7, 489: 8, 309: 8, 413: 8, 501: 8, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 311: 8, 510: 8, 528: 5, 532: 6, 715: 8, 560: 8, 562: 8, 707: 8, 789: 5, 869: 4, 880: 6, 761: 7, 840: 5, 842: 5, 844: 8, 313: 8, 381: 8, 386: 8, 810: 8, 322: 7, 384: 4, 800: 6, 1033: 7, 1034: 7, 1296: 4, 753: 5, 388: 8, 288: 5, 497: 8, 463: 3, 304: 3, 977: 8, 1001: 8, 1280: 4, 320: 4, 352: 5, 563: 5, 565: 5, 1221: 5, 1011: 6, 1017: 8, 1020: 8, 1249: 8, 1300: 8, 328: 1, 1217: 8, 1233: 8, 1259: 8, 1261: 7, 1263: 4, 1265: 8, 1267: 1, 1930: 7, 1271: 8
}],
}
+1 -1
View File
@@ -137,7 +137,7 @@ class CarInterface(CarInterfaceBase):
# These cars have been put into dashcam only due to both a lack of users and test coverage.
# These cars likely still work fine. Once a user confirms each car works and a test route is
# added to selfdrive/car/tests/routes.py, we can remove it from this list.
ret.dashcamOnly = candidate in {CAR.CADILLAC_ATS, CAR.HOLDEN_ASTRA, CAR.MALIBU, CAR.BUICK_REGAL, CAR.EQUINOX} or \
ret.dashcamOnly = candidate in {CAR.CADILLAC_ATS, CAR.HOLDEN_ASTRA, CAR.MALIBU, CAR.BUICK_REGAL} or \
(ret.networkLocation == NetworkLocation.gateway and ret.radarUnavailable)
# Start with a baseline tuning for all GM vehicles. Override tuning as needed in each model section below.
+9
View File
@@ -719,6 +719,7 @@ FW_VERSIONS = {
b'36802-TLA-A040\x00\x00',
b'36802-TLA-A050\x00\x00',
b'36802-TLA-A060\x00\x00',
b'36802-TLA-A070\x00\x00',
b'36802-TMC-Q040\x00\x00',
b'36802-TMC-Q070\x00\x00',
b'36802-TNY-A030\x00\x00',
@@ -873,6 +874,7 @@ FW_VERSIONS = {
(Ecu.programmedFuelInjection, 0x18da10f1, None): [
b'37805-5MR-3050\x00\x00',
b'37805-5MR-3250\x00\x00',
b'37805-5MR-4070\x00\x00',
b'37805-5MR-4080\x00\x00',
b'37805-5MR-4180\x00\x00',
b'37805-5MR-A240\x00\x00',
@@ -918,6 +920,7 @@ FW_VERSIONS = {
b'28102-5MX-A900\x00\x00',
b'28102-5MX-A910\x00\x00',
b'28102-5MX-C001\x00\x00',
b'28102-5MX-C910\x00\x00',
b'28102-5MX-D001\x00\x00',
b'28102-5MX-D710\x00\x00',
b'28102-5MX-K610\x00\x00',
@@ -955,6 +958,7 @@ FW_VERSIONS = {
b'78109-THR-C320\x00\x00',
b'78109-THR-C330\x00\x00',
b'78109-THR-CE20\x00\x00',
b'78109-THR-CL10\x00\x00',
b'78109-THR-DA20\x00\x00',
b'78109-THR-DA30\x00\x00',
b'78109-THR-DA40\x00\x00',
@@ -1024,6 +1028,7 @@ FW_VERSIONS = {
b'37805-RLV-F120\x00\x00',
b'37805-RLV-L080\x00\x00',
b'37805-RLV-L090\x00\x00',
b'37805-RLV-L150\x00\x00',
b'37805-RLV-L160\x00\x00',
b'37805-RLV-L180\x00\x00',
b'37805-RLV-L350\x00\x00',
@@ -1244,6 +1249,7 @@ FW_VERSIONS = {
b'39990-T6Z-A020\x00\x00',
b'39990-T6Z-A030\x00\x00',
b'39990-T6Z-A050\x00\x00',
b'39990-T6Z-A110\x00\x00',
],
(Ecu.fwdRadar, 0x18dab0f1, None): [
b'36161-T6Z-A020\x00\x00',
@@ -1251,6 +1257,7 @@ FW_VERSIONS = {
b'36161-T6Z-A420\x00\x00',
b'36161-T6Z-A520\x00\x00',
b'36161-T6Z-A620\x00\x00',
b'36161-T6Z-A720\x00\x00',
b'36161-TJZ-A120\x00\x00',
],
(Ecu.gateway, 0x18daeff1, None): [
@@ -1258,6 +1265,7 @@ FW_VERSIONS = {
b'38897-T6Z-A110\x00\x00',
],
(Ecu.combinationMeter, 0x18da60f1, None): [
b'78108-T6Z-AF10\x00\x00',
b'78109-T6Z-A420\x00\x00',
b'78109-T6Z-A510\x00\x00',
b'78109-T6Z-A710\x00\x00',
@@ -1275,6 +1283,7 @@ FW_VERSIONS = {
b'57114-T6Z-A120\x00\x00',
b'57114-T6Z-A130\x00\x00',
b'57114-T6Z-A520\x00\x00',
b'57114-T6Z-A610\x00\x00',
b'57114-TJZ-A520\x00\x00',
],
},
+11 -2
View File
@@ -159,7 +159,7 @@ CAR_INFO: Dict[str, Optional[Union[HondaCarInfo, List[HondaCarInfo]]]] = {
HondaCarInfo("Honda Pilot 2016-22", min_steer_speed=12. * CV.MPH_TO_MS),
HondaCarInfo("Honda Passport 2019-23", "All", min_steer_speed=12. * CV.MPH_TO_MS),
],
CAR.RIDGELINE: HondaCarInfo("Honda Ridgeline 2017-23", min_steer_speed=12. * CV.MPH_TO_MS),
CAR.RIDGELINE: HondaCarInfo("Honda Ridgeline 2017-24", min_steer_speed=12. * CV.MPH_TO_MS),
CAR.INSIGHT: HondaCarInfo("Honda Insight 2019-22", "All", min_steer_speed=3. * CV.MPH_TO_MS),
CAR.HONDA_E: HondaCarInfo("Honda e 2020", "All", min_steer_speed=3. * CV.MPH_TO_MS),
CAR.CLARITY: HondaCarInfo("Honda Clarity 2018-22"),
@@ -207,10 +207,19 @@ FW_QUERY_CONFIG = FwQueryConfig(
[StdQueries.UDS_VERSION_REQUEST],
[StdQueries.UDS_VERSION_RESPONSE],
bus=1,
logging=True,
obd_multiplexing=False,
),
],
# 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={
Ecu.programmedFuelInjection: [CAR.CIVIC_BOSCH, CAR.CRV_5G],
Ecu.transmission: [CAR.CIVIC_BOSCH, CAR.CRV_5G],
Ecu.vsa: [CAR.CIVIC_BOSCH, CAR.CRV_5G],
Ecu.combinationMeter: [CAR.CIVIC_BOSCH, CAR.CRV_5G],
Ecu.gateway: [CAR.CIVIC_BOSCH, CAR.CRV_5G],
Ecu.electricBrakeBooster: [CAR.CIVIC_BOSCH, CAR.CRV_5G],
},
extra_ecus=[
# The only other ECU on PT bus accessible by camera on radarless Civic
(Ecu.unknown, 0x18DAB3F1, None),
+18
View File
@@ -914,12 +914,14 @@ FW_VERSIONS = {
(Ecu.transmission, 0x7e1, None): [
b'\xf1\x87VDGMD15352242DD3w\x87gxwvgv\x87wvw\x88wXwffVfffUfw\x88o\xff\x06J\xf1\x81E14\x00\x00\x00\x00\x00\x00\x00\xf1\x00bcshcm49 E14\x00\x00\x00\x00\x00\x00\x00SHI0G50NB1tc5\xb7',
b'\xf1\x87VDGMD15866192DD3x\x88x\x89wuFvvfUf\x88vWwgwwwvfVgx\x87o\xff\xbc^\xf1\x81E14\x00\x00\x00\x00\x00\x00\x00\xf1\x00bcshcm49 E14\x00\x00\x00\x00\x00\x00\x00SHI0G50NB1tc5\xb7',
b'\xf1\x87VDHMD16446682DD3WwwxxvGw\x88\x88\x87\x88\x88whxx\x87\x87\x87\x85fUfwu_\xffT\xf8\xf1\x81E14\x00\x00\x00\x00\x00\x00\x00\xf1\x00bcshcm49 E14\x00\x00\x00\x00\x00\x00\x00SHI0G50NB1tc5\xb7',
],
(Ecu.fwdRadar, 0x7d0, None): [
b'\xf1\x00HI__ SCC F-CUP 1.00 1.01 96400-D2100 ',
],
(Ecu.fwdCamera, 0x7c4, None): [
b'\xf1\x00HI LKAS AT USA LHD 1.00 1.00 95895-D2020 160302',
b'\xf1\x00HI LKAS AT USA LHD 1.00 1.00 95895-D2030 170208',
],
(Ecu.engine, 0x7e0, None): [
b'\xf1\x810000000000\x00',
@@ -971,6 +973,7 @@ FW_VERSIONS = {
b'\xf1\x00BD MDPS C 1.00 1.02 56310-XX000 4BD2C102',
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): [
@@ -987,11 +990,13 @@ FW_VERSIONS = {
b'\xf1\x81616F2051\x00\x00\x00\x00\x00\x00\x00\x00',
],
(Ecu.abs, 0x7d1, None): [
b'\xf1\x00\x00\x00\x00\x00\x00\x00',
b'\xf1\x816VGRAH00018.ELF\xf1\x00\x00\x00\x00\x00\x00\x00',
b'\xf1\x8758900-M7AB0 \xf1\x816VQRAD00127.ELF\xf1\x00\x00\x00\x00\x00\x00\x00',
],
(Ecu.transmission, 0x7e1, None): [
b'\xf1\x006V2B0_C2\x00\x006V2C6051\x00\x00CBD0N20NL1\x00\x00\x00\x00',
b'\xf1\x006V2B0_C2\x00\x006V2C6051\x00\x00CBD0N20NL1\x90@\xc6\xae',
b'\xf1\x816U2VC051\x00\x00\xf1\x006U2V0_C2\x00\x006U2VC051\x00\x00DBD0T16SS0\x00\x00\x00\x00',
b"\xf1\x816U2VC051\x00\x00\xf1\x006U2V0_C2\x00\x006U2VC051\x00\x00DBD0T16SS0\xcf\x1e'\xc3",
],
@@ -1198,16 +1203,19 @@ FW_VERSIONS = {
},
CAR.KIA_NIRO_PHEV_2022: {
(Ecu.engine, 0x7e0, None): [
b'\xf1\x816H6G5051\x00\x00\x00\x00\x00\x00\x00\x00',
b'\xf1\x816H6G6051\x00\x00\x00\x00\x00\x00\x00\x00',
],
(Ecu.transmission, 0x7e1, None): [
b'\xf1\x006U3H1_C2\x00\x006U3J9051\x00\x00PDE0G16NL3\x00\x00\x00\x00',
b'\xf1\x816U3J9051\x00\x00\xf1\x006U3H1_C2\x00\x006U3J9051\x00\x00PDE0G16NL3\x00\x00\x00\x00',
],
(Ecu.eps, 0x7d4, None): [
b'\xf1\x00DE MDPS C 1.00 1.01 56310G5520\x00 4DEPC101',
],
(Ecu.fwdCamera, 0x7c4, None): [
b'\xf1\x00DEP MFC AT USA LHD 1.00 1.00 99211-G5500 210428',
b'\xf1\x00DEP MFC AT USA LHD 1.00 1.06 99211-G5000 201028',
],
(Ecu.fwdRadar, 0x7d0, None): [
b'\xf1\x00DEhe SCC F-CUP 1.00 1.00 99110-G5600 ',
@@ -1347,22 +1355,28 @@ FW_VERSIONS = {
CAR.ELANTRA_GT_I30: {
(Ecu.fwdCamera, 0x7c4, None): [
b'\xf1\x00PD LKAS AT KOR LHD 1.00 1.02 95740-G3000 A51',
b'\xf1\x00PD LKAS AT USA LHD 1.00 1.02 95740-G3000 A51',
b'\xf1\x00PD LKAS AT USA LHD 1.01 1.01 95740-G3100 A54',
],
(Ecu.transmission, 0x7e1, None): [
b'\xf1\x006U2U0_C2\x00\x006U2T0051\x00\x00DPD0D16KS0u\xce\x1fk',
b'\xf1\x006U2V0_C2\x00\x006U2V8051\x00\x00DPD0T16NS4\x00\x00\x00\x00',
b'\xf1\x006U2V0_C2\x00\x006U2V8051\x00\x00DPD0T16NS4\xda\x7f\xd6\xa7',
b'\xf1\x006U2V0_C2\x00\x006U2VA051\x00\x00DPD0H16NS0e\x0e\xcd\x8e',
],
(Ecu.eps, 0x7d4, None): [
b'\xf1\x00PD MDPS C 1.00 1.00 56310G3300\x00 4PDDC100',
b'\xf1\x00PD MDPS C 1.00 1.03 56310/G3300 4PDDC103',
b'\xf1\x00PD MDPS C 1.00 1.04 56310/G3300 4PDDC104',
],
(Ecu.abs, 0x7d1, None): [
b'\xf1\x00PD ESC \t 104\x18\t\x03 58920-G3350',
b'\xf1\x00PD ESC \x0b 103\x17\x110 58920-G3350',
b'\xf1\x00PD ESC \x0b 104\x18\t\x03 58920-G3350',
],
(Ecu.fwdRadar, 0x7d0, None): [
b'\xf1\x00PD__ SCC F-CUP 1.00 1.00 96400-G3300 ',
b'\xf1\x00PD__ SCC F-CUP 1.01 1.00 96400-G3100 ',
b'\xf1\x00PD__ SCC FNCUP 1.01 1.00 96400-G3000 ',
],
},
@@ -1542,6 +1556,7 @@ FW_VERSIONS = {
b'\xf1\x00NE1 MFC AT USA LHD 1.00 1.03 99211-GI010 220401',
b'\xf1\x00NE1 MFC AT USA LHD 1.00 1.05 99211-GI010 220614',
b'\xf1\x00NE1 MFC AT USA LHD 1.00 1.06 99211-GI010 230110',
b'\xf1\x00NE1 MFC AT KOR LHD 1.00 1.00 99211-GI020 230719',
],
},
CAR.IONIQ_6: {
@@ -1555,6 +1570,7 @@ FW_VERSIONS = {
},
CAR.TUCSON_4TH_GEN: {
(Ecu.fwdCamera, 0x7c4, None): [
b'\xf1\x00NX4 FR_CMR AT EUR LHD 1.00 1.00 99211-N9220 14K',
b'\xf1\x00NX4 FR_CMR AT EUR LHD 1.00 2.02 99211-N9000 14E',
b'\xf1\x00NX4 FR_CMR AT USA LHD 1.00 1.00 99211-N9210 14G',
b'\xf1\x00NX4 FR_CMR AT USA LHD 1.00 1.00 99211-N9220 14K',
@@ -1567,6 +1583,7 @@ FW_VERSIONS = {
(Ecu.fwdRadar, 0x7d0, None): [
b'\xf1\x00NX4__ 1.00 1.00 99110-N9100 ',
b'\xf1\x00NX4__ 1.00 1.01 99110-N9000 ',
b'\xf1\x00NX4__ 1.00 1.02 99110-N9000 ',
b'\xf1\x00NX4__ 1.01 1.00 99110-N9100 ',
],
},
@@ -1625,6 +1642,7 @@ FW_VERSIONS = {
],
(Ecu.fwdRadar, 0x7d0, None): [
b'\xf1\x00MQ4_ SCC F-CUP 1.00 1.06 99110-P2000 ',
b'\xf1\x00MQ4_ SCC FHCUP 1.00 1.00 99110-R5000 ',
b'\xf1\x00MQ4_ SCC FHCUP 1.00 1.06 99110-P2000 ',
b'\xf1\x00MQ4_ SCC FHCUP 1.00 1.08 99110-P2000 ',
],
+24 -24
View File
@@ -11,7 +11,6 @@ from openpilot.selfdrive.car.interfaces import CarInterfaceBase
from openpilot.selfdrive.car.disable_ecu import disable_ecu
Ecu = car.CarParams.Ecu
SafetyModel = car.CarParams.SafetyModel
ButtonType = car.CarState.ButtonEvent.Type
EventName = car.CarEvent.EventName
ENABLE_BUTTONS = (Buttons.RES_ACCEL, Buttons.SET_DECEL, Buttons.CANCEL)
@@ -19,17 +18,6 @@ BUTTONS_DICT = {Buttons.RES_ACCEL: ButtonType.accelCruise, Buttons.SET_DECEL: Bu
Buttons.GAP_DIST: ButtonType.gapAdjustCruise, Buttons.CANCEL: ButtonType.cancel}
def set_safety_config_hyundai(candidate, CAN, can_fd=False):
platform = SafetyModel.hyundaiCanfd if can_fd else \
SafetyModel.hyundaiLegacy if candidate in LEGACY_SAFETY_MODE_CAR else \
SafetyModel.hyundai
cfgs = [get_safety_config(platform), ]
if CAN.ECAN >= 4:
cfgs.insert(0, get_safety_config(SafetyModel.noOutput))
return cfgs
class CarInterface(CarInterfaceBase):
@staticmethod
def _get_params(ret, candidate, fingerprint, car_fw, experimental_long, docs):
@@ -55,6 +43,7 @@ class CarInterface(CarInterfaceBase):
# detect HDA2 with ADAS Driving ECU
if hda2:
ret.flags |= HyundaiFlags.CANFD_HDA2.value
if 0x110 in fingerprint[CAN.CAM]:
ret.flags |= HyundaiFlags.CANFD_HDA2_ALT_STEERING.value
else:
@@ -314,22 +303,33 @@ class CarInterface(CarInterfaceBase):
ret.spFlags |= HyundaiFlagsSP.SP_NAV_MSG.value
# *** panda safety config ***
ret.safetyConfigs = set_safety_config_hyundai(candidate, CAN, can_fd=(candidate in CANFD_CAR))
if hda2:
ret.flags |= HyundaiFlags.CANFD_HDA2.value
ret.safetyConfigs[-1].safetyParam |= Panda.FLAG_HYUNDAI_CANFD_HDA2
if candidate in CANFD_CAR:
if hda2 and ret.flags & HyundaiFlags.CANFD_HDA2_ALT_STEERING:
ret.safetyConfigs[-1].safetyParam |= Panda.FLAG_HYUNDAI_CANFD_HDA2_ALT_STEERING
cfgs = [get_safety_config(car.CarParams.SafetyModel.hyundaiCanfd), ]
if CAN.ECAN >= 4:
cfgs.insert(0, get_safety_config(car.CarParams.SafetyModel.noOutput))
ret.safetyConfigs = cfgs
if ret.flags & HyundaiFlags.CANFD_HDA2:
ret.safetyConfigs[-1].safetyParam |= Panda.FLAG_HYUNDAI_CANFD_HDA2
if ret.flags & HyundaiFlags.CANFD_HDA2_ALT_STEERING:
ret.safetyConfigs[-1].safetyParam |= Panda.FLAG_HYUNDAI_CANFD_HDA2_ALT_STEERING
if ret.flags & HyundaiFlags.CANFD_ALT_BUTTONS:
ret.safetyConfigs[-1].safetyParam |= Panda.FLAG_HYUNDAI_CANFD_ALT_BUTTONS
if ret.flags & HyundaiFlags.CANFD_CAMERA_SCC:
ret.safetyConfigs[-1].safetyParam |= Panda.FLAG_HYUNDAI_CAMERA_SCC
else:
if candidate in LEGACY_SAFETY_MODE_CAR:
# these cars require a special panda safety mode due to missing counters and checksums in the messages
ret.safetyConfigs = [get_safety_config(car.CarParams.SafetyModel.hyundaiLegacy)]
else:
ret.safetyConfigs = [get_safety_config(car.CarParams.SafetyModel.hyundai, 0)]
if candidate in CAMERA_SCC_CAR:
ret.safetyConfigs[0].safetyParam |= Panda.FLAG_HYUNDAI_CAMERA_SCC
if ret.spFlags & HyundaiFlagsSP.SP_ENHANCED_SCC:
ret.safetyConfigs[0].safetyParam |= Panda.FLAG_HYUNDAI_ESCC
if ret.flags & HyundaiFlags.CANFD_CAMERA_SCC or candidate in CAMERA_SCC_CAR:
ret.safetyConfigs[-1].safetyParam |= Panda.FLAG_HYUNDAI_CAMERA_SCC
if ret.spFlags & HyundaiFlagsSP.SP_ENHANCED_SCC:
ret.safetyConfigs[0].safetyParam |= Panda.FLAG_HYUNDAI_ESCC
if ret.openpilotLongitudinalControl:
ret.safetyConfigs[-1].safetyParam |= Panda.FLAG_HYUNDAI_LONG
if ret.flags & HyundaiFlags.HYBRID:
+50 -2
View File
@@ -260,7 +260,10 @@ CAR_INFO: Dict[str, Optional[Union[HyundaiCarInfo, List[HyundaiCarInfo]]]] = {
HyundaiCarInfo("Kia Niro Plug-in Hybrid 2018-19", "All", min_enable_speed=10. * CV.MPH_TO_MS, car_parts=CarParts.common([CarHarness.hyundai_c])),
HyundaiCarInfo("Kia Niro Plug-in Hybrid 2020", "All", car_parts=CarParts.common([CarHarness.hyundai_d])),
],
CAR.KIA_NIRO_PHEV_2022: HyundaiCarInfo("Kia Niro Plug-in Hybrid 2022", "All", car_parts=CarParts.common([CarHarness.hyundai_f])),
CAR.KIA_NIRO_PHEV_2022: [
HyundaiCarInfo("Kia Niro Plug-in Hybrid 2021", "All", car_parts=CarParts.common([CarHarness.hyundai_d])),
HyundaiCarInfo("Kia Niro Plug-in Hybrid 2022", "All", car_parts=CarParts.common([CarHarness.hyundai_f])),
],
CAR.KIA_NIRO_HEV_2021: [
HyundaiCarInfo("Kia Niro Hybrid 2021", car_parts=CarParts.common([CarHarness.hyundai_d])),
HyundaiCarInfo("Kia Niro Hybrid 2022", car_parts=CarParts.common([CarHarness.hyundai_f])),
@@ -413,6 +416,9 @@ HYUNDAI_VERSION_REQUEST_MULTI = bytes([uds.SERVICE_TYPE.READ_DATA_BY_IDENTIFIER]
p16(uds.DATA_IDENTIFIER_TYPE.APPLICATION_SOFTWARE_IDENTIFICATION) + \
p16(0xf100)
HYUNDAI_ECU_MANUFACTURING_DATE = bytes([uds.SERVICE_TYPE.READ_DATA_BY_IDENTIFIER]) + \
p16(uds.DATA_IDENTIFIER_TYPE.ECU_MANUFACTURING_DATE)
HYUNDAI_VERSION_RESPONSE = bytes([uds.SERVICE_TYPE.READ_DATA_BY_IDENTIFIER + 0x40])
# Regex patterns for parsing platform code, FW date, and part number from FW versions
@@ -431,6 +437,8 @@ PLATFORM_CODE_ECUS = [Ecu.fwdRadar, Ecu.fwdCamera, Ecu.eps]
# TODO: there are date codes in the ABS firmware versions in hex
DATE_FW_ECUS = [Ecu.fwdCamera]
ALL_HYUNDAI_ECUS = [Ecu.eps, Ecu.abs, Ecu.fwdRadar, Ecu.fwdCamera, Ecu.engine, Ecu.parkingAdas, Ecu.transmission, Ecu.adas, Ecu.hvac, Ecu.cornerRadar]
FW_QUERY_CONFIG = FwQueryConfig(
requests=[
# TODO: minimize shared whitelists for CAN and cornerRadar for CAN-FD
@@ -464,13 +472,52 @@ FW_QUERY_CONFIG = FwQueryConfig(
obd_multiplexing=False,
),
# CAN-FD debugging queries
# CAN & CAN FD query to understand the three digit date code
# HDA2 cars usually use 6 digit date codes, so skip bus 1
Request(
[HYUNDAI_ECU_MANUFACTURING_DATE],
[HYUNDAI_VERSION_RESPONSE],
whitelist_ecus=[Ecu.fwdCamera],
bus=0,
auxiliary=True,
logging=True,
),
# CAN & CAN FD logging queries (from camera)
Request(
[HYUNDAI_VERSION_REQUEST_LONG],
[HYUNDAI_VERSION_RESPONSE],
whitelist_ecus=ALL_HYUNDAI_ECUS,
bus=0,
auxiliary=True,
logging=True,
),
Request(
[HYUNDAI_VERSION_REQUEST_MULTI],
[HYUNDAI_VERSION_RESPONSE],
whitelist_ecus=ALL_HYUNDAI_ECUS,
bus=0,
auxiliary=True,
logging=True,
),
Request(
[HYUNDAI_VERSION_REQUEST_LONG],
[HYUNDAI_VERSION_RESPONSE],
whitelist_ecus=ALL_HYUNDAI_ECUS,
bus=1,
auxiliary=True,
obd_multiplexing=False,
logging=True,
),
# CAN-FD alt request logging queries
Request(
[HYUNDAI_VERSION_REQUEST_ALT],
[HYUNDAI_VERSION_RESPONSE],
whitelist_ecus=[Ecu.parkingAdas, Ecu.hvac],
bus=0,
auxiliary=True,
logging=True,
),
Request(
[HYUNDAI_VERSION_REQUEST_ALT],
@@ -478,6 +525,7 @@ FW_QUERY_CONFIG = FwQueryConfig(
whitelist_ecus=[Ecu.parkingAdas, Ecu.hvac],
bus=1,
auxiliary=True,
logging=True,
obd_multiplexing=False,
),
],
+5 -5
View File
@@ -1,6 +1,5 @@
import json
import os
import time
import numpy as np
import tomllib
from abc import abstractmethod, ABC
@@ -323,13 +322,14 @@ class RadarInterfaceBase(ABC):
self.pts = {}
self.delay = 0
self.radar_ts = CP.radarTimeStep
self.frame = 0
self.no_radar_sleep = 'NO_RADAR_SLEEP' in os.environ
def update(self, can_strings):
ret = car.RadarData.new_message()
if not self.no_radar_sleep:
time.sleep(self.radar_ts) # radard runs on RI updates
return ret
self.frame += 1
if (self.frame % int(100 * self.radar_ts)) == 0:
return car.RadarData.new_message()
return None
class CarStateBase(ABC):
+7 -4
View File
@@ -5,11 +5,14 @@ from functools import partial
import cereal.messaging as messaging
from openpilot.common.swaglog import cloudlog
from openpilot.selfdrive.boardd.boardd import can_list_to_can_capnp
from openpilot.selfdrive.car.fw_query_definitions import AddrType
from panda.python.uds import CanClient, IsoTpMessage, FUNCTIONAL_ADDRS, get_rx_addr_for_tx_addr
class IsoTpParallelQuery:
def __init__(self, sendcan, logcan, bus, addrs, request, response, response_offset=0x8, functional_addrs=None, debug=False, response_pending_timeout=10):
def __init__(self, sendcan: messaging.PubSocket, logcan: messaging.SubSocket, bus: int, addrs: list[int] | list[AddrType],
request: list[bytes], response: list[bytes], response_offset: int = 0x8,
functional_addrs: list[int] | None = None, debug: bool = False, response_pending_timeout: float = 10) -> None:
self.sendcan = sendcan
self.logcan = logcan
self.bus = bus
@@ -24,7 +27,7 @@ class IsoTpParallelQuery:
assert tx_addr not in FUNCTIONAL_ADDRS, f"Functional address should be defined in functional_addrs: {hex(tx_addr)}"
self.msg_addrs = {tx_addr: get_rx_addr_for_tx_addr(tx_addr[0], rx_offset=response_offset) for tx_addr in real_addrs}
self.msg_buffer = defaultdict(list)
self.msg_buffer: dict[int, list[tuple[int, int, bytes, int]]] = defaultdict(list)
def rx(self):
"""Drain can socket and sort messages into buffers based on address"""
@@ -63,7 +66,7 @@ class IsoTpParallelQuery:
messaging.drain_sock_raw(self.logcan)
self.msg_buffer = defaultdict(list)
def _create_isotp_msg(self, tx_addr, sub_addr, rx_addr):
def _create_isotp_msg(self, tx_addr: int, sub_addr: int | None, rx_addr: int):
can_client = CanClient(self._can_tx, partial(self._can_rx, rx_addr, sub_addr=sub_addr), tx_addr, rx_addr,
self.bus, sub_addr=sub_addr, debug=self.debug)
@@ -73,7 +76,7 @@ class IsoTpParallelQuery:
# as well as reduces chances we process messages from previous queries
return IsoTpMessage(can_client, timeout=0, separation_time=0.01, debug=self.debug, max_len=max_len)
def get_data(self, timeout, total_timeout=60.):
def get_data(self, timeout: float, total_timeout: float = 60.) -> dict[AddrType, bytes]:
self._drain_rx()
# Create message objects
+1 -1
View File
@@ -32,7 +32,7 @@ class CarState(CarStateBase):
# Match panda speed reading
speed_kph = cp.vl["ENGINE_DATA"]["SPEED"]
ret.standstill = speed_kph < .1
ret.standstill = speed_kph <= .1
can_gear = int(cp.vl["GEAR"]["GEAR"])
ret.gearShifter = self.parse_gear_shifter(self.shifter_values.get(can_gear, None))
+1 -1
View File
@@ -22,7 +22,7 @@ class CarInterface(CarInterfaceBase):
def _update(self, c):
self.sm.update(0)
gps_sock = 'gpsLocationExternal' if self.sm.rcv_frame['gpsLocationExternal'] > 1 else 'gpsLocation'
gps_sock = 'gpsLocationExternal' if self.sm.recv_frame['gpsLocationExternal'] > 1 else 'gpsLocation'
ret = car.CarState.new_message()
ret.vEgo = self.sm[gps_sock].speed
+13
View File
@@ -45,15 +45,28 @@ FW_VERSIONS = {
},
CAR.LEAF: {
(Ecu.abs, 0x740, None): [
b'476605SA1C',
b'476605SC2D',
b'476606WK7B',
b'476606WK9B',
],
(Ecu.eps, 0x742, None): [
b'5SA2A\x99A\x05\x02N123F\x15b\x00\x00\x00\x00\x00\x00\x00\x80',
b'5SA2A\xb7A\x05\x02N123F\x15\xa2\x00\x00\x00\x00\x00\x00\x00\x80',
b'5SN2A\xb7A\x05\x02N123F\x15\xa2\x00\x00\x00\x00\x00\x00\x00\x80',
b'5SN2A\xb7A\x05\x02N126F\x15\xb2\x00\x00\x00\x00\x00\x00\x00\x80',
],
(Ecu.fwdCamera, 0x707, None): [
b'5SA0ADB\x04\x18\x00\x00\x00\x00\x00_*6\x04\x94a\x00\x00\x00\x80',
b'5SA2ADB\x04\x18\x00\x00\x00\x00\x00_*6\x04\x94a\x00\x00\x00\x80',
b'6WK2ADB\x04\x18\x00\x00\x00\x00\x00R;1\x18\x99\x10\x00\x00\x00\x80',
b'6WK2BDB\x04\x18\x00\x00\x00\x00\x00R;1\x18\x99\x10\x00\x00\x00\x80',
b'6WK2CDB\x04\x18\x00\x00\x00\x00\x00R=1\x18\x99\x10\x00\x00\x00\x80',
],
(Ecu.gateway, 0x18dad0f1, None): [
b'284U25SA3C',
b'284U25SP1C',
b'284U26WK0A',
b'284U26WK0C',
],
},
+9
View File
@@ -13,6 +13,15 @@ def create_steering_control(packer, apply_steer, steer_req):
return packer.make_can_msg("ES_LKAS", 0, values)
def create_steering_control_angle(packer, apply_steer, steer_req):
values = {
"LKAS_Output": apply_steer,
"LKAS_Request": steer_req,
"SET_3": 3
}
return packer.make_can_msg("ES_LKAS_ANGLE", 0, values)
def create_steering_status(packer):
return packer.make_can_msg("ES_LKAS_State", 0, {})
+12
View File
@@ -0,0 +1,12 @@
#!/bin/bash
SCRIPT_DIR=$(dirname "$0")
BASEDIR=$(realpath "$SCRIPT_DIR/../../../")
cd $BASEDIR
MAX_EXAMPLES=300
INTERNAL_SEG_CNT=300
FILEREADER_CACHE=1
INTERNAL_SEG_LIST=selfdrive/car/tests/test_models_segs.txt
cd selfdrive/car/tests && pytest test_models.py test_car_interfaces.py
+3 -1
View File
@@ -20,7 +20,6 @@ non_tested_cars = [
GM.CADILLAC_ATS,
GM.HOLDEN_ASTRA,
GM.MALIBU,
GM.EQUINOX,
HYUNDAI.GENESIS_G90,
HONDA.ODYSSEY_CHN,
VOLKSWAGEN.CRAFTER_MK2, # need a route from an ACC-equipped Crafter
@@ -46,6 +45,7 @@ routes = [
CarTestRoute("3d84727705fecd04|2021-05-25--08-38-56", CHRYSLER.PACIFICA_2020),
CarTestRoute("221c253375af4ee9|2022-06-15--18-38-24", CHRYSLER.RAM_1500),
CarTestRoute("8fb5eabf914632ae|2022-08-04--17-28-53", CHRYSLER.RAM_HD, segment=6),
CarTestRoute("3379c85aeedc8285|2023-12-07--17-49-39", CHRYSLER.DODGE_DURANGO),
CarTestRoute("54827bf84c38b14f|2023-01-25--14-14-11", FORD.BRONCO_SPORT_MK1),
CarTestRoute("f8eaaccd2a90aef8|2023-05-04--15-10-09", FORD.ESCAPE_MK4),
@@ -59,6 +59,7 @@ routes = [
CarTestRoute("7cc2a8365b4dd8a9|2018-12-02--12-10-44", GM.ACADIA),
CarTestRoute("aa20e335f61ba898|2019-02-05--16-59-04", GM.BUICK_REGAL),
CarTestRoute("75a6bcb9b8b40373|2023-03-11--22-47-33", GM.BUICK_LACROSSE),
CarTestRoute("e746f59bc96fd789|2024-01-31--22-25-58", GM.EQUINOX),
CarTestRoute("ef8f2185104d862e|2023-02-09--18-37-13", GM.ESCALADE),
CarTestRoute("46460f0da08e621e|2021-10-26--07-21-46", GM.ESCALADE_ESV),
CarTestRoute("168f8b3be57f66ae|2023-09-12--21-44-42", GM.ESCALADE_ESV_2019),
@@ -207,6 +208,7 @@ routes = [
CarTestRoute("ec429c0f37564e3c|2020-02-01--17-28-12", TOYOTA.LEXUS_NX), # hybrid
CarTestRoute("3fd5305f8b6ca765|2021-04-28--19-26-49", TOYOTA.LEXUS_NX_TSS2),
CarTestRoute("09ae96064ed85a14|2022-06-09--12-22-31", TOYOTA.LEXUS_NX_TSS2), # hybrid
CarTestRoute("4765fbbf59e3cd88|2024-02-06--17-45-32", TOYOTA.LEXUS_LC_TSS2),
CarTestRoute("0a302ffddbb3e3d3|2020-02-08--16-19-08", TOYOTA.HIGHLANDER_TSS2),
CarTestRoute("437e4d2402abf524|2021-05-25--07-58-50", TOYOTA.HIGHLANDER_TSS2), # hybrid
CarTestRoute("3183cd9b021e89ce|2021-05-25--10-34-44", TOYOTA.HIGHLANDER),
+15 -5
View File
@@ -14,6 +14,10 @@ from openpilot.selfdrive.car.car_helpers import interfaces
from openpilot.selfdrive.car.fingerprints import all_known_cars
from openpilot.selfdrive.car.fw_versions import FW_VERSIONS
from openpilot.selfdrive.car.interfaces import get_interface_attr
from openpilot.selfdrive.controls.lib.latcontrol_angle import LatControlAngle
from openpilot.selfdrive.controls.lib.latcontrol_pid import LatControlPID
from openpilot.selfdrive.controls.lib.latcontrol_torque import LatControlTorque
from openpilot.selfdrive.controls.lib.longcontrol import LongControl
from openpilot.selfdrive.test.fuzzy_generation import DrawType, FuzzyGenerator
ALL_ECUS = list({ecu for ecus in FW_VERSIONS.values() for ecu in ecus.keys()})
@@ -42,11 +46,6 @@ def get_fuzzy_car_interface_args(draw: DrawType) -> dict:
class TestCarInterfaces(unittest.TestCase):
@classmethod
def setUpClass(cls):
os.environ['NO_RADAR_SLEEP'] = '1'
# FIXME: Due to the lists used in carParams, Phase.target is very slow and will cause
# many generated examples to overrun when max_examples > ~20, don't use it
@parameterized.expand([(car,) for car in sorted(all_known_cars())])
@@ -105,6 +104,17 @@ class TestCarInterfaces(unittest.TestCase):
car_interface.apply(CC, now_nanos)
now_nanos += DT_CTRL * 1e9 # 10ms
# Test controller initialization
# TODO: wait until card refactor is merged to run controller a few times,
# hypothesis also slows down significantly with just one more message draw
LongControl(car_params)
if car_params.steerControlType == car.CarParams.SteerControlType.angle:
LatControlAngle(car_params, car_interface)
elif car_params.lateralTuning.which() == 'pid':
LatControlPID(car_params, car_interface)
elif car_params.lateralTuning.which() == 'torque':
LatControlTorque(car_params, car_interface)
# Test radar interface
RadarInterface = importlib.import_module(f'selfdrive.car.{car_params.carName}.radar_interface').RadarInterface
radar_interface = RadarInterface(car_params)
+18 -17
View File
@@ -1,5 +1,4 @@
#!/usr/bin/env python3
import pytest
import random
import time
import unittest
@@ -226,7 +225,7 @@ class TestFwFingerprintTiming(unittest.TestCase):
def test_startup_timing(self):
# Tests worse-case VIN query time and typical present ECU query time
vin_ref_times = {'worst': 1.5, 'best': 0.5} # best assumes we go through all queries to get a match
vin_ref_times = {'worst': 1.0, 'best': 0.5} # best assumes we go through all queries to get a match
present_ecu_ref_time = 0.75
def fake_get_ecu_addrs(*_, timeout):
@@ -252,9 +251,8 @@ class TestFwFingerprintTiming(unittest.TestCase):
self._assert_timing(self.total_time / self.N, vin_ref_times[name])
print(f'get_vin {name} case, query time={self.total_time / self.N} seconds')
@pytest.mark.timeout(60)
def test_fw_query_timing(self):
total_ref_time = 6.8
total_ref_time = {1: 5.95, 2: 6.85}
brand_ref_times = {
1: {
'gm': 0.5,
@@ -262,7 +260,7 @@ class TestFwFingerprintTiming(unittest.TestCase):
'chrysler': 0.3,
'ford': 0.1,
'honda': 0.55,
'hyundai': 0.65,
'hyundai': 1.05,
'mazda': 0.1,
'nissan': 0.8,
'subaru': 0.45,
@@ -272,28 +270,31 @@ class TestFwFingerprintTiming(unittest.TestCase):
},
2: {
'ford': 0.2,
'hyundai': 1.05,
'hyundai': 1.85,
}
}
total_time = 0
total_times = {1: 0.0, 2: 0.0}
for num_pandas in (1, 2):
for brand, config in FW_QUERY_CONFIGS.items():
with self.subTest(brand=brand, num_pandas=num_pandas):
multi_panda_requests = [r for r in config.requests if r.bus > 3]
if not len(multi_panda_requests) and num_pandas > 1:
raise unittest.SkipTest("No multi-panda FW queries")
avg_time = self._benchmark_brand(brand, num_pandas)
total_time += avg_time
total_times[num_pandas] += avg_time
avg_time = round(avg_time, 2)
self._assert_timing(avg_time, brand_ref_times[num_pandas][brand])
ref_time = brand_ref_times[num_pandas].get(brand)
if ref_time is None:
# ref time should be same as 1 panda if no aux queries
ref_time = brand_ref_times[num_pandas - 1][brand]
self._assert_timing(avg_time, ref_time)
print(f'{brand=}, {num_pandas=}, {len(config.requests)=}, avg FW query time={avg_time} seconds')
with self.subTest(brand='all_brands'):
total_time = round(total_time, 2)
self._assert_timing(total_time, total_ref_time)
print(f'all brands, total FW query time={total_time} seconds')
for num_pandas in (1, 2):
with self.subTest(brand='all_brands', num_pandas=num_pandas):
total_time = round(total_times[num_pandas], 2)
self._assert_timing(total_time, total_ref_time[num_pandas])
print(f'all brands, total FW query time={total_time} seconds')
if __name__ == "__main__":
+11 -17
View File
@@ -23,9 +23,8 @@ from openpilot.selfdrive.car.tests.routes import non_tested_cars, routes, CarTes
from openpilot.selfdrive.controls.controlsd import Controls
from openpilot.selfdrive.test.helpers import read_segment_list
from openpilot.system.hardware.hw import DEFAULT_DOWNLOAD_CACHE_ROOT
from openpilot.tools.lib.comma_car_segments import get_url
from openpilot.tools.lib.logreader import LogReader
from openpilot.tools.lib.route import Route, SegmentName, RouteName
from openpilot.tools.lib.logreader import LogReader, internal_source, openpilotci_source
from openpilot.tools.lib.route import SegmentName
from panda.tests.libpanda import libpanda_py
@@ -37,7 +36,7 @@ NUM_JOBS = int(os.environ.get("NUM_JOBS", "1"))
JOB_ID = int(os.environ.get("JOB_ID", "0"))
INTERNAL_SEG_LIST = os.environ.get("INTERNAL_SEG_LIST", "")
INTERNAL_SEG_CNT = int(os.environ.get("INTERNAL_SEG_CNT", "0"))
MAX_EXAMPLES = int(os.environ.get("MAX_EXAMPLES", "50"))
MAX_EXAMPLES = int(os.environ.get("MAX_EXAMPLES", "300"))
CI = os.environ.get("CI", None) is not None
@@ -75,14 +74,6 @@ class TestCarModelBase(unittest.TestCase):
elm_frame: Optional[int]
car_safety_mode_frame: Optional[int]
@classmethod
def get_logreader(cls, seg):
if len(INTERNAL_SEG_LIST):
route_name = RouteName(cls.test_route.route)
return LogReader(f"cd:/{route_name.dongle_id}/{route_name.time_str}/{seg}/rlog.bz2")
else:
return LogReader(get_url(cls.test_route.route, seg))
@classmethod
def get_testing_data_from_logreader(cls, lr):
car_fw = []
@@ -133,22 +124,26 @@ class TestCarModelBase(unittest.TestCase):
if cls.test_route.segment is not None:
test_segs = (cls.test_route.segment,)
# Try the primary method first (CI or internal)
is_internal = len(INTERNAL_SEG_LIST)
for seg in test_segs:
segment_range = f"{cls.test_route.route}/{seg}"
try:
lr = cls.get_logreader(seg)
lr = LogReader(segment_range, default_source=internal_source if is_internal else openpilotci_source)
return cls.get_testing_data_from_logreader(lr)
except Exception:
pass
# Route is not in CI bucket, assume either user has access (private), or it is public
# test_route_on_ci_bucket will fail when running in CI
if not len(INTERNAL_SEG_LIST):
if not is_internal:
cls.test_route_on_bucket = False
for seg in test_segs:
segment_range = f"{cls.test_route.route}/{seg}"
try:
lr = LogReader(Route(cls.test_route.route).log_paths()[seg])
lr = LogReader(segment_range)
return cls.get_testing_data_from_logreader(lr)
except Exception:
pass
@@ -239,7 +234,6 @@ class TestCarModelBase(unittest.TestCase):
self.assertEqual(can_invalid_cnt, 0)
def test_radar_interface(self):
os.environ['NO_RADAR_SLEEP'] = "1"
RadarInterface = importlib.import_module(f'selfdrive.car.{self.CP.carName}.radar_interface').RadarInterface
RI = RadarInterface(self.CP)
assert RI
+1 -1
View File
@@ -42,7 +42,7 @@ legend = ["LAT_ACCEL_FACTOR", "MAX_LAT_ACCEL_MEASURED", "FRICTION"]
"CHEVROLET BOLT EUV 2022" = [2.0, 2.0, 0.05]
"CHEVROLET SILVERADO 1500 2020" = [1.9, 1.9, 0.112]
"CHEVROLET TRAILBLAZER 2021" = [1.33, 1.9, 0.16]
"CHEVROLET EQUINOX 2019" = [2.0, 2.0, 0.05]
"CHEVROLET EQUINOX 2019" = [2.5, 2.5, 0.05]
"VOLKSWAGEN PASSAT NMS" = [2.5, 2.5, 0.1]
"VOLKSWAGEN SHARAN 2ND GEN" = [2.5, 2.5, 0.1]
"HYUNDAI SANTA CRUZ 1ST GEN" = [2.7, 2.7, 0.1]
@@ -5,12 +5,15 @@ legend = ["LAT_ACCEL_FACTOR", "MAX_LAT_ACCEL_MEASURED", "FRICTION"]
"MAZDA CX-5 2022" = "MAZDA CX-9 2021"
"MAZDA CX-9" = "MAZDA CX-9 2021"
"DODGE DURANGO 2021" = "CHRYSLER PACIFICA 2020"
"TOYOTA ALPHARD 2020" = "TOYOTA SIENNA 2018"
"TOYOTA PRIUS v 2017" = "TOYOTA PRIUS 2017"
"LEXUS IS 2018" = "LEXUS NX 2018"
"LEXUS CT HYBRID 2018" = "LEXUS NX 2018"
"LEXUS ES 2018" = "TOYOTA CAMRY 2018"
"LEXUS RC 2020" = "LEXUS NX 2020"
"LEXUS LC 2024" = "LEXUS NX 2020"
"KIA OPTIMA 4TH GEN" = "HYUNDAI SONATA 2020"
"KIA OPTIMA 4TH GEN FACELIFT" = "HYUNDAI SONATA 2020"
+2 -2
View File
@@ -59,10 +59,10 @@ class CarController:
apply_steer = apply_meas_steer_torque_limits(new_steer, self.last_steer, CS.out.steeringTorqueEps, self.params)
# >100 degree/sec steering fault prevention
self.steer_rate_counter, apply_steer_req = common_fault_avoidance(abs(CS.out.steeringRateDeg) >= MAX_STEER_RATE, CC.latActive,
self.steer_rate_counter, apply_steer_req = common_fault_avoidance(abs(CS.out.steeringRateDeg) >= MAX_STEER_RATE, lat_active,
self.steer_rate_counter, MAX_STEER_RATE_FRAMES)
if not CC.latActive:
if not lat_active:
apply_steer = 0
# *** steer angle ***
+6
View File
@@ -104,6 +104,9 @@ class CarState(CarStateBase):
ret.leftBlinker = cp.vl["BLINKERS_STATE"]["TURN_SIGNALS"] == 1
ret.rightBlinker = cp.vl["BLINKERS_STATE"]["TURN_SIGNALS"] == 2
if self.CP.carFingerprint != CAR.MIRAI:
ret.engineRpm = cp.vl["ENGINE_RPM"]["RPM"]
ret.steeringTorque = cp.vl["STEER_TORQUE_SENSOR"]["STEER_TORQUE_DRIVER"]
ret.steeringTorqueEps = cp.vl["STEER_TORQUE_SENSOR"]["STEER_TORQUE_EPS"] * self.eps_torque_scale
# we could use the override bit from dbc, but it's triggered at too high torque values
@@ -274,6 +277,9 @@ class CarState(CarStateBase):
("STEER_TORQUE_SENSOR", 50),
]
if CP.carFingerprint != CAR.MIRAI:
messages.append(("ENGINE_RPM", 42))
if CP.carFingerprint in UNSUPPORTED_DSU_CAR:
messages.append(("DSU_CRUISE", 5))
messages.append(("PCM_CRUISE_ALT", 1))
+24
View File
@@ -38,6 +38,7 @@ FW_VERSIONS = {
b'F152607180\x00\x00\x00\x00\x00\x00',
b'F152641040\x00\x00\x00\x00\x00\x00',
b'F152641050\x00\x00\x00\x00\x00\x00',
b'F152641060\x00\x00\x00\x00\x00\x00',
b'F152641061\x00\x00\x00\x00\x00\x00',
],
(Ecu.dsu, 0x791, None): [
@@ -55,10 +56,12 @@ FW_VERSIONS = {
b'\x01896630725100\x00\x00\x00\x00',
b'\x01896630725200\x00\x00\x00\x00',
b'\x01896630725300\x00\x00\x00\x00',
b'\x01896630725400\x00\x00\x00\x00',
b'\x01896630735100\x00\x00\x00\x00',
b'\x01896630738000\x00\x00\x00\x00',
b'\x02896630724000\x00\x00\x00\x00897CF3302002\x00\x00\x00\x00',
b'\x02896630728000\x00\x00\x00\x00897CF3302002\x00\x00\x00\x00',
b'\x02896630734000\x00\x00\x00\x00897CF3305001\x00\x00\x00\x00',
b'\x02896630737000\x00\x00\x00\x00897CF3305001\x00\x00\x00\x00',
],
(Ecu.fwdRadar, 0x750, 0xf): [
@@ -1361,16 +1364,19 @@ FW_VERSIONS = {
b'\x018966378G3000\x00\x00\x00\x00',
],
(Ecu.engine, 0x7e0, None): [
b'\x0237881000\x00\x00\x00\x00\x00\x00\x00\x00A4701000\x00\x00\x00\x00\x00\x00\x00\x00',
b'\x0237887000\x00\x00\x00\x00\x00\x00\x00\x00A4701000\x00\x00\x00\x00\x00\x00\x00\x00',
b'\x02378A0000\x00\x00\x00\x00\x00\x00\x00\x00A4701000\x00\x00\x00\x00\x00\x00\x00\x00',
b'\x02378F4000\x00\x00\x00\x00\x00\x00\x00\x00A4701000\x00\x00\x00\x00\x00\x00\x00\x00',
],
(Ecu.abs, 0x7b0, None): [
b'\x01F152678221\x00\x00\x00\x00\x00\x00',
b'F152678200\x00\x00\x00\x00\x00\x00',
b'F152678210\x00\x00\x00\x00\x00\x00',
b'F152678211\x00\x00\x00\x00\x00\x00',
],
(Ecu.eps, 0x7a1, None): [
b'8965B78110\x00\x00\x00\x00\x00\x00',
b'8965B78120\x00\x00\x00\x00\x00\x00',
],
(Ecu.fwdRadar, 0x750, 0xf): [
@@ -1383,6 +1389,23 @@ FW_VERSIONS = {
b'\x028646F7803100\x00\x00\x00\x008646G2601400\x00\x00\x00\x00',
],
},
CAR.LEXUS_LC_TSS2: {
(Ecu.engine, 0x7e0, None): [
b'\x0131130000\x00\x00\x00\x00\x00\x00\x00\x00',
],
(Ecu.abs, 0x7b0, None): [
b'F152611390\x00\x00\x00\x00\x00\x00',
],
(Ecu.eps, 0x7a1, None): [
b'8965B11091\x00\x00\x00\x00\x00\x00',
],
(Ecu.fwdRadar, 0x750, 0xf): [
b'\x018821F6201400\x00\x00\x00\x00',
],
(Ecu.fwdCamera, 0x750, 0x6d): [
b'\x028646F1105200\x00\x00\x00\x008646G3304000\x00\x00\x00\x00',
],
},
CAR.LEXUS_RC: {
(Ecu.engine, 0x700, None): [
b'\x01896632461100\x00\x00\x00\x00',
@@ -1417,6 +1440,7 @@ FW_VERSIONS = {
},
CAR.LEXUS_RX: {
(Ecu.engine, 0x700, None): [
b'\x01896630E36100\x00\x00\x00\x00',
b'\x01896630E36200\x00\x00\x00\x00',
b'\x01896630E36300\x00\x00\x00\x00',
b'\x01896630E37100\x00\x00\x00\x00',
+6
View File
@@ -182,6 +182,12 @@ class CarInterface(CarInterfaceBase):
ret.tireStiffnessFactor = 0.444 # not optimized yet
ret.mass = 4070 * CV.LB_TO_KG
elif candidate == CAR.LEXUS_LC_TSS2:
ret.wheelbase = 2.87
ret.steerRatio = 13.0
ret.tireStiffnessFactor = 0.444 # not optimized yet
ret.mass = 4500 * CV.LB_TO_KG
elif candidate == CAR.PRIUS_TSS2:
ret.wheelbase = 2.70002 # from toyota online sepc.
ret.steerRatio = 13.4 # True steerRatio from older prius
+5 -1
View File
@@ -81,6 +81,7 @@ class CAR(StrEnum):
LEXUS_IS_TSS2 = "LEXUS IS 2023"
LEXUS_NX = "LEXUS NX 2018"
LEXUS_NX_TSS2 = "LEXUS NX 2020"
LEXUS_LC_TSS2 = "LEXUS LC 2024"
LEXUS_RC = "LEXUS RC 2020"
LEXUS_RX = "LEXUS RX 2016"
LEXUS_RX_TSS2 = "LEXUS RX 2020"
@@ -206,6 +207,7 @@ CAR_INFO: Dict[str, Union[ToyotaCarInfo, List[ToyotaCarInfo]]] = {
ToyotaCarInfo("Lexus NX 2020-21"),
ToyotaCarInfo("Lexus NX Hybrid 2020-21"),
],
CAR.LEXUS_LC_TSS2: ToyotaCarInfo("Lexus LC 2024"),
CAR.LEXUS_RC: ToyotaCarInfo("Lexus RC 2018-20"),
CAR.LEXUS_RX: [
ToyotaCarInfo("Lexus RX 2016", "Lexus Safety System+"),
@@ -444,6 +446,7 @@ DBC = {
CAR.PRIUS: dbc_dict('toyota_nodsu_pt_generated', 'toyota_adas'),
CAR.PRIUS_V: dbc_dict('toyota_new_mc_pt_generated', 'toyota_adas'),
CAR.COROLLA: dbc_dict('toyota_new_mc_pt_generated', 'toyota_adas'),
CAR.LEXUS_LC_TSS2: dbc_dict('toyota_nodsu_pt_generated', 'toyota_tss2_adas'),
CAR.LEXUS_RC: dbc_dict('toyota_tnga_k_pt_generated', 'toyota_adas'),
CAR.LEXUS_RX: dbc_dict('toyota_tnga_k_pt_generated', 'toyota_adas'),
CAR.LEXUS_RX_TSS2: dbc_dict('toyota_nodsu_pt_generated', 'toyota_tss2_adas'),
@@ -480,7 +483,8 @@ EPS_SCALE = defaultdict(lambda: 73, {CAR.PRIUS: 66, CAR.COROLLA: 88, CAR.LEXUS_I
# Toyota/Lexus Safety Sense 2.0 and 2.5
TSS2_CAR = {CAR.RAV4_TSS2, CAR.RAV4_TSS2_2022, CAR.RAV4_TSS2_2023, CAR.COROLLA_TSS2, CAR.LEXUS_ES_TSS2,
CAR.LEXUS_RX_TSS2, CAR.HIGHLANDER_TSS2, CAR.PRIUS_TSS2, CAR.CAMRY_TSS2, CAR.LEXUS_IS_TSS2,
CAR.MIRAI, CAR.LEXUS_NX_TSS2, CAR.ALPHARD_TSS2, CAR.AVALON_TSS2, CAR.CHR_TSS2}
CAR.MIRAI, CAR.LEXUS_NX_TSS2, CAR.LEXUS_LC_TSS2, CAR.ALPHARD_TSS2, CAR.AVALON_TSS2,
CAR.CHR_TSS2}
NO_DSU_CAR = TSS2_CAR | {CAR.CHR, CAR.CAMRY}
+10 -4
View File
@@ -15,7 +15,7 @@ def is_valid_vin(vin: str):
return re.fullmatch(VIN_RE, vin) is not None
def get_vin(logcan, sendcan, buses, timeout=0.1, retry=3, debug=False):
def get_vin(logcan, sendcan, buses, timeout=0.1, retry=2, debug=False):
for i in range(retry):
for bus in buses:
for request, response, valid_buses, vin_addrs, functional_addrs, rx_offset in (
@@ -26,8 +26,14 @@ def get_vin(logcan, sendcan, buses, timeout=0.1, retry=3, debug=False):
if bus not in valid_buses:
continue
# When querying functional addresses, ideally we respond to everything that sends a first frame to avoid leaving the
# ECU in a temporary bad state. Note that we may not cover all ECUs and response offsets. TODO: query physical addrs
tx_addrs = vin_addrs
if functional_addrs is not None:
tx_addrs = [a for a in range(0x700, 0x800) if a != 0x7DF] + list(range(0x18DA00F1, 0x18DB00F1, 0x100))
try:
query = IsoTpParallelQuery(sendcan, logcan, bus, vin_addrs, [request, ], [response, ], response_offset=rx_offset,
query = IsoTpParallelQuery(sendcan, logcan, bus, tx_addrs, [request, ], [response, ], response_offset=rx_offset,
functional_addrs=functional_addrs, debug=debug)
results = query.get_data(timeout)
@@ -42,8 +48,8 @@ def get_vin(logcan, sendcan, buses, timeout=0.1, retry=3, debug=False):
if vin.startswith(b'\x11'):
vin = vin[1:18]
cloudlog.warning(f"got vin with {request=}")
return get_rx_addr_for_tx_addr(addr), bus, vin.decode()
cloudlog.error(f"got vin with {request=}")
return get_rx_addr_for_tx_addr(addr, rx_offset=rx_offset), bus, vin.decode()
except Exception:
cloudlog.exception("VIN query exception")
+5
View File
@@ -320,6 +320,7 @@ FW_VERSIONS = {
b'\xf1\x8704E906024L \xf1\x895595',
b'\xf1\x8704E906024L \xf1\x899970',
b'\xf1\x8704E906027MS\xf1\x896223',
b'\xf1\x8705E906013DB\xf1\x893361',
b'\xf1\x875G0906259T \xf1\x890003',
],
(Ecu.transmission, 0x7e1, None): [
@@ -327,6 +328,7 @@ FW_VERSIONS = {
b'\xf1\x8709S927158BS\xf1\x893642',
b'\xf1\x8709S927158BS\xf1\x893694',
b'\xf1\x8709S927158CK\xf1\x893770',
b'\xf1\x8709S927158JC\xf1\x894113',
b'\xf1\x8709S927158R \xf1\x893552',
b'\xf1\x8709S927158R \xf1\x893587',
b'\xf1\x870GC300020N \xf1\x892803',
@@ -340,9 +342,11 @@ FW_VERSIONS = {
b'\xf1\x875Q0959655BR\xf1\x890403\xf1\x82\x1319170031313300314240011550159333463100',
b'\xf1\x875Q0959655CB\xf1\x890421\xf1\x82\x1314171231313500314642021650169333613100',
b'\xf1\x875Q0959655CB\xf1\x890421\xf1\x82\x1314171231313500314643021650169333613100',
b'\xf1\x875Q0959655CB\xf1\x890421\xf1\x82\x1317171231313500314642023050309333613100',
],
(Ecu.eps, 0x712, None): [
b'\xf1\x873Q0909144M \xf1\x895082\xf1\x82\x0571A10A11A1',
b'\xf1\x875QM907144D \xf1\x891063\xf1\x82\x000_A1080_OM',
b'\xf1\x875QM909144B \xf1\x891081\xf1\x82\x0521A10A01A1',
b'\xf1\x875QM909144B \xf1\x891081\xf1\x82\x0521B00404A1',
b'\xf1\x875QM909144C \xf1\x891082\xf1\x82\x0521A00642A1',
@@ -350,6 +354,7 @@ FW_VERSIONS = {
b'\xf1\x875QN909144B \xf1\x895082\xf1\x82\x0571A10A11A1',
],
(Ecu.fwdRadar, 0x757, None): [
b'\xf1\x872Q0907572AA\xf1\x890396',
b'\xf1\x875Q0907572N \xf1\x890681',
b'\xf1\x875Q0907572P \xf1\x890682',
b'\xf1\x875Q0907572R \xf1\x890771',
+2 -2
View File
@@ -223,8 +223,8 @@ CAR_INFO: Dict[str, Union[VWCarInfo, List[VWCarInfo]]] = {
VWCarInfo("Volkswagen Golf SportsVan 2015-20"),
],
CAR.JETTA_MK7: [
VWCarInfo("Volkswagen Jetta 2018-22"),
VWCarInfo("Volkswagen Jetta GLI 2021-22"),
VWCarInfo("Volkswagen Jetta 2018-24"),
VWCarInfo("Volkswagen Jetta GLI 2021-24"),
],
CAR.PASSAT_MK8: [
VWCarInfo("Volkswagen Passat 2015-22", footnotes=[Footnote.PASSAT]),
+20 -8
View File
@@ -60,7 +60,7 @@ class Controls:
config_realtime_process(4, Priority.CTRL_HIGH)
# Ensure the current branch is cached, otherwise the first iteration of controlsd lags
self.branch = get_short_branch("")
self.branch = get_short_branch()
# Setup sockets
self.pm = messaging.PubMaster(['sendcan', 'controlsState', 'carState',
@@ -80,7 +80,8 @@ class Controls:
'driverMonitoringState', 'longitudinalPlan', 'liveLocationKalman',
'managerState', 'liveParameters', 'radarState', 'liveTorqueParameters',
'testJoystick', 'longitudinalPlanSP', 'modelV2SP'] + self.camera_packets + self.sensor_packets,
ignore_alive=ignore, ignore_avg_freq=['radarState', 'testJoystick'], ignore_valid=['testJoystick', ])
ignore_alive=ignore, ignore_avg_freq=ignore+['radarState', 'testJoystick'], ignore_valid=['testJoystick', ],
frequency=int(1/DT_CTRL))
if CI is None:
# wait for one pandaState and one CAN packet
@@ -330,7 +331,7 @@ class Controls:
num_events = len(self.events)
not_running = {p.name for p in self.sm['managerState'].processes if not p.running and p.shouldBeRunning}
if self.sm.rcv_frame['managerState'] and (not_running - IGNORE_PROCESSES):
if self.sm.recv_frame['managerState'] and (not_running - IGNORE_PROCESSES):
self.events.add(EventName.processNotRunning)
if not_running != self.not_running_prev:
cloudlog.event("process_not_running", not_running=not_running, error=True)
@@ -387,7 +388,7 @@ class Controls:
self.events.add(EventName.paramsdTemporaryError)
# conservative HW alert. if the data or frequency are off, locationd will throw an error
if any((self.sm.frame - self.sm.rcv_frame[s])*DT_CTRL > 10. for s in self.sensor_packets):
if any((self.sm.frame - self.sm.recv_frame[s])*DT_CTRL > 10. for s in self.sensor_packets):
self.events.add(EventName.sensorDataInvalid)
if not REPLAY:
@@ -452,6 +453,17 @@ class Controls:
self.set_initial_state()
self.params.put_bool_nonblocking("ControlsReady", True)
cloudlog.event(
"controlsd.initialized",
dt=self.sm.frame*DT_CTRL,
timeout=timed_out,
canValid=CS.canValid,
invalid=[s for s, valid in self.sm.valid.items() if not valid],
not_alive=[s for s, alive in self.sm.alive.items() if not alive],
not_freq_ok=[s for s, freq_ok in self.sm.freq_ok.items() if not freq_ok],
error=True,
)
# Check for CAN timeout
if not can_strs:
self.can_rcv_timeout_counter += 1
@@ -610,7 +622,7 @@ class Controls:
if not self.joystick_mode:
# accel PID loop
pid_accel_limits = self.CI.get_pid_accel_limits(self.CP, CS.vEgo, self.v_cruise_helper.v_cruise_kph * CV.KPH_TO_MS)
t_since_plan = (self.sm.frame - self.sm.rcv_frame['longitudinalPlan']) * DT_CTRL
t_since_plan = (self.sm.frame - self.sm.recv_frame['longitudinalPlan']) * DT_CTRL
actuators.accel = self.LoC.update(CC.longActive, CS, long_plan, pid_accel_limits, t_since_plan)
# Steering PID loop and lateral MPC
@@ -621,9 +633,9 @@ class Controls:
self.sm['liveLocationKalman'])
else:
lac_log = log.ControlsState.LateralDebugState.new_message()
if self.sm.rcv_frame['testJoystick'] > 0:
if self.sm.recv_frame['testJoystick'] > 0:
# reset joystick if it hasn't been received in a while
should_reset_joystick = (self.sm.frame - self.sm.rcv_frame['testJoystick'])*DT_CTRL > 0.2
should_reset_joystick = (self.sm.frame - self.sm.recv_frame['testJoystick'])*DT_CTRL > 0.2
if not should_reset_joystick:
joystick_axes = self.sm['testJoystick'].axes
else:
@@ -698,7 +710,7 @@ class Controls:
CC.cruiseControl.override = self.enabled and not CC.longActive and self.CP.openpilotLongitudinalControl
CC.cruiseControl.cancel = CS.cruiseState.enabled and (not self.enabled or not self.CP.pcmCruise)
if self.joystick_mode and self.sm.rcv_frame['testJoystick'] > 0 and self.sm['testJoystick'].buttons[0]:
if self.joystick_mode and self.sm.recv_frame['testJoystick'] > 0 and self.sm['testJoystick'].buttons[0]:
CC.cruiseControl.cancel = True
speeds = self.sm['longitudinalPlan'].speeds
+1 -1
View File
@@ -224,7 +224,7 @@ def user_soft_disable_alert(alert_text_2: str) -> AlertCallbackType:
return func
def startup_master_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int) -> Alert:
branch = get_short_branch("") # Ensure get_short_branch is cached to avoid lags on startup
branch = get_short_branch() # Ensure get_short_branch is cached to avoid lags on startup
if "REPLAY" in os.environ:
branch = "replay"
+1 -2
View File
@@ -29,11 +29,10 @@ def plannerd_thread():
longitudinal_planner = LongitudinalPlanner(CP)
pm = messaging.PubMaster(['longitudinalPlan', 'uiPlan', 'longitudinalPlanSP'])
sm = messaging.SubMaster(['carControl', 'carState', 'controlsState', 'radarState', 'modelV2'],
poll=['radarState', 'modelV2'], ignore_avg_freq=['radarState'])
poll='modelV2', ignore_avg_freq=['radarState'])
while True:
sm.update()
if sm.updated['modelV2']:
longitudinal_planner.update(sm)
longitudinal_planner.publish(sm, pm)
+10 -18
View File
@@ -8,7 +8,7 @@ import capnp
from cereal import messaging, log, car
from openpilot.common.numpy_fast import interp
from openpilot.common.params import Params
from openpilot.common.realtime import Ratekeeper, Priority, config_realtime_process
from openpilot.common.realtime import DT_CTRL, Ratekeeper, Priority, config_realtime_process
from openpilot.common.swaglog import cloudlog
from openpilot.common.simple_kalman import KF1D
@@ -201,6 +201,7 @@ class RadarD:
self.v_ego = 0.0
self.v_ego_hist = deque([0.0], maxlen=delay+1)
self.last_v_ego_frame = -1
self.radar_state: Optional[capnp._DynamicStructBuilder] = None
self.radar_state_valid = False
@@ -208,6 +209,7 @@ class RadarD:
self.ready = False
def update(self, sm: messaging.SubMaster, rr: Optional[car.RadarData]):
self.ready = sm.seen['modelV2']
self.current_time = 1e-9*max(sm.logMonoTime.values())
radar_points = []
@@ -216,11 +218,10 @@ class RadarD:
radar_points = rr.points
radar_errors = rr.errors
if sm.updated['carState']:
if sm.recv_frame['carState'] != self.last_v_ego_frame:
self.v_ego = sm['carState'].vEgo
self.v_ego_hist.append(self.v_ego)
if sm.updated['modelV2']:
self.ready = True
self.last_v_ego_frame = sm.recv_frame['carState']
ar_pts = {}
for pt in radar_points:
@@ -282,7 +283,7 @@ class RadarD:
# fuses camera and radar data for best lead detection
def radard_thread(sm: Optional[messaging.SubMaster] = None, pm: Optional[messaging.PubMaster] = None, can_sock: Optional[messaging.SubSocket] = None):
def main():
config_realtime_process(5, Priority.CTRL_LOW)
# wait for stats about the car to come in from controls
@@ -296,12 +297,9 @@ def radard_thread(sm: Optional[messaging.SubMaster] = None, pm: Optional[messagi
RadarInterface = importlib.import_module(f'selfdrive.car.{CP.carName}.radar_interface').RadarInterface
# *** setup messaging
if can_sock is None:
can_sock = messaging.sub_sock('can')
if sm is None:
sm = messaging.SubMaster(['modelV2', 'carState'], ignore_avg_freq=['modelV2', 'carState']) # Can't check average frequency, since radar determines timing
if pm is None:
pm = messaging.PubMaster(['radarState', 'liveTracks'])
can_sock = messaging.sub_sock('can')
sm = messaging.SubMaster(['modelV2', 'carState'], frequency=int(1./DT_CTRL))
pm = messaging.PubMaster(['radarState', 'liveTracks'])
RI = RadarInterface(CP)
@@ -311,21 +309,15 @@ def radard_thread(sm: Optional[messaging.SubMaster] = None, pm: Optional[messagi
while 1:
can_strings = messaging.drain_sock_raw(can_sock, wait_for_one=True)
rr = RI.update(can_strings)
sm.update(0)
if rr is None:
continue
sm.update(0)
RD.update(sm, rr)
RD.publish(pm, -rk.remaining*1000.0)
rk.monitor_time()
def main(sm: Optional[messaging.SubMaster] = None, pm: Optional[messaging.PubMaster] = None, can_sock: messaging.SubSocket = None):
radard_thread(sm, pm, can_sock)
if __name__ == "__main__":
main()
+7 -19
View File
@@ -1,14 +1,14 @@
#!/usr/bin/env python3
import os
import sys
import argparse
import json
import codecs
import cereal.messaging as messaging
from hexdump import hexdump
from cereal import log
from cereal.services import SERVICE_LIST
from openpilot.tools.lib.live_logreader import raw_live_logreader
codecs.register_error("strict", codecs.backslashreplace_errors)
@@ -22,32 +22,20 @@ if __name__ == "__main__":
parser.add_argument('--no-print', action='store_true')
parser.add_argument('--addr', default='127.0.0.1')
parser.add_argument('--values', help='values to monitor (instead of entire event)')
parser.add_argument("socket", type=str, nargs='*', help="socket names to dump. defaults to all services defined in cereal")
parser.add_argument("socket", type=str, nargs='*', default=list(SERVICE_LIST.keys()), help="socket names to dump. defaults to all services defined in cereal")
args = parser.parse_args()
if args.addr != "127.0.0.1":
os.environ["ZMQ"] = "1"
messaging.context = messaging.Context()
poller = messaging.Poller()
for m in args.socket if len(args.socket) > 0 else SERVICE_LIST:
messaging.sub_sock(m, poller, addr=args.addr)
lr = raw_live_logreader(args.socket, args.addr)
values = None
if args.values:
values = [s.strip().split(".") for s in args.values.split(",")]
while 1:
polld = poller.poll(100)
for sock in polld:
msg = sock.receive()
with log.Event.from_bytes(msg) as log_evt:
evt = log_evt
for msg in lr:
with log.Event.from_bytes(msg) as evt:
if not args.no_print:
if args.pipe:
sys.stdout.write(msg)
sys.stdout.write(str(msg))
sys.stdout.flush()
elif args.raw:
hexdump(msg)
+1 -1
View File
@@ -260,7 +260,7 @@ def main() -> NoReturn:
set_realtime_priority(1)
pm = messaging.PubMaster(['liveCalibration'])
sm = messaging.SubMaster(['cameraOdometry', 'carState', 'carParams'], poll=['cameraOdometry'])
sm = messaging.SubMaster(['cameraOdometry', 'carState', 'carParams'], poll='cameraOdometry')
calibrator = Calibrator(param_put=True)
+1 -1
View File
@@ -124,7 +124,7 @@ def main():
REPLAY = bool(int(os.getenv("REPLAY", "0")))
pm = messaging.PubMaster(['liveParameters'])
sm = messaging.SubMaster(['liveLocationKalman', 'carState'], poll=['liveLocationKalman'])
sm = messaging.SubMaster(['liveLocationKalman', 'carState'], poll='liveLocationKalman')
params_reader = Params()
# wait for stats about the car to come in from controls
+1 -1
View File
@@ -218,7 +218,7 @@ def main(demo=False):
config_realtime_process([0, 1, 2, 3], 5)
pm = messaging.PubMaster(['liveTorqueParameters'])
sm = messaging.SubMaster(['carControl', 'carState', 'liveLocationKalman'], poll=['liveLocationKalman'])
sm = messaging.SubMaster(['carControl', 'carState', 'liveLocationKalman'], poll='liveLocationKalman')
params = Params()
with car.CarParams.from_bytes(params.get("CarParams", block=True)) as CP:
+8 -5
View File
@@ -1,9 +1,10 @@
import errno
import fcntl
import os
import sys
import fcntl
import errno
import signal
import pathlib
import shutil
import signal
import subprocess
import tempfile
import threading
@@ -52,11 +53,13 @@ def write_onroad_params(started, params):
def save_bootlog():
# copy current params
tmp = tempfile.mkdtemp()
shutil.copytree(Params().get_param_path() + "/..", tmp, dirs_exist_ok=True)
params_dirname = pathlib.Path(Params().get_param_path()).name
params_dir = os.path.join(tmp, params_dirname)
shutil.copytree(Params().get_param_path(), params_dir, dirs_exist_ok=True)
def fn(tmpdir):
env = os.environ.copy()
env['PARAMS_ROOT'] = tmpdir
env['PARAMS_COPY_PATH'] = tmpdir
subprocess.call("./bootlog", cwd=os.path.join(BASEDIR, "system/loggerd"), env=env)
shutil.rmtree(tmpdir)
t = threading.Thread(target=fn, args=(tmp, ))
+8 -5
View File
@@ -125,9 +125,9 @@ def manager_init() -> None:
params.put("Version", get_version())
params.put("TermsVersion", terms_version)
params.put("TrainingVersion", training_version)
params.put("GitCommit", get_commit(default=""))
params.put("GitBranch", get_short_branch(default=""))
params.put("GitRemote", get_origin(default=""))
params.put("GitCommit", get_commit())
params.put("GitBranch", get_short_branch())
params.put("GitRemote", get_origin())
params.put_bool("IsTestedBranch", is_tested_branch())
params.put_bool("IsReleaseBranch", is_release_branch())
@@ -139,6 +139,9 @@ def manager_init() -> None:
serial = params.get("HardwareSerial")
raise Exception(f"Registration failed for device {serial}")
os.environ['DONGLE_ID'] = dongle_id # Needed for swaglog
os.environ['GIT_ORIGIN'] = get_normalized_origin() # Needed for swaglog
os.environ['GIT_BRANCH'] = get_short_branch() # Needed for swaglog
os.environ['GIT_COMMIT'] = get_commit() # Needed for swaglog
if not is_dirty():
os.environ['CLEAN'] = '1'
@@ -184,7 +187,7 @@ def manager_thread() -> None:
ignore.append("pandad")
ignore += [x for x in os.getenv("BLOCK", "").split(",") if len(x) > 0]
sm = messaging.SubMaster(['deviceState', 'carParams'], poll=['deviceState'])
sm = messaging.SubMaster(['deviceState', 'carParams'], poll='deviceState')
pm = messaging.PubMaster(['managerState'])
write_onroad_params(False, params)
@@ -193,7 +196,7 @@ def manager_thread() -> None:
started_prev = False
while True:
sm.update()
sm.update(1000)
started = sm['deviceState'].started
+9 -7
View File
@@ -5,6 +5,8 @@ import signal
import time
import unittest
from parameterized import parameterized
from cereal import car
from openpilot.common.params import Params
import openpilot.selfdrive.manager.manager as manager
@@ -38,13 +40,13 @@ class TestManager(unittest.TestCase):
# TODO: ensure there are blacklisted procs until we have a dedicated test
self.assertTrue(len(BLACKLIST_PROCS), "No blacklisted procs to test not_run")
def test_startup_time(self):
for _ in range(10):
start = time.monotonic()
os.environ['PREPAREONLY'] = '1'
manager.main()
t = time.monotonic() - start
assert t < MAX_STARTUP_TIME, f"startup took {t}s, expected <{MAX_STARTUP_TIME}s"
@parameterized.expand([(i,) for i in range(10)])
def test_startup_time(self, index):
start = time.monotonic()
os.environ['PREPAREONLY'] = '1'
manager.main()
t = time.monotonic() - start
assert t < MAX_STARTUP_TIME, f"startup took {t}s, expected <{MAX_STARTUP_TIME}s"
@unittest.skip("this test is flaky the way it's currently written, should be moved to test_onroad")
def test_clean_exit(self):
+2 -2
View File
@@ -16,7 +16,7 @@ def dmonitoringd_thread():
params = Params()
pm = messaging.PubMaster(['driverMonitoringState', 'driverMonitoringStateSP'])
sm = messaging.SubMaster(['driverStateV2', 'liveCalibration', 'carState', 'controlsState', 'modelV2'], poll=['driverStateV2'])
sm = messaging.SubMaster(['driverStateV2', 'liveCalibration', 'carState', 'controlsState', 'modelV2'], poll='driverStateV2')
driver_status = DriverStatus(rhd_saved=params.get_bool("IsRhdDetected"))
hands_on_wheel_status = HandsOnWheelStatus()
@@ -50,7 +50,7 @@ def dmonitoringd_thread():
# Get data from dmonitoringmodeld
events = Events()
if sm.all_checks():
if sm.all_checks() and len(sm['liveCalibration'].rpyCalib):
driver_status.update_states(sm['driverStateV2'], sm['liveCalibration'].rpyCalib, sm['carState'].vEgo, sm['controlsState'].enabled)
# Block engaging after max number of distrations
+8 -6
View File
@@ -4,6 +4,8 @@ import random
import unittest
import numpy as np
from parameterized import parameterized
import cereal.messaging as messaging
from openpilot.common.params import Params
from openpilot.selfdrive.manager.process_config import managed_processes
@@ -24,7 +26,7 @@ class TestNavd(unittest.TestCase):
managed_processes['navd'].start()
for _ in range(30):
self.sm.update(1000)
if all(f > 0 for f in self.sm.rcv_frame.values()):
if all(f > 0 for f in self.sm.recv_frame.values()):
break
else:
raise Exception("didn't get a route")
@@ -50,11 +52,11 @@ class TestNavd(unittest.TestCase):
}
self._check_route(start, end)
def test_random(self):
for _ in range(10):
start = {"latitude": random.uniform(-90, 90), "longitude": random.uniform(-180, 180)}
end = {"latitude": random.uniform(-90, 90), "longitude": random.uniform(-180, 180)}
self._check_route(start, end, check_coords=False)
@parameterized.expand([(i,) for i in range(10)])
def test_random(self, index):
start = {"latitude": random.uniform(-90, 90), "longitude": random.uniform(-180, 180)}
end = {"latitude": random.uniform(-90, 90), "longitude": random.uniform(-180, 180)}
self._check_route(start, end, check_coords=False)
if __name__ == "__main__":
+1 -1
View File
@@ -44,7 +44,7 @@ def set_tag(key: str, value: str) -> None:
def init(project: SentryProject) -> bool:
# forks like to mess with this, so double check
comma_remote = is_comma_remote() and "commaai" in get_origin(default="")
comma_remote = is_comma_remote() and "commaai" in get_origin()
if not comma_remote or not is_registered_device() or PC:
return False
-3
View File
@@ -7,9 +7,6 @@ elif [ "$1" = "sim" ]; then
elif [ "$1" = "prebuilt" ]; then
export DOCKER_IMAGE=openpilot-prebuilt
export DOCKER_FILE=Dockerfile.openpilot
elif [ "$1" = "cl" ]; then
export DOCKER_IMAGE=openpilot-base-cl
export DOCKER_FILE=Dockerfile.openpilot_base_cl
else
echo "Invalid docker build image: '$1'"
exit 1
+8
View File
@@ -0,0 +1,8 @@
#!/usr/bin/env bash
set -e
# Loop something forever until it fails, for verifying new tests
while true; do
$@
done
@@ -762,7 +762,6 @@ def generate_environ_config(CP=None, fingerprint=None, log_dir=None) -> Dict[str
if log_dir is not None:
environ_dict["LOG_ROOT"] = log_dir
environ_dict["NO_RADAR_SLEEP"] = "1"
environ_dict["REPLAY"] = "1"
# Regen or python process
+1 -1
View File
@@ -1 +1 @@
d9a3a0d4e806b49ec537233d30926bec70308485
7d25b1f7d0bd3b506fa4e72ff893728894eb1a45
@@ -58,7 +58,7 @@ segments = [
("VOLKSWAGEN", "regen8BDFE7307A0|2023-10-30--23-19-36--0"),
("MAZDA", "regen2E9F1A15FD5|2023-10-30--23-20-36--0"),
("FORD", "regen6D39E54606E|2023-10-30--23-20-54--0"),
]
]
# dashcamOnly makes don't need to be tested until a full port is done
excluded_interfaces = ["mock", "tesla"]
@@ -107,7 +107,9 @@ def test_process(cfg, lr, segment, ref_log_path, new_log_path, ignore_fields=Non
# check to make sure openpilot is engaged in the route
if cfg.proc_name == "controlsd":
if not check_openpilot_enabled(log_msgs):
return f"Route did not enable at all or for long enough: {new_log_path}", log_msgs
# FIXME: these segments should work, but the replay enabling logic is too brittle
if segment not in ("regen6CA24BC3035|2023-10-30--23-14-28--0", "regen7D2D3F82D5B|2023-10-30--23-15-55--0"):
return f"Route did not enable at all or for long enough: {new_log_path}", log_msgs
try:
return compare_logs(ref_log_msgs, log_msgs, ignore_fields + cfg.ignore, ignore_msgs, cfg.tolerance), log_msgs
@@ -160,7 +162,7 @@ if __name__ == "__main__":
sys.exit(1)
cur_commit = get_commit()
if cur_commit is None:
if not cur_commit:
raise Exception("Couldn't get current commit")
print(f"***** testing against commit {ref_commit} *****")
-2
View File
@@ -80,12 +80,10 @@ def profile(proc, func, car='toyota'):
if __name__ == '__main__':
from openpilot.selfdrive.controls.controlsd import main as controlsd_thread
from openpilot.selfdrive.controls.radard import radard_thread
from openpilot.selfdrive.locationd.paramsd import main as paramsd_thread
from openpilot.selfdrive.controls.plannerd import main as plannerd_thread
procs = {
'radard': radard_thread,
'controlsd': controlsd_thread,
'paramsd': paramsd_thread,
'plannerd': plannerd_thread,
+10
View File
@@ -0,0 +1,10 @@
#!/bin/bash
SCRIPT_DIR=$(dirname "$0")
BASEDIR=$(realpath "$SCRIPT_DIR/../../")
cd $BASEDIR
# tests that our build system's dependencies are configured properly,
# needs a machine with lots of cores
scons --clean
scons --no-cache --random -j$(nproc)
+4 -10
View File
@@ -39,7 +39,7 @@ PROCS = {
"./ui": 18.0,
"selfdrive.locationd.paramsd": 9.0,
"./sensord": 7.0,
"selfdrive.controls.radard": 4.5,
"selfdrive.controls.radard": 7.0,
"selfdrive.modeld.modeld": 13.0,
"selfdrive.modeld.dmonitoringmodeld": 8.0,
"selfdrive.modeld.navmodeld": 1.0,
@@ -57,7 +57,7 @@ PROCS = {
"selfdrive.boardd.pandad": 0,
"selfdrive.statsd": 0.4,
"selfdrive.navd.navd": 0.4,
"system.loggerd.uploader": (0.5, 10.0),
"system.loggerd.uploader": (0.5, 15.0),
"system.loggerd.deleter": 0.1,
}
@@ -112,17 +112,11 @@ class TestOnroad(unittest.TestCase):
# setup env
params = Params()
if "CI" in os.environ:
params.clear_all()
params.remove("CurrentRoute")
set_params_enabled()
os.environ['TESTING_CLOSET'] = '1'
if os.path.exists(Paths.log_root()):
shutil.rmtree(Paths.log_root())
os.system("rm /dev/shm/*")
# Make sure athena isn't running
os.system("pkill -9 -f athena")
# start manager and run openpilot for a minute
proc = None
@@ -132,7 +126,7 @@ class TestOnroad(unittest.TestCase):
sm = messaging.SubMaster(['carState'])
with Timeout(150, "controls didn't start"):
while sm.rcv_frame['carState'] < 0:
while sm.recv_frame['carState'] < 0:
sm.update(1000)
# make sure we get at least two full segments
@@ -429,4 +423,4 @@ class TestOnroad(unittest.TestCase):
if __name__ == "__main__":
unittest.main()
pytest.main()
+9 -6
View File
@@ -29,12 +29,15 @@ def test_time_to_onroad():
time.sleep(1)
# wait for engageability
with Timeout(10, "timed out waiting for engageable"):
while True:
sm.update(1000)
if sm['controlsState'].engageable:
break
time.sleep(1)
try:
with Timeout(10, "timed out waiting for engageable"):
while True:
sm.update(1000)
if sm['controlsState'].engageable:
break
time.sleep(1)
finally:
print(f"onroad events: {sm['onroadEvents']}")
print(f"engageable after {time.monotonic() - start_time:.2f}s")
# once we're enageable, must be for the next few seconds
+2 -2
View File
@@ -166,7 +166,7 @@ def hw_state_thread(end_event, hw_queue):
def thermald_thread(end_event, hw_queue) -> None:
pm = messaging.PubMaster(['deviceState'])
sm = messaging.SubMaster(["peripheralState", "gpsLocationExternal", "controlsState", "pandaStates"], poll=["pandaStates"])
sm = messaging.SubMaster(["peripheralState", "gpsLocationExternal", "controlsState", "pandaStates"], poll="pandaStates")
count = 0
@@ -233,7 +233,7 @@ def thermald_thread(end_event, hw_queue) -> None:
if TICI:
fan_controller = TiciFanController()
elif (time.monotonic() - sm.rcv_time['pandaStates']) > DISCONNECT_TIMEOUT:
elif (time.monotonic() - sm.recv_time['pandaStates']) > DISCONNECT_TIMEOUT:
if onroad_conditions["ignition"]:
onroad_conditions["ignition"] = False
cloudlog.error("panda timed out onroad")
+1 -1
View File
@@ -124,7 +124,7 @@ def report_tombstone_apport(fn):
clean_path = path.replace('/', '_')
date = datetime.datetime.now().strftime("%Y-%m-%d--%H-%M-%S")
new_fn = f"{date}_{get_commit(default='nocommit')[:8]}_{safe_fn(clean_path)}"[:MAX_TOMBSTONE_FN_LEN]
new_fn = f"{date}_{(get_commit() or 'nocommit')[:8]}_{safe_fn(clean_path)}"[:MAX_TOMBSTONE_FN_LEN]
crashlog_dir = os.path.join(Paths.log_root(), "crash")
os.makedirs(crashlog_dir, exist_ok=True)
+1
View File
@@ -4,6 +4,7 @@ moc_*
translations/main_test_en.*
ui
mui
watch3
installer/installers/*
qt/text
+3
View File
@@ -92,6 +92,9 @@ if GetOption('extras') and arch != "Darwin":
# build updater UI
qt_env.Program("qt/setup/updater", ["qt/setup/updater.cc", asset_obj], LIBS=qt_libs)
# build mui
qt_env.Program("mui", ["mui.cc"], LIBS=qt_libs)
# build installers
senv = qt_env.Clone()
senv['LINKFLAGS'].append('-Wl,-strip-debug')
+50
View File
@@ -0,0 +1,50 @@
#include <QApplication>
#include <QtWidgets>
#include <QTimer>
#include "cereal/messaging/messaging.h"
#include "selfdrive/ui/ui.h"
#include "selfdrive/ui/qt/qt_window.h"
int main(int argc, char *argv[]) {
QApplication a(argc, argv);
QWidget w;
setMainWindow(&w);
w.setStyleSheet("background-color: black;");
// our beautiful UI
QVBoxLayout *layout = new QVBoxLayout(&w);
QLabel *label = new QLabel("");
layout->addWidget(label, 0, Qt::AlignCenter);
QTimer timer;
QObject::connect(&timer, &QTimer::timeout, [=]() {
static SubMaster sm({"deviceState", "controlsState"});
bool onroad_prev = sm.allAliveAndValid({"deviceState"}) &&
sm["deviceState"].getDeviceState().getStarted();
sm.update(0);
bool onroad = sm.allAliveAndValid({"deviceState"}) &&
sm["deviceState"].getDeviceState().getStarted();
if (onroad) {
label->setText("");
auto cs = sm["controlsState"].getControlsState();
UIStatus status = cs.getEnabled() ? STATUS_ENGAGED : STATUS_DISENGAGED;
label->setStyleSheet(QString("color: %1; font-size: 250px;").arg(bg_colors[status].name()));
} else {
label->setText("offroad");
label->setStyleSheet("color: grey; font-size: 40px;");
}
if ((onroad != onroad_prev) || sm.frame < 2) {
Hardware::set_brightness(50);
Hardware::set_display_power(onroad);
}
});
timer.start(50);
return a.exec();
}
+2 -2
View File
@@ -199,7 +199,7 @@ OnboardingWindow::OnboardingWindow(QWidget *parent) : QStackedWidget(parent) {
TermsPage* terms = new TermsPage(this);
addWidget(terms);
connect(terms, &TermsPage::acceptedTerms, [=]() {
Params().put("HasAcceptedTerms", current_terms_version);
params.put("HasAcceptedTerms", current_terms_version);
accepted_terms = true;
updateActiveScreen();
});
@@ -209,7 +209,7 @@ OnboardingWindow::OnboardingWindow(QWidget *parent) : QStackedWidget(parent) {
addWidget(tr);
connect(tr, &TrainingGuide::completedTraining, [=]() {
training_done = true;
Params().put("CompletedTrainingVersion", current_training_version);
params.put("CompletedTrainingVersion", current_training_version);
updateActiveScreen();
});
+1 -1
View File
@@ -290,7 +290,7 @@ void AnnotatedCameraWidget::updateState(const UIState &s) {
const auto nav_instruction = sm["navInstruction"].getNavInstruction();
// Handle older routes where vCruiseCluster is not set
float v_cruise = cs.getVCruiseCluster() == 0.0 ? cs.getVCruise() : cs.getVCruiseCluster();
float v_cruise = cs.getVCruiseCluster() == 0.0 ? cs.getVCruise() : cs.getVCruiseCluster();
setSpeed = cs_alive ? v_cruise : SET_SPEED_NA;
is_cruise_set = setSpeed > 0 && (int)setSpeed != SET_SPEED_NA;
if (is_cruise_set && !s.scene.is_metric) {
+1 -1
View File
@@ -42,7 +42,7 @@ sound_list: Dict[int, Tuple[str, Optional[int], float]] = {
}
def check_controls_timeout_alert(sm):
controls_missing = time.monotonic() - sm.rcv_time['controlsState']
controls_missing = time.monotonic() - sm.recv_time['controlsState']
if controls_missing > CONTROLS_TIMEOUT:
if sm['controlsState'].enabled and (controls_missing - CONTROLS_TIMEOUT) < 10:
+82 -82
View File
@@ -413,96 +413,96 @@ def main() -> None:
cloudlog.warning("updates are disabled by the DisableUpdates param")
exit(0)
ov_lock_fd = open(LOCK_FILE, 'w')
try:
fcntl.flock(ov_lock_fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
except OSError as e:
raise RuntimeError("couldn't get overlay lock; is another instance running?") from e
# Set low io priority
proc = psutil.Process()
if psutil.LINUX:
proc.ionice(psutil.IOPRIO_CLASS_BE, value=7)
# Check if we just performed an update
if Path(os.path.join(STAGING_ROOT, "old_openpilot")).is_dir():
cloudlog.event("update installed")
if not params.get("InstallDate"):
t = datetime.datetime.utcnow().isoformat()
params.put("InstallDate", t.encode('utf8'))
updater = Updater()
update_failed_count = 0 # TODO: Load from param?
wait_helper = WaitTimeHelper()
# invalidate old finalized update
set_consistent_flag(False)
# set initial state
params.put("UpdaterState", "idle")
# Run the update loop
first_run = True
while True:
wait_helper.ready_event.clear()
# Attempt an update
exception = None
with open(LOCK_FILE, 'w') as ov_lock_fd:
try:
# TODO: reuse overlay from previous updated instance if it looks clean
init_overlay()
fcntl.flock(ov_lock_fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
except OSError as e:
raise RuntimeError("couldn't get overlay lock; is another instance running?") from e
# ensure we have some params written soon after startup
updater.set_params(False, update_failed_count, exception)
# Set low io priority
proc = psutil.Process()
if psutil.LINUX:
proc.ionice(psutil.IOPRIO_CLASS_BE, value=7)
if not system_time_valid() or first_run:
first_run = False
wait_helper.sleep(60)
continue
# Check if we just performed an update
if Path(os.path.join(STAGING_ROOT, "old_openpilot")).is_dir():
cloudlog.event("update installed")
update_failed_count += 1
if not params.get("InstallDate"):
t = datetime.datetime.utcnow().isoformat()
params.put("InstallDate", t.encode('utf8'))
# check for update
params.put("UpdaterState", "checking...")
updater.check_for_update()
updater = Updater()
update_failed_count = 0 # TODO: Load from param?
wait_helper = WaitTimeHelper()
# download update
last_fetch = read_time_from_param(params, "UpdaterLastFetchTime")
timed_out = last_fetch is None or (datetime.datetime.utcnow() - last_fetch > datetime.timedelta(days=3))
user_requested_fetch = wait_helper.user_request == UserRequest.FETCH
if params.get_bool("NetworkMetered") and not timed_out and not user_requested_fetch:
cloudlog.info("skipping fetch, connection metered")
elif wait_helper.user_request == UserRequest.CHECK:
cloudlog.info("skipping fetch, only checking")
else:
updater.fetch_update()
write_time_to_param(params, "UpdaterLastFetchTime")
update_failed_count = 0
except subprocess.CalledProcessError as e:
cloudlog.event(
"update process failed",
cmd=e.cmd,
output=e.output,
returncode=e.returncode
)
exception = f"command failed: {e.cmd}\n{e.output}"
OVERLAY_INIT.unlink(missing_ok=True)
except Exception as e:
cloudlog.exception("uncaught updated exception, shouldn't happen")
exception = str(e)
OVERLAY_INIT.unlink(missing_ok=True)
# invalidate old finalized update
set_consistent_flag(False)
try:
params.put("UpdaterState", "idle")
update_successful = (update_failed_count == 0)
updater.set_params(update_successful, update_failed_count, exception)
except Exception:
cloudlog.exception("uncaught updated exception while setting params, shouldn't happen")
# set initial state
params.put("UpdaterState", "idle")
# infrequent attempts if we successfully updated recently
wait_helper.user_request = UserRequest.NONE
wait_helper.sleep(5*60 if update_failed_count > 0 else 1.5*60*60)
# Run the update loop
first_run = True
while True:
wait_helper.ready_event.clear()
# Attempt an update
exception = None
try:
# TODO: reuse overlay from previous updated instance if it looks clean
init_overlay()
# ensure we have some params written soon after startup
updater.set_params(False, update_failed_count, exception)
if not system_time_valid() or first_run:
first_run = False
wait_helper.sleep(60)
continue
update_failed_count += 1
# check for update
params.put("UpdaterState", "checking...")
updater.check_for_update()
# download update
last_fetch = read_time_from_param(params, "UpdaterLastFetchTime")
timed_out = last_fetch is None or (datetime.datetime.utcnow() - last_fetch > datetime.timedelta(days=3))
user_requested_fetch = wait_helper.user_request == UserRequest.FETCH
if params.get_bool("NetworkMetered") and not timed_out and not user_requested_fetch:
cloudlog.info("skipping fetch, connection metered")
elif wait_helper.user_request == UserRequest.CHECK:
cloudlog.info("skipping fetch, only checking")
else:
updater.fetch_update()
write_time_to_param(params, "UpdaterLastFetchTime")
update_failed_count = 0
except subprocess.CalledProcessError as e:
cloudlog.event(
"update process failed",
cmd=e.cmd,
output=e.output,
returncode=e.returncode
)
exception = f"command failed: {e.cmd}\n{e.output}"
OVERLAY_INIT.unlink(missing_ok=True)
except Exception as e:
cloudlog.exception("uncaught updated exception, shouldn't happen")
exception = str(e)
OVERLAY_INIT.unlink(missing_ok=True)
try:
params.put("UpdaterState", "idle")
update_successful = (update_failed_count == 0)
updater.set_params(update_successful, update_failed_count, exception)
except Exception:
cloudlog.exception("uncaught updated exception while setting params, shouldn't happen")
# infrequent attempts if we successfully updated recently
wait_helper.user_request = UserRequest.NONE
wait_helper.sleep(5*60 if update_failed_count > 0 else 1.5*60*60)
if __name__ == "__main__":
+1
View File
@@ -606,6 +606,7 @@ void CameraState::camera_open(MultiCameraState *multi_cam_state_, int camera_num
LOGD("start csiphy: %d", ret);
ret = device_control(multi_cam_state->isp_fd, CAM_START_DEV, session_handle, isp_dev_handle);
LOGD("start isp: %d", ret);
assert(ret == 0);
// TODO: this is unneeded, should we be doing the start i2c in a different way?
//ret = device_control(sensor_fd, CAM_START_DEV, session_handle, sensor_dev_handle);
+9 -9
View File
@@ -1,9 +1,9 @@
[
{
"name": "boot",
"url": "https://commadist.azureedge.net/agnosupdate/boot-1cc21f31a7c09772fd759e6f2a614974bf4f2fc320c91a799ffadd11abc1f85f.img.xz",
"hash": "1cc21f31a7c09772fd759e6f2a614974bf4f2fc320c91a799ffadd11abc1f85f",
"hash_raw": "1cc21f31a7c09772fd759e6f2a614974bf4f2fc320c91a799ffadd11abc1f85f",
"url": "https://commadist.azureedge.net/agnosupdate/boot-f0de74e139b8b99224738d4e72a5b1831758f20b09ff6bb28f3aaaae1c4c1ebe.img.xz",
"hash": "f0de74e139b8b99224738d4e72a5b1831758f20b09ff6bb28f3aaaae1c4c1ebe",
"hash_raw": "f0de74e139b8b99224738d4e72a5b1831758f20b09ff6bb28f3aaaae1c4c1ebe",
"size": 15636480,
"sparse": false,
"full_check": true,
@@ -61,17 +61,17 @@
},
{
"name": "system",
"url": "https://commadist.azureedge.net/agnosupdate/system-38402b90b65729f8a4feb729c8a862cdf306659a85f27431d3ff7e52d4027082.img.xz",
"hash": "5dc1718e21c49e4fa910fbb3b2321381f497b38335a0cf3ca923157d589abe89",
"hash_raw": "38402b90b65729f8a4feb729c8a862cdf306659a85f27431d3ff7e52d4027082",
"url": "https://commadist.azureedge.net/agnosupdate/system-3bfb0f3a6bf677bdc8a6227f49ce3258025e1886dc81533e3fbacb356b5601db.img.xz",
"hash": "10ac02f18c5f1cde5a888a3411d3701b929c3488753467e77aad6085db058eb9",
"hash_raw": "3bfb0f3a6bf677bdc8a6227f49ce3258025e1886dc81533e3fbacb356b5601db",
"size": 10737418240,
"sparse": true,
"full_check": false,
"has_ab": true,
"alt": {
"hash": "1809e36d8e376e0a0c8348e3f684aba4100fe0382042c051efd0e946af1ce696",
"url": "https://commadist.azureedge.net/agnosupdate/system-skip-chunks-38402b90b65729f8a4feb729c8a862cdf306659a85f27431d3ff7e52d4027082.img.xz",
"size": 4077270244
"hash": "a7db41b93b587f8f9c3f83a3313f186445c4bdf07283cd6a5421dfbc0286c9db",
"url": "https://commadist.azureedge.net/agnosupdate/system-skip-chunks-3bfb0f3a6bf677bdc8a6227f49ce3258025e1886dc81533e3fbacb356b5601db.img.xz",
"size": 4548131508
}
}
]
+1
View File
@@ -72,6 +72,7 @@ public:
std::map<std::string, std::string> ret = {
{"/BUILD", util::read_file("/BUILD")},
{"lsblk", util::check_output("lsblk -o NAME,SIZE,STATE,VENDOR,MODEL,REV,SERIAL")},
{"SOM ID", util::read_file("/sys/devices/platform/vendor/vendor:gpio-som-id/som_id")},
};
std::string bs = util::check_output("abctl --boot_slot");
+24 -11
View File
@@ -457,24 +457,37 @@ class Tici(HardwareBase):
def configure_modem(self):
sim_id = self.get_sim_info().get('sim_id', '')
# configure modem as data-centric
cmds = [
'AT+QNVW=5280,0,"0102000000000000"',
'AT+QNVFW="/nv/item_files/ims/IMS_enable",00',
'AT+QNVFW="/nv/item_files/modem/mmode/ue_usage_setting",01',
]
modem = self.get_modem()
try:
manufacturer = str(modem.Get(MM_MODEM, 'Manufacturer', dbus_interface=DBUS_PROPS, timeout=TIMEOUT))
except Exception:
manufacturer = None
cmds = []
if manufacturer == 'Cavli Inc.':
cmds += [
# use sim slot
'AT^SIMSWAP=1',
# configure ECM mode
'AT$QCPCFG=usbNet,1'
]
else:
cmds += [
# configure modem as data-centric
'AT+QNVW=5280,0,"0102000000000000"',
'AT+QNVFW="/nv/item_files/ims/IMS_enable",00',
'AT+QNVFW="/nv/item_files/modem/mmode/ue_usage_setting",01',
]
# clear out old blue prime initial APN
os.system('mmcli -m any --3gpp-set-initial-eps-bearer-settings="apn="')
for cmd in cmds:
try:
modem.Command(cmd, math.ceil(TIMEOUT), dbus_interface=MM_MODEM, timeout=TIMEOUT)
except Exception:
pass
# blue prime
blue_prime = sim_id.startswith('8901410')
initial_apn = "Broadband" if blue_prime else ""
os.system(f'mmcli -m any --3gpp-set-initial-eps-bearer-settings="apn={initial_apn}"')
# eSIM prime
if sim_id.startswith('8985235'):
dest = "/etc/NetworkManager/system-connections/esim.nmconnection"
+1 -6
View File
@@ -12,11 +12,6 @@ HARDWARE = Tici()
@pytest.mark.tici
class TestHardware(unittest.TestCase):
@classmethod
def setUpClass(cls):
HARDWARE.initialize_hardware()
HARDWARE.set_power_save(False)
def test_power_save_time(self):
ts = []
for _ in range(5):
@@ -30,4 +25,4 @@ class TestHardware(unittest.TestCase):
if __name__ == "__main__":
unittest.main()
pytest.main()
+72 -27
View File
@@ -1,4 +1,6 @@
#!/usr/bin/env python3
from collections import defaultdict, deque
import sys
import pytest
import unittest
import time
@@ -11,29 +13,32 @@ import cereal.messaging as messaging
from cereal.services import SERVICE_LIST
from openpilot.common.mock import mock_messages
from openpilot.selfdrive.car.car_helpers import write_car_param
from openpilot.system.hardware import HARDWARE
from openpilot.system.hardware.tici.power_monitor import get_power
from openpilot.selfdrive.manager.process_config import managed_processes
from openpilot.selfdrive.manager.manager import manager_cleanup
SAMPLE_TIME = 8 # seconds to sample power
SAMPLE_TIME = 8 # seconds to sample power
MAX_WARMUP_TIME = 30 # seconds to wait for SAMPLE_TIME consecutive valid samples
@dataclass
class Proc:
name: str
procs: List[str]
power: float
msgs: List[str]
rtol: float = 0.05
atol: float = 0.12
warmup: float = 6.
@property
def name(self):
return '+'.join(self.procs)
PROCS = [
Proc('camerad', 2.1, msgs=['roadCameraState', 'wideRoadCameraState', 'driverCameraState']),
Proc('modeld', 1.12, atol=0.2, msgs=['modelV2']),
Proc('dmonitoringmodeld', 0.4, msgs=['driverStateV2']),
Proc('encoderd', 0.23, msgs=[]),
Proc('mapsd', 0.05, msgs=['mapRenderState']),
Proc('navmodeld', 0.05, msgs=['navModel']),
Proc(['camerad'], 2.1, msgs=['roadCameraState', 'wideRoadCameraState', 'driverCameraState']),
Proc(['modeld'], 1.12, atol=0.2, msgs=['modelV2']),
Proc(['dmonitoringmodeld'], 0.4, msgs=['driverStateV2']),
Proc(['encoderd'], 0.23, msgs=[]),
Proc(['mapsd', 'navmodeld'], 0.05, msgs=['mapRenderState', 'navModel']),
]
@@ -41,8 +46,6 @@ PROCS = [
class TestPowerDraw(unittest.TestCase):
def setUp(self):
HARDWARE.initialize_hardware()
HARDWARE.set_power_save(False)
write_car_param()
# wait a bit for power save to disable
@@ -51,41 +54,83 @@ class TestPowerDraw(unittest.TestCase):
def tearDown(self):
manager_cleanup()
def get_expected_messages(self, proc):
return int(sum(SAMPLE_TIME * SERVICE_LIST[msg].frequency for msg in proc.msgs))
def valid_msg_count(self, proc, msg_counts):
msgs_received = sum(msg_counts[msg] for msg in proc.msgs)
msgs_expected = self.get_expected_messages(proc)
return np.core.numeric.isclose(msgs_expected, msgs_received, rtol=.02, atol=2)
def valid_power_draw(self, proc, used):
return np.core.numeric.isclose(used, proc.power, rtol=proc.rtol, atol=proc.atol)
def tabulate_msg_counts(self, msgs_and_power):
msg_counts = defaultdict(lambda: 0)
for _, counts in msgs_and_power:
for msg, count in counts.items():
msg_counts[msg] += count
return msg_counts
def get_power_with_warmup_for_target(self, proc, prev):
socks = {msg: messaging.sub_sock(msg) for msg in proc.msgs}
for sock in socks.values():
messaging.drain_sock_raw(sock)
msgs_and_power = deque([], maxlen=SAMPLE_TIME)
start_time = time.monotonic()
while (time.monotonic() - start_time) < MAX_WARMUP_TIME:
power = get_power(1)
iteration_msg_counts = {}
for msg,sock in socks.items():
iteration_msg_counts[msg] = len(messaging.drain_sock_raw(sock))
msgs_and_power.append((power, iteration_msg_counts))
if len(msgs_and_power) < SAMPLE_TIME:
continue
msg_counts = self.tabulate_msg_counts(msgs_and_power)
now = np.mean([m[0] for m in msgs_and_power])
if self.valid_msg_count(proc, msg_counts) and self.valid_power_draw(proc, now - prev):
break
return now, msg_counts, time.monotonic() - start_time - SAMPLE_TIME
@mock_messages(['liveLocationKalman'])
def test_camera_procs(self):
baseline = get_power()
prev = baseline
used = {}
warmup_time = {}
msg_counts = {}
for proc in PROCS:
socks = {msg: messaging.sub_sock(msg) for msg in proc.msgs}
managed_processes[proc.name].start()
time.sleep(proc.warmup)
for sock in socks.values():
messaging.drain_sock_raw(sock)
now = get_power(SAMPLE_TIME)
for proc in PROCS:
for p in proc.procs:
managed_processes[p].start()
now, local_msg_counts, warmup_time[proc.name] = self.get_power_with_warmup_for_target(proc, prev)
msg_counts.update(local_msg_counts)
used[proc.name] = now - prev
prev = now
for msg,sock in socks.items():
msg_counts[msg] = len(messaging.drain_sock_raw(sock))
manager_cleanup()
tab = [['process', 'expected (W)', 'measured (W)', '# msgs expected', '# msgs received']]
tab = [['process', 'expected (W)', 'measured (W)', '# msgs expected', '# msgs received', "warmup time (s)"]]
for proc in PROCS:
cur = used[proc.name]
expected = proc.power
msgs_received = sum(msg_counts[msg] for msg in proc.msgs)
msgs_expected = int(sum(SAMPLE_TIME * SERVICE_LIST[msg].frequency for msg in proc.msgs))
tab.append([proc.name, round(expected, 2), round(cur, 2), msgs_expected, msgs_received])
tab.append([proc.name, round(expected, 2), round(cur, 2), self.get_expected_messages(proc), msgs_received, round(warmup_time[proc.name], 2)])
with self.subTest(proc=proc.name):
np.testing.assert_allclose(msgs_expected, msgs_received, rtol=.02, atol=2)
np.testing.assert_allclose(cur, expected, rtol=proc.rtol, atol=proc.atol)
self.assertTrue(self.valid_msg_count(proc, msg_counts), f"expected {self.get_expected_messages(proc)} msgs, got {msgs_received} msgs")
self.assertTrue(self.valid_power_draw(proc, cur), f"expected {expected:.2f}W, got {cur:.2f}W")
print(tabulate(tab))
print(f"Baseline {baseline:.2f}W\n")
if __name__ == "__main__":
unittest.main()
pytest.main(sys.argv)
+6
View File
@@ -2,6 +2,10 @@
VideoEncoder::VideoEncoder(const EncoderInfo &encoder_info, int in_width, int in_height)
: encoder_info(encoder_info), in_width(in_width), in_height(in_height) {
out_width = encoder_info.frame_width > 0 ? encoder_info.frame_width : in_width;
out_height = encoder_info.frame_height > 0 ? encoder_info.frame_height : in_height;
pm.reset(new PubMaster({encoder_info.publish_name}));
}
@@ -25,6 +29,8 @@ void VideoEncoder::publisher_publish(VideoEncoder *e, int segment_num, uint32_t
edata.setFlags(flags);
edata.setLen(dat.size());
edat.setData(dat);
edat.setWidth(out_width);
edat.setHeight(out_height);
if (flags & V4L2_BUF_FLAG_KEYFRAME) edat.setHeader(header);
uint32_t bytes_size = capnp::computeSerializedSizeInWords(msg) * sizeof(capnp::word);
+2 -1
View File
@@ -22,10 +22,11 @@ public:
virtual void encoder_open(const char* path) = 0;
virtual void encoder_close() = 0;
static void publisher_publish(VideoEncoder *e, int segment_num, uint32_t idx, VisionIpcBufExtra &extra, unsigned int flags, kj::ArrayPtr<capnp::byte> header, kj::ArrayPtr<capnp::byte> dat);
void publisher_publish(VideoEncoder *e, int segment_num, uint32_t idx, VisionIpcBufExtra &extra, unsigned int flags, kj::ArrayPtr<capnp::byte> header, kj::ArrayPtr<capnp::byte> dat);
protected:
int in_width, in_height;
int out_width, out_height;
const EncoderInfo encoder_info;
private:
+7 -7
View File
@@ -29,16 +29,16 @@ FfmpegEncoder::FfmpegEncoder(const EncoderInfo &encoder_info, int in_width, int
frame = av_frame_alloc();
assert(frame);
frame->format = AV_PIX_FMT_YUV420P;
frame->width = encoder_info.frame_width;
frame->height = encoder_info.frame_height;
frame->linesize[0] = encoder_info.frame_width;
frame->linesize[1] = encoder_info.frame_width/2;
frame->linesize[2] = encoder_info.frame_width/2;
frame->width = out_width;
frame->height = out_height;
frame->linesize[0] = out_width;
frame->linesize[1] = out_width/2;
frame->linesize[2] = out_width/2;
convert_buf.resize(in_width * in_height * 3 / 2);
if (in_width != encoder_info.frame_width || in_height != encoder_info.frame_height) {
downscale_buf.resize(encoder_info.frame_width * encoder_info.frame_height * 3 / 2);
if (in_width != out_width || in_height != out_height) {
downscale_buf.resize(out_width * out_height * 3 / 2);
}
}

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